export function dogGroomingCalculator(form: FormTs) {
const sizeRates: Record<string, number> = {
'small': 40, // Under 15 lbs
'medium': 55, // 15-40 lbs
'large': 70, // 40-70 lbs
'xlarge': 90, // 70+ lbs
'giant': 110 // 100+ lbs
};
const coatMultipliers: Record<string, number> = {
'smooth': 1.0,
'short': 1.0,
'medium': 1.2,
'long': 1.4,
'double': 1.5,
'curly': 1.6,
'wire': 1.3
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Dog Grooming Quote',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Service Location Section
const locationSection = form.addSubform('serviceLocation', { title: '๐ Service Location' });
locationSection.addRow(row => {
row.addRadioButton('serviceType', {
label: 'Service Type',
options: [
{ id: 'salon', name: 'Salon Visit (Drop off at our location)' },
{ id: 'mobile', name: 'Mobile Grooming (We come to you)' }
],
defaultValue: 'salon',
orientation: 'vertical'
});
});
locationSection.addRow(row => {
row.addAddress('homeAddress', {
label: 'Your Address',
placeholder: 'Enter your 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,
isVisible: () => locationSection.radioButton('serviceType')?.value() === 'mobile'
});
});
locationSection.addRow(row => {
row.addTextPanel('mobileServiceInfo', {
computedValue: () => {
const addressField = locationSection.address('homeAddress');
const miles = addressField?.distance();
if (miles == null) return '๐ Enter address for mobile grooming fee';
if (miles <= 10) return '๐ Within service area - $25 mobile fee';
if (miles <= 20) return '๐ Extended area - $40 mobile fee';
if (miles <= 30) return '๐ Remote area - $55 mobile fee';
return '๐ Outside service area - Please contact us';
},
customStyles: { 'font-size': '0.9rem', 'color': '#db2777', 'background': '#fdf2f8', 'padding': '10px', 'border-radius': '6px' },
isVisible: () => locationSection.radioButton('serviceType')?.value() === 'mobile'
});
});
// Dog Details Section
const dogSection = form.addSubform('dogDetails', { title: '๐ Your Dog' });
dogSection.addRow(row => {
row.addDropdown('dogSize', {
label: 'Dog Size',
options: [
{ id: 'small', name: 'Small (Under 15 lbs) - $40 base' },
{ id: 'medium', name: 'Medium (15-40 lbs) - $55 base' },
{ id: 'large', name: 'Large (40-70 lbs) - $70 base' },
{ id: 'xlarge', name: 'X-Large (70-100 lbs) - $90 base' },
{ id: 'giant', name: 'Giant (100+ lbs) - $110 base' }
],
defaultValue: 'medium',
isRequired: true
}, '1fr');
row.addDropdown('coatType', {
label: 'Coat Type',
options: [
{ id: 'smooth', name: 'Smooth (Beagle, Boxer)' },
{ id: 'short', name: 'Short (Lab, Pit Bull)' },
{ id: 'medium', name: 'Medium (Golden, Border Collie)' },
{ id: 'long', name: 'Long (Shih Tzu, Afghan)' },
{ id: 'double', name: 'Double Coat (Husky, Shepherd)' },
{ id: 'curly', name: 'Curly (Poodle, Doodle)' },
{ id: 'wire', name: 'Wire (Terrier, Schnauzer)' }
],
defaultValue: 'short',
isRequired: true
}, '1fr');
});
dogSection.addRow(row => {
row.addDropdown('coatCondition', {
label: 'Coat Condition',
options: [
{ id: 'excellent', name: 'Excellent - Recently groomed' },
{ id: 'good', name: 'Good - Regular brushing' },
{ id: 'fair', name: 'Fair - Some tangles' },
{ id: 'matted', name: 'Matted - Needs dematting (+$20-50)' }
],
defaultValue: 'good'
}, '1fr');
row.addDropdown('temperament', {
label: 'Temperament',
options: [
{ id: 'calm', name: 'Calm & Cooperative' },
{ id: 'nervous', name: 'Nervous/Anxious (+$10)' },
{ id: 'difficult', name: 'Difficult/Aggressive (+$20)' }
],
defaultValue: 'calm'
}, '1fr');
});
// Service Package Section
const packageSection = form.addSubform('servicePackage', { title: 'โ๏ธ Grooming Package' });
packageSection.addRow(row => {
row.addRadioButton('package', {
label: 'Select Service',
options: [
{ id: 'bath', name: 'Bath & Brush - Bath, blow dry, brush out, ear cleaning, nail trim' },
{ id: 'full', name: 'Full Groom - Bath & Brush + haircut/styling' },
{ id: 'deluxe', name: 'Deluxe Spa - Full Groom + teeth brushing, paw treatment, cologne' },
{ id: 'puppy', name: 'Puppy Package - Gentle intro grooming for puppies under 6 months' }
],
defaultValue: 'full',
orientation: 'vertical',
isRequired: true
});
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('nailGrind', {
label: 'Nail Grinding (+$10)',
defaultValue: false
}, '1fr');
row.addCheckbox('teethBrushing', {
label: 'Teeth Brushing (+$10)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('deShed', {
label: 'De-shedding Treatment (+$20-40)',
defaultValue: false
}, '1fr');
row.addCheckbox('fleaTick', {
label: 'Flea & Tick Treatment (+$15)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('pawTreatment', {
label: 'Paw Pad Treatment (+$8)',
defaultValue: false
}, '1fr');
row.addCheckbox('facialScrub', {
label: 'Facial Scrub (+$10)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('cologne', {
label: 'Finishing Cologne (+$5)',
defaultValue: false
}, '1fr');
row.addCheckbox('bandana', {
label: 'Bandana or Bow (+$3)',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Helper to calculate mobile grooming fee
const getMobileGroomingFee = () => {
if (locationSection.radioButton('serviceType')?.value() !== 'mobile') return 0;
const addressField = locationSection.address('homeAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 10) return 25;
if (miles <= 20) return 40;
if (miles <= 30) return 55;
return 70;
};
// Results Section
const resultsSection = form.addSubform('results', { title: '๐ฐ Quote Summary', isCollapsible: false });
const getPackageMultiplier = () => {
const pkg = packageSection.radioButton('package')?.value() || 'full';
const multipliers: Record<string, number> = {
'bath': 0.7,
'full': 1.0,
'deluxe': 1.4,
'puppy': 0.6
};
return multipliers[pkg] || 1;
};
const getConditionExtra = () => {
const condition = dogSection.dropdown('coatCondition')?.value() || 'good';
const extras: Record<string, number> = {
'excellent': 0,
'good': 0,
'fair': 10,
'matted': 35
};
return extras[condition] || 0;
};
const getTemperamentExtra = () => {
const temperament = dogSection.dropdown('temperament')?.value() || 'calm';
const extras: Record<string, number> = {
'calm': 0,
'nervous': 10,
'difficult': 20
};
return extras[temperament] || 0;
};
resultsSection.addRow(row => {
row.addPriceDisplay('basePrice', {
label: 'Base Service Price',
computedValue: () => {
const size = dogSection.dropdown('dogSize')?.value() || 'medium';
const coat = dogSection.dropdown('coatType')?.value() || 'short';
const baseRate = sizeRates[size] || 55;
const coatMult = coatMultipliers[coat] || 1;
const packageMult = getPackageMultiplier();
return Math.round(baseRate * coatMult * packageMult);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('adjustments', {
label: 'Condition & Temperament',
computedValue: () => {
return getConditionExtra() + getTemperamentExtra();
},
variant: 'default',
prefix: '+'
}, '1fr');
});
resultsSection.addRow(row => {
row.addPriceDisplay('addonsTotal', {
label: 'Add-ons',
computedValue: () => {
let total = 0;
if (addonsSection.checkbox('nailGrind')?.value()) total += 10;
if (addonsSection.checkbox('teethBrushing')?.value()) total += 10;
if (addonsSection.checkbox('deShed')?.value()) {
const size = dogSection.dropdown('dogSize')?.value() || 'medium';
total += size === 'xlarge' || size === 'giant' ? 40 : size === 'large' ? 30 : 20;
}
if (addonsSection.checkbox('fleaTick')?.value()) total += 15;
if (addonsSection.checkbox('pawTreatment')?.value()) total += 8;
if (addonsSection.checkbox('facialScrub')?.value()) total += 10;
if (addonsSection.checkbox('cologne')?.value()) total += 5;
if (addonsSection.checkbox('bandana')?.value()) total += 3;
return total;
},
variant: 'default',
prefix: '+'
});
});
resultsSection.addRow(row => {
row.addPriceDisplay('mobileFee', {
label: 'Mobile Grooming Fee',
computedValue: () => getMobileGroomingFee(),
variant: 'default',
prefix: '+',
isVisible: () => locationSection.radioButton('serviceType')?.value() === 'mobile'
});
});
resultsSection.addSpacer({ showLine: true, lineStyle: 'solid', lineColor: '#e2e8f0' });
resultsSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Grooming Price',
computedValue: () => {
const size = dogSection.dropdown('dogSize')?.value() || 'medium';
const coat = dogSection.dropdown('coatType')?.value() || 'short';
const baseRate = sizeRates[size] || 55;
const coatMult = coatMultipliers[coat] || 1;
const packageMult = getPackageMultiplier();
let total = baseRate * coatMult * packageMult;
total += getConditionExtra();
total += getTemperamentExtra();
// Add-ons
if (addonsSection.checkbox('nailGrind')?.value()) total += 10;
if (addonsSection.checkbox('teethBrushing')?.value()) total += 10;
if (addonsSection.checkbox('deShed')?.value()) {
total += size === 'xlarge' || size === 'giant' ? 40 : size === 'large' ? 30 : 20;
}
if (addonsSection.checkbox('fleaTick')?.value()) total += 15;
if (addonsSection.checkbox('pawTreatment')?.value()) total += 8;
if (addonsSection.checkbox('facialScrub')?.value()) total += 10;
if (addonsSection.checkbox('cologne')?.value()) total += 5;
if (addonsSection.checkbox('bandana')?.value()) total += 3;
// Mobile grooming fee
total += getMobileGroomingFee();
return Math.round(total);
},
variant: 'large'
});
});
resultsSection.addRow(row => {
row.addTextPanel('timeEstimate', {
computedValue: () => {
const size = dogSection.dropdown('dogSize')?.value() || 'medium';
const pkg = packageSection.radioButton('package')?.value() || 'full';
const condition = dogSection.dropdown('coatCondition')?.value() || 'good';
let baseTime = size === 'small' ? 60 : size === 'medium' ? 90 : size === 'large' ? 120 : 150;
if (pkg === 'bath') baseTime *= 0.6;
if (pkg === 'deluxe') baseTime *= 1.3;
if (condition === 'matted') baseTime += 30;
const hours = Math.floor(baseTime / 60);
const mins = baseTime % 60;
return `Estimated time: ${hours > 0 ? hours + ' hour' + (hours > 1 ? 's ' : ' ') : ''}${mins > 0 ? mins + ' minutes' : ''}`;
},
customStyles: { 'font-size': '0.9rem', 'color': '#475569' }
});
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Grooming Price',
computedValue: () => {
const size = dogSection.dropdown('dogSize')?.value() || 'medium';
const coat = dogSection.dropdown('coatType')?.value() || 'short';
const baseRate = sizeRates[size] || 55;
const coatMult = coatMultipliers[coat] || 1;
const packageMult = getPackageMultiplier();
let total = baseRate * coatMult * packageMult;
total += getConditionExtra();
total += getTemperamentExtra();
if (addonsSection.checkbox('nailGrind')?.value()) total += 10;
if (addonsSection.checkbox('teethBrushing')?.value()) total += 10;
if (addonsSection.checkbox('deShed')?.value()) {
total += size === 'xlarge' || size === 'giant' ? 40 : size === 'large' ? 30 : 20;
}
if (addonsSection.checkbox('fleaTick')?.value()) total += 15;
if (addonsSection.checkbox('pawTreatment')?.value()) total += 8;
if (addonsSection.checkbox('facialScrub')?.value()) total += 10;
if (addonsSection.checkbox('cologne')?.value()) total += 5;
if (addonsSection.checkbox('bandana')?.value()) total += 3;
// Mobile grooming fee
total += getMobileGroomingFee();
return Math.round(total);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Final price confirmed after assessment.',
customStyles: { 'font-size': '0.85rem', 'color': '#94a3b8', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Book Appointment'
});
}