export function paintingEstimateCalculator(form: FormTs) {
// Base rates per square foot
const paintingRates: Record<string, number> = {
'interior-walls': 2.5,
'interior-ceiling': 2.0,
'exterior-siding': 3.5,
'exterior-trim': 4.0,
'deck-fence': 3.0
};
// Paint quality multipliers
const paintQualityMultipliers: Record<string, number> = {
'economy': 0.85,
'standard': 1.0,
'premium': 1.35,
'luxury': 1.75
};
// Room size to square feet mapping
const roomSizeSqFt: Record<string, number> = {
'small': 120,
'medium': 200,
'large': 320,
'xlarge': 450
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Get Your Painting Estimate',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Service Location Section
const locationSection = form.addSubform('serviceLocation', { title: '๐ Project Location' });
locationSection.addRow(row => {
row.addAddress('propertyAddress', {
label: 'Property Address',
placeholder: 'Enter the 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
});
});
locationSection.addRow(row => {
row.addTextPanel('serviceAreaInfo', {
computedValue: () => {
const addressField = locationSection.address('propertyAddress');
const miles = addressField?.distance();
if (miles == null) return '๐ Enter address to check service area';
if (miles <= 20) return '๐ Within service area - Standard pricing';
if (miles <= 40) return '๐ Extended area - $75 travel fee';
if (miles <= 60) return '๐ Remote area - $150 travel fee';
return '๐ Long distance - Custom quote required';
},
customStyles: { 'font-size': '0.9rem', 'color': '#c2410c', 'background': '#fff7ed', 'padding': '10px', 'border-radius': '6px' }
});
});
// Project Type Section
const projectSection = form.addSubform('project', { title: '๐ Project Details' });
projectSection.addRow(row => {
row.addRadioButton('projectType', {
label: 'Project Type',
options: [
{ id: 'interior', name: 'Interior Only' },
{ id: 'exterior', name: 'Exterior Only' },
{ id: 'both', name: 'Interior & Exterior' }
],
defaultValue: 'interior',
orientation: 'horizontal',
isRequired: true
});
});
// Interior Details
const interiorSection = form.addSubform('interior', {
title: '๐ Interior Details',
isVisible: () => {
const type = projectSection.radioButton('projectType')?.value();
return type === 'interior' || type === 'both';
}
});
interiorSection.addRow(row => {
row.addInteger('rooms', {
label: 'Number of Rooms',
min: 1,
max: 20,
defaultValue: 3,
isRequired: true
}, '1fr');
row.addDropdown('avgRoomSize', {
label: 'Average Room Size',
options: [
{ id: 'small', name: 'Small (< 150 sq ft)' },
{ id: 'medium', name: 'Medium (150-250 sq ft)' },
{ id: 'large', name: 'Large (250-400 sq ft)' },
{ id: 'xlarge', name: 'Extra Large (> 400 sq ft)' }
],
defaultValue: 'medium',
isRequired: true
}, '1fr');
});
interiorSection.addRow(row => {
row.addCheckbox('includeCeilings', {
label: 'Include Ceilings',
defaultValue: true
}, '1fr');
row.addCheckbox('includeTrim', {
label: 'Include Trim & Baseboards',
defaultValue: true
}, '1fr');
});
interiorSection.addRow(row => {
row.addDropdown('wallCondition', {
label: 'Wall Condition',
options: [
{ id: 'excellent', name: 'Excellent - Minimal prep needed' },
{ id: 'good', name: 'Good - Some patching required' },
{ id: 'fair', name: 'Fair - Significant prep work' },
{ id: 'poor', name: 'Poor - Major repairs needed' }
],
defaultValue: 'good'
});
});
// Exterior Details
const exteriorSection = form.addSubform('exterior', {
title: '๐ก Exterior Details',
isVisible: () => {
const type = projectSection.radioButton('projectType')?.value();
return type === 'exterior' || type === 'both';
}
});
exteriorSection.addRow(row => {
row.addInteger('exteriorSqFt', {
label: 'Exterior Square Footage',
min: 500,
max: 10000,
defaultValue: 2000,
placeholder: 'Paintable surface area',
isRequired: true
}, '1fr');
row.addDropdown('stories', {
label: 'Number of Stories',
options: [
{ id: '1', name: '1 Story' },
{ id: '2', name: '2 Stories (+15%)' },
{ id: '3', name: '3+ Stories (+30%)' }
],
defaultValue: '1'
}, '1fr');
});
exteriorSection.addRow(row => {
row.addCheckbox('includeExtTrim', {
label: 'Include Trim & Fascia',
defaultValue: true
}, '1fr');
row.addCheckbox('includeShutters', {
label: 'Include Shutters/Doors',
defaultValue: false
}, '1fr');
});
exteriorSection.addRow(row => {
row.addDropdown('exteriorCondition', {
label: 'Surface Condition',
options: [
{ id: 'excellent', name: 'Excellent - Power wash only' },
{ id: 'good', name: 'Good - Light scraping needed' },
{ id: 'fair', name: 'Fair - Moderate prep work' },
{ id: 'poor', name: 'Poor - Extensive preparation' }
],
defaultValue: 'good'
});
});
// Paint Options
const paintSection = form.addSubform('paint', { title: '๐จ Paint Options' });
paintSection.addRow(row => {
row.addRadioButton('paintQuality', {
label: 'Paint Quality',
options: [
{ id: 'economy', name: 'Economy (-15%)' },
{ id: 'standard', name: 'Standard' },
{ id: 'premium', name: 'Premium (+35%)' },
{ id: 'luxury', name: 'Luxury (+75%)' }
],
defaultValue: 'standard',
orientation: 'horizontal'
});
});
paintSection.addRow(row => {
row.addInteger('coats', {
label: 'Number of Coats',
min: 1,
max: 3,
defaultValue: 2
}, '1fr');
row.addCheckbox('colorChange', {
label: 'Significant Color Change (Dark to Light)',
defaultValue: false
}, '1fr');
});
// Additional Services
const additionalSection = form.addSubform('additional', { title: 'โจ Additional Services' });
additionalSection.addRow(row => {
row.addCheckbox('wallpaperRemoval', {
label: 'Wallpaper Removal (+$200)',
defaultValue: false
}, '1fr');
row.addCheckbox('leadPaint', {
label: 'Lead Paint Handling (+$500)',
defaultValue: false
}, '1fr');
});
additionalSection.addRow(row => {
row.addCheckbox('furnitureMoving', {
label: 'Furniture Moving (+$150)',
defaultValue: false
}, '1fr');
row.addCheckbox('cleanup', {
label: 'Extended Cleanup (+$100)',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Helper to calculate travel fee
const getTravelFee = () => {
const addressField = locationSection.address('propertyAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 20) return 0;
if (miles <= 40) return 75;
if (miles <= 60) return 150;
return 200 + Math.floor((miles - 60) / 20) * 50;
};
// Price Summary
const summarySection = form.addSubform('summary', { title: '๐ฐ Your Estimate', isCollapsible: false });
summarySection.addRow(row => {
row.addPriceDisplay('interiorCost', {
label: 'Interior Painting',
computedValue: () => {
const type = projectSection.radioButton('projectType')?.value();
if (type !== 'interior' && type !== 'both') return 0;
const rooms = interiorSection.integer('rooms')?.value() || 3;
const sizeKey = interiorSection.dropdown('avgRoomSize')?.value() || 'medium';
const roomSqFt = roomSizeSqFt[sizeKey] || 200;
const includeCeilings = interiorSection.checkbox('includeCeilings')?.value();
const includeTrim = interiorSection.checkbox('includeTrim')?.value();
const condition = interiorSection.dropdown('wallCondition')?.value() || 'good';
const wallArea = rooms * roomSqFt * 3.2;
let total = wallArea * (paintingRates['interior-walls'] || 2.5);
if (includeCeilings) {
total += rooms * roomSqFt * (paintingRates['interior-ceiling'] || 2.0);
}
if (includeTrim) {
total += rooms * 50 * 4;
}
const conditionMultipliers: Record<string, number> = {
'excellent': 0.9, 'good': 1.0, 'fair': 1.25, 'poor': 1.5
};
total *= conditionMultipliers[condition] || 1;
return Math.round(total);
},
variant: 'default',
isVisible: () => {
const type = projectSection.radioButton('projectType')?.value();
return type === 'interior' || type === 'both';
}
}, '1fr');
row.addPriceDisplay('exteriorCost', {
label: 'Exterior Painting',
computedValue: () => {
const type = projectSection.radioButton('projectType')?.value();
if (type !== 'exterior' && type !== 'both') return 0;
const sqft = exteriorSection.integer('exteriorSqFt')?.value() || 2000;
const stories = exteriorSection.dropdown('stories')?.value() || '1';
const includeExtTrim = exteriorSection.checkbox('includeExtTrim')?.value();
const includeShutters = exteriorSection.checkbox('includeShutters')?.value();
const condition = exteriorSection.dropdown('exteriorCondition')?.value() || 'good';
let total = sqft * (paintingRates['exterior-siding'] || 3.5);
const storiesMultiplier: Record<string, number> = { '1': 1, '2': 1.15, '3': 1.30 };
total *= storiesMultiplier[stories] || 1;
if (includeExtTrim) {
total *= 1.15;
}
if (includeShutters) {
total += 400;
}
const conditionMultipliers: Record<string, number> = {
'excellent': 0.85, 'good': 1.0, 'fair': 1.3, 'poor': 1.6
};
total *= conditionMultipliers[condition] || 1;
return Math.round(total);
},
variant: 'default',
isVisible: () => {
const type = projectSection.radioButton('projectType')?.value();
return type === 'exterior' || type === 'both';
}
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('paintQualityAdjustment', {
label: 'Paint Quality Adjustment',
computedValue: () => {
const quality = paintSection.radioButton('paintQuality')?.value() || 'standard';
const multiplier = paintQualityMultipliers[quality] || 1;
if (multiplier === 1) return 0;
const type = projectSection.radioButton('projectType')?.value();
let baseCost = 0;
if (type === 'interior' || type === 'both') {
const rooms = interiorSection.integer('rooms')?.value() || 3;
baseCost += rooms * 800;
}
if (type === 'exterior' || type === 'both') {
const sqft = exteriorSection.integer('exteriorSqFt')?.value() || 2000;
baseCost += sqft * 3.5;
}
return Math.round(baseCost * (multiplier - 1));
},
variant: 'default',
prefix: () => {
const quality = paintSection.radioButton('paintQuality')?.value() || 'standard';
const multiplier = paintQualityMultipliers[quality] || 1;
return multiplier >= 1 ? '+' : '';
}
}, '1fr');
row.addPriceDisplay('additionalServices', {
label: 'Additional Services',
computedValue: () => {
let total = 0;
if (additionalSection.checkbox('wallpaperRemoval')?.value()) total += 200;
if (additionalSection.checkbox('leadPaint')?.value()) total += 500;
if (additionalSection.checkbox('furnitureMoving')?.value()) total += 150;
if (additionalSection.checkbox('cleanup')?.value()) total += 100;
return total;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => getTravelFee(),
variant: 'default',
prefix: '+'
});
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalEstimate', {
label: 'Total Estimated Cost',
computedValue: () => {
const type = projectSection.radioButton('projectType')?.value();
const quality = paintSection.radioButton('paintQuality')?.value() || 'standard';
const qualityMultiplier = paintQualityMultipliers[quality] || 1;
const coats = paintSection.integer('coats')?.value() || 2;
const colorChange = paintSection.checkbox('colorChange')?.value();
let total = 0;
// Interior
if (type === 'interior' || type === 'both') {
const rooms = interiorSection.integer('rooms')?.value() || 3;
const sizeKey = interiorSection.dropdown('avgRoomSize')?.value() || 'medium';
const roomSqFt = roomSizeSqFt[sizeKey] || 200;
const includeCeilings = interiorSection.checkbox('includeCeilings')?.value();
const includeTrim = interiorSection.checkbox('includeTrim')?.value();
const condition = interiorSection.dropdown('wallCondition')?.value() || 'good';
const wallArea = rooms * roomSqFt * 3.2;
let interiorCost = wallArea * (paintingRates['interior-walls'] || 2.5);
if (includeCeilings) {
interiorCost += rooms * roomSqFt * (paintingRates['interior-ceiling'] || 2.0);
}
if (includeTrim) {
interiorCost += rooms * 50 * 4;
}
const conditionMultipliers: Record<string, number> = {
'excellent': 0.9, 'good': 1.0, 'fair': 1.25, 'poor': 1.5
};
interiorCost *= conditionMultipliers[condition] || 1;
total += interiorCost;
}
// Exterior
if (type === 'exterior' || type === 'both') {
const sqft = exteriorSection.integer('exteriorSqFt')?.value() || 2000;
const stories = exteriorSection.dropdown('stories')?.value() || '1';
const includeExtTrim = exteriorSection.checkbox('includeExtTrim')?.value();
const includeShutters = exteriorSection.checkbox('includeShutters')?.value();
const condition = exteriorSection.dropdown('exteriorCondition')?.value() || 'good';
let exteriorCost = sqft * (paintingRates['exterior-siding'] || 3.5);
const storiesMultiplier: Record<string, number> = { '1': 1, '2': 1.15, '3': 1.30 };
exteriorCost *= storiesMultiplier[stories] || 1;
if (includeExtTrim) exteriorCost *= 1.15;
if (includeShutters) exteriorCost += 400;
const conditionMultipliers: Record<string, number> = {
'excellent': 0.85, 'good': 1.0, 'fair': 1.3, 'poor': 1.6
};
exteriorCost *= conditionMultipliers[condition] || 1;
total += exteriorCost;
}
// Apply quality multiplier
total *= qualityMultiplier;
// Coats adjustment
if (coats > 2) {
total *= 1 + (0.35 * (coats - 2));
}
if (colorChange) {
total *= 1.2;
}
// Additional services
if (additionalSection.checkbox('wallpaperRemoval')?.value()) total += 200;
if (additionalSection.checkbox('leadPaint')?.value()) total += 500;
if (additionalSection.checkbox('furnitureMoving')?.value()) total += 150;
if (additionalSection.checkbox('cleanup')?.value()) total += 100;
// Travel fee
total += getTravelFee();
return Math.round(total);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'On-site inspection recommended for final quote.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Request Free Quote'
});
}