export function martialArtsLessonsCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Martial Arts Lessons Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Discipline Section
const disciplineSection = form.addSubform('discipline', { title: '๐ฅ Discipline & Class Type' });
disciplineSection.addRow(row => {
row.addRadioButton('discipline', {
label: 'Martial Art',
options: [
{ id: 'karate', name: 'Karate' },
{ id: 'taekwondo', name: 'Taekwondo' },
{ id: 'bjj', name: 'Brazilian Jiu-Jitsu (BJJ)' },
{ id: 'judo', name: 'Judo' },
{ id: 'mma', name: 'Mixed Martial Arts (MMA)' },
{ id: 'muay-thai', name: 'Muay Thai / Kickboxing' },
{ id: 'kung-fu', name: 'Kung Fu' },
{ id: 'krav-maga', name: 'Krav Maga' }
],
defaultValue: 'karate',
orientation: 'vertical',
isRequired: true
});
});
disciplineSection.addRow(row => {
row.addDropdown('classType', {
label: 'Class Type',
options: [
{ id: 'group', name: 'Group Classes' },
{ id: 'private', name: 'Private Lessons' },
{ id: 'semi-private', name: 'Semi-Private (2-3 students)' },
{ id: 'trial', name: 'Trial Class / Intro Package' }
],
defaultValue: 'group',
isRequired: true
}, '1fr');
row.addDropdown('classesPerWeek', {
label: 'Classes per Week',
options: [
{ id: '1', name: '1 class' },
{ id: '2', name: '2 classes' },
{ id: '3', name: '3 classes' },
{ id: 'unlimited', name: 'Unlimited' }
],
defaultValue: '2',
isVisible: () => disciplineSection.dropdown('classType')?.value() === 'group'
}, '1fr');
});
// Student Info Section
const studentSection = form.addSubform('student', { title: '๐ค Student Information' });
studentSection.addRow(row => {
row.addDropdown('ageGroup', {
label: 'Age Group',
options: [
{ id: 'tiny-tigers', name: 'Tiny Tigers (4-6)' },
{ id: 'kids', name: 'Kids (7-12)' },
{ id: 'teens', name: 'Teens (13-17)' },
{ id: 'adults', name: 'Adults (18+)' }
],
defaultValue: 'adults',
isRequired: true
}, '1fr');
row.addDropdown('skillLevel', {
label: 'Current Level',
options: [
{ id: 'beginner', name: 'Beginner (White Belt)' },
{ id: 'intermediate', name: 'Intermediate (Color Belts)' },
{ id: 'advanced', name: 'Advanced (Brown/Red Belt)' },
{ id: 'black-belt', name: 'Black Belt' }
],
defaultValue: 'beginner'
}, '1fr');
});
studentSection.addRow(row => {
row.addInteger('familyMembers', {
label: 'Family Members Enrolling',
min: 1,
max: 6,
defaultValue: 1,
tooltip: 'Discounts available for multiple family members'
}, '1fr');
});
// Membership Section
const membershipSection = form.addSubform('membership', { title: '๐
Membership Duration' });
membershipSection.addRow(row => {
row.addRadioButton('duration', {
label: 'Commitment',
options: [
{ id: 'monthly', name: 'Month-to-Month' },
{ id: '3-month', name: '3 Months (5% off)' },
{ id: '6-month', name: '6 Months (10% off)' },
{ id: '12-month', name: '12 Months (15% off)' }
],
defaultValue: 'monthly',
orientation: 'horizontal',
isVisible: () => disciplineSection.dropdown('classType')?.value() !== 'trial'
});
});
// Private Lessons Section
const privateSection = form.addSubform('private', {
title: '๐ฏ Private Lesson Details',
isVisible: () => ['private', 'semi-private'].includes(disciplineSection.dropdown('classType')?.value() || '')
});
privateSection.addRow(row => {
row.addDropdown('sessionLength', {
label: 'Session Length',
options: [
{ id: '30', name: '30 Minutes' },
{ id: '45', name: '45 Minutes' },
{ id: '60', name: '1 Hour' },
{ id: '90', name: '1.5 Hours' }
],
defaultValue: '60'
}, '1fr');
row.addDropdown('sessionsPerMonth', {
label: 'Sessions per Month',
options: [
{ id: '1', name: '1 session' },
{ id: '2', name: '2 sessions' },
{ id: '4', name: '4 sessions' },
{ id: '8', name: '8 sessions' }
],
defaultValue: '4'
}, '1fr');
});
privateSection.addRow(row => {
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 Instructor' }
],
defaultValue: 'certified'
}, '1fr');
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: 'โจ Additional Services' });
addonsSection.addRow(row => {
row.addCheckbox('registration', {
label: 'Registration Fee (one-time)',
defaultValue: true
}, '1fr');
row.addCheckbox('uniform', {
label: 'Uniform (Gi/Dobok)',
defaultValue: true
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('sparringGear', {
label: 'Sparring Gear Set',
defaultValue: false
}, '1fr');
row.addCheckbox('beltTest', {
label: 'Belt Testing Fee',
defaultValue: false
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('competition', {
label: 'Competition Training',
defaultValue: false
}, '1fr');
row.addCheckbox('weapons', {
label: 'Weapons Training',
defaultValue: false,
isVisible: () => ['karate', 'taekwondo', 'kung-fu'].includes(disciplineSection.radioButton('discipline')?.value() || '')
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Section
const pricingSection = form.addSubform('pricing', { title: '๐ฐ Pricing', isCollapsible: false });
const calculatePrice = () => {
const discipline = disciplineSection.radioButton('discipline')?.value() || 'karate';
const classType = disciplineSection.dropdown('classType')?.value() || 'group';
const classesPerWeek = disciplineSection.dropdown('classesPerWeek')?.value() || '2';
const ageGroup = studentSection.dropdown('ageGroup')?.value() || 'adults';
const skillLevel = studentSection.dropdown('skillLevel')?.value() || 'beginner';
const familyMembers = studentSection.integer('familyMembers')?.value() || 1;
const duration = membershipSection.radioButton('duration')?.value() || 'monthly';
const sessionLength = parseInt(privateSection.dropdown('sessionLength')?.value() || '60');
const sessionsPerMonth = parseInt(privateSection.dropdown('sessionsPerMonth')?.value() || '4');
const instructorLevel = privateSection.dropdown('instructorLevel')?.value() || 'certified';
// Base monthly rates by discipline
const disciplineRates: Record<string, number> = {
'karate': 120,
'taekwondo': 120,
'bjj': 150,
'judo': 110,
'mma': 160,
'muay-thai': 140,
'kung-fu': 130,
'krav-maga': 150
};
let monthlyRate = disciplineRates[discipline] || 120;
// Class frequency adjustment for group classes
if (classType === 'group') {
const frequencyMult: Record<string, number> = {
'1': 0.6,
'2': 1.0,
'3': 1.3,
'unlimited': 1.5
};
monthlyRate *= frequencyMult[classesPerWeek] || 1.0;
}
// Age group adjustment
const ageMult: Record<string, number> = {
'tiny-tigers': 0.75,
'kids': 0.85,
'teens': 0.95,
'adults': 1.0
};
monthlyRate *= ageMult[ageGroup] || 1.0;
// Calculate based on class type
if (classType === 'trial') {
return {
monthly: 49,
perClass: 0,
total: 49,
savings: 0,
addonsTotal: 0,
registrationFee: 0,
uniformCost: 0,
sessions: 3,
isTrialPackage: true
};
}
// Private lesson pricing
if (classType === 'private' || classType === 'semi-private') {
const basePrivateRate = classType === 'private' ? 75 : 50;
const instructorMult: Record<string, number> = {
'assistant': 0.7,
'certified': 1.0,
'senior': 1.3,
'master': 1.8
};
let sessionRate = basePrivateRate * (sessionLength / 60);
sessionRate *= instructorMult[instructorLevel] || 1.0;
// Discipline adjustment for private lessons
if (['bjj', 'mma', 'krav-maga'].includes(discipline)) {
sessionRate *= 1.15;
}
monthlyRate = sessionRate * sessionsPerMonth;
}
// Family discount
let familyDiscount = 0;
if (familyMembers >= 2) {
familyDiscount = 0.1 + (familyMembers - 2) * 0.05;
familyDiscount = Math.min(familyDiscount, 0.25);
}
// Duration discount
const durationDiscounts: Record<string, number> = {
'monthly': 0,
'3-month': 0.05,
'6-month': 0.10,
'12-month': 0.15
};
const durationDiscount = durationDiscounts[duration] || 0;
const totalDiscount = familyDiscount + durationDiscount;
const discountedRate = monthlyRate * (1 - totalDiscount);
const perMemberRate = discountedRate;
const totalMonthly = discountedRate * familyMembers;
// Add-ons (one-time)
let addonsTotal = 0;
let registrationFee = 0;
let uniformCost = 0;
if (addonsSection.checkbox('registration')?.value()) {
registrationFee = 75 * familyMembers;
addonsTotal += registrationFee;
}
if (addonsSection.checkbox('uniform')?.value()) {
const uniformPrices: Record<string, number> = {
'karate': 65,
'taekwondo': 70,
'bjj': 120,
'judo': 80,
'mma': 50,
'muay-thai': 45,
'kung-fu': 75,
'krav-maga': 50
};
uniformCost = (uniformPrices[discipline] || 65) * familyMembers;
addonsTotal += uniformCost;
}
if (addonsSection.checkbox('sparringGear')?.value()) {
addonsTotal += 150 * familyMembers;
}
if (addonsSection.checkbox('beltTest')?.value()) {
const testFees: Record<string, number> = {
'beginner': 50,
'intermediate': 75,
'advanced': 125,
'black-belt': 250
};
addonsTotal += (testFees[skillLevel] || 50) * familyMembers;
}
if (addonsSection.checkbox('competition')?.value()) {
addonsTotal += 50 * familyMembers;
}
if (addonsSection.checkbox('weapons')?.value()) {
addonsTotal += 40 * familyMembers;
}
const savings = monthlyRate * familyMembers - totalMonthly;
return {
monthly: Math.round(perMemberRate),
perClass: Math.round(perMemberRate / (classType === 'group' ? parseInt(classesPerWeek === 'unlimited' ? '12' : classesPerWeek) * 4 : sessionsPerMonth)),
total: Math.round(totalMonthly),
savings: Math.round(savings),
addonsTotal: Math.round(addonsTotal),
registrationFee: Math.round(registrationFee),
uniformCost: Math.round(uniformCost),
sessions: classType === 'group' ? (classesPerWeek === 'unlimited' ? 12 : parseInt(classesPerWeek)) * 4 : sessionsPerMonth,
isTrialPackage: false
};
};
pricingSection.addRow(row => {
row.addPriceDisplay('monthly', {
label: () => calculatePrice().isTrialPackage ? 'Trial Package (3 classes)' : 'Monthly per Student',
computedValue: () => calculatePrice().monthly,
variant: 'default'
}, '1fr');
row.addPriceDisplay('perClass', {
label: 'Per Class/Session',
computedValue: () => calculatePrice().perClass,
variant: 'default',
isVisible: () => !calculatePrice().isTrialPackage
}, '1fr');
});
pricingSection.addRow(row => {
row.addPriceDisplay('savings', {
label: 'Monthly Savings',
computedValue: () => calculatePrice().savings,
variant: 'success',
isVisible: () => calculatePrice().savings > 0
}, '1fr');
row.addPriceDisplay('addons', {
label: 'One-Time Fees & Equipment',
computedValue: () => calculatePrice().addonsTotal,
variant: 'default',
isVisible: () => calculatePrice().addonsTotal > 0
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('total', {
label: () => {
const familyMembers = studentSection.integer('familyMembers')?.value() || 1;
return familyMembers > 1 ? `Monthly Total (${familyMembers} students)` : 'Monthly Total';
},
computedValue: () => calculatePrice().total,
variant: 'large'
}, '1fr');
row.addPriceDisplay('firstMonth', {
label: 'First Month Total',
computedValue: () => calculatePrice().total + calculatePrice().addonsTotal,
variant: 'default',
isVisible: () => calculatePrice().addonsTotal > 0
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Prices may vary by location. Belt testing and tournament fees are separate. Ask about family discounts!',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Start Training'
});
}