import React, { useMemo, useState } from "react"; // Clean, uniform standalone preview with consistent font sizing and image export support. const DAYS = [ { key: "mon", label: "MON", focus: "PUSH" }, { key: "tue", label: "TUE", focus: "PULL" }, { key: "wed", label: "WED", focus: "COND" }, { key: "thu", label: "THU", focus: "PUSH" }, { key: "fri", label: "FRI", focus: "PULL" }, { key: "sat", label: "SAT", focus: "STREET" }, ] as const; type DayKey = typeof DAYS[number]["key"]; type BMode = "BASE App Skill" | "Strength Program"; const BASE_SKILLS = [ "Back Lever", "Elbow Lever", "Front Lever", "Handstand", "Muscle Up", "Planche", "Ring Muscle Up", "Shoulder Stand", "Handstand Push Up", ] as const; type DayConfig = { priority: number; compC: string[]; compA_choice: "Upper" | "Lower"; bMode: BMode; bSkill?: typeof BASE_SKILLS[number]; order: string[]; }; type WeekPlan = Record; const COMP_C_DAY_LIFTS: Record = { mon: ["Dips", "Squats"], tue: ["Pull Ups", "Deadlifts"], wed: [], thu: ["Bench", "Hip Thrust"], fri: ["Ring Pull + MU", "Squats"], sat: ["Dips", "Squats", "Pull Ups"], }; const COMP_A_DAY_CHOICES: Record = { mon: ["Chest + Triceps", "Glutes + Abductors"], tue: ["Lats + Upper Back + Biceps", "Quads + Hamstrings + Adductors"], wed: ["Conditioning", "Conditioning"], thu: ["Shoulders + Triceps", "Hamstrings + Quads"], fri: ["Upper Back + Lats + Biceps", "Glutes + Hamstrings + Abductors"], sat: ["Upper focus", "Lower focus"], }; const defaultDay = (k: DayKey): DayConfig => ({ priority: 3, compC: k === "sat" ? [] : (COMP_C_DAY_LIFTS[k].length ? [COMP_C_DAY_LIFTS[k][0]] : []), compA_choice: "Upper", bMode: "Strength Program", // order excludes fixed cards (WARM UP at top, CORE FINISHER at bottom) order: k === "wed" ? ["Conditioning"] : k === "sat" ? ["STREET LIFT", "STREET WORK", "Conditioning Circuit"] : ["Comp C", "Comp A", "Comp B"], }); const defaultWeek = (): WeekPlan => ({ mon: defaultDay("mon"), tue: defaultDay("tue"), wed: defaultDay("wed"), thu: defaultDay("thu"), fri: defaultDay("fri"), sat: defaultDay("sat"), }); const heat = (p: number) => `hsla(${220 - (p - 1) * 55}, 80%, 50%, 0.06)`; function RowHeader({ title, onUp, onDown }: { title: string; onUp: () => void; onDown: () => void }) { return (
{title}
); } export default function TrainingSplitPlannerStandalone() { const [member, setMember] = useState("Member 001"); const [week, setWeek] = useState(defaultWeek); const maxPriority = useMemo(() => Math.max(...Object.values(week).map(d => d.priority)), [week]); const setDay = (k: DayKey, patch: Partial) => setWeek(prev => ({ ...prev, [k]: { ...prev[k], ...patch } })); const move = (k: DayKey, id: string, dir: -1 | 1) => setWeek(prev => { const d = prev[k]; const i = d.order.indexOf(id); if (i < 0) return prev; const j = Math.max(0, Math.min(d.order.length - 1, i + dir)); if (i === j) return prev; const order = [...d.order]; const [it] = order.splice(i, 1); order.splice(j, 0, it); return { ...prev, [k]: { ...d, order } }; }); const exportImage = async () => { const node = document.querySelector(".export-root") as HTMLElement | null; if (!node) return; const { width, height } = node.getBoundingClientRect(); const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttribute("width", String(Math.ceil(width))); svg.setAttribute("height", String(Math.ceil(height))); const fo = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); fo.setAttribute("x", "0"); fo.setAttribute("y", "0"); fo.setAttribute("width", "100%"); fo.setAttribute("height", "100%"); const clone = node.cloneNode(true) as HTMLElement; // Ensure fonts and background render clone.style.background = getComputedStyle(document.body).background || "#f2f2f2"; fo.appendChild(clone); svg.appendChild(fo); const svgData = new XMLSerializer().serializeToString(svg); const img = new Image(); const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" }); const url = URL.createObjectURL(blob); img.onload = () => { const canvas = document.createElement("canvas"); canvas.width = Math.ceil(width); canvas.height = Math.ceil(height); const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.fillStyle = "#f2f2f2"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0); URL.revokeObjectURL(url); const a = document.createElement("a"); a.download = `${member.replace(/\s+/g, "_")}_training_split.png`; a.href = canvas.toDataURL("image/png"); a.click(); }; img.src = url; }; return (
Training Split Planner
Uniform text. Square cards. Export as image.
Member setMember(e.target.value)} />
{DAYS.map(({key,label,focus})=>{ const d = week[key]; const cOptions = COMP_C_DAY_LIFTS[key] || []; const [upLbl, loLbl] = COMP_A_DAY_CHOICES[key]; return (
{label}
{focus}
Importance setDay(key,{priority: Number(e.target.value)})} />
{/* Fixed top: WARM UP */}
WARM UP
Activation and Mobility
{(d.order || []).map((cellId, idx)=> (
{/* movable card header */}
{cellId}
{key==='wed' && cellId==='Conditioning' && ( ["Level 3","Level 2","Level 1"].map(opt=> ( )) )} {key==='sat' && cellId==='STREET LIFT' && ( <>
Choose up to 2 lifts
{(COMP_C_DAY_LIFTS[key] || []).map(opt=>{ const selected = d.compC.includes(opt); const cap = !selected && d.compC.length>=2; return ( ); })} )} {key==='sat' && cellId==='STREET WORK' &&
Street-specific drills or flow. Configure later.
} {key==='sat' && cellId==='Conditioning Circuit' &&
Add your circuit template here.
} {key!=='wed' && key!=='sat' && cellId==='Comp C' && ( (COMP_C_DAY_LIFTS[key] || []).map(opt=> (
); }