import React, { useMemo, useState } from "react"; import { motion } from "framer-motion"; import { AlertCircle, CheckCircle2, Dumbbell, FileDown, ShieldAlert, HeartPulse, } from "lucide-react"; type EquipmentKey = "gym" | "bands" | "freeWeights" | "bodyWeight"; type FmsKey = | "deepSquat" | "hurdleStep" | "inlineLunge" | "shoulderMobility" | "activeStraightLegRaise" | "trunkStabilityPushUp" | "rotaryStability"; type ContraKey = | "highBloodPressure" | "cardiacHistory" | "lowBackPain" | "shoulderPain" | "kneePain" | "anklePain" | "pregnant" | "postSurgery" | "otherMedical"; type JointKey = | "footAnkle" | "knee" | "hip" | "lumbar" | "thoracic" | "cervical" | "shoulder" | "elbowWrist"; type ResistanceGoal = "hypertrophy" | "strength" | "endurance" | "fat_loss" | "general_fitness"; type CvFitnessLevel = "low" | "below_average" | "average" | "good" | "excellent"; type CardioPreference = "walking" | "cycling" | "rowing" | "running" | "mixed"; type Intake = { clientName: string; trainerName: string; age: string; sex: string; weightKg: string; heightCm: string; bodyFat: string; restingHeartRate: string; systolicBP: string; diastolicBP: string; goal: string; resistanceGoal: ResistanceGoal; trainingAge: string; frequencyPerWeek: string; sessionDurationMin: string; programLengthWeeks: string; cvFitnessLevel: CvFitnessLevel; cardioPreference: CardioPreference; healthHistory: string; currentMedications: string; injuryNotes: string; equipment: Record; contraindications: Record; kineticChain: Record; fms: Record; clearingTests: { shoulderPainWithClearing: boolean; extensionPainWithClearing: boolean; flexionPainWithClearing: boolean; rotaryPainWithClearing: boolean; }; }; type SubmissionPayload = { intake: Intake; rules: { training_3_days_or_less_is_total_body: boolean; include_all_7_patterns: boolean; include_primary_and_assistance_exercises: boolean; include_resistance_training: boolean; include_cardiovascular_training: boolean; include_flexibility_training: boolean; pdf_only_output: boolean; output_packet_includes_trainer_reference_and_client_handout: boolean; trainer_reference_includes_progression_and_deload: boolean; client_handout_must_be_simple_and_trackable: boolean; resistance_training_must_include_sets_reps_intensity_sets_per_week_tempo_and_rest: boolean; conditioning_must_consider_cv_fitness_level: boolean; collect_rhr_and_blood_pressure: boolean; deload_every_6_to_8_weeks: boolean; progress_supported_to_integrated_then_more_closed_chain_when_tolerated: boolean; }; outputTemplate: { packetTitle: string; sections: string[]; trainerReferenceFields: string[]; clientHandoutFields: string[]; resistancePrescriptionColumns: string[]; cardioFields: string[]; flexibilityFields: string[]; }; }; const APP_CONFIG = { appName: "Program Designer", apiBaseUrl: "/api/generate-program-pdf", pdfFilenamePrefix: "training-program-packet", }; const equipmentLabels: Record = { gym: "Gym Machines", bands: "Resistance Bands", freeWeights: "Free Weights", bodyWeight: "Body Weight", }; const contraLabels: Record = { highBloodPressure: "High Blood Pressure", cardiacHistory: "Cardiac History", lowBackPain: "Low Back Pain", shoulderPain: "Shoulder Pain", kneePain: "Knee Pain", anklePain: "Ankle Pain", pregnant: "Pregnancy", postSurgery: "Post Surgery", otherMedical: "Other Medical Concern", }; const jointLabels: Record = { footAnkle: "Foot / Ankle", knee: "Knee", hip: "Hip", lumbar: "Lumbar Spine", thoracic: "Thoracic Spine", cervical: "Cervical Spine", shoulder: "Shoulder", elbowWrist: "Elbow / Wrist", }; const resistanceGoalLabels: Record = { hypertrophy: "Hypertrophy", strength: "Strength", endurance: "Muscular Endurance", fat_loss: "Fat Loss / General Conditioning", general_fitness: "General Fitness", }; const cvFitnessLabels: Record = { low: "Low", below_average: "Below Average", average: "Average", good: "Good", excellent: "Excellent", }; const cardioPreferenceLabels: Record = { walking: "Walking", cycling: "Cycling", rowing: "Rowing", running: "Running", mixed: "Mixed Modes", }; const fmsFields: { key: FmsKey; label: string }[] = [ { key: "deepSquat", label: "Deep Squat" }, { key: "hurdleStep", label: "Hurdle Step" }, { key: "inlineLunge", label: "Inline Lunge" }, { key: "shoulderMobility", label: "Shoulder Mobility" }, { key: "activeStraightLegRaise", label: "ASLR" }, { key: "trunkStabilityPushUp", label: "Trunk Stability Push-Up" }, { key: "rotaryStability", label: "Rotary Stability" }, ]; const defaultIntake: Intake = { clientName: "", trainerName: "", age: "", sex: "", weightKg: "", heightCm: "", bodyFat: "", restingHeartRate: "", systolicBP: "", diastolicBP: "", goal: "General fitness and movement quality", resistanceGoal: "hypertrophy", trainingAge: "beginner", frequencyPerWeek: "3", sessionDurationMin: "60", programLengthWeeks: "6", cvFitnessLevel: "average", cardioPreference: "walking", healthHistory: "", currentMedications: "", injuryNotes: "", equipment: { gym: true, bands: true, freeWeights: true, bodyWeight: true, }, contraindications: { highBloodPressure: false, cardiacHistory: false, lowBackPain: false, shoulderPain: false, kneePain: false, anklePain: false, pregnant: false, postSurgery: false, otherMedical: false, }, kineticChain: { footAnkle: "", knee: "", hip: "", lumbar: "", thoracic: "", cervical: "", shoulder: "", elbowWrist: "", }, fms: { deepSquat: 1, hurdleStep: 1, inlineLunge: 1, shoulderMobility: 1, activeStraightLegRaise: 1, trunkStabilityPushUp: 1, rotaryStability: 1, }, clearingTests: { shoulderPainWithClearing: false, extensionPainWithClearing: false, flexionPainWithClearing: false, rotaryPainWithClearing: false, }, }; const styles: Record = { page: { minHeight: "100vh", background: "#f8fafc", padding: "24px", fontFamily: "Arial, sans-serif", color: "#111827", }, container: { maxWidth: "1100px", margin: "0 auto", }, card: { background: "#ffffff", border: "1px solid #e5e7eb", borderRadius: "20px", boxShadow: "0 2px 8px rgba(0,0,0,0.04)", overflow: "hidden", }, header: { padding: "24px", borderBottom: "1px solid #e5e7eb", }, content: { padding: "24px", }, hero: { display: "flex", gap: "16px", alignItems: "flex-start", marginBottom: "20px", }, iconBox: { border: "1px solid #e5e7eb", borderRadius: "16px", padding: "12px", display: "inline-flex", background: "#fff", }, title: { margin: 0, fontSize: "28px", fontWeight: 700, }, subtitle: { margin: "6px 0 0 0", color: "#6b7280", fontSize: "14px", }, metricGrid: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: "12px", marginTop: "20px", }, metricCard: { border: "1px solid #e5e7eb", borderRadius: "16px", padding: "16px", background: "#fff", }, metricLabel: { fontSize: "11px", textTransform: "uppercase", letterSpacing: "0.05em", color: "#6b7280", }, metricValue: { marginTop: "6px", fontSize: "22px", fontWeight: 700, }, section: { marginBottom: "32px", }, sectionTitle: { margin: 0, fontSize: "20px", fontWeight: 700, }, sectionDescription: { margin: "6px 0 0 0", fontSize: "14px", color: "#6b7280", }, grid4: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: "16px", marginTop: "16px", }, grid3: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: "16px", marginTop: "16px", }, field: { display: "flex", flexDirection: "column", gap: "8px", }, label: { fontSize: "14px", fontWeight: 600, }, input: { width: "100%", boxSizing: "border-box", border: "1px solid #d1d5db", borderRadius: "12px", padding: "12px 14px", fontSize: "14px", background: "#fff", }, textarea: { width: "100%", boxSizing: "border-box", border: "1px solid #d1d5db", borderRadius: "12px", padding: "12px 14px", fontSize: "14px", minHeight: "120px", resize: "vertical", background: "#fff", }, checkboxGrid: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: "12px", marginTop: "16px", }, checkboxCard: { display: "flex", gap: "10px", alignItems: "flex-start", border: "1px solid #e5e7eb", borderRadius: "16px", padding: "14px", background: "#fff", }, divider: { border: 0, borderTop: "1px solid #e5e7eb", margin: "28px 0", }, statusBox: { borderRadius: "16px", padding: "14px 16px", marginTop: "18px", fontSize: "14px", }, actionBox: { border: "1px solid #e5e7eb", borderRadius: "16px", padding: "18px", background: "#fff", }, actionRow: { display: "flex", gap: "16px", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", }, button: { border: 0, borderRadius: "14px", padding: "12px 18px", background: "#111827", color: "#fff", fontSize: "14px", fontWeight: 700, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: "8px", }, buttonDisabled: { opacity: 0.6, cursor: "not-allowed", }, cautionBox: { border: "1px solid #fcd34d", background: "#fffbeb", color: "#92400e", borderRadius: "16px", padding: "16px", marginTop: "18px", }, infoBox: { border: "1px solid #dbeafe", background: "#eff6ff", color: "#1e3a8a", borderRadius: "16px", padding: "16px", marginTop: "18px", }, smallText: { fontSize: "14px", color: "#6b7280", margin: "6px 0 0 0", }, tablePreview: { width: "100%", borderCollapse: "collapse", marginTop: "12px", fontSize: "13px", }, th: { background: "#f59e0b", color: "#fff", textAlign: "left", padding: "10px", border: "1px solid #d1d5db", }, td: { padding: "10px", border: "1px solid #d1d5db", background: "#fafaf9", verticalAlign: "top", }, }; function num(value: string, fallback = 0) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : fallback; } function fmsTotal(intake: Intake) { return Object.values(intake.fms).reduce((sum, value) => sum + value, 0); } function bmi(intake: Intake) { const w = num(intake.weightKg); const h = num(intake.heightCm) / 100; if (!w || !h) return null; return +(w / (h * h)).toFixed(1); } function usesTotalBodyRule(intake: Intake) { return num(intake.frequencyPerWeek, 3) <= 3; } function resistanceSummary(goal: ResistanceGoal) { switch (goal) { case "strength": return "Backend should bias lower rep ranges, longer rest, and higher relative loading for primary lifts."; case "endurance": return "Backend should bias higher rep ranges, lower loads, shorter rest, and higher local muscular fatigue tolerance."; case "hypertrophy": return "Backend should bias moderate rep ranges, moderate rest, and sufficient weekly volume."; case "fat_loss": return "Backend should bias moderate density, total body patterns, and supporting conditioning work."; default: return "Backend should bias general fitness dosing with broad movement coverage and manageable fatigue."; } } function cardioSummary(level: CvFitnessLevel) { switch (level) { case "low": return "Conditioning should begin with lower-intensity intervals or steady work and conservative progression."; case "below_average": return "Conditioning should progress gradually with moderate introductory volume."; case "average": return "Conditioning can use standard moderate-intensity prescriptions and measured progression."; case "good": return "Conditioning may include longer intervals or greater weekly volume if tolerated."; case "excellent": return "Conditioning can tolerate higher workload or more advanced interval structure if goals require it."; } } function riskMessages(intake: Intake) { const flags: string[] = []; if (intake.clearingTests.shoulderPainWithClearing) flags.push("Shoulder clearing test pain recorded."); if (intake.clearingTests.extensionPainWithClearing) flags.push("Extension clearing test pain recorded."); if (intake.clearingTests.flexionPainWithClearing) flags.push("Flexion clearing test pain recorded."); if (intake.clearingTests.rotaryPainWithClearing) flags.push("Rotary clearing test pain recorded."); if (intake.contraindications.highBloodPressure) flags.push("High blood pressure flagged."); if (intake.contraindications.cardiacHistory) flags.push("Cardiac history flagged."); if (intake.contraindications.postSurgery) flags.push("Post-surgical history flagged."); return flags; } function buildPayload(intake: Intake): SubmissionPayload { return { intake, rules: { training_3_days_or_less_is_total_body: true, include_all_7_patterns: true, include_primary_and_assistance_exercises: true, include_resistance_training: true, include_cardiovascular_training: true, include_flexibility_training: true, pdf_only_output: true, output_packet_includes_trainer_reference_and_client_handout: true, trainer_reference_includes_progression_and_deload: true, client_handout_must_be_simple_and_trackable: true, resistance_training_must_include_sets_reps_intensity_sets_per_week_tempo_and_rest: true, conditioning_must_consider_cv_fitness_level: true, collect_rhr_and_blood_pressure: true, deload_every_6_to_8_weeks: true, progress_supported_to_integrated_then_more_closed_chain_when_tolerated: true, }, outputTemplate: { packetTitle: "Trainer Reference + Client Handout Packet", sections: [ "Program Summary", "Trainer Reference Sheet", "Client Handout", ], trainerReferenceFields: [ "program length", "training frequency", "split type", "resting heart rate", "blood pressure", "warm-up", "resistance training goal", "resistance training prescription", "cardiovascular prescription", "flexibility prescription", "intensity", "repetitions", "sets per week", "tempo", "rest intervals", "progression notes", "deload week", "substitutions", ], clientHandoutFields: [ "day label", "exercise", "intensity", "repetitions", "sets per week", "tempo", "rest interval", "load used", "cardio", "flexibility", "completion tracking boxes", "notes", ], resistancePrescriptionColumns: [ "Exercises", "Intensity", "Repetitions", "Sets/Week", "Tempo (C:I:E)", "Rest Interval Between Sets", ], cardioFields: [ "mode", "frequency", "duration", "intensity / zone / RPE", "progression", ], flexibilityFields: [ "exercise", "duration or reps", "frequency", ], }, }; } async function requestProgramPdf(payload: SubmissionPayload) { const response = await fetch(APP_CONFIG.apiBaseUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error("PDF generation failed"); } const blob = await response.blob(); const contentType = response.headers.get("content-type") || ""; if (!contentType.includes("pdf")) { throw new Error("Server did not return a PDF file"); } return blob; } function downloadBlob(blob: Blob, filename: string) { const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = filename; document.body.appendChild(anchor); anchor.click(); anchor.remove(); URL.revokeObjectURL(url); } function buildFilename(intake: Intake) { const safeName = (intake.clientName || "client") .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); return `${APP_CONFIG.pdfFilenamePrefix}-${safeName || "client"}.pdf`; } function FieldLabel({ children }: { children: React.ReactNode }) { return ; } function SectionHeader({ title, description }: { title: string; description: string }) { return (

{title}

{description}

); } export default function TrainingProgramDesignerEmbed() { const [intake, setIntake] = useState(defaultIntake); const [isSubmitting, setIsSubmitting] = useState(false); const [status, setStatus] = useState<"idle" | "success" | "error">("idle"); const [message, setMessage] = useState(""); const currentBmi = useMemo(() => bmi(intake), [intake]); const currentFmsTotal = useMemo(() => fmsTotal(intake), [intake]); const currentRiskMessages = useMemo(() => riskMessages(intake), [intake]); const totalBodyRuleActive = useMemo(() => usesTotalBodyRule(intake), [intake]); function setField(key: K, value: Intake[K]) { setIntake((prev) => ({ ...prev, [key]: value })); } function setEquipment(key: EquipmentKey, value: boolean) { setIntake((prev) => ({ ...prev, equipment: { ...prev.equipment, [key]: value } })); } function setContra(key: ContraKey, value: boolean) { setIntake((prev) => ({ ...prev, contraindications: { ...prev.contraindications, [key]: value } })); } function setFms(key: FmsKey, value: number) { setIntake((prev) => ({ ...prev, fms: { ...prev.fms, [key]: value } })); } function setJoint(key: JointKey, value: string) { setIntake((prev) => ({ ...prev, kineticChain: { ...prev.kineticChain, [key]: value } })); } function setClearing(key: K, value: boolean) { setIntake((prev) => ({ ...prev, clearingTests: { ...prev.clearingTests, [key]: value } })); } async function handleGenerate() { setIsSubmitting(true); setStatus("idle"); setMessage(""); try { const payload = buildPayload(intake); const pdfBlob = await requestProgramPdf(payload); downloadBlob(pdfBlob, buildFilename(intake)); setStatus("success"); setMessage("Your trainer reference and client handout PDF packet has been generated."); } catch (error) { setStatus("error"); setMessage(error instanceof Error ? error.message : "Unable to generate the PDF packet."); } finally { setIsSubmitting(false); } } return (

{APP_CONFIG.appName}

Generate a printable packet that includes a trainer reference sheet and a client handout with resistance, conditioning, and flexibility programming.

BMI
{currentBmi ?? "—"}
FMS Total
{currentFmsTotal}
Resting HR
{intake.restingHeartRate || "—"}
Blood Pressure
{intake.systolicBP && intake.diastolicBP ? `${intake.systolicBP}/${intake.diastolicBP}` : "—"}
Resistance Goal
{resistanceGoalLabels[intake.resistanceGoal]}
CV Fitness Level
{cvFitnessLabels[intake.cvFitnessLevel]}
Client Name setField("clientName", e.target.value)} />
Trainer Name setField("trainerName", e.target.value)} />
Age setField("age", e.target.value)} />
Sex setField("sex", e.target.value)} />
Weight (kg) setField("weightKg", e.target.value)} />
Height (cm) setField("heightCm", e.target.value)} />
Body Fat % setField("bodyFat", e.target.value)} />
Training Age
Resting Heart Rate setField("restingHeartRate", e.target.value)} placeholder="bpm" />
Systolic BP setField("systolicBP", e.target.value)} placeholder="mmHg" />
Diastolic BP setField("diastolicBP", e.target.value)} placeholder="mmHg" />
Program Length (weeks) setField("programLengthWeeks", e.target.value)} />
Training Days / Week setField("frequencyPerWeek", e.target.value)} />
Session Duration (min) setField("sessionDurationMin", e.target.value)} />
Resistance Training Goal
CV Fitness Level
Preferred Conditioning Mode
Primary Goal setField("goal", e.target.value)} />

Health History