export function windowCleaningCalculator(form: FormTs) {
// Base prices per window by type
const windowTypePrices: Record<string, number> = {
'standard': 5,
'large': 8,
'picture': 12,
'french': 10,
'skylights': 15,
'sliding-door': 12,
'storefront': 6
};
// Property type base fees
const propertyFees: Record<string, number> = {
'residential-1': 0, // 1 story
'residential-2': 25, // 2 story
'residential-3': 50, // 3 story
'commercial-small': 30,
'commercial-large': 75
};
// Frequency discounts
const frequencyDiscounts: Record<string, number> = {
'one-time': 0,
'quarterly': 10,
'bi-monthly': 15,
'monthly': 20
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Window 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 <= 12) return '๐ Within service area - No travel fee';
if (miles <= 25) return '๐ Extended area - $15 travel fee';
if (miles <= 40) return '๐ Remote area - $30 travel fee';
return '๐ Long distance - $45+ 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: 'residential-1', name: 'Residential - 1 Story' },
{ id: 'residential-2', name: 'Residential - 2 Story (+$25)' },
{ id: 'residential-3', name: 'Residential - 3+ Story (+$50)' },
{ id: 'commercial-small', name: 'Commercial - Small (+$30)' },
{ id: 'commercial-large', name: 'Commercial - Large (+$75)' }
],
defaultValue: 'residential-1',
isRequired: true
}, '1fr');
row.addDropdown('serviceType', {
label: 'Service Type',
options: [
{ id: 'exterior', name: 'Exterior Only' },
{ id: 'interior', name: 'Interior Only' },
{ id: 'both', name: 'Interior & Exterior (+80%)' }
],
defaultValue: 'both',
isRequired: true
}, '1fr');
});
// Window Count Section
const windowsSection = form.addSubform('windowCount', { title: '๐ช Window Count by Type' });
windowsSection.addRow(row => {
row.addInteger('standardWindows', {
label: 'Standard Windows ($5 each)',
min: 0,
max: 100,
defaultValue: 15,
placeholder: 'e.g. 15'
}, '1fr');
row.addInteger('largeWindows', {
label: 'Large Windows ($8 each)',
min: 0,
max: 50,
defaultValue: 4,
placeholder: 'e.g. 4'
}, '1fr');
});
windowsSection.addRow(row => {
row.addInteger('pictureWindows', {
label: 'Picture/Bay Windows ($12 each)',
min: 0,
max: 20,
defaultValue: 2,
placeholder: 'e.g. 2'
}, '1fr');
row.addInteger('frenchWindows', {
label: 'French Doors/Windows ($10 each)',
min: 0,
max: 20,
defaultValue: 1,
placeholder: 'e.g. 1'
}, '1fr');
});
windowsSection.addRow(row => {
row.addInteger('skylights', {
label: 'Skylights ($15 each)',
min: 0,
max: 20,
defaultValue: 0,
placeholder: 'e.g. 0'
}, '1fr');
row.addInteger('slidingDoors', {
label: 'Sliding Glass Doors ($12 each)',
min: 0,
max: 20,
defaultValue: 2,
placeholder: 'e.g. 2'
}, '1fr');
});
windowsSection.addRow(row => {
row.addInteger('storefrontPanes', {
label: 'Storefront Panes ($6 each)',
min: 0,
max: 200,
defaultValue: 0,
placeholder: 'Commercial only',
isVisible: () => {
const type = propertySection.dropdown('propertyType')?.value();
return type === 'commercial-small' || type === 'commercial-large';
}
}, '1fr');
});
// Service Frequency Section
const frequencySection = form.addSubform('serviceFrequency', { title: '๐
Service Frequency' });
frequencySection.addRow(row => {
row.addRadioButton('frequency', {
label: 'How often do you need service?',
options: [
{ id: 'one-time', name: 'One-time cleaning' },
{ id: 'quarterly', name: 'Quarterly (4x/year) - 10% off' },
{ id: 'bi-monthly', name: 'Bi-monthly (6x/year) - 15% off' },
{ id: 'monthly', name: 'Monthly (12x/year) - 20% off' }
],
defaultValue: 'one-time',
orientation: 'vertical'
});
});
// Additional Services Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('screenCleaning', {
label: 'Screen Cleaning (+$3/screen)',
defaultValue: false
}, '1fr');
row.addCheckbox('trackCleaning', {
label: 'Window Track Cleaning (+$4/window)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('hardWaterRemoval', {
label: 'Hard Water Stain Removal (+$8/window)',
defaultValue: false
}, '1fr');
row.addCheckbox('pressureWashing', {
label: 'Window Frame Pressure Wash (+$2/window)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('gutterCleaning', {
label: 'Gutter Cleaning (Add-on +$75-150)',
defaultValue: false
}, '1fr');
row.addCheckbox('solarPanels', {
label: 'Solar Panel Cleaning (+$10/panel)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addInteger('screenCount', {
label: 'Number of Screens',
min: 0,
max: 100,
defaultValue: 15,
isVisible: () => addonsSection.checkbox('screenCleaning')?.value() === true
}, '1fr');
row.addInteger('solarPanelCount', {
label: 'Number of Solar Panels',
min: 0,
max: 50,
defaultValue: 10,
isVisible: () => addonsSection.checkbox('solarPanels')?.value() === true
}, '1fr');
});
addonsSection.addRow(row => {
row.addDropdown('gutterSize', {
label: 'Gutter Size',
options: [
{ id: 'small', name: 'Small Home (up to 100 linear ft) +$75' },
{ id: 'medium', name: 'Medium Home (100-200 linear ft) +$100' },
{ id: 'large', name: 'Large Home (200+ linear ft) +$150' }
],
defaultValue: 'medium',
isVisible: () => addonsSection.checkbox('gutterCleaning')?.value() === true
}, '1fr');
});
// Condition Section
const conditionSection = form.addSubform('condition', { title: '๐ Window Condition' });
conditionSection.addRow(row => {
row.addRadioButton('dirtLevel', {
label: 'Current condition of windows',
options: [
{ id: 'light', name: 'Light - Recently cleaned, minimal dirt' },
{ id: 'normal', name: 'Normal - Standard residential buildup' },
{ id: 'heavy', name: 'Heavy - Not cleaned in over a year (+25%)' },
{ id: 'severe', name: 'Severe - Construction dust, neglected (+50%)' }
],
defaultValue: 'normal',
orientation: 'vertical'
});
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Price Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Your Quote', isCollapsible: false });
// Helper to calculate travel fee based on distance
const getTravelFee = () => {
const addressField = propertySection.address('serviceAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 12) return 0;
if (miles <= 25) return 15;
if (miles <= 40) return 30;
return 45 + Math.floor((miles - 40) / 15) * 10; // +$10 per 15 miles beyond 40
};
const getTotalWindowCount = () => {
return (windowsSection.integer('standardWindows')?.value() || 0) +
(windowsSection.integer('largeWindows')?.value() || 0) +
(windowsSection.integer('pictureWindows')?.value() || 0) +
(windowsSection.integer('frenchWindows')?.value() || 0) +
(windowsSection.integer('skylights')?.value() || 0) +
(windowsSection.integer('slidingDoors')?.value() || 0) +
(windowsSection.integer('storefrontPanes')?.value() || 0);
};
const calculateWindowsPrice = () => {
const standard = windowsSection.integer('standardWindows')?.value() || 0;
const large = windowsSection.integer('largeWindows')?.value() || 0;
const picture = windowsSection.integer('pictureWindows')?.value() || 0;
const french = windowsSection.integer('frenchWindows')?.value() || 0;
const skylights = windowsSection.integer('skylights')?.value() || 0;
const sliding = windowsSection.integer('slidingDoors')?.value() || 0;
const storefront = windowsSection.integer('storefrontPanes')?.value() || 0;
return (standard * (windowTypePrices['standard'] ?? 5)) +
(large * (windowTypePrices['large'] ?? 8)) +
(picture * (windowTypePrices['picture'] ?? 12)) +
(french * (windowTypePrices['french'] ?? 10)) +
(skylights * (windowTypePrices['skylights'] ?? 15)) +
(sliding * (windowTypePrices['sliding-door'] ?? 12)) +
(storefront * (windowTypePrices['storefront'] ?? 6));
};
summarySection.addRow(row => {
row.addPriceDisplay('windowsPrice', {
label: () => `Windows (${getTotalWindowCount()} total)`,
computedValue: () => {
let price = calculateWindowsPrice();
const serviceType = propertySection.dropdown('serviceType')?.value() || 'both';
if (serviceType === 'both') price *= 1.8;
return Math.round(price);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('propertyFee', {
label: 'Property Access Fee',
computedValue: () => {
const type = propertySection.dropdown('propertyType')?.value() || 'residential-1';
return propertyFees[type] || 0;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('conditionAdjustment', {
label: 'Condition Adjustment',
computedValue: () => {
let price = calculateWindowsPrice();
const serviceType = propertySection.dropdown('serviceType')?.value() || 'both';
if (serviceType === 'both') price *= 1.8;
const dirt = conditionSection.radioButton('dirtLevel')?.value() || 'normal';
if (dirt === 'heavy') return Math.round(price * 0.25);
if (dirt === 'severe') return Math.round(price * 0.50);
return 0;
},
variant: 'default',
prefix: '+',
isVisible: () => {
const dirt = conditionSection.radioButton('dirtLevel')?.value();
return dirt === 'heavy' || dirt === 'severe';
}
}, '1fr');
row.addPriceDisplay('addonsTotal', {
label: 'Additional Services',
computedValue: () => {
let total = 0;
const windowCount = getTotalWindowCount();
if (addonsSection.checkbox('screenCleaning')?.value()) {
const screens = addonsSection.integer('screenCount')?.value() || 0;
total += screens * 3;
}
if (addonsSection.checkbox('trackCleaning')?.value()) {
total += windowCount * 4;
}
if (addonsSection.checkbox('hardWaterRemoval')?.value()) {
total += windowCount * 8;
}
if (addonsSection.checkbox('pressureWashing')?.value()) {
total += windowCount * 2;
}
if (addonsSection.checkbox('gutterCleaning')?.value()) {
const size = addonsSection.dropdown('gutterSize')?.value() || 'medium';
const gutterPrices: Record<string, number> = { 'small': 75, 'medium': 100, 'large': 150 };
total += gutterPrices[size] || 100;
}
if (addonsSection.checkbox('solarPanels')?.value()) {
const panels = addonsSection.integer('solarPanelCount')?.value() || 0;
total += panels * 10;
}
return total;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => getTravelFee(),
variant: 'default',
prefix: '+'
});
});
summarySection.addRow(row => {
row.addPriceDisplay('frequencyDiscount', {
label: 'Frequency Discount',
computedValue: () => {
let price = calculateWindowsPrice();
const serviceType = propertySection.dropdown('serviceType')?.value() || 'both';
if (serviceType === 'both') price *= 1.8;
const type = propertySection.dropdown('propertyType')?.value() || 'residential-1';
price += propertyFees[type] || 0;
const dirt = conditionSection.radioButton('dirtLevel')?.value() || 'normal';
if (dirt === 'heavy') price *= 1.25;
if (dirt === 'severe') price *= 1.50;
const frequency = frequencySection.radioButton('frequency')?.value() || 'one-time';
const discountPercent = frequencyDiscounts[frequency] || 0;
return -Math.round(price * discountPercent / 100);
},
variant: 'success',
isVisible: () => {
const frequency = frequencySection.radioButton('frequency')?.value();
return frequency !== 'one-time';
}
});
});
summarySection.addSpacer({ showLine: true, lineStyle: 'solid', lineColor: '#e2e8f0' });
summarySection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Price',
computedValue: () => {
// Base window price
let total = calculateWindowsPrice();
// Service type multiplier
const serviceType = propertySection.dropdown('serviceType')?.value() || 'both';
if (serviceType === 'both') total *= 1.8;
// Property fee
const type = propertySection.dropdown('propertyType')?.value() || 'residential-1';
total += propertyFees[type] || 0;
// Condition adjustment
const dirt = conditionSection.radioButton('dirtLevel')?.value() || 'normal';
if (dirt === 'heavy') total *= 1.25;
if (dirt === 'severe') total *= 1.50;
// Add-ons
const windowCount = getTotalWindowCount();
if (addonsSection.checkbox('screenCleaning')?.value()) {
const screens = addonsSection.integer('screenCount')?.value() || 0;
total += screens * 3;
}
if (addonsSection.checkbox('trackCleaning')?.value()) total += windowCount * 4;
if (addonsSection.checkbox('hardWaterRemoval')?.value()) total += windowCount * 8;
if (addonsSection.checkbox('pressureWashing')?.value()) total += windowCount * 2;
if (addonsSection.checkbox('gutterCleaning')?.value()) {
const size = addonsSection.dropdown('gutterSize')?.value() || 'medium';
const gutterPrices: Record<string, number> = { 'small': 75, 'medium': 100, 'large': 150 };
total += gutterPrices[size] || 100;
}
if (addonsSection.checkbox('solarPanels')?.value()) {
const panels = addonsSection.integer('solarPanelCount')?.value() || 0;
total += panels * 10;
}
// Frequency discount
const frequency = frequencySection.radioButton('frequency')?.value() || 'one-time';
const discountPercent = frequencyDiscounts[frequency] || 0;
total *= (1 - discountPercent / 100);
// Add travel fee (not subject to discount)
total += getTravelFee();
return Math.round(total);
},
variant: 'large',
suffix: () => {
const frequency = frequencySection.radioButton('frequency')?.value();
if (frequency === 'one-time') return '';
return '/visit';
}
});
});
summarySection.addRow(row => {
row.addTextPanel('annualSavings', {
computedValue: () => {
const frequency = frequencySection.radioButton('frequency')?.value() || 'one-time';
if (frequency === 'one-time') return '';
let basePrice = calculateWindowsPrice();
const serviceType = propertySection.dropdown('serviceType')?.value() || 'both';
if (serviceType === 'both') basePrice *= 1.8;
const type = propertySection.dropdown('propertyType')?.value() || 'residential-1';
basePrice += propertyFees[type] || 0;
const visitsPerYear: Record<string, number> = {
'quarterly': 4,
'bi-monthly': 6,
'monthly': 12
};
const visits = visitsPerYear[frequency] || 1;
const discountPercent = frequencyDiscounts[frequency] || 0;
const savings = Math.round(basePrice * visits * discountPercent / 100);
return `Annual savings with recurring service: $${savings}`;
},
customStyles: { 'font-size': '0.9rem', 'color': '#059669' },
isVisible: () => {
const frequency = frequencySection.radioButton('frequency')?.value();
return frequency !== 'one-time';
}
});
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Price',
computedValue: () => {
let total = calculateWindowsPrice();
const serviceType = propertySection.dropdown('serviceType')?.value() || 'both';
if (serviceType === 'both') total *= 1.8;
const type = propertySection.dropdown('propertyType')?.value() || 'residential-1';
total += propertyFees[type] || 0;
const dirt = conditionSection.radioButton('dirtLevel')?.value() || 'normal';
if (dirt === 'heavy') total *= 1.25;
if (dirt === 'severe') total *= 1.50;
const windowCount = getTotalWindowCount();
if (addonsSection.checkbox('screenCleaning')?.value()) {
const screens = addonsSection.integer('screenCount')?.value() || 0;
total += screens * 3;
}
if (addonsSection.checkbox('trackCleaning')?.value()) total += windowCount * 4;
if (addonsSection.checkbox('hardWaterRemoval')?.value()) total += windowCount * 8;
if (addonsSection.checkbox('pressureWashing')?.value()) total += windowCount * 2;
if (addonsSection.checkbox('gutterCleaning')?.value()) {
const size = addonsSection.dropdown('gutterSize')?.value() || 'medium';
const gutterPrices: Record<string, number> = { 'small': 75, 'medium': 100, 'large': 150 };
total += gutterPrices[size] || 100;
}
if (addonsSection.checkbox('solarPanels')?.value()) {
const panels = addonsSection.integer('solarPanelCount')?.value() || 0;
total += panels * 10;
}
const frequency = frequencySection.radioButton('frequency')?.value() || 'one-time';
const discountPercent = frequencyDiscounts[frequency] || 0;
total *= (1 - discountPercent / 100);
// Add travel fee (not subject to discount)
total += getTravelFee();
return Math.round(total);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Minimum service charge: $75.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Book Service'
});
}