export function flowerArrangementCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Flower Arrangement Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Event Type Section
const eventSection = form.addSubform('event', { title: '🎉 Event Details' });
eventSection.addRow(row => {
row.addDropdown('eventType', {
label: 'Event Type',
options: [
{ id: 'wedding', name: 'Wedding' },
{ id: 'corporate', name: 'Corporate Event' },
{ id: 'birthday', name: 'Birthday Party' },
{ id: 'memorial', name: 'Memorial / Funeral' },
{ id: 'holiday', name: 'Holiday Event' },
{ id: 'personal', name: 'Personal Gift' },
{ id: 'other', name: 'Other Event' }
],
defaultValue: 'wedding',
isRequired: true
}, '1fr');
row.addDropdown('season', {
label: 'Event Season',
options: [
{ id: 'spring', name: 'Spring (Mar-May)' },
{ id: 'summer', name: 'Summer (Jun-Aug)' },
{ id: 'fall', name: 'Fall (Sep-Nov)' },
{ id: 'winter', name: 'Winter (Dec-Feb)' }
],
defaultValue: 'spring',
tooltip: 'Season affects flower availability and pricing'
}, '1fr');
});
// Arrangement Types Section
const arrangementsSection = form.addSubform('arrangements', { title: '💐 Arrangement Types' });
arrangementsSection.addRow(row => {
row.addInteger('bridesBouquet', {
label: "Bride's Bouquet",
min: 0,
max: 5,
defaultValue: 1,
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '1fr');
row.addInteger('bridesmaidBouquets', {
label: 'Bridesmaid Bouquets',
min: 0,
max: 20,
defaultValue: 4,
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '1fr');
});
arrangementsSection.addRow(row => {
row.addInteger('boutonnieres', {
label: 'Boutonnieres',
min: 0,
max: 50,
defaultValue: 6,
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '1fr');
row.addInteger('corsages', {
label: 'Corsages',
min: 0,
max: 50,
defaultValue: 4,
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '1fr');
});
arrangementsSection.addRow(row => {
row.addInteger('centerpieces', {
label: 'Centerpieces',
min: 0,
max: 100,
defaultValue: 15,
tooltip: 'Table centerpieces'
}, '1fr');
row.addInteger('largearrangements', {
label: 'Large Arrangements',
min: 0,
max: 50,
defaultValue: 2,
tooltip: 'Ceremony/entrance arrangements'
}, '1fr');
});
arrangementsSection.addRow(row => {
row.addInteger('smallArrangements', {
label: 'Small Arrangements',
min: 0,
max: 100,
defaultValue: 5,
tooltip: 'Accent pieces, cocktail tables'
}, '1fr');
row.addInteger('loosePetals', {
label: 'Petal Packages',
min: 0,
max: 20,
defaultValue: 0,
tooltip: 'Loose petals for aisles, tables'
}, '1fr');
});
// Flower Style Section
const styleSection = form.addSubform('style', { title: '🌸 Flower Style & Quality' });
styleSection.addRow(row => {
row.addRadioButton('quality', {
label: 'Flower Quality',
options: [
{ id: 'standard', name: 'Standard - Fresh quality flowers' },
{ id: 'premium', name: 'Premium - Higher-end varieties' },
{ id: 'luxury', name: 'Luxury - Rare & exotic blooms' }
],
defaultValue: 'premium',
isRequired: true
});
});
styleSection.addRow(row => {
row.addDropdown('style', {
label: 'Design Style',
options: [
{ id: 'classic', name: 'Classic / Traditional' },
{ id: 'romantic', name: 'Romantic / Garden' },
{ id: 'modern', name: 'Modern / Minimalist' },
{ id: 'bohemian', name: 'Bohemian / Wild' },
{ id: 'tropical', name: 'Tropical' },
{ id: 'rustic', name: 'Rustic / Country' }
],
defaultValue: 'romantic'
}, '1fr');
row.addDropdown('size', {
label: 'Arrangement Fullness',
options: [
{ id: 'petite', name: 'Petite / Minimal' },
{ id: 'standard', name: 'Standard' },
{ id: 'lush', name: 'Lush / Full' },
{ id: 'overflowing', name: 'Overflowing / Dramatic' }
],
defaultValue: 'standard',
tooltip: 'Affects amount of flowers used'
}, '1fr');
});
// Additional Services Section
const servicesSection = form.addSubform('services', { title: '✨ Additional Services' });
servicesSection.addRow(row => {
row.addCheckbox('delivery', {
label: 'Delivery',
defaultValue: true
}, '1fr');
row.addCheckbox('setup', {
label: 'On-site Setup',
defaultValue: true,
tooltip: 'Professional arrangement setup'
}, '1fr');
});
servicesSection.addRow(row => {
row.addCheckbox('breakdown', {
label: 'Breakdown / Removal',
defaultValue: false,
tooltip: 'Post-event flower removal'
}, '1fr');
row.addCheckbox('consultation', {
label: 'Design Consultation',
defaultValue: false,
tooltip: 'In-person design meeting'
}, '1fr');
});
servicesSection.addRow(row => {
row.addCheckbox('rentals', {
label: 'Vase/Container Rentals',
defaultValue: false,
tooltip: 'Premium containers included'
}, '1fr');
row.addCheckbox('preservation', {
label: 'Bouquet Preservation',
defaultValue: false,
tooltip: 'Dry and preserve bouquet',
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '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('personalFlowers', {
label: 'Personal Flowers',
computedValue: () => {
const quality = styleSection.radioButton('quality')?.value() || 'premium';
const size = styleSection.dropdown('size')?.value() || 'standard';
const qualityMultipliers: Record<string, number> = { 'standard': 1, 'premium': 1.5, 'luxury': 2.5 };
const sizeMultipliers: Record<string, number> = { 'petite': 0.7, 'standard': 1, 'lush': 1.4, 'overflowing': 1.8 };
const mult = (qualityMultipliers[quality] || 1) * (sizeMultipliers[size] || 1);
const bridesBouquet = (eventSection.dropdown('eventType')?.value() === 'wedding' ? arrangementsSection.integer('bridesBouquet')?.value() || 0 : 0) * 200 * mult;
const bridesmaidBouquets = (eventSection.dropdown('eventType')?.value() === 'wedding' ? arrangementsSection.integer('bridesmaidBouquets')?.value() || 0 : 0) * 85 * mult;
const boutonnieres = (eventSection.dropdown('eventType')?.value() === 'wedding' ? arrangementsSection.integer('boutonnieres')?.value() || 0 : 0) * 18 * mult;
const corsages = (eventSection.dropdown('eventType')?.value() === 'wedding' ? arrangementsSection.integer('corsages')?.value() || 0 : 0) * 35 * mult;
return Math.round((bridesBouquet + bridesmaidBouquets + boutonnieres + corsages) * 100) / 100;
},
variant: 'default',
isVisible: () => eventSection.dropdown('eventType')?.value() === 'wedding'
}, '1fr');
row.addPriceDisplay('arrangements', {
label: 'Arrangements',
computedValue: () => {
const quality = styleSection.radioButton('quality')?.value() || 'premium';
const size = styleSection.dropdown('size')?.value() || 'standard';
const qualityMultipliers: Record<string, number> = { 'standard': 1, 'premium': 1.5, 'luxury': 2.5 };
const sizeMultipliers: Record<string, number> = { 'petite': 0.7, 'standard': 1, 'lush': 1.4, 'overflowing': 1.8 };
const mult = (qualityMultipliers[quality] || 1) * (sizeMultipliers[size] || 1);
const centerpieces = (arrangementsSection.integer('centerpieces')?.value() || 0) * 95 * mult;
const large = (arrangementsSection.integer('largearrangements')?.value() || 0) * 275 * mult;
const small = (arrangementsSection.integer('smallArrangements')?.value() || 0) * 45 * mult;
const petals = (arrangementsSection.integer('loosePetals')?.value() || 0) * 50;
return Math.round((centerpieces + large + small + petals) * 100) / 100;
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('seasonalAdjustment', {
label: 'Seasonal Adjustment',
computedValue: () => {
const season = eventSection.dropdown('season')?.value() || 'spring';
const quality = styleSection.radioButton('quality')?.value() || 'premium';
const size = styleSection.dropdown('size')?.value() || 'standard';
const qualityMultipliers: Record<string, number> = { 'standard': 1, 'premium': 1.5, 'luxury': 2.5 };
const sizeMultipliers: Record<string, number> = { 'petite': 0.7, 'standard': 1, 'lush': 1.4, 'overflowing': 1.8 };
const mult = (qualityMultipliers[quality] || 1) * (sizeMultipliers[size] || 1);
// Calculate base total first
const centerpieces = (arrangementsSection.integer('centerpieces')?.value() || 0) * 95 * mult;
const large = (arrangementsSection.integer('largearrangements')?.value() || 0) * 275 * mult;
const small = (arrangementsSection.integer('smallArrangements')?.value() || 0) * 45 * mult;
let baseTotal = centerpieces + large + small;
if (eventSection.dropdown('eventType')?.value() === 'wedding') {
baseTotal += (arrangementsSection.integer('bridesBouquet')?.value() || 0) * 200 * mult;
baseTotal += (arrangementsSection.integer('bridesmaidBouquets')?.value() || 0) * 85 * mult;
baseTotal += (arrangementsSection.integer('boutonnieres')?.value() || 0) * 18 * mult;
baseTotal += (arrangementsSection.integer('corsages')?.value() || 0) * 35 * mult;
}
const seasonalMultipliers: Record<string, number> = { 'spring': 0, 'summer': 0.05, 'fall': 0.1, 'winter': 0.15 };
return Math.round(baseTotal * (seasonalMultipliers[season] || 0) * 100) / 100;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('services', {
label: 'Services',
computedValue: () => {
let services = 0;
if (servicesSection.checkbox('delivery')?.value()) services += 75;
if (servicesSection.checkbox('setup')?.value()) services += 150;
if (servicesSection.checkbox('breakdown')?.value()) services += 100;
if (servicesSection.checkbox('consultation')?.value()) services += 75;
if (servicesSection.checkbox('rentals')?.value()) services += 200;
if (servicesSection.checkbox('preservation')?.value()) services += 350;
return services;
},
variant: 'default'
}, '1fr');
});
// Quote Section
const quoteSection = form.addSubform('quote', { title: '💰 Your Quote', isCollapsible: false });
quoteSection.addRow(row => {
row.addPriceDisplay('totalQuote', {
label: 'Total Floral Package',
computedValue: () => {
const quality = styleSection.radioButton('quality')?.value() || 'premium';
const size = styleSection.dropdown('size')?.value() || 'standard';
const season = eventSection.dropdown('season')?.value() || 'spring';
const qualityMultipliers: Record<string, number> = { 'standard': 1, 'premium': 1.5, 'luxury': 2.5 };
const sizeMultipliers: Record<string, number> = { 'petite': 0.7, 'standard': 1, 'lush': 1.4, 'overflowing': 1.8 };
const seasonalMultipliers: Record<string, number> = { 'spring': 1, 'summer': 1.05, 'fall': 1.1, 'winter': 1.15 };
const mult = (qualityMultipliers[quality] || 1) * (sizeMultipliers[size] || 1) * (seasonalMultipliers[season] || 1);
let total = 0;
// Personal flowers (wedding only)
if (eventSection.dropdown('eventType')?.value() === 'wedding') {
total += (arrangementsSection.integer('bridesBouquet')?.value() || 0) * 200 * mult;
total += (arrangementsSection.integer('bridesmaidBouquets')?.value() || 0) * 85 * mult;
total += (arrangementsSection.integer('boutonnieres')?.value() || 0) * 18 * mult;
total += (arrangementsSection.integer('corsages')?.value() || 0) * 35 * mult;
}
// Arrangements
total += (arrangementsSection.integer('centerpieces')?.value() || 0) * 95 * mult;
total += (arrangementsSection.integer('largearrangements')?.value() || 0) * 275 * mult;
total += (arrangementsSection.integer('smallArrangements')?.value() || 0) * 45 * mult;
total += (arrangementsSection.integer('loosePetals')?.value() || 0) * 50;
// Services
if (servicesSection.checkbox('delivery')?.value()) total += 75;
if (servicesSection.checkbox('setup')?.value()) total += 150;
if (servicesSection.checkbox('breakdown')?.value()) total += 100;
if (servicesSection.checkbox('consultation')?.value()) total += 75;
if (servicesSection.checkbox('rentals')?.value()) total += 200;
if (servicesSection.checkbox('preservation')?.value()) total += 350;
return Math.round(total * 100) / 100;
},
variant: 'large'
});
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '🌷 Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addTextPanel('summaryText', {
computedValue: () => {
const eventType = eventSection.dropdown('eventType')?.value() || 'wedding';
const quality = styleSection.radioButton('quality')?.value() || 'premium';
const eventLabels: Record<string, string> = {
'wedding': 'Wedding', 'corporate': 'Corporate Event', 'birthday': 'Birthday',
'memorial': 'Memorial', 'holiday': 'Holiday', 'personal': 'Personal', 'other': 'Event'
};
const qualityLabels: Record<string, string> = { 'standard': 'standard', 'premium': 'premium', 'luxury': 'luxury' };
return `${eventLabels[eventType]} florals with ${qualityLabels[quality]} quality flowers`;
},
customStyles: { 'font-size': '0.95rem', 'font-weight': '500', 'text-align': 'center', 'color': '#1e293b' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices are estimates. Final quote may vary based on flower availability and specific design requirements.',
customStyles: { 'font-size': '0.8rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Request Consultation'
});
}