@burdenoff/microfe-mybodyshield 2026.705.1 → 2026.705.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mybodyshield/components/DataTable.js +8 -4
- package/dist/mybodyshield/components/DataTable.js.map +1 -1
- package/dist/mybodyshield/components/EvidenceUpload.js +92 -4
- package/dist/mybodyshield/components/EvidenceUpload.js.map +1 -0
- package/dist/mybodyshield/components/FileLink.js +35 -2
- package/dist/mybodyshield/components/FileLink.js.map +1 -0
- package/dist/mybodyshield/components/MybDiscussion.js +107 -6
- package/dist/mybodyshield/components/MybDiscussion.js.map +1 -0
- package/dist/mybodyshield/components/MybTags.js +148 -4
- package/dist/mybodyshield/components/MybTags.js.map +1 -0
- package/dist/mybodyshield/components/Pagination.js.map +1 -1
- package/dist/mybodyshield/components/UserSearchSelect.js +148 -0
- package/dist/mybodyshield/components/UserSearchSelect.js.map +1 -0
- package/dist/mybodyshield/components/index.js +1 -0
- package/dist/mybodyshield/hooks/index.js +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js +47 -27
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js.map +1 -1
- package/dist/mybodyshield/hooks/useMybComments.js +44 -2
- package/dist/mybodyshield/hooks/useMybComments.js.map +1 -0
- package/dist/mybodyshield/hooks/useMybFiles.js +80 -3
- package/dist/mybodyshield/hooks/useMybFiles.js.map +1 -0
- package/dist/mybodyshield/hooks/useMybTags.js +88 -2
- package/dist/mybodyshield/hooks/useMybTags.js.map +1 -0
- package/dist/mybodyshield/hooks/useMybUsers.js +36 -0
- package/dist/mybodyshield/hooks/useMybUsers.js.map +1 -0
- package/dist/mybodyshield/pages/DashboardPage.js +41 -36
- package/dist/mybodyshield/pages/DashboardPage.js.map +1 -1
- package/dist/mybodyshield/pages/_shared/visuals.js +15 -9
- package/dist/mybodyshield/pages/_shared/visuals.js.map +1 -1
- package/dist/mybodyshield/pages/coaching/CoachingPage.js +171 -147
- package/dist/mybodyshield/pages/coaching/CoachingPage.js.map +1 -1
- package/dist/mybodyshield/pages/goals/GoalsPage.js +0 -8
- package/dist/mybodyshield/pages/goals/GoalsPage.js.map +1 -1
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js +352 -278
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +600 -296
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/progress/ProgressPage.js +88 -51
- package/dist/mybodyshield/pages/progress/ProgressPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.js +146 -126
- package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.js +1 -4
- package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProgramDetailPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/programs/ProgramDetailPage.tsx"],"sourcesContent":["/**\n * Program detail — a single training block with its workouts & exercises.\n * @module mybodyshield/pages/programs\n *\n * Renders one `mybProgram` (with its nested `workouts[].exercises[]`), lifecycle\n * transition controls (activate / complete / archive) and dialogs to add\n * workouts and exercises. All data & mutations hit the real `myb*` contract.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Plus, CheckCircle2, Archive, Play, Dumbbell } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n} from '../../components';\nimport { IconChip, WORKOUT_TYPE_META, MUSCLE_META } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybProgram,\n useMybAddWorkout,\n useMybAddExercise,\n useMybTransitionProgram,\n useMybArchiveProgram,\n} from '../../hooks';\nimport {\n formatEnumLabel,\n formatDate,\n formatNumber,\n formatWeight,\n formatDuration,\n formatMinutes,\n} from '../../utils/formatters';\nimport type {\n MybWorkout,\n MybExercise,\n MybWorkoutType,\n MybMuscleGroup,\n MybProgramStatus,\n MybCreateWorkoutInput,\n MybCreateExerciseInput,\n} from '../../types';\n\nconst WORKOUT_TYPES: readonly MybWorkoutType[] = [\n 'STRENGTH',\n 'CARDIO',\n 'HIIT',\n 'MOBILITY',\n 'YOGA',\n 'RECOVERY',\n];\nconst MUSCLE_GROUPS: readonly MybMuscleGroup[] = [\n 'CHEST',\n 'BACK',\n 'LEGS',\n 'SHOULDERS',\n 'ARMS',\n 'CORE',\n 'FULL_BODY',\n];\n\n/** Parse a numeric text field → number | null (null when blank / invalid). */\nconst toNum = (raw: string): number | null => {\n const t = raw.trim();\n if (t === '') return null;\n const n = Number(t);\n return Number.isFinite(n) ? n : null;\n};\n\nconst Chip: FC<{ children: ReactNode }> = ({ children }) => (\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {children}\n </span>\n);\n\nconst MuscleChip: FC<{ group: MybMuscleGroup }> = ({ group }) => {\n const tint = MUSCLE_META[group].tint;\n return (\n <span\n className=\"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium\"\n style={{ backgroundColor: `color-mix(in srgb, ${tint} 16%, transparent)`, color: tint }}\n >\n {formatEnumLabel(group)}\n </span>\n );\n};\n\n/** Token-safe generic enum <Select>. */\nfunction EnumSelect<T extends string>({\n id,\n value,\n onChange,\n options,\n}: {\n id?: string;\n value: T;\n onChange: (value: T) => void;\n options: readonly T[];\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as T)}>\n <SelectTrigger id={id}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {formatEnumLabel(opt)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\ninterface WorkoutForm {\n name: string;\n type: MybWorkoutType;\n weekNumber: string;\n dayOfWeek: string;\n durationMin: string;\n}\n\nconst EMPTY_WORKOUT: WorkoutForm = {\n name: '',\n type: 'STRENGTH',\n weekNumber: '1',\n dayOfWeek: '1',\n durationMin: '',\n};\n\ninterface ExerciseForm {\n name: string;\n muscleGroup: MybMuscleGroup;\n sets: string;\n reps: string;\n targetWeight: string;\n restSeconds: string;\n instructions: string;\n}\n\nconst EMPTY_EXERCISE: ExerciseForm = {\n name: '',\n muscleGroup: 'FULL_BODY',\n sets: '',\n reps: '',\n targetWeight: '',\n restSeconds: '',\n instructions: '',\n};\n\nfunction ExerciseRow({ exercise }: { exercise: MybExercise }) {\n return (\n <div className=\"flex flex-col gap-2 py-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-medium text-text-primary\">{exercise.name}</p>\n <MuscleChip group={exercise.muscleGroup} />\n </div>\n {exercise.instructions && (\n <p className=\"mt-1 text-xs text-text-tertiary\">{exercise.instructions}</p>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-4 text-sm tabular-nums text-text-secondary\">\n <span>\n {formatNumber(exercise.sets)}×{formatNumber(exercise.reps)} @{' '}\n {formatWeight(exercise.targetWeight)}\n </span>\n <span className=\"text-text-tertiary\">Rest {formatDuration(exercise.restSeconds)}</span>\n </div>\n </div>\n );\n}\n\nfunction WorkoutCard({\n workout,\n onAddExercise,\n}: {\n workout: MybWorkout;\n onAddExercise: (workoutId: string) => void;\n}) {\n const meta = WORKOUT_TYPE_META[workout.type];\n const TypeIcon = meta.icon;\n const exercises = useMemo(\n () => [...(workout.exercises ?? [])].sort((a, b) => a.orderIndex - b.orderIndex),\n [workout.exercises]\n );\n\n return (\n <GlassCard treatment=\"glass\" className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<TypeIcon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{workout.name}</h3>\n <StatusBadge status={workout.type} />\n </div>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n Week {formatNumber(workout.weekNumber)} · Day {formatNumber(workout.dayOfWeek)} ·{' '}\n {formatMinutes(workout.durationMin)}\n </p>\n </div>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onAddExercise(workout.id)}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add exercise\n </Button>\n </div>\n\n {exercises.length === 0 ? (\n <p className=\"mt-4 rounded-lg border border-dashed border-border-seam px-3 py-4 text-center text-sm text-text-tertiary\">\n No exercises yet — add the first movement to this workout.\n </p>\n ) : (\n <div className=\"mt-4 divide-y divide-border-seam/60\">\n {exercises.map((exercise) => (\n <ExerciseRow key={exercise.id} exercise={exercise} />\n ))}\n </div>\n )}\n </GlassCard>\n );\n}\n\nexport function ProgramDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useMyBodyShield();\n\n const { data: program, isLoading, isError, refetch } = useMybProgram(id);\n\n const transition = useMybTransitionProgram();\n const archiveM = useMybArchiveProgram();\n const addWorkout = useMybAddWorkout();\n const addExercise = useMybAddExercise();\n\n const [actionErr, setActionErr] = useState<string | null>(null);\n\n // Add-workout dialog\n const [workoutOpen, setWorkoutOpen] = useState(false);\n const [workoutForm, setWorkoutForm] = useState<WorkoutForm>(EMPTY_WORKOUT);\n const [workoutErr, setWorkoutErr] = useState<string | null>(null);\n\n // Add-exercise dialog (targets a specific workout)\n const [exerciseTarget, setExerciseTarget] = useState<string | null>(null);\n const [exerciseForm, setExerciseForm] = useState<ExerciseForm>(EMPTY_EXERCISE);\n const [exerciseErr, setExerciseErr] = useState<string | null>(null);\n\n const workouts = useMemo(\n () =>\n [...(program?.workouts ?? [])].sort(\n (a, b) =>\n a.weekNumber - b.weekNumber || a.dayOfWeek - b.dayOfWeek || a.orderIndex - b.orderIndex\n ),\n [program?.workouts]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Program\">\n <LoadingView label=\"Loading program…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Program\">\n <ErrorView message=\"We couldn't load this program.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!program) {\n return (\n <PageLayout title=\"Program\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"Program not found\"\n description=\"This program may have been deleted or you don't have access to it.\"\n action={\n <Button variant=\"outline\" onClick={() => navigateTo('programs')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" /> Back to programs\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const programId = program.id;\n\n const doTransition = async (status: MybProgramStatus) => {\n setActionErr(null);\n try {\n await transition.mutateAsync({ id: programId, status });\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to update program status.');\n }\n };\n\n const doArchive = async () => {\n if (!window.confirm('Archive this program? It will be moved out of your active library.')) {\n return;\n }\n setActionErr(null);\n try {\n await archiveM.mutateAsync(programId);\n await refetch();\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to archive program.');\n }\n };\n\n const openAddWorkout = () => {\n setWorkoutForm(EMPTY_WORKOUT);\n setWorkoutErr(null);\n setWorkoutOpen(true);\n };\n\n const submitWorkout = async () => {\n if (!workoutForm.name.trim()) {\n setWorkoutErr('Workout name is required.');\n return;\n }\n const input: MybCreateWorkoutInput = {\n programId,\n name: workoutForm.name.trim(),\n type: workoutForm.type,\n weekNumber: toNum(workoutForm.weekNumber),\n dayOfWeek: toNum(workoutForm.dayOfWeek),\n durationMin: toNum(workoutForm.durationMin),\n };\n try {\n await addWorkout.mutateAsync(input);\n setWorkoutOpen(false);\n } catch (e) {\n setWorkoutErr(e instanceof Error ? e.message : 'Failed to add workout.');\n }\n };\n\n const openAddExercise = (workoutId: string) => {\n setExerciseForm(EMPTY_EXERCISE);\n setExerciseErr(null);\n setExerciseTarget(workoutId);\n };\n\n const submitExercise = async () => {\n if (!exerciseTarget) return;\n if (!exerciseForm.name.trim()) {\n setExerciseErr('Exercise name is required.');\n return;\n }\n const input: MybCreateExerciseInput = {\n workoutId: exerciseTarget,\n name: exerciseForm.name.trim(),\n muscleGroup: exerciseForm.muscleGroup,\n sets: toNum(exerciseForm.sets),\n reps: toNum(exerciseForm.reps),\n targetWeight: toNum(exerciseForm.targetWeight),\n restSeconds: toNum(exerciseForm.restSeconds),\n instructions: exerciseForm.instructions.trim() || null,\n };\n try {\n await addExercise.mutateAsync(input);\n setExerciseTarget(null);\n } catch (e) {\n setExerciseErr(e instanceof Error ? e.message : 'Failed to add exercise.');\n }\n };\n\n return (\n <PageLayout\n title={program.name}\n actions={\n <>\n {program.status === 'DRAFT' && (\n <Button onClick={() => void doTransition('ACTIVE')} disabled={transition.isPending}>\n <Play className=\"mr-1 h-4 w-4\" /> Activate\n </Button>\n )}\n {program.status === 'ACTIVE' && (\n <Button onClick={() => void doTransition('COMPLETED')} disabled={transition.isPending}>\n <CheckCircle2 className=\"mr-1 h-4 w-4\" /> Complete\n </Button>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button\n variant=\"outline\"\n onClick={() => void doArchive()}\n disabled={archiveM.isPending}\n >\n <Archive className=\"mr-1 h-4 w-4\" /> Archive\n </Button>\n )}\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n </>\n }\n >\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"mb-4 inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> All programs\n </button>\n\n {/* Header card */}\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{program.name}</h2>\n <StatusBadge status={program.status} />\n </div>\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n <Chip>{formatEnumLabel(program.phase)}</Chip>\n <Chip>{formatEnumLabel(program.level)}</Chip>\n <Chip>{formatEnumLabel(program.goalType)}</Chip>\n <Chip>\n {formatNumber(program.weeks)} wk × {formatNumber(program.daysPerWeek)} days\n </Chip>\n </div>\n <p className=\"mt-3 text-sm text-text-secondary\">\n {formatDate(program.startDate)} – {formatDate(program.endDate)}\n </p>\n {program.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {program.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n {program.description && (\n <p className=\"mt-3 max-w-2xl text-sm text-text-secondary\">{program.description}</p>\n )}\n </div>\n </div>\n\n {actionErr && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionErr}\n </p>\n )}\n </GlassCard>\n\n {/* Workouts */}\n <Section\n title=\"Workouts\"\n description=\"Sessions in this block, ordered by week, day & sequence.\"\n >\n {workouts.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"No workouts yet\"\n description=\"Add workouts to lay out the training week, then attach exercises to each one.\"\n action={\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {workouts.map((workout) => (\n <WorkoutCard key={workout.id} workout={workout} onAddExercise={openAddExercise} />\n ))}\n </div>\n )}\n </Section>\n\n {/* Add-workout dialog */}\n <ResponsiveDialog open={workoutOpen} onOpenChange={setWorkoutOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add workout</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a training session to this program. Attach exercises once it's created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {workoutErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {workoutErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-name\">Name</Label>\n <Input\n id=\"myb-wk-name\"\n value={workoutForm.name}\n onChange={(e) => setWorkoutForm({ ...workoutForm, name: e.target.value })}\n placeholder=\"e.g. Upper Body — Push\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-type\">Type</Label>\n <EnumSelect\n id=\"myb-wk-type\"\n value={workoutForm.type}\n onChange={(v) => setWorkoutForm({ ...workoutForm, type: v })}\n options={WORKOUT_TYPES}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-week\">Week</Label>\n <Input\n id=\"myb-wk-week\"\n type=\"number\"\n min={1}\n value={workoutForm.weekNumber}\n onChange={(e) => setWorkoutForm({ ...workoutForm, weekNumber: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-day\">Day</Label>\n <Input\n id=\"myb-wk-day\"\n type=\"number\"\n min={1}\n max={7}\n value={workoutForm.dayOfWeek}\n onChange={(e) => setWorkoutForm({ ...workoutForm, dayOfWeek: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-dur\">Duration (min)</Label>\n <Input\n id=\"myb-wk-dur\"\n type=\"number\"\n min={0}\n value={workoutForm.durationMin}\n onChange={(e) => setWorkoutForm({ ...workoutForm, durationMin: e.target.value })}\n placeholder=\"60\"\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setWorkoutOpen(false)}\n disabled={addWorkout.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitWorkout()} disabled={addWorkout.isPending}>\n {addWorkout.isPending ? 'Adding…' : 'Add workout'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add-exercise dialog */}\n <ResponsiveDialog\n open={exerciseTarget !== null}\n onOpenChange={(open) => {\n if (!open) setExerciseTarget(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add exercise</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a movement with its target sets, reps, load & rest.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {exerciseErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {exerciseErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-name\">Name</Label>\n <Input\n id=\"myb-ex-name\"\n value={exerciseForm.name}\n onChange={(e) => setExerciseForm({ ...exerciseForm, name: e.target.value })}\n placeholder=\"e.g. Barbell Bench Press\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-muscle\">Muscle group</Label>\n <EnumSelect\n id=\"myb-ex-muscle\"\n value={exerciseForm.muscleGroup}\n onChange={(v) => setExerciseForm({ ...exerciseForm, muscleGroup: v })}\n options={MUSCLE_GROUPS}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-sets\">Sets</Label>\n <Input\n id=\"myb-ex-sets\"\n type=\"number\"\n min={0}\n value={exerciseForm.sets}\n onChange={(e) => setExerciseForm({ ...exerciseForm, sets: e.target.value })}\n placeholder=\"3\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-reps\">Reps</Label>\n <Input\n id=\"myb-ex-reps\"\n type=\"number\"\n min={0}\n value={exerciseForm.reps}\n onChange={(e) => setExerciseForm({ ...exerciseForm, reps: e.target.value })}\n placeholder=\"10\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-weight\">Weight (kg)</Label>\n <Input\n id=\"myb-ex-weight\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={exerciseForm.targetWeight}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, targetWeight: e.target.value })\n }\n placeholder=\"60\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-rest\">Rest (sec)</Label>\n <Input\n id=\"myb-ex-rest\"\n type=\"number\"\n min={0}\n value={exerciseForm.restSeconds}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, restSeconds: e.target.value })\n }\n placeholder=\"90\"\n />\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-instructions\">Instructions</Label>\n <Textarea\n id=\"myb-ex-instructions\"\n rows={3}\n value={exerciseForm.instructions}\n onChange={(e) => setExerciseForm({ ...exerciseForm, instructions: e.target.value })}\n placeholder=\"Cues, tempo, form notes…\"\n />\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setExerciseTarget(null)}\n disabled={addExercise.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitExercise()} disabled={addExercise.isPending}>\n {addExercise.isPending ? 'Adding…' : 'Add exercise'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiEA,IAAM,KAA2C;CAC/C;CACA;CACA;CACA;CACA;CACA;CACD,EACK,KAA2C;CAC/C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAGK,KAAS,MAA+B;CAC5C,IAAM,IAAI,EAAI,MAAM;AACpB,KAAI,MAAM,GAAI,QAAO;CACrB,IAAM,IAAI,OAAO,EAAE;AACnB,QAAO,OAAO,SAAS,EAAE,GAAG,IAAI;GAG5B,KAAqC,EAAE,kBAC3C,kBAAC,QAAD;CAAM,WAAU;CACb;CACI,CAAA,EAGH,KAA6C,EAAE,eAAY;CAC/D,IAAM,IAAO,EAAY,GAAO;AAChC,QACE,kBAAC,QAAD;EACE,WAAU;EACV,OAAO;GAAE,iBAAiB,sBAAsB,EAAK;GAAqB,OAAO;GAAM;YAEtF,EAAgB,EAAM;EAClB,CAAA;;AAKX,SAAS,EAA6B,EACpC,OACA,UACA,aACA,cAMC;AACD,QACE,kBAAC,GAAD;EAAe;EAAO,gBAAgB,MAAM,EAAS,EAAO;YAA5D,CACE,kBAAC,GAAD;GAAmB;aACjB,kBAAC,GAAD,EAAe,CAAA;GACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAQ,KAAK,MACZ,kBAAC,GAAD;GAAsB,OAAO;aAC1B,EAAgB,EAAI;GACV,EAFI,EAEJ,CACb,EACY,CAAA,CACT;;;AAYb,IAAM,IAA6B;CACjC,MAAM;CACN,MAAM;CACN,YAAY;CACZ,WAAW;CACX,aAAa;CACd,EAYK,IAA+B;CACnC,MAAM;CACN,aAAa;CACb,MAAM;CACN,MAAM;CACN,cAAc;CACd,aAAa;CACb,cAAc;CACf;AAED,SAAS,EAAY,EAAE,eAAuC;AAC5D,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAiC,EAAS;KAAS,CAAA,EAChE,kBAAC,GAAD,EAAY,OAAO,EAAS,aAAe,CAAA,CACvC;OACL,EAAS,gBACR,kBAAC,KAAD;IAAG,WAAU;cAAmC,EAAS;IAAiB,CAAA,CAExE;MACN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD,EAAA,UAAA;IACG,EAAa,EAAS,KAAK;IAAC;IAAE,EAAa,EAAS,KAAK;IAAC;IAAG;IAC7D,EAAa,EAAS,aAAa;IAC/B,EAAA,CAAA,EACP,kBAAC,QAAD;IAAM,WAAU;cAAhB,CAAqC,SAAM,EAAe,EAAS,YAAY,CAAQ;MACnF;KACF;;;AAIV,SAAS,GAAY,EACnB,YACA,oBAIC;CACD,IAAM,IAAO,EAAkB,EAAQ,OACjC,IAAW,EAAK,MAChB,IAAY,QACV,CAAC,GAAI,EAAQ,aAAa,EAAE,CAAE,CAAC,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,WAAW,EAChF,CAAC,EAAQ,UAAU,CACpB;AAED,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAU,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;KAAE,MAAM,EAAK;KAAQ,CAAA,EACrE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAmC,EAAQ;OAAU,CAAA,EACnE,kBAAC,GAAD,EAAa,QAAQ,EAAQ,MAAQ,CAAA,CACjC;SACN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAAiD;OACzC,EAAa,EAAQ,WAAW;OAAC;OAAQ,EAAa,EAAQ,UAAU;OAAC;OAAG;OACjF,EAAc,EAAQ,YAAY;OACjC;QACA;OACF;OACN,kBAAC,GAAD;IAAQ,SAAQ;IAAU,MAAK;IAAK,eAAe,EAAc,EAAQ,GAAG;cAA5E,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,gBAC1B;MACL;MAEL,EAAU,WAAW,IACpB,kBAAC,KAAD;GAAG,WAAU;aAA2G;GAEpH,CAAA,GAEJ,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,KAAK,MACd,kBAAC,GAAD,EAAyC,aAAY,EAAnC,EAAS,GAA0B,CACrD;GACE,CAAA,CAEE;;;AAIhB,SAAgB,IAAoB;CAClC,IAAM,EAAE,UAAO,IAA2B,EACpC,EAAE,kBAAe,GAAiB,EAElC,EAAE,MAAM,GAAS,cAAW,YAAS,eAAY,GAAc,EAAG,EAElE,IAAa,IAAyB,EACtC,IAAW,IAAsB,EACjC,IAAa,IAAkB,EAC/B,IAAc,IAAmB,EAEjC,CAAC,GAAW,KAAgB,EAAwB,KAAK,EAGzD,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAa,KAAkB,EAAsB,EAAc,EACpE,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAG3D,CAAC,GAAgB,KAAqB,EAAwB,KAAK,EACnE,CAAC,GAAc,KAAmB,EAAuB,EAAe,EACxE,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAE7D,KAAW,QAEb,CAAC,GAAI,GAAS,YAAY,EAAE,CAAE,CAAC,MAC5B,GAAG,MACF,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAChF,EACH,CAAC,GAAS,SAAS,CACpB;AAED,KAAI,EACF,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,IAAD,EAAa,OAAM,oBAAqB,CAAA;EAC7B,CAAA;AAIjB,KAAI,EACF,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,IAAD;GAAW,SAAQ;GAAiC,eAAe,KAAK,GAAS;GAAI,CAAA;EAC1E,CAAA;AAIjB,KAAI,CAAC,EACH,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GACE,cAAa;GACb,MAAM,kBAAC,IAAD,EAAU,WAAU,aAAc,CAAA;GACxC,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,GAAD;IAAQ,SAAQ;IAAU,eAAe,EAAW,WAAW;cAA/D,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EAAA,oBAC/B;;GAEX,CAAA;EACS,CAAA;CAIjB,IAAM,IAAY,EAAQ,IAEpB,KAAe,OAAO,MAA6B;AACvD,IAAa,KAAK;AAClB,MAAI;AACF,SAAM,EAAW,YAAY;IAAE,IAAI;IAAW;IAAQ,CAAC;WAChD,GAAG;AACV,KAAa,aAAa,QAAQ,EAAE,UAAU,mCAAmC;;IAI/E,KAAY,YAAY;AACvB,aAAO,QAAQ,qEAAqE,EAGzF;KAAa,KAAK;AAClB,OAAI;AAEF,IADA,MAAM,EAAS,YAAY,EAAU,EACrC,MAAM,GAAS;YACR,GAAG;AACV,MAAa,aAAa,QAAQ,EAAE,UAAU,6BAA6B;;;IAIzE,UAAuB;AAG3B,EAFA,EAAe,EAAc,EAC7B,EAAc,KAAK,EACnB,EAAe,GAAK;IAGhB,KAAgB,YAAY;AAChC,MAAI,CAAC,EAAY,KAAK,MAAM,EAAE;AAC5B,KAAc,4BAA4B;AAC1C;;EAEF,IAAM,IAA+B;GACnC;GACA,MAAM,EAAY,KAAK,MAAM;GAC7B,MAAM,EAAY;GAClB,YAAY,EAAM,EAAY,WAAW;GACzC,WAAW,EAAM,EAAY,UAAU;GACvC,aAAa,EAAM,EAAY,YAAY;GAC5C;AACD,MAAI;AAEF,GADA,MAAM,EAAW,YAAY,EAAM,EACnC,EAAe,GAAM;WACd,GAAG;AACV,KAAc,aAAa,QAAQ,EAAE,UAAU,yBAAyB;;IAItE,MAAmB,MAAsB;AAG7C,EAFA,EAAgB,EAAe,EAC/B,EAAe,KAAK,EACpB,EAAkB,EAAU;IAGxB,KAAiB,YAAY;AACjC,MAAI,CAAC,EAAgB;AACrB,MAAI,CAAC,EAAa,KAAK,MAAM,EAAE;AAC7B,KAAe,6BAA6B;AAC5C;;EAEF,IAAM,IAAgC;GACpC,WAAW;GACX,MAAM,EAAa,KAAK,MAAM;GAC9B,aAAa,EAAa;GAC1B,MAAM,EAAM,EAAa,KAAK;GAC9B,MAAM,EAAM,EAAa,KAAK;GAC9B,cAAc,EAAM,EAAa,aAAa;GAC9C,aAAa,EAAM,EAAa,YAAY;GAC5C,cAAc,EAAa,aAAa,MAAM,IAAI;GACnD;AACD,MAAI;AAEF,GADA,MAAM,EAAY,YAAY,EAAM,EACpC,EAAkB,KAAK;WAChB,GAAG;AACV,KAAe,aAAa,QAAQ,EAAE,UAAU,0BAA0B;;;AAI9E,QACE,kBAAC,GAAD;EACE,OAAO,EAAQ;EACf,SACE,kBAAA,IAAA,EAAA,UAAA;GACG,EAAQ,WAAW,WAClB,kBAAC,GAAD;IAAQ,eAAe,KAAK,GAAa,SAAS;IAAE,UAAU,EAAW;cAAzE,CACE,kBAAC,IAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,YAC1B;;GAEV,EAAQ,WAAW,YAClB,kBAAC,GAAD;IAAQ,eAAe,KAAK,GAAa,YAAY;IAAE,UAAU,EAAW;cAA5E,CACE,kBAAC,IAAD,EAAc,WAAU,gBAAiB,CAAA,EAAA,YAClC;;GAEV,EAAQ,WAAW,cAClB,kBAAC,GAAD;IACE,SAAQ;IACR,eAAe,KAAK,IAAW;IAC/B,UAAU,EAAS;cAHrB,CAKE,kBAAC,IAAD,EAAS,WAAU,gBAAiB,CAAA,EAAA,WAC7B;;GAEX,kBAAC,GAAD;IAAQ,SAAS;cAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,eAC1B;;GACR,EAAA,CAAA;YA1BP;GA6BE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,WAAW;IACrC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,gBAC1B;;GAGT,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA2C,EAAQ;SAAU,CAAA,EAC3E,kBAAC,GAAD,EAAa,QAAQ,EAAQ,QAAU,CAAA,CACnC;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAA,UAAO,EAAgB,EAAQ,MAAM,EAAQ,CAAA;SAC7C,kBAAC,GAAD,EAAA,UAAO,EAAgB,EAAQ,MAAM,EAAQ,CAAA;SAC7C,kBAAC,GAAD,EAAA,UAAO,EAAgB,EAAQ,SAAS,EAAQ,CAAA;SAChD,kBAAC,GAAD,EAAA,UAAA;UACG,EAAa,EAAQ,MAAM;UAAC;UAAO,EAAa,EAAQ,YAAY;UAAC;UACjE,EAAA,CAAA;SACH;;OACN,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAW,EAAQ,UAAU;SAAC;SAAI,EAAW,EAAQ,QAAQ;SAC5D;;OACH,EAAQ,KAAK,SAAS,KACrB,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAQ,KAAK,KAAK,MACjB,kBAAC,QAAD;SAEE,WAAU;mBAFZ,CAGC,KACG,EACG;WAJA,EAIA,CACP;QACE,CAAA;OAEP,EAAQ,eACP,kBAAC,KAAD;QAAG,WAAU;kBAA8C,EAAQ;QAAgB,CAAA;OAEjF;;KACF,CAAA,EAEL,KACC,kBAAC,KAAD;KAAG,WAAU;eACV;KACC,CAAA,CAEI;;GAGZ,kBAAC,IAAD;IACE,OAAM;IACN,aAAY;cAEX,GAAS,WAAW,IACnB,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,IAAD,EAAU,WAAU,aAAc,CAAA;KACxC,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAQ,SAAS;gBAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,eAC1B;;KAEX,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,GAAS,KAAK,MACb,kBAAC,IAAD;MAAuC;MAAS,eAAe;MAAmB,EAAhE,EAAQ,GAAwD,CAClF;KACE,CAAA;IAEA,CAAA;GAGV,kBAAC,GAAD;IAAkB,MAAM;IAAa,cAAc;cACjD,kBAAC,GAAD;KAAyB,WAAU;eAAnC;MACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,eAAmC,CAAA,EAC1D,kBAAC,GAAD,EAAA,UAA6B,+EAEC,CAAA,CACP,EAAA,CAAA;MAEzB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KACC,kBAAC,KAAD;SAAG,WAAU;mBACV;SACC,CAAA;QAGN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAc;UAAY,CAAA,EACzC,kBAAC,GAAD;UACE,IAAG;UACH,OAAO,EAAY;UACnB,WAAW,MAAM,EAAe;WAAE,GAAG;WAAa,MAAM,EAAE,OAAO;WAAO,CAAC;UACzE,aAAY;UACZ,CAAA,CACE;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAc;UAAY,CAAA,EACzC,kBAAC,GAAD;UACE,IAAG;UACH,OAAO,EAAY;UACnB,WAAW,MAAM,EAAe;WAAE,GAAG;WAAa,MAAM;WAAG,CAAC;UAC5D,SAAS;UACT,CAAA,CACE;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAc;YAAY,CAAA,EACzC,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,OAAO,EAAY;YACnB,WAAW,MAAM,EAAe;aAAE,GAAG;aAAa,YAAY,EAAE,OAAO;aAAO,CAAC;YAC/E,aAAY;YACZ,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAa;YAAW,CAAA,EACvC,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,KAAK;YACL,OAAO,EAAY;YACnB,WAAW,MAAM,EAAe;aAAE,GAAG;aAAa,WAAW,EAAE,OAAO;aAAO,CAAC;YAC9E,aAAY;YACZ,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAa;YAAsB,CAAA,EAClD,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,OAAO,EAAY;YACnB,WAAW,MAAM,EAAe;aAAE,GAAG;aAAa,aAAa,EAAE,OAAO;aAAO,CAAC;YAChF,aAAY;YACZ,CAAA,CACE;;UACF;;QACF;;MAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OACE,SAAQ;OACR,eAAe,EAAe,GAAM;OACpC,UAAU,EAAW;iBACtB;OAEQ,CAAA,EACT,kBAAC,GAAD;OAAQ,eAAe,KAAK,IAAe;OAAE,UAAU,EAAW;iBAC/D,EAAW,YAAY,YAAY;OAC7B,CAAA,CACc,EAAA,CAAA;MACD;;IACT,CAAA;GAGnB,kBAAC,GAAD;IACE,MAAM,MAAmB;IACzB,eAAe,MAAS;AACtB,KAAK,KAAM,EAAkB,KAAK;;cAGpC,kBAAC,GAAD;KAAyB,WAAU;eAAnC;MACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,gBAAoC,CAAA,EAC3D,kBAAC,GAAD,EAAA,UAA6B,2DAEC,CAAA,CACP,EAAA,CAAA;MAEzB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KACC,kBAAC,KAAD;SAAG,WAAU;mBACV;SACC,CAAA;QAGN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAc;UAAY,CAAA,EACzC,kBAAC,GAAD;UACE,IAAG;UACH,OAAO,EAAa;UACpB,WAAW,MAAM,EAAgB;WAAE,GAAG;WAAc,MAAM,EAAE,OAAO;WAAO,CAAC;UAC3E,aAAY;UACZ,CAAA,CACE;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAgB;UAAoB,CAAA,EACnD,kBAAC,GAAD;UACE,IAAG;UACH,OAAO,EAAa;UACpB,WAAW,MAAM,EAAgB;WAAE,GAAG;WAAc,aAAa;WAAG,CAAC;UACrE,SAAS;UACT,CAAA,CACE;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAc;YAAY,CAAA,EACzC,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,OAAO,EAAa;YACpB,WAAW,MAAM,EAAgB;aAAE,GAAG;aAAc,MAAM,EAAE,OAAO;aAAO,CAAC;YAC3E,aAAY;YACZ,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAc;YAAY,CAAA,EACzC,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,OAAO,EAAa;YACpB,WAAW,MAAM,EAAgB;aAAE,GAAG;aAAc,MAAM,EAAE,OAAO;aAAO,CAAC;YAC3E,aAAY;YACZ,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAgB;YAAmB,CAAA,EAClD,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,MAAK;YACL,OAAO,EAAa;YACpB,WAAW,MACT,EAAgB;aAAE,GAAG;aAAc,cAAc,EAAE,OAAO;aAAO,CAAC;YAEpE,aAAY;YACZ,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD;YAAO,SAAQ;sBAAc;YAAkB,CAAA,EAC/C,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,KAAK;YACL,OAAO,EAAa;YACpB,WAAW,MACT,EAAgB;aAAE,GAAG;aAAc,aAAa,EAAE,OAAO;aAAO,CAAC;YAEnE,aAAY;YACZ,CAAA,CACE;;UACF;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAsB;UAAoB,CAAA,EACzD,kBAAC,IAAD;UACE,IAAG;UACH,MAAM;UACN,OAAO,EAAa;UACpB,WAAW,MAAM,EAAgB;WAAE,GAAG;WAAc,cAAc,EAAE,OAAO;WAAO,CAAC;UACnF,aAAY;UACZ,CAAA,CACE;;QACF;;MAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OACE,SAAQ;OACR,eAAe,EAAkB,KAAK;OACtC,UAAU,EAAY;iBACvB;OAEQ,CAAA,EACT,kBAAC,GAAD;OAAQ,eAAe,KAAK,IAAgB;OAAE,UAAU,EAAY;iBACjE,EAAY,YAAY,YAAY;OAC9B,CAAA,CACc,EAAA,CAAA;MACD;;IACT,CAAA;GACR"}
|
|
1
|
+
{"version":3,"file":"ProgramDetailPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/programs/ProgramDetailPage.tsx"],"sourcesContent":["/**\n * Program detail — a single training block with its workouts & exercises.\n * @module mybodyshield/pages/programs\n *\n * Renders one `mybProgram` (with its nested `workouts[].exercises[]`), lifecycle\n * transition controls (activate / complete / archive) and dialogs to add\n * workouts and exercises. All data & mutations hit the real `myb*` contract.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Plus,\n CheckCircle2,\n Archive,\n Play,\n Dumbbell,\n Pencil,\n Flame,\n Lock,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n EvidenceUpload,\n FileLink,\n MybDiscussion,\n MybTags,\n} from '../../components';\nimport { IconChip, WORKOUT_TYPE_META, MUSCLE_META } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybProgram,\n useMybUpdateProgram,\n useMybAddWorkout,\n useMybAddExercise,\n useMybTransitionProgram,\n useMybArchiveProgram,\n type UploadedMybFile,\n} from '../../hooks';\nimport {\n formatEnumLabel,\n formatDate,\n formatNumber,\n formatWeight,\n formatDuration,\n formatMinutes,\n} from '../../utils/formatters';\nimport type {\n MybWorkout,\n MybExercise,\n MybWorkoutType,\n MybMuscleGroup,\n MybProgramStatus,\n MybGoalType,\n MybFitnessLevel,\n MybProgramPhase,\n MybCreateWorkoutInput,\n MybCreateExerciseInput,\n MybUpdateProgramInput,\n} from '../../types';\n\nconst WORKOUT_TYPES: readonly MybWorkoutType[] = [\n 'STRENGTH',\n 'CARDIO',\n 'HIIT',\n 'MOBILITY',\n 'YOGA',\n 'RECOVERY',\n];\nconst MUSCLE_GROUPS: readonly MybMuscleGroup[] = [\n 'CHEST',\n 'BACK',\n 'LEGS',\n 'SHOULDERS',\n 'ARMS',\n 'CORE',\n 'FULL_BODY',\n];\n\n/** Parse a numeric text field → number | null (null when blank / invalid). */\nconst toNum = (raw: string): number | null => {\n const t = raw.trim();\n if (t === '') return null;\n const n = Number(t);\n return Number.isFinite(n) ? n : null;\n};\n\nconst Chip: FC<{ children: ReactNode }> = ({ children }) => (\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {children}\n </span>\n);\n\nconst MuscleChip: FC<{ group: MybMuscleGroup }> = ({ group }) => {\n const tint = MUSCLE_META[group].tint;\n return (\n <span\n className=\"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium\"\n style={{ backgroundColor: `color-mix(in srgb, ${tint} 16%, transparent)`, color: tint }}\n >\n {formatEnumLabel(group)}\n </span>\n );\n};\n\n/** Token-safe generic enum <Select>. */\nfunction EnumSelect<T extends string>({\n id,\n value,\n onChange,\n options,\n}: {\n id?: string;\n value: T;\n onChange: (value: T) => void;\n options: readonly T[];\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as T)}>\n <SelectTrigger id={id}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {formatEnumLabel(opt)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\ninterface WorkoutForm {\n name: string;\n type: MybWorkoutType;\n weekNumber: string;\n dayOfWeek: string;\n durationMin: string;\n}\n\nconst EMPTY_WORKOUT: WorkoutForm = {\n name: '',\n type: 'STRENGTH',\n weekNumber: '1',\n dayOfWeek: '1',\n durationMin: '',\n};\n\ninterface ExerciseForm {\n name: string;\n muscleGroup: MybMuscleGroup;\n sets: string;\n reps: string;\n targetWeight: string;\n restSeconds: string;\n instructions: string;\n}\n\nconst EMPTY_EXERCISE: ExerciseForm = {\n name: '',\n muscleGroup: 'FULL_BODY',\n sets: '',\n reps: '',\n targetWeight: '',\n restSeconds: '',\n instructions: '',\n};\n\nconst GOAL_TYPES: readonly MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\nconst LEVELS: readonly MybFitnessLevel[] = ['BEGINNER', 'INTERMEDIATE', 'ADVANCED', 'ELITE'];\nconst PHASES: readonly MybProgramPhase[] = ['BUILD', 'PEAK', 'DELOAD', 'MAINTENANCE'];\n\ninterface ProgramEditForm {\n name: string;\n description: string;\n goalType: MybGoalType;\n level: MybFitnessLevel;\n phase: MybProgramPhase;\n weeks: string;\n daysPerWeek: string;\n startDate: string;\n endDate: string;\n}\n\nconst EMPTY_EDIT: ProgramEditForm = {\n name: '',\n description: '',\n goalType: 'GENERAL_FITNESS',\n level: 'BEGINNER',\n phase: 'BUILD',\n weeks: '',\n daysPerWeek: '',\n startDate: '',\n endDate: '',\n};\n\n/** ISO datetime → `YYYY-MM-DD` for a native `<input type=\"date\">`. */\nconst toDateInput = (iso: string | null | undefined): string => {\n if (!iso) return '';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n return d.toISOString().slice(0, 10);\n};\n\nfunction ExerciseRow({ exercise }: { exercise: MybExercise }) {\n return (\n <div className=\"flex flex-col gap-2 py-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-medium text-text-primary\">{exercise.name}</p>\n <MuscleChip group={exercise.muscleGroup} />\n </div>\n {exercise.instructions && (\n <p className=\"mt-1 text-xs text-text-tertiary\">{exercise.instructions}</p>\n )}\n {exercise.demo?.id && (\n <div className=\"mt-1.5\">\n <FileLink fileId={exercise.demo.id} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-4 text-sm tabular-nums text-text-secondary\">\n <span>\n {formatNumber(exercise.sets)}×{formatNumber(exercise.reps)} @{' '}\n {formatWeight(exercise.targetWeight)}\n </span>\n <span className=\"text-text-tertiary\">Rest {formatDuration(exercise.restSeconds)}</span>\n </div>\n </div>\n );\n}\n\nfunction WorkoutCard({\n workout,\n canEdit,\n onAddExercise,\n}: {\n workout: MybWorkout;\n canEdit: boolean;\n onAddExercise: (workoutId: string) => void;\n}) {\n const meta = WORKOUT_TYPE_META[workout.type];\n const TypeIcon = meta.icon;\n const exercises = useMemo(\n () => [...(workout.exercises ?? [])].sort((a, b) => a.orderIndex - b.orderIndex),\n [workout.exercises]\n );\n\n return (\n <GlassCard treatment=\"glass\" className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<TypeIcon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{workout.name}</h3>\n <StatusBadge status={workout.type} />\n </div>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n Week {formatNumber(workout.weekNumber)} · Day {formatNumber(workout.dayOfWeek)} ·{' '}\n {formatMinutes(workout.durationMin)}\n </p>\n </div>\n </div>\n {canEdit && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => onAddExercise(workout.id)}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add exercise\n </Button>\n )}\n </div>\n\n {exercises.length === 0 ? (\n <p className=\"mt-4 rounded-lg border border-dashed border-border-seam px-3 py-4 text-center text-sm text-text-tertiary\">\n No exercises yet — add the first movement to this workout.\n </p>\n ) : (\n <div className=\"mt-4 divide-y divide-border-seam/60\">\n {exercises.map((exercise) => (\n <ExerciseRow key={exercise.id} exercise={exercise} />\n ))}\n </div>\n )}\n </GlassCard>\n );\n}\n\nexport function ProgramDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useMyBodyShield();\n\n const { data: program, isLoading, isError, refetch } = useMybProgram(id);\n\n const transition = useMybTransitionProgram();\n const archiveM = useMybArchiveProgram();\n const updateProgram = useMybUpdateProgram();\n const addWorkout = useMybAddWorkout();\n const addExercise = useMybAddExercise();\n\n const [actionErr, setActionErr] = useState<string | null>(null);\n\n // Add-workout dialog\n const [workoutOpen, setWorkoutOpen] = useState(false);\n const [workoutForm, setWorkoutForm] = useState<WorkoutForm>(EMPTY_WORKOUT);\n const [workoutErr, setWorkoutErr] = useState<string | null>(null);\n\n // Add-exercise dialog (targets a specific workout)\n const [exerciseTarget, setExerciseTarget] = useState<string | null>(null);\n const [exerciseForm, setExerciseForm] = useState<ExerciseForm>(EMPTY_EXERCISE);\n const [exerciseDemo, setExerciseDemo] = useState<UploadedMybFile | null>(null);\n const [exerciseErr, setExerciseErr] = useState<string | null>(null);\n\n // Edit-program dialog\n const [editOpen, setEditOpen] = useState(false);\n const [editForm, setEditForm] = useState<ProgramEditForm>(EMPTY_EDIT);\n const [editErr, setEditErr] = useState<string | null>(null);\n\n const workouts = useMemo(\n () =>\n [...(program?.workouts ?? [])].sort(\n (a, b) =>\n a.weekNumber - b.weekNumber || a.dayOfWeek - b.dayOfWeek || a.orderIndex - b.orderIndex\n ),\n [program?.workouts]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Program\">\n <LoadingView label=\"Loading program…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Program\">\n <ErrorView message=\"We couldn't load this program.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!program) {\n return (\n <PageLayout title=\"Program\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"Program not found\"\n description=\"This program may have been deleted or you don't have access to it.\"\n action={\n <Button variant=\"outline\" onClick={() => navigateTo('programs')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" /> Back to programs\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const programId = program.id;\n // You only structure DRAFT/ACTIVE programs — COMPLETED/ARCHIVED are locked.\n const canStructure = program.status === 'DRAFT' || program.status === 'ACTIVE';\n\n const openEdit = () => {\n setEditForm({\n name: program.name,\n description: program.description ?? '',\n goalType: program.goalType,\n level: program.level,\n phase: program.phase,\n weeks: program.weeks ? String(program.weeks) : '',\n daysPerWeek: program.daysPerWeek ? String(program.daysPerWeek) : '',\n startDate: toDateInput(program.startDate),\n endDate: toDateInput(program.endDate),\n });\n setEditErr(null);\n setEditOpen(true);\n };\n\n const submitEdit = async () => {\n if (!editForm.name.trim()) {\n setEditErr('Program name is required.');\n return;\n }\n const input: MybUpdateProgramInput = {\n name: editForm.name.trim(),\n description: editForm.description.trim() || null,\n goalType: editForm.goalType,\n level: editForm.level,\n phase: editForm.phase,\n weeks: toNum(editForm.weeks),\n daysPerWeek: toNum(editForm.daysPerWeek),\n startDate: editForm.startDate ? new Date(editForm.startDate).toISOString() : null,\n endDate: editForm.endDate ? new Date(editForm.endDate).toISOString() : null,\n };\n setEditErr(null);\n try {\n await updateProgram.mutateAsync({ id: programId, input });\n setEditOpen(false);\n } catch (e) {\n setEditErr(e instanceof Error ? e.message : 'Failed to update program.');\n }\n };\n\n const doTransition = async (status: MybProgramStatus) => {\n setActionErr(null);\n try {\n await transition.mutateAsync({ id: programId, status });\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to update program status.');\n }\n };\n\n const doArchive = async () => {\n if (!window.confirm('Archive this program? It will be moved out of your active library.')) {\n return;\n }\n setActionErr(null);\n try {\n await archiveM.mutateAsync(programId);\n await refetch();\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to archive program.');\n }\n };\n\n const openAddWorkout = () => {\n setWorkoutForm(EMPTY_WORKOUT);\n setWorkoutErr(null);\n setWorkoutOpen(true);\n };\n\n const submitWorkout = async () => {\n if (!workoutForm.name.trim()) {\n setWorkoutErr('Workout name is required.');\n return;\n }\n const input: MybCreateWorkoutInput = {\n programId,\n name: workoutForm.name.trim(),\n type: workoutForm.type,\n weekNumber: toNum(workoutForm.weekNumber),\n dayOfWeek: toNum(workoutForm.dayOfWeek),\n durationMin: toNum(workoutForm.durationMin),\n };\n try {\n await addWorkout.mutateAsync(input);\n setWorkoutOpen(false);\n } catch (e) {\n setWorkoutErr(e instanceof Error ? e.message : 'Failed to add workout.');\n }\n };\n\n const openAddExercise = (workoutId: string) => {\n setExerciseForm(EMPTY_EXERCISE);\n setExerciseDemo(null);\n setExerciseErr(null);\n setExerciseTarget(workoutId);\n };\n\n const submitExercise = async () => {\n if (!exerciseTarget) return;\n if (!exerciseForm.name.trim()) {\n setExerciseErr('Exercise name is required.');\n return;\n }\n const input: MybCreateExerciseInput = {\n workoutId: exerciseTarget,\n name: exerciseForm.name.trim(),\n muscleGroup: exerciseForm.muscleGroup,\n sets: toNum(exerciseForm.sets),\n reps: toNum(exerciseForm.reps),\n targetWeight: toNum(exerciseForm.targetWeight),\n restSeconds: toNum(exerciseForm.restSeconds),\n instructions: exerciseForm.instructions.trim() || null,\n demoFileId: exerciseDemo?.fileId ?? null,\n };\n try {\n await addExercise.mutateAsync(input);\n setExerciseTarget(null);\n } catch (e) {\n setExerciseErr(e instanceof Error ? e.message : 'Failed to add exercise.');\n }\n };\n\n return (\n <PageLayout\n title={program.name}\n actions={\n <>\n {program.status === 'DRAFT' && (\n <Button onClick={() => void doTransition('ACTIVE')} disabled={transition.isPending}>\n <Play className=\"mr-1 h-4 w-4\" /> Activate\n </Button>\n )}\n {program.status === 'ACTIVE' && (\n <>\n <Button\n onClick={() => void doTransition('COMPLETED')}\n disabled={transition.isPending}\n >\n <CheckCircle2 className=\"mr-1 h-4 w-4\" /> Complete\n </Button>\n <Button variant=\"outline\" onClick={() => navigateTo('train')}>\n <Flame className=\"mr-1 h-4 w-4\" /> Start a session\n </Button>\n </>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button variant=\"outline\" onClick={openEdit} disabled={updateProgram.isPending}>\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button\n variant=\"outline\"\n onClick={() => void doArchive()}\n disabled={archiveM.isPending}\n >\n <Archive className=\"mr-1 h-4 w-4\" /> Archive\n </Button>\n )}\n {canStructure && (\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n )}\n </>\n }\n >\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"mb-4 inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> All programs\n </button>\n\n {/* Header card */}\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{program.name}</h2>\n <StatusBadge status={program.status} />\n </div>\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n <Chip>{formatEnumLabel(program.phase)}</Chip>\n <Chip>{formatEnumLabel(program.level)}</Chip>\n <Chip>{formatEnumLabel(program.goalType)}</Chip>\n <Chip>\n {formatNumber(program.weeks)} wk × {formatNumber(program.daysPerWeek)} days\n </Chip>\n </div>\n <p className=\"mt-3 text-sm text-text-secondary\">\n {formatDate(program.startDate)} – {formatDate(program.endDate)}\n </p>\n {(program.tags ?? []).length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {(program.tags ?? []).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n {program.description && (\n <p className=\"mt-3 max-w-2xl text-sm text-text-secondary\">{program.description}</p>\n )}\n </div>\n </div>\n\n {/* Editable platform tags (the inline #tags above are a read-only supplement). */}\n <div className=\"mt-5 border-t border-border-seam pt-4\">\n <MybTags entityType=\"mybodyshield-program\" entityId={program.id} label=\"Platform tags\" />\n </div>\n\n {actionErr && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionErr}\n </p>\n )}\n </GlassCard>\n\n {/* Workouts */}\n <Section\n title=\"Workouts\"\n description=\"Sessions in this block, ordered by week, day & sequence.\"\n >\n {!canStructure && (\n <p className=\"mb-4 inline-flex items-center gap-1.5 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2 text-xs text-text-tertiary\">\n <Lock className=\"h-3.5 w-3.5 shrink-0\" />\n This program is {formatEnumLabel(program.status).toLowerCase()} — its structure is\n locked and can no longer be edited.\n </p>\n )}\n {workouts.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"No workouts yet\"\n description={\n canStructure\n ? 'Add workouts to lay out the training week, then attach exercises to each one.'\n : 'This program has no workouts, and its structure is locked in its current status.'\n }\n action={\n canStructure ? (\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {workouts.map((workout) => (\n <WorkoutCard\n key={workout.id}\n workout={workout}\n canEdit={canStructure}\n onAddExercise={openAddExercise}\n />\n ))}\n </div>\n )}\n </Section>\n\n {/* Coach & member notes */}\n <MybDiscussion\n entityType=\"mybodyshield-program\"\n entityId={program.id}\n title=\"Program notes\"\n description=\"Coach and member notes for this training block.\"\n />\n\n {/* Add-workout dialog */}\n <ResponsiveDialog open={workoutOpen} onOpenChange={setWorkoutOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add workout</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a training session to this program. Attach exercises once it's created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {workoutErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {workoutErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-name\">Name</Label>\n <Input\n id=\"myb-wk-name\"\n value={workoutForm.name}\n onChange={(e) => setWorkoutForm({ ...workoutForm, name: e.target.value })}\n placeholder=\"e.g. Upper Body — Push\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-type\">Type</Label>\n <EnumSelect\n id=\"myb-wk-type\"\n value={workoutForm.type}\n onChange={(v) => setWorkoutForm({ ...workoutForm, type: v })}\n options={WORKOUT_TYPES}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-week\">Week</Label>\n <Input\n id=\"myb-wk-week\"\n type=\"number\"\n min={1}\n value={workoutForm.weekNumber}\n onChange={(e) => setWorkoutForm({ ...workoutForm, weekNumber: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-day\">Day</Label>\n <Input\n id=\"myb-wk-day\"\n type=\"number\"\n min={1}\n max={7}\n value={workoutForm.dayOfWeek}\n onChange={(e) => setWorkoutForm({ ...workoutForm, dayOfWeek: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-dur\">Duration (min)</Label>\n <Input\n id=\"myb-wk-dur\"\n type=\"number\"\n min={0}\n value={workoutForm.durationMin}\n onChange={(e) => setWorkoutForm({ ...workoutForm, durationMin: e.target.value })}\n placeholder=\"60\"\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setWorkoutOpen(false)}\n disabled={addWorkout.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitWorkout()} disabled={addWorkout.isPending}>\n {addWorkout.isPending ? 'Adding…' : 'Add workout'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add-exercise dialog */}\n <ResponsiveDialog\n open={exerciseTarget !== null}\n onOpenChange={(open) => {\n if (!open) setExerciseTarget(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add exercise</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a movement with its target sets, reps, load & rest.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {exerciseErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {exerciseErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-name\">Name</Label>\n <Input\n id=\"myb-ex-name\"\n value={exerciseForm.name}\n onChange={(e) => setExerciseForm({ ...exerciseForm, name: e.target.value })}\n placeholder=\"e.g. Barbell Bench Press\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-muscle\">Muscle group</Label>\n <EnumSelect\n id=\"myb-ex-muscle\"\n value={exerciseForm.muscleGroup}\n onChange={(v) => setExerciseForm({ ...exerciseForm, muscleGroup: v })}\n options={MUSCLE_GROUPS}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-sets\">Sets</Label>\n <Input\n id=\"myb-ex-sets\"\n type=\"number\"\n min={0}\n value={exerciseForm.sets}\n onChange={(e) => setExerciseForm({ ...exerciseForm, sets: e.target.value })}\n placeholder=\"3\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-reps\">Reps</Label>\n <Input\n id=\"myb-ex-reps\"\n type=\"number\"\n min={0}\n value={exerciseForm.reps}\n onChange={(e) => setExerciseForm({ ...exerciseForm, reps: e.target.value })}\n placeholder=\"10\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-weight\">Weight (kg)</Label>\n <Input\n id=\"myb-ex-weight\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={exerciseForm.targetWeight}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, targetWeight: e.target.value })\n }\n placeholder=\"60\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-rest\">Rest (sec)</Label>\n <Input\n id=\"myb-ex-rest\"\n type=\"number\"\n min={0}\n value={exerciseForm.restSeconds}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, restSeconds: e.target.value })\n }\n placeholder=\"90\"\n />\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-instructions\">Instructions</Label>\n <Textarea\n id=\"myb-ex-instructions\"\n rows={3}\n value={exerciseForm.instructions}\n onChange={(e) => setExerciseForm({ ...exerciseForm, instructions: e.target.value })}\n placeholder=\"Cues, tempo, form notes…\"\n />\n </div>\n\n <EvidenceUpload\n id=\"myb-ex-demo\"\n label=\"Demo media (optional)\"\n value={exerciseDemo}\n onChange={setExerciseDemo}\n accept=\"video/*,image/*\"\n disabled={addExercise.isPending}\n hint=\"Attach a short demo clip or image to show how the movement is performed.\"\n />\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setExerciseTarget(null)}\n disabled={addExercise.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitExercise()} disabled={addExercise.isPending}>\n {addExercise.isPending ? 'Adding…' : 'Add exercise'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit-program dialog */}\n <ResponsiveDialog open={editOpen} onOpenChange={setEditOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit program</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Update this program's plan, goal & schedule. Workouts stay untouched.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {editErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {editErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-name\">Name</Label>\n <Input\n id=\"myb-edit-name\"\n value={editForm.name}\n onChange={(e) => setEditForm({ ...editForm, name: e.target.value })}\n placeholder=\"e.g. Hypertrophy Block A\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-desc\">Description</Label>\n <Textarea\n id=\"myb-edit-desc\"\n rows={3}\n value={editForm.description}\n onChange={(e) => setEditForm({ ...editForm, description: e.target.value })}\n placeholder=\"Focus, progression scheme, notes…\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-goal\">Goal type</Label>\n <EnumSelect\n id=\"myb-edit-goal\"\n value={editForm.goalType}\n onChange={(v) => setEditForm({ ...editForm, goalType: v })}\n options={GOAL_TYPES}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-level\">Level</Label>\n <EnumSelect\n id=\"myb-edit-level\"\n value={editForm.level}\n onChange={(v) => setEditForm({ ...editForm, level: v })}\n options={LEVELS}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-phase\">Phase</Label>\n <EnumSelect\n id=\"myb-edit-phase\"\n value={editForm.phase}\n onChange={(v) => setEditForm({ ...editForm, phase: v })}\n options={PHASES}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-weeks\">Weeks</Label>\n <Input\n id=\"myb-edit-weeks\"\n type=\"number\"\n min={1}\n value={editForm.weeks}\n onChange={(e) => setEditForm({ ...editForm, weeks: e.target.value })}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-days\">Days / week</Label>\n <Input\n id=\"myb-edit-days\"\n type=\"number\"\n min={1}\n max={7}\n value={editForm.daysPerWeek}\n onChange={(e) => setEditForm({ ...editForm, daysPerWeek: e.target.value })}\n placeholder=\"4\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-start\">Start date</Label>\n <Input\n id=\"myb-edit-start\"\n type=\"date\"\n value={editForm.startDate}\n onChange={(e) => setEditForm({ ...editForm, startDate: e.target.value })}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-end\">End date</Label>\n <Input\n id=\"myb-edit-end\"\n type=\"date\"\n value={editForm.endDate}\n onChange={(e) => setEditForm({ ...editForm, endDate: e.target.value })}\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setEditOpen(false)}\n disabled={updateProgram.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitEdit()} disabled={updateProgram.isPending}>\n {updateProgram.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -10,20 +10,22 @@ import "../../hooks/index.js";
|
|
|
10
10
|
import { DataTable as le } from "../../components/DataTable.js";
|
|
11
11
|
import { FilterTabs as ue } from "../../components/FilterTabs.js";
|
|
12
12
|
import { Pagination as de } from "../../components/Pagination.js";
|
|
13
|
+
import { EvidenceUpload as fe } from "../../components/EvidenceUpload.js";
|
|
14
|
+
import { FileLink as pe } from "../../components/FileLink.js";
|
|
13
15
|
import "../../components/index.js";
|
|
14
|
-
import { Tooltip as
|
|
15
|
-
import { ResponsiveContainer as
|
|
16
|
-
import { CartesianGrid as
|
|
17
|
-
import { Line as
|
|
18
|
-
import { XAxis as
|
|
19
|
-
import { YAxis as
|
|
20
|
-
import { LineChart as
|
|
21
|
-
import { useEffect as
|
|
22
|
-
import { Activity as
|
|
23
|
-
import { Fragment as
|
|
24
|
-
import { Button as m, GlassCard as
|
|
16
|
+
import { Tooltip as c } from "../../../node_modules/recharts/es6/component/Tooltip.js";
|
|
17
|
+
import { ResponsiveContainer as me } from "../../../node_modules/recharts/es6/component/ResponsiveContainer.js";
|
|
18
|
+
import { CartesianGrid as he } from "../../../node_modules/recharts/es6/cartesian/CartesianGrid.js";
|
|
19
|
+
import { Line as ge } from "../../../node_modules/recharts/es6/cartesian/Line.js";
|
|
20
|
+
import { XAxis as _e } from "../../../node_modules/recharts/es6/cartesian/XAxis.js";
|
|
21
|
+
import { YAxis as ve } from "../../../node_modules/recharts/es6/cartesian/YAxis.js";
|
|
22
|
+
import { LineChart as ye } from "../../../node_modules/recharts/es6/chart/LineChart.js";
|
|
23
|
+
import { useEffect as be, useMemo as l, useState as u } from "react";
|
|
24
|
+
import { Activity as xe, ArrowDownUp as Se, Plus as d, Sigma as Ce } from "lucide-react";
|
|
25
|
+
import { Fragment as we, jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
26
|
+
import { Button as m, GlassCard as Te, Input as h, Label as g, ResponsiveDialog as Ee, ResponsiveDialogContent as _, ResponsiveDialogDescription as v, ResponsiveDialogFooter as y, ResponsiveDialogHeader as b, ResponsiveDialogTitle as x, Select as S, SelectContent as C, SelectItem as w, SelectTrigger as T, SelectValue as E, Textarea as D } from "@burdenoff/fe-libs/ui";
|
|
25
27
|
//#region src/mybodyshield/pages/progress/ProgressPage.tsx
|
|
26
|
-
var
|
|
28
|
+
var O = [
|
|
27
29
|
"WEIGHT",
|
|
28
30
|
"BODY_FAT",
|
|
29
31
|
"RESTING_HEART_RATE",
|
|
@@ -32,7 +34,7 @@ var D = [
|
|
|
32
34
|
"HIPS",
|
|
33
35
|
"ARM",
|
|
34
36
|
"THIGH"
|
|
35
|
-
],
|
|
37
|
+
], De = {
|
|
36
38
|
WEIGHT: "down",
|
|
37
39
|
BODY_FAT: "down",
|
|
38
40
|
RESTING_HEART_RATE: "down",
|
|
@@ -49,11 +51,12 @@ var D = [
|
|
|
49
51
|
value: "",
|
|
50
52
|
unit: o[e].unit,
|
|
51
53
|
measuredAt: j(/* @__PURE__ */ new Date()),
|
|
52
|
-
notes: ""
|
|
54
|
+
notes: "",
|
|
55
|
+
photo: null
|
|
53
56
|
});
|
|
54
57
|
function N() {
|
|
55
|
-
let [j, N] = u("WEIGHT"), [
|
|
56
|
-
|
|
58
|
+
let [j, N] = u("WEIGHT"), [Oe, P] = u(!1), [F, I] = u(() => M("WEIGHT")), [L, R] = u(null), z = ce(A), B = z.reset;
|
|
59
|
+
be(() => {
|
|
57
60
|
B();
|
|
58
61
|
}, [j, B]);
|
|
59
62
|
let V = s({
|
|
@@ -65,7 +68,7 @@ function N() {
|
|
|
65
68
|
limit: A,
|
|
66
69
|
offset: z.offset
|
|
67
70
|
}
|
|
68
|
-
}), U = se(), W = o[j], G = W.icon,
|
|
71
|
+
}), U = se(), W = o[j], G = W.icon, ke = De[j], K = l(() => V.data?.items ?? [], [V.data]), q = l(() => [...K].sort((e, t) => new Date(e.measuredAt).getTime() - new Date(t.measuredAt).getTime()), [K]), J = q[q.length - 1], Y = q[q.length - 2], X = J && Y ? J.value - Y.value : void 0, Ae = l(() => q.map((e) => ({
|
|
69
72
|
label: ee(e.measuredAt),
|
|
70
73
|
value: e.value
|
|
71
74
|
})), [q]), Z = l(() => {
|
|
@@ -77,12 +80,12 @@ function N() {
|
|
|
77
80
|
max: Math.max(...e),
|
|
78
81
|
avg: e.reduce((e, t) => e + t, 0) / e.length
|
|
79
82
|
};
|
|
80
|
-
}, [q]),
|
|
83
|
+
}, [q]), je = O.map((e) => ({
|
|
81
84
|
value: e,
|
|
82
85
|
label: n(e)
|
|
83
86
|
})), Q = () => {
|
|
84
87
|
I(M(j)), R(null), P(!0);
|
|
85
|
-
},
|
|
88
|
+
}, Me = async () => {
|
|
86
89
|
let e = Number(F.value);
|
|
87
90
|
if (F.value.trim() === "" || !Number.isFinite(e)) {
|
|
88
91
|
R("Enter a valid measurement value.");
|
|
@@ -94,7 +97,8 @@ function N() {
|
|
|
94
97
|
value: e,
|
|
95
98
|
unit: F.unit.trim() || o[F.type].unit,
|
|
96
99
|
measuredAt: F.measuredAt ? new Date(F.measuredAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
97
|
-
notes: F.notes.trim() ? F.notes.trim() : null
|
|
100
|
+
notes: F.notes.trim() ? F.notes.trim() : null,
|
|
101
|
+
photoFileId: F.photo?.fileId ?? null
|
|
98
102
|
};
|
|
99
103
|
try {
|
|
100
104
|
await U.mutateAsync(t), P(!1), N(F.type), z.reset();
|
|
@@ -113,7 +117,7 @@ function N() {
|
|
|
113
117
|
/* @__PURE__ */ f("div", {
|
|
114
118
|
className: "mb-6 overflow-x-auto",
|
|
115
119
|
children: /* @__PURE__ */ f(ue, {
|
|
116
|
-
options:
|
|
120
|
+
options: je,
|
|
117
121
|
value: j,
|
|
118
122
|
onChange: N
|
|
119
123
|
})
|
|
@@ -133,7 +137,7 @@ function N() {
|
|
|
133
137
|
}) : /* @__PURE__ */ p("div", {
|
|
134
138
|
className: "space-y-6",
|
|
135
139
|
children: [
|
|
136
|
-
/* @__PURE__ */ p(
|
|
140
|
+
/* @__PURE__ */ p(Te, {
|
|
137
141
|
treatment: "glass",
|
|
138
142
|
className: "p-6",
|
|
139
143
|
children: [/* @__PURE__ */ p("div", {
|
|
@@ -163,17 +167,17 @@ function N() {
|
|
|
163
167
|
children: /* @__PURE__ */ f(oe, {
|
|
164
168
|
delta: X,
|
|
165
169
|
unit: ` ${W.unit}`,
|
|
166
|
-
goodDirection:
|
|
170
|
+
goodDirection: ke
|
|
167
171
|
})
|
|
168
172
|
})]
|
|
169
173
|
})]
|
|
170
174
|
}), /* @__PURE__ */ f("div", {
|
|
171
175
|
className: "mt-6 h-64 w-full",
|
|
172
|
-
children: /* @__PURE__ */ f(
|
|
176
|
+
children: /* @__PURE__ */ f(me, {
|
|
173
177
|
width: "100%",
|
|
174
178
|
height: "100%",
|
|
175
|
-
children: /* @__PURE__ */ p(
|
|
176
|
-
data:
|
|
179
|
+
children: /* @__PURE__ */ p(ye, {
|
|
180
|
+
data: Ae,
|
|
177
181
|
margin: {
|
|
178
182
|
top: 8,
|
|
179
183
|
right: 12,
|
|
@@ -181,12 +185,12 @@ function N() {
|
|
|
181
185
|
left: -8
|
|
182
186
|
},
|
|
183
187
|
children: [
|
|
184
|
-
/* @__PURE__ */ f(
|
|
188
|
+
/* @__PURE__ */ f(he, {
|
|
185
189
|
stroke: "var(--color-border-seam)",
|
|
186
190
|
strokeDasharray: "3 3",
|
|
187
191
|
vertical: !1
|
|
188
192
|
}),
|
|
189
|
-
/* @__PURE__ */ f(
|
|
193
|
+
/* @__PURE__ */ f(_e, {
|
|
190
194
|
dataKey: "label",
|
|
191
195
|
stroke: "var(--color-border-seam)",
|
|
192
196
|
tick: {
|
|
@@ -196,7 +200,7 @@ function N() {
|
|
|
196
200
|
tickMargin: 8,
|
|
197
201
|
minTickGap: 24
|
|
198
202
|
}),
|
|
199
|
-
/* @__PURE__ */ f(
|
|
203
|
+
/* @__PURE__ */ f(ve, {
|
|
200
204
|
stroke: "var(--color-border-seam)",
|
|
201
205
|
tick: {
|
|
202
206
|
fill: "var(--color-text-tertiary)",
|
|
@@ -205,7 +209,7 @@ function N() {
|
|
|
205
209
|
width: 44,
|
|
206
210
|
domain: ["auto", "auto"]
|
|
207
211
|
}),
|
|
208
|
-
/* @__PURE__ */ f(
|
|
212
|
+
/* @__PURE__ */ f(c, {
|
|
209
213
|
cursor: { stroke: "var(--color-border-seam)" },
|
|
210
214
|
contentStyle: {
|
|
211
215
|
background: "var(--color-bg-surface)",
|
|
@@ -217,7 +221,7 @@ function N() {
|
|
|
217
221
|
itemStyle: { color: "var(--color-text-primary)" },
|
|
218
222
|
formatter: (e) => [`${r(Number(e))} ${W.unit}`, n(j)]
|
|
219
223
|
}),
|
|
220
|
-
/* @__PURE__ */ f(
|
|
224
|
+
/* @__PURE__ */ f(ge, {
|
|
221
225
|
type: "monotone",
|
|
222
226
|
dataKey: "value",
|
|
223
227
|
name: n(j),
|
|
@@ -235,26 +239,39 @@ function N() {
|
|
|
235
239
|
})
|
|
236
240
|
})]
|
|
237
241
|
}),
|
|
238
|
-
Z && /* @__PURE__ */ p("div", {
|
|
242
|
+
Z && /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("div", {
|
|
239
243
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
240
244
|
children: [
|
|
241
245
|
/* @__PURE__ */ f(t, {
|
|
242
|
-
icon: /* @__PURE__ */ f(
|
|
246
|
+
icon: /* @__PURE__ */ f(xe, { className: "h-5 w-5" }),
|
|
243
247
|
value: r(Z.count),
|
|
244
|
-
label: "
|
|
248
|
+
label: "Recent readings"
|
|
245
249
|
}),
|
|
246
250
|
/* @__PURE__ */ f(t, {
|
|
247
|
-
icon: /* @__PURE__ */ f(
|
|
251
|
+
icon: /* @__PURE__ */ f(Ce, { className: "h-5 w-5" }),
|
|
248
252
|
value: `${r(Z.avg, { maximumFractionDigits: 1 })} ${W.unit}`,
|
|
249
|
-
label: `
|
|
253
|
+
label: `Window average ${n(j).toLowerCase()}`
|
|
250
254
|
}),
|
|
251
255
|
/* @__PURE__ */ f(t, {
|
|
252
|
-
icon: /* @__PURE__ */ f(
|
|
256
|
+
icon: /* @__PURE__ */ f(Se, { className: "h-5 w-5" }),
|
|
253
257
|
value: `${r(Z.min)}–${r(Z.max)}`,
|
|
254
|
-
label: `
|
|
258
|
+
label: `Window range (${W.unit})`
|
|
255
259
|
})
|
|
256
260
|
]
|
|
257
|
-
}),
|
|
261
|
+
}), /* @__PURE__ */ p("p", {
|
|
262
|
+
className: "mt-2 text-xs text-text-tertiary",
|
|
263
|
+
children: [
|
|
264
|
+
"Based on your most recent ",
|
|
265
|
+
r(Z.count),
|
|
266
|
+
" ",
|
|
267
|
+
n(j).toLowerCase(),
|
|
268
|
+
" reading",
|
|
269
|
+
Z.count === 1 ? "" : "s",
|
|
270
|
+
" (up to ",
|
|
271
|
+
r(k),
|
|
272
|
+
") — not a full-history total."
|
|
273
|
+
]
|
|
274
|
+
})] }),
|
|
258
275
|
/* @__PURE__ */ f(ie, {
|
|
259
276
|
title: "Measurement log",
|
|
260
277
|
description: "Every recorded reading for this metric.",
|
|
@@ -269,7 +286,7 @@ function N() {
|
|
|
269
286
|
icon: /* @__PURE__ */ f(G, { className: "h-10 w-10" }),
|
|
270
287
|
title: "No measurements on this page",
|
|
271
288
|
description: "Go back a page or record a new measurement."
|
|
272
|
-
}) : /* @__PURE__ */ p(
|
|
289
|
+
}) : /* @__PURE__ */ p(we, { children: [/* @__PURE__ */ f(le, {
|
|
273
290
|
columns: [
|
|
274
291
|
{
|
|
275
292
|
key: "value",
|
|
@@ -304,6 +321,14 @@ function N() {
|
|
|
304
321
|
className: "text-text-tertiary",
|
|
305
322
|
children: "—"
|
|
306
323
|
})
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
key: "photo",
|
|
327
|
+
header: "Photo",
|
|
328
|
+
render: (e) => e.photo?.id ? /* @__PURE__ */ f(pe, { fileId: e.photo.id }) : /* @__PURE__ */ f("span", {
|
|
329
|
+
className: "text-text-tertiary",
|
|
330
|
+
children: "—"
|
|
331
|
+
})
|
|
307
332
|
}
|
|
308
333
|
],
|
|
309
334
|
rows: $,
|
|
@@ -322,11 +347,11 @@ function N() {
|
|
|
322
347
|
})
|
|
323
348
|
]
|
|
324
349
|
}),
|
|
325
|
-
/* @__PURE__ */ f(
|
|
326
|
-
open:
|
|
350
|
+
/* @__PURE__ */ f(Ee, {
|
|
351
|
+
open: Oe,
|
|
327
352
|
onOpenChange: P,
|
|
328
|
-
children: /* @__PURE__ */ p(
|
|
329
|
-
/* @__PURE__ */ p(
|
|
353
|
+
children: /* @__PURE__ */ p(_, { children: [
|
|
354
|
+
/* @__PURE__ */ p(b, { children: [/* @__PURE__ */ f(x, { children: "Record measurement" }), /* @__PURE__ */ f(v, { children: "Log a new body-metric reading to track your progress over time." })] }),
|
|
330
355
|
/* @__PURE__ */ p("div", {
|
|
331
356
|
className: "space-y-4 py-2",
|
|
332
357
|
children: [
|
|
@@ -335,7 +360,7 @@ function N() {
|
|
|
335
360
|
children: [/* @__PURE__ */ f(g, {
|
|
336
361
|
htmlFor: "myb-metric-type",
|
|
337
362
|
children: "Metric"
|
|
338
|
-
}), /* @__PURE__ */ p(
|
|
363
|
+
}), /* @__PURE__ */ p(S, {
|
|
339
364
|
value: F.type,
|
|
340
365
|
onValueChange: (e) => {
|
|
341
366
|
let t = e;
|
|
@@ -345,10 +370,10 @@ function N() {
|
|
|
345
370
|
unit: o[t].unit
|
|
346
371
|
}));
|
|
347
372
|
},
|
|
348
|
-
children: [/* @__PURE__ */ f(
|
|
373
|
+
children: [/* @__PURE__ */ f(T, {
|
|
349
374
|
id: "myb-metric-type",
|
|
350
|
-
children: /* @__PURE__ */ f(
|
|
351
|
-
}), /* @__PURE__ */ f(
|
|
375
|
+
children: /* @__PURE__ */ f(E, { placeholder: "Select a metric" })
|
|
376
|
+
}), /* @__PURE__ */ f(C, { children: O.map((e) => /* @__PURE__ */ f(w, {
|
|
352
377
|
value: e,
|
|
353
378
|
children: n(e)
|
|
354
379
|
}, e)) })]
|
|
@@ -409,7 +434,7 @@ function N() {
|
|
|
409
434
|
children: [/* @__PURE__ */ f(g, {
|
|
410
435
|
htmlFor: "myb-metric-notes",
|
|
411
436
|
children: "Notes"
|
|
412
|
-
}), /* @__PURE__ */ f(
|
|
437
|
+
}), /* @__PURE__ */ f(D, {
|
|
413
438
|
id: "myb-metric-notes",
|
|
414
439
|
value: F.notes,
|
|
415
440
|
onChange: (e) => I((t) => ({
|
|
@@ -420,19 +445,31 @@ function N() {
|
|
|
420
445
|
rows: 3
|
|
421
446
|
})]
|
|
422
447
|
}),
|
|
448
|
+
/* @__PURE__ */ f(fe, {
|
|
449
|
+
id: "myb-metric-photo",
|
|
450
|
+
label: "Progress photo (optional)",
|
|
451
|
+
value: F.photo,
|
|
452
|
+
onChange: (e) => I((t) => ({
|
|
453
|
+
...t,
|
|
454
|
+
photo: e
|
|
455
|
+
})),
|
|
456
|
+
accept: "image/*",
|
|
457
|
+
disabled: U.isPending,
|
|
458
|
+
hint: "Attach a progress photo for this reading."
|
|
459
|
+
}),
|
|
423
460
|
L && /* @__PURE__ */ f("p", {
|
|
424
461
|
className: "text-sm text-status-error-text",
|
|
425
462
|
children: L
|
|
426
463
|
})
|
|
427
464
|
]
|
|
428
465
|
}),
|
|
429
|
-
/* @__PURE__ */ p(
|
|
466
|
+
/* @__PURE__ */ p(y, { children: [/* @__PURE__ */ f(m, {
|
|
430
467
|
variant: "outline",
|
|
431
468
|
onClick: () => P(!1),
|
|
432
469
|
disabled: U.isPending,
|
|
433
470
|
children: "Cancel"
|
|
434
471
|
}), /* @__PURE__ */ f(m, {
|
|
435
|
-
onClick:
|
|
472
|
+
onClick: Me,
|
|
436
473
|
disabled: U.isPending,
|
|
437
474
|
children: U.isPending ? "Recording…" : "Record"
|
|
438
475
|
})] })
|