export function cateringCostCalculator(form: FormTs) {
const mealStyles: Record<string, number> = {
'boxed': 15,
'buffet': 35,
'family': 40,
'plated': 55,
'stations': 65,
'premium': 85
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Catering Quote Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Event Details Section
const eventSection = form.addSubform('eventDetails', { title: '๐
Event Details' });
eventSection.addRow(row => {
row.addInteger('guestCount', {
label: 'Number of Guests',
min: 10,
max: 1000,
defaultValue: 50,
isRequired: true
}, '1fr');
row.addDropdown('eventType', {
label: 'Event Type',
options: [
{ id: 'corporate', name: 'Corporate Event' },
{ id: 'wedding', name: 'Wedding Reception' },
{ id: 'social', name: 'Social Gathering' },
{ id: 'holiday', name: 'Holiday Party' },
{ id: 'fundraiser', name: 'Fundraiser/Gala' },
{ id: 'birthday', name: 'Birthday/Anniversary' }
],
defaultValue: 'corporate',
isRequired: true
}, '1fr');
});
eventSection.addRow(row => {
row.addDropdown('mealTime', {
label: 'Meal Time',
options: [
{ id: 'breakfast', name: 'Breakfast (-20%)' },
{ id: 'brunch', name: 'Brunch' },
{ id: 'lunch', name: 'Lunch (-10%)' },
{ id: 'dinner', name: 'Dinner' },
{ id: 'reception', name: 'Reception/Appetizers Only (-30%)' }
],
defaultValue: 'dinner',
isRequired: true
}, '1fr');
row.addDropdown('serviceDay', {
label: 'Day of Event',
options: [
{ id: 'weekday', name: 'Weekday' },
{ id: 'friday', name: 'Friday (+10%)' },
{ id: 'saturday', name: 'Saturday (+15%)' },
{ id: 'sunday', name: 'Sunday (+10%)' },
{ id: 'holiday', name: 'Holiday (+25%)' }
],
defaultValue: 'saturday'
}, '1fr');
});
// Menu Selection Section
const menuSection = form.addSubform('menuSelection', { title: '๐ฝ๏ธ Menu & Service Style' });
menuSection.addRow(row => {
row.addRadioButton('mealStyle', {
label: 'Service Style',
options: [
{ id: 'boxed', name: 'Boxed Meals ($15/person) - Individual packaged meals' },
{ id: 'buffet', name: 'Buffet ($35/person) - Self-service stations' },
{ id: 'family', name: 'Family Style ($40/person) - Shared platters at tables' },
{ id: 'plated', name: 'Plated ($55/person) - Individual courses served' },
{ id: 'stations', name: 'Action Stations ($65/person) - Chef-attended stations' },
{ id: 'premium', name: 'Premium Plated ($85/person) - Multi-course fine dining' }
],
defaultValue: 'buffet',
orientation: 'vertical',
isRequired: true
});
});
menuSection.addRow(row => {
row.addDropdown('cuisineStyle', {
label: 'Cuisine Style',
options: [
{ id: 'american', name: 'American Classic' },
{ id: 'italian', name: 'Italian' },
{ id: 'mexican', name: 'Mexican/Southwest' },
{ id: 'asian', name: 'Asian Fusion' },
{ id: 'mediterranean', name: 'Mediterranean' },
{ id: 'bbq', name: 'BBQ' },
{ id: 'mixed', name: 'Mixed/Custom Menu' }
],
defaultValue: 'american'
}, '1fr');
row.addDropdown('dietaryNeeds', {
label: 'Special Dietary Needs',
options: [
{ id: 'none', name: 'Standard Menu' },
{ id: 'some', name: 'Some Dietary Options (+5%)' },
{ id: 'extensive', name: 'Extensive Options (+10%)' },
{ id: 'custom', name: 'Fully Custom Menu (+15%)' }
],
defaultValue: 'none'
}, '1fr');
});
// Beverages Section
const beverageSection = form.addSubform('beverages', { title: '๐ฅ Beverages' });
beverageSection.addRow(row => {
row.addDropdown('nonAlcoholic', {
label: 'Non-Alcoholic',
options: [
{ id: 'none', name: 'None' },
{ id: 'basic', name: 'Basic (Water, Soda) - $3/person' },
{ id: 'standard', name: 'Standard (+ Coffee, Tea) - $5/person' },
{ id: 'premium', name: 'Premium (+ Juices, Specialty) - $10/person' }
],
defaultValue: 'standard'
}, '1fr');
row.addDropdown('alcohol', {
label: 'Alcoholic Beverages',
options: [
{ id: 'none', name: 'None' },
{ id: 'beer-wine', name: 'Beer & Wine - $15/person' },
{ id: 'limited', name: 'Limited Bar - $25/person' },
{ id: 'full', name: 'Full Open Bar - $40/person' },
{ id: 'premium', name: 'Premium Open Bar - $60/person' }
],
defaultValue: 'none'
}, '1fr');
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('appetizers', {
label: 'Passed Appetizers (+$8/person)',
defaultValue: false
}, '1fr');
row.addCheckbox('dessert', {
label: 'Dessert Station (+$10/person)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('lateNight', {
label: 'Late Night Snacks (+$8/person)',
defaultValue: false
}, '1fr');
row.addCheckbox('cake', {
label: 'Custom Cake/Dessert (+$5/person)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('servers', {
label: 'Wait Staff (+$150/server)',
defaultValue: true
}, '1fr');
row.addCheckbox('bartender', {
label: 'Bartender (+$200/bartender)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addInteger('serverCount', {
label: 'Number of Servers',
min: 1,
max: 20,
defaultValue: 2,
isVisible: () => addonsSection.checkbox('servers')?.value() === true
}, '1fr');
row.addInteger('bartenderCount', {
label: 'Number of Bartenders',
min: 1,
max: 5,
defaultValue: 1,
isVisible: () => addonsSection.checkbox('bartender')?.value() === true
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('rentals', {
label: 'Equipment Rentals (Tables, Linens, etc.)',
defaultValue: false
}, '1fr');
row.addDecimal('rentalBudget', {
label: 'Rental Budget ($)',
min: 0,
max: 10000,
defaultValue: 500,
decimalPlaces: 0,
isVisible: () => addonsSection.checkbox('rentals')?.value() === true
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Results Section
const resultsSection = form.addSubform('results', { title: '๐ฐ Quote Summary', isCollapsible: false });
const getMealTimeMultiplier = () => {
const mealTime = eventSection.dropdown('mealTime')?.value() || 'dinner';
const multipliers: Record<string, number> = {
'breakfast': 0.8,
'brunch': 1.0,
'lunch': 0.9,
'dinner': 1.0,
'reception': 0.7
};
return multipliers[mealTime] || 1;
};
const getDayMultiplier = () => {
const day = eventSection.dropdown('serviceDay')?.value() || 'saturday';
const multipliers: Record<string, number> = {
'weekday': 1.0,
'friday': 1.1,
'saturday': 1.15,
'sunday': 1.1,
'holiday': 1.25
};
return multipliers[day] || 1;
};
const getDietaryMultiplier = () => {
const dietary = menuSection.dropdown('dietaryNeeds')?.value() || 'none';
const multipliers: Record<string, number> = {
'none': 1.0,
'some': 1.05,
'extensive': 1.1,
'custom': 1.15
};
return multipliers[dietary] || 1;
};
resultsSection.addRow(row => {
row.addPriceDisplay('foodCost', {
label: 'Food Cost',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const style = menuSection.radioButton('mealStyle')?.value() || 'buffet';
const baseRate = mealStyles[style] || 35;
let cost = guests * baseRate * getMealTimeMultiplier() * getDietaryMultiplier();
if (addonsSection.checkbox('appetizers')?.value()) cost += guests * 8;
if (addonsSection.checkbox('dessert')?.value()) cost += guests * 10;
if (addonsSection.checkbox('lateNight')?.value()) cost += guests * 8;
if (addonsSection.checkbox('cake')?.value()) cost += guests * 5;
return Math.round(cost);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('beverageCost', {
label: 'Beverage Cost',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const nonAlcPrices: Record<string, number> = {
'none': 0, 'basic': 3, 'standard': 5, 'premium': 10
};
const alcPrices: Record<string, number> = {
'none': 0, 'beer-wine': 15, 'limited': 25, 'full': 40, 'premium': 60
};
const nonAlc = beverageSection.dropdown('nonAlcoholic')?.value() || 'standard';
const alc = beverageSection.dropdown('alcohol')?.value() || 'none';
return Math.round(guests * ((nonAlcPrices[nonAlc] || 0) + (alcPrices[alc] || 0)));
},
variant: 'default'
}, '1fr');
});
resultsSection.addRow(row => {
row.addPriceDisplay('staffCost', {
label: 'Staff & Service',
computedValue: () => {
let total = 0;
if (addonsSection.checkbox('servers')?.value()) {
total += (addonsSection.integer('serverCount')?.value() || 2) * 150;
}
if (addonsSection.checkbox('bartender')?.value()) {
total += (addonsSection.integer('bartenderCount')?.value() || 1) * 200;
}
return total;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('rentalCost', {
label: 'Rentals',
computedValue: () => {
if (addonsSection.checkbox('rentals')?.value()) {
return addonsSection.decimal('rentalBudget')?.value() || 500;
}
return 0;
},
variant: 'default'
}, '1fr');
});
resultsSection.addSpacer({ showLine: true, lineStyle: 'solid', lineColor: '#e2e8f0' });
resultsSection.addRow(row => {
row.addPriceDisplay('subtotal', {
label: 'Subtotal',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const style = menuSection.radioButton('mealStyle')?.value() || 'buffet';
const baseRate = mealStyles[style] || 35;
let food = guests * baseRate * getMealTimeMultiplier() * getDietaryMultiplier();
if (addonsSection.checkbox('appetizers')?.value()) food += guests * 8;
if (addonsSection.checkbox('dessert')?.value()) food += guests * 10;
if (addonsSection.checkbox('lateNight')?.value()) food += guests * 8;
if (addonsSection.checkbox('cake')?.value()) food += guests * 5;
const nonAlcPrices: Record<string, number> = {
'none': 0, 'basic': 3, 'standard': 5, 'premium': 10
};
const alcPrices: Record<string, number> = {
'none': 0, 'beer-wine': 15, 'limited': 25, 'full': 40, 'premium': 60
};
const nonAlc = beverageSection.dropdown('nonAlcoholic')?.value() || 'standard';
const alc = beverageSection.dropdown('alcohol')?.value() || 'none';
const beverages = guests * ((nonAlcPrices[nonAlc] || 0) + (alcPrices[alc] || 0));
let staff = 0;
if (addonsSection.checkbox('servers')?.value()) {
staff += (addonsSection.integer('serverCount')?.value() || 2) * 150;
}
if (addonsSection.checkbox('bartender')?.value()) {
staff += (addonsSection.integer('bartenderCount')?.value() || 1) * 200;
}
let rentals = 0;
if (addonsSection.checkbox('rentals')?.value()) {
rentals = addonsSection.decimal('rentalBudget')?.value() || 500;
}
return Math.round(food + beverages + staff + rentals);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('dayAdjustment', {
label: 'Day-of-Week Adjustment',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const style = menuSection.radioButton('mealStyle')?.value() || 'buffet';
const baseRate = mealStyles[style] || 35;
const food = guests * baseRate * getMealTimeMultiplier() * getDietaryMultiplier();
const dayMult = getDayMultiplier();
return Math.round(food * (dayMult - 1));
},
variant: 'default',
prefix: '+'
}, '1fr');
});
resultsSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Estimate',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const style = menuSection.radioButton('mealStyle')?.value() || 'buffet';
const baseRate = mealStyles[style] || 35;
let food = guests * baseRate * getMealTimeMultiplier() * getDietaryMultiplier() * getDayMultiplier();
if (addonsSection.checkbox('appetizers')?.value()) food += guests * 8;
if (addonsSection.checkbox('dessert')?.value()) food += guests * 10;
if (addonsSection.checkbox('lateNight')?.value()) food += guests * 8;
if (addonsSection.checkbox('cake')?.value()) food += guests * 5;
const nonAlcPrices: Record<string, number> = {
'none': 0, 'basic': 3, 'standard': 5, 'premium': 10
};
const alcPrices: Record<string, number> = {
'none': 0, 'beer-wine': 15, 'limited': 25, 'full': 40, 'premium': 60
};
const nonAlc = beverageSection.dropdown('nonAlcoholic')?.value() || 'standard';
const alc = beverageSection.dropdown('alcohol')?.value() || 'none';
const beverages = guests * ((nonAlcPrices[nonAlc] || 0) + (alcPrices[alc] || 0));
let staff = 0;
if (addonsSection.checkbox('servers')?.value()) {
staff += (addonsSection.integer('serverCount')?.value() || 2) * 150;
}
if (addonsSection.checkbox('bartender')?.value()) {
staff += (addonsSection.integer('bartenderCount')?.value() || 1) * 200;
}
let rentals = 0;
if (addonsSection.checkbox('rentals')?.value()) {
rentals = addonsSection.decimal('rentalBudget')?.value() || 500;
}
return Math.round(food + beverages + staff + rentals);
},
variant: 'large'
});
});
resultsSection.addRow(row => {
row.addPriceDisplay('perPerson', {
label: 'Per Person Cost',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const total = resultsSection.priceDisplay('totalPrice')?.value() || 0;
return Math.round(total / guests);
},
variant: 'default'
});
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Estimate',
computedValue: () => {
const guests = eventSection.integer('guestCount')?.value() || 50;
const style = menuSection.radioButton('mealStyle')?.value() || 'buffet';
const baseRate = mealStyles[style] || 35;
let food = guests * baseRate * getMealTimeMultiplier() * getDietaryMultiplier() * getDayMultiplier();
if (addonsSection.checkbox('appetizers')?.value()) food += guests * 8;
if (addonsSection.checkbox('dessert')?.value()) food += guests * 10;
if (addonsSection.checkbox('lateNight')?.value()) food += guests * 8;
if (addonsSection.checkbox('cake')?.value()) food += guests * 5;
const nonAlcPrices: Record<string, number> = {
'none': 0, 'basic': 3, 'standard': 5, 'premium': 10
};
const alcPrices: Record<string, number> = {
'none': 0, 'beer-wine': 15, 'limited': 25, 'full': 40, 'premium': 60
};
const nonAlc = beverageSection.dropdown('nonAlcoholic')?.value() || 'standard';
const alc = beverageSection.dropdown('alcohol')?.value() || 'none';
const beverages = guests * ((nonAlcPrices[nonAlc] || 0) + (alcPrices[alc] || 0));
let staff = 0;
if (addonsSection.checkbox('servers')?.value()) {
staff += (addonsSection.integer('serverCount')?.value() || 2) * 150;
}
if (addonsSection.checkbox('bartender')?.value()) {
staff += (addonsSection.integer('bartenderCount')?.value() || 1) * 200;
}
let rentals = 0;
if (addonsSection.checkbox('rentals')?.value()) {
rentals = addonsSection.decimal('rentalBudget')?.value() || 500;
}
return Math.round(food + beverages + staff + rentals);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Final pricing based on menu and venue details.',
customStyles: { 'font-size': '0.85rem', 'color': '#94a3b8', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Request Custom Proposal'
});
}