export function plumbingServiceCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Plumbing Service Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Service Location Section
const locationSection = form.addSubform('serviceLocation', { title: '๐ Your Location' });
locationSection.addRow(row => {
row.addAddress('propertyAddress', {
label: 'Property Address',
placeholder: 'Enter your 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 <= 15) return '๐ Within service area - Standard call fee';
if (miles <= 30) return '๐ Extended area - $35 additional travel';
if (miles <= 50) return '๐ Remote area - $65 additional travel';
return '๐ Outside service area - Please call for availability';
},
customStyles: { 'font-size': '0.9rem', 'color': '#0369a1', 'background': '#e0f2fe', 'padding': '10px', 'border-radius': '6px' }
});
});
// Service Type Section
const serviceSection = form.addSubform('service', { title: '๐ง Service Type' });
serviceSection.addRow(row => {
row.addRadioButton('serviceType', {
label: 'What do you need help with?',
options: [
{ id: 'repair', name: 'Repair/Fix' },
{ id: 'installation', name: 'New Installation' },
{ id: 'maintenance', name: 'Maintenance/Inspection' },
{ id: 'emergency', name: 'Emergency Service' },
{ id: 'remodel', name: 'Remodel/Renovation' }
],
defaultValue: 'repair',
orientation: 'vertical',
isRequired: true
});
});
serviceSection.addRow(row => {
row.addDropdown('category', {
label: 'Service Category',
options: [
{ id: 'drain', name: 'Drain/Sewer' },
{ id: 'faucet', name: 'Faucets & Fixtures' },
{ id: 'toilet', name: 'Toilet' },
{ id: 'water-heater', name: 'Water Heater' },
{ id: 'pipe', name: 'Pipes & Leaks' },
{ id: 'garbage-disposal', name: 'Garbage Disposal' },
{ id: 'sump-pump', name: 'Sump Pump' },
{ id: 'water-softener', name: 'Water Softener/Filtration' },
{ id: 'gas-line', name: 'Gas Line' },
{ id: 'other', name: 'Other' }
],
defaultValue: 'drain',
isRequired: true
}, '1fr');
});
// Problem Details Section
const problemSection = form.addSubform('problem', { title: '๐ Problem Details' });
problemSection.addRow(row => {
row.addDropdown('drainIssue', {
label: 'Drain Issue',
options: [
{ id: 'slow', name: 'Slow Drain' },
{ id: 'clogged', name: 'Completely Clogged' },
{ id: 'backup', name: 'Sewage Backup' },
{ id: 'smell', name: 'Bad Smell' },
{ id: 'main-line', name: 'Main Line Issue' }
],
defaultValue: 'slow',
isVisible: () => serviceSection.dropdown('category')?.value() === 'drain'
}, '1fr');
row.addDropdown('faucetIssue', {
label: 'Faucet Issue',
options: [
{ id: 'drip', name: 'Dripping/Leaking' },
{ id: 'no-water', name: 'No Water Flow' },
{ id: 'low-pressure', name: 'Low Pressure' },
{ id: 'replace', name: 'Replace Faucet' },
{ id: 'install-new', name: 'Install New Faucet' }
],
defaultValue: 'drip',
isVisible: () => serviceSection.dropdown('category')?.value() === 'faucet'
}, '1fr');
});
problemSection.addRow(row => {
row.addDropdown('toiletIssue', {
label: 'Toilet Issue',
options: [
{ id: 'running', name: 'Running/Won\'t Stop' },
{ id: 'clogged', name: 'Clogged' },
{ id: 'leak-base', name: 'Leaking at Base' },
{ id: 'weak-flush', name: 'Weak Flush' },
{ id: 'replace', name: 'Replace Toilet' },
{ id: 'install-new', name: 'Install New Toilet' }
],
defaultValue: 'running',
isVisible: () => serviceSection.dropdown('category')?.value() === 'toilet'
}, '1fr');
row.addDropdown('waterHeaterIssue', {
label: 'Water Heater Issue',
options: [
{ id: 'no-hot', name: 'No Hot Water' },
{ id: 'not-enough', name: 'Not Enough Hot Water' },
{ id: 'leaking', name: 'Leaking' },
{ id: 'strange-noise', name: 'Strange Noises' },
{ id: 'replace', name: 'Replace Unit' },
{ id: 'install-new', name: 'New Installation' }
],
defaultValue: 'no-hot',
isVisible: () => serviceSection.dropdown('category')?.value() === 'water-heater'
}, '1fr');
});
problemSection.addRow(row => {
row.addDropdown('pipeIssue', {
label: 'Pipe Issue',
options: [
{ id: 'leak-visible', name: 'Visible Leak' },
{ id: 'leak-hidden', name: 'Hidden Leak' },
{ id: 'burst', name: 'Burst Pipe' },
{ id: 'frozen', name: 'Frozen Pipe' },
{ id: 'repipe', name: 'Repiping Needed' },
{ id: 'water-damage', name: 'Water Damage' }
],
defaultValue: 'leak-visible',
isVisible: () => serviceSection.dropdown('category')?.value() === 'pipe'
}, '1fr');
});
problemSection.addRow(row => {
row.addDropdown('urgency', {
label: 'Urgency Level',
options: [
{ id: 'flexible', name: 'Flexible - Within a week' },
{ id: 'soon', name: 'Soon - Within 2-3 days' },
{ id: 'urgent', name: 'Urgent - Today/Tomorrow' },
{ id: 'emergency', name: 'Emergency - ASAP' }
],
defaultValue: 'soon'
}, '1fr');
row.addDropdown('accessibility', {
label: 'Access to Problem Area',
options: [
{ id: 'easy', name: 'Easy Access' },
{ id: 'moderate', name: 'Moderate (behind walls, etc.)' },
{ id: 'difficult', name: 'Difficult (underground, etc.)' }
],
defaultValue: 'easy'
}, '1fr');
});
// Property Details Section
const propertySection = form.addSubform('property', { title: '๐ Property Details' });
propertySection.addRow(row => {
row.addDropdown('propertyType', {
label: 'Property Type',
options: [
{ id: 'house', name: 'Single Family Home' },
{ id: 'apartment', name: 'Apartment/Condo' },
{ id: 'townhouse', name: 'Townhouse' },
{ id: 'commercial', name: 'Commercial' },
{ id: 'multi-family', name: 'Multi-Family' }
],
defaultValue: 'house'
}, '1fr');
row.addDropdown('propertyAge', {
label: 'Property Age',
options: [
{ id: 'new', name: 'Less than 10 years' },
{ id: 'medium', name: '10-30 years' },
{ id: 'old', name: '30-50 years' },
{ id: 'very-old', name: 'Over 50 years' }
],
defaultValue: 'medium'
}, '1fr');
});
propertySection.addRow(row => {
row.addInteger('floors', {
label: 'Number of Floors',
min: 1,
max: 5,
defaultValue: 1
}, '1fr');
row.addInteger('bathrooms', {
label: 'Number of Bathrooms',
min: 1,
max: 10,
defaultValue: 2,
isVisible: () => serviceSection.radioButton('serviceType')?.value() === 'remodel' || serviceSection.dropdown('category')?.value() === 'pipe'
}, '1fr');
});
// Additional Services Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('cameraInspection', {
label: 'Camera Inspection',
defaultValue: false,
tooltip: 'Video inspection of pipes/drains'
}, '1fr');
row.addCheckbox('leakDetection', {
label: 'Leak Detection Service',
defaultValue: false,
tooltip: 'Electronic leak detection'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('waterTesting', {
label: 'Water Quality Testing',
defaultValue: false
}, '1fr');
row.addCheckbox('preventiveMaintenance', {
label: 'Preventive Maintenance Plan',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('permitHandling', {
label: 'Permit Handling',
defaultValue: false,
tooltip: 'Required for some installations'
}, '1fr');
row.addCheckbox('cleanup', {
label: 'Full Cleanup & Disposal',
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 <= 15) return 0;
if (miles <= 30) return 35;
if (miles <= 50) return 65;
return 85 + Math.floor((miles - 50) / 15) * 20;
};
// Quote Section
const quoteSection = form.addSubform('quote', { title: '๐ฐ Estimated Quote', isCollapsible: false });
const calculateQuote = () => {
const serviceType = serviceSection.radioButton('serviceType')?.value() || 'repair';
const category = serviceSection.dropdown('category')?.value() || 'drain';
const urgency = problemSection.dropdown('urgency')?.value() || 'soon';
const accessibility = problemSection.dropdown('accessibility')?.value() || 'easy';
const propertyAge = propertySection.dropdown('propertyAge')?.value() || 'medium';
// Base service call fee
let serviceFee = 75;
if (serviceType === 'emergency') serviceFee = 150;
// Base labor by category
const categoryLabor: Record<string, { min: number; max: number }> = {
'drain': { min: 100, max: 350 },
'faucet': { min: 80, max: 250 },
'toilet': { min: 100, max: 400 },
'water-heater': { min: 150, max: 2500 },
'pipe': { min: 150, max: 1500 },
'garbage-disposal': { min: 100, max: 350 },
'sump-pump': { min: 200, max: 800 },
'water-softener': { min: 200, max: 1500 },
'gas-line': { min: 200, max: 1000 },
'other': { min: 100, max: 500 }
};
let laborMin = categoryLabor[category]?.min || 100;
let laborMax = categoryLabor[category]?.max || 500;
// Service type multiplier
const serviceMultipliers: Record<string, number> = {
'repair': 1.0,
'installation': 1.5,
'maintenance': 0.6,
'emergency': 1.8,
'remodel': 2.5
};
const serviceMult = serviceMultipliers[serviceType] || 1.0;
laborMin *= serviceMult;
laborMax *= serviceMult;
// Urgency adjustment
const urgencyMultipliers: Record<string, number> = {
'flexible': 1.0,
'soon': 1.0,
'urgent': 1.25,
'emergency': 1.75
};
const urgencyMult = urgencyMultipliers[urgency] || 1.0;
laborMin *= urgencyMult;
laborMax *= urgencyMult;
// Accessibility adjustment
const accessMultipliers: Record<string, number> = {
'easy': 1.0,
'moderate': 1.3,
'difficult': 1.8
};
const accessMult = accessMultipliers[accessibility] || 1.0;
laborMin *= accessMult;
laborMax *= accessMult;
// Property age adjustment
const ageMultipliers: Record<string, number> = {
'new': 1.0,
'medium': 1.1,
'old': 1.25,
'very-old': 1.4
};
const ageMult = ageMultipliers[propertyAge] || 1.0;
laborMin *= ageMult;
laborMax *= ageMult;
// Add-ons
let addons = 0;
if (addonsSection.checkbox('cameraInspection')?.value()) addons += 150;
if (addonsSection.checkbox('leakDetection')?.value()) addons += 200;
if (addonsSection.checkbox('waterTesting')?.value()) addons += 75;
if (addonsSection.checkbox('preventiveMaintenance')?.value()) addons += 150;
if (addonsSection.checkbox('permitHandling')?.value()) addons += 100;
if (addonsSection.checkbox('cleanup')?.value()) addons += 75;
const travelFee = getTravelFee();
const totalMin = serviceFee + laborMin + addons + travelFee;
const totalMax = serviceFee + laborMax + addons + travelFee;
return {
serviceFee: Math.round(serviceFee),
laborMin: Math.round(laborMin),
laborMax: Math.round(laborMax),
addons: Math.round(addons),
travelFee: Math.round(travelFee),
totalMin: Math.round(totalMin),
totalMax: Math.round(totalMax)
};
};
quoteSection.addRow(row => {
row.addPriceDisplay('serviceFee', {
label: 'Service Call Fee',
computedValue: () => calculateQuote().serviceFee,
variant: 'default'
}, '1fr');
row.addTextPanel('laborRange', {
label: 'Labor Estimate',
computedValue: () => `$${calculateQuote().laborMin} - $${calculateQuote().laborMax}`,
customStyles: { 'font-weight': '500' }
}, '1fr');
});
quoteSection.addRow(row => {
row.addPriceDisplay('addons', {
label: 'Additional Services',
computedValue: () => calculateQuote().addons,
variant: 'default',
isVisible: () => calculateQuote().addons > 0
});
});
quoteSection.addRow(row => {
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => calculateQuote().travelFee,
variant: 'default',
prefix: '+',
isVisible: () => calculateQuote().travelFee > 0
});
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐งพ Total Estimate',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addTextPanel('totalRange', {
computedValue: () => `$${calculateQuote().totalMin} - $${calculateQuote().totalMax}`,
customStyles: { 'font-size': '2rem', 'font-weight': '700', 'color': '#059669', 'text-align': 'center' }
});
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Parts and materials not included. Final price may vary after on-site inspection.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Request Plumber Visit'
});
}