@titan-design/react-ui 0.2.7 → 0.4.0

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.
Files changed (92) hide show
  1. package/dist/bodymap-BhG55xHM.d.mts +318 -0
  2. package/dist/bodymap-BhG55xHM.d.ts +318 -0
  3. package/dist/bodymap.d.mts +3 -0
  4. package/dist/bodymap.d.ts +3 -0
  5. package/dist/bodymap.js +3014 -0
  6. package/dist/bodymap.js.map +1 -0
  7. package/dist/bodymap.mjs +1088 -0
  8. package/dist/bodymap.mjs.map +1 -0
  9. package/dist/chunk-2SISKTWM.mjs +1027 -0
  10. package/dist/chunk-2SISKTWM.mjs.map +1 -0
  11. package/dist/{chunk-5SSKGS6E.mjs → chunk-7XPB4NAO.mjs} +124 -4
  12. package/dist/chunk-7XPB4NAO.mjs.map +1 -0
  13. package/dist/{chunk-3VLOBS6O.mjs → chunk-SNVKL5WZ.mjs} +42 -5
  14. package/dist/chunk-SNVKL5WZ.mjs.map +1 -0
  15. package/dist/index.d.mts +416 -250
  16. package/dist/index.d.ts +416 -250
  17. package/dist/index.js +1567 -1123
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +1491 -2201
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/theme/index.d.mts +288 -2
  22. package/dist/theme/index.d.ts +288 -2
  23. package/dist/theme/index.js +159 -2
  24. package/dist/theme/index.js.map +1 -1
  25. package/dist/theme/index.mjs +2 -2
  26. package/dist/theme/tokens-css.d.mts +5 -3
  27. package/dist/theme/tokens-css.d.ts +5 -3
  28. package/dist/theme/tokens-css.js +318 -9
  29. package/dist/theme/tokens-css.js.map +1 -1
  30. package/dist/theme/tokens-css.mjs +1 -1
  31. package/dist/theme/tokens-css.mjs.map +1 -1
  32. package/package.json +10 -1
  33. package/src/bodymap.ts +37 -0
  34. package/src/components/custom/Table/Table.test.tsx +27 -0
  35. package/src/components/custom/Table/Table.tsx +33 -17
  36. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +118 -0
  37. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +171 -0
  38. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +442 -0
  39. package/src/components/custom/Workout/BodyMap.tsx +7 -9
  40. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +25 -29
  41. package/src/components/custom/Workout/CapacityBandChart.tsx +2 -3
  42. package/src/components/custom/Workout/ExerciseCard.test.tsx +24 -0
  43. package/src/components/custom/Workout/ExerciseCard.tsx +24 -26
  44. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +225 -0
  45. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +128 -0
  46. package/src/components/custom/Workout/ExerciseDetailPage.tsx +624 -0
  47. package/src/components/custom/Workout/InputBar.tsx +10 -12
  48. package/src/components/custom/Workout/MesoCard.tsx +2 -4
  49. package/src/components/custom/Workout/MesoProgressBar.test.tsx +12 -0
  50. package/src/components/custom/Workout/MesoProgressBar.tsx +5 -1
  51. package/src/components/custom/Workout/MesoStatusCard.tsx +17 -15
  52. package/src/components/custom/Workout/MuscleGroupChip.tsx +2 -4
  53. package/src/components/custom/Workout/PlaceholderStrip.tsx +2 -4
  54. package/src/components/custom/Workout/PrHistoryModal.tsx +12 -14
  55. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +257 -0
  56. package/src/components/custom/Workout/ProgramPlanningPage.test.tsx +142 -0
  57. package/src/components/custom/Workout/ProgramPlanningPage.tsx +418 -0
  58. package/src/components/custom/Workout/ReadinessCheck.tsx +13 -12
  59. package/src/components/custom/Workout/RestTimer.test.tsx +10 -0
  60. package/src/components/custom/Workout/RestTimer.tsx +12 -9
  61. package/src/components/custom/Workout/SetRow.tsx +16 -19
  62. package/src/components/custom/Workout/StatusDot.tsx +1 -0
  63. package/src/components/custom/Workout/StrengthTrendChart.tsx +15 -17
  64. package/src/components/custom/Workout/TempoBar.stories.tsx +95 -0
  65. package/src/components/custom/Workout/TempoBar.test.tsx +103 -0
  66. package/src/components/custom/Workout/TempoBar.tsx +215 -0
  67. package/src/components/custom/Workout/TempoDisplay.tsx +3 -1
  68. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +212 -0
  69. package/src/components/custom/Workout/TrainingStatusPage.test.tsx +131 -0
  70. package/src/components/custom/Workout/TrainingStatusPage.tsx +312 -0
  71. package/src/components/custom/Workout/VelocityStrip.test.tsx +10 -0
  72. package/src/components/custom/Workout/VelocityStrip.tsx +19 -16
  73. package/src/components/custom/Workout/WeekRow.tsx +2 -1
  74. package/src/components/custom/Workout/WorkoutCard.tsx +3 -6
  75. package/src/components/custom/Workout/index.ts +80 -32
  76. package/src/test/nativewind-stub.ts +13 -0
  77. package/src/theme/ThemeProvider.test.tsx +24 -0
  78. package/src/theme/ThemeProvider.tsx +50 -0
  79. package/src/theme/config.completeness.test.ts +77 -0
  80. package/src/theme/config.ts +151 -0
  81. package/src/theme/index.ts +2 -0
  82. package/src/theme/manifest/design-freeze.test.ts +155 -0
  83. package/src/theme/manifest/extract-css-properties.fixture.html +29 -0
  84. package/src/theme/manifest/extract-css-properties.test.ts +112 -0
  85. package/src/theme/resolve-color.ts +19 -0
  86. package/src/theme/tokens-css.ts +5 -3
  87. package/src/theme/workout-tokens.ts +12 -14
  88. package/src/utils/index.ts +10 -0
  89. package/src/utils/workout-format.test.ts +81 -0
  90. package/src/utils/workout-format.ts +83 -0
  91. package/dist/chunk-3VLOBS6O.mjs.map +0 -1
  92. package/dist/chunk-5SSKGS6E.mjs.map +0 -1
