export function gutterCleaningCalculator(form: FormTs) {
// Base prices per linear foot
const basePricePerFoot = 1.50;
// Story height multipliers
const storyMultipliers: Record<string, number> = {
'single': 1.0,
'two': 1.3,
'three': 1.6,
'four-plus': 2.0
};
// Minimum charge
const minimumCharge = 100;
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Gutter Cleaning Quote',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Home Details Section
const homeSection = form.addSubform('homeDetails', { title: '๐ Home Details' });
homeSection.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
});
});
homeSection.addRow(row => {
row.addTextPanel('travelZoneInfo', {
computedValue: () => {
const addressField = homeSection.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 - $25 travel fee';
if (miles <= 50) return '๐ Remote area - $50 travel fee';
return '๐ Long distance - $75+ travel fee';
},
customStyles: { 'font-size': '0.9rem', 'color': '#0369a1', 'background': '#e0f2fe', 'padding': '10px', 'border-radius': '6px' }
});
});
homeSection.addRow(row => {
row.addInteger('linearFeet', {
label: 'Linear Feet of Gutters',
min: 50,
max: 1000,
defaultValue: 150,
placeholder: 'e.g. 150',
isRequired: true
}, '1fr');
row.addDropdown('stories', {
label: 'Number of Stories',
options: [
{ id: 'single', name: 'Single Story' },
{ id: 'two', name: 'Two Story (+30%)' },
{ id: 'three', name: 'Three Story (+60%)' },
{ id: 'four-plus', name: 'Four+ Story (+100%)' }
],
defaultValue: 'single',
isRequired: true
}, '1fr');
});
homeSection.addRow(row => {
row.addDropdown('roofType', {
label: 'Roof Type',
options: [
{ id: 'standard', name: 'Standard Pitch' },
{ id: 'steep', name: 'Steep Pitch (+20%)' },
{ id: 'flat', name: 'Flat/Low Pitch' },
{ id: 'complex', name: 'Complex/Multiple Levels (+30%)' }
],
defaultValue: 'standard'
}, '1fr');
row.addDropdown('accessibility', {
label: 'Accessibility',
options: [
{ id: 'easy', name: 'Easy (Clear ground access)' },
{ id: 'moderate', name: 'Moderate (Some obstacles, +15%)' },
{ id: 'difficult', name: 'Difficult (Landscaping, fences, +30%)' }
],
defaultValue: 'easy'
}, '1fr');
});
// Gutter Condition Section
const conditionSection = form.addSubform('gutterCondition', { title: '๐ง Gutter Condition' });
conditionSection.addRow(row => {
row.addDropdown('debrisLevel', {
label: 'Debris Level',
options: [
{ id: 'light', name: 'Light (Regular maintenance)' },
{ id: 'moderate', name: 'Moderate (Seasonal buildup, +20%)' },
{ id: 'heavy', name: 'Heavy (1+ year neglected, +40%)' },
{ id: 'extreme', name: 'Extreme (Packed/composted, +60%)' }
],
defaultValue: 'moderate',
isRequired: true
}, '1fr');
row.addDropdown('gutterType', {
label: 'Gutter Type',
options: [
{ id: 'standard', name: 'Standard K-Style' },
{ id: 'half-round', name: 'Half-Round (+10%)' },
{ id: 'box', name: 'Box Gutters (+20%)' },
{ id: 'copper', name: 'Copper (Premium care, +25%)' }
],
defaultValue: 'standard'
}, '1fr');
});
conditionSection.addRow(row => {
row.addCheckbox('hasGuards', {
label: 'Gutter guards installed (removal/reinstall +$3/ft)',
defaultValue: false
}, '1fr');
});
// Additional Services Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('downspoutFlush', {
label: 'Downspout Flush (+$10 per downspout)',
defaultValue: true
}, '1fr');
row.addInteger('downspoutCount', {
label: 'Number of Downspouts',
min: 1,
max: 20,
defaultValue: 4,
isVisible: () => addonsSection.checkbox('downspoutFlush')?.value() === true
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('minorRepairs', {
label: 'Minor Repairs (resealing, tightening) (+$75)',
defaultValue: false
}, '1fr');
row.addCheckbox('roofDebris', {
label: 'Roof Debris Removal (+$50)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('inspection', {
label: 'Full Gutter System Inspection (+$35)',
defaultValue: false
}, '1fr');
row.addCheckbox('beforeAfterPhotos', {
label: 'Before/After Photo Documentation (+$15)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('groundCleanup', {
label: 'Full Ground Cleanup (+$40)',
defaultValue: true
}, '1fr');
row.addCheckbox('scheduleMaintenance', {
label: 'Schedule Recurring Service (10% off next visit)',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Helper to calculate travel fee
const getTravelFee = () => {
const addressField = homeSection.address('serviceAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 15) return 0;
if (miles <= 30) return 25;
if (miles <= 50) return 50;
return 75 + Math.floor((miles - 50) / 10) * 10;
};
// Quote Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Your Quote', isCollapsible: false });
const getStoryMultiplier = () => {
const stories = homeSection.dropdown('stories')?.value() || 'single';
return storyMultipliers[stories] || 1.0;
};
const getRoofTypeMultiplier = () => {
const roofType = homeSection.dropdown('roofType')?.value() || 'standard';
const multipliers: Record<string, number> = {
'standard': 1.0,
'steep': 1.2,
'flat': 1.0,
'complex': 1.3
};
return multipliers[roofType] || 1.0;
};
const getAccessibilityMultiplier = () => {
const access = homeSection.dropdown('accessibility')?.value() || 'easy';
const multipliers: Record<string, number> = {
'easy': 1.0,
'moderate': 1.15,
'difficult': 1.3
};
return multipliers[access] || 1.0;
};
const getDebrisMultiplier = () => {
const debris = conditionSection.dropdown('debrisLevel')?.value() || 'moderate';
const multipliers: Record<string, number> = {
'light': 1.0,
'moderate': 1.2,
'heavy': 1.4,
'extreme': 1.6
};
return multipliers[debris] || 1.0;
};
const getGutterTypeMultiplier = () => {
const type = conditionSection.dropdown('gutterType')?.value() || 'standard';
const multipliers: Record<string, number> = {
'standard': 1.0,
'half-round': 1.1,
'box': 1.2,
'copper': 1.25
};
return multipliers[type] || 1.0;
};
summarySection.addRow(row => {
row.addPriceDisplay('baseCost', {
label: 'Base Cleaning Cost',
computedValue: () => {
const linearFeet = homeSection.integer('linearFeet')?.value() || 150;
const baseCost = linearFeet * basePricePerFoot;
const totalMultiplier = getStoryMultiplier() * getRoofTypeMultiplier() * getAccessibilityMultiplier() * getDebrisMultiplier() * getGutterTypeMultiplier();
return Math.max(Math.round(baseCost * totalMultiplier), minimumCharge);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('guardsCost', {
label: 'Gutter Guard Service',
computedValue: () => {
if (!conditionSection.checkbox('hasGuards')?.value()) return 0;
const linearFeet = homeSection.integer('linearFeet')?.value() || 150;
return linearFeet * 3;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('addonsCost', {
label: 'Additional Services',
computedValue: () => {
let addons = 0;
if (addonsSection.checkbox('downspoutFlush')?.value()) {
const count = addonsSection.integer('downspoutCount')?.value() || 4;
addons += count * 10;
}
if (addonsSection.checkbox('minorRepairs')?.value()) addons += 75;
if (addonsSection.checkbox('roofDebris')?.value()) addons += 50;
if (addonsSection.checkbox('inspection')?.value()) addons += 35;
if (addonsSection.checkbox('beforeAfterPhotos')?.value()) addons += 15;
if (addonsSection.checkbox('groundCleanup')?.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 linearFeet = homeSection.integer('linearFeet')?.value() || 150;
// Base cost with all multipliers
const baseCost = linearFeet * basePricePerFoot;
const totalMultiplier = getStoryMultiplier() * getRoofTypeMultiplier() * getAccessibilityMultiplier() * getDebrisMultiplier() * getGutterTypeMultiplier();
let total = Math.max(baseCost * totalMultiplier, minimumCharge);
// Gutter guards
if (conditionSection.checkbox('hasGuards')?.value()) {
total += linearFeet * 3;
}
// Add-ons
if (addonsSection.checkbox('downspoutFlush')?.value()) {
const count = addonsSection.integer('downspoutCount')?.value() || 4;
total += count * 10;
}
if (addonsSection.checkbox('minorRepairs')?.value()) total += 75;
if (addonsSection.checkbox('roofDebris')?.value()) total += 50;
if (addonsSection.checkbox('inspection')?.value()) total += 35;
if (addonsSection.checkbox('beforeAfterPhotos')?.value()) total += 15;
if (addonsSection.checkbox('groundCleanup')?.value()) total += 40;
// Add travel fee
total += getTravelFee();
return Math.round(total);
},
variant: 'large'
});
});
summarySection.addRow(row => {
row.addTextPanel('recurringNote', {
computedValue: () => {
if (addonsSection.checkbox('scheduleMaintenance')?.value()) {
return 'Schedule recurring service and save 10% on your next visit!';
}
return '';
},
customStyles: { 'font-size': '0.9rem', 'color': '#059669', 'font-weight': '500' }
});
});
// 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 linearFeet = homeSection.integer('linearFeet')?.value() || 150;
const baseCost = linearFeet * basePricePerFoot;
const totalMultiplier = getStoryMultiplier() * getRoofTypeMultiplier() * getAccessibilityMultiplier() * getDebrisMultiplier() * getGutterTypeMultiplier();
let total = Math.max(baseCost * totalMultiplier, minimumCharge);
if (conditionSection.checkbox('hasGuards')?.value()) {
total += linearFeet * 3;
}
if (addonsSection.checkbox('downspoutFlush')?.value()) {
const count = addonsSection.integer('downspoutCount')?.value() || 4;
total += count * 10;
}
if (addonsSection.checkbox('minorRepairs')?.value()) total += 75;
if (addonsSection.checkbox('roofDebris')?.value()) total += 50;
if (addonsSection.checkbox('inspection')?.value()) total += 35;
if (addonsSection.checkbox('beforeAfterPhotos')?.value()) total += 15;
if (addonsSection.checkbox('groundCleanup')?.value()) total += 40;
// Add travel fee
total += getTravelFee();
return Math.round(total);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Final price confirmed after on-site assessment.',
customStyles: { 'font-size': '0.85rem', 'color': '#94a3b8', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Get Free Quote'
});
}