export function landscapingQuoteForm(form: FormTs) {
form.setTitle(() => '๐ฟ Get Your Lawn Care Quote ๐ก');
form.configureCompletionScreen({
type: 'text',
title: () => '๐ Quote Ready!',
message: () => 'Your lawn care quote has been generated. Download your PDF to save the details.'
});
// Pricing data
const sizeRates: Record<string, number> = {
'small': 35, // up to 5,000 sq ft
'medium': 55, // 5,001-10,000 sq ft
'large': 85, // 10,001-20,000 sq ft
'xlarge': 120 // 20,000+ sq ft
};
const serviceMultipliers: Record<string, number> = {
'mowing': 1.0,
'full-care': 2.0,
'seasonal-cleanup': 1.5
};
const frequencyDiscounts: Record<string, number> = {
'one-time': 0,
'weekly': 20,
'bi-weekly': 15,
'monthly': 10
};
// Add-on prices
const addonPrices: Record<string, number> = {
'edging': 15,
'weedControl': 25,
'fertilizing': 40,
'leafRemoval': 50,
'hedgeTrimming': 45,
'mulching': 60,
'aeration': 75,
'overseeding': 55
};
// Property Details
const property = form.addSubform('property', {
title: () => '๐ Property Details',
mobileBreakpoint: 0
});
property.addRow(row => {
row.addDropdown('size', {
label: 'Lawn Size',
defaultValue: 'medium',
isRequired: true,
options: [
{ id: 'small', name: '๐ฑ Small (up to 5,000 sq ft)' },
{ id: 'medium', name: '๐ฟ Medium (5,001-10,000 sq ft)' },
{ id: 'large', name: '๐ณ Large (10,001-20,000 sq ft)' },
{ id: 'xlarge', name: '๐๏ธ Extra Large (20,000+ sq ft)' }
]
}, '1fr');
row.addDropdown('type', {
label: 'Property Type',
defaultValue: 'residential',
isRequired: true,
options: [
{ id: 'residential', name: '๐ก Residential' },
{ id: 'commercial', name: '๐ข Commercial' }
]
}, '1fr');
});
property.addRow(row => {
row.addDropdown('terrain', {
label: 'Terrain',
defaultValue: 'flat',
isRequired: true,
options: [
{ id: 'flat', name: 'โ Flat' },
{ id: 'sloped', name: '๐ Sloped (+15%)' },
{ id: 'complex', name: '๐ Complex (+25%)' }
]
}, '1fr');
row.addDropdown('condition', {
label: 'Current Condition',
defaultValue: 'maintained',
isRequired: true,
options: [
{ id: 'maintained', name: 'โจ Well Maintained' },
{ id: 'overgrown', name: '๐พ Overgrown (+20%)' },
{ id: 'neglected', name: '๐ฟ Neglected (+40%)' }
]
}, '1fr');
});
// Service Options
const options = form.addSubform('options', {
title: () => '๐ฑ Service Options',
mobileBreakpoint: 0
});
options.addRow(row => {
row.addRadioButton('serviceType', {
label: 'Service Type',
defaultValue: 'mowing',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'mowing', name: '๐ Mowing Only' },
{ id: 'full-care', name: '๐ Full Lawn Care' },
{ id: 'seasonal-cleanup', name: '๐ Seasonal Cleanup' }
]
});
row.addRadioButton('frequency', {
label: 'Frequency',
defaultValue: 'bi-weekly',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'one-time', name: '1๏ธโฃ One-Time' },
{ id: 'weekly', name: '๐ Weekly' },
{ id: 'bi-weekly', name: '๐๏ธ Bi-Weekly' },
{ id: 'monthly', name: '๐
Monthly' }
]
});
});
// Add-ons as CheckboxList
const addons = form.addSubform('addons', {
title: () => 'โ Add-On Services',
mobileBreakpoint: 500
});
addons.addRow(row => {
row.addCheckboxList('lawnExtras', {
label: 'Lawn Care Extras',
orientation: 'vertical',
options: [
{ id: 'edging', name: 'โ๏ธ Edging' },
{ id: 'weedControl', name: '๐ฟ Weed Control' },
{ id: 'fertilizing', name: '๐งช Fertilizing' },
{ id: 'aeration', name: '๐จ Aeration' }
]
}, '1fr');
row.addCheckboxList('yardExtras', {
label: 'Yard Extras',
orientation: 'vertical',
options: [
{ id: 'leafRemoval', name: '๐ Leaf Removal' },
{ id: 'hedgeTrimming', name: '๐ฒ Hedge Trimming' },
{ id: 'mulching', name: '๐ชต Mulching' },
{ id: 'overseeding', name: '๐พ Overseeding' }
]
}, '1fr');
});
// Quote Summary
const summary = form.addSubform('summary', {
title: () => '๐ฐ Your Quote',
isCollapsible: false,
});
// Calculate base price
const getBasePrice = () => {
const size = property.dropdown('size')?.value() || 'medium';
const terrain = property.dropdown('terrain')?.value() || 'flat';
const condition = property.dropdown('condition')?.value() || 'maintained';
const propertyType = property.dropdown('type')?.value() || 'residential';
let baseRate = sizeRates[size] || 55;
// Terrain multiplier
if (terrain === 'sloped') baseRate *= 1.15;
if (terrain === 'complex') baseRate *= 1.25;
// Condition multiplier
if (condition === 'overgrown') baseRate *= 1.20;
if (condition === 'neglected') baseRate *= 1.40;
// Commercial premium
if (propertyType === 'commercial') baseRate *= 1.25;
return Math.round(baseRate);
};
// Calculate add-ons total
const getAddonsTotal = () => {
let total = 0;
const lawnExtras = addons.checkboxList('lawnExtras')?.value() || [];
const yardExtras = addons.checkboxList('yardExtras')?.value() || [];
const allSelected = [...lawnExtras, ...yardExtras];
for (const addonId of allSelected) {
total += addonPrices[addonId] || 0;
}
return total;
};
// Get selected add-ons for display
const getSelectedAddons = () => {
const lawnExtras = addons.checkboxList('lawnExtras')?.value() || [];
const yardExtras = addons.checkboxList('yardExtras')?.value() || [];
return [...lawnExtras, ...yardExtras];
};
// Calculate final price
const getFinalPrice = () => {
const serviceType = options.radioButton('serviceType')?.value() || 'mowing';
const frequency = options.radioButton('frequency')?.value() || 'bi-weekly';
const basePrice = getBasePrice();
const multiplier = serviceMultipliers[serviceType] || 1.0;
const adjustedBase = basePrice * multiplier;
const addonsTotal = getAddonsTotal();
const subtotal = adjustedBase + addonsTotal;
const discountPercent = frequencyDiscounts[frequency] || 0;
const discount = subtotal * (discountPercent / 100);
return Math.round(subtotal - discount);
};
// Get discount amount
const getDiscountAmount = () => {
const serviceType = options.radioButton('serviceType')?.value() || 'mowing';
const frequency = options.radioButton('frequency')?.value() || 'bi-weekly';
const basePrice = getBasePrice();
const multiplier = serviceMultipliers[serviceType] || 1.0;
const adjustedBase = basePrice * multiplier;
const addonsTotal = getAddonsTotal();
const subtotal = adjustedBase + addonsTotal;
const discountPercent = frequencyDiscounts[frequency] || 0;
return Math.round(subtotal * (discountPercent / 100));
};
summary.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Estimated Price',
computedValue: () => getFinalPrice(),
alignment: 'center',
variant: 'large',
suffix: () => {
const frequency = options.radioButton('frequency')?.value();
if (frequency === 'weekly') return '/week';
if (frequency === 'bi-weekly') return '/visit';
if (frequency === 'monthly') return '/month';
return '';
}
});
});
summary.addRow(row => {
row.addTextPanel('savings', {
computedValue: () => {
const frequency = options.radioButton('frequency')?.value() || 'one-time';
const discountPercent = frequencyDiscounts[frequency] || 0;
if (discountPercent > 0) {
return `๐ You save ${discountPercent}% with ${frequency} service!`;
}
return '๐ก Choose recurring service to save up to 20%';
},
customStyles: {
fontSize: '0.9rem',
color: '#059669',
fontWeight: '500',
textAlign: 'center'
}
});
});
form.configureSubmitButton({
label: () => 'Get My Quote'
});
// Configure PDF generation
form.configurePdf('quoteDocument', (pdf) => {
pdf.configure({
filename: 'lawn-care-quote.pdf',
pageSize: 'A4',
allowUserDownload: true,
downloadButtonLabel: 'Download Your Quote',
header: {
title: 'Lawn Care Service Quote',
subtitle: 'Professional Landscaping Services'
},
footer: {
text: 'Thank you for choosing our lawn care services!',
showPageNumbers: true
}
});
// Property Summary Section
pdf.addSection('Property Details', (section) => {
section.addRow((row) => {
const sizeMap: Record<string, string> = {
'small': 'Small (up to 5,000 sq ft)',
'medium': 'Medium (5,001-10,000 sq ft)',
'large': 'Large (10,001-20,000 sq ft)',
'xlarge': 'Extra Large (20,000+ sq ft)'
};
const size = property.dropdown('size')?.value() || 'medium';
row.addField('Lawn Size', sizeMap[size] || size);
const typeMap: Record<string, string> = {
'residential': 'Residential',
'commercial': 'Commercial'
};
const type = property.dropdown('type')?.value() || 'residential';
row.addField('Property Type', typeMap[type] || type);
});
section.addRow((row) => {
const terrainMap: Record<string, string> = {
'flat': 'Flat',
'sloped': 'Sloped',
'complex': 'Complex'
};
const terrain = property.dropdown('terrain')?.value() || 'flat';
row.addField('Terrain', terrainMap[terrain] || terrain);
const conditionMap: Record<string, string> = {
'maintained': 'Well Maintained',
'overgrown': 'Overgrown',
'neglected': 'Neglected'
};
const condition = property.dropdown('condition')?.value() || 'maintained';
row.addField('Condition', conditionMap[condition] || condition);
});
});
// Service Options Section
pdf.addSection('Service Details', (section) => {
section.addRow((row) => {
const serviceTypeMap: Record<string, string> = {
'mowing': 'Mowing Only',
'full-care': 'Full Lawn Care',
'seasonal-cleanup': 'Seasonal Cleanup'
};
const type = options.radioButton('serviceType')?.value() || 'mowing';
row.addField('Service Type', serviceTypeMap[type] || type);
});
section.addRow((row) => {
const frequencyMap: Record<string, string> = {
'one-time': 'One-Time Service',
'weekly': 'Weekly',
'bi-weekly': 'Bi-Weekly',
'monthly': 'Monthly'
};
const freq = options.radioButton('frequency')?.value() || 'bi-weekly';
row.addField('Frequency', frequencyMap[freq] || freq);
});
});
// Add-ons Section (only if any selected)
const selectedAddons = getSelectedAddons();
if (selectedAddons.length > 0) {
pdf.addSection('Add-On Services', (section) => {
const addonLabels: Record<string, string> = {
'edging': 'Edging',
'weedControl': 'Weed Control',
'fertilizing': 'Fertilizing',
'aeration': 'Aeration',
'leafRemoval': 'Leaf Removal',
'hedgeTrimming': 'Hedge Trimming',
'mulching': 'Mulching',
'overseeding': 'Overseeding'
};
const tableRows = selectedAddons.map(addonId => [
addonLabels[addonId] || addonId,
`$${addonPrices[addonId] || 0}`
]);
section.addTable(['Service', 'Price'], tableRows);
section.addSpacer(10);
section.addRow((row) => {
row.addField('Add-ons Subtotal', `$${getAddonsTotal()}`);
});
});
}
// Pricing Summary Section
pdf.addSection('Quote Summary', (section) => {
const serviceType = options.radioButton('serviceType')?.value() || 'mowing';
const frequency = options.radioButton('frequency')?.value() || 'bi-weekly';
const multiplier = serviceMultipliers[serviceType] || 1.0;
const basePrice = getBasePrice();
const adjustedBase = Math.round(basePrice * multiplier);
section.addRow((row) => {
row.addField('Base Service Price', `$${adjustedBase}`);
});
if (selectedAddons.length > 0) {
section.addRow((row) => {
row.addField('Add-ons Total', `$${getAddonsTotal()}`);
});
}
const discountAmount = getDiscountAmount();
if (discountAmount > 0) {
const discountPercent = frequencyDiscounts[frequency] || 0;
section.addRow((row) => {
row.addField(`Discount (${discountPercent}% ${frequency})`, `-$${discountAmount}`);
});
}
section.addDivider();
section.addSpacer(10);
const suffix = frequency === 'weekly' ? '/week'
: frequency === 'bi-weekly' ? '/visit'
: frequency === 'monthly' ? '/month'
: '';
section.addRow((row) => {
row.addField('TOTAL', `$${getFinalPrice()}${suffix}`);
});
if (discountAmount > 0) {
section.addSpacer(10);
section.addText(`You're saving $${discountAmount} with your ${frequency} service!`);
}
});
// Terms Section
pdf.addSection('Terms & Conditions', (section) => {
section.addText('โข This quote is valid for 30 days from the date of generation.');
section.addText('โข Prices may vary based on actual property condition upon inspection.');
section.addText('โข Cancellations require 24-hour advance notice.');
section.addText('โข Recurring service discounts apply to the total including add-ons.');
section.addSpacer(10);
section.addText('Questions? Contact us at support@lawnservice.com');
});
});
}