export function marathonReadinessQuiz(form: FormTs) {
form.setTitle(() => '🏃 Is Your Body Ready for Marathon Training?');
// ============ SCORING SYSTEM ============
const scores = form.state<Record<string, number>>({});
const updateScore = (category: string, points: number) => {
scores.update(current => ({ ...current, [category]: points }));
};
const getTotalScore = () => {
const s = scores();
return Object.values(s).reduce((sum, val) => sum + (val || 0), 0);
};
const getMaxScore = () => 100;
const getScorePercentage = () => Math.round((getTotalScore() / getMaxScore()) * 100);
const getReadinessLevel = (): 'notReady' | 'needsWork' | 'almostReady' | 'ready' => {
const pct = getScorePercentage();
if (pct >= 80) return 'ready';
if (pct >= 60) return 'almostReady';
if (pct >= 40) return 'needsWork';
return 'notReady';
};
const getReadinessLabel = () => {
const level = getReadinessLevel();
const labels = {
notReady: '🚫 Not Ready Yet',
needsWork: '⚠️ Needs Foundation Work',
almostReady: '🏃 Almost Ready',
ready: '✅ Ready to Train!'
};
return labels[level];
};
const getReadinessColor = () => {
const level = getReadinessLevel();
const colors = {
notReady: '#dc2626',
needsWork: '#ea580c',
almostReady: '#ca8a04',
ready: '#16a34a'
};
return colors[level];
};
const getRecommendedTimeline = () => {
const level = getReadinessLevel();
const timelines = {
notReady: '9-12 months to your first marathon',
needsWork: '6-9 months with proper build-up',
almostReady: '4-6 months of focused training',
ready: '16-20 weeks on a structured plan'
};
return timelines[level];
};
// ============ COMPLETION SCREEN ============
form.configureCompletionScreen({
type: 'text',
title: () => getReadinessLabel(),
message: () => {
const level = getReadinessLevel();
const pct = getScorePercentage();
const messages = {
notReady: `Your readiness score is ${pct}%. Before starting marathon training, you need to build a stronger foundation. Focus on consistent running, injury prevention, and overall fitness. Your personalized report includes a build-up plan.`,
needsWork: `Your readiness score is ${pct}%. You have some running experience but gaps to address. Recommended timeline: ${getRecommendedTimeline()}. Your report includes specific areas to improve.`,
almostReady: `Your readiness score is ${pct}%. You're close! A few more weeks of base building and you'll be marathon-ready. Timeline: ${getRecommendedTimeline()}.`,
ready: `Excellent! Your readiness score is ${pct}%. You have the foundation for marathon training. Timeline: ${getRecommendedTimeline()}. Download your personalized training approach.`
};
return messages[level];
}
});
// ============ PAGES SETUP ============
const pages = form.addPages('quiz-pages', {
heightMode: 'current-page'
});
// ============ PAGE 1: Running Background ============
const page1 = pages.addPage('running-background', { mobileBreakpoint: 500 });
page1.addRow(row => {
row.addTextPanel('header1', {
label: 'Step 1 of 6: Running Background',
computedValue: () => 'Let\'s assess your current running experience',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page1.addSpacer({ height: '24px' });
page1.addRow(row => {
row.addRadioButton('runningExperience', {
label: 'How long have you been running regularly?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'beginner', name: '🆕 Less than 6 months' },
{ id: 'novice', name: '📅 6 months to 1 year' },
{ id: 'intermediate', name: '🏃 1-2 years' },
{ id: 'experienced', name: '🏅 2+ years' }
],
onValueChange: (val) => {
const points = { beginner: 5, novice: 10, intermediate: 18, experienced: 25 };
updateScore('experience', points[val as keyof typeof points] || 0);
}
});
});
page1.addRow(row => {
row.addRadioButton('weeklyMileage', {
label: 'What\'s your current weekly running mileage/km?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'low', name: '📏 Less than 15 miles (25 km)' },
{ id: 'moderate', name: '📏 15-25 miles (25-40 km)' },
{ id: 'good', name: '📏 25-35 miles (40-55 km)' },
{ id: 'high', name: '📏 35+ miles (55+ km)' }
],
onValueChange: (val) => {
const points = { low: 5, moderate: 10, good: 18, high: 25 };
updateScore('mileage', points[val as keyof typeof points] || 0);
}
});
});
page1.addRow(row => {
row.addRadioButton('longestRun', {
label: 'What\'s the longest run you\'ve completed in the past 3 months?',
isRequired: true,
orientation: 'vertical',
tooltip: 'Long runs are crucial for marathon preparation',
options: [
{ id: 'short', name: '🏃 Less than 6 miles (10 km)' },
{ id: 'moderate', name: '🏃 6-10 miles (10-16 km)' },
{ id: 'halfMarathon', name: '🏃 10-13 miles (16-21 km - half marathon distance)' },
{ id: 'long', name: '🏃 13+ miles (21+ km)' }
],
onValueChange: (val) => {
const points = { short: 2, moderate: 8, halfMarathon: 15, long: 20 };
updateScore('longRun', points[val as keyof typeof points] || 0);
}
});
});
// ============ PAGE 2: Race Experience ============
const page2 = pages.addPage('race-experience', { mobileBreakpoint: 500 });
page2.addRow(row => {
row.addTextPanel('header2', {
label: 'Step 2 of 6: Race Experience',
computedValue: () => 'Have you competed in running events before?',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page2.addSpacer({ height: '24px' });
page2.addRow(row => {
row.addSuggestionChips('raceHistory', {
label: 'Which distances have you raced? (Select all that apply)',
min: 0,
suggestions: [
{ id: '5k', name: '5K' },
{ id: '10k', name: '10K' },
{ id: 'half', name: 'Half Marathon' },
{ id: 'marathon', name: 'Marathon' },
{ id: 'ultra', name: 'Ultra Marathon' },
{ id: 'none', name: 'No races yet' }
],
onValueChange: (vals) => {
if (!vals || vals.length === 0 || vals.includes('none')) {
updateScore('raceExp', 0);
return;
}
let points = 0;
if (vals.includes('5k')) points += 2;
if (vals.includes('10k')) points += 3;
if (vals.includes('half')) points += 5;
if (vals.includes('marathon')) points += 8;
if (vals.includes('ultra')) points += 10;
updateScore('raceExp', Math.min(points, 10));
}
});
});
page2.addRow(row => {
row.addRadioButton('recentRace', {
label: 'When was your most recent race?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'never', name: '❌ Never raced' },
{ id: 'over2years', name: '📅 Over 2 years ago' },
{ id: 'within2years', name: '📅 Within the past 2 years' },
{ id: 'within6months', name: '📅 Within the past 6 months' }
]
});
});
page2.addRow(row => {
row.addTextPanel('raceScore', {
computedValue: () => {
const s = scores();
const score = s['experience'] || 0;
return `🏃 Running Base Score: ${score + (s['mileage'] || 0) + (s['longRun'] || 0)}/70`;
},
customStyles: {
fontSize: '1rem',
fontWeight: '600',
color: '#1e40af',
textAlign: 'center',
padding: '12px',
background: '#dbeafe',
borderRadius: '8px',
marginTop: '1rem'
}
});
});
// ============ PAGE 3: Physical Health ============
const page3 = pages.addPage('physical-health', { mobileBreakpoint: 500 });
page3.addRow(row => {
row.addTextPanel('header3', {
label: 'Step 3 of 6: Physical Health',
computedValue: () => 'Marathon training puts significant stress on your body',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page3.addSpacer({ height: '24px' });
page3.addRow(row => {
row.addRadioButton('injuryHistory', {
label: 'Have you had any running injuries in the past 12 months?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'none', name: '✅ No injuries' },
{ id: 'minor', name: '🤕 Minor injury (1-2 weeks off)' },
{ id: 'moderate', name: '🤕 Moderate injury (2-6 weeks off)' },
{ id: 'major', name: '🤕 Major injury (6+ weeks off)' },
{ id: 'current', name: '⚠️ Currently injured' }
],
onValueChange: (val) => {
const points = { none: 10, minor: 7, moderate: 4, major: 2, current: 0 };
updateScore('injury', points[val as keyof typeof points] || 0);
}
});
});
page3.addRow(row => {
row.addRadioButton('crossTraining', {
label: 'Do you do strength training or cross-training?',
isRequired: true,
orientation: 'vertical',
tooltip: 'Strength work helps prevent injuries and improves running economy',
options: [
{ id: 'none', name: '❌ No, running only' },
{ id: 'occasional', name: '🏋️ Occasionally (1x/month)' },
{ id: 'regular', name: '🏋️ Regularly (1-2x/week)' },
{ id: 'consistent', name: '🏋️ Consistently (2-3x/week)' }
],
onValueChange: (val) => {
const points = { none: 0, occasional: 3, regular: 7, consistent: 10 };
updateScore('strength', points[val as keyof typeof points] || 0);
}
});
});
page3.addRow(row => {
row.addRadioButton('bmi', {
label: 'How would you describe your current body weight?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'ideal', name: '✅ Ideal racing weight' },
{ id: 'close', name: '👍 Close to ideal (within 10 lbs/5 kg)' },
{ id: 'needsWork', name: '⚡ Could lose some weight for performance' },
{ id: 'significant', name: '⚠️ Significantly overweight for running' }
],
onValueChange: (val) => {
const points = { ideal: 5, close: 4, needsWork: 2, significant: 0 };
updateScore('weight', points[val as keyof typeof points] || 0);
}
});
});
// ============ PAGE 4: Lifestyle Factors ============
const page4 = pages.addPage('lifestyle', { mobileBreakpoint: 500 });
page4.addRow(row => {
row.addTextPanel('header4', {
label: 'Step 4 of 6: Lifestyle & Recovery',
computedValue: () => 'Marathon training requires lifestyle commitment',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page4.addSpacer({ height: '24px' });
page4.addRow(row => {
row.addSlider('sleepHours', {
label: 'How many hours of sleep do you typically get?',
isRequired: true,
min: 4,
max: 10,
step: 0.5,
defaultValue: 7,
showValue: true,
unit: 'hours',
onValueChange: (val) => {
if (!val) return;
let points = 0;
if (val >= 8) points = 5;
else if (val >= 7) points = 4;
else if (val >= 6) points = 2;
else points = 0;
updateScore('sleep', points);
}
});
});
page4.addRow(row => {
row.addSlider('weeklyHours', {
label: 'How many hours per week can you dedicate to training?',
isRequired: true,
min: 2,
max: 15,
step: 1,
defaultValue: 6,
showValue: true,
unit: 'hours',
tooltip: 'Include running, strength work, stretching, and recovery',
onValueChange: (val) => {
if (!val) return;
let points = 0;
if (val >= 8) points = 5;
else if (val >= 6) points = 4;
else if (val >= 4) points = 2;
else points = 1;
updateScore('time', points);
}
});
});
page4.addRow(row => {
row.addRadioButton('nutrition', {
label: 'How would you rate your nutrition habits?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'poor', name: '🍔 Poor - eat whatever, whenever' },
{ id: 'average', name: '🍽️ Average - somewhat balanced' },
{ id: 'good', name: '🥗 Good - mostly healthy, some planning' },
{ id: 'excellent', name: '🏆 Excellent - strategic fueling for performance' }
],
onValueChange: (val) => {
const points = { poor: 0, average: 2, good: 4, excellent: 5 };
updateScore('nutrition', points[val as keyof typeof points] || 0);
}
});
});
// ============ PAGE 5: Mental Readiness ============
const page5 = pages.addPage('mental', { mobileBreakpoint: 500 });
page5.addRow(row => {
row.addTextPanel('header5', {
label: 'Step 5 of 6: Mental Readiness',
computedValue: () => 'The marathon is as much mental as physical',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page5.addSpacer({ height: '24px' });
page5.addRow(row => {
row.addRatingScale('motivation', {
label: 'How motivated are you to complete a marathon?',
isRequired: true,
preset: 'custom',
min: 1,
max: 5,
lowLabel: 'Curious',
highLabel: 'Absolutely committed',
variant: 'buttons',
alignment: 'center'
});
});
page5.addRow(row => {
row.addRadioButton('goal', {
label: 'What\'s your marathon goal?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'finish', name: '🏁 Just finish - time doesn\'t matter' },
{ id: 'comfortable', name: '😊 Finish comfortably - enjoy the experience' },
{ id: 'pr', name: '⏱️ Personal Record - have a time goal' },
{ id: 'qualify', name: '🏆 Qualify for a major (Boston, etc.)' }
]
});
});
page5.addRow(row => {
row.addRadioButton('supportSystem', {
label: 'Do you have support for your marathon journey?',
isRequired: true,
orientation: 'vertical',
options: [
{ id: 'none', name: '🚶 Solo - training completely alone' },
{ id: 'virtual', name: '💻 Virtual - online community/apps' },
{ id: 'friends', name: '👥 Running friends/group' },
{ id: 'coach', name: '🎯 Coach or structured program' }
]
});
});
// ============ PAGE 6: Results ============
const page6 = pages.addPage('results', { mobileBreakpoint: 500 });
page6.addRow(row => {
row.addTextPanel('header6', {
label: 'Step 6 of 6: Your Marathon Readiness',
computedValue: () => 'Here\'s your personalized assessment',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page6.addSpacer({ height: '24px' });
page6.addRow(row => {
row.addTextPanel('readinessLevel', {
computedValue: () => getReadinessLabel(),
customStyles: () => ({
fontSize: '1.8rem',
fontWeight: '800',
textAlign: 'center',
color: getReadinessColor(),
padding: '20px',
background: '#f9fafb',
borderRadius: '12px',
border: `3px solid ${getReadinessColor()}`
})
});
});
page6.addRow(row => {
row.addTextPanel('scoreDisplay', {
computedValue: () => `Readiness Score: ${getScorePercentage()}%`,
customStyles: {
fontSize: '1.2rem',
fontWeight: '600',
color: '#374151',
textAlign: 'center',
marginTop: '10px'
}
});
});
page6.addRow(row => {
row.addTextPanel('timeline', {
computedValue: () => `Recommended timeline: ${getRecommendedTimeline()}`,
customStyles: {
fontSize: '1rem',
fontWeight: '600',
color: '#7c3aed',
textAlign: 'center',
padding: '12px',
background: '#f5f3ff',
borderRadius: '8px',
marginTop: '15px'
}
});
});
// Score breakdown
const breakdownSection = page6.addSubform('breakdown', {
title: '📊 Readiness Breakdown',
isCollapsible: true,
customStyles: {
marginTop: '1.5rem',
background: '#f9fafb',
borderRadius: '8px'
}
});
breakdownSection.addRow(row => {
row.addTextPanel('runningBase', {
label: '🏃 Running Base',
computedValue: () => {
const s = scores();
const score = (s['experience'] || 0) + (s['mileage'] || 0) + (s['longRun'] || 0);
return `${score}/70 points`;
},
customStyles: { padding: '8px 12px', background: '#dbeafe', borderRadius: '6px', marginBottom: '8px' }
}, '1fr');
row.addTextPanel('physical', {
label: '💪 Physical Health',
computedValue: () => {
const s = scores();
const score = (s['injury'] || 0) + (s['strength'] || 0) + (s['weight'] || 0);
return `${score}/25 points`;
},
customStyles: { padding: '8px 12px', background: '#dbeafe', borderRadius: '6px', marginBottom: '8px' }
}, '1fr');
});
breakdownSection.addRow(row => {
row.addTextPanel('lifestyle', {
label: '🌙 Lifestyle',
computedValue: () => {
const s = scores();
const score = (s['sleep'] || 0) + (s['time'] || 0) + (s['nutrition'] || 0);
return `${score}/15 points`;
},
customStyles: { padding: '8px 12px', background: '#dbeafe', borderRadius: '6px', marginBottom: '8px' }
}, '1fr');
row.addTextPanel('raceExpScore', {
label: '🏅 Race Experience',
computedValue: () => {
const s = scores();
return `${s['raceExp'] || 0}/10 points`;
},
customStyles: { padding: '8px 12px', background: '#dbeafe', borderRadius: '6px', marginBottom: '8px' }
}, '1fr');
});
// ============ PAGE 7: Lead Capture ============
const page7 = pages.addPage('lead-capture', { mobileBreakpoint: 500 });
page7.addRow(row => {
row.addTextPanel('header7', {
label: 'Step 7 of 7: Get Your Training Plan',
computedValue: () => 'Enter your details to receive personalized recommendations',
customStyles: {
fontSize: '0.9rem',
color: '#6b7280',
marginBottom: '1rem'
}
});
});
page7.addSpacer({ height: '24px' });
page7.addRow(row => {
row.addTextbox('name', {
label: 'Your Name',
isRequired: true,
placeholder: 'Alex Runner'
}, '1fr');
row.addEmail('email', {
label: 'Email Address',
isRequired: true,
placeholder: 'alex@runner.com'
}, '1fr');
});
page7.addRow(row => {
row.addDropdown('targetRace', {
label: 'Target Marathon (optional)',
options: [
{ id: 'unsure', name: 'Not sure yet' },
{ id: 'local', name: 'Local marathon' },
{ id: 'nyc', name: 'New York City Marathon' },
{ id: 'chicago', name: 'Chicago Marathon' },
{ id: 'boston', name: 'Boston Marathon' },
{ id: 'london', name: 'London Marathon' },
{ id: 'berlin', name: 'Berlin Marathon' },
{ id: 'other', name: 'Other major marathon' }
],
placeholder: 'Select a race'
}, '1fr');
row.addDropdown('targetDate', {
label: 'Target Date (optional)',
options: [
{ id: 'unsure', name: 'Not sure yet' },
{ id: '3months', name: 'In 3-4 months' },
{ id: '6months', name: 'In 5-6 months' },
{ id: '9months', name: 'In 7-9 months' },
{ id: '12months', name: 'In 10-12 months' },
{ id: 'over12', name: 'Over 12 months' }
],
placeholder: 'Select timeframe'
}, '1fr');
});
page7.addRow(row => {
row.addCheckboxList('consent', {
options: [
{
id: 'report',
name: '🏃 Send me my marathon readiness report',
isRequired: true
},
{
id: 'plan',
name: '📋 Send me a sample training plan'
},
{
id: 'tips',
name: '💡 Send me weekly training tips'
}
],
defaultValue: ['report'],
orientation: 'vertical'
});
});
// ============ PDF REPORT ============
form.configurePdf('marathon-readiness-report', pdf => {
pdf.configure({
filename: 'marathon-readiness-report.pdf',
pageSize: 'A4',
allowUserDownload: true,
downloadButtonLabel: '🏃 Download Readiness Report',
header: {
title: 'Marathon Readiness Assessment',
subtitle: 'Your Personalized Training Recommendations'
},
footer: {
text: 'Generated by FormTs Marathon Readiness Quiz',
showPageNumbers: true
}
});
pdf.addSection('Executive Summary', section => {
section.addRow(row => {
row.addField('Readiness Level', getReadinessLabel());
row.addField('Score', `${getScorePercentage()}%`);
});
section.addRow(row => {
row.addField('Recommended Timeline', getRecommendedTimeline());
});
});
pdf.addSection('Score Breakdown', section => {
const s = scores();
section.addTable(
['Category', 'Score', 'Max', 'Status'],
[
['Running Experience', `${s['experience'] || 0}`, '25', (s['experience'] || 0) >= 18 ? '✅ Good' : '⚠️ Build More'],
['Weekly Mileage', `${s['mileage'] || 0}`, '25', (s['mileage'] || 0) >= 18 ? '✅ Good' : '⚠️ Increase'],
['Long Run Capability', `${s['longRun'] || 0}`, '20', (s['longRun'] || 0) >= 15 ? '✅ Good' : '⚠️ Build Up'],
['Injury History', `${s['injury'] || 0}`, '10', (s['injury'] || 0) >= 7 ? '✅ Good' : '⚠️ Address'],
['Strength Training', `${s['strength'] || 0}`, '10', (s['strength'] || 0) >= 7 ? '✅ Good' : '⚠️ Add'],
['Lifestyle Factors', `${(s['sleep'] || 0) + (s['time'] || 0) + (s['nutrition'] || 0)}`, '15', 'Review']
]
);
});
pdf.addPageBreak();
pdf.addSection('Priority Actions', section => {
const level = getReadinessLevel();
const s = scores();
if (level === 'notReady') {
section.addText('Your Focus Areas:');
section.addText('1. Build consistent running habit (4-5 days/week)');
section.addText('2. Gradually increase weekly mileage by 10%/week');
section.addText('3. Complete a 5K and 10K race before marathon training');
section.addText('4. Add 2 strength sessions per week');
} else if (level === 'needsWork') {
section.addText('Your Focus Areas:');
if ((s['mileage'] || 0) < 15) section.addText('1. Increase weekly mileage to 25-30 miles');
if ((s['longRun'] || 0) < 15) section.addText('2. Build long run to half-marathon distance');
if ((s['strength'] || 0) < 7) section.addText('3. Add regular strength training');
section.addText('4. Consider a half-marathon race as intermediate goal');
} else if (level === 'almostReady') {
section.addText('Fine-Tuning Recommendations:');
section.addText('1. Maintain current mileage for 2-3 weeks');
section.addText('2. Practice race nutrition on long runs');
section.addText('3. Add one tempo or speed session per week');
} else {
section.addText('Training Recommendations:');
section.addText('1. Select a 16-20 week marathon plan');
section.addText('2. Build in recovery weeks every 3-4 weeks');
section.addText('3. Practice race-day nutrition strategy');
section.addText('4. Plan a tune-up race (half marathon) mid-training');
}
});
pdf.addSection('Sample Week Structure', section => {
const level = getReadinessLevel();
if (level === 'ready' || level === 'almostReady') {
section.addTable(
['Day', 'Workout'],
[
['Monday', 'Rest or cross-training'],
['Tuesday', 'Speed/tempo work (6-8 miles)'],
['Wednesday', 'Easy run (5-6 miles)'],
['Thursday', 'Medium-long run (8-10 miles)'],
['Friday', 'Rest or easy 3-4 miles'],
['Saturday', 'Long run (12-20 miles)'],
['Sunday', 'Recovery run (4-6 miles) or rest']
]
);
} else {
section.addText('Build your base first with this structure:');
section.addTable(
['Day', 'Workout'],
[
['Monday', 'Rest'],
['Tuesday', 'Easy run (3-4 miles)'],
['Wednesday', 'Cross-training or strength'],
['Thursday', 'Easy run (3-4 miles)'],
['Friday', 'Rest'],
['Saturday', 'Long run (gradually increase)'],
['Sunday', 'Easy recovery run or rest']
]
);
}
});
});
// ============ SUBMIT BUTTON ============
form.configureSubmitButton({
label: () => `🏃 Get My Training Plan (${getReadinessLabel()})`
});
form.configureSubmitBehavior({
sendToServer: true
});
}