@burdenoff/microfe-atheletebridge 2026.629.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.
Files changed (36) hide show
  1. package/dist/atheletebridge/AthleteBridgeRoot.js +45 -37
  2. package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
  3. package/dist/atheletebridge/AthleteBridgeRoutes.js +10 -6
  4. package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
  5. package/dist/atheletebridge/components/AthleteSelect.js +66 -0
  6. package/dist/atheletebridge/components/AthleteSelect.js.map +1 -0
  7. package/dist/atheletebridge/components/DrillVideoLink.js +28 -0
  8. package/dist/atheletebridge/components/DrillVideoLink.js.map +1 -0
  9. package/dist/atheletebridge/components/MetricsCard.js +20 -46
  10. package/dist/atheletebridge/components/MetricsCard.js.map +1 -1
  11. package/dist/atheletebridge/components/UserSearchSelect.js +129 -0
  12. package/dist/atheletebridge/components/UserSearchSelect.js.map +1 -0
  13. package/dist/atheletebridge/components/domain.js +10 -1
  14. package/dist/atheletebridge/components/domain.js.map +1 -1
  15. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +313 -180
  16. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
  17. package/dist/atheletebridge/pages/DashboardPage.js +77 -69
  18. package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
  19. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +4 -4
  20. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
  21. package/dist/atheletebridge/pages/performance/PerformancePage.js +496 -0
  22. package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -0
  23. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +176 -142
  24. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
  25. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +86 -85
  26. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
  27. package/dist/atheletebridge/pages/sessions/SessionFormPage.js +92 -98
  28. package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -1
  29. package/dist/atheletebridge/pages/teams/TeamDetailPage.js +132 -137
  30. package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
  31. package/dist/atheletebridge/utils/formatters.js +2 -25
  32. package/dist/atheletebridge/utils/formatters.js.map +1 -1
  33. package/dist/atheletebridge/utils/url.js +17 -0
  34. package/dist/atheletebridge/utils/url.js.map +1 -0
  35. package/dist/index.js +4 -4
  36. package/package.json +5 -5
@@ -3,16 +3,17 @@ import { PageLayout as t } from "../../components/PageLayout.js";
3
3
  import { StatusBadge as n } from "../../components/StatusBadge.js";
4
4
  import { PROGRAM_LEVEL_LABELS as r, SPORT_LABELS as i } from "../../components/domain.js";
5
5
  import { EmptyState as a } from "../../components/EmptyState.js";
6
- import { ErrorState as ee, LoadingState as te } from "../../components/LoadingState.js";
7
- import { useAddDrill as o, useArchiveProgram as s, useProgram as c, useRemoveDrill as l, useTransitionProgram as u } from "../../hooks/useAthleteBridgeApi.js";
8
- import { FormField as d, fieldInputClass as f } from "../../components/form.js";
6
+ import { ErrorState as o, LoadingState as ee } from "../../components/LoadingState.js";
7
+ import { useAddDrill as te, useArchiveProgram as ne, useProgram as re, useRemoveDrill as s, useTransitionProgram as c, useUploadFile as l } from "../../hooks/useAthleteBridgeApi.js";
8
+ import { FormField as u, fieldInputClass as d } from "../../components/form.js";
9
+ import { DrillVideoLink as f } from "../../components/DrillVideoLink.js";
9
10
  import { useMemo as p, useState as m } from "react";
10
- import { Archive as h, ArrowLeft as g, Copy as _, Edit as v, Loader2 as y, Plus as b, Send as x, Trash2 as S, Undo2 as C } from "lucide-react";
11
- import { useParams as w } from "react-router-dom";
12
- import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
13
- import { EmphasisPanel as O, GlassCard as k, ResponsiveDialog as A, ResponsiveDialogContent as j, ResponsiveDialogFooter as M, ResponsiveDialogHeader as N, ResponsiveDialogTitle as P } from "@burdenoff/fe-libs/ui";
11
+ import { Archive as h, ArrowLeft as g, Copy as _, Edit as v, FileVideo as y, Loader2 as b, Plus as x, Send as ie, Trash2 as ae, Undo2 as oe } from "lucide-react";
12
+ import { useParams as se } from "react-router-dom";
13
+ import { Fragment as ce, jsx as S, jsxs as C } from "react/jsx-runtime";
14
+ import { EmphasisPanel as w, GlassCard as T, ResponsiveDialog as E, ResponsiveDialogContent as D, ResponsiveDialogFooter as O, ResponsiveDialogHeader as k, ResponsiveDialogTitle as A } from "@burdenoff/fe-libs/ui";
14
15
  //#region src/atheletebridge/pages/programs/ProgramDetailPage.tsx
