export function pressureWashingCalculator(form: FormTs) {
// Base prices per square foot by surface type
const surfacePrices: Record<string, number> = {
'concrete': 0.15,
'wood-deck': 0.25,
'brick': 0.20,
'vinyl-siding': 0.30,
'stucco': 0.35,
'pavers': 0.25
};
// Minimum charges by surface type
const minimumCharges: Record<string, number> = {
'concrete': 100,
'wood-deck': 150,
'brick': 150,
'vinyl-siding': 200,
'stucco': 200,
'pavers': 150
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Pressure Washing 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 <= 15) return '๐ Within service area - No travel fee';
if (miles <= 30) return '๐ Extended area - $40 travel fee';
if (miles <= 50) return '๐ Remote area - $75 travel fee';
return '๐ Long distance - $100+ 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', name: 'Residential' },
{ id: 'commercial', name: 'Commercial (+20%)' }
],
defaultValue: 'residential',
isRequired: true
}, '1fr');
row.addDropdown('accessibility', {
label: 'Site Accessibility',
options: [
{ id: 'easy', name: 'Easy Access' },
{ id: 'moderate', name: 'Moderate Access (+10%)' },
{ id: 'difficult', name: 'Difficult Access (+25%)' }
],
defaultValue: 'easy'
}, '1fr');
});
// Surface Selection Section
const surfaceSection = form.addSubform('surfaceDetails', { title: '๐งน Surface to Clean' });
surfaceSection.addRow(row => {
row.addRadioButton('surfaceType', {
label: 'Surface Type',
options: [
{ id: 'concrete', name: 'Concrete (Driveway, Sidewalk, Patio) - $0.15/sq ft' },
{ id: 'wood-deck', name: 'Wood Deck - $0.25/sq ft' },
{ id: 'brick', name: 'Brick Surface - $0.20/sq ft' },
{ id: 'vinyl-siding', name: 'Vinyl Siding - $0.30/sq ft' },
{ id: 'stucco', name: 'Stucco - $0.35/sq ft' },
{ id: 'pavers', name: 'Pavers - $0.25/sq ft' }
],
defaultValue: 'concrete',
orientation: 'vertical',
isRequired: true
});
});
surfaceSection.addRow(row => {
row.addInteger('squareFootage', {
label: 'Area Size (sq ft)',
min: 50,
max: 50000,
defaultValue: 500,
placeholder: 'e.g. 500',
isRequired: true
}, '1fr');
row.addDropdown('dirtLevel', {
label: 'Dirt/Stain Level',
options: [
{ id: 'light', name: 'Light (Regular maintenance)' },
{ id: 'moderate', name: 'Moderate (+15%)' },
{ id: 'heavy', name: 'Heavy (Years of buildup, +30%)' },
{ id: 'extreme', name: 'Extreme (Oil stains, graffiti, +50%)' }
],
defaultValue: 'moderate'
}, '1fr');
});
// Additional Services Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('sealant', {
label: 'Apply Sealant (+$0.10/sq ft)',
defaultValue: false
}, '1fr');
row.addCheckbox('gutterCleaning', {
label: 'Include Gutter Exterior (+$75)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('moldTreatment', {
label: 'Mold/Mildew Treatment (+$50)',
defaultValue: false
}, '1fr');
row.addCheckbox('oilStainRemoval', {
label: 'Oil Stain Pre-Treatment (+$35)',
defaultValue: false,
isVisible: () => surfaceSection.radioButton('surfaceType')?.value() === 'concrete'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('furnitureMoving', {
label: 'Move Outdoor Furniture (+$40)',
defaultValue: false,
isVisible: () => {
const surface = surfaceSection.radioButton('surfaceType')?.value();
return surface === 'wood-deck' || surface === 'concrete' || surface === 'pavers';
}
}, '1fr');
row.addCheckbox('rushService', {
label: 'Rush Service (within 48hrs, +25%)',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Helper to calculate travel fee
const getTravelFee = () => {
const addressField = propertySection.address('serviceAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 15) return 0;
if (miles <= 30) return 40;
if (miles <= 50) return 75;
return 100 + Math.floor((miles - 50) / 10) * 15;
};
// Quote Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Your Quote', isCollapsible: false });
const getPropertyMultiplier = () => {
const type = propertySection.dropdown('propertyType')?.value() || 'residential';
return type === 'commercial' ? 1.2 : 1.0;
};
const getAccessibilityMultiplier = () => {
const access = propertySection.dropdown('accessibility')?.value() || 'easy';
const multipliers: Record<string, number> = {
'easy': 1.0,
'moderate': 1.1,
'difficult': 1.25
};
return multipliers[access] || 1.0;
};
const getDirtMultiplier = () => {
const dirt = surfaceSection.dropdown('dirtLevel')?.value() || 'moderate';
const multipliers: Record<string, number> = {
'light': 1.0,
'moderate': 1.15,
'heavy': 1.3,
'extreme': 1.5
};
return multipliers[dirt] || 1.0;
};
summarySection.addRow(row => {
row.addPriceDisplay('baseCost', {
label: 'Base Cleaning Cost',
computedValue: () => {
const surface = surfaceSection.radioButton('surfaceType')?.value() || 'concrete';
const sqft = surfaceSection.integer('squareFootage')?.value() || 500;
const pricePerSqFt = surfacePrices[surface] || 0.15;
const minCharge = minimumCharges[surface] || 100;
const calculated = sqft * pricePerSqFt * getDirtMultiplier();
return Math.max(calculated, minCharge);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('adjustments', {
label: 'Property Adjustments',
computedValue: () => {
const surface = surfaceSection.radioButton('surfaceType')?.value() || 'concrete';
const sqft = surfaceSection.integer('squareFootage')?.value() || 500;
const pricePerSqFt = surfacePrices[surface] || 0.15;
const minCharge = minimumCharges[surface] || 100;
const baseCost = Math.max(sqft * pricePerSqFt * getDirtMultiplier(), minCharge);
const adjustedCost = baseCost * getPropertyMultiplier() * getAccessibilityMultiplier();
return adjustedCost - baseCost;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('addonsCost', {
label: 'Additional Services',
computedValue: () => {
const sqft = surfaceSection.integer('squareFootage')?.value() || 500;
let addons = 0;
if (addonsSection.checkbox('sealant')?.value()) addons += sqft * 0.10;
if (addonsSection.checkbox('gutterCleaning')?.value()) addons += 75;
if (addonsSection.checkbox('moldTreatment')?.value()) addons += 50;
if (addonsSection.checkbox('oilStainRemoval')?.value()) addons += 35;
if (addonsSection.checkbox('furnitureMoving')?.value()) addons += 40;
return addons;
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => getTravelFee(),
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addSpacer({ showLine: true, lineStyle: 'solid', lineColor: '#e2e8f0' });
summarySection.addRow(row => {
row.addPriceDisplay('totalEstimate', {
label: 'Total Estimated Cost',
computedValue: () => {
const surface = surfaceSection.radioButton('surfaceType')?.value() || 'concrete';
const sqft = surfaceSection.integer('squareFootage')?.value() || 500;
const pricePerSqFt = surfacePrices[surface] || 0.15;
const minCharge = minimumCharges[surface] || 100;
// Base cost with dirt level
let baseCost = Math.max(sqft * pricePerSqFt * getDirtMultiplier(), minCharge);
// Property and accessibility adjustments
baseCost *= getPropertyMultiplier() * getAccessibilityMultiplier();
// Add-ons
let addons = 0;
if (addonsSection.checkbox('sealant')?.value()) addons += sqft * 0.10;
if (addonsSection.checkbox('gutterCleaning')?.value()) addons += 75;
if (addonsSection.checkbox('moldTreatment')?.value()) addons += 50;
if (addonsSection.checkbox('oilStainRemoval')?.value()) addons += 35;
if (addonsSection.checkbox('furnitureMoving')?.value()) addons += 40;
let total = baseCost + addons;
// Rush service
if (addonsSection.checkbox('rushService')?.value()) {
total *= 1.25;
}
// Add travel fee
total += getTravelFee();
return Math.round(total);
},
variant: 'large'
});
});
// Sticky Summary Section
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalEstimate', {
label: 'Total Estimated Cost',
computedValue: () => {
const surface = surfaceSection.radioButton('surfaceType')?.value() || 'concrete';
const sqft = surfaceSection.integer('squareFootage')?.value() || 500;
const pricePerSqFt = surfacePrices[surface] || 0.15;
const minCharge = minimumCharges[surface] || 100;
let baseCost = Math.max(sqft * pricePerSqFt * getDirtMultiplier(), minCharge);
baseCost *= getPropertyMultiplier() * getAccessibilityMultiplier();
let addons = 0;
if (addonsSection.checkbox('sealant')?.value()) addons += sqft * 0.10;
if (addonsSection.checkbox('gutterCleaning')?.value()) addons += 75;
if (addonsSection.checkbox('moldTreatment')?.value()) addons += 50;
if (addonsSection.checkbox('oilStainRemoval')?.value()) addons += 35;
if (addonsSection.checkbox('furnitureMoving')?.value()) addons += 40;
let total = baseCost + addons;
if (addonsSection.checkbox('rushService')?.value()) {
total *= 1.25;
}
// Add travel fee
total += getTravelFee();
return Math.round(total);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Final price may vary based on on-site inspection.',
customStyles: { 'font-size': '0.85rem', 'color': '#94a3b8', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Get Free Quote'
});
}