export function drivewayPavingCalculator(form: FormTs) {
// Pricing data per square foot
const materialPrices: Record<string, { low: number; high: number }> = {
'asphalt': { low: 3, high: 7 },
'concrete': { low: 6, high: 12 },
'pavers': { low: 10, high: 25 },
'gravel': { low: 1, high: 3 },
'stamped-concrete': { low: 12, high: 20 },
'exposed-aggregate': { low: 8, high: 15 },
'heated': { low: 15, high: 30 },
'permeable': { low: 8, high: 18 }
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Driveway Paving Cost Calculator',
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 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' }
});
});
// Driveway Dimensions Section
const dimensionsSection = form.addSubform('dimensions', { title: '📐 Driveway Dimensions' });
dimensionsSection.addRow(row => {
row.addInteger('length', {
label: 'Length (feet)',
min: 10,
max: 500,
defaultValue: 40,
isRequired: true,
tooltip: 'Typical driveway is 30-50 feet'
}, '1fr');
row.addInteger('width', {
label: 'Width (feet)',
min: 8,
max: 50,
defaultValue: 12,
isRequired: true,
tooltip: 'Single car: 10-12ft, Double: 20-24ft'
}, '1fr');
});
dimensionsSection.addRow(row => {
row.addPriceDisplay('squareFootage', {
label: 'Total Square Footage',
computedValue: () => {
const length = dimensionsSection.integer('length')?.value() || 40;
const width = dimensionsSection.integer('width')?.value() || 12;
return length * width;
},
variant: 'default',
suffix: ' sq ft'
}, '1fr');
row.addDropdown('shape', {
label: 'Driveway Shape',
options: [
{ id: 'straight', name: 'Straight' },
{ id: 'curved', name: 'Curved (+10%)' },
{ id: 'circular', name: 'Circular (+20%)' },
{ id: 'custom', name: 'Custom/Complex (+25%)' }
],
defaultValue: 'straight'
}, '1fr');
});
// Material Selection Section
const materialSection = form.addSubform('material', { title: '🧱 Material Selection' });
materialSection.addRow(row => {
row.addDropdown('materialType', {
label: 'Paving Material',
options: [
{ id: 'asphalt', name: 'Asphalt ($3-7/sqft)' },
{ id: 'concrete', name: 'Concrete ($6-12/sqft)' },
{ id: 'pavers', name: 'Brick/Stone Pavers ($10-25/sqft)' },
{ id: 'gravel', name: 'Gravel ($1-3/sqft)' },
{ id: 'stamped-concrete', name: 'Stamped Concrete ($12-20/sqft)' },
{ id: 'exposed-aggregate', name: 'Exposed Aggregate ($8-15/sqft)' },
{ id: 'heated', name: 'Heated Driveway ($15-30/sqft)' },
{ id: 'permeable', name: 'Permeable Pavers ($8-18/sqft)' }
],
defaultValue: 'concrete',
isRequired: true
}, '1fr');
row.addDropdown('qualityTier', {
label: 'Quality Tier',
options: [
{ id: 'economy', name: 'Economy (Lower end)' },
{ id: 'standard', name: 'Standard (Mid-range)' },
{ id: 'premium', name: 'Premium (Higher end)' }
],
defaultValue: 'standard'
}, '1fr');
});
materialSection.addRow(row => {
row.addDropdown('thickness', {
label: 'Material Thickness',
options: [
{ id: 'standard', name: 'Standard (4" concrete, 2" asphalt)' },
{ id: 'heavy-duty', name: 'Heavy Duty (+30% for trucks/RVs)' }
],
defaultValue: 'standard'
}, '1fr');
row.addDropdown('paverStyle', {
label: 'Paver Style',
options: [
{ id: 'basic', name: 'Basic Pattern' },
{ id: 'herringbone', name: 'Herringbone (+10%)' },
{ id: 'basket-weave', name: 'Basket Weave (+15%)' },
{ id: 'custom', name: 'Custom Pattern (+25%)' }
],
defaultValue: 'basic',
isVisible: () => {
const mat = materialSection.dropdown('materialType')?.value();
return mat === 'pavers';
}
}, '1fr');
});
// Site Preparation Section
const siteSection = form.addSubform('site', { title: '🚜 Site Preparation' });
siteSection.addRow(row => {
row.addDropdown('existingCondition', {
label: 'Existing Surface',
options: [
{ id: 'bare', name: 'Bare Ground (minimal prep)' },
{ id: 'gravel', name: 'Existing Gravel' },
{ id: 'asphalt', name: 'Old Asphalt (needs removal)' },
{ id: 'concrete', name: 'Old Concrete (needs removal)' },
{ id: 'pavers', name: 'Old Pavers (needs removal)' }
],
defaultValue: 'bare'
}, '1fr');
row.addDropdown('slope', {
label: 'Ground Slope',
options: [
{ id: 'flat', name: 'Flat/Minimal Slope' },
{ id: 'slight', name: 'Slight Slope (+5%)' },
{ id: 'moderate', name: 'Moderate Slope (+15%)' },
{ id: 'steep', name: 'Steep Slope (+30%)' }
],
defaultValue: 'flat'
}, '1fr');
});
siteSection.addRow(row => {
row.addCheckbox('excavation', {
label: 'Excavation Required',
defaultValue: true,
tooltip: '+$2-4/sqft for proper base excavation'
}, '1fr');
row.addCheckbox('grading', {
label: 'Grading/Leveling',
defaultValue: true,
tooltip: '+$1-3/sqft for proper drainage slope'
}, '1fr');
});
siteSection.addRow(row => {
row.addCheckbox('subBase', {
label: 'Compacted Sub-Base',
defaultValue: true,
tooltip: '+$1-2/sqft for crushed stone base'
}, '1fr');
row.addCheckbox('geotextile', {
label: 'Geotextile Fabric',
defaultValue: false,
tooltip: '+$0.50/sqft for weed barrier and stability'
}, '1fr');
});
// Features & Additions Section
const featuresSection = form.addSubform('features', { title: '✨ Features & Additions' });
featuresSection.addRow(row => {
row.addCheckbox('edging', {
label: 'Decorative Edging/Border',
defaultValue: false,
tooltip: '+$5-15/linear foot for border'
}, '1fr');
row.addCheckbox('apron', {
label: 'Driveway Apron (street connection)',
defaultValue: false,
tooltip: '+$500-1,500 for apron reconstruction'
}, '1fr');
});
featuresSection.addRow(row => {
row.addCheckbox('sealing', {
label: 'Sealcoating (asphalt)',
defaultValue: false,
tooltip: '+$0.20-0.40/sqft for protective sealant',
isVisible: () => materialSection.dropdown('materialType')?.value() === 'asphalt'
}, '1fr');
row.addCheckbox('coloring', {
label: 'Color/Stain',
defaultValue: false,
tooltip: '+$2-4/sqft for decorative coloring'
}, '1fr');
});
featuresSection.addRow(row => {
row.addCheckbox('drainage', {
label: 'Drainage System',
defaultValue: false,
tooltip: '+$1,000-3,000 for channel drains'
}, '1fr');
row.addCheckbox('lighting', {
label: 'Driveway Lighting',
defaultValue: false,
tooltip: '+$500-2,000 for solar or wired lighting'
}, '1fr');
});
// Turnaround & Parking Section
const extrasSection = form.addSubform('extras', { title: '🚗 Additional Areas' });
extrasSection.addRow(row => {
row.addCheckbox('turnaround', {
label: 'Add Turnaround Area',
defaultValue: false,
tooltip: 'Extra 200-400 sqft at same material cost'
}, '1fr');
row.addInteger('turnaroundSize', {
label: 'Turnaround Size (sqft)',
min: 100,
max: 800,
defaultValue: 300,
isVisible: () => extrasSection.checkbox('turnaround')?.value()
}, '1fr');
});
extrasSection.addRow(row => {
row.addCheckbox('parkingPad', {
label: 'Add Parking Pad',
defaultValue: false,
tooltip: 'Extra parking area beside driveway'
}, '1fr');
row.addInteger('parkingSize', {
label: 'Parking Pad Size (sqft)',
min: 100,
max: 1000,
defaultValue: 400,
isVisible: () => extrasSection.checkbox('parkingPad')?.value()
}, '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;
};
// Cost Breakdown Section
const breakdownSection = form.addSubform('breakdown', { title: '📊 Cost Breakdown', isCollapsible: false });
breakdownSection.addRow(row => {
row.addPriceDisplay('materialCost', {
label: 'Materials & Installation',
computedValue: () => {
const length = dimensionsSection.integer('length')?.value() || 40;
const width = dimensionsSection.integer('width')?.value() || 12;
const sqft = length * width;
const material = materialSection.dropdown('materialType')?.value() || 'concrete';
const quality = materialSection.dropdown('qualityTier')?.value() || 'standard';
const shape = dimensionsSection.dropdown('shape')?.value() || 'straight';
const thickness = materialSection.dropdown('thickness')?.value() || 'standard';
const prices = materialPrices[material] || { low: 6, high: 12 };
let pricePerSqft = prices.low;
if (quality === 'standard') pricePerSqft = (prices.low + prices.high) / 2;
else if (quality === 'premium') pricePerSqft = prices.high;
// Shape multiplier
const shapeMults: Record<string, number> = {
'straight': 1, 'curved': 1.1, 'circular': 1.2, 'custom': 1.25
};
pricePerSqft *= shapeMults[shape] || 1;
// Thickness multiplier
if (thickness === 'heavy-duty') pricePerSqft *= 1.3;
// Paver style
if (material === 'pavers') {
const style = materialSection.dropdown('paverStyle')?.value() || 'basic';
const styleMults: Record<string, number> = {
'basic': 1, 'herringbone': 1.1, 'basket-weave': 1.15, 'custom': 1.25
};
pricePerSqft *= styleMults[style] || 1;
}
return Math.round(sqft * pricePerSqft);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('sitePrepCost', {
label: 'Site Preparation',
computedValue: () => {
const length = dimensionsSection.integer('length')?.value() || 40;
const width = dimensionsSection.integer('width')?.value() || 12;
const sqft = length * width;
const existing = siteSection.dropdown('existingCondition')?.value() || 'bare';
const slope = siteSection.dropdown('slope')?.value() || 'flat';
let cost = 0;
// Removal costs
const removalCosts: Record<string, number> = {
'bare': 0, 'gravel': 0.5, 'asphalt': 2, 'concrete': 3, 'pavers': 2.5
};
cost += (removalCosts[existing] || 0) * sqft;
// Slope adjustment
const slopeMults: Record<string, number> = {
'flat': 1, 'slight': 1.05, 'moderate': 1.15, 'steep': 1.3
};
// Site work
if (siteSection.checkbox('excavation')?.value()) cost += sqft * 3;
if (siteSection.checkbox('grading')?.value()) cost += sqft * 2;
if (siteSection.checkbox('subBase')?.value()) cost += sqft * 1.5;
if (siteSection.checkbox('geotextile')?.value()) cost += sqft * 0.5;
cost *= slopeMults[slope] || 1;
return Math.round(cost);
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('featuresCost', {
label: 'Features & Additions',
computedValue: () => {
const length = dimensionsSection.integer('length')?.value() || 40;
const width = dimensionsSection.integer('width')?.value() || 12;
const sqft = length * width;
const perimeter = 2 * (length + width);
let cost = 0;
if (featuresSection.checkbox('edging')?.value()) cost += perimeter * 10;
if (featuresSection.checkbox('apron')?.value()) cost += 1000;
if (featuresSection.checkbox('sealing')?.value()) cost += sqft * 0.30;
if (featuresSection.checkbox('coloring')?.value()) cost += sqft * 3;
if (featuresSection.checkbox('drainage')?.value()) cost += 2000;
if (featuresSection.checkbox('lighting')?.value()) cost += 1200;
return Math.round(cost);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('extraAreasCost', {
label: 'Additional Areas',
computedValue: () => {
const material = materialSection.dropdown('materialType')?.value() || 'concrete';
const quality = materialSection.dropdown('qualityTier')?.value() || 'standard';
const prices = materialPrices[material] || { low: 6, high: 12 };
let pricePerSqft = prices.low;
if (quality === 'standard') pricePerSqft = (prices.low + prices.high) / 2;
else if (quality === 'premium') pricePerSqft = prices.high;
let cost = 0;
if (extrasSection.checkbox('turnaround')?.value()) {
const size = extrasSection.integer('turnaroundSize')?.value() || 300;
cost += size * pricePerSqft * 1.2; // includes site prep
}
if (extrasSection.checkbox('parkingPad')?.value()) {
const size = extrasSection.integer('parkingSize')?.value() || 400;
cost += size * pricePerSqft * 1.2;
}
return Math.round(cost);
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('travelFee', {
label: 'Travel Fee',
computedValue: () => getTravelFee(),
variant: 'default',
prefix: '+',
isVisible: () => getTravelFee() > 0
});
});
breakdownSection.addRow(row => {
row.addPriceDisplay('pricePerSqft', {
label: 'Effective Price/Sqft',
computedValue: () => {
const length = dimensionsSection.integer('length')?.value() || 40;
const width = dimensionsSection.integer('width')?.value() || 12;
const mainSqft = length * width;
let totalSqft = mainSqft;
if (extrasSection.checkbox('turnaround')?.value()) {
totalSqft += extrasSection.integer('turnaroundSize')?.value() || 300;
}
if (extrasSection.checkbox('parkingPad')?.value()) {
totalSqft += extrasSection.integer('parkingSize')?.value() || 400;
}
// Simplified total calculation
const material = materialSection.dropdown('materialType')?.value() || 'concrete';
const quality = materialSection.dropdown('qualityTier')?.value() || 'standard';
const prices = materialPrices[material] || { low: 6, high: 12 };
let pricePerSqft = prices.low;
if (quality === 'standard') pricePerSqft = (prices.low + prices.high) / 2;
else if (quality === 'premium') pricePerSqft = prices.high;
// Add typical site prep
pricePerSqft += 4;
return Math.round(pricePerSqft * 100) / 100;
},
variant: 'default'
}, '1fr');
row.addTextPanel('rangeNote', {
computedValue: () => {
const material = materialSection.dropdown('materialType')?.value() || 'concrete';
const prices = materialPrices[material] || { low: 6, high: 12 };
return `Material range: $${prices.low}-${prices.high}/sqft`;
},
customStyles: { 'font-size': '0.9rem', 'color': '#64748b', 'padding-top': '8px' }
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '🚗 Total Project Cost',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('totalCost', {
label: 'Estimated Total',
computedValue: () => {
const length = dimensionsSection.integer('length')?.value() || 40;
const width = dimensionsSection.integer('width')?.value() || 12;
const sqft = length * width;
const perimeter = 2 * (length + width);
const material = materialSection.dropdown('materialType')?.value() || 'concrete';
const quality = materialSection.dropdown('qualityTier')?.value() || 'standard';
const shape = dimensionsSection.dropdown('shape')?.value() || 'straight';
const thickness = materialSection.dropdown('thickness')?.value() || 'standard';
const existing = siteSection.dropdown('existingCondition')?.value() || 'bare';
const slope = siteSection.dropdown('slope')?.value() || 'flat';
// Material cost
const prices = materialPrices[material] || { low: 6, high: 12 };
let pricePerSqft = prices.low;
if (quality === 'standard') pricePerSqft = (prices.low + prices.high) / 2;
else if (quality === 'premium') pricePerSqft = prices.high;
const shapeMults: Record<string, number> = {
'straight': 1, 'curved': 1.1, 'circular': 1.2, 'custom': 1.25
};
pricePerSqft *= shapeMults[shape] || 1;
if (thickness === 'heavy-duty') pricePerSqft *= 1.3;
if (material === 'pavers') {
const style = materialSection.dropdown('paverStyle')?.value() || 'basic';
const styleMults: Record<string, number> = {
'basic': 1, 'herringbone': 1.1, 'basket-weave': 1.15, 'custom': 1.25
};
pricePerSqft *= styleMults[style] || 1;
}
let total = sqft * pricePerSqft;
// Site prep
const removalCosts: Record<string, number> = {
'bare': 0, 'gravel': 0.5, 'asphalt': 2, 'concrete': 3, 'pavers': 2.5
};
let siteCost = (removalCosts[existing] || 0) * sqft;
const slopeMults: Record<string, number> = {
'flat': 1, 'slight': 1.05, 'moderate': 1.15, 'steep': 1.3
};
if (siteSection.checkbox('excavation')?.value()) siteCost += sqft * 3;
if (siteSection.checkbox('grading')?.value()) siteCost += sqft * 2;
if (siteSection.checkbox('subBase')?.value()) siteCost += sqft * 1.5;
if (siteSection.checkbox('geotextile')?.value()) siteCost += sqft * 0.5;
total += siteCost * (slopeMults[slope] || 1);
// Features
if (featuresSection.checkbox('edging')?.value()) total += perimeter * 10;
if (featuresSection.checkbox('apron')?.value()) total += 1000;
if (featuresSection.checkbox('sealing')?.value()) total += sqft * 0.30;
if (featuresSection.checkbox('coloring')?.value()) total += sqft * 3;
if (featuresSection.checkbox('drainage')?.value()) total += 2000;
if (featuresSection.checkbox('lighting')?.value()) total += 1200;
// Extra areas
if (extrasSection.checkbox('turnaround')?.value()) {
const size = extrasSection.integer('turnaroundSize')?.value() || 300;
total += size * pricePerSqft * 1.2;
}
if (extrasSection.checkbox('parkingPad')?.value()) {
const size = extrasSection.integer('parkingSize')?.value() || 400;
total += size * pricePerSqft * 1.2;
}
const travelFee = getTravelFee();
return Math.round(total + travelFee);
},
variant: 'large'
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices vary by location, contractor, and site conditions. Get 3+ quotes from licensed contractors. This estimate excludes permits if required.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Get Paving Quotes'
});
}