export function massageTherapyCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Massage Therapy Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Massage Type Section
const typeSection = form.addSubform('type', { title: '๐ Massage Type' });
typeSection.addRow(row => {
row.addRadioButton('massageType', {
label: 'Type of Massage',
options: [
{ id: 'swedish', name: 'Swedish/Relaxation' },
{ id: 'deep-tissue', name: 'Deep Tissue' },
{ id: 'sports', name: 'Sports Massage' },
{ id: 'hot-stone', name: 'Hot Stone' },
{ id: 'prenatal', name: 'Prenatal' },
{ id: 'thai', name: 'Thai Massage' },
{ id: 'shiatsu', name: 'Shiatsu' },
{ id: 'couples', name: 'Couples Massage' },
{ id: 'lymphatic', name: 'Lymphatic Drainage' },
{ id: 'trigger-point', name: 'Trigger Point Therapy' }
],
defaultValue: 'swedish',
orientation: 'vertical',
isRequired: true
});
});
// Session Details Section
const sessionSection = form.addSubform('session', { title: 'โฑ๏ธ Session Details' });
sessionSection.addRow(row => {
row.addDropdown('duration', {
label: 'Session Duration',
options: [
{ id: '30', name: '30 Minutes' },
{ id: '60', name: '60 Minutes' },
{ id: '90', name: '90 Minutes' },
{ id: '120', name: '2 Hours' }
],
defaultValue: '60',
isRequired: true
}, '1fr');
row.addDropdown('therapistLevel', {
label: 'Therapist Experience',
options: [
{ id: 'standard', name: 'Licensed Therapist' },
{ id: 'senior', name: 'Senior Therapist (5+ years)' },
{ id: 'specialist', name: 'Specialist/Advanced Certified' },
{ id: 'master', name: 'Master Therapist' }
],
defaultValue: 'standard'
}, '1fr');
});
sessionSection.addRow(row => {
row.addDropdown('location', {
label: 'Service Location',
options: [
{ id: 'spa', name: 'Spa/Clinic' },
{ id: 'mobile', name: 'Mobile/In-Home' },
{ id: 'corporate', name: 'Corporate/Office' },
{ id: 'hotel', name: 'Hotel Room' }
],
defaultValue: 'spa'
}, '1fr');
row.addDropdown('timing', {
label: 'Appointment Time',
options: [
{ id: 'regular', name: 'Regular Hours' },
{ id: 'evening', name: 'Evening (after 6pm)' },
{ id: 'weekend', name: 'Weekend' },
{ id: 'holiday', name: 'Holiday' }
],
defaultValue: 'regular'
}, '1fr');
});
// Package & Frequency Section
const packageSection = form.addSubform('package', { title: '๐ฆ Package & Frequency' });
packageSection.addRow(row => {
row.addDropdown('packageType', {
label: 'Package Type',
options: [
{ id: 'single', name: 'Single Session' },
{ id: '3-pack', name: '3 Session Package (5% off)' },
{ id: '6-pack', name: '6 Session Package (10% off)' },
{ id: '12-pack', name: '12 Session Package (15% off)' },
{ id: 'monthly', name: 'Monthly Membership' }
],
defaultValue: 'single'
}, '1fr');
row.addInteger('people', {
label: 'Number of People',
min: 1,
max: 10,
defaultValue: 1,
tooltip: 'For couples or group bookings'
}, '1fr');
});
// Enhancements Section
const enhancementsSection = form.addSubform('enhancements', { title: 'โจ Enhancements & Add-ons' });
enhancementsSection.addRow(row => {
row.addCheckbox('aromatherapy', {
label: 'Aromatherapy (+$15)',
defaultValue: false
}, '1fr');
row.addCheckbox('hotTowels', {
label: 'Hot Towel Treatment (+$10)',
defaultValue: false
}, '1fr');
});
enhancementsSection.addRow(row => {
row.addCheckbox('scalp', {
label: 'Scalp Massage (+$15)',
defaultValue: false
}, '1fr');
row.addCheckbox('foot', {
label: 'Foot Reflexology (+$20)',
defaultValue: false
}, '1fr');
});
enhancementsSection.addRow(row => {
row.addCheckbox('cupping', {
label: 'Cupping Therapy (+$25)',
defaultValue: false
}, '1fr');
row.addCheckbox('cbdOil', {
label: 'CBD Oil Upgrade (+$20)',
defaultValue: false
}, '1fr');
});
enhancementsSection.addRow(row => {
row.addCheckbox('extendedTime', {
label: 'Extended Focus Area (+$15)',
defaultValue: false,
tooltip: 'Extra time on specific problem areas'
}, '1fr');
row.addCheckbox('premiumProducts', {
label: 'Premium Products (+$15)',
defaultValue: false
}, '1fr');
});
// Focus Areas (for therapeutic massages)
const focusSection = form.addSubform('focus', {
title: '๐ฏ Focus Areas',
isVisible: () => ['deep-tissue', 'sports', 'trigger-point'].includes(typeSection.radioButton('massageType')?.value() || '')
});
focusSection.addRow(row => {
row.addCheckbox('neck', {
label: 'Neck & Shoulders',
defaultValue: true
}, '1fr');
row.addCheckbox('back', {
label: 'Back (upper/lower)',
defaultValue: true
}, '1fr');
});
focusSection.addRow(row => {
row.addCheckbox('legs', {
label: 'Legs & Calves',
defaultValue: false
}, '1fr');
row.addCheckbox('arms', {
label: 'Arms & Hands',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Section
const pricingSection = form.addSubform('pricing', { title: '๐ฐ Pricing', isCollapsible: false });
const calculatePrice = () => {
const massageType = typeSection.radioButton('massageType')?.value() || 'swedish';
const duration = parseInt(sessionSection.dropdown('duration')?.value() || '60');
const therapistLevel = sessionSection.dropdown('therapistLevel')?.value() || 'standard';
const location = sessionSection.dropdown('location')?.value() || 'spa';
const timing = sessionSection.dropdown('timing')?.value() || 'regular';
const packageType = packageSection.dropdown('packageType')?.value() || 'single';
const people = packageSection.integer('people')?.value() || 1;
// Base rates per hour by massage type
const typeRates: Record<string, number> = {
'swedish': 80,
'deep-tissue': 95,
'sports': 100,
'hot-stone': 110,
'prenatal': 90,
'thai': 100,
'shiatsu': 95,
'couples': 160,
'lymphatic': 100,
'trigger-point': 105
};
let baseRate = typeRates[massageType] || 80;
// Duration adjustment
const durationMultiplier = duration / 60;
let sessionPrice = baseRate * durationMultiplier;
// Shorter sessions have slight premium per minute
if (duration === 30) sessionPrice = baseRate * 0.6;
// Therapist level multiplier
const therapistMult: Record<string, number> = {
'standard': 1.0,
'senior': 1.2,
'specialist': 1.4,
'master': 1.6
};
sessionPrice *= therapistMult[therapistLevel] || 1.0;
// Location adjustment
const locationMult: Record<string, number> = {
'spa': 1.0,
'mobile': 1.3,
'corporate': 1.2,
'hotel': 1.25
};
sessionPrice *= locationMult[location] || 1.0;
// Timing adjustment
const timingMult: Record<string, number> = {
'regular': 1.0,
'evening': 1.1,
'weekend': 1.15,
'holiday': 1.3
};
sessionPrice *= timingMult[timing] || 1.0;
// Enhancements
let enhancements = 0;
if (enhancementsSection.checkbox('aromatherapy')?.value()) enhancements += 15;
if (enhancementsSection.checkbox('hotTowels')?.value()) enhancements += 10;
if (enhancementsSection.checkbox('scalp')?.value()) enhancements += 15;
if (enhancementsSection.checkbox('foot')?.value()) enhancements += 20;
if (enhancementsSection.checkbox('cupping')?.value()) enhancements += 25;
if (enhancementsSection.checkbox('cbdOil')?.value()) enhancements += 20;
if (enhancementsSection.checkbox('extendedTime')?.value()) enhancements += 15;
if (enhancementsSection.checkbox('premiumProducts')?.value()) enhancements += 15;
sessionPrice += enhancements;
// People multiplier (couples/group)
sessionPrice *= people;
// Package discounts
const packageDiscounts: Record<string, number> = {
'single': 0,
'3-pack': 0.05,
'6-pack': 0.10,
'12-pack': 0.15,
'monthly': 0.20
};
const discount = packageDiscounts[packageType] || 0;
// Package sessions
const packageSessions: Record<string, number> = {
'single': 1,
'3-pack': 3,
'6-pack': 6,
'12-pack': 12,
'monthly': 1
};
const sessions = packageSessions[packageType] || 1;
const discountedPrice = sessionPrice * (1 - discount);
const packageTotal = discountedPrice * sessions;
// Gratuity suggestion
const suggestedTip = sessionPrice * 0.20;
return {
basePrice: Math.round(sessionPrice - enhancements),
enhancements: Math.round(enhancements),
perSession: Math.round(sessionPrice),
discountedSession: Math.round(discountedPrice),
packageTotal: Math.round(packageTotal),
discount: Math.round(discount * 100),
sessions,
suggestedTip: Math.round(suggestedTip),
duration,
people
};
};
pricingSection.addRow(row => {
row.addPriceDisplay('basePrice', {
label: 'Base Massage Price',
computedValue: () => calculatePrice().basePrice,
variant: 'default'
}, '1fr');
row.addPriceDisplay('enhancements', {
label: 'Enhancements',
computedValue: () => calculatePrice().enhancements,
variant: 'default',
isVisible: () => calculatePrice().enhancements > 0
}, '1fr');
});
pricingSection.addRow(row => {
row.addPriceDisplay('perSession', {
label: () => `Per Session (${calculatePrice().duration} min${calculatePrice().people > 1 ? ', ' + calculatePrice().people + ' people' : ''})`,
computedValue: () => calculatePrice().perSession,
variant: 'default'
}, '1fr');
row.addPriceDisplay('discounted', {
label: () => `After ${calculatePrice().discount}% Package Discount`,
computedValue: () => calculatePrice().discountedSession,
variant: 'success',
isVisible: () => calculatePrice().discount > 0
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('total', {
label: () => calculatePrice().sessions > 1 ? `Package Total (${calculatePrice().sessions} sessions)` : 'Session Total',
computedValue: () => calculatePrice().packageTotal,
variant: 'large'
}, '1fr');
row.addPriceDisplay('tip', {
label: 'Suggested Gratuity (20%)',
computedValue: () => calculatePrice().suggestedTip,
variant: 'default'
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Prices may vary. Gratuity is not included in displayed prices.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Book Appointment'
});
}