15
- var F = {
16
+ var j = {
16
17
  name: "",
17
18
  description: "",
18
19
  weekNumber: "1",
@@ -20,15 +21,15 @@ var F = {
20
21
  reps: "",
21
22
  durationSec: "",
22
23
  restSec: ""
23
- }, I = (e) => {
24
+ }, M = (e) => {
24
25
  let t = e.trim();
25
26
  if (!t) return;
26
27
  let n = Number.parseInt(t, 10);
27
28
  return Number.isNaN(n) ? void 0 : n;
28
29
  };
29
- function L() {
30
- let { id: L } = w(), { navigateTo: R } = e(), z = c(L), B = u(), V = s(), H = o(), U = l(L), [W, G] = m(!1), [K, q] = m(F), [J, Y] = m(null), X = p(() => {
31
- let e = z.data?.drills ?? [], t = /* @__PURE__ */ new Map();
30
+ function N() {
31
+ let { id: N } = se(), { navigateTo: P } = e(), F = re(N), I = c(), L = ne(), R = te(), z = s(N), B = l(), [V, H] = m(!1), [U, W] = m(j), [G, K] = m(null), [q, J] = m(!1), [Y, X] = m(null), le = p(() => {
32
+ let e = F.data?.drills ?? [], t = /* @__PURE__ */ new Map();
32
33
  for (let n of e) {
33
34
  let e = t.get(n.weekNumber) ?? [];
34
35
  e.push(n), t.set(n.weekNumber, e);
@@ -37,52 +38,52 @@ function L() {
37
38
  week: e,
38
39
  drills: [...t].sort((e, t) => e.orderIndex - t.orderIndex)
39
40
  }));
40
- }, [z.data]);
41
- if (z.isLoading) return /* @__PURE__ */ E(t, {
41
+ }, [F.data]);
42
+ if (F.isLoading) return /* @__PURE__ */ S(t, {
42
43
  title: "Program",
43
- children: /* @__PURE__ */ E(te, { label: "Loading program…" })
44
+ children: /* @__PURE__ */ S(ee, { label: "Loading program…" })
44
45
  });
45
- if (z.isError || !z.data) return /* @__PURE__ */ E(t, {
46
+ if (F.isError || !F.data) return /* @__PURE__ */ S(t, {
46
47
  title: "Program",
47
- children: /* @__PURE__ */ E(ee, {
48
+ children: /* @__PURE__ */ S(o, {
48
49
  title: "Program not found",
49
50
  message: "This program does not exist or you do not have access to it.",
50
- onRetry: () => R("/programs")
51
+ onRetry: () => P("/programs")
51
52
  })
52
53
  });
53
- let Z = z.data, Q = Z.drills?.length ?? 0, $ = (e) => {
54
- L && B.mutate({
55
- id: L,
54
+ let Z = F.data, Q = Z.drills?.length ?? 0, $ = (e) => {
55
+ N && I.mutate({
56
+ id: N,
56
57
  status: e
57
58
  });
58
59
  };
59
- return /* @__PURE__ */ D(t, {
60
+ return /* @__PURE__ */ C(t, {
60
61
  title: Z.name,
61
62
  description: `${i[Z.sport]} · ${r[Z.level]} · ${Z.weeks} ${Z.weeks === 1 ? "week" : "weeks"}`,
62
- actions: /* @__PURE__ */ D("div", {
63
+ actions: /* @__PURE__ */ C("div", {
63
64
  className: "flex flex-wrap items-center gap-2",
64
65
  children: [
65
- /* @__PURE__ */ D("button", {
66
+ /* @__PURE__ */ C("button", {
66
67
  type: "button",
67
- onClick: () => R("/programs"),
68
+ onClick: () => P("/programs"),
68
69
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
69
- children: [/* @__PURE__ */ E(g, { className: "h-4 w-4" }), " Back"]
70
+ children: [/* @__PURE__ */ S(g, { className: "h-4 w-4" }), " Back"]
70
71
  }),
71
- /* @__PURE__ */ D("button", {
72
+ /* @__PURE__ */ C("button", {
72
73
  type: "button",
73
- onClick: () => R(`/programs/${Z.id}/edit`),
74
+ onClick: () => P(`/programs/${Z.id}/edit`),
74
75
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
75
- children: [/* @__PURE__ */ E(v, { className: "h-4 w-4" }), " Edit"]
76
+ children: [/* @__PURE__ */ S(v, { className: "h-4 w-4" }), " Edit"]
76
77
  }),
77
- Z.status !== "ARCHIVED" && /* @__PURE__ */ D("button", {
78
+ Z.status !== "ARCHIVED" && /* @__PURE__ */ C("button", {
78
79
  type: "button",
79
80
  onClick: async () => {
80
- L && (await V.mutateAsync(L), R("/programs"));
81
+ N && (await L.mutateAsync(N), P("/programs"));
81
82
  },
82
- disabled: V.isPending,
83
+ disabled: L.isPending,
83
84
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60",
84
85
  children: [
85
- V.isPending ? /* @__PURE__ */ E(y, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ E(h, { className: "h-4 w-4" }),
86
+ L.isPending ? /* @__PURE__ */ S(b, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ S(h, { className: "h-4 w-4" }),
86
87
  " ",
87
88
  "Archive"
88
89
  ]
@@ -90,226 +91,238 @@ function L() {
90
91
  ]
91
92
  }),
92
93
  children: [
93
- /* @__PURE__ */ D(k, {
94
+ /* @__PURE__ */ C(T, {
94
95
  treatment: "insight",
95
96
  className: "mb-8 p-6",
96
97
  children: [
97
- /* @__PURE__ */ D("div", {
98
+ /* @__PURE__ */ C("div", {
98
99
  className: "flex flex-wrap items-center gap-3",
99
- children: [/* @__PURE__ */ E(n, { status: Z.status }), Z.isTemplate && /* @__PURE__ */ D("span", {
100
+ children: [/* @__PURE__ */ S(n, { status: Z.status }), Z.isTemplate && /* @__PURE__ */ C("span", {
100
101
  className: "inline-flex items-center gap-1 text-sm text-text-secondary",
101
- children: [/* @__PURE__ */ E(_, { className: "h-3.5 w-3.5" }), " Template"]
102
+ children: [/* @__PURE__ */ S(_, { className: "h-3.5 w-3.5" }), " Template"]
102
103
  })]
103
104
  }),
104
- Z.summary && /* @__PURE__ */ E("p", {
105
+ Z.summary && /* @__PURE__ */ S("p", {
105
106
  className: "mt-4 text-sm text-text-secondary",
106
107
  children: Z.summary
107
108
  }),
108
- Z.tags.length > 0 && /* @__PURE__ */ E("div", {
109
+ Z.tags.length > 0 && /* @__PURE__ */ S("div", {
109
110
  className: "mt-4 flex flex-wrap gap-1.5",
110
- children: Z.tags.map((e) => /* @__PURE__ */ E("span", {
111
+ children: Z.tags.map((e) => /* @__PURE__ */ S("span", {
111
112
  className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
112
113
  children: e
113
114
  }, e))
114
115
  }),
115
- /* @__PURE__ */ D("div", {
116
+ /* @__PURE__ */ C("div", {
116
117
  className: "mt-5 flex flex-wrap items-center gap-2 border-t border-border-seam pt-5",
117
118
  children: [
118
- /* @__PURE__ */ E("span", {
119
+ /* @__PURE__ */ S("span", {
119
120
  className: "text-sm font-medium text-text-primary",
120
121
  children: "Publish lifecycle:"
121
122
  }),
122
- Z.status === "DRAFT" && /* @__PURE__ */ D("button", {
123
+ Z.status === "DRAFT" && /* @__PURE__ */ C("button", {
123
124
  type: "button",
124
125
  onClick: () => $("PUBLISHED"),
125
- disabled: B.isPending,
126
+ disabled: I.isPending,
126
127
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
127
- children: [/* @__PURE__ */ E(x, { className: "h-4 w-4" }), " Publish"]
128
+ children: [/* @__PURE__ */ S(ie, { className: "h-4 w-4" }), " Publish"]
128
129
  }),
129
- Z.status === "PUBLISHED" && /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ D("button", {
130
+ Z.status === "PUBLISHED" && /* @__PURE__ */ C(ce, { children: [/* @__PURE__ */ C("button", {
130
131
  type: "button",
131
132
  onClick: () => $("DRAFT"),
132
- disabled: B.isPending,
133
+ disabled: I.isPending,
133
134
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-60",
134
- children: [/* @__PURE__ */ E(C, { className: "h-4 w-4" }), " Pull back to draft"]
135
- }), /* @__PURE__ */ D("button", {
135
+ children: [/* @__PURE__ */ S(oe, { className: "h-4 w-4" }), " Pull back to draft"]
136
+ }), /* @__PURE__ */ C("button", {
136
137
  type: "button",
137
138
  onClick: () => $("ARCHIVED"),
138
- disabled: B.isPending,
139
+ disabled: I.isPending,
139
140
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-60",
140
- children: [/* @__PURE__ */ E(h, { className: "h-4 w-4" }), " Archive"]
141
+ children: [/* @__PURE__ */ S(h, { className: "h-4 w-4" }), " Archive"]
141
142
  })] }),
142
- Z.status === "ARCHIVED" && /* @__PURE__ */ E("span", {
143
+ Z.status === "ARCHIVED" && /* @__PURE__ */ S("span", {
143
144
  className: "text-sm text-text-secondary",
144
145
  children: "This program is archived."
145
146
  }),
146
- B.isPending && /* @__PURE__ */ E(y, { className: "h-4 w-4 animate-spin text-text-secondary" })
147
+ I.isPending && /* @__PURE__ */ S(b, { className: "h-4 w-4 animate-spin text-text-secondary" })
147
148
  ]
148
149
  })
149
150
  ]
150
151
  }),
151
- /* @__PURE__ */ D("section", { children: [/* @__PURE__ */ D("div", {
152
+ /* @__PURE__ */ C("section", { children: [/* @__PURE__ */ C("div", {
152
153
  className: "mb-4 flex items-center justify-between",
153
- children: [/* @__PURE__ */ D("h2", {
154
+ children: [/* @__PURE__ */ C("h2", {
154
155
  className: "text-lg font-semibold text-text-primary",
155
156
  children: [
156
157
  "Drills (",
157
158
  Q,
158
159
  ")"
159
160
  ]
160
- }), /* @__PURE__ */ D("button", {
161
+ }), /* @__PURE__ */ C("button", {
161
162
  type: "button",
162
163
  onClick: () => {
163
- Y(null), G(!0);
164
+ X(null), H(!0);
164
165
  },
165
166
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
166
- children: [/* @__PURE__ */ E(b, { className: "h-4 w-4" }), " Add drill"]
167
+ children: [/* @__PURE__ */ S(x, { className: "h-4 w-4" }), " Add drill"]
167
168
  })]
168
- }), Q === 0 ? /* @__PURE__ */ E(a, {
169
+ }), Q === 0 ? /* @__PURE__ */ S(a, {
169
170
  illustration: "empty-data",
170
- icon: /* @__PURE__ */ E(b, { className: "h-10 w-10" }),
171
+ icon: /* @__PURE__ */ S(x, { className: "h-10 w-10" }),
171
172
  title: "No drills yet",
172
173
  description: "Add drills with sets, reps and durations, organized by week."
173
- }) : /* @__PURE__ */ E("div", {
174
+ }) : /* @__PURE__ */ S("div", {
174
175
  className: "space-y-6",
175
- children: X.map(({ week: e, drills: t }) => /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ D("h3", {
176
+ children: le.map(({ week: e, drills: t }) => /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("h3", {
176
177
  className: "mb-3 text-sm font-semibold uppercase tracking-wide text-text-secondary",
177
178
  children: ["Week ", e]
178
- }), /* @__PURE__ */ E(O, { children: /* @__PURE__ */ E("div", {
179
+ }), /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S("div", {
179
180
  className: "divide-y divide-border-seam",
180
- children: t.map((e) => /* @__PURE__ */ D("div", {
181
+ children: t.map((e) => /* @__PURE__ */ C("div", {
181
182
  className: "flex items-start gap-4 py-3",
182
- children: [/* @__PURE__ */ D("div", {
183
+ children: [/* @__PURE__ */ C("div", {
183
184
  className: "min-w-0 flex-1",
184
185
  children: [
185
- /* @__PURE__ */ E("p", {
186
+ /* @__PURE__ */ S("p", {
186
187
  className: "text-sm font-medium text-text-primary",
187
188
  children: e.name
188
189
  }),
189
- e.description && /* @__PURE__ */ E("p", {
190
+ e.description && /* @__PURE__ */ S("p", {
190
191
  className: "mt-0.5 text-xs text-text-secondary",
191
192
  children: e.description
192
193
  }),
193
- /* @__PURE__ */ D("div", {
194
+ /* @__PURE__ */ C("div", {
194
195
  className: "mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-text-secondary",
195
196
  children: [
196
- e.sets !== null && /* @__PURE__ */ D("span", { children: [e.sets, " sets"] }),
197
- e.reps !== null && /* @__PURE__ */ D("span", { children: [e.reps, " reps"] }),
198
- e.durationSec !== null && /* @__PURE__ */ D("span", { children: [e.durationSec, "s work"] }),
199
- e.restSec !== null && /* @__PURE__ */ D("span", { children: [e.restSec, "s rest"] })
197
+ e.sets !== null && /* @__PURE__ */ C("span", { children: [e.sets, " sets"] }),
198
+ e.reps !== null && /* @__PURE__ */ C("span", { children: [e.reps, " reps"] }),
199
+ e.durationSec !== null && /* @__PURE__ */ C("span", { children: [e.durationSec, "s work"] }),
200
+ e.restSec !== null && /* @__PURE__ */ C("span", { children: [e.restSec, "s rest"] })
200
201
  ]
201
- })
202
+ }),
203
+ e.video?.id && /* @__PURE__ */ S("div", { children: /* @__PURE__ */ S(f, { fileId: e.video.id }) })
202
204
  ]
203
- }), /* @__PURE__ */ E("button", {
205
+ }), /* @__PURE__ */ S("button", {
204
206
  type: "button",
205
- onClick: () => void U.mutate(e.id),
206
- disabled: U.isPending,
207
+ onClick: () => void z.mutate(e.id),
208
+ disabled: z.isPending,
207
209
  "aria-label": `Remove ${e.name}`,
208
210
  className: "rounded-lg p-2 text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-60",
209
- children: /* @__PURE__ */ E(S, { className: "h-4 w-4" })
211
+ children: /* @__PURE__ */ S(ae, { className: "h-4 w-4" })
210
212
  })]
211
213
  }, e.id))
212
214
  }) })] }, e))
213
215
  })] }),
214
- /* @__PURE__ */ E(A, {
215
- open: W,
216
- onOpenChange: G,
217
- children: /* @__PURE__ */ D(j, { children: [/* @__PURE__ */ E(N, { children: /* @__PURE__ */ E(P, { children: "Add drill" }) }), /* @__PURE__ */ D("form", {
216
+ /* @__PURE__ */ S(E, {
217
+ open: V,
218
+ onOpenChange: H,
219
+ children: /* @__PURE__ */ C(D, { children: [/* @__PURE__ */ S(k, { children: /* @__PURE__ */ S(A, { children: "Add drill" }) }), /* @__PURE__ */ C("form", {
218
220
  onSubmit: async (e) => {
219
- if (e.preventDefault(), Y(null), !L) return;
220
- if (!K.name.trim()) {
221
- Y("Drill name is required.");
221
+ if (e.preventDefault(), X(null), !N) return;
222
+ if (!U.name.trim()) {
223
+ X("Drill name is required.");
222
224
  return;
223
225
  }
224
- let t = I(K.weekNumber) ?? 1;
226
+ let t = M(U.weekNumber) ?? 1, n;
227
+ if (G) {
228
+ J(!0);
229
+ try {
230
+ n = await B(G);
231
+ } catch (e) {
232
+ X(e instanceof Error ? e.message : "Failed to upload the demo video."), J(!1);
233
+ return;
234
+ }
235
+ J(!1);
236
+ }
225
237
  try {
226
- await H.mutateAsync({
227
- programId: L,
228
- name: K.name.trim(),
229
- description: K.description.trim() || void 0,
238
+ await R.mutateAsync({
239
+ programId: N,
240
+ name: U.name.trim(),
241
+ description: U.description.trim() || void 0,
230
242
  weekNumber: t,
231
- orderIndex: z.data?.drills?.filter((e) => e.weekNumber === t).length ?? 0,
232
- sets: I(K.sets),
233
- reps: I(K.reps),
234
- durationSec: I(K.durationSec),
235
- restSec: I(K.restSec)
236
- }), q(F), G(!1);
243
+ orderIndex: F.data?.drills?.filter((e) => e.weekNumber === t).length ?? 0,
244
+ sets: M(U.sets),
245
+ reps: M(U.reps),
246
+ durationSec: M(U.durationSec),
247
+ restSec: M(U.restSec),
248
+ videoFileId: n
249
+ }), W(j), K(null), H(!1);
237
250
  } catch (e) {
238
- Y(e instanceof Error ? e.message : "Failed to add drill.");
251
+ X(e instanceof Error ? e.message : "Failed to add drill.");
239
252
  }
240
253
  },
241
254
  className: "space-y-4 px-1",
242
255
  children: [
243
- /* @__PURE__ */ E(d, {
256
+ /* @__PURE__ */ S(u, {
244
257
  label: "Name",
245
258
  htmlFor: "drillName",
246
259
  required: !0,
247
- children: /* @__PURE__ */ E("input", {
260
+ children: /* @__PURE__ */ S("input", {
248
261
  id: "drillName",
249
- className: f,
250
- value: K.name,
251
- onChange: (e) => q((t) => ({
262
+ className: d,
263
+ value: U.name,
264
+ onChange: (e) => W((t) => ({
252
265
  ...t,
253
266
  name: e.target.value
254
267
  })),
255
268
  placeholder: "e.g. Box jumps"
256
269
  })
257
270
  }),
258
- /* @__PURE__ */ E(d, {
271
+ /* @__PURE__ */ S(u, {
259
272
  label: "Description",
260
273
  htmlFor: "drillDesc",
261
- children: /* @__PURE__ */ E("textarea", {
274
+ children: /* @__PURE__ */ S("textarea", {
262
275
  id: "drillDesc",
263
- className: f,
276
+ className: d,
264
277
  rows: 2,
265
- value: K.description,
266
- onChange: (e) => q((t) => ({
278
+ value: U.description,
279
+ onChange: (e) => W((t) => ({
267
280
  ...t,
268
281
  description: e.target.value
269
282
  })),
270
283
  placeholder: "How to perform the drill."
271
284
  })
272
285
  }),
273
- /* @__PURE__ */ D("div", {
274
- className: "grid grid-cols-3 gap-4",
286
+ /* @__PURE__ */ C("div", {
287
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
275
288
  children: [
276
- /* @__PURE__ */ E(d, {
289
+ /* @__PURE__ */ S(u, {
277
290
  label: "Week",
278
291
  htmlFor: "drillWeek",
279
- children: /* @__PURE__ */ E("input", {
292
+ children: /* @__PURE__ */ S("input", {
280
293
  id: "drillWeek",
281
294
  inputMode: "numeric",
282
- className: f,
283
- value: K.weekNumber,
284
- onChange: (e) => q((t) => ({
295
+ className: d,
296
+ value: U.weekNumber,
297
+ onChange: (e) => W((t) => ({
285
298
  ...t,
286
299
  weekNumber: e.target.value
287
300
  }))
288
301
  })
289
302
  }),
290
- /* @__PURE__ */ E(d, {
303
+ /* @__PURE__ */ S(u, {
291
304
  label: "Sets",
292
305
  htmlFor: "drillSets",
293
- children: /* @__PURE__ */ E("input", {
306
+ children: /* @__PURE__ */ S("input", {
294
307
  id: "drillSets",
295
308
  inputMode: "numeric",
296
- className: f,
297
- value: K.sets,
298
- onChange: (e) => q((t) => ({
309
+ className: d,
310
+ value: U.sets,
311
+ onChange: (e) => W((t) => ({
299
312
  ...t,
300
313
  sets: e.target.value
301
314
  }))
302
315
  })
303
316
  }),
304
- /* @__PURE__ */ E(d, {
317
+ /* @__PURE__ */ S(u, {
305
318
  label: "Reps",
306
319
  htmlFor: "drillReps",
307
- children: /* @__PURE__ */ E("input", {
320
+ children: /* @__PURE__ */ S("input", {
308
321
  id: "drillReps",
309
322
  inputMode: "numeric",
310
- className: f,
311
- value: K.reps,
312
- onChange: (e) => q((t) => ({
323
+ className: d,
324
+ value: U.reps,
325
+ onChange: (e) => W((t) => ({
313
326
  ...t,
314
327
  reps: e.target.value
315
328
  }))
@@ -317,50 +330,71 @@ function L() {
317
330
  })
318
331
  ]
319
332
  }),
320
- /* @__PURE__ */ D("div", {
333
+ /* @__PURE__ */ C("div", {
321
334
  className: "grid grid-cols-2 gap-4",
322
- children: [/* @__PURE__ */ E(d, {
335
+ children: [/* @__PURE__ */ S(u, {
323
336
  label: "Work (sec)",
324
337
  htmlFor: "drillDuration",
325
- children: /* @__PURE__ */ E("input", {
338
+ children: /* @__PURE__ */ S("input", {
326
339
  id: "drillDuration",
327
340
  inputMode: "numeric",
328
- className: f,
329
- value: K.durationSec,
330
- onChange: (e) => q((t) => ({
341
+ className: d,
342
+ value: U.durationSec,
343
+ onChange: (e) => W((t) => ({
331
344
  ...t,
332
345
  durationSec: e.target.value
333
346
  }))
334
347
  })
335
- }), /* @__PURE__ */ E(d, {
348
+ }), /* @__PURE__ */ S(u, {
336
349
  label: "Rest (sec)",
337
350
  htmlFor: "drillRest",
338
- children: /* @__PURE__ */ E("input", {
351
+ children: /* @__PURE__ */ S("input", {
339
352
  id: "drillRest",
340
353
  inputMode: "numeric",
341
- className: f,
342
- value: K.restSec,
343
- onChange: (e) => q((t) => ({
354
+ className: d,
355
+ value: U.restSec,
356
+ onChange: (e) => W((t) => ({
344
357
  ...t,
345
358
  restSec: e.target.value
346
359
  }))
347
360
  })
348
361
  })]
349
362
  }),
350
- J && /* @__PURE__ */ E("p", {
363
+ /* @__PURE__ */ C(u, {
364
+ label: "Demo video",
365
+ htmlFor: "drillVideo",
366
+ hint: "Optional — attach a short demo clip; it's stored in workspace files.",
367
+ children: [/* @__PURE__ */ S("input", {
368
+ id: "drillVideo",
369
+ type: "file",
370
+ accept: "video/*",
371
+ className: d,
372
+ onChange: (e) => K(e.target.files?.[0] ?? null)
373
+ }), G && /* @__PURE__ */ C("p", {
374
+ className: "mt-1 inline-flex items-center gap-1 text-xs text-text-secondary",
375
+ children: [
376
+ /* @__PURE__ */ S(y, { className: "h-3.5 w-3.5" }),
377
+ " ",
378
+ G.name
379
+ ]
380
+ })]
381
+ }),
382
+ Y && /* @__PURE__ */ S("p", {
351
383
  className: "text-sm text-status-error-text",
352
- children: J
384
+ children: Y
353
385
  }),
354
- /* @__PURE__ */ D(M, { children: [/* @__PURE__ */ E("button", {
386
+ /* @__PURE__ */ C(O, { children: [/* @__PURE__ */ S("button", {
355
387
  type: "button",
356
- onClick: () => G(!1),
388
+ onClick: () => {
389
+ H(!1), K(null);
390
+ },
357
391
  className: "rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
358
392
  children: "Cancel"
359
- }), /* @__PURE__ */ D("button", {
393
+ }), /* @__PURE__ */ C("button", {
360
394
  type: "submit",
361
- disabled: H.isPending,
395
+ disabled: R.isPending || q,
362
396
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
363
- children: [H.isPending && /* @__PURE__ */ E(y, { className: "h-4 w-4 animate-spin" }), "Add drill"]
397
+ children: [(R.isPending || q) && /* @__PURE__ */ S(b, { className: "h-4 w-4 animate-spin" }), q ? "Uploading video…" : "Add drill"]
364
398
  })] })
365
399
  ]
366
400
  })] })
@@ -369,6 +403,6 @@ function L() {
369
403
  });
370
404
  }
371
405
  //#endregion
372
- export { L as ProgramDetailPage };
406
+ export { N as ProgramDetailPage };
373
407
 
374
408
  //# sourceMappingURL=ProgramDetailPage.js.map