export function weddingCostCalculator(form: FormTs) {
// ==================== HELPER FUNCTIONS ====================
const isDestination = () => basicSection.checkbox('isDestination')?.value() === true;
const isLuxury = () => ['upscale', 'luxury'].includes(basicSection.dropdown('weddingStyle')?.value() || '');
const isBudget = () => basicSection.dropdown('weddingStyle')?.value() === 'budget';
const getSeasonFromDate = () => {
const dateStr = basicSection.datepicker('weddingDate')?.value();
if (!dateStr) return 'shoulder';
const month = new Date(dateStr).getMonth() + 1;
if (month >= 6 && month <= 9) return 'peak';
if (month >= 4 && month <= 5 || month === 10) return 'shoulder';
return 'off-peak';
};
const getSeasonMultiplier = () => {
const season = getSeasonFromDate();
const multipliers: Record<string, number> = { 'peak': 1.3, 'shoulder': 1.0, 'off-peak': 0.8 };
return multipliers[season] || 1;
};
const getDayMultiplier = () => {
const day = basicSection.dropdown('dayOfWeek')?.value() || 'saturday';
const multipliers: Record<string, number> = { 'saturday': 1.2, 'friday': 1.1, 'sunday': 1.0, 'weekday': 0.85 };
return multipliers[day] || 1;
};
const getStyleMultiplier = () => {
const style = basicSection.dropdown('weddingStyle')?.value() || 'average';
const multipliers: Record<string, number> = { 'budget': 0.7, 'average': 1.0, 'upscale': 1.4, 'luxury': 2.0 };
return multipliers[style] || 1;
};
const getGuestCount = () => basicSection.slider('guestCount')?.value() || 100;
// ==================== HEADER ====================
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => '💒 Wedding Budget Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b', 'text-align': 'center' }
});
});
form.addRow(row => {
row.addTextPanel('subtitle', {
computedValue: () => 'Get a realistic estimate for your dream wedding',
customStyles: { 'font-size': '0.95rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.addSpacer({ height: 24 });
// ==================== BASIC DETAILS ====================
const basicSection = form.addSubform('basic', { title: '📅 Wedding Details' });
basicSection.addRow(row => {
row.addDatepicker('weddingDate', {
label: 'Wedding Date',
minDate: () => new Date().toISOString().split('T')[0],
tooltip: 'Season pricing calculated automatically'
}, '1fr');
row.addDropdown('dayOfWeek', {
label: 'Day of Week',
options: [
{ id: 'saturday', name: 'Saturday (+20%)' },
{ id: 'friday', name: 'Friday (+10%)' },
{ id: 'sunday', name: 'Sunday (standard)' },
{ id: 'weekday', name: 'Weekday (-15%)' }
],
defaultValue: 'saturday'
}, '1fr');
});
basicSection.addRow(row => {
row.addTextPanel('seasonInfo', {
computedValue: () => {
const season = getSeasonFromDate();
const labels: Record<string, string> = {
'peak': '☀️ Peak Season (Jun-Sep) - prices +30%',
'shoulder': '🍂 Shoulder Season (Apr-May, Oct) - standard prices',
'off-peak': '❄️ Off-Peak (Nov-Mar) - prices -20%'
};
return basicSection.datepicker('weddingDate')?.value()
? labels[season]
: 'Select a date to see seasonal pricing';
},
customStyles: { 'font-size': '0.9rem', 'color': '#6366f1', 'font-style': 'italic' }
});
});
basicSection.addRow(row => {
row.addSlider('guestCount', {
label: 'Number of Guests',
min: 20,
max: 400,
step: 10,
defaultValue: 100,
unit: 'guests'
});
});
basicSection.addRow(row => {
row.addDropdown('weddingStyle', {
label: 'Wedding Style',
options: [
{ id: 'budget', name: 'Budget-Friendly - DIY touches, prioritize essentials' },
{ id: 'average', name: 'Classic - Traditional wedding experience' },
{ id: 'upscale', name: 'Upscale - Premium vendors, extra details' },
{ id: 'luxury', name: 'Luxury - No compromises, the best of everything' }
],
defaultValue: 'average'
});
});
basicSection.addRow(row => {
row.addCheckbox('isDestination', {
label: 'This is a destination wedding',
defaultValue: false
});
});
// ==================== DESTINATION WEDDING ====================
const destinationSection = form.addSubform('destination', {
title: '✈️ Destination Details',
isVisible: isDestination
});
destinationSection.addRow(row => {
row.addDropdown('destinationType', {
label: 'Destination Type',
options: [
{ id: 'beach', name: '🏝️ Beach / Tropical' },
{ id: 'europe', name: '🏰 European / Castle' },
{ id: 'mountain', name: '⛰️ Mountain / Vineyard' },
{ id: 'city', name: '🌆 Major City' },
{ id: 'other', name: '📍 Other Location' }
],
defaultValue: 'beach'
}, '1fr');
row.addSlider('travelingGuests', {
label: 'Guests Traveling',
min: 10,
max: 200,
step: 5,
defaultValue: 50,
unit: 'guests',
tooltip: 'Number of guests requiring travel'
}, '1fr');
});
destinationSection.addRow(row => {
row.addCheckboxList('destinationExtras', {
label: 'Destination Extras',
options: [
{ id: 'welcome', name: 'Welcome Party/Dinner (+$3,000)' },
{ id: 'brunch', name: 'Morning-After Brunch (+$1,500)' },
{ id: 'transport', name: 'Guest Transportation (+$2,000)' },
{ id: 'activities', name: 'Group Activities (+$1,500)' }
],
orientation: 'vertical'
});
});
// ==================== VENUE ====================
const venueSection = form.addSubform('venue', { title: '🏛️ Venue' });
venueSection.addRow(row => {
row.addRadioButton('venueType', {
label: 'Venue Type',
options: () => {
if (isDestination()) {
return [
{ id: 'resort', name: 'Resort/Hotel ($8,000)' },
{ id: 'villa', name: 'Private Villa ($12,000)' },
{ id: 'beach-venue', name: 'Beach Venue ($6,000)' },
{ id: 'castle', name: 'Castle/Historic ($15,000)' }
];
}
if (isLuxury()) {
return [
{ id: 'estate', name: 'Estate/Mansion ($8,000)' },
{ id: 'country-club', name: 'Country Club ($6,000)' },
{ id: 'hotel-luxury', name: 'Luxury Hotel ($10,000)' },
{ id: 'vineyard', name: 'Vineyard/Winery ($7,000)' },
{ id: 'museum', name: 'Museum/Gallery ($12,000)' }
];
}
if (isBudget()) {
return [
{ id: 'backyard', name: 'Backyard/Private Home ($500)' },
{ id: 'restaurant', name: 'Restaurant ($2,500)' },
{ id: 'park', name: 'Public Park ($800)' },
{ id: 'community', name: 'Community Center ($1,500)' }
];
}
return [
{ id: 'barn', name: 'Barn/Rustic ($3,500)' },
{ id: 'hotel', name: 'Hotel Ballroom ($5,000)' },
{ id: 'garden', name: 'Garden/Outdoor ($4,000)' },
{ id: 'restaurant-upscale', name: 'Upscale Restaurant ($4,500)' },
{ id: 'country-club-std', name: 'Country Club ($6,000)' }
];
},
defaultValue: () => {
if (isDestination()) return 'resort';
if (isLuxury()) return 'estate';
if (isBudget()) return 'restaurant';
return 'hotel';
},
orientation: 'vertical'
});
});
venueSection.addRow(row => {
row.addCheckbox('ceremonyDifferent', {
label: 'Ceremony at different location',
defaultValue: false
}, '1fr');
row.addDropdown('ceremonyCost', {
label: 'Ceremony Venue',
options: [
{ id: 'church', name: 'Church/Religious ($500)' },
{ id: 'outdoor', name: 'Outdoor/Garden ($1,000)' },
{ id: 'chapel', name: 'Chapel/Historic ($1,500)' }
],
defaultValue: 'church',
isVisible: () => venueSection.checkbox('ceremonyDifferent')?.value() === true
}, '1fr');
});
// ==================== CATERING ====================
const cateringSection = form.addSubform('catering', { title: '🍽️ Food & Drinks' });
cateringSection.addRow(row => {
row.addDropdown('mealStyle', {
label: 'Meal Style',
options: () => {
if (isLuxury()) {
return [
{ id: 'plated-premium', name: 'Premium Plated ($120/person)' },
{ id: 'tasting', name: 'Tasting Menu ($150/person)' },
{ id: 'stations-luxury', name: 'Luxury Food Stations ($130/person)' }
];
}
return [
{ id: 'buffet', name: 'Buffet ($45/person)' },
{ id: 'family', name: 'Family Style ($55/person)' },
{ id: 'plated', name: 'Plated Dinner ($65/person)' },
{ id: 'stations', name: 'Food Stations ($75/person)' },
{ id: 'plated-premium', name: 'Premium Plated ($95/person)' }
];
},
defaultValue: () => isLuxury() ? 'plated-premium' : 'plated'
}, '1fr');
row.addDropdown('barService', {
label: 'Bar Service',
options: [
{ id: 'none', name: 'No Alcohol ($0)' },
{ id: 'beer-wine', name: 'Beer & Wine ($25/person)' },
{ id: 'limited', name: 'Limited Open Bar ($40/person)' },
{ id: 'full', name: 'Full Open Bar ($55/person)' },
{ id: 'premium', name: 'Premium/Top Shelf ($80/person)' }
],
defaultValue: 'limited'
}, '1fr');
});
cateringSection.addRow(row => {
row.addCheckboxList('cateringExtras', {
label: 'Food Add-ons',
options: [
{ id: 'cocktail', name: 'Cocktail Hour Appetizers (+$18/person)' },
{ id: 'late-night', name: 'Late Night Snacks (+$12/person)' },
{ id: 'cake', name: 'Wedding Cake (+$800)' },
{ id: 'dessert-bar', name: 'Dessert Bar (+$600)' }
],
defaultValue: ['cocktail', 'cake'],
orientation: 'vertical'
});
});
// ==================== PHOTOGRAPHY & VIDEO ====================
const photoSection = form.addSubform('photo', { title: '📸 Photography & Videography' });
photoSection.addRow(row => {
row.addRadioButton('photoPackage', {
label: 'Photography',
options: [
{ id: 'none', name: 'None - friend/family ($0)' },
{ id: 'basic', name: 'Basic - 4 hours ($1,800)' },
{ id: 'standard', name: 'Standard - 8 hours ($3,500)' },
{ id: 'premium', name: 'Premium - Full day + engagement ($5,500)' },
{ id: 'luxury', name: 'Luxury - 2 photographers + albums ($8,000)' }
],
defaultValue: 'standard',
orientation: 'vertical'
}, '1fr');
row.addRadioButton('videoPackage', {
label: 'Videography',
options: [
{ id: 'none', name: 'None ($0)' },
{ id: 'highlight', name: 'Highlight Reel - 3-5 min ($1,800)' },
{ id: 'documentary', name: 'Documentary Style ($3,500)' },
{ id: 'cinematic', name: 'Cinematic Film ($5,500)' },
{ id: 'full', name: 'Full Coverage + Drone ($7,500)' }
],
defaultValue: 'none',
orientation: 'vertical'
}, '1fr');
});
// ==================== ENTERTAINMENT ====================
const entertainmentSection = form.addSubform('entertainment', { title: '🎵 Entertainment' });
entertainmentSection.addRow(row => {
row.addDropdown('receptionMusic', {
label: 'Reception Music',
options: () => {
if (isLuxury()) {
return [
{ id: 'dj-premium', name: 'Premium DJ + MC ($2,500)' },
{ id: 'band-small', name: '4-6 Piece Band ($5,000)' },
{ id: 'band-large', name: '8-12 Piece Band ($10,000)' },
{ id: 'both', name: 'Band + DJ Combo ($12,000)' }
];
}
return [
{ id: 'playlist', name: 'DIY Playlist ($0)' },
{ id: 'dj', name: 'Professional DJ ($1,500)' },
{ id: 'dj-premium', name: 'Premium DJ + Lighting ($2,500)' },
{ id: 'band-small', name: '4-6 Piece Band ($5,000)' }
];
},
defaultValue: () => isLuxury() ? 'band-small' : 'dj'
}, '1fr');
row.addDropdown('ceremonyMusic', {
label: 'Ceremony Music',
options: [
{ id: 'none', name: 'Venue Sound System ($0)' },
{ id: 'soloist', name: 'Solo Musician ($500)' },
{ id: 'duo', name: 'Duo/Trio ($1,000)' },
{ id: 'quartet', name: 'String Quartet ($1,800)' }
],
defaultValue: 'soloist'
}, '1fr');
});
entertainmentSection.addRow(row => {
row.addCheckboxList('entertainmentExtras', {
label: 'Entertainment Add-ons',
options: [
{ id: 'photobooth', name: 'Photo Booth (+$900)' },
{ id: 'uplighting', name: 'Uplighting/Mood Lighting (+$800)' },
{ id: 'sparklers', name: 'Sparkler Exit (+$200)' },
{ id: 'fireworks', name: 'Fireworks Display (+$2,500)' }
],
orientation: 'vertical',
isVisible: () => !isBudget()
});
});
// ==================== FLOWERS & DECOR ====================
const decorSection = form.addSubform('decor', { title: '💐 Flowers & Decor' });
decorSection.addRow(row => {
row.addDropdown('floralPackage', {
label: 'Floral Package',
options: () => {
if (isLuxury()) {
return [
{ id: 'premium', name: 'Premium - lush arrangements ($6,000)' },
{ id: 'luxury', name: 'Luxury - floral installations ($10,000)' },
{ id: 'extravagant', name: 'Extravagant - full floral design ($15,000+)' }
];
}
if (isBudget()) {
return [
{ id: 'diy', name: 'DIY / Grocery Store ($300)' },
{ id: 'minimal', name: 'Minimal - bouquet + boutonnieres ($600)' },
{ id: 'simple', name: 'Simple - essentials only ($1,200)' }
];
}
return [
{ id: 'minimal', name: 'Minimal - essentials only ($800)' },
{ id: 'moderate', name: 'Moderate - ceremony + reception ($2,000)' },
{ id: 'full', name: 'Full - complete floral design ($4,000)' },
{ id: 'premium', name: 'Premium - lush arrangements ($6,000)' }
];
},
defaultValue: () => {
if (isLuxury()) return 'premium';
if (isBudget()) return 'minimal';
return 'moderate';
}
}, '1fr');
row.addDropdown('decorPackage', {
label: 'Decor & Rentals',
options: [
{ id: 'none', name: 'Venue provides all ($0)' },
{ id: 'minimal', name: 'Minimal rentals ($500)' },
{ id: 'moderate', name: 'Chair covers + linens ($1,500)' },
{ id: 'full', name: 'Full styling + rentals ($3,500)' },
{ id: 'designer', name: 'Event designer ($6,000+)' }
],
defaultValue: 'moderate'
}, '1fr');
});
// ==================== ATTIRE & BEAUTY ====================
const attireSection = form.addSubform('attire', { title: '👗 Attire & Beauty' });
attireSection.addRow(row => {
row.addDropdown('dressbudget', {
label: 'Wedding Dress',
options: () => {
if (isLuxury()) {
return [
{ id: 'designer', name: 'Designer ($5,000)' },
{ id: 'couture', name: 'Couture ($10,000)' },
{ id: 'custom', name: 'Custom/Haute Couture ($15,000+)' }
];
}
return [
{ id: 'budget', name: 'Budget-friendly ($500)' },
{ id: 'moderate', name: 'Moderate ($1,500)' },
{ id: 'upscale', name: 'Upscale ($3,000)' },
{ id: 'designer', name: 'Designer ($5,000)' }
];
},
defaultValue: () => isLuxury() ? 'designer' : 'moderate'
}, '1fr');
row.addDropdown('suitBudget', {
label: 'Suit/Tuxedo',
options: [
{ id: 'rental', name: 'Rental ($200)' },
{ id: 'purchase', name: 'Purchase Off-Rack ($500)' },
{ id: 'made-to-measure', name: 'Made-to-Measure ($1,200)' },
{ id: 'custom', name: 'Custom Tailored ($2,500)' }
],
defaultValue: 'purchase'
}, '1fr');
});
attireSection.addRow(row => {
row.addCheckboxList('beautyServices', {
label: 'Beauty & Prep',
options: [
{ id: 'hair', name: 'Hair Styling (+$200)' },
{ id: 'makeup', name: 'Professional Makeup (+$200)' },
{ id: 'trial', name: 'Hair & Makeup Trial (+$150)' },
{ id: 'bridesmaids', name: 'Bridesmaid Hair/Makeup (+$150/person)' }
],
defaultValue: ['hair', 'makeup'],
orientation: 'vertical'
});
});
// ==================== ADDITIONAL SERVICES ====================
const additionalSection = form.addSubform('additional', { title: '✨ Additional Services' });
additionalSection.addRow(row => {
row.addCheckboxList('planningServices', {
label: 'Planning & Coordination',
options: [
{ id: 'day-of', name: 'Day-of Coordinator (+$1,500)' },
{ id: 'partial', name: 'Partial Planner (+$3,000)' },
{ id: 'full', name: 'Full Wedding Planner (+$5,000)' }
],
orientation: 'vertical'
}, '1fr');
row.addCheckboxList('ceremonyServices', {
label: 'Ceremony',
options: [
{ id: 'officiant', name: 'Officiant (+$400)' },
{ id: 'programs', name: 'Ceremony Programs (+$200)' },
{ id: 'unity', name: 'Unity Ceremony Items (+$100)' }
],
defaultValue: ['officiant'],
orientation: 'vertical'
}, '1fr');
});
additionalSection.addRow(row => {
row.addCheckboxList('paperGoods', {
label: 'Paper Goods & Favors',
options: [
{ id: 'invites-basic', name: 'Invitations - Basic (+$300)' },
{ id: 'invites-premium', name: 'Invitations - Premium (+$800)' },
{ id: 'signage', name: 'Signage & Seating Chart (+$300)' },
{ id: 'favors', name: 'Guest Favors (+$4/person)' }
],
defaultValue: ['invites-basic'],
orientation: 'vertical'
}, '1fr');
row.addCheckboxList('logistics', {
label: 'Logistics',
options: [
{ id: 'limo', name: 'Couple Transportation (+$600)' },
{ id: 'shuttle', name: 'Guest Shuttle (+$1,200)' },
{ id: 'valet', name: 'Valet Parking (+$800)' }
],
orientation: 'vertical'
}, '1fr');
});
// ==================== PRICE CALCULATIONS ====================
form.addSpacer({ height: 20, showLine: true, lineStyle: 'solid' });
const priceSection = form.addSubform('price', { title: '💰 Budget Breakdown', isCollapsible: false });
const getVenueCost = () => {
const venue = venueSection.radioButton('venueType')?.value() || 'hotel';
const venuePrices: Record<string, number> = {
// Destination
'resort': 8000, 'villa': 12000, 'beach-venue': 6000, 'castle': 15000,
// Luxury
'estate': 8000, 'country-club': 6000, 'hotel-luxury': 10000, 'vineyard': 7000, 'museum': 12000,
// Budget
'backyard': 500, 'restaurant': 2500, 'park': 800, 'community': 1500,
// Average
'barn': 3500, 'hotel': 5000, 'garden': 4000, 'restaurant-upscale': 4500, 'country-club-std': 6000
};
let cost = (venuePrices[venue] || 5000) * getSeasonMultiplier() * getDayMultiplier();
if (venueSection.checkbox('ceremonyDifferent')?.value()) {
const ceremonyPrices: Record<string, number> = { 'church': 500, 'outdoor': 1000, 'chapel': 1500 };
cost += ceremonyPrices[venueSection.dropdown('ceremonyCost')?.value() || 'church'] || 500;
}
return Math.round(cost);
};
const getCateringCost = () => {
const guests = getGuestCount();
const meal = cateringSection.dropdown('mealStyle')?.value() || 'plated';
const bar = cateringSection.dropdown('barService')?.value() || 'limited';
const extras = cateringSection.checkboxList('cateringExtras')?.value() || [];
const mealPrices: Record<string, number> = {
'buffet': 45, 'family': 55, 'plated': 65, 'stations': 75, 'plated-premium': 95,
'tasting': 150, 'stations-luxury': 130
};
const barPrices: Record<string, number> = {
'none': 0, 'beer-wine': 25, 'limited': 40, 'full': 55, 'premium': 80
};
let cost = guests * ((mealPrices[meal] || 65) + (barPrices[bar] || 40));
if (extras.includes('cocktail')) cost += guests * 18;
if (extras.includes('late-night')) cost += guests * 12;
if (extras.includes('cake')) cost += 800;
if (extras.includes('dessert-bar')) cost += 600;
return Math.round(cost);
};
const getPhotoCost = () => {
const photo = photoSection.radioButton('photoPackage')?.value() || 'standard';
const video = photoSection.radioButton('videoPackage')?.value() || 'none';
const photoPrices: Record<string, number> = {
'none': 0, 'basic': 1800, 'standard': 3500, 'premium': 5500, 'luxury': 8000
};
const videoPrices: Record<string, number> = {
'none': 0, 'highlight': 1800, 'documentary': 3500, 'cinematic': 5500, 'full': 7500
};
return (photoPrices[photo] || 0) + (videoPrices[video] || 0);
};
const getEntertainmentCost = () => {
const reception = entertainmentSection.dropdown('receptionMusic')?.value() || 'dj';
const ceremony = entertainmentSection.dropdown('ceremonyMusic')?.value() || 'soloist';
const extras = entertainmentSection.checkboxList('entertainmentExtras')?.value() || [];
const receptionPrices: Record<string, number> = {
'playlist': 0, 'dj': 1500, 'dj-premium': 2500, 'band-small': 5000, 'band-large': 10000, 'both': 12000
};
const ceremonyPrices: Record<string, number> = {
'none': 0, 'soloist': 500, 'duo': 1000, 'quartet': 1800
};
let cost = (receptionPrices[reception] || 0) + (ceremonyPrices[ceremony] || 0);
if (extras.includes('photobooth')) cost += 900;
if (extras.includes('uplighting')) cost += 800;
if (extras.includes('sparklers')) cost += 200;
if (extras.includes('fireworks')) cost += 2500;
return cost;
};
const getDecorCost = () => {
const floral = decorSection.dropdown('floralPackage')?.value() || 'moderate';
const decor = decorSection.dropdown('decorPackage')?.value() || 'moderate';
const floralPrices: Record<string, number> = {
'diy': 300, 'minimal': 800, 'simple': 1200, 'moderate': 2000, 'full': 4000,
'premium': 6000, 'luxury': 10000, 'extravagant': 15000
};
const decorPrices: Record<string, number> = {
'none': 0, 'minimal': 500, 'moderate': 1500, 'full': 3500, 'designer': 6000
};
return (floralPrices[floral] || 0) + (decorPrices[decor] || 0);
};
const getAttireCost = () => {
const dress = attireSection.dropdown('dressbudget')?.value() || 'moderate';
const suit = attireSection.dropdown('suitBudget')?.value() || 'purchase';
const beauty = attireSection.checkboxList('beautyServices')?.value() || [];
const dressPrices: Record<string, number> = {
'budget': 500, 'moderate': 1500, 'upscale': 3000, 'designer': 5000, 'couture': 10000, 'custom': 15000
};
const suitPrices: Record<string, number> = {
'rental': 200, 'purchase': 500, 'made-to-measure': 1200, 'custom': 2500
};
let cost = (dressPrices[dress] || 0) + (suitPrices[suit] || 0);
if (beauty.includes('hair')) cost += 200;
if (beauty.includes('makeup')) cost += 200;
if (beauty.includes('trial')) cost += 150;
if (beauty.includes('bridesmaids')) cost += 150 * 4; // assume 4 bridesmaids
return cost;
};
const getAdditionalCost = () => {
const planning = additionalSection.checkboxList('planningServices')?.value() || [];
const ceremony = additionalSection.checkboxList('ceremonyServices')?.value() || [];
const paper = additionalSection.checkboxList('paperGoods')?.value() || [];
const logistics = additionalSection.checkboxList('logistics')?.value() || [];
const guests = getGuestCount();
let cost = 0;
if (planning.includes('day-of')) cost += 1500;
if (planning.includes('partial')) cost += 3000;
if (planning.includes('full')) cost += 5000;
if (ceremony.includes('officiant')) cost += 400;
if (ceremony.includes('programs')) cost += 200;
if (ceremony.includes('unity')) cost += 100;
if (paper.includes('invites-basic')) cost += 300;
if (paper.includes('invites-premium')) cost += 800;
if (paper.includes('signage')) cost += 300;
if (paper.includes('favors')) cost += guests * 4;
if (logistics.includes('limo')) cost += 600;
if (logistics.includes('shuttle')) cost += 1200;
if (logistics.includes('valet')) cost += 800;
return cost;
};
const getDestinationCost = () => {
if (!isDestination()) return 0;
const extras = destinationSection.checkboxList('destinationExtras')?.value() || [];
let cost = 0;
if (extras.includes('welcome')) cost += 3000;
if (extras.includes('brunch')) cost += 1500;
if (extras.includes('transport')) cost += 2000;
if (extras.includes('activities')) cost += 1500;
return cost;
};
priceSection.addRow(row => {
row.addPriceDisplay('venueTotal', {
label: 'Venue',
computedValue: getVenueCost,
variant: 'default'
}, '1fr');
row.addPriceDisplay('cateringTotal', {
label: 'Food & Drinks',
computedValue: getCateringCost,
variant: 'default'
}, '1fr');
});
priceSection.addRow(row => {
row.addPriceDisplay('photoTotal', {
label: 'Photo & Video',
computedValue: getPhotoCost,
variant: 'default'
}, '1fr');
row.addPriceDisplay('entertainmentTotal', {
label: 'Entertainment',
computedValue: getEntertainmentCost,
variant: 'default'
}, '1fr');
});
priceSection.addRow(row => {
row.addPriceDisplay('decorTotal', {
label: 'Flowers & Decor',
computedValue: getDecorCost,
variant: 'default'
}, '1fr');
row.addPriceDisplay('attireTotal', {
label: 'Attire & Beauty',
computedValue: getAttireCost,
variant: 'default'
}, '1fr');
});
priceSection.addRow(row => {
row.addPriceDisplay('additionalTotal', {
label: 'Additional Services',
computedValue: getAdditionalCost,
variant: 'default'
}, '1fr');
row.addPriceDisplay('destinationTotal', {
label: 'Destination Extras',
computedValue: getDestinationCost,
variant: 'default',
isVisible: isDestination
}, '1fr');
});
// ==================== FINAL SUMMARY ====================
const finalSection = form.addSubform('final', {
title: '🧾 Total Budget',
isCollapsible: false
});
const getTotal = () => {
return getVenueCost() + getCateringCost() + getPhotoCost() + getEntertainmentCost() +
getDecorCost() + getAttireCost() + getAdditionalCost() + getDestinationCost();
};
finalSection.addRow(row => {
row.addPriceDisplay('totalBudget', {
label: 'Estimated Total',
computedValue: getTotal,
variant: 'large'
});
});
finalSection.addRow(row => {
row.addPriceDisplay('perGuest', {
label: 'Cost Per Guest',
computedValue: () => Math.round(getTotal() / getGuestCount()),
variant: 'default'
}, '1fr');
row.addTextPanel('contingency', {
computedValue: () => {
const buffer = Math.round(getTotal() * 0.1);
return `Recommended 10% contingency: $${buffer.toLocaleString()}`;
},
customStyles: { 'font-size': '0.9rem', 'color': '#059669', 'font-weight': '500' }
}, '1fr');
});
finalSection.addRow(row => {
row.addTextPanel('summary', {
computedValue: () => {
const guests = getGuestCount();
const style = basicSection.dropdown('weddingStyle')?.value();
const styleLabels: Record<string, string> = {
'budget': 'Budget-friendly', 'average': 'Classic', 'upscale': 'Upscale', 'luxury': 'Luxury'
};
const dest = isDestination() ? 'Destination ' : '';
return `${dest}${styleLabels[style || 'average']} wedding • ${guests} guests`;
},
customStyles: { 'font-size': '0.95rem', 'font-weight': '500', 'text-align': 'center' }
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Estimates based on national averages. Actual costs vary by location and vendor.',
customStyles: { 'font-size': '0.8rem', 'color': '#64748b', 'text-align': 'center', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Get Personalized Quote'
});
}