export function barServiceCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Bar Service Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Event Details Section
const eventSection = form.addSubform('event', { title: '🎉 Event Details' });
eventSection.addRow(row => {
row.addInteger('guestCount', {
label: 'Number of Guests',
min: 10,
max: 1000,
defaultValue: 100,
isRequired: true
}, '1fr');
row.addDecimal('serviceDuration', {
label: 'Service Duration (Hours)',
min: 1,
max: 12,
defaultValue: 5,
isRequired: true,
tooltip: 'Total hours bar will be open'
}, '1fr');
});
eventSection.addRow(row => {
row.addDropdown('eventType', {
label: 'Event Type',
options: [
{ id: 'wedding', name: 'Wedding Reception' },
{ id: 'corporate', name: 'Corporate Event' },
{ id: 'birthday', name: 'Birthday / Anniversary' },
{ id: 'cocktail', name: 'Cocktail Party' },
{ id: 'holiday', name: 'Holiday Party' },
{ id: 'other', name: 'Other Private Event' }
],
defaultValue: 'wedding'
}, '1fr');
row.addDropdown('drinkingLevel', {
label: 'Expected Drinking Level',
options: [
{ id: 'light', name: 'Light (1-2 drinks/person)' },
{ id: 'moderate', name: 'Moderate (3-4 drinks/person)' },
{ id: 'heavy', name: 'Heavy (5+ drinks/person)' }
],
defaultValue: 'moderate',
tooltip: 'Affects beverage quantities'
}, '1fr');
});
// Bar Type Section
const barTypeSection = form.addSubform('barType', { title: '🍸 Bar Service Type' });
barTypeSection.addRow(row => {
row.addRadioButton('serviceType', {
label: 'Service Type',
options: [
{ id: 'open', name: 'Open Bar - Host pays all' },
{ id: 'limited', name: 'Limited Bar - Beer & wine only' },
{ id: 'consumption', name: 'Consumption Bar - Pay per drink' },
{ id: 'cash', name: 'Cash Bar - Guests pay' }
],
defaultValue: 'open',
isRequired: true
});
});
barTypeSection.addRow(row => {
row.addDropdown('liquorQuality', {
label: 'Liquor Quality',
options: [
{ id: 'well', name: 'Well / House Brands' },
{ id: 'call', name: 'Call Brands' },
{ id: 'premium', name: 'Premium Brands' },
{ id: 'top-shelf', name: 'Top Shelf' }
],
defaultValue: 'call',
isVisible: () => barTypeSection.radioButton('serviceType')?.value() === 'open'
}, '1fr');
row.addDropdown('wineSelection', {
label: 'Wine Selection',
options: [
{ id: 'house', name: 'House Wine' },
{ id: 'mid-range', name: 'Mid-Range Selection' },
{ id: 'premium', name: 'Premium Selection' }
],
defaultValue: 'mid-range'
}, '1fr');
});
// Beverage Options Section
const beverageSection = form.addSubform('beverages', { title: '🍷 Beverage Options' });
beverageSection.addRow(row => {
row.addCheckbox('beer', {
label: 'Beer Selection',
defaultValue: true
}, '1fr');
row.addCheckbox('wine', {
label: 'Wine Selection',
defaultValue: true
}, '1fr');
});
beverageSection.addRow(row => {
row.addCheckbox('liquor', {
label: 'Full Liquor Bar',
defaultValue: true,
isVisible: () => barTypeSection.radioButton('serviceType')?.value() !== 'limited'
}, '1fr');
row.addCheckbox('signatureCocktails', {
label: 'Signature Cocktails',
defaultValue: false,
tooltip: '2-3 custom cocktails for your event'
}, '1fr');
});
beverageSection.addRow(row => {
row.addCheckbox('champagneToast', {
label: 'Champagne Toast',
defaultValue: false,
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '1fr');
row.addCheckbox('nonAlcoholic', {
label: 'Mocktail Station',
defaultValue: false,
tooltip: 'Premium non-alcoholic options'
}, '1fr');
});
// Staffing Section
const staffingSection = form.addSubform('staffing', { title: '👨🍳 Staffing & Equipment' });
staffingSection.addRow(row => {
row.addInteger('bartenders', {
label: 'Number of Bartenders',
min: 1,
max: 20,
defaultValue: 2,
tooltip: 'Recommended: 1 per 50-75 guests'
}, '1fr');
row.addCheckbox('barback', {
label: 'Barback Support',
defaultValue: true,
tooltip: 'Assistants for restocking'
}, '1fr');
});
staffingSection.addRow(row => {
row.addCheckbox('barRental', {
label: 'Portable Bar Rental',
defaultValue: true
}, '1fr');
row.addCheckbox('glassware', {
label: 'Glassware Rental',
defaultValue: true
}, '1fr');
});
staffingSection.addRow(row => {
row.addCheckbox('iceService', {
label: 'Ice & Coolers',
defaultValue: true
}, '1fr');
row.addCheckbox('garnishes', {
label: 'Premium Garnishes',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Breakdown
const breakdownSection = form.addSubform('breakdown', { title: '📊 Price Breakdown', isCollapsible: true });
breakdownSection.addRow(row => {
row.addPriceDisplay('beverageCost', {
label: 'Beverage Cost',
computedValue: () => {
const guestCount = eventSection.integer('guestCount')?.value() || 100;
const duration = eventSection.decimal('serviceDuration')?.value() || 5;
const serviceType = barTypeSection.radioButton('serviceType')?.value() || 'open';
const liquorQuality = barTypeSection.dropdown('liquorQuality')?.value() || 'call';
const drinkingLevel = eventSection.dropdown('drinkingLevel')?.value() || 'moderate';
if (serviceType === 'cash') return 0;
// Base per-person rates
const baseRates: Record<string, number> = {
'open': 35, 'limited': 20, 'consumption': 25
};
let rate = baseRates[serviceType] || 35;
// Quality multipliers
const qualityMultipliers: Record<string, number> = {
'well': 0.8, 'call': 1, 'premium': 1.3, 'top-shelf': 1.6
};
if (serviceType === 'open') {
rate *= qualityMultipliers[liquorQuality] || 1;
}
// Drinking level multipliers
const drinkingMultipliers: Record<string, number> = {
'light': 0.7, 'moderate': 1, 'heavy': 1.4
};
rate *= drinkingMultipliers[drinkingLevel] || 1;
// Duration adjustment (over 4 hours)
if (duration > 4) {
rate *= 1 + ((duration - 4) * 0.1);
}
return Math.round(rate * guestCount * 100) / 100;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('staffingCost', {
label: 'Staffing Cost',
computedValue: () => {
const duration = eventSection.decimal('serviceDuration')?.value() || 5;
const bartenders = staffingSection.integer('bartenders')?.value() || 2;
const hasBarback = staffingSection.checkbox('barback')?.value();
const bartenderRate = 45; // per hour
const barbackRate = 25; // per hour
let cost = bartenders * bartenderRate * duration;
if (hasBarback) {
cost += Math.ceil(bartenders / 2) * barbackRate * duration;
}
return Math.round(cost * 100) / 100;
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('equipmentCost', {
label: 'Equipment Rental',
computedValue: () => {
let cost = 0;
if (staffingSection.checkbox('barRental')?.value()) cost += 250;
if (staffingSection.checkbox('glassware')?.value()) {
const guests = eventSection.integer('guestCount')?.value() || 100;
cost += Math.ceil(guests / 25) * 50;
}
if (staffingSection.checkbox('iceService')?.value()) cost += 100;
if (staffingSection.checkbox('garnishes')?.value()) cost += 75;
return cost;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('upgradesCost', {
label: 'Upgrades & Add-ons',
computedValue: () => {
const guestCount = eventSection.integer('guestCount')?.value() || 100;
let cost = 0;
if (beverageSection.checkbox('signatureCocktails')?.value()) cost += 250;
if (beverageSection.checkbox('champagneToast')?.value()) cost += guestCount * 8;
if (beverageSection.checkbox('nonAlcoholic')?.value()) cost += 150;
return cost;
},
variant: 'default'
}, '1fr');
});
// Quote Section
const quoteSection = form.addSubform('quote', { title: '💰 Your Quote', isCollapsible: false });
quoteSection.addRow(row => {
row.addPriceDisplay('perPersonCost', {
label: 'Cost per Guest',
computedValue: () => {
const guestCount = eventSection.integer('guestCount')?.value() || 100;
const duration = eventSection.decimal('serviceDuration')?.value() || 5;
const serviceType = barTypeSection.radioButton('serviceType')?.value() || 'open';
const liquorQuality = barTypeSection.dropdown('liquorQuality')?.value() || 'call';
const drinkingLevel = eventSection.dropdown('drinkingLevel')?.value() || 'moderate';
const bartenders = staffingSection.integer('bartenders')?.value() || 2;
const hasBarback = staffingSection.checkbox('barback')?.value();
// Beverage cost
let beverageCost = 0;
if (serviceType !== 'cash') {
const baseRates: Record<string, number> = { 'open': 35, 'limited': 20, 'consumption': 25 };
let rate = baseRates[serviceType] || 35;
const qualityMultipliers: Record<string, number> = { 'well': 0.8, 'call': 1, 'premium': 1.3, 'top-shelf': 1.6 };
if (serviceType === 'open') rate *= qualityMultipliers[liquorQuality] || 1;
const drinkingMultipliers: Record<string, number> = { 'light': 0.7, 'moderate': 1, 'heavy': 1.4 };
rate *= drinkingMultipliers[drinkingLevel] || 1;
if (duration > 4) rate *= 1 + ((duration - 4) * 0.1);
beverageCost = rate * guestCount;
}
// Staffing cost
let staffingCost = bartenders * 45 * duration;
if (hasBarback) staffingCost += Math.ceil(bartenders / 2) * 25 * duration;
// Equipment
let equipmentCost = 0;
if (staffingSection.checkbox('barRental')?.value()) equipmentCost += 250;
if (staffingSection.checkbox('glassware')?.value()) equipmentCost += Math.ceil(guestCount / 25) * 50;
if (staffingSection.checkbox('iceService')?.value()) equipmentCost += 100;
if (staffingSection.checkbox('garnishes')?.value()) equipmentCost += 75;
// Upgrades
let upgrades = 0;
if (beverageSection.checkbox('signatureCocktails')?.value()) upgrades += 250;
if (beverageSection.checkbox('champagneToast')?.value()) upgrades += guestCount * 8;
if (beverageSection.checkbox('nonAlcoholic')?.value()) upgrades += 150;
const total = beverageCost + staffingCost + equipmentCost + upgrades;
return Math.round((total / guestCount) * 100) / 100;
},
variant: 'success'
}, '1fr');
row.addPriceDisplay('totalQuote', {
label: 'Total Bar Service',
computedValue: () => {
const guestCount = eventSection.integer('guestCount')?.value() || 100;
const duration = eventSection.decimal('serviceDuration')?.value() || 5;
const serviceType = barTypeSection.radioButton('serviceType')?.value() || 'open';
const liquorQuality = barTypeSection.dropdown('liquorQuality')?.value() || 'call';
const drinkingLevel = eventSection.dropdown('drinkingLevel')?.value() || 'moderate';
const bartenders = staffingSection.integer('bartenders')?.value() || 2;
const hasBarback = staffingSection.checkbox('barback')?.value();
// Beverage cost
let beverageCost = 0;
if (serviceType !== 'cash') {
const baseRates: Record<string, number> = { 'open': 35, 'limited': 20, 'consumption': 25 };
let rate = baseRates[serviceType] || 35;
const qualityMultipliers: Record<string, number> = { 'well': 0.8, 'call': 1, 'premium': 1.3, 'top-shelf': 1.6 };
if (serviceType === 'open') rate *= qualityMultipliers[liquorQuality] || 1;
const drinkingMultipliers: Record<string, number> = { 'light': 0.7, 'moderate': 1, 'heavy': 1.4 };
rate *= drinkingMultipliers[drinkingLevel] || 1;
if (duration > 4) rate *= 1 + ((duration - 4) * 0.1);
beverageCost = rate * guestCount;
}
// Staffing cost
let staffingCost = bartenders * 45 * duration;
if (hasBarback) staffingCost += Math.ceil(bartenders / 2) * 25 * duration;
// Equipment
let equipmentCost = 0;
if (staffingSection.checkbox('barRental')?.value()) equipmentCost += 250;
if (staffingSection.checkbox('glassware')?.value()) equipmentCost += Math.ceil(guestCount / 25) * 50;
if (staffingSection.checkbox('iceService')?.value()) equipmentCost += 100;
if (staffingSection.checkbox('garnishes')?.value()) equipmentCost += 75;
// Upgrades
let upgrades = 0;
if (beverageSection.checkbox('signatureCocktails')?.value()) upgrades += 250;
if (beverageSection.checkbox('champagneToast')?.value()) upgrades += guestCount * 8;
if (beverageSection.checkbox('nonAlcoholic')?.value()) upgrades += 150;
return Math.round((beverageCost + staffingCost + equipmentCost + upgrades) * 100) / 100;
},
variant: 'large'
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '🍹 Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addTextPanel('summaryText', {
computedValue: () => {
const guestCount = eventSection.integer('guestCount')?.value() || 100;
const duration = eventSection.decimal('serviceDuration')?.value() || 5;
const serviceType = barTypeSection.radioButton('serviceType')?.value() || 'open';
const typeLabels: Record<string, string> = {
'open': 'open bar', 'limited': 'beer & wine bar', 'consumption': 'consumption bar', 'cash': 'cash bar'
};
return `${typeLabels[serviceType]} for ${guestCount} guests over ${duration} hours`;
},
customStyles: { 'font-size': '0.95rem', 'font-weight': '500', 'text-align': 'center', 'color': '#1e293b' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices are estimates. Final cost depends on venue requirements and specific beverage selections.',
customStyles: { 'font-size': '0.8rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Book Bar Service'
});
}