export function danceSchoolCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Dance School Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Dance Style Section
const styleSection = form.addSubform('style', { title: '๐ Dance Style' });
styleSection.addRow(row => {
row.addDropdown('danceStyle', {
label: 'Dance Style',
options: [
{ id: 'ballet', name: 'Ballet' },
{ id: 'jazz', name: 'Jazz' },
{ id: 'contemporary', name: 'Contemporary/Modern' },
{ id: 'hip-hop', name: 'Hip Hop' },
{ id: 'tap', name: 'Tap' },
{ id: 'ballroom', name: 'Ballroom' },
{ id: 'latin', name: 'Latin (Salsa, Bachata)' },
{ id: 'swing', name: 'Swing/Lindy Hop' },
{ id: 'belly', name: 'Belly Dance' },
{ id: 'pole', name: 'Pole Dancing' },
{ id: 'breakdance', name: 'Breakdancing' },
{ id: 'wedding', name: 'Wedding Dance' }
],
defaultValue: 'ballet',
isRequired: true
}, '1fr');
row.addDropdown('level', {
label: 'Experience Level',
options: [
{ id: 'absolute-beginner', name: 'Absolute Beginner' },
{ id: 'beginner', name: 'Beginner' },
{ id: 'intermediate', name: 'Intermediate' },
{ id: 'advanced', name: 'Advanced' },
{ id: 'pre-professional', name: 'Pre-Professional' }
],
defaultValue: 'beginner'
}, '1fr');
});
styleSection.addRow(row => {
row.addDropdown('ageGroup', {
label: 'Age Group',
options: [
{ id: 'toddler', name: 'Toddler (2-4)' },
{ id: 'child', name: 'Child (5-8)' },
{ id: 'preteen', name: 'Pre-Teen (9-12)' },
{ id: 'teen', name: 'Teen (13-17)' },
{ id: 'adult', name: 'Adult (18-54)' },
{ id: 'senior', name: 'Senior (55+)' }
],
defaultValue: 'adult'
}, '1fr');
row.addDropdown('goal', {
label: 'Your Goal',
options: [
{ id: 'fun', name: 'Fun & Fitness' },
{ id: 'skill', name: 'Skill Development' },
{ id: 'social', name: 'Social Dancing' },
{ id: 'performance', name: 'Performance/Recital' },
{ id: 'competition', name: 'Competition' },
{ id: 'wedding', name: 'Wedding Preparation' }
],
defaultValue: 'fun'
}, '1fr');
});
// Lesson Type Section
const lessonSection = form.addSubform('lesson', { title: '๐ฅ Lesson Type' });
lessonSection.addRow(row => {
row.addRadioButton('lessonType', {
label: 'Class Format',
options: [
{ id: 'private', name: 'Private (1-on-1)' },
{ id: 'semi-private', name: 'Semi-Private (2-3)' },
{ id: 'group', name: 'Group Class' },
{ id: 'couples', name: 'Couples Lesson' },
{ id: 'workshop', name: 'Workshop/Masterclass' },
{ id: 'intensive', name: 'Intensive/Camp' }
],
defaultValue: 'group',
orientation: 'vertical',
isRequired: true
});
});
// Session Details Section
const sessionSection = form.addSubform('session', { title: 'โฑ๏ธ Session Details' });
sessionSection.addRow(row => {
row.addDropdown('duration', {
label: 'Class Duration',
options: [
{ id: '30', name: '30 Minutes' },
{ id: '45', name: '45 Minutes' },
{ id: '60', name: '1 Hour' },
{ id: '90', name: '1.5 Hours' },
{ id: '120', name: '2 Hours' }
],
defaultValue: '60',
isRequired: true
}, '1fr');
row.addDropdown('instructorLevel', {
label: 'Instructor Level',
options: [
{ id: 'assistant', name: 'Assistant Instructor' },
{ id: 'certified', name: 'Certified Instructor' },
{ id: 'senior', name: 'Senior Instructor' },
{ id: 'master', name: 'Master Teacher' },
{ id: 'professional', name: 'Professional Dancer' }
],
defaultValue: 'certified'
}, '1fr');
});
sessionSection.addRow(row => {
row.addDropdown('frequency', {
label: 'Classes per Week',
options: [
{ id: '1', name: '1x per week' },
{ id: '2', name: '2x per week' },
{ id: '3', name: '3x per week' },
{ id: '4', name: '4x per week' },
{ id: '5', name: '5+ per week (unlimited)' }
],
defaultValue: '1'
}, '1fr');
row.addInteger('weddingLessons', {
label: 'Number of Lessons',
min: 1,
max: 20,
defaultValue: 6,
isVisible: () => styleSection.dropdown('danceStyle')?.value() === 'wedding'
}, '1fr');
});
// Intensive/Workshop Details
const intensiveSection = form.addSubform('intensive', {
title: '๐ญ Intensive Details',
isVisible: () => ['workshop', 'intensive'].includes(lessonSection.radioButton('lessonType')?.value() || '')
});
intensiveSection.addRow(row => {
row.addDropdown('intensiveType', {
label: 'Intensive Type',
options: [
{ id: 'single-workshop', name: 'Single Workshop (2-3 hours)' },
{ id: 'weekend', name: 'Weekend Intensive' },
{ id: 'week', name: 'Week-Long Intensive' },
{ id: 'summer', name: 'Summer Intensive' }
],
defaultValue: 'single-workshop'
}, '1fr');
row.addInteger('intensiveDays', {
label: 'Number of Days',
min: 1,
max: 30,
defaultValue: 5,
isVisible: () => intensiveSection.dropdown('intensiveType')?.value() !== 'single-workshop'
}, '1fr');
});
// Package Section
const packageSection = form.addSubform('package', { title: '๐ฆ Packages & Memberships' });
packageSection.addRow(row => {
row.addDropdown('package', {
label: 'Package Type',
options: [
{ id: 'drop-in', name: 'Drop-In Class' },
{ id: 'class-pack-5', name: '5 Class Pack (5% off)' },
{ id: 'class-pack-10', name: '10 Class Pack (10% off)' },
{ id: 'class-pack-20', name: '20 Class Pack (15% off)' },
{ id: 'monthly-unlimited', name: 'Monthly Unlimited' },
{ id: 'yearly', name: 'Annual Membership' }
],
defaultValue: 'drop-in'
}, '1fr');
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: 'โจ Add-ons' });
addonsSection.addRow(row => {
row.addCheckbox('registrationFee', {
label: 'Registration Fee (+$25)',
defaultValue: false,
tooltip: 'One-time new student fee'
}, '1fr');
row.addCheckbox('costumeRental', {
label: 'Costume/Outfit Rental (+$30)',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('shoes', {
label: 'Dance Shoes (+$40-80)',
defaultValue: false
}, '1fr');
row.addCheckbox('recitalFee', {
label: 'Recital Fee (+$75)',
defaultValue: false,
isVisible: () => styleSection.dropdown('goal')?.value() === 'performance'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('videoReview', {
label: 'Video Review (+$20)',
defaultValue: false
}, '1fr');
row.addCheckbox('choreography', {
label: 'Custom Choreography (+$100)',
defaultValue: false,
isVisible: () => styleSection.dropdown('danceStyle')?.value() === 'wedding'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Section
const pricingSection = form.addSubform('pricing', { title: '๐ฐ Pricing', isCollapsible: false });
const calculatePrice = () => {
const danceStyle = styleSection.dropdown('danceStyle')?.value() || 'ballet';
const level = styleSection.dropdown('level')?.value() || 'beginner';
const lessonType = lessonSection.radioButton('lessonType')?.value() || 'group';
const duration = parseInt(sessionSection.dropdown('duration')?.value() || '60');
const instructorLevel = sessionSection.dropdown('instructorLevel')?.value() || 'certified';
const frequency = parseInt(sessionSection.dropdown('frequency')?.value() || '1');
const packageType = packageSection.dropdown('package')?.value() || 'drop-in';
// Base rates per hour by lesson type
const lessonRates: Record<string, number> = {
'private': 75,
'semi-private': 50,
'group': 20,
'couples': 90,
'workshop': 0,
'intensive': 0
};
let baseRate = lessonRates[lessonType] || 20;
// Dance style multiplier
const styleMult: Record<string, number> = {
'ballet': 1.15,
'jazz': 1.0,
'contemporary': 1.1,
'hip-hop': 1.0,
'tap': 1.0,
'ballroom': 1.2,
'latin': 1.1,
'swing': 1.0,
'belly': 1.0,
'pole': 1.3,
'breakdance': 1.1,
'wedding': 1.25
};
baseRate *= styleMult[danceStyle] || 1.0;
// Instructor level
const instructorMult: Record<string, number> = {
'assistant': 0.8,
'certified': 1.0,
'senior': 1.25,
'master': 1.5,
'professional': 2.0
};
baseRate *= instructorMult[instructorLevel] || 1.0;
// Level adjustment
const levelMult: Record<string, number> = {
'absolute-beginner': 1.0,
'beginner': 1.0,
'intermediate': 1.05,
'advanced': 1.15,
'pre-professional': 1.3
};
baseRate *= levelMult[level] || 1.0;
// Duration adjustment
let classPrice = baseRate * (duration / 60);
// Workshop/Intensive pricing
if (lessonType === 'workshop' || lessonType === 'intensive') {
const intensiveType = intensiveSection.dropdown('intensiveType')?.value() || 'single-workshop';
const days = intensiveSection.integer('intensiveDays')?.value() || 5;
const intensiveRates: Record<string, number> = {
'single-workshop': 45,
'weekend': 150,
'week': 300,
'summer': 200
};
if (intensiveType === 'single-workshop') {
classPrice = intensiveRates[intensiveType] ?? 0;
} else if (intensiveType === 'summer') {
classPrice = (intensiveRates[intensiveType] ?? 0) * days;
} else {
classPrice = (intensiveRates[intensiveType] ?? 0) * (days / 2);
}
}
// Wedding dance specific
if (danceStyle === 'wedding') {
const weddingLessons = sessionSection.integer('weddingLessons')?.value() || 6;
const weddingPackagePrice = baseRate * (duration / 60) * weddingLessons * 0.9;
if (lessonType === 'private' || lessonType === 'couples') {
classPrice = weddingPackagePrice / weddingLessons;
}
}
// Add-ons
let addons = 0;
if (addonsSection.checkbox('registrationFee')?.value()) addons += 25;
if (addonsSection.checkbox('costumeRental')?.value()) addons += 30;
if (addonsSection.checkbox('shoes')?.value()) addons += 60;
if (addonsSection.checkbox('recitalFee')?.value()) addons += 75;
if (addonsSection.checkbox('videoReview')?.value()) addons += 20;
if (addonsSection.checkbox('choreography')?.value()) addons += 100;
// Package discounts and calculations
const packageDiscounts: Record<string, number> = {
'drop-in': 0,
'class-pack-5': 0.05,
'class-pack-10': 0.10,
'class-pack-20': 0.15,
'monthly-unlimited': 0.25,
'yearly': 0.35
};
const discount = packageDiscounts[packageType] || 0;
const packageQuantities: Record<string, number> = {
'drop-in': 1,
'class-pack-5': 5,
'class-pack-10': 10,
'class-pack-20': 20,
'monthly-unlimited': frequency * 4,
'yearly': frequency * 48
};
const sessions = packageQuantities[packageType] || 1;
const discountedPrice = classPrice * (1 - discount);
let packageTotal = discountedPrice * sessions + addons;
// Monthly unlimited cap
if (packageType === 'monthly-unlimited') {
const unlimitedMonthly = Math.min(packageTotal, 150 * (frequency / 3));
packageTotal = unlimitedMonthly + addons;
}
const monthlyEstimate = discountedPrice * frequency * 4;
return {
perClass: Math.round(classPrice),
discountedClass: Math.round(discountedPrice),
packageTotal: Math.round(packageTotal),
monthlyEstimate: Math.round(monthlyEstimate),
discount: Math.round(discount * 100),
sessions,
addons: Math.round(addons),
duration
};
};
pricingSection.addRow(row => {
row.addPriceDisplay('perClass', {
label: () => `Per Class (${calculatePrice().duration} min)`,
computedValue: () => calculatePrice().perClass,
variant: 'default'
}, '1fr');
row.addPriceDisplay('discounted', {
label: () => `After ${calculatePrice().discount}% Discount`,
computedValue: () => calculatePrice().discountedClass,
variant: 'success',
isVisible: () => calculatePrice().discount > 0
}, '1fr');
});
pricingSection.addRow(row => {
row.addPriceDisplay('addons', {
label: 'Add-ons & Fees',
computedValue: () => calculatePrice().addons,
variant: 'default',
isVisible: () => calculatePrice().addons > 0
});
});
// 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} classes)` : 'Class Total',
computedValue: () => calculatePrice().packageTotal,
variant: 'large'
}, '1fr');
row.addPriceDisplay('monthly', {
label: 'Monthly Estimate',
computedValue: () => calculatePrice().monthlyEstimate,
variant: 'default',
isVisible: () => calculatePrice().sessions === 1
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Prices may vary by location. Dance attire not included.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Register for Classes'
});
}