export function electricalWorkCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Electrical Work 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 - $40 additional travel';
if (miles <= 50) return '๐ Remote area - $75 additional travel';
return '๐ Outside service area - Please call for availability';
},
customStyles: { 'font-size': '0.9rem', 'color': '#ca8a04', 'background': '#fefce8', 'padding': '10px', 'border-radius': '6px' }
});
});
// Service Type Section
const serviceSection = form.addSubform('service', { title: 'โก Service Type' });
serviceSection.addRow(row => {
row.addRadioButton('serviceType', {
label: 'Type of Work',
options: [
{ id: 'repair', name: 'Repair/Troubleshooting' },
{ id: 'installation', name: 'New Installation' },
{ id: 'upgrade', name: 'Upgrade/Replacement' },
{ id: 'inspection', name: 'Inspection/Safety Check' },
{ id: 'emergency', name: 'Emergency Service' }
],
defaultValue: 'repair',
orientation: 'vertical',
isRequired: true
});
});
serviceSection.addRow(row => {
row.addDropdown('category', {
label: 'Work Category',
options: [
{ id: 'outlet', name: 'Outlets & Switches' },
{ id: 'lighting', name: 'Lighting & Fixtures' },
{ id: 'panel', name: 'Electrical Panel' },
{ id: 'wiring', name: 'Wiring & Circuits' },
{ id: 'ceiling-fan', name: 'Ceiling Fan' },
{ id: 'ev-charger', name: 'EV Charger Installation' },
{ id: 'generator', name: 'Generator' },
{ id: 'smoke-detector', name: 'Smoke/CO Detectors' },
{ id: 'smart-home', name: 'Smart Home Devices' },
{ id: 'outdoor', name: 'Outdoor/Landscape Lighting' },
{ id: 'other', name: 'Other' }
],
defaultValue: 'outlet',
isRequired: true
}, '1fr');
});
// Job Details Section
const jobSection = form.addSubform('job', { title: '๐ Job Details' });
jobSection.addRow(row => {
row.addInteger('quantity', {
label: () => {
const category = serviceSection.dropdown('category')?.value();
if (category === 'outlet') return 'Number of Outlets/Switches';
if (category === 'lighting') return 'Number of Fixtures';
if (category === 'ceiling-fan') return 'Number of Fans';
if (category === 'smoke-detector') return 'Number of Detectors';
return 'Quantity';
},
min: 1,
max: 50,
defaultValue: 1,
isVisible: () => !['panel', 'wiring', 'ev-charger', 'generator'].includes(serviceSection.dropdown('category')?.value() || '')
}, '1fr');
row.addDropdown('complexity', {
label: 'Job Complexity',
options: [
{ id: 'simple', name: 'Simple - Basic replacement/repair' },
{ id: 'moderate', name: 'Moderate - Some new wiring needed' },
{ id: 'complex', name: 'Complex - Extensive wiring/permits' }
],
defaultValue: 'simple'
}, '1fr');
});
jobSection.addRow(row => {
row.addDropdown('panelWork', {
label: 'Panel Work Needed',
options: [
{ id: 'breaker', name: 'Replace Breaker' },
{ id: 'add-circuit', name: 'Add New Circuit' },
{ id: 'subpanel', name: 'Install Sub-Panel' },
{ id: 'upgrade-100', name: 'Upgrade to 100 Amp' },
{ id: 'upgrade-200', name: 'Upgrade to 200 Amp' },
{ id: 'upgrade-400', name: 'Upgrade to 400 Amp' },
{ id: 'replace', name: 'Full Panel Replacement' }
],
defaultValue: 'breaker',
isVisible: () => serviceSection.dropdown('category')?.value() === 'panel'
}, '1fr');
row.addDropdown('evChargerLevel', {
label: 'EV Charger Level',
options: [
{ id: 'level1', name: 'Level 1 (120V)' },
{ id: 'level2-40', name: 'Level 2 - 40 Amp' },
{ id: 'level2-50', name: 'Level 2 - 50 Amp' },
{ id: 'level2-80', name: 'Level 2 - 80 Amp' }
],
defaultValue: 'level2-40',
isVisible: () => serviceSection.dropdown('category')?.value() === 'ev-charger'
}, '1fr');
});
jobSection.addRow(row => {
row.addDropdown('generatorType', {
label: 'Generator Type',
options: [
{ id: 'portable-hookup', name: 'Portable Generator Hookup' },
{ id: 'standby-small', name: 'Standby - Small (7-10kW)' },
{ id: 'standby-medium', name: 'Standby - Medium (11-16kW)' },
{ id: 'standby-large', name: 'Standby - Large (17-22kW)' },
{ id: 'whole-home', name: 'Whole Home (22kW+)' }
],
defaultValue: 'standby-medium',
isVisible: () => serviceSection.dropdown('category')?.value() === 'generator'
}, '1fr');
row.addDropdown('wiringScope', {
label: 'Wiring Scope',
options: [
{ id: 'single-room', name: 'Single Room' },
{ id: 'multiple-rooms', name: 'Multiple Rooms' },
{ id: 'whole-floor', name: 'Whole Floor' },
{ id: 'whole-house', name: 'Whole House Rewire' }
],
defaultValue: 'single-room',
isVisible: () => serviceSection.dropdown('category')?.value() === 'wiring'
}, '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' }
],
defaultValue: 'house'
}, '1fr');
row.addDropdown('accessibility', {
label: 'Access to Work Area',
options: [
{ id: 'easy', name: 'Easy - Exposed/Accessible' },
{ id: 'moderate', name: 'Moderate - Attic/Basement' },
{ id: 'difficult', name: 'Difficult - Behind walls' }
],
defaultValue: 'easy'
}, '1fr');
});
propertySection.addRow(row => {
row.addDropdown('homeAge', {
label: 'Home Age',
options: [
{ id: 'new', name: 'Less than 20 years' },
{ id: 'medium', name: '20-40 years' },
{ id: 'old', name: '40-60 years' },
{ id: 'very-old', name: 'Over 60 years' }
],
defaultValue: 'medium'
}, '1fr');
row.addInteger('sqft', {
label: 'Square Footage',
min: 100,
max: 20000,
defaultValue: 2000,
isVisible: () => serviceSection.dropdown('category')?.value() === 'wiring'
}, '1fr');
});
// Additional Services Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('permit', {
label: 'Permit & Inspection',
defaultValue: false,
tooltip: 'Required for many electrical jobs'
}, '1fr');
row.addCheckbox('diagnosis', {
label: 'Diagnostic Service',
defaultValue: false,
tooltip: 'Troubleshooting unknown issues'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('surgeProtection', {
label: 'Whole-Home Surge Protection',
defaultValue: false
}, '1fr');
row.addCheckbox('grounding', {
label: 'Grounding/Bonding Check',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('arcFault', {
label: 'Arc-Fault Breakers (AFCI)',
defaultValue: false
}, '1fr');
row.addCheckbox('gfci', {
label: 'GFCI Outlets',
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 40;
if (miles <= 50) return 75;
return 100 + Math.floor((miles - 50) / 15) * 25;
};
// 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() || 'outlet';
const complexity = jobSection.dropdown('complexity')?.value() || 'simple';
const quantity = jobSection.integer('quantity')?.value() || 1;
const accessibility = propertySection.dropdown('accessibility')?.value() || 'easy';
const homeAge = propertySection.dropdown('homeAge')?.value() || 'medium';
// Base prices by category
const categoryPrices: Record<string, { min: number; max: number }> = {
'outlet': { min: 75, max: 200 },
'lighting': { min: 100, max: 350 },
'panel': { min: 200, max: 4000 },
'wiring': { min: 300, max: 8000 },
'ceiling-fan': { min: 150, max: 400 },
'ev-charger': { min: 500, max: 2500 },
'generator': { min: 500, max: 15000 },
'smoke-detector': { min: 50, max: 150 },
'smart-home': { min: 75, max: 250 },
'outdoor': { min: 200, max: 1500 },
'other': { min: 100, max: 500 }
};
let baseMin = categoryPrices[category]?.min || 100;
let baseMax = categoryPrices[category]?.max || 500;
// Panel work specifics
if (category === 'panel') {
const panelWork = jobSection.dropdown('panelWork')?.value() || 'breaker';
const panelPrices: Record<string, { min: number; max: number }> = {
'breaker': { min: 150, max: 300 },
'add-circuit': { min: 200, max: 500 },
'subpanel': { min: 500, max: 1500 },
'upgrade-100': { min: 1500, max: 2500 },
'upgrade-200': { min: 2000, max: 4000 },
'upgrade-400': { min: 4000, max: 8000 },
'replace': { min: 1500, max: 3500 }
};
baseMin = panelPrices[panelWork]?.min || 200;
baseMax = panelPrices[panelWork]?.max || 1000;
}
// EV charger specifics
if (category === 'ev-charger') {
const level = jobSection.dropdown('evChargerLevel')?.value() || 'level2-40';
const evPrices: Record<string, { min: number; max: number }> = {
'level1': { min: 300, max: 600 },
'level2-40': { min: 500, max: 1200 },
'level2-50': { min: 700, max: 1500 },
'level2-80': { min: 1000, max: 2500 }
};
baseMin = evPrices[level]?.min || 500;
baseMax = evPrices[level]?.max || 1500;
}
// Generator specifics
if (category === 'generator') {
const genType = jobSection.dropdown('generatorType')?.value() || 'standby-medium';
const genPrices: Record<string, { min: number; max: number }> = {
'portable-hookup': { min: 400, max: 1000 },
'standby-small': { min: 3000, max: 6000 },
'standby-medium': { min: 5000, max: 10000 },
'standby-large': { min: 8000, max: 14000 },
'whole-home': { min: 12000, max: 20000 }
};
baseMin = genPrices[genType]?.min || 5000;
baseMax = genPrices[genType]?.max || 10000;
}
// Quantity multiplier (with volume discount)
if (!['panel', 'wiring', 'ev-charger', 'generator'].includes(category)) {
const discount = quantity > 5 ? 0.85 : quantity > 2 ? 0.9 : 1;
baseMin = baseMin * quantity * discount;
baseMax = baseMax * quantity * discount;
}
// Complexity multiplier
const complexityMult: Record<string, number> = {
'simple': 1.0,
'moderate': 1.4,
'complex': 2.0
};
baseMin *= complexityMult[complexity] || 1.0;
baseMax *= complexityMult[complexity] || 1.0;
// Accessibility multiplier
const accessMult: Record<string, number> = {
'easy': 1.0,
'moderate': 1.25,
'difficult': 1.6
};
baseMin *= accessMult[accessibility] || 1.0;
baseMax *= accessMult[accessibility] || 1.0;
// Home age multiplier
const ageMult: Record<string, number> = {
'new': 1.0,
'medium': 1.1,
'old': 1.3,
'very-old': 1.5
};
baseMin *= ageMult[homeAge] || 1.0;
baseMax *= ageMult[homeAge] || 1.0;
// Emergency multiplier
if (serviceType === 'emergency') {
baseMin *= 1.5;
baseMax *= 1.5;
}
// Service call fee
let serviceFee = 75;
if (serviceType === 'emergency') serviceFee = 150;
// Add-ons
let addons = 0;
if (addonsSection.checkbox('permit')?.value()) addons += 150;
if (addonsSection.checkbox('diagnosis')?.value()) addons += 100;
if (addonsSection.checkbox('surgeProtection')?.value()) addons += 300;
if (addonsSection.checkbox('grounding')?.value()) addons += 200;
if (addonsSection.checkbox('arcFault')?.value()) addons += 50 * quantity;
if (addonsSection.checkbox('gfci')?.value()) addons += 30 * quantity;
const travelFee = getTravelFee();
const totalMin = serviceFee + baseMin + addons + travelFee;
const totalMax = serviceFee + baseMax + addons + travelFee;
return {
serviceFee: Math.round(serviceFee),
laborMin: Math.round(baseMin),
laborMax: Math.round(baseMax),
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 & Materials Estimate',
computedValue: () => `$${calculateQuote().laborMin.toLocaleString()} - $${calculateQuote().laborMax.toLocaleString()}`,
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.toLocaleString()} - $${calculateQuote().totalMax.toLocaleString()}`,
customStyles: { 'font-size': '2rem', 'font-weight': '700', 'color': '#059669', 'text-align': 'center' }
});
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Estimate includes typical materials. Complex jobs may require on-site evaluation.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Request Electrician'
});
}