export function yogaClassCalculator(form: FormTs) {
// Pricing data
const classTypePrices: Record<string, number> = {
'drop-in': 20,
'beginner': 18,
'vinyasa': 22,
'hot-yoga': 25,
'yin': 20,
'restorative': 22,
'power': 24,
'prenatal': 25,
'kids': 15,
'aerial': 35,
'private': 80
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Yoga Class Pricing Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Class Selection Section
const classSection = form.addSubform('class', { title: '🧘 Class Selection' });
classSection.addRow(row => {
row.addDropdown('classType', {
label: 'Class Type',
options: [
{ id: 'drop-in', name: 'Drop-in/All Levels ($20)' },
{ id: 'beginner', name: 'Beginner/Gentle ($18)' },
{ id: 'vinyasa', name: 'Vinyasa Flow ($22)' },
{ id: 'hot-yoga', name: 'Hot Yoga/Bikram ($25)' },
{ id: 'yin', name: 'Yin Yoga ($20)' },
{ id: 'restorative', name: 'Restorative ($22)' },
{ id: 'power', name: 'Power Yoga ($24)' },
{ id: 'prenatal', name: 'Prenatal/Postnatal ($25)' },
{ id: 'kids', name: 'Kids Yoga ($15)' },
{ id: 'aerial', name: 'Aerial Yoga ($35)' },
{ id: 'private', name: 'Private Session ($80)' }
],
defaultValue: 'vinyasa',
isRequired: true
}, '1fr');
row.addDropdown('duration', {
label: 'Class Duration',
options: [
{ id: '45', name: '45 minutes' },
{ id: '60', name: '60 minutes (Standard)' },
{ id: '75', name: '75 minutes' },
{ id: '90', name: '90 minutes (Extended)' }
],
defaultValue: '60',
isRequired: true
}, '1fr');
});
classSection.addRow(row => {
row.addDropdown('instructor', {
label: 'Instructor Level',
options: [
{ id: 'standard', name: 'Studio Instructor' },
{ id: 'senior', name: 'Senior Instructor (+$5)' },
{ id: 'master', name: 'Master Teacher (+$10)' },
{ id: 'guest', name: 'Guest/Celebrity Teacher (+$15)' }
],
defaultValue: 'standard'
}, '1fr');
row.addDropdown('timeSlot', {
label: 'Time Slot',
options: [
{ id: 'off-peak', name: 'Off-Peak (10am-4pm weekdays)' },
{ id: 'peak', name: 'Peak (6-9am, 5-8pm)' },
{ id: 'weekend', name: 'Weekend' }
],
defaultValue: 'peak'
}, '1fr');
});
// Package Options Section
const packageSection = form.addSubform('package', { title: '📦 Package Options' });
packageSection.addRow(row => {
row.addDropdown('purchaseType', {
label: 'Purchase Type',
options: [
{ id: 'single', name: 'Single Class' },
{ id: 'class-pack-5', name: '5-Class Pack (10% off)' },
{ id: 'class-pack-10', name: '10-Class Pack (15% off)' },
{ id: 'class-pack-20', name: '20-Class Pack (20% off)' },
{ id: 'monthly-unlimited', name: 'Monthly Unlimited' },
{ id: 'annual', name: 'Annual Membership' }
],
defaultValue: 'single',
isRequired: true
}, '1fr');
row.addInteger('participants', {
label: 'Number of Participants',
min: 1,
max: 10,
defaultValue: 1,
tooltip: 'For group bookings',
isVisible: () => {
const type = packageSection.dropdown('purchaseType')?.value();
return type === 'single' || type?.startsWith('class-pack');
}
}, '1fr');
});
packageSection.addRow(row => {
row.addDropdown('membershipTier', {
label: 'Membership Tier',
options: [
{ id: 'basic', name: 'Basic ($99/month - 8 classes)' },
{ id: 'standard', name: 'Standard ($149/month - unlimited)' },
{ id: 'premium', name: 'Premium ($199/month - unlimited + perks)' }
],
defaultValue: 'standard',
isVisible: () => packageSection.dropdown('purchaseType')?.value() === 'monthly-unlimited'
}, '1fr');
row.addDropdown('annualPlan', {
label: 'Annual Plan',
options: [
{ id: 'basic-annual', name: 'Basic ($999/year - 8 classes/month)' },
{ id: 'unlimited-annual', name: 'Unlimited ($1,499/year)' },
{ id: 'premium-annual', name: 'Premium ($1,999/year - all access)' }
],
defaultValue: 'unlimited-annual',
isVisible: () => packageSection.dropdown('purchaseType')?.value() === 'annual'
}, '1fr');
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: '✨ Add-ons & Extras' });
addonsSection.addRow(row => {
row.addCheckbox('matRental', {
label: 'Mat Rental',
defaultValue: false,
tooltip: '+$3 per class'
}, '1fr');
row.addCheckbox('towelService', {
label: 'Towel Service',
defaultValue: false,
tooltip: '+$2 per class'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('propsKit', {
label: 'Props Kit (blocks, strap, blanket)',
defaultValue: false,
tooltip: '+$5 per class'
}, '1fr');
row.addCheckbox('lockerRental', {
label: 'Locker Rental',
defaultValue: false,
tooltip: '+$5/month or $2/day'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('showerAmenities', {
label: 'Premium Shower Amenities',
defaultValue: false,
tooltip: '+$3 per visit'
}, '1fr');
row.addCheckbox('recording', {
label: 'Class Recording Access',
defaultValue: false,
tooltip: '+$10/month for on-demand library'
}, '1fr');
});
// Discounts Section
const discountsSection = form.addSubform('discounts', { title: '🎁 Discounts & Offers' });
discountsSection.addRow(row => {
row.addDropdown('discountType', {
label: 'Discount',
options: [
{ id: 'none', name: 'No Discount' },
{ id: 'new-student', name: 'New Student (First month 50% off)' },
{ id: 'student', name: 'Student (15% off with ID)' },
{ id: 'senior', name: 'Senior 65+ (15% off)' },
{ id: 'military', name: 'Military/First Responder (20% off)' },
{ id: 'corporate', name: 'Corporate Partner (10% off)' },
{ id: 'referral', name: 'Referral Credit ($15 off)' }
],
defaultValue: 'none'
}, '1fr');
row.addCheckbox('autopay', {
label: 'Auto-Pay Discount',
defaultValue: true,
tooltip: '5% off monthly memberships'
}, '1fr');
});
// Private Sessions Section
const privateSection = form.addSubform('private', {
title: '👤 Private Session Options',
isVisible: () => classSection.dropdown('classType')?.value() === 'private'
});
privateSection.addRow(row => {
row.addDropdown('privateType', {
label: 'Session Type',
options: [
{ id: 'individual', name: 'Individual ($80)' },
{ id: 'duo', name: 'Duo/Partner ($120)' },
{ id: 'small-group', name: 'Small Group 3-5 ($150)' },
{ id: 'corporate', name: 'Corporate Group ($250)' }
],
defaultValue: 'individual'
}, '1fr');
row.addDropdown('privateDuration', {
label: 'Session Duration',
options: [
{ id: '60', name: '60 minutes' },
{ id: '90', name: '90 minutes (+50%)' },
{ id: '120', name: '120 minutes (+100%)' }
],
defaultValue: '60'
}, '1fr');
});
privateSection.addRow(row => {
row.addCheckbox('homeVisit', {
label: 'Home/Office Visit',
defaultValue: false,
tooltip: '+$30 travel fee'
}, '1fr');
row.addCheckbox('customProgram', {
label: 'Custom Program Design',
defaultValue: false,
tooltip: '+$50 for personalized sequence'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Cost Breakdown Section
const breakdownSection = form.addSubform('breakdown', { title: '📊 Cost Breakdown', isCollapsible: false });
breakdownSection.addRow(row => {
row.addPriceDisplay('baseCost', {
label: 'Base Class/Package Cost',
computedValue: () => {
const classType = classSection.dropdown('classType')?.value() || 'vinyasa';
const duration = classSection.dropdown('duration')?.value() || '60';
const purchaseType = packageSection.dropdown('purchaseType')?.value() || 'single';
const participants = packageSection.integer('participants')?.value() || 1;
let basePrice = classTypePrices[classType] || 20;
// Duration adjustment
const durationMults: Record<string, number> = {
'45': 0.85, '60': 1, '75': 1.15, '90': 1.3
};
basePrice *= durationMults[duration] || 1;
// Instructor premium
const instructor = classSection.dropdown('instructor')?.value() || 'standard';
const instructorPremiums: Record<string, number> = {
'standard': 0, 'senior': 5, 'master': 10, 'guest': 15
};
basePrice += instructorPremiums[instructor] || 0;
// Package calculation
if (purchaseType === 'single') {
return Math.round(basePrice * participants);
} else if (purchaseType === 'class-pack-5') {
return Math.round(basePrice * 5 * 0.9 * participants);
} else if (purchaseType === 'class-pack-10') {
return Math.round(basePrice * 10 * 0.85 * participants);
} else if (purchaseType === 'class-pack-20') {
return Math.round(basePrice * 20 * 0.8 * participants);
} else if (purchaseType === 'monthly-unlimited') {
const tier = packageSection.dropdown('membershipTier')?.value() || 'standard';
const tierPrices: Record<string, number> = {
'basic': 99, 'standard': 149, 'premium': 199
};
return tierPrices[tier] || 149;
} else if (purchaseType === 'annual') {
const plan = packageSection.dropdown('annualPlan')?.value() || 'unlimited-annual';
const planPrices: Record<string, number> = {
'basic-annual': 999, 'unlimited-annual': 1499, 'premium-annual': 1999
};
return planPrices[plan] || 1499;
}
return Math.round(basePrice);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('addonsCost', {
label: 'Add-ons',
computedValue: () => {
const purchaseType = packageSection.dropdown('purchaseType')?.value() || 'single';
let cost = 0;
// Calculate number of uses
let uses = 1;
if (purchaseType === 'class-pack-5') uses = 5;
else if (purchaseType === 'class-pack-10') uses = 10;
else if (purchaseType === 'class-pack-20') uses = 20;
else if (purchaseType === 'monthly-unlimited') uses = 12; // estimate
else if (purchaseType === 'annual') uses = 1; // flat fees
if (purchaseType === 'monthly-unlimited' || purchaseType === 'annual') {
// Monthly add-ons
if (addonsSection.checkbox('lockerRental')?.value()) cost += 5;
if (addonsSection.checkbox('recording')?.value()) cost += 10;
if (purchaseType === 'annual') cost *= 12;
} else {
// Per-class add-ons
if (addonsSection.checkbox('matRental')?.value()) cost += 3 * uses;
if (addonsSection.checkbox('towelService')?.value()) cost += 2 * uses;
if (addonsSection.checkbox('propsKit')?.value()) cost += 5 * uses;
if (addonsSection.checkbox('lockerRental')?.value()) cost += 2 * uses;
if (addonsSection.checkbox('showerAmenities')?.value()) cost += 3 * uses;
}
return cost;
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('privateCost', {
label: 'Private Session Cost',
computedValue: () => {
if (classSection.dropdown('classType')?.value() !== 'private') return 0;
const privateType = privateSection.dropdown('privateType')?.value() || 'individual';
const duration = privateSection.dropdown('privateDuration')?.value() || '60';
const typePrices: Record<string, number> = {
'individual': 80, 'duo': 120, 'small-group': 150, 'corporate': 250
};
let cost = typePrices[privateType] || 80;
// Duration adjustment
if (duration === '90') cost *= 1.5;
else if (duration === '120') cost *= 2;
// Extras
if (privateSection.checkbox('homeVisit')?.value()) cost += 30;
if (privateSection.checkbox('customProgram')?.value()) cost += 50;
return Math.round(cost);
},
variant: 'default',
isVisible: () => classSection.dropdown('classType')?.value() === 'private'
}, '1fr');
row.addPriceDisplay('discountAmount', {
label: 'Discount',
computedValue: () => {
const purchaseType = packageSection.dropdown('purchaseType')?.value() || 'single';
const discountType = discountsSection.dropdown('discountType')?.value() || 'none';
const autopay = discountsSection.checkbox('autopay')?.value();
// Get base cost (simplified)
let baseCost = 0;
if (purchaseType === 'monthly-unlimited') {
const tier = packageSection.dropdown('membershipTier')?.value() || 'standard';
const tierPrices: Record<string, number> = { 'basic': 99, 'standard': 149, 'premium': 199 };
baseCost = tierPrices[tier] || 149;
} else if (purchaseType === 'annual') {
const plan = packageSection.dropdown('annualPlan')?.value() || 'unlimited-annual';
const planPrices: Record<string, number> = {
'basic-annual': 999, 'unlimited-annual': 1499, 'premium-annual': 1999
};
baseCost = planPrices[plan] || 1499;
} else {
baseCost = 20; // simplified
}
let discount = 0;
if (discountType === 'new-student' && purchaseType === 'monthly-unlimited') {
discount = baseCost * 0.5;
} else if (discountType === 'student' || discountType === 'senior') {
discount = baseCost * 0.15;
} else if (discountType === 'military') {
discount = baseCost * 0.2;
} else if (discountType === 'corporate') {
discount = baseCost * 0.1;
} else if (discountType === 'referral') {
discount = 15;
}
if (autopay && purchaseType === 'monthly-unlimited') {
discount += baseCost * 0.05;
}
return -Math.round(discount);
},
variant: 'default'
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '🧘 Total Cost',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('totalCost', {
label: 'Total',
computedValue: () => {
const classType = classSection.dropdown('classType')?.value() || 'vinyasa';
const duration = classSection.dropdown('duration')?.value() || '60';
const purchaseType = packageSection.dropdown('purchaseType')?.value() || 'single';
const participants = packageSection.integer('participants')?.value() || 1;
const discountType = discountsSection.dropdown('discountType')?.value() || 'none';
const autopay = discountsSection.checkbox('autopay')?.value();
let total = 0;
// Base cost
if (classType === 'private') {
const privateType = privateSection.dropdown('privateType')?.value() || 'individual';
const privateDuration = privateSection.dropdown('privateDuration')?.value() || '60';
const typePrices: Record<string, number> = {
'individual': 80, 'duo': 120, 'small-group': 150, 'corporate': 250
};
total = typePrices[privateType] || 80;
if (privateDuration === '90') total *= 1.5;
else if (privateDuration === '120') total *= 2;
if (privateSection.checkbox('homeVisit')?.value()) total += 30;
if (privateSection.checkbox('customProgram')?.value()) total += 50;
} else {
let basePrice = classTypePrices[classType] || 20;
const durationMults: Record<string, number> = {
'45': 0.85, '60': 1, '75': 1.15, '90': 1.3
};
basePrice *= durationMults[duration] || 1;
const instructor = classSection.dropdown('instructor')?.value() || 'standard';
const instructorPremiums: Record<string, number> = {
'standard': 0, 'senior': 5, 'master': 10, 'guest': 15
};
basePrice += instructorPremiums[instructor] || 0;
if (purchaseType === 'single') {
total = basePrice * participants;
} else if (purchaseType === 'class-pack-5') {
total = basePrice * 5 * 0.9 * participants;
} else if (purchaseType === 'class-pack-10') {
total = basePrice * 10 * 0.85 * participants;
} else if (purchaseType === 'class-pack-20') {
total = basePrice * 20 * 0.8 * participants;
} else if (purchaseType === 'monthly-unlimited') {
const tier = packageSection.dropdown('membershipTier')?.value() || 'standard';
const tierPrices: Record<string, number> = { 'basic': 99, 'standard': 149, 'premium': 199 };
total = tierPrices[tier] || 149;
} else if (purchaseType === 'annual') {
const plan = packageSection.dropdown('annualPlan')?.value() || 'unlimited-annual';
const planPrices: Record<string, number> = {
'basic-annual': 999, 'unlimited-annual': 1499, 'premium-annual': 1999
};
total = planPrices[plan] || 1499;
}
}
// Add-ons
let uses = 1;
if (purchaseType === 'class-pack-5') uses = 5;
else if (purchaseType === 'class-pack-10') uses = 10;
else if (purchaseType === 'class-pack-20') uses = 20;
if (purchaseType === 'monthly-unlimited' || purchaseType === 'annual') {
if (addonsSection.checkbox('lockerRental')?.value()) total += 5;
if (addonsSection.checkbox('recording')?.value()) total += 10;
if (purchaseType === 'annual') total += 15 * 11; // 12 months - already added once
} else {
if (addonsSection.checkbox('matRental')?.value()) total += 3 * uses;
if (addonsSection.checkbox('towelService')?.value()) total += 2 * uses;
if (addonsSection.checkbox('propsKit')?.value()) total += 5 * uses;
if (addonsSection.checkbox('lockerRental')?.value()) total += 2 * uses;
if (addonsSection.checkbox('showerAmenities')?.value()) total += 3 * uses;
}
// Discounts
if (discountType === 'new-student' && purchaseType === 'monthly-unlimited') {
total *= 0.5;
} else if (discountType === 'student' || discountType === 'senior') {
total *= 0.85;
} else if (discountType === 'military') {
total *= 0.8;
} else if (discountType === 'corporate') {
total *= 0.9;
} else if (discountType === 'referral') {
total -= 15;
}
if (autopay && purchaseType === 'monthly-unlimited') {
total *= 0.95;
}
return Math.round(Math.max(0, total));
},
variant: 'large'
});
});
summarySection.addRow(row => {
row.addTextPanel('perClass', {
computedValue: () => {
const purchaseType = packageSection.dropdown('purchaseType')?.value() || 'single';
if (purchaseType === 'monthly-unlimited') {
return 'Unlimited classes - attend as often as you like!';
} else if (purchaseType === 'annual') {
return 'Best value! Save over 2 months compared to monthly pricing.';
}
return 'Class packs never expire. Use at your own pace.';
},
customStyles: { 'font-size': '0.9rem', 'color': '#059669', 'text-align': 'center', 'font-weight': '500' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices may vary by location. First class is often free for new students. Contact studio for current promotions.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Book Yoga Classes'
});
}