export function petGroomingForm(form: FormTs) {
form.setTitle(() => '๐พ Pet Grooming Quote Calculator');
form.configureCompletionScreen({
type: 'text',
title: () => '๐ Quote Ready!',
message: () => 'Your custom grooming quote has been generated. Download your PDF to save the details.'
});
// Pricing data
const sizePrices: Record<string, number> = {
'small': 45,
'medium': 65,
'large': 85,
'xlarge': 110
};
const coatMultipliers: Record<string, number> = {
'short': 1.0,
'medium': 1.2,
'long': 1.4,
'double': 1.6,
'curly': 1.5
};
const groomingPackages: Record<string, number> = {
'bath': 0,
'full': 25,
'deluxe': 50
};
const addonPrices: Record<string, number> = {
'nailTrim': 15,
'nailGrind': 20,
'teethBrushing': 15,
'earCleaning': 12,
'analGlands': 18,
'fleaTreatment': 25,
'deShedding': 30,
'pawTrim': 10,
'faceTrim': 15,
'cologne': 8
};
// Pet Details
const petDetails = form.addSubform('petDetails', {
title: () => '๐ Pet Details',
mobileBreakpoint: 0
});
petDetails.addRow(row => {
row.addDropdown('petType', {
label: 'Pet Type',
defaultValue: 'dog',
isRequired: true,
options: [
{ id: 'dog', name: '๐ Dog' },
{ id: 'cat', name: '๐ฑ Cat' }
]
}, '1fr');
row.addDropdown('size', {
label: 'Size',
defaultValue: 'medium',
isRequired: true,
options: [
{ id: 'small', name: '๐ฉ Small (under 15 lbs)' },
{ id: 'medium', name: '๐ Medium (15-40 lbs)' },
{ id: 'large', name: '๐ฆฎ Large (40-70 lbs)' },
{ id: 'xlarge', name: '๐โ๐ฆบ X-Large (70+ lbs)' }
]
}, '1fr');
});
petDetails.addRow(row => {
row.addDropdown('coatType', {
label: 'Coat Type',
defaultValue: 'medium',
isRequired: true,
options: [
{ id: 'short', name: 'โจ Short/Smooth' },
{ id: 'medium', name: '๐ Medium' },
{ id: 'long', name: '๐ Long' },
{ id: 'double', name: '๐งฅ Double Coat' },
{ id: 'curly', name: '๐ Curly/Poodle-type' }
]
}, '1fr');
row.addTextbox('breed', {
label: 'Breed (optional)',
placeholder: 'e.g., Golden Retriever, Shih Tzu'
}, '1fr');
});
// Grooming Package
const packageSection = form.addSubform('package', {
title: () => 'โ๏ธ Grooming Package',
mobileBreakpoint: 0
});
packageSection.addRow(row => {
row.addRadioButton('groomingType', {
label: 'Select Package',
defaultValue: 'full',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'bath', name: '๐ Bath & Brush - Shampoo, blow dry, brush out' },
{ id: 'full', name: 'โ๏ธ Full Groom (+$25) - Bath + haircut, styling' },
{ id: 'deluxe', name: '๐ Deluxe Spa (+$50) - Full groom + premium treatments' }
]
});
});
// Add-ons
const addonsSection = form.addSubform('addons', {
title: () => 'โจ Add-On Services',
mobileBreakpoint: 500
});
addonsSection.addRow(row => {
row.addCheckboxList('nailServices', {
label: 'Nail Services',
orientation: 'vertical',
options: [
{ id: 'nailTrim', name: 'โ๏ธ Nail Trim (+$15)' },
{ id: 'nailGrind', name: '๐
Nail Grinding (+$20)' },
{ id: 'pawTrim', name: '๐พ Paw Pad Trim (+$10)' }
]
}, '1fr');
row.addCheckboxList('hygieneServices', {
label: 'Hygiene Services',
orientation: 'vertical',
options: [
{ id: 'teethBrushing', name: '๐ฆท Teeth Brushing (+$15)' },
{ id: 'earCleaning', name: '๐ Ear Cleaning (+$12)' },
{ id: 'analGlands', name: '๐ Anal Glands (+$18)' }
]
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckboxList('coatServices', {
label: 'Coat Services',
orientation: 'vertical',
options: [
{ id: 'deShedding', name: '๐งน De-shedding Treatment (+$30)' },
{ id: 'fleaTreatment', name: '๐ Flea Treatment (+$25)' },
{ id: 'faceTrim', name: '๐ Face Trim (+$15)' }
]
}, '1fr');
row.addCheckboxList('extras', {
label: 'Finishing Touches',
orientation: 'vertical',
options: [
{ id: 'cologne', name: 'โจ Cologne/Perfume (+$8)' }
]
}, '1fr');
});
// Calculate functions
const getBasePrice = () => {
const size = petDetails.dropdown('size')?.value() || 'medium';
return sizePrices[size] ?? 65;
};
const getCoatMultiplier = () => {
const coatType = petDetails.dropdown('coatType')?.value() || 'medium';
return coatMultipliers[coatType] ?? 1.2;
};
const getPackagePrice = () => {
const groomingType = packageSection.radioButton('groomingType')?.value() || 'full';
return groomingPackages[groomingType] ?? 25;
};
const getAddonsTotal = () => {
let total = 0;
const nailServices = addonsSection.checkboxList('nailServices')?.value() || [];
const hygieneServices = addonsSection.checkboxList('hygieneServices')?.value() || [];
const coatServices = addonsSection.checkboxList('coatServices')?.value() || [];
const extras = addonsSection.checkboxList('extras')?.value() || [];
const allSelected = [...nailServices, ...hygieneServices, ...coatServices, ...extras];
for (const addonId of allSelected) {
total += addonPrices[addonId] ?? 0;
}
return total;
};
const getSelectedAddons = () => {
const nailServices = addonsSection.checkboxList('nailServices')?.value() || [];
const hygieneServices = addonsSection.checkboxList('hygieneServices')?.value() || [];
const coatServices = addonsSection.checkboxList('coatServices')?.value() || [];
const extras = addonsSection.checkboxList('extras')?.value() || [];
return [...nailServices, ...hygieneServices, ...coatServices, ...extras];
};
const getTotalPrice = () => {
const baseWithCoat = Math.round(getBasePrice() * getCoatMultiplier());
return baseWithCoat + getPackagePrice() + getAddonsTotal();
};
// Quote Summary
const summary = form.addSubform('summary', {
title: () => '๐ฐ Your Grooming Quote',
isCollapsible: false,
});
summary.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Price',
computedValue: () => getTotalPrice(),
alignment: 'center',
variant: 'large'
});
});
summary.addRow(row => {
row.addTextPanel('includes', {
computedValue: () => {
const groomingType = packageSection.radioButton('groomingType')?.value() || 'full';
const packageNames: Record<string, string> = {
'bath': 'Bath & Brush package',
'full': 'Full Grooming package',
'deluxe': 'Deluxe Spa package'
};
return `๐พ Includes: ${packageNames[groomingType] ?? 'Full Grooming package'} with professional care`;
},
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
textAlign: 'center'
}
});
});
form.configureSubmitButton({
label: () => 'Book This Grooming'
});
// Configure PDF
form.configurePdf('quoteDocument', (pdf) => {
pdf.configure({
filename: 'pet-grooming-quote.pdf',
pageSize: 'A4',
allowUserDownload: true,
downloadButtonLabel: 'Download Your Quote',
header: {
title: 'Pet Grooming Quote',
subtitle: 'Custom Service Package'
},
footer: {
text: 'Thank you for choosing us for your pet grooming needs!',
showPageNumbers: true
}
});
pdf.addSection('Pet Details', (section) => {
section.addRow((row) => {
const petType = petDetails.dropdown('petType')?.value() || 'dog';
row.addField('Pet Type', petType === 'dog' ? 'Dog' : 'Cat');
const size = petDetails.dropdown('size')?.value() || 'medium';
const sizeLabels: Record<string, string> = {
'small': 'Small (under 15 lbs)',
'medium': 'Medium (15-40 lbs)',
'large': 'Large (40-70 lbs)',
'xlarge': 'X-Large (70+ lbs)'
};
row.addField('Size', sizeLabels[size] ?? size);
});
section.addRow((row) => {
const coatType = petDetails.dropdown('coatType')?.value() || 'medium';
const coatLabels: Record<string, string> = {
'short': 'Short/Smooth',
'medium': 'Medium',
'long': 'Long',
'double': 'Double Coat',
'curly': 'Curly/Poodle-type'
};
row.addField('Coat Type', coatLabels[coatType] ?? coatType);
const breed = petDetails.textbox('breed')?.value() || '';
if (breed) {
row.addField('Breed', breed);
}
});
});
pdf.addSection('Grooming Package', (section) => {
section.addRow((row) => {
const groomingType = packageSection.radioButton('groomingType')?.value() || 'full';
const packageLabels: Record<string, string> = {
'bath': 'Bath & Brush',
'full': 'Full Groom',
'deluxe': 'Deluxe Spa'
};
row.addField('Package', packageLabels[groomingType] ?? groomingType);
row.addField('Package Price', `$${getPackagePrice()}`);
});
});
const selectedAddons = getSelectedAddons();
if (selectedAddons.length > 0) {
pdf.addSection('Add-On Services', (section) => {
const addonLabels: Record<string, string> = {
'nailTrim': 'Nail Trim',
'nailGrind': 'Nail Grinding',
'teethBrushing': 'Teeth Brushing',
'earCleaning': 'Ear Cleaning',
'analGlands': 'Anal Glands',
'fleaTreatment': 'Flea Treatment',
'deShedding': 'De-shedding Treatment',
'pawTrim': 'Paw Pad Trim',
'faceTrim': 'Face Trim',
'cologne': 'Cologne/Perfume'
};
const tableRows = selectedAddons.map(addonId => [
addonLabels[addonId] ?? addonId,
`$${addonPrices[addonId] ?? 0}`
]);
section.addTable(['Service', 'Price'], tableRows);
});
}
pdf.addSection('Quote Summary', (section) => {
section.addRow((row) => {
const baseWithCoat = Math.round(getBasePrice() * getCoatMultiplier());
row.addField('Base Grooming (size + coat)', `$${baseWithCoat}`);
});
const packagePrice = getPackagePrice();
if (packagePrice > 0) {
section.addRow((row) => {
row.addField('Package Upgrade', `$${packagePrice}`);
});
}
if (selectedAddons.length > 0) {
section.addRow((row) => {
row.addField('Add-ons', `$${getAddonsTotal()}`);
});
}
section.addDivider();
section.addSpacer(10);
section.addRow((row) => {
row.addField('TOTAL', `$${getTotalPrice()}`);
});
});
pdf.addSection('What\'s Included', (section) => {
const groomingType = packageSection.radioButton('groomingType')?.value() || 'full';
const includes: Record<string, string[]> = {
'bath': ['Premium shampoo & conditioner', 'Blow dry & brush out', 'Ear check', 'Bandana or bow'],
'full': ['Everything in Bath & Brush', 'Breed-appropriate haircut', 'Styling & finishing', 'Nail trim included'],
'deluxe': ['Everything in Full Groom', 'Blueberry facial', 'Paw moisturizing treatment', 'Premium cologne', 'Extended brush & massage']
};
const items = includes[groomingType] ?? includes['full'] ?? [];
items.forEach(item => {
section.addText(`โข ${item}`);
});
});
pdf.addSection('Important Information', (section) => {
section.addText('โข This quote is valid for 14 days from the date of generation.');
section.addText('โข Please arrive 5 minutes before your appointment time.');
section.addText('โข Matted coats may require additional charges.');
section.addText('โข Cancellations require 24-hour notice.');
section.addSpacer(10);
section.addText('Questions? Contact us at hello@petgrooming.com');
});
});
}