export function cleaningQuoteForm(form: FormTs) {
form.setTitle(() => '๐งน Get Your Cleaning Quote โจ');
form.configureCompletionScreen({
type: 'text',
title: () => '๐ Quote Ready!',
message: () => 'Your cleaning quote has been generated. Download your PDF to save the details.'
});
// Pricing data
const baseRates: Record<string, number> = {
'apartment': 80,
'house': 120,
'condo': 95,
'townhouse': 110
};
const cleaningMultipliers: Record<string, number> = {
'standard': 1.0,
'deep': 1.5,
'move-out': 1.75
};
const frequencyDiscounts: Record<string, number> = {
'one-time': 0,
'weekly': 20,
'bi-weekly': 15,
'monthly': 10
};
// Add-on prices
const addonPrices: Record<string, number> = {
'insideOven': 35,
'insideFridge': 35,
'insideCabinets': 50,
'windowsInterior': 45,
'laundry': 30,
'dishes': 25,
'organizing': 60,
'petHairRemoval': 40
};
// Property Details
const property = form.addSubform('property', {
title: () => '๐ Property Details',
mobileBreakpoint: 0
});
property.addRow(row => {
row.addDropdown('type', {
label: 'Property Type',
defaultValue: 'house',
isRequired: true,
options: [
{ id: 'apartment', name: '๐ข Apartment' },
{ id: 'house', name: '๐ก House' },
{ id: 'condo', name: '๐ฌ Condo' },
{ id: 'townhouse', name: '๐๏ธ Townhouse' }
]
}, '1fr');
row.addInteger('sqft', {
label: 'Square Feet',
min: 500,
max: 5000,
defaultValue: 1500,
isRequired: true,
placeholder: 'e.g. 1500'
}, '1fr');
});
property.addRow(row => {
row.addInteger('bedrooms', {
label: 'Bedrooms',
min: 1,
max: 6,
defaultValue: 3,
isRequired: true
}, '1fr');
row.addInteger('bathrooms', {
label: 'Bathrooms',
min: 1,
max: 5,
defaultValue: 2,
isRequired: true
}, '1fr');
});
// Cleaning Options
const options = form.addSubform('options', {
title: () => '๐งผ Cleaning Options',
mobileBreakpoint: 0
});
options.addRow(row => {
row.addRadioButton('cleaningType', {
label: 'Cleaning Type',
defaultValue: 'standard',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'standard', name: '๐งน Standard' },
{ id: 'deep', name: '๐ฌ Deep Clean' },
{ id: 'move-out', name: '๐ฆ Move-Out' }
]
});
row.addRadioButton('frequency', {
label: 'Frequency',
defaultValue: 'one-time',
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('kitchenExtras', {
label: 'Kitchen Extras',
orientation: 'vertical',
options: [
{ id: 'insideOven', name: '๐ฅ Inside Oven' },
{ id: 'insideFridge', name: 'โ๏ธ Inside Fridge' },
{ id: 'insideCabinets', name: '๐๏ธ Inside Cabinets' },
{ id: 'dishes', name: '๐ฝ๏ธ Wash Dishes' }
]
}, '1fr');
row.addCheckboxList('homeExtras', {
label: 'Home Extras',
orientation: 'vertical',
options: [
{ id: 'windowsInterior', name: '๐ช Interior Windows' },
{ id: 'laundry', name: '๐งบ Laundry' },
{ id: 'organizing', name: '๐ Organizing' },
{ id: 'petHairRemoval', name: '๐พ Pet Hair Removal' }
]
}, '1fr');
});
// Quote Summary
const summary = form.addSubform('summary', {
title: () => '๐ฐ Your Quote',
isCollapsible: false,
});
// Calculate base price
const getBasePrice = () => {
const type = property.dropdown('type')?.value() || 'house';
const sqft = property.integer('sqft')?.value() || 1500;
const bedrooms = property.integer('bedrooms')?.value() || 3;
const bathrooms = property.integer('bathrooms')?.value() || 2;
const baseRate = baseRates[type] || 120;
const sqftCost = Math.floor(sqft / 500) * 15;
const roomCost = (bedrooms * 15) + (bathrooms * 20);
return baseRate + sqftCost + roomCost;
};
// Calculate add-ons total
const getAddonsTotal = () => {
let total = 0;
const kitchenExtras = addons.checkboxList('kitchenExtras')?.value() || [];
const homeExtras = addons.checkboxList('homeExtras')?.value() || [];
const allSelected = [...kitchenExtras, ...homeExtras];
for (const addonId of allSelected) {
total += addonPrices[addonId] || 0;
}
return total;
};
// Get selected add-ons for display
const getSelectedAddons = () => {
const kitchenExtras = addons.checkboxList('kitchenExtras')?.value() || [];
const homeExtras = addons.checkboxList('homeExtras')?.value() || [];
return [...kitchenExtras, ...homeExtras];
};
// Calculate final price
const getFinalPrice = () => {
const cleaningType = options.radioButton('cleaningType')?.value() || 'standard';
const frequency = options.radioButton('frequency')?.value() || 'one-time';
const basePrice = getBasePrice();
const multiplier = cleaningMultipliers[cleaningType] || 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 cleaningType = options.radioButton('cleaningType')?.value() || 'standard';
const frequency = options.radioButton('frequency')?.value() || 'one-time';
const basePrice = getBasePrice();
const multiplier = cleaningMultipliers[cleaningType] || 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: 'cleaning-quote.pdf',
pageSize: 'A4',
allowUserDownload: true,
downloadButtonLabel: 'Download Your Quote',
header: {
title: 'Cleaning Service Quote',
subtitle: 'Professional Home Cleaning Services'
},
footer: {
text: 'Thank you for choosing our cleaning services!',
showPageNumbers: true
}
});
// Property Summary Section
pdf.addSection('Property Details', (section) => {
section.addRow((row) => {
const propertyTypeMap: Record<string, string> = {
'apartment': 'Apartment',
'house': 'House',
'condo': 'Condo',
'townhouse': 'Townhouse'
};
const type = property.dropdown('type')?.value() || 'house';
row.addField('Property Type', propertyTypeMap[type] || type);
row.addField('Square Feet', `${property.integer('sqft')?.value() || 1500} sq ft`);
});
section.addRow((row) => {
row.addField('Bedrooms', `${property.integer('bedrooms')?.value() || 3}`);
row.addField('Bathrooms', `${property.integer('bathrooms')?.value() || 2}`);
});
});
// Cleaning Options Section
pdf.addSection('Service Details', (section) => {
section.addRow((row) => {
const cleaningTypeMap: Record<string, string> = {
'standard': 'Standard Cleaning',
'deep': 'Deep Clean',
'move-out': 'Move-Out Cleaning'
};
const type = options.radioButton('cleaningType')?.value() || 'standard';
row.addField('Cleaning Type', cleaningTypeMap[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() || 'one-time';
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> = {
'insideOven': 'Inside Oven',
'insideFridge': 'nside Fridge',
'insideCabinets': 'Inside Cabinets',
'dishes': 'Wash Dishes',
'windowsInterior': 'Interior Windows',
'laundry': 'Laundry',
'organizing': 'Organizing',
'petHairRemoval': 'Pet Hair Removal'
};
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 cleaningType = options.radioButton('cleaningType')?.value() || 'standard';
const frequency = options.radioButton('frequency')?.value() || 'one-time';
const multiplier = cleaningMultipliers[cleaningType] || 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@cleaningservice.com');
});
});
}