export function cleaningCostCalculator(form: FormTs) {
// Property type pricing (base rates)
const propertyRates: Record<string, number> = {
'apartment': 80,
'house': 120,
'condo': 90,
'townhouse': 110,
'office': 150
};
// Cleaning type multipliers
const cleaningTypeMultipliers: Record<string, number> = {
'standard': 1.0,
'deep': 1.5,
'move-out': 1.75,
'move-in': 1.6,
'post-construction': 2.0
};
// Frequency discounts
const frequencyDiscounts: Record<string, number> = {
'one-time': 0,
'weekly': 20,
'bi-weekly': 15,
'monthly': 10
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Get Your Instant Cleaning Quote',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Property Details Section
const propertySection = form.addSubform('propertyDetails', { title: '๐ Property Details' });
propertySection.addRow(row => {
row.addAddress('serviceAddress', {
label: 'Service Address',
placeholder: 'Enter property address...',
showMap: true,
showDistance: true,
referenceAddress: {
formattedAddress: 'Service Center, Denver, CO',
coordinates: { lat: 39.7392, lng: -104.9903 }
},
restrictToCountries: ['US', 'CA'],
distanceUnit: 'miles',
isRequired: true
});
});
propertySection.addRow(row => {
row.addTextPanel('travelZoneInfo', {
computedValue: () => {
const addressField = propertySection.address('serviceAddress');
const miles = addressField?.distance();
if (miles == null) return '๐ Enter address to calculate travel fee';
if (miles <= 10) return '๐ Within service area - No travel fee';
if (miles <= 25) return '๐ Extended area - $15 travel fee';
if (miles <= 50) return '๐ Remote area - $35 travel fee';
return '๐ Long distance - $50+ travel fee';
},
customStyles: { 'font-size': '0.9rem', 'color': '#0369a1', 'background': '#e0f2fe', 'padding': '10px', 'border-radius': '6px' }
});
});
propertySection.addRow(row => {
row.addDropdown('propertyType', {
label: 'Property Type',
options: [
{ id: 'apartment', name: 'Apartment' },
{ id: 'house', name: 'House' },
{ id: 'condo', name: 'Condo' },
{ id: 'townhouse', name: 'Townhouse' },
{ id: 'office', name: 'Office/Commercial' }
],
defaultValue: 'house',
isRequired: true
}, '1fr');
row.addInteger('squareFootage', {
label: 'Square Footage',
min: 200,
max: 10000,
defaultValue: 1500,
placeholder: 'e.g. 1500',
isRequired: true
}, '1fr');
});
propertySection.addRow(row => {
row.addInteger('bedrooms', {
label: 'Bedrooms',
min: 0,
max: 10,
defaultValue: 2,
isRequired: true
}, '1fr');
row.addInteger('bathrooms', {
label: 'Bathrooms',
min: 1,
max: 10,
defaultValue: 2,
isRequired: true
}, '1fr');
});
// Cleaning Options Section
const optionsSection = form.addSubform('cleaningOptions', { title: '๐งน Cleaning Options' });
optionsSection.addRow(row => {
row.addRadioButton('cleaningType', {
label: 'Cleaning Type',
options: [
{ id: 'standard', name: 'Standard Cleaning' },
{ id: 'deep', name: 'Deep Cleaning (+50%)' },
{ id: 'move-out', name: 'Move-Out Cleaning (+75%)' },
{ id: 'move-in', name: 'Move-In Cleaning (+60%)' },
{ id: 'post-construction', name: 'Post-Construction (+100%)' }
],
defaultValue: 'standard',
orientation: 'vertical',
isRequired: true
}, '1fr');
row.addRadioButton('frequency', {
label: 'Service Frequency',
options: [
{ id: 'one-time', name: 'One-Time Service' },
{ id: 'weekly', name: 'Weekly (-20%)' },
{ id: 'bi-weekly', name: 'Bi-Weekly (-15%)' },
{ id: 'monthly', name: 'Monthly (-10%)' }
],
defaultValue: 'one-time',
orientation: 'vertical',
isRequired: true
}, '1fr');
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
// Pricing for add-ons (windows and organizing are calculated dynamically)
const addonPrices: Record<string, number> = {
'insideOven': 35,
'insideFridge': 35,
'insideCabinets': 50,
'laundryService': 30
};
addonsSection.addRow(row => {
row.addCheckboxList('extras', {
label: 'Select Add-ons',
options: [
{ id: 'insideOven', name: 'Inside Oven Cleaning (+$35)' },
{ id: 'insideFridge', name: 'Inside Refrigerator (+$35)' },
{ id: 'insideCabinets', name: 'Inside Cabinets (+$50)' },
{ id: 'windowsCleaning', name: 'Interior Windows (+$5/window)' },
{ id: 'laundryService', name: 'Laundry - Wash & Fold (+$30)' },
{ id: 'organizing', name: 'Organizing Service (+$50/hour)' }
],
orientation: 'vertical'
});
});
addonsSection.addRow(row => {
row.addInteger('windowsCount', {
label: 'Number of Windows',
min: 1,
max: 50,
defaultValue: 10,
isVisible: () => (addonsSection.checkboxList('extras')?.value() || []).includes('windowsCleaning')
}, '1fr');
row.addInteger('organizingHours', {
label: 'Organizing Hours',
min: 1,
max: 8,
defaultValue: 2,
isVisible: () => (addonsSection.checkboxList('extras')?.value() || []).includes('organizing')
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Price Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Your Quote', isCollapsible: false });
summarySection.addRow(row => {
row.addPriceDisplay('basePrice', {
label: 'Base Price',
computedValue: () => {
const propertyType = propertySection.dropdown('propertyType')?.value() || 'house';
const bedrooms = propertySection.integer('bedrooms')?.value() || 2;
const bathrooms = propertySection.integer('bathrooms')?.value() || 2;
const sqft = propertySection.integer('squareFootage')?.value() || 1500;
const baseRate = propertyRates[propertyType] || 120;
const bedroomCost = bedrooms * 20;
const bathroomCost = bathrooms * 25;
const sqftCost = Math.floor(sqft / 500) * 15;
return baseRate + bedroomCost + bathroomCost + sqftCost;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('cleaningTypeAdjustment', {
label: 'Cleaning Type Adjustment',
computedValue: () => {
const propertyType = propertySection.dropdown('propertyType')?.value() || 'house';
const bedrooms = propertySection.integer('bedrooms')?.value() || 2;
const bathrooms = propertySection.integer('bathrooms')?.value() || 2;
const sqft = propertySection.integer('squareFootage')?.value() || 1500;
const cleaningType = optionsSection.radioButton('cleaningType')?.value() || 'standard';
const baseRate = propertyRates[propertyType] || 120;
const bedroomCost = bedrooms * 20;
const bathroomCost = bathrooms * 25;
const sqftCost = Math.floor(sqft / 500) * 15;
const basePrice = baseRate + bedroomCost + bathroomCost + sqftCost;
const multiplier = cleaningTypeMultipliers[cleaningType] || 1;
return basePrice * (multiplier - 1);
},
variant: 'default',
prefix: '+'
}, '1fr');
});
// Helper to calculate travel fee
const getTravelFee = () => {
const addressField = propertySection.address('serviceAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 10) return 0;
if (miles <= 25) return 15;
if (miles <= 50) return 35;
return 50 + Math.floor((miles - 50) / 10) * 5; // $5 per additional 10 miles beyond 50
};
// Helper to calculate add-ons total
const getAddonsTotal = () => {
const selected = addonsSection.checkboxList('extras')?.value() || [];
let total = 0;
for (const id of selected) {
if (id === 'windowsCleaning') {
const windows = addonsSection.integer('windowsCount')?.value() || 0;
total += windows * 5;
} else if (id === 'organizing') {
const hours = addonsSection.integer('organizingHours')?.value() || 0;
total += hours * 50;
} else {
total += addonPrices[id] || 0;
}
}
return total;
};
summarySection.addRow(row => {
row.addPriceDisplay('addonsTotal', {
label: 'Add-ons Total',
computedValue: () => getAddonsTotal(),
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => getTravelFee(),
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('frequencyDiscount', {
label: 'Frequency Discount',
computedValue: () => {
const propertyType = propertySection.dropdown('propertyType')?.value() || 'house';
const bedrooms = propertySection.integer('bedrooms')?.value() || 2;
const bathrooms = propertySection.integer('bathrooms')?.value() || 2;
const sqft = propertySection.integer('squareFootage')?.value() || 1500;
const cleaningType = optionsSection.radioButton('cleaningType')?.value() || 'standard';
const frequency = optionsSection.radioButton('frequency')?.value() || 'one-time';
const baseRate = propertyRates[propertyType] || 120;
const bedroomCost = bedrooms * 20;
const bathroomCost = bathrooms * 25;
const sqftCost = Math.floor(sqft / 500) * 15;
const basePrice = baseRate + bedroomCost + bathroomCost + sqftCost;
const multiplier = cleaningTypeMultipliers[cleaningType] || 1;
const adjustedPrice = basePrice * multiplier;
const discountPercent = frequencyDiscounts[frequency] || 0;
return -(adjustedPrice * discountPercent / 100);
},
variant: 'success',
prefix: ''
}, '1fr');
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Estimated Price',
computedValue: () => {
const propertyType = propertySection.dropdown('propertyType')?.value() || 'house';
const bedrooms = propertySection.integer('bedrooms')?.value() || 2;
const bathrooms = propertySection.integer('bathrooms')?.value() || 2;
const sqft = propertySection.integer('squareFootage')?.value() || 1500;
const cleaningType = optionsSection.radioButton('cleaningType')?.value() || 'standard';
const frequency = optionsSection.radioButton('frequency')?.value() || 'one-time';
// Base price calculation
const baseRate = propertyRates[propertyType] || 120;
const bedroomCost = bedrooms * 20;
const bathroomCost = bathrooms * 25;
const sqftCost = Math.floor(sqft / 500) * 15;
const basePrice = baseRate + bedroomCost + bathroomCost + sqftCost;
// Apply cleaning type multiplier
const multiplier = cleaningTypeMultipliers[cleaningType] || 1;
let total = basePrice * multiplier;
// Add-ons
total += getAddonsTotal();
// Apply frequency discount
const discountPercent = frequencyDiscounts[frequency] || 0;
total = total * (1 - discountPercent / 100);
// Add travel fee
total += getTravelFee();
return Math.round(total);
},
variant: 'large',
suffix: () => {
const frequency = optionsSection.radioButton('frequency')?.value();
if (frequency === 'weekly') return '/week';
if (frequency === 'bi-weekly') return '/visit';
if (frequency === 'monthly') return '/month';
return '';
}
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Final price may vary based on actual conditions.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
// Contact Information Section - appears after price is shown
const contactSection = form.addSubform('contactInfo', { title: '๐ Get Your Quote' });
contactSection.addRow(row => {
row.addTextbox('name', {
label: 'Your Name',
placeholder: 'John Smith',
isRequired: true
}, '1fr');
row.addEmail('email', {
label: 'Email Address',
placeholder: 'john@example.com',
isRequired: true
}, '1fr');
});
contactSection.addRow(row => {
row.addTextbox('phone', {
label: 'Phone Number',
placeholder: '(555) 123-4567',
isRequired: false
}, '1fr');
row.addDatepicker('preferredDate', {
label: 'Preferred Date',
isRequired: false
}, '1fr');
});
contactSection.addRow(row => {
row.addTextarea('notes', {
label: 'Special Requests or Notes',
placeholder: 'Any specific areas to focus on, access instructions, pets, etc.',
rows: 3,
isRequired: false
});
});
form.configureSubmitButton({
label: 'Request Booking'
});
}