export function bakeryOrderCalculator(form: FormTs) {
// Cake base prices by size (number of servings)
const cakePrices: Record<string, number> = {
'small': 45, // 8-12 servings
'medium': 75, // 16-24 servings
'large': 120, // 30-40 servings
'xlarge': 180, // 50-60 servings
'tiered-2': 250, // 2-tier
'tiered-3': 400 // 3-tier
};
// Flavor premiums
const flavorPremiums: Record<string, number> = {
'vanilla': 0,
'chocolate': 0,
'red-velvet': 15,
'carrot': 15,
'lemon': 10,
'marble': 10,
'custom': 25
};
// Filling prices
const fillingPrices: Record<string, number> = {
'buttercream': 0,
'cream-cheese': 10,
'fruit': 15,
'mousse': 20,
'ganache': 15,
'custard': 15
};
// Frosting style multipliers
const frostingMultipliers: Record<string, number> = {
'buttercream-smooth': 1.0,
'buttercream-textured': 1.0,
'fondant': 1.4,
'naked': 0.85,
'semi-naked': 0.9
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Custom Bakery Order',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Cake Selection Section
const cakeSection = form.addSubform('cakeSelection', { title: '๐ Cake Selection' });
cakeSection.addRow(row => {
row.addRadioButton('cakeSize', {
label: 'Cake Size',
options: [
{ id: 'small', name: 'Small (8-12 servings) - $45' },
{ id: 'medium', name: 'Medium (16-24 servings) - $75' },
{ id: 'large', name: 'Large (30-40 servings) - $120' },
{ id: 'xlarge', name: 'Extra Large (50-60 servings) - $180' },
{ id: 'tiered-2', name: '2-Tier (40-50 servings) - $250' },
{ id: 'tiered-3', name: '3-Tier (70-100 servings) - $400' }
],
defaultValue: 'medium',
orientation: 'vertical',
isRequired: true
});
});
cakeSection.addRow(row => {
row.addDropdown('flavor', {
label: 'Cake Flavor',
options: [
{ id: 'vanilla', name: 'Classic Vanilla' },
{ id: 'chocolate', name: 'Rich Chocolate' },
{ id: 'red-velvet', name: 'Red Velvet (+$15)' },
{ id: 'carrot', name: 'Carrot Cake (+$15)' },
{ id: 'lemon', name: 'Lemon (+$10)' },
{ id: 'marble', name: 'Marble (+$10)' },
{ id: 'custom', name: 'Custom Flavor (+$25)' }
],
defaultValue: 'vanilla',
isRequired: true
}, '1fr');
row.addDropdown('filling', {
label: 'Filling',
options: [
{ id: 'buttercream', name: 'Buttercream' },
{ id: 'cream-cheese', name: 'Cream Cheese (+$10)' },
{ id: 'fruit', name: 'Fresh Fruit (+$15)' },
{ id: 'mousse', name: 'Chocolate Mousse (+$20)' },
{ id: 'ganache', name: 'Ganache (+$15)' },
{ id: 'custard', name: 'Vanilla Custard (+$15)' }
],
defaultValue: 'buttercream',
isRequired: true
}, '1fr');
});
// Decoration Section
const decorSection = form.addSubform('decoration', { title: '๐จ Decoration' });
decorSection.addRow(row => {
row.addRadioButton('frostingStyle', {
label: 'Frosting Style',
options: [
{ id: 'buttercream-smooth', name: 'Smooth Buttercream' },
{ id: 'buttercream-textured', name: 'Textured Buttercream' },
{ id: 'fondant', name: 'Fondant (+40%)' },
{ id: 'naked', name: 'Naked Cake (-15%)' },
{ id: 'semi-naked', name: 'Semi-Naked (-10%)' }
],
defaultValue: 'buttercream-smooth',
orientation: 'vertical',
isRequired: true
});
});
decorSection.addRow(row => {
row.addCheckbox('customColors', {
label: 'Custom Colors (+$15)',
defaultValue: false
}, '1fr');
row.addCheckbox('goldAccents', {
label: 'Gold/Silver Accents (+$25)',
defaultValue: false
}, '1fr');
});
decorSection.addRow(row => {
row.addCheckbox('freshFlowers', {
label: 'Fresh Flowers (+$35)',
defaultValue: false
}, '1fr');
row.addCheckbox('cakeTopper', {
label: 'Custom Cake Topper (+$20)',
defaultValue: false
}, '1fr');
});
decorSection.addRow(row => {
row.addCheckbox('fondantFigures', {
label: 'Fondant Figures/Decorations (+$50)',
defaultValue: false
}, '1fr');
row.addCheckbox('edibleImage', {
label: 'Edible Image Print (+$15)',
defaultValue: false
}, '1fr');
});
decorSection.addRow(row => {
row.addTextarea('specialInstructions', {
label: 'Special Instructions or Theme',
placeholder: 'Describe any special requests, theme, colors, or message for the cake...',
rows: 3
});
});
// Additional Items Section
const extrasSection = form.addSubform('extras', { title: '๐ง Additional Items' });
extrasSection.addRow(row => {
row.addInteger('cupcakeDozen', {
label: 'Cupcakes (per dozen)',
min: 0,
max: 20,
defaultValue: 0,
placeholder: '0'
}, '1fr');
row.addInteger('cakePops', {
label: 'Cake Pops (per dozen)',
min: 0,
max: 20,
defaultValue: 0,
placeholder: '0'
}, '1fr');
});
extrasSection.addRow(row => {
row.addInteger('cookies', {
label: 'Decorated Cookies (per dozen)',
min: 0,
max: 20,
defaultValue: 0,
placeholder: '0'
}, '1fr');
row.addInteger('macarons', {
label: 'Macarons (per dozen)',
min: 0,
max: 20,
defaultValue: 0,
placeholder: '0'
}, '1fr');
});
// Delivery Section
const deliverySection = form.addSubform('delivery', { title: '๐ Pickup & Delivery' });
deliverySection.addRow(row => {
row.addRadioButton('deliveryOption', {
label: 'Delivery Option',
options: [
{ id: 'pickup', name: 'Store Pickup (Free)' },
{ id: 'local', name: 'Local Delivery (within 10 miles) - $25' },
{ id: 'extended', name: 'Extended Delivery (10-25 miles) - $45' },
{ id: 'setup', name: 'Delivery + Setup - $75' }
],
defaultValue: 'pickup',
orientation: 'vertical',
isRequired: true
});
});
deliverySection.addRow(row => {
row.addCheckbox('rush', {
label: 'Rush Order (less than 48 hours) (+50%)',
defaultValue: false
});
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Price Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Order Summary', isCollapsible: false });
summarySection.addRow(row => {
row.addPriceDisplay('cakeBase', {
label: 'Cake Base Price',
computedValue: () => {
const size = cakeSection.radioButton('cakeSize')?.value() || 'medium';
const flavor = cakeSection.dropdown('flavor')?.value() || 'vanilla';
const filling = cakeSection.dropdown('filling')?.value() || 'buttercream';
const frostingStyle = decorSection.radioButton('frostingStyle')?.value() || 'buttercream-smooth';
const basePrice = cakePrices[size] || 75;
const flavorPremium = flavorPremiums[flavor] || 0;
const fillingPrice = fillingPrices[filling] || 0;
const frostingMultiplier = frostingMultipliers[frostingStyle] || 1.0;
return Math.round((basePrice + flavorPremium + fillingPrice) * frostingMultiplier);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('decorations', {
label: 'Decorations',
computedValue: () => {
let total = 0;
if (decorSection.checkbox('customColors')?.value()) total += 15;
if (decorSection.checkbox('goldAccents')?.value()) total += 25;
if (decorSection.checkbox('freshFlowers')?.value()) total += 35;
if (decorSection.checkbox('cakeTopper')?.value()) total += 20;
if (decorSection.checkbox('fondantFigures')?.value()) total += 50;
if (decorSection.checkbox('edibleImage')?.value()) total += 15;
return total;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('additionalItems', {
label: 'Additional Items',
computedValue: () => {
const cupcakes = extrasSection.integer('cupcakeDozen')?.value() || 0;
const cakePops = extrasSection.integer('cakePops')?.value() || 0;
const cookies = extrasSection.integer('cookies')?.value() || 0;
const macarons = extrasSection.integer('macarons')?.value() || 0;
return (cupcakes * 36) + (cakePops * 30) + (cookies * 42) + (macarons * 48);
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('delivery', {
label: 'Delivery',
computedValue: () => {
const option = deliverySection.radioButton('deliveryOption')?.value() || 'pickup';
const deliveryPrices: Record<string, number> = {
'pickup': 0,
'local': 25,
'extended': 45,
'setup': 75
};
return deliveryPrices[option] || 0;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('rushFee', {
label: 'Rush Order Fee',
computedValue: () => {
const rush = deliverySection.checkbox('rush')?.value();
if (!rush) return 0;
const size = cakeSection.radioButton('cakeSize')?.value() || 'medium';
const flavor = cakeSection.dropdown('flavor')?.value() || 'vanilla';
const filling = cakeSection.dropdown('filling')?.value() || 'buttercream';
const frostingStyle = decorSection.radioButton('frostingStyle')?.value() || 'buttercream-smooth';
const basePrice = cakePrices[size] || 75;
const flavorPremium = flavorPremiums[flavor] || 0;
const fillingPrice = fillingPrices[filling] || 0;
const frostingMultiplier = frostingMultipliers[frostingStyle] || 1.0;
const cakeTotal = (basePrice + flavorPremium + fillingPrice) * frostingMultiplier;
return Math.round(cakeTotal * 0.5);
},
variant: 'default',
prefix: '+',
isVisible: () => deliverySection.checkbox('rush')?.value() === true
});
});
const finalSection = form.addSubform('final', {
title: '๐งพ Total',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Order Total',
computedValue: () => {
const size = cakeSection.radioButton('cakeSize')?.value() || 'medium';
const flavor = cakeSection.dropdown('flavor')?.value() || 'vanilla';
const filling = cakeSection.dropdown('filling')?.value() || 'buttercream';
const frostingStyle = decorSection.radioButton('frostingStyle')?.value() || 'buttercream-smooth';
const rush = deliverySection.checkbox('rush')?.value();
// Cake price
const basePrice = cakePrices[size] || 75;
const flavorPremium = flavorPremiums[flavor] || 0;
const fillingPrice = fillingPrices[filling] || 0;
const frostingMultiplier = frostingMultipliers[frostingStyle] || 1.0;
let cakeTotal = (basePrice + flavorPremium + fillingPrice) * frostingMultiplier;
// Decorations
let decorTotal = 0;
if (decorSection.checkbox('customColors')?.value()) decorTotal += 15;
if (decorSection.checkbox('goldAccents')?.value()) decorTotal += 25;
if (decorSection.checkbox('freshFlowers')?.value()) decorTotal += 35;
if (decorSection.checkbox('cakeTopper')?.value()) decorTotal += 20;
if (decorSection.checkbox('fondantFigures')?.value()) decorTotal += 50;
if (decorSection.checkbox('edibleImage')?.value()) decorTotal += 15;
// Additional items
const cupcakes = extrasSection.integer('cupcakeDozen')?.value() || 0;
const cakePops = extrasSection.integer('cakePops')?.value() || 0;
const cookies = extrasSection.integer('cookies')?.value() || 0;
const macarons = extrasSection.integer('macarons')?.value() || 0;
const extrasTotal = (cupcakes * 36) + (cakePops * 30) + (cookies * 42) + (macarons * 48);
// Delivery
const deliveryOption = deliverySection.radioButton('deliveryOption')?.value() || 'pickup';
const deliveryPrices: Record<string, number> = {
'pickup': 0,
'local': 25,
'extended': 45,
'setup': 75
};
const deliveryTotal = deliveryPrices[deliveryOption] || 0;
// Rush fee
const rushFee = rush ? cakeTotal * 0.5 : 0;
return Math.round(cakeTotal + decorTotal + extrasTotal + deliveryTotal + rushFee);
},
variant: 'large'
}, '1fr');
row.addPriceDisplay('deposit', {
label: 'Deposit Required (50%)',
computedValue: () => {
const size = cakeSection.radioButton('cakeSize')?.value() || 'medium';
const flavor = cakeSection.dropdown('flavor')?.value() || 'vanilla';
const filling = cakeSection.dropdown('filling')?.value() || 'buttercream';
const frostingStyle = decorSection.radioButton('frostingStyle')?.value() || 'buttercream-smooth';
const rush = deliverySection.checkbox('rush')?.value();
const basePrice = cakePrices[size] || 75;
const flavorPremium = flavorPremiums[flavor] || 0;
const fillingPrice = fillingPrices[filling] || 0;
const frostingMultiplier = frostingMultipliers[frostingStyle] || 1.0;
let cakeTotal = (basePrice + flavorPremium + fillingPrice) * frostingMultiplier;
let decorTotal = 0;
if (decorSection.checkbox('customColors')?.value()) decorTotal += 15;
if (decorSection.checkbox('goldAccents')?.value()) decorTotal += 25;
if (decorSection.checkbox('freshFlowers')?.value()) decorTotal += 35;
if (decorSection.checkbox('cakeTopper')?.value()) decorTotal += 20;
if (decorSection.checkbox('fondantFigures')?.value()) decorTotal += 50;
if (decorSection.checkbox('edibleImage')?.value()) decorTotal += 15;
const cupcakes = extrasSection.integer('cupcakeDozen')?.value() || 0;
const cakePops = extrasSection.integer('cakePops')?.value() || 0;
const cookies = extrasSection.integer('cookies')?.value() || 0;
const macarons = extrasSection.integer('macarons')?.value() || 0;
const extrasTotal = (cupcakes * 36) + (cakePops * 30) + (cookies * 42) + (macarons * 48);
const deliveryOption = deliverySection.radioButton('deliveryOption')?.value() || 'pickup';
const deliveryPrices: Record<string, number> = {
'pickup': 0,
'local': 25,
'extended': 45,
'setup': 75
};
const deliveryTotal = deliveryPrices[deliveryOption] || 0;
const rushFee = rush ? cakeTotal * 0.5 : 0;
const total = cakeTotal + decorTotal + extrasTotal + deliveryTotal + rushFee;
return Math.round(total * 0.5);
},
variant: 'default'
}, '1fr');
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => '50% deposit required to confirm order. Please order at least 1 week in advance for custom cakes.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Place Order'
});
}