export function skiSnowboardLessonsCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Ski & Snowboard Lessons Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Sport & Lesson Type Section
const sportSection = form.addSubform('sport', { title: '๐ฟ Sport & Lesson Type' });
sportSection.addRow(row => {
row.addRadioButton('sport', {
label: 'Sport',
options: [
{ id: 'ski', name: 'Skiing' },
{ id: 'snowboard', name: 'Snowboarding' },
{ id: 'both', name: 'Both (Multi-Sport)' }
],
defaultValue: 'ski',
orientation: 'horizontal',
isRequired: true
});
});
sportSection.addRow(row => {
row.addRadioButton('lessonType', {
label: 'Lesson Type',
options: [
{ id: 'group', name: 'Group Lesson' },
{ id: 'private', name: 'Private Lesson' },
{ id: 'semi-private', name: 'Semi-Private (2-3 people)' },
{ id: 'multi-day', name: 'Multi-Day Program' },
{ id: 'first-timer', name: 'First-Timer Package' }
],
defaultValue: 'group',
orientation: 'vertical',
isRequired: true
});
});
// Student Info Section
const studentSection = form.addSubform('student', { title: '๐ค Student Information' });
studentSection.addRow(row => {
row.addDropdown('ageGroup', {
label: 'Age Group',
options: [
{ id: 'mini', name: 'Mini (3-4 years)' },
{ id: 'kids', name: 'Kids (5-7 years)' },
{ id: 'junior', name: 'Junior (8-12 years)' },
{ id: 'teen', name: 'Teen (13-17 years)' },
{ id: 'adult', name: 'Adult (18+)' },
{ id: 'senior', name: 'Senior (65+)' }
],
defaultValue: 'adult',
isRequired: true
}, '1fr');
row.addDropdown('skillLevel', {
label: 'Skill Level',
options: [
{ id: 'first-timer', name: 'First Timer' },
{ id: 'beginner', name: 'Beginner (Green Runs)' },
{ id: 'intermediate', name: 'Intermediate (Blue Runs)' },
{ id: 'advanced', name: 'Advanced (Black Runs)' },
{ id: 'expert', name: 'Expert (Double Black)' }
],
defaultValue: 'first-timer'
}, '1fr');
});
studentSection.addRow(row => {
row.addInteger('participants', {
label: 'Number of Participants',
min: 1,
max: 8,
defaultValue: 1,
tooltip: 'Group discounts available for 3+'
}, '1fr');
});
// Lesson Details Section
const lessonSection = form.addSubform('lesson', { title: 'โฑ๏ธ Lesson Details' });
lessonSection.addRow(row => {
row.addDropdown('duration', {
label: 'Lesson Duration',
options: [
{ id: '1', name: '1 Hour' },
{ id: '2', name: '2 Hours' },
{ id: '3', name: '3 Hours (Half Day)' },
{ id: '6', name: '6 Hours (Full Day)' }
],
defaultValue: '2',
isVisible: () => !['multi-day', 'first-timer'].includes(sportSection.radioButton('lessonType')?.value() || '')
}, '1fr');
row.addDropdown('instructorLevel', {
label: 'Instructor Level',
options: [
{ id: 'certified', name: 'Certified Instructor' },
{ id: 'senior', name: 'Senior Instructor' },
{ id: 'expert', name: 'Expert/Race Coach' },
{ id: 'olympian', name: 'Former Pro/Olympian' }
],
defaultValue: 'certified',
isVisible: () => ['private', 'semi-private'].includes(sportSection.radioButton('lessonType')?.value() || '')
}, '1fr');
});
// Multi-Day Section
const multiDaySection = form.addSubform('multiDay', {
title: '๐
Multi-Day Program',
isVisible: () => sportSection.radioButton('lessonType')?.value() === 'multi-day'
});
multiDaySection.addRow(row => {
row.addDropdown('days', {
label: 'Number of Days',
options: [
{ id: '2', name: '2 Days' },
{ id: '3', name: '3 Days' },
{ id: '4', name: '4 Days' },
{ id: '5', name: '5 Days (Full Week)' }
],
defaultValue: '3'
}, '1fr');
row.addDropdown('dailyHours', {
label: 'Hours per Day',
options: [
{ id: '3', name: '3 Hours (Half Day)' },
{ id: '6', name: '6 Hours (Full Day)' }
],
defaultValue: '3'
}, '1fr');
});
// Equipment & Rentals Section
const equipmentSection = form.addSubform('equipment', { title: '๐ฟ Equipment & Add-ons' });
equipmentSection.addRow(row => {
row.addCheckbox('equipmentRental', {
label: 'Equipment Rental (skis/board, boots, poles)',
defaultValue: true
}, '1fr');
row.addCheckbox('helmetRental', {
label: 'Helmet Rental',
defaultValue: true
}, '1fr');
});
equipmentSection.addRow(row => {
row.addCheckbox('liftTicket', {
label: 'Lift Ticket Included',
defaultValue: false
}, '1fr');
row.addDropdown('liftType', {
label: 'Lift Ticket Type',
options: [
{ id: 'beginner', name: 'Beginner Area Only' },
{ id: 'half-day', name: 'Half Day (AM or PM)' },
{ id: 'full-day', name: 'Full Day' }
],
defaultValue: 'beginner',
isVisible: () => equipmentSection.checkbox('liftTicket')?.value() === true
}, '1fr');
});
equipmentSection.addRow(row => {
row.addCheckbox('videoAnalysis', {
label: 'Video Analysis (+$50)',
defaultValue: false,
isVisible: () => ['private', 'semi-private'].includes(sportSection.radioButton('lessonType')?.value() || '')
}, '1fr');
row.addCheckbox('lunch', {
label: 'Lunch Included',
defaultValue: false,
isVisible: () => lessonSection.dropdown('duration')?.value() === '6' || sportSection.radioButton('lessonType')?.value() === 'multi-day'
}, '1fr');
});
// Season & Location Section
const seasonSection = form.addSubform('season', { title: '๐๏ธ Season & Location' });
seasonSection.addRow(row => {
row.addDropdown('season', {
label: 'Time of Season',
options: [
{ id: 'early', name: 'Early Season (Nov-Dec)' },
{ id: 'peak', name: 'Peak Season (Dec 20 - Jan 5)' },
{ id: 'regular', name: 'Regular Season (Jan-Mar)' },
{ id: 'spring', name: 'Spring Season (Apr)' }
],
defaultValue: 'regular'
}, '1fr');
row.addDropdown('resortTier', {
label: 'Resort Type',
options: [
{ id: 'local', name: 'Local/Regional Resort' },
{ id: 'destination', name: 'Destination Resort' },
{ id: 'premier', name: 'Premier Resort (Vail, Aspen, etc.)' }
],
defaultValue: 'destination'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Section
const pricingSection = form.addSubform('pricing', { title: '๐ฐ Pricing', isCollapsible: false });
const calculatePrice = () => {
const sport = sportSection.radioButton('sport')?.value() || 'ski';
const lessonType = sportSection.radioButton('lessonType')?.value() || 'group';
const ageGroup = studentSection.dropdown('ageGroup')?.value() || 'adult';
const skillLevel = studentSection.dropdown('skillLevel')?.value() || 'first-timer';
const participants = studentSection.integer('participants')?.value() || 1;
const duration = parseInt(lessonSection.dropdown('duration')?.value() || '2');
const instructorLevel = lessonSection.dropdown('instructorLevel')?.value() || 'certified';
const days = parseInt(multiDaySection.dropdown('days')?.value() || '3');
const dailyHours = parseInt(multiDaySection.dropdown('dailyHours')?.value() || '3');
const season = seasonSection.dropdown('season')?.value() || 'regular';
const resortTier = seasonSection.dropdown('resortTier')?.value() || 'destination';
// Base rates per hour
let baseRate = 0;
if (lessonType === 'group') {
baseRate = 60;
} else if (lessonType === 'private') {
baseRate = 150;
} else if (lessonType === 'semi-private') {
baseRate = 100;
} else if (lessonType === 'first-timer') {
// First-timer package is fixed price
let firstTimerPrice = 175;
if (resortTier === 'premier') firstTimerPrice = 250;
if (resortTier === 'local') firstTimerPrice = 125;
if (season === 'peak') firstTimerPrice *= 1.3;
return {
lessonCost: Math.round(firstTimerPrice * participants),
equipmentCost: 0,
liftCost: 0,
addons: 0,
total: Math.round(firstTimerPrice * participants),
perPerson: Math.round(firstTimerPrice),
savings: 0,
isPackage: true,
packageIncludes: 'Lesson, equipment rental, beginner lift ticket'
};
} else if (lessonType === 'multi-day') {
baseRate = dailyHours === 6 ? 100 : 65;
}
// Resort tier multiplier
const tierMult: Record<string, number> = {
'local': 0.7,
'destination': 1.0,
'premier': 1.5
};
baseRate *= tierMult[resortTier] || 1.0;
// Season multiplier
const seasonMult: Record<string, number> = {
'early': 0.9,
'peak': 1.4,
'regular': 1.0,
'spring': 0.85
};
baseRate *= seasonMult[season] || 1.0;
// Instructor level multiplier for private lessons
if (['private', 'semi-private'].includes(lessonType)) {
const instructorMult: Record<string, number> = {
'certified': 1.0,
'senior': 1.25,
'expert': 1.5,
'olympian': 2.5
};
baseRate *= instructorMult[instructorLevel] || 1.0;
}
// Age group adjustment (kids programs often cost more due to ratios)
if (['mini', 'kids'].includes(ageGroup) && lessonType === 'group') {
baseRate *= 1.1;
}
// Calculate lesson cost
let lessonCost = 0;
if (lessonType === 'multi-day') {
const dailyCost = baseRate * dailyHours;
const dayDiscount = 1 - ((days - 1) * 0.05); // 5% off per additional day
lessonCost = dailyCost * days * Math.max(dayDiscount, 0.8);
} else {
lessonCost = baseRate * duration;
}
// Group discount for 3+ people
if (participants >= 3 && ['private', 'semi-private'].includes(lessonType)) {
lessonCost *= 0.9;
}
// Multi-sport surcharge
if (sport === 'both') {
lessonCost *= 1.15;
}
const totalLessonCost = lessonCost * (lessonType === 'group' ? participants : 1);
// Equipment costs
let equipmentCost = 0;
if (equipmentSection.checkbox('equipmentRental')?.value()) {
const equipBase = sport === 'snowboard' ? 55 : 50;
equipmentCost += equipBase * participants;
if (lessonType === 'multi-day') {
equipmentCost *= days * 0.85; // Multi-day rental discount
}
}
if (equipmentSection.checkbox('helmetRental')?.value()) {
let helmetCost = 15 * participants;
if (lessonType === 'multi-day') {
helmetCost *= days * 0.85;
}
equipmentCost += helmetCost;
}
// Lift ticket costs
let liftCost = 0;
if (equipmentSection.checkbox('liftTicket')?.value()) {
const liftType = equipmentSection.dropdown('liftType')?.value() || 'beginner';
const liftPrices: Record<string, number> = {
'beginner': 45,
'half-day': 95,
'full-day': 150
};
liftCost = (liftPrices[liftType] || 45) * participants;
if (resortTier === 'premier') liftCost *= 1.4;
if (resortTier === 'local') liftCost *= 0.7;
if (lessonType === 'multi-day') liftCost *= days;
}
// Add-ons
let addons = 0;
if (equipmentSection.checkbox('videoAnalysis')?.value()) {
addons += 50;
}
if (equipmentSection.checkbox('lunch')?.value()) {
let lunchCost = 25 * participants;
if (lessonType === 'multi-day') lunchCost *= days;
addons += lunchCost;
}
const total = totalLessonCost + equipmentCost + liftCost + addons;
const perPerson = total / participants;
return {
lessonCost: Math.round(totalLessonCost),
equipmentCost: Math.round(equipmentCost),
liftCost: Math.round(liftCost),
addons: Math.round(addons),
total: Math.round(total),
perPerson: Math.round(perPerson),
savings: 0,
isPackage: false,
packageIncludes: ''
};
};
pricingSection.addRow(row => {
row.addPriceDisplay('lessonCost', {
label: () => {
const lessonType = sportSection.radioButton('lessonType')?.value();
if (lessonType === 'first-timer') return 'First-Timer Package';
if (lessonType === 'multi-day') return 'Multi-Day Program';
return 'Lesson Cost';
},
computedValue: () => calculatePrice().lessonCost,
variant: 'default'
}, '1fr');
row.addPriceDisplay('equipment', {
label: 'Equipment Rental',
computedValue: () => calculatePrice().equipmentCost,
variant: 'default',
isVisible: () => calculatePrice().equipmentCost > 0 && !calculatePrice().isPackage
}, '1fr');
});
pricingSection.addRow(row => {
row.addPriceDisplay('liftTicket', {
label: 'Lift Ticket(s)',
computedValue: () => calculatePrice().liftCost,
variant: 'default',
isVisible: () => calculatePrice().liftCost > 0 && !calculatePrice().isPackage
}, '1fr');
row.addPriceDisplay('addons', {
label: 'Add-ons',
computedValue: () => calculatePrice().addons,
variant: 'default',
isVisible: () => calculatePrice().addons > 0
}, '1fr');
});
pricingSection.addRow(row => {
row.addTextPanel('packageNote', {
computedValue: () => `Package includes: ${calculatePrice().packageIncludes}`,
customStyles: { 'font-size': '0.9rem', 'color': '#059669', 'font-weight': '500' },
isVisible: () => calculatePrice().isPackage
});
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('total', {
label: () => {
const participants = studentSection.integer('participants')?.value() || 1;
return participants > 1 ? `Total (${participants} people)` : 'Total';
},
computedValue: () => calculatePrice().total,
variant: 'large'
}, '1fr');
row.addPriceDisplay('perPerson', {
label: 'Per Person',
computedValue: () => calculatePrice().perPerson,
variant: 'default',
isVisible: () => (studentSection.integer('participants')?.value() || 1) > 1
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Prices vary by resort and availability. Book early for peak season dates. Gratuity for instructors is customary (15-20%).',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Book Lessons'
});
}