@@ -0,0 +1,1088 @@
1
+ import { WORKOUT_TOKENS, cn, Badge, Sparkline, MesoStatusCard } from './chunk-2SISKTWM.mjs';
2
+ import { jsxs, jsx, resolveColor } from './chunk-SNVKL5WZ.mjs';
3
+ import './chunk-7XPB4NAO.mjs';
4
+ import { useMemo, useState, useEffect } from 'react';
5
+ import { Animated, Easing, View, Text, Pressable, ScrollView } from 'react-native';
6
+ import BodyHighlighter from 'react-native-body-highlighter';
7
+
8
+ // src/components/custom/Workout/muscleTaxonomy.ts
9
+ var MuscleGroup = /* @__PURE__ */ ((MuscleGroup5) => {
10
+ MuscleGroup5["CHEST"] = "chest";
11
+ MuscleGroup5["FRONT_DELTS"] = "front_delts";
12
+ MuscleGroup5["SIDE_DELTS"] = "side_delts";
13
+ MuscleGroup5["TRICEPS"] = "triceps";
14
+ MuscleGroup5["LATS"] = "lats";
15
+ MuscleGroup5["UPPER_BACK"] = "upper_back";
16
+ MuscleGroup5["REAR_DELTS"] = "rear_delts";
17
+ MuscleGroup5["BICEPS"] = "biceps";
18
+ MuscleGroup5["FOREARMS"] = "forearms";
19
+ MuscleGroup5["QUADS"] = "quads";
20
+ MuscleGroup5["HAMSTRINGS"] = "hamstrings";
21
+ MuscleGroup5["GLUTES"] = "glutes";
22
+ MuscleGroup5["CALVES"] = "calves";
23
+ MuscleGroup5["ABS"] = "abs";
24
+ MuscleGroup5["OBLIQUES"] = "obliques";
25
+ return MuscleGroup5;
26
+ })(MuscleGroup || {});
27
+ var SimpleMuscleGroup = /* @__PURE__ */ ((SimpleMuscleGroup3) => {
28
+ SimpleMuscleGroup3["CHEST"] = "chest";
29
+ SimpleMuscleGroup3["BACK"] = "back";
30
+ SimpleMuscleGroup3["SHOULDERS"] = "shoulders";
31
+ SimpleMuscleGroup3["BICEPS"] = "biceps";
32
+ SimpleMuscleGroup3["TRICEPS"] = "triceps";
33
+ SimpleMuscleGroup3["LEGS"] = "legs";
34
+ SimpleMuscleGroup3["CORE"] = "core";
35
+ SimpleMuscleGroup3["FOREARMS"] = "forearms";
36
+ return SimpleMuscleGroup3;
37
+ })(SimpleMuscleGroup || {});
38
+ var SIMPLE_TO_DETAILED = {
39
+ ["chest" /* CHEST */]: ["chest" /* CHEST */],
40
+ ["back" /* BACK */]: ["lats" /* LATS */, "upper_back" /* UPPER_BACK */],
41
+ ["shoulders" /* SHOULDERS */]: [
42
+ "front_delts" /* FRONT_DELTS */,
43
+ "side_delts" /* SIDE_DELTS */,
44
+ "rear_delts" /* REAR_DELTS */
45
+ ],
46
+ ["biceps" /* BICEPS */]: ["biceps" /* BICEPS */],
47
+ ["triceps" /* TRICEPS */]: ["triceps" /* TRICEPS */],
48
+ ["legs" /* LEGS */]: [
49
+ "quads" /* QUADS */,
50
+ "hamstrings" /* HAMSTRINGS */,
51
+ "glutes" /* GLUTES */,
52
+ "calves" /* CALVES */
53
+ ],
54
+ ["core" /* CORE */]: ["abs" /* ABS */, "obliques" /* OBLIQUES */],
55
+ ["forearms" /* FOREARMS */]: ["forearms" /* FOREARMS */]
56
+ };
57
+ var DETAILED_TO_SIMPLE = Object.entries(
58
+ SIMPLE_TO_DETAILED
59
+ ).reduce(
60
+ (acc, [simple, groups]) => {
61
+ for (const group of groups) {
62
+ acc[group] = simple;
63
+ }
64
+ return acc;
65
+ },
66
+ {}
67
+ );
68
+ var MUSCLE_TO_CATEGORY = {
69
+ ["chest" /* CHEST */]: "push",
70
+ ["front_delts" /* FRONT_DELTS */]: "push",
71
+ ["side_delts" /* SIDE_DELTS */]: "push",
72
+ ["triceps" /* TRICEPS */]: "push",
73
+ ["lats" /* LATS */]: "pull",
74
+ ["upper_back" /* UPPER_BACK */]: "pull",
75
+ ["rear_delts" /* REAR_DELTS */]: "pull",
76
+ ["biceps" /* BICEPS */]: "pull",
77
+ ["forearms" /* FOREARMS */]: "pull",
78
+ ["quads" /* QUADS */]: "legs",
79
+ ["hamstrings" /* HAMSTRINGS */]: "legs",
80
+ ["glutes" /* GLUTES */]: "legs",
81
+ ["calves" /* CALVES */]: "legs",
82
+ ["abs" /* ABS */]: "core",
83
+ ["obliques" /* OBLIQUES */]: "core"
84
+ };
85
+ var MUSCLE_TO_SVG_SLUGS = {
86
+ ["chest" /* CHEST */]: ["chest"],
87
+ ["front_delts" /* FRONT_DELTS */]: ["deltoids"],
88
+ ["side_delts" /* SIDE_DELTS */]: ["deltoids"],
89
+ ["rear_delts" /* REAR_DELTS */]: ["deltoids"],
90
+ ["triceps" /* TRICEPS */]: ["triceps"],
91
+ ["lats" /* LATS */]: ["upper-back"],
92
+ ["upper_back" /* UPPER_BACK */]: ["upper-back", "trapezius"],
93
+ ["biceps" /* BICEPS */]: ["biceps"],
94
+ ["forearms" /* FOREARMS */]: ["forearm"],
95
+ ["quads" /* QUADS */]: ["quadriceps"],
96
+ ["hamstrings" /* HAMSTRINGS */]: ["hamstring"],
97
+ ["glutes" /* GLUTES */]: ["gluteal"],
98
+ ["calves" /* CALVES */]: ["calves"],
99
+ ["abs" /* ABS */]: ["abs"],
100
+ ["obliques" /* OBLIQUES */]: ["obliques"]
101
+ };
102
+ var MUSCLE_DISPLAY_NAMES = {
103
+ ["chest" /* CHEST */]: "Chest",
104
+ ["front_delts" /* FRONT_DELTS */]: "Front Delts",
105
+ ["side_delts" /* SIDE_DELTS */]: "Side Delts",
106
+ ["rear_delts" /* REAR_DELTS */]: "Rear Delts",
107
+ ["triceps" /* TRICEPS */]: "Triceps",
108
+ ["lats" /* LATS */]: "Lats",
109
+ ["upper_back" /* UPPER_BACK */]: "Upper Back",
110
+ ["biceps" /* BICEPS */]: "Biceps",
111
+ ["forearms" /* FOREARMS */]: "Forearms",
112
+ ["quads" /* QUADS */]: "Quads",
113
+ ["hamstrings" /* HAMSTRINGS */]: "Hamstrings",
114
+ ["glutes" /* GLUTES */]: "Glutes",
115
+ ["calves" /* CALVES */]: "Calves",
116
+ ["abs" /* ABS */]: "Abs",
117
+ ["obliques" /* OBLIQUES */]: "Obliques"
118
+ };
119
+ var SIMPLE_DISPLAY_NAMES = {
120
+ ["chest" /* CHEST */]: "Chest",
121
+ ["back" /* BACK */]: "Back",
122
+ ["shoulders" /* SHOULDERS */]: "Shoulders",
123
+ ["biceps" /* BICEPS */]: "Biceps",
124
+ ["triceps" /* TRICEPS */]: "Triceps",
125
+ ["legs" /* LEGS */]: "Legs",
126
+ ["core" /* CORE */]: "Core",
127
+ ["forearms" /* FOREARMS */]: "Forearms"
128
+ };
129
+ var DEFAULT_VOLUME_LANDMARKS = {
130
+ ["chest" /* CHEST */]: { mev: 8, mav: 14, mrv: 20 },
131
+ ["lats" /* LATS */]: { mev: 8, mav: 14, mrv: 20 },
132
+ ["upper_back" /* UPPER_BACK */]: { mev: 6, mav: 12, mrv: 18 },
133
+ ["front_delts" /* FRONT_DELTS */]: { mev: 2, mav: 6, mrv: 10 },
134
+ ["side_delts" /* SIDE_DELTS */]: { mev: 6, mav: 14, mrv: 22 },
135
+ ["rear_delts" /* REAR_DELTS */]: { mev: 6, mav: 12, mrv: 18 },
136
+ ["biceps" /* BICEPS */]: { mev: 4, mav: 10, mrv: 18 },
137
+ ["triceps" /* TRICEPS */]: { mev: 4, mav: 8, mrv: 14 },
138
+ ["forearms" /* FOREARMS */]: { mev: 2, mav: 6, mrv: 12 },
139
+ ["quads" /* QUADS */]: { mev: 6, mav: 12, mrv: 18 },
140
+ ["hamstrings" /* HAMSTRINGS */]: { mev: 4, mav: 10, mrv: 16 },
141
+ ["glutes" /* GLUTES */]: { mev: 4, mav: 10, mrv: 16 },
142
+ ["calves" /* CALVES */]: { mev: 6, mav: 10, mrv: 16 },
143
+ ["abs" /* ABS */]: { mev: 0, mav: 8, mrv: 16 },
144
+ ["obliques" /* OBLIQUES */]: { mev: 0, mav: 6, mrv: 12 }
145
+ };
146
+ var VOLUME_STATUS_LABELS = {
147
+ under: "under-trained",
148
+ maintenance: "maintenance",
149
+ productive: "productive",
150
+ over: "over-reaching"
151
+ };
152
+ var STATUS_SEVERITY = {
153
+ under: 1,
154
+ maintenance: 2,
155
+ productive: 3,
156
+ over: 4
157
+ };
158
+ function getHeatmapColor(status, intensity = 0) {
159
+ const heatmap = WORKOUT_TOKENS.heatmap;
160
+ switch (status) {
161
+ case "under":
162
+ return heatmap.under;
163
+ case "maintenance":
164
+ return heatmap.maintenance;
165
+ case "productive":
166
+ return intensity >= 0.85 ? heatmap.approaching : heatmap.productive;
167
+ case "over":
168
+ return heatmap.over;
169
+ default:
170
+ return heatmap.none;
171
+ }
172
+ }
173
+ function isMoreSevere(a, b) {
174
+ return STATUS_SEVERITY[a] >= STATUS_SEVERITY[b];
175
+ }
176
+ var SLUG_TO_PRIMARY_MUSCLE = {
177
+ chest: "chest" /* CHEST */,
178
+ deltoids: "side_delts" /* SIDE_DELTS */,
179
+ triceps: "triceps" /* TRICEPS */,
180
+ "upper-back": "lats" /* LATS */,
181
+ trapezius: "upper_back" /* UPPER_BACK */,
182
+ biceps: "biceps" /* BICEPS */,
183
+ forearm: "forearms" /* FOREARMS */,
184
+ quadriceps: "quads" /* QUADS */,
185
+ hamstring: "hamstrings" /* HAMSTRINGS */,
186
+ gluteal: "glutes" /* GLUTES */,
187
+ calves: "calves" /* CALVES */,
188
+ abs: "abs" /* ABS */,
189
+ obliques: "obliques" /* OBLIQUES */
190
+ };
191
+
192
+ // src/components/custom/Workout/BodyMap.tsx
193
+ var Body = BodyHighlighter.default ?? BodyHighlighter;
194
+ var OUTLINE_FILL = "rgba(255,255,255,0.08)";
195
+ var OUTLINE_BORDER = "rgba(255,255,255,0.12)";
196
+ var BODY_SCALE = 0.8;
197
+ function buildSlugParts(data) {
198
+ const bySlug = /* @__PURE__ */ new Map();
199
+ for (const d of data) {
200
+ for (const slug of MUSCLE_TO_SVG_SLUGS[d.muscleGroup] ?? []) {
201
+ const existing = bySlug.get(slug);
202
+ if (!existing || isMoreSevere(d.volumeStatus, existing.status)) {
203
+ bySlug.set(slug, { status: d.volumeStatus, intensity: d.intensity });
204
+ }
205
+ }
206
+ }
207
+ return Array.from(bySlug.entries()).map(([slug, v]) => ({
208
+ slug,
209
+ color: getHeatmapColor(v.status, v.intensity)
210
+ }));
211
+ }
212
+ function buildSimpleLegend(data) {
213
+ const bySimple = /* @__PURE__ */ new Map();
214
+ for (const d of data) {
215
+ const simple = DETAILED_TO_SIMPLE[d.muscleGroup];
216
+ const existing = bySimple.get(simple);
217
+ if (!existing) {
218
+ bySimple.set(simple, {
219
+ key: simple,
220
+ name: SIMPLE_DISPLAY_NAMES[simple],
221
+ status: d.volumeStatus,
222
+ intensity: d.intensity,
223
+ sets: d.weeklySets,
224
+ muscle: d.muscleGroup
225
+ });
226
+ continue;
227
+ }
228
+ existing.sets += d.weeklySets;
229
+ existing.intensity = Math.max(existing.intensity, d.intensity);
230
+ if (isMoreSevere(d.volumeStatus, existing.status)) {
231
+ existing.status = d.volumeStatus;
232
+ existing.muscle = d.muscleGroup;
233
+ }
234
+ }
235
+ return Array.from(bySimple.values());
236
+ }
237
+ function buildDetailedLegend(data) {
238
+ return data.map((d) => ({
239
+ key: d.muscleGroup,
240
+ name: MUSCLE_DISPLAY_NAMES[d.muscleGroup],
241
+ status: d.volumeStatus,
242
+ intensity: d.intensity,
243
+ sets: d.weeklySets,
244
+ muscle: d.muscleGroup
245
+ }));
246
+ }
247
+ function BodyMap({
248
+ data,
249
+ view,
250
+ onViewChange,
251
+ onMusclePress,
252
+ highlightedMuscle,
253
+ mode = "detailed",
254
+ className,
255
+ ...props
256
+ }) {
257
+ const slugParts = useMemo(() => buildSlugParts(data), [data]);
258
+ const legend = useMemo(
259
+ () => mode === "simple" ? buildSimpleLegend(data) : buildDetailedLegend(data),
260
+ [data, mode]
261
+ );
262
+ const [highlight] = useState(() => new Animated.Value(highlightedMuscle ? 1 : 0));
263
+ useEffect(() => {
264
+ const animation = Animated.timing(highlight, {
265
+ toValue: highlightedMuscle ? 1 : 0,
266
+ duration: 250,
267
+ easing: Easing.out(Easing.ease),
268
+ useNativeDriver: false
269
+ });
270
+ animation.start();
271
+ return () => animation.stop();
272
+ }, [highlightedMuscle, highlight]);
273
+ const scale = highlight.interpolate({ inputRange: [0, 1], outputRange: [1, 1.02] });
274
+ const glowColor = highlightedMuscle ? getHeatmapColor(
275
+ data.find((d) => d.muscleGroup === highlightedMuscle)?.volumeStatus,
276
+ data.find((d) => d.muscleGroup === highlightedMuscle)?.intensity ?? 0
277
+ ) : null;
278
+ const handleBodyPress = (part) => {
279
+ if (!onMusclePress || !part.slug) return;
280
+ const fromData = data.find(
281
+ (d) => (MUSCLE_TO_SVG_SLUGS[d.muscleGroup] ?? []).includes(part.slug)
282
+ );
283
+ const muscle = fromData?.muscleGroup ?? SLUG_TO_PRIMARY_MUSCLE[part.slug];
284
+ if (muscle) onMusclePress(muscle);
285
+ };
286
+ return /* @__PURE__ */ jsxs(View, { className: cn(className), testID: "body-map", ...props, children: [
287
+ /* @__PURE__ */ jsx(ViewToggle, { view, onViewChange }),
288
+ /* @__PURE__ */ jsx(
289
+ Animated.View,
290
+ {
291
+ style: [
292
+ {
293
+ width: 200 * BODY_SCALE,
294
+ alignSelf: "center",
295
+ transform: [{ scale }]
296
+ },
297
+ glowColor ? { boxShadow: `0 0 14px 2px ${glowColor}` } : void 0
298
+ ],
299
+ "aria-hidden": true,
300
+ accessibilityElementsHidden: true,
301
+ importantForAccessibility: "no-hide-descendants",
302
+ testID: "body-map-svg",
303
+ children: /* @__PURE__ */ jsx(
304
+ Body,
305
+ {
306
+ side: view,
307
+ data: slugParts,
308
+ scale: BODY_SCALE,
309
+ gender: "male",
310
+ defaultFill: OUTLINE_FILL,
311
+ border: OUTLINE_BORDER,
312
+ onBodyPartPress: handleBodyPress
313
+ }
314
+ )
315
+ }
316
+ ),
317
+ legend.length === 0 ? /* @__PURE__ */ jsx(
318
+ Text,
319
+ {
320
+ className: "text-text-secondary",
321
+ style: {
322
+ marginTop: 8,
323
+ fontSize: 12,
324
+ textAlign: "center",
325
+ fontFamily: "Inter, sans-serif"
326
+ },
327
+ testID: "body-map-empty",
328
+ children: "No training data"
329
+ }
330
+ ) : /* @__PURE__ */ jsx(
331
+ View,
332
+ {
333
+ className: "flex-row flex-wrap",
334
+ style: { marginTop: 10, gap: 6, justifyContent: "center" },
335
+ testID: "body-map-legend",
336
+ children: legend.map((entry) => /* @__PURE__ */ jsx(
337
+ MuscleButton,
338
+ {
339
+ entry,
340
+ highlighted: highlightedMuscle === entry.muscle,
341
+ onMusclePress
342
+ },
343
+ entry.key
344
+ ))
345
+ }
346
+ )
347
+ ] });
348
+ }
349
+ function ViewToggle({ view, onViewChange }) {
350
+ return /* @__PURE__ */ jsx(
351
+ View,
352
+ {
353
+ className: "flex-row self-center",
354
+ style: { gap: 4, marginBottom: 8 },
355
+ testID: "body-map-view-toggle",
356
+ children: ["front", "back"].map((side) => {
357
+ const active = view === side;
358
+ return /* @__PURE__ */ jsx(
359
+ Pressable,
360
+ {
361
+ onPress: () => onViewChange?.(side),
362
+ accessibilityRole: "button",
363
+ accessibilityLabel: `Show ${side} view`,
364
+ "aria-pressed": active,
365
+ disabled: !onViewChange,
366
+ style: {
367
+ paddingHorizontal: 12,
368
+ paddingVertical: 4,
369
+ borderRadius: 9999,
370
+ backgroundColor: active ? "rgba(255,121,0,0.16)" : "transparent",
371
+ borderWidth: 1,
372
+ borderColor: active ? "#FF7900" : resolveColor("border-strong")
373
+ },
374
+ testID: `body-map-toggle-${side}`,
375
+ children: /* @__PURE__ */ jsx(
376
+ Text,
377
+ {
378
+ style: {
379
+ fontSize: 12,
380
+ fontFamily: "Inter, sans-serif",
381
+ fontWeight: active ? "700" : "500",
382
+ color: active ? "#FF7900" : resolveColor("text-secondary"),
383
+ textTransform: "capitalize"
384
+ },
385
+ children: side
386
+ }
387
+ )
388
+ },
389
+ side
390
+ );
391
+ })
392
+ }
393
+ );
394
+ }
395
+ function MuscleButton({ entry, highlighted, onMusclePress }) {
396
+ const dotColor = getHeatmapColor(entry.status, entry.intensity);
397
+ const label = `${entry.name}, ${VOLUME_STATUS_LABELS[entry.status]}, ${entry.sets} sets this week`;
398
+ return /* @__PURE__ */ jsxs(
399
+ Pressable,
400
+ {
401
+ onPress: () => onMusclePress?.(entry.muscle),
402
+ accessibilityRole: "button",
403
+ accessibilityLabel: label,
404
+ "aria-pressed": highlighted,
405
+ className: "bg-surface-raised",
406
+ style: {
407
+ flexDirection: "row",
408
+ alignItems: "center",
409
+ gap: 5,
410
+ paddingHorizontal: 8,
411
+ paddingVertical: 3,
412
+ borderRadius: 9999,
413
+ borderWidth: 1,
414
+ borderColor: highlighted ? "#FF7900" : resolveColor("border-default")
415
+ },
416
+ testID: `body-map-muscle-${entry.key}`,
417
+ children: [
418
+ /* @__PURE__ */ jsx(
419
+ View,
420
+ {
421
+ style: { width: 7, height: 7, borderRadius: 9999, backgroundColor: dotColor },
422
+ accessibilityElementsHidden: true,
423
+ testID: `body-map-muscle-dot-${entry.key}`
424
+ }
425
+ ),
426
+ /* @__PURE__ */ jsx(
427
+ Text,
428
+ {
429
+ className: "text-text-primary",
430
+ style: {
431
+ fontSize: 11,
432
+ fontFamily: "Inter, sans-serif",
433
+ fontWeight: "500"
434
+ },
435
+ accessibilityElementsHidden: true,
436
+ children: entry.name
437
+ }
438
+ )
439
+ ]
440
+ }
441
+ );
442
+ }
443
+ var BRAND_PRIMARY = "#FF7900";
444
+ var VOLUME_GRADIENT = "linear-gradient(90deg, #2196F3 0%, #14B8A6 50%, #D14343 100%)";
445
+ var SLIDE_OFFSET = 400;
446
+ var BACKDROP_OPACITY = 0.4;
447
+ var STATUS_BADGE_COLOR = {
448
+ under: "info",
449
+ maintenance: "warning",
450
+ productive: "success",
451
+ over: "error"
452
+ };
453
+ function clamp01(value) {
454
+ if (value < 0) return 0;
455
+ if (value > 1) return 1;
456
+ return value;
457
+ }
458
+ function markerFraction(weeklySets, { mev, mrv }) {
459
+ const span = mrv - mev;
460
+ if (span <= 0) return weeklySets >= mrv ? 1 : 0;
461
+ return clamp01((weeklySets - mev) / span);
462
+ }
463
+ function BodyMapDetailPanel({
464
+ muscleGroup,
465
+ displayName,
466
+ weeklySets,
467
+ landmarks,
468
+ volumeStatus,
469
+ lastTrained,
470
+ weeklyHistory,
471
+ contributingExercises,
472
+ upcomingExercises,
473
+ isOpen,
474
+ visible,
475
+ onClose,
476
+ onViewExercises,
477
+ ...props
478
+ }) {
479
+ const open = isOpen ?? visible ?? false;
480
+ const [translateY] = useState(() => new Animated.Value(SLIDE_OFFSET));
481
+ const [backdrop] = useState(() => new Animated.Value(0));
482
+ useEffect(() => {
483
+ if (!open) return;
484
+ const animation = Animated.parallel([
485
+ Animated.timing(translateY, {
486
+ toValue: 0,
487
+ duration: 400,
488
+ easing: Easing.out(Easing.ease),
489
+ useNativeDriver: false
490
+ }),
491
+ Animated.timing(backdrop, {
492
+ toValue: BACKDROP_OPACITY,
493
+ duration: 300,
494
+ easing: Easing.out(Easing.ease),
495
+ useNativeDriver: false
496
+ })
497
+ ]);
498
+ animation.start();
499
+ return () => animation.stop();
500
+ }, [open, translateY, backdrop]);
501
+ if (!open) return null;
502
+ const dialogLabel = `${displayName} volume details`;
503
+ const markerLeft = markerFraction(weeklySets, landmarks) * 100;
504
+ const hasContributing = (contributingExercises?.length ?? 0) > 0;
505
+ const hasUpcoming = (upcomingExercises?.length ?? 0) > 0;
506
+ return /* @__PURE__ */ jsxs(
507
+ View,
508
+ {
509
+ style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, justifyContent: "flex-end" },
510
+ testID: "body-map-detail-panel-root",
511
+ children: [
512
+ /* @__PURE__ */ jsx(
513
+ Animated.View,
514
+ {
515
+ style: {
516
+ position: "absolute",
517
+ top: 0,
518
+ left: 0,
519
+ right: 0,
520
+ bottom: 0,
521
+ backgroundColor: "#000000",
522
+ opacity: backdrop
523
+ },
524
+ testID: "body-map-detail-panel-backdrop-anim",
525
+ children: /* @__PURE__ */ jsx(
526
+ Pressable,
527
+ {
528
+ onPress: onClose,
529
+ accessibilityRole: "button",
530
+ accessibilityLabel: `Close ${displayName} details`,
531
+ style: { flex: 1 },
532
+ testID: "body-map-detail-panel-backdrop"
533
+ }
534
+ )
535
+ }
536
+ ),
537
+ /* @__PURE__ */ jsxs(
538
+ Animated.View,
539
+ {
540
+ accessibilityRole: "dialog",
541
+ accessibilityLabel: dialogLabel,
542
+ "aria-label": dialogLabel,
543
+ className: "bg-surface-elevated",
544
+ style: {
545
+ borderTopLeftRadius: 16,
546
+ borderTopRightRadius: 16,
547
+ maxHeight: "88%",
548
+ transform: [{ translateY }]
549
+ },
550
+ testID: "body-map-detail-panel",
551
+ ...props,
552
+ children: [
553
+ /* @__PURE__ */ jsx(
554
+ Pressable,
555
+ {
556
+ onPress: onClose,
557
+ accessibilityRole: "button",
558
+ accessibilityLabel: `Close ${displayName} details`,
559
+ hitSlop: 12,
560
+ style: { alignSelf: "center", paddingVertical: 8 },
561
+ testID: "body-map-detail-panel-handle",
562
+ children: /* @__PURE__ */ jsx(
563
+ View,
564
+ {
565
+ className: "bg-border-strong",
566
+ style: { width: 40, height: 4, borderRadius: 2 },
567
+ accessibilityElementsHidden: true
568
+ }
569
+ )
570
+ }
571
+ ),
572
+ /* @__PURE__ */ jsxs(
573
+ ScrollView,
574
+ {
575
+ style: { paddingHorizontal: 16 },
576
+ contentContainerStyle: { paddingBottom: 20 },
577
+ testID: "body-map-detail-panel-scroll",
578
+ children: [
579
+ /* @__PURE__ */ jsxs(
580
+ View,
581
+ {
582
+ className: "flex-row items-center justify-between",
583
+ style: { gap: 8, paddingTop: 4, paddingBottom: 4 },
584
+ testID: "body-map-detail-panel-header",
585
+ children: [
586
+ /* @__PURE__ */ jsxs(View, { className: "flex-row items-center", style: { gap: 8, flexShrink: 1 }, children: [
587
+ /* @__PURE__ */ jsx(
588
+ Text,
589
+ {
590
+ accessibilityRole: "header",
591
+ className: "text-text-primary",
592
+ style: {
593
+ fontSize: 16,
594
+ fontFamily: '"Space Grotesk", sans-serif',
595
+ fontWeight: "700"
596
+ },
597
+ testID: "body-map-detail-panel-title",
598
+ children: displayName
599
+ }
600
+ ),
601
+ /* @__PURE__ */ jsx(
602
+ Badge,
603
+ {
604
+ variant: "subtle",
605
+ color: STATUS_BADGE_COLOR[volumeStatus],
606
+ size: "sm",
607
+ testID: "body-map-detail-panel-status-badge",
608
+ children: VOLUME_STATUS_LABELS[volumeStatus]
609
+ }
610
+ )
611
+ ] }),
612
+ /* @__PURE__ */ jsx(
613
+ Pressable,
614
+ {
615
+ onPress: onClose,
616
+ accessibilityRole: "button",
617
+ accessibilityLabel: `Close ${displayName} details`,
618
+ hitSlop: 8,
619
+ style: { padding: 4, margin: -4 },
620
+ testID: "body-map-detail-panel-close",
621
+ children: /* @__PURE__ */ jsx(Text, { className: "text-text-secondary", style: { fontSize: 22, lineHeight: 22 }, children: "\xD7" })
622
+ }
623
+ )
624
+ ]
625
+ }
626
+ ),
627
+ lastTrained != null && /* @__PURE__ */ jsx(
628
+ Text,
629
+ {
630
+ className: "text-text-secondary",
631
+ style: { fontSize: 12, fontFamily: "Inter, sans-serif" },
632
+ testID: "body-map-detail-panel-last-trained",
633
+ children: `Last trained ${lastTrained}`
634
+ }
635
+ ),
636
+ /* @__PURE__ */ jsxs(View, { style: { marginTop: 16 }, testID: "body-map-detail-panel-volume", children: [
637
+ /* @__PURE__ */ jsxs(
638
+ View,
639
+ {
640
+ className: "flex-row items-center justify-between",
641
+ style: { marginBottom: 6 },
642
+ accessibilityElementsHidden: true,
643
+ children: [
644
+ /* @__PURE__ */ jsx(Text, { className: "text-text-tertiary", style: { fontSize: 10, fontFamily: "Inter, sans-serif", fontWeight: "600" }, children: `MEV ${landmarks.mev}` }),
645
+ /* @__PURE__ */ jsx(Text, { className: "text-text-tertiary", style: { fontSize: 10, fontFamily: "Inter, sans-serif", fontWeight: "600" }, children: `MRV ${landmarks.mrv}` })
646
+ ]
647
+ }
648
+ ),
649
+ /* @__PURE__ */ jsxs(
650
+ View,
651
+ {
652
+ style: { height: 14, justifyContent: "center" },
653
+ accessibilityRole: "progressbar",
654
+ accessibilityValue: { min: landmarks.mev, max: landmarks.mrv, now: weeklySets },
655
+ accessibilityLabel: `${weeklySets} weekly sets, between MEV ${landmarks.mev} and MRV ${landmarks.mrv}`,
656
+ "aria-valuenow": weeklySets,
657
+ "aria-valuemin": landmarks.mev,
658
+ "aria-valuemax": landmarks.mrv,
659
+ testID: "body-map-detail-panel-volume-bar",
660
+ children: [
661
+ /* @__PURE__ */ jsx(
662
+ View,
663
+ {
664
+ style: { height: 8, borderRadius: 4, backgroundImage: VOLUME_GRADIENT },
665
+ accessibilityElementsHidden: true
666
+ }
667
+ ),
668
+ /* @__PURE__ */ jsx(
669
+ View,
670
+ {
671
+ className: "border-text-primary bg-surface-elevated",
672
+ style: {
673
+ position: "absolute",
674
+ left: `${markerLeft}%`,
675
+ marginLeft: -7,
676
+ width: 14,
677
+ height: 14,
678
+ borderRadius: 7,
679
+ borderWidth: 2,
680
+ boxShadow: "0 0 4px rgba(0,0,0,0.5)"
681
+ },
682
+ accessibilityElementsHidden: true,
683
+ testID: "body-map-detail-panel-volume-marker"
684
+ }
685
+ )
686
+ ]
687
+ }
688
+ )
689
+ ] }),
690
+ /* @__PURE__ */ jsxs(View, { className: "flex-row items-baseline", style: { marginTop: 14, gap: 4 }, children: [
691
+ /* @__PURE__ */ jsx(
692
+ Text,
693
+ {
694
+ className: "text-text-primary",
695
+ style: {
696
+ fontSize: 24,
697
+ fontFamily: '"Space Grotesk", sans-serif',
698
+ fontWeight: "700"
699
+ },
700
+ testID: "body-map-detail-panel-set-count",
701
+ children: weeklySets
702
+ }
703
+ ),
704
+ /* @__PURE__ */ jsx(
705
+ Text,
706
+ {
707
+ className: "text-text-secondary",
708
+ style: { fontSize: 12, fontFamily: "Inter, sans-serif" },
709
+ testID: "body-map-detail-panel-mrv-suffix",
710
+ children: `/ ${landmarks.mrv} MRV`
711
+ }
712
+ )
713
+ ] }),
714
+ weeklyHistory != null && weeklyHistory.length > 0 && /* @__PURE__ */ jsxs(View, { style: { marginTop: 14 }, testID: "body-map-detail-panel-sparkline", children: [
715
+ /* @__PURE__ */ jsx(
716
+ Text,
717
+ {
718
+ className: "text-text-tertiary",
719
+ style: {
720
+ fontSize: 10,
721
+ fontFamily: "Inter, sans-serif",
722
+ fontWeight: "600",
723
+ marginBottom: 6
724
+ },
725
+ accessibilityElementsHidden: true,
726
+ children: "VOLUME TREND"
727
+ }
728
+ ),
729
+ /* @__PURE__ */ jsx(Sparkline, { data: weeklyHistory, width: 80, height: 30, highlightLast: true })
730
+ ] }),
731
+ hasContributing && /* @__PURE__ */ jsxs(View, { style: { marginTop: 16 }, testID: "body-map-detail-panel-contributing", children: [
732
+ /* @__PURE__ */ jsx(
733
+ Text,
734
+ {
735
+ className: "text-text-tertiary",
736
+ style: {
737
+ fontSize: 10,
738
+ fontFamily: "Inter, sans-serif",
739
+ fontWeight: "600",
740
+ marginBottom: 8
741
+ },
742
+ children: "CONTRIBUTING EXERCISES"
743
+ }
744
+ ),
745
+ contributingExercises.map((exercise, index) => /* @__PURE__ */ jsxs(
746
+ View,
747
+ {
748
+ className: "flex-row items-center justify-between bg-surface-raised border-border",
749
+ style: {
750
+ gap: 10,
751
+ paddingVertical: 8,
752
+ paddingHorizontal: 12,
753
+ marginBottom: 6,
754
+ borderRadius: 8,
755
+ borderWidth: 1
756
+ },
757
+ testID: `body-map-detail-panel-contributing-${index}`,
758
+ children: [
759
+ /* @__PURE__ */ jsx(
760
+ Text,
761
+ {
762
+ className: "text-text-primary",
763
+ style: { flex: 1, fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "600" },
764
+ testID: `body-map-detail-panel-contributing-${index}-name`,
765
+ children: exercise.name
766
+ }
767
+ ),
768
+ /* @__PURE__ */ jsx(
769
+ Text,
770
+ {
771
+ className: "text-text-secondary",
772
+ style: { fontSize: 12, fontFamily: "Inter, sans-serif" },
773
+ testID: `body-map-detail-panel-contributing-${index}-detail`,
774
+ children: `${exercise.sets} sets \xB7 ${Math.round(exercise.contributionWeight * 100)}%`
775
+ }
776
+ )
777
+ ]
778
+ },
779
+ `${exercise.name}-${index}`
780
+ ))
781
+ ] }),
782
+ hasUpcoming && /* @__PURE__ */ jsxs(View, { style: { marginTop: 16 }, testID: "body-map-detail-panel-upcoming", children: [
783
+ /* @__PURE__ */ jsx(
784
+ Text,
785
+ {
786
+ className: "text-text-tertiary",
787
+ style: {
788
+ fontSize: 10,
789
+ fontFamily: "Inter, sans-serif",
790
+ fontWeight: "600",
791
+ marginBottom: 8
792
+ },
793
+ children: "UPCOMING"
794
+ }
795
+ ),
796
+ upcomingExercises.map((exercise, index) => /* @__PURE__ */ jsxs(
797
+ View,
798
+ {
799
+ className: "flex-row items-center justify-between",
800
+ style: { gap: 10, paddingVertical: 6 },
801
+ testID: `body-map-detail-panel-upcoming-${index}`,
802
+ children: [
803
+ /* @__PURE__ */ jsx(
804
+ Text,
805
+ {
806
+ className: "text-text-primary",
807
+ style: { flex: 1, fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "600" },
808
+ testID: `body-map-detail-panel-upcoming-${index}-name`,
809
+ children: exercise.name
810
+ }
811
+ ),
812
+ /* @__PURE__ */ jsx(
813
+ Text,
814
+ {
815
+ className: "text-text-tertiary",
816
+ style: { fontSize: 12, fontFamily: "Inter, sans-serif" },
817
+ testID: `body-map-detail-panel-upcoming-${index}-detail`,
818
+ children: `${exercise.workoutName} \xB7 ${exercise.sets} sets`
819
+ }
820
+ )
821
+ ]
822
+ },
823
+ `${exercise.name}-${index}`
824
+ ))
825
+ ] }),
826
+ onViewExercises != null && /* @__PURE__ */ jsx(
827
+ Pressable,
828
+ {
829
+ onPress: onViewExercises,
830
+ accessibilityRole: "button",
831
+ accessibilityLabel: `View ${displayName} exercises`,
832
+ style: {
833
+ marginTop: 16,
834
+ paddingVertical: 10,
835
+ borderRadius: 8,
836
+ alignItems: "center",
837
+ backgroundColor: "rgba(255,121,0,0.12)",
838
+ borderWidth: 1,
839
+ borderColor: "rgba(255,121,0,0.3)"
840
+ },
841
+ testID: "body-map-detail-panel-view-exercises",
842
+ children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "700", color: BRAND_PRIMARY }, children: "View exercises" })
843
+ }
844
+ )
845
+ ]
846
+ }
847
+ )
848
+ ]
849
+ }
850
+ )
851
+ ]
852
+ }
853
+ );
854
+ }
855
+ var STATUS_ORDER = ["under", "maintenance", "productive", "over"];
856
+ function deriveTrainingSummary(muscles) {
857
+ const statusCounts = {
858
+ under: 0,
859
+ maintenance: 0,
860
+ productive: 0,
861
+ over: 0
862
+ };
863
+ let totalWeeklySets = 0;
864
+ for (const muscle of muscles) {
865
+ totalWeeklySets += muscle.weeklySets;
866
+ statusCounts[muscle.volumeStatus] += 1;
867
+ }
868
+ return { totalWeeklySets, trackedMuscles: muscles.length, statusCounts };
869
+ }
870
+ function toBodyMapData(muscles, side) {
871
+ return muscles.filter((muscle) => muscle.side === side).map(({ muscleGroup, intensity, volumeStatus, weeklySets }) => ({
872
+ muscleGroup,
873
+ intensity,
874
+ volumeStatus,
875
+ weeklySets
876
+ }));
877
+ }
878
+ var SUMMARY_CARDS = [
879
+ { key: "total", label: "Weekly Sets" },
880
+ { key: "productive", label: "Productive" },
881
+ { key: "under", label: "Under" },
882
+ { key: "over", label: "Over" }
883
+ ];
884
+ function SummaryCards({ summary }) {
885
+ return /* @__PURE__ */ jsx(View, { className: "flex-row flex-wrap", style: { gap: 8 }, testID: "training-status-page-summary", children: SUMMARY_CARDS.map(({ key, label }) => {
886
+ const value = key === "total" ? summary.totalWeeklySets : summary.statusCounts[key];
887
+ return /* @__PURE__ */ jsxs(
888
+ View,
889
+ {
890
+ className: "bg-surface-elevated border-border",
891
+ style: {
892
+ flexGrow: 1,
893
+ flexBasis: "46%",
894
+ padding: 12,
895
+ borderRadius: 10,
896
+ borderWidth: 1
897
+ },
898
+ testID: `training-status-page-summary-${key}`,
899
+ children: [
900
+ /* @__PURE__ */ jsx(
901
+ Text,
902
+ {
903
+ className: "text-text-primary",
904
+ style: {
905
+ fontSize: 22,
906
+ fontFamily: '"Space Grotesk", sans-serif',
907
+ fontWeight: "700"
908
+ },
909
+ children: value
910
+ }
911
+ ),
912
+ /* @__PURE__ */ jsx(
913
+ Text,
914
+ {
915
+ className: "text-text-tertiary",
916
+ style: {
917
+ marginTop: 2,
918
+ fontSize: 11,
919
+ fontFamily: "Inter, sans-serif",
920
+ fontWeight: "600",
921
+ letterSpacing: 0.4,
922
+ textTransform: "uppercase"
923
+ },
924
+ children: label
925
+ }
926
+ )
927
+ ]
928
+ },
929
+ key
930
+ );
931
+ }) });
932
+ }
933
+ function StatusLegend() {
934
+ return /* @__PURE__ */ jsx(
935
+ View,
936
+ {
937
+ className: "flex-row flex-wrap",
938
+ style: { gap: 10, justifyContent: "center" },
939
+ testID: "training-status-page-legend",
940
+ children: STATUS_ORDER.map((status) => /* @__PURE__ */ jsxs(
941
+ View,
942
+ {
943
+ className: "flex-row items-center",
944
+ style: { gap: 5 },
945
+ testID: `training-status-page-legend-${status}`,
946
+ children: [
947
+ /* @__PURE__ */ jsx(
948
+ View,
949
+ {
950
+ style: {
951
+ width: 8,
952
+ height: 8,
953
+ borderRadius: 9999,
954
+ backgroundColor: getHeatmapColor(status, 0.6)
955
+ },
956
+ accessibilityElementsHidden: true
957
+ }
958
+ ),
959
+ /* @__PURE__ */ jsx(
960
+ Text,
961
+ {
962
+ className: "text-text-secondary",
963
+ style: {
964
+ fontSize: 11,
965
+ fontFamily: "Inter, sans-serif",
966
+ textTransform: "capitalize"
967
+ },
968
+ children: VOLUME_STATUS_LABELS[status]
969
+ }
970
+ )
971
+ ]
972
+ },
973
+ status
974
+ ))
975
+ }
976
+ );
977
+ }
978
+ function BodyMapColumn({ side, muscles, selected, onSelect }) {
979
+ const data = useMemo(() => toBodyMapData(muscles, side), [muscles, side]);
980
+ return /* @__PURE__ */ jsx(View, { style: { flexGrow: 1, flexBasis: "46%" }, testID: `training-status-page-bodymap-${side}`, children: /* @__PURE__ */ jsx(
981
+ BodyMap,
982
+ {
983
+ data,
984
+ view: side,
985
+ onMusclePress: onSelect,
986
+ highlightedMuscle: selected,
987
+ mode: "detailed"
988
+ }
989
+ ) });
990
+ }
991
+ function TrainingStatusPage({
992
+ title = "Training Status",
993
+ meso,
994
+ muscles,
995
+ className,
996
+ ...props
997
+ }) {
998
+ const [selected, setSelected] = useState(null);
999
+ const summary = useMemo(() => deriveTrainingSummary(muscles), [muscles]);
1000
+ const active = selected != null ? muscles.find((m) => m.muscleGroup === selected) ?? null : null;
1001
+ return /* @__PURE__ */ jsxs(
1002
+ View,
1003
+ {
1004
+ className: ["bg-background-base", className].filter(Boolean).join(" "),
1005
+ style: { position: "relative", width: 390 },
1006
+ accessibilityRole: "main",
1007
+ "aria-label": title,
1008
+ testID: "training-status-page",
1009
+ ...props,
1010
+ children: [
1011
+ /* @__PURE__ */ jsxs(View, { style: { padding: 16, gap: 16 }, testID: "training-status-page-content", children: [
1012
+ /* @__PURE__ */ jsx(
1013
+ Text,
1014
+ {
1015
+ accessibilityRole: "header",
1016
+ className: "text-text-primary",
1017
+ style: {
1018
+ fontSize: 20,
1019
+ fontFamily: '"Space Grotesk", sans-serif',
1020
+ fontWeight: "700"
1021
+ },
1022
+ testID: "training-status-page-title",
1023
+ children: title
1024
+ }
1025
+ ),
1026
+ /* @__PURE__ */ jsx(MesoStatusCard, { ...meso }),
1027
+ /* @__PURE__ */ jsx(SummaryCards, { summary }),
1028
+ /* @__PURE__ */ jsxs(
1029
+ View,
1030
+ {
1031
+ className: "bg-surface-elevated border-border",
1032
+ style: {
1033
+ borderWidth: 1,
1034
+ borderRadius: 12,
1035
+ padding: 12,
1036
+ gap: 12
1037
+ },
1038
+ testID: "training-status-page-bodymaps",
1039
+ children: [
1040
+ /* @__PURE__ */ jsxs(View, { className: "flex-row", style: { gap: 8 }, children: [
1041
+ /* @__PURE__ */ jsx(
1042
+ BodyMapColumn,
1043
+ {
1044
+ side: "front",
1045
+ muscles,
1046
+ selected,
1047
+ onSelect: setSelected
1048
+ }
1049
+ ),
1050
+ /* @__PURE__ */ jsx(
1051
+ BodyMapColumn,
1052
+ {
1053
+ side: "back",
1054
+ muscles,
1055
+ selected,
1056
+ onSelect: setSelected
1057
+ }
1058
+ )
1059
+ ] }),
1060
+ /* @__PURE__ */ jsx(StatusLegend, {})
1061
+ ]
1062
+ }
1063
+ )
1064
+ ] }),
1065
+ active != null && /* @__PURE__ */ jsx(
1066
+ BodyMapDetailPanel,
1067
+ {
1068
+ muscleGroup: active.muscleGroup,
1069
+ displayName: active.displayName,
1070
+ weeklySets: active.weeklySets,
1071
+ landmarks: active.landmarks,
1072
+ volumeStatus: active.volumeStatus,
1073
+ lastTrained: active.lastTrained,
1074
+ weeklyHistory: active.weeklyHistory,
1075
+ contributingExercises: active.contributingExercises,
1076
+ upcomingExercises: active.upcomingExercises,
1077
+ isOpen: true,
1078
+ onClose: () => setSelected(null)
1079
+ }
1080
+ )
1081
+ ]
1082
+ }
1083
+ );
1084
+ }
1085
+
1086
+ export { BodyMap, BodyMapDetailPanel, DEFAULT_VOLUME_LANDMARKS, DETAILED_TO_SIMPLE, MUSCLE_DISPLAY_NAMES, MUSCLE_TO_CATEGORY, MUSCLE_TO_SVG_SLUGS, MuscleGroup, SIMPLE_DISPLAY_NAMES, SIMPLE_TO_DETAILED, SimpleMuscleGroup, TrainingStatusPage, VOLUME_STATUS_LABELS, deriveTrainingSummary, getHeatmapColor, toBodyMapData };
1087
+ //# sourceMappingURL=bodymap.mjs.map
1088
+ //# sourceMappingURL=bodymap.mjs.map