export function solarPanelInstallationCalculator(form: FormTs) {
// System size pricing per watt (before incentives)
const pricePerWatt: Record<string, number> = {
'standard': 2.75,
'premium': 3.25,
'high-efficiency': 3.75
};
// Roof type adjustments
const roofTypeMultipliers: Record<string, number> = {
'asphalt-shingle': 1.0,
'metal': 1.1,
'tile': 1.25,
'flat': 1.15,
'slate': 1.35
};
// Battery storage costs
const batteryPricing: Record<string, number> = {
'none': 0,
'basic': 8000,
'standard': 12000,
'premium': 18000
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Solar Panel Installation Estimate',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Service Location Section
const locationSection = form.addSubform('serviceLocation', { title: '📍 Installation Location' });
locationSection.addRow(row => {
row.addAddress('propertyAddress', {
label: 'Property Address',
placeholder: 'Enter your 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 <= 30) return '📍 Within service area - No travel fee';
if (miles <= 60) return '📍 Extended area - $200 travel fee';
if (miles <= 100) return '📍 Remote area - $400 travel fee';
return '📍 Outside service area - Please call for availability';
},
customStyles: { 'font-size': '0.9rem', 'color': '#0369a1', 'background': '#e0f2fe', 'padding': '10px', 'border-radius': '6px' }
});
});
// Property Details Section
const propertySection = form.addSubform('propertyDetails', { title: '🏠 Property Details' });
propertySection.addRow(row => {
row.addDropdown('roofType', {
label: 'Roof Type',
options: [
{ id: 'asphalt-shingle', name: 'Asphalt Shingle' },
{ id: 'metal', name: 'Metal Roof' },
{ id: 'tile', name: 'Tile Roof' },
{ id: 'flat', name: 'Flat Roof' },
{ id: 'slate', name: 'Slate Roof' }
],
defaultValue: 'asphalt-shingle',
isRequired: true
}, '1fr');
row.addDropdown('roofAge', {
label: 'Roof Age',
options: [
{ id: 'new', name: 'Less than 5 years' },
{ id: 'moderate', name: '5-15 years' },
{ id: 'old', name: '15-25 years' },
{ id: 'very-old', name: 'Over 25 years' }
],
defaultValue: 'moderate',
isRequired: true
}, '1fr');
});
propertySection.addRow(row => {
row.addInteger('monthlyBill', {
label: 'Average Monthly Electric Bill ($)',
min: 50,
max: 1000,
defaultValue: 150,
placeholder: 'e.g. 150',
isRequired: true
}, '1fr');
row.addDropdown('sunExposure', {
label: 'Sun Exposure',
options: [
{ id: 'excellent', name: 'Excellent (Full sun)' },
{ id: 'good', name: 'Good (Some shade)' },
{ id: 'moderate', name: 'Moderate (Partial shade)' },
{ id: 'limited', name: 'Limited (Significant shade)' }
],
defaultValue: 'good',
isRequired: true
}, '1fr');
});
// System Configuration Section
const systemSection = form.addSubform('systemConfig', { title: '⚡ System Configuration' });
systemSection.addRow(row => {
row.addRadioButton('panelQuality', {
label: 'Panel Quality',
options: [
{ id: 'standard', name: 'Standard (18% efficiency)' },
{ id: 'premium', name: 'Premium (20% efficiency)' },
{ id: 'high-efficiency', name: 'High-Efficiency (22%+ efficiency)' }
],
defaultValue: 'standard',
orientation: 'vertical',
isRequired: true
});
});
systemSection.addRow(row => {
row.addSlider('systemSize', {
label: 'System Size (kW)',
min: 3,
max: 15,
step: 0.5,
defaultValue: 6,
showValue: true
});
});
systemSection.addRow(row => {
row.addRadioButton('batteryStorage', {
label: 'Battery Storage',
options: [
{ id: 'none', name: 'No Battery' },
{ id: 'basic', name: 'Basic (5 kWh) +$8,000' },
{ id: 'standard', name: 'Standard (10 kWh) +$12,000' },
{ id: 'premium', name: 'Premium (15 kWh) +$18,000' }
],
defaultValue: 'none',
orientation: 'vertical',
isRequired: true
});
});
// Additional Options Section
const addonsSection = form.addSubform('addons', { title: '✨ Additional Options' });
addonsSection.addRow(row => {
row.addCheckbox('monitoring', {
label: 'Smart Monitoring System (+$500)',
defaultValue: true
}, '1fr');
row.addCheckbox('evCharger', {
label: 'EV Charger Ready Wiring (+$800)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('critterGuard', {
label: 'Critter Guard (+$400)',
defaultValue: false
}, '1fr');
row.addCheckbox('extendedWarranty', {
label: 'Extended Warranty (+$1,200)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('roofReinforcement', {
label: 'Roof Reinforcement (if needed) (+$2,500)',
defaultValue: false,
isVisible: () => {
const roofAge = propertySection.dropdown('roofAge')?.value();
return roofAge === 'old' || roofAge === 'very-old';
}
}, '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 <= 30) return 0;
if (miles <= 60) return 200;
if (miles <= 100) return 400;
return 600;
};
// Price Summary Section
const summarySection = form.addSubform('summary', { title: '💰 Cost Breakdown', isCollapsible: false });
summarySection.addRow(row => {
row.addPriceDisplay('systemCost', {
label: 'Solar Panel System',
computedValue: () => {
const panelQuality = systemSection.radioButton('panelQuality')?.value() || 'standard';
const systemSize = systemSection.slider('systemSize')?.value() || 6;
const roofType = propertySection.dropdown('roofType')?.value() || 'asphalt-shingle';
const basePrice = pricePerWatt[panelQuality] || 2.75;
const roofMultiplier = roofTypeMultipliers[roofType] || 1.0;
const systemWatts = systemSize * 1000;
return Math.round(systemWatts * basePrice * roofMultiplier);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('batteryCost', {
label: 'Battery Storage',
computedValue: () => {
const battery = systemSection.radioButton('batteryStorage')?.value() || 'none';
return batteryPricing[battery] || 0;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('addonsCost', {
label: 'Additional Options',
computedValue: () => {
let total = 0;
if (addonsSection.checkbox('monitoring')?.value()) total += 500;
if (addonsSection.checkbox('evCharger')?.value()) total += 800;
if (addonsSection.checkbox('critterGuard')?.value()) total += 400;
if (addonsSection.checkbox('extendedWarranty')?.value()) total += 1200;
if (addonsSection.checkbox('roofReinforcement')?.value()) total += 2500;
return total;
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => getTravelFee(),
variant: 'default',
prefix: '+',
isVisible: () => getTravelFee() > 0
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('federalTaxCredit', {
label: 'Federal Tax Credit (30%)',
computedValue: () => {
const panelQuality = systemSection.radioButton('panelQuality')?.value() || 'standard';
const systemSize = systemSection.slider('systemSize')?.value() || 6;
const roofType = propertySection.dropdown('roofType')?.value() || 'asphalt-shingle';
const battery = systemSection.radioButton('batteryStorage')?.value() || 'none';
const basePrice = pricePerWatt[panelQuality] || 2.75;
const roofMultiplier = roofTypeMultipliers[roofType] || 1.0;
const systemWatts = systemSize * 1000;
const systemCost = systemWatts * basePrice * roofMultiplier;
const batteryCost = batteryPricing[battery] || 0;
let addonsCost = 0;
if (addonsSection.checkbox('monitoring')?.value()) addonsCost += 500;
if (addonsSection.checkbox('evCharger')?.value()) addonsCost += 800;
if (addonsSection.checkbox('critterGuard')?.value()) addonsCost += 400;
if (addonsSection.checkbox('extendedWarranty')?.value()) addonsCost += 1200;
if (addonsSection.checkbox('roofReinforcement')?.value()) addonsCost += 2500;
const travelFee = getTravelFee();
const totalBeforeCredit = systemCost + batteryCost + addonsCost + travelFee;
return -Math.round(totalBeforeCredit * 0.30);
},
variant: 'success',
prefix: ''
});
});
const finalSection = form.addSubform('final', {
title: '☀️ Your Estimate',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalCost', {
label: 'Estimated Cost After Incentives',
computedValue: () => {
const panelQuality = systemSection.radioButton('panelQuality')?.value() || 'standard';
const systemSize = systemSection.slider('systemSize')?.value() || 6;
const roofType = propertySection.dropdown('roofType')?.value() || 'asphalt-shingle';
const battery = systemSection.radioButton('batteryStorage')?.value() || 'none';
const basePrice = pricePerWatt[panelQuality] || 2.75;
const roofMultiplier = roofTypeMultipliers[roofType] || 1.0;
const systemWatts = systemSize * 1000;
const systemCost = systemWatts * basePrice * roofMultiplier;
const batteryCost = batteryPricing[battery] || 0;
let addonsCost = 0;
if (addonsSection.checkbox('monitoring')?.value()) addonsCost += 500;
if (addonsSection.checkbox('evCharger')?.value()) addonsCost += 800;
if (addonsSection.checkbox('critterGuard')?.value()) addonsCost += 400;
if (addonsSection.checkbox('extendedWarranty')?.value()) addonsCost += 1200;
if (addonsSection.checkbox('roofReinforcement')?.value()) addonsCost += 2500;
const travelFee = getTravelFee();
const totalBeforeCredit = systemCost + batteryCost + addonsCost + travelFee;
const federalCredit = totalBeforeCredit * 0.30;
return Math.round(totalBeforeCredit - federalCredit);
},
variant: 'large'
}, '1fr');
row.addPriceDisplay('estimatedSavings', {
label: 'Estimated Annual Savings',
computedValue: () => {
const monthlyBill = propertySection.integer('monthlyBill')?.value() || 150;
const sunExposure = propertySection.dropdown('sunExposure')?.value() || 'good';
const exposureMultiplier: Record<string, number> = {
'excellent': 0.95,
'good': 0.85,
'moderate': 0.70,
'limited': 0.50
};
const offsetPercent = exposureMultiplier[sunExposure] || 0.85;
return Math.round(monthlyBill * 12 * offsetPercent);
},
variant: 'success',
suffix: '/year'
}, '1fr');
});
finalSection.addRow(row => {
row.addTextPanel('payback', {
computedValue: () => {
const panelQuality = systemSection.radioButton('panelQuality')?.value() || 'standard';
const systemSize = systemSection.slider('systemSize')?.value() || 6;
const roofType = propertySection.dropdown('roofType')?.value() || 'asphalt-shingle';
const battery = systemSection.radioButton('batteryStorage')?.value() || 'none';
const monthlyBill = propertySection.integer('monthlyBill')?.value() || 150;
const sunExposure = propertySection.dropdown('sunExposure')?.value() || 'good';
const basePrice = pricePerWatt[panelQuality] || 2.75;
const roofMultiplier = roofTypeMultipliers[roofType] || 1.0;
const systemWatts = systemSize * 1000;
const systemCost = systemWatts * basePrice * roofMultiplier;
const batteryCost = batteryPricing[battery] || 0;
let addonsCost = 0;
if (addonsSection.checkbox('monitoring')?.value()) addonsCost += 500;
if (addonsSection.checkbox('evCharger')?.value()) addonsCost += 800;
if (addonsSection.checkbox('critterGuard')?.value()) addonsCost += 400;
if (addonsSection.checkbox('extendedWarranty')?.value()) addonsCost += 1200;
if (addonsSection.checkbox('roofReinforcement')?.value()) addonsCost += 2500;
const travelFee = getTravelFee();
const totalCost = (systemCost + batteryCost + addonsCost + travelFee) * 0.70;
const exposureMultiplier: Record<string, number> = {
'excellent': 0.95,
'good': 0.85,
'moderate': 0.70,
'limited': 0.50
};
const annualSavings = monthlyBill * 12 * (exposureMultiplier[sunExposure] || 0.85);
const paybackYears = Math.round(totalCost / annualSavings * 10) / 10;
return `Estimated payback period: ${paybackYears} years`;
},
customStyles: { 'font-size': '0.95rem', 'color': '#059669', 'font-weight': '500' }
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Estimates based on average costs. Final price depends on site assessment. Additional state/local incentives may apply.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Get Free Quote'
});
}