@burdenoff/microfe-atheletebridge 2026.625.2 → 2026.628.1
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/atheletebridge/AthleteBridgeRoot.js +49 -129
- package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
- package/dist/atheletebridge/AthleteBridgeRoutes.js +65 -184
- package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
- package/dist/atheletebridge/components/CursorPager.js +34 -0
- package/dist/atheletebridge/components/CursorPager.js.map +1 -0
- package/dist/atheletebridge/components/FilterSelect.js +22 -0
- package/dist/atheletebridge/components/FilterSelect.js.map +1 -0
- package/dist/atheletebridge/components/FixtureCard.js +76 -0
- package/dist/atheletebridge/components/FixtureCard.js.map +1 -0
- package/dist/atheletebridge/components/LoadingState.js +36 -0
- package/dist/atheletebridge/components/LoadingState.js.map +1 -0
- package/dist/atheletebridge/components/ProgramCard.js +69 -0
- package/dist/atheletebridge/components/ProgramCard.js.map +1 -0
- package/dist/atheletebridge/components/SessionCard.js +76 -0
- package/dist/atheletebridge/components/SessionCard.js.map +1 -0
- package/dist/atheletebridge/components/StatusBadge.js +44 -35
- package/dist/atheletebridge/components/StatusBadge.js.map +1 -1
- package/dist/atheletebridge/components/TeamCard.js +81 -0
- package/dist/atheletebridge/components/TeamCard.js.map +1 -0
- package/dist/atheletebridge/components/domain.js +47 -0
- package/dist/atheletebridge/components/domain.js.map +1 -0
- package/dist/atheletebridge/components/form.js +29 -0
- package/dist/atheletebridge/components/form.js.map +1 -0
- package/dist/atheletebridge/context/AthleteBridgeContext.js +6 -6
- package/dist/atheletebridge/context/AthleteBridgeContext.js.map +1 -1
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +342 -165
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
- package/dist/atheletebridge/index.js +1 -1
- package/dist/atheletebridge/pages/DashboardPage.js +169 -0
- package/dist/atheletebridge/pages/DashboardPage.js.map +1 -0
- package/dist/atheletebridge/pages/StandingsPage.js +166 -0
- package/dist/atheletebridge/pages/StandingsPage.js.map +1 -0
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +201 -0
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -0
- package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js +205 -0
- package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js.map +1 -0
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +92 -0
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -0
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +374 -0
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -0
- package/dist/atheletebridge/pages/programs/ProgramFormPage.js +207 -0
- package/dist/atheletebridge/pages/programs/ProgramFormPage.js.map +1 -0
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js +113 -0
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -0
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +224 -0
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -0
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js +206 -0
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -0
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js +92 -0
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -0
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js +350 -0
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -0
- package/dist/atheletebridge/pages/teams/TeamFormPage.js +222 -0
- package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -0
- package/dist/atheletebridge/pages/teams/TeamsListPage.js +101 -0
- package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -0
- package/dist/atheletebridge/store/atheletebridgeStore.js +2 -13
- package/dist/atheletebridge/store/atheletebridgeStore.js.map +1 -1
- package/dist/atheletebridge/utils/datetime.js +10 -0
- package/dist/atheletebridge/utils/datetime.js.map +1 -0
- package/dist/atheletebridge/utils/formatters.js +1 -4
- package/dist/atheletebridge/utils/formatters.js.map +1 -1
- package/package.json +1 -1
- package/dist/atheletebridge/components/InvestmentCard.js +0 -96
- package/dist/atheletebridge/components/InvestmentCard.js.map +0 -1
- package/dist/atheletebridge/components/RatingDisplay.js +0 -35
- package/dist/atheletebridge/components/RatingDisplay.js.map +0 -1
- package/dist/atheletebridge/mocks/data.js +0 -1517
- package/dist/atheletebridge/mocks/data.js.map +0 -1
- package/dist/atheletebridge/mocks/users.js +0 -34
- package/dist/atheletebridge/mocks/users.js.map +0 -1
- package/dist/atheletebridge/pages/HomePage.js +0 -936
- package/dist/atheletebridge/pages/HomePage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/AdminAnalyticsPage.js +0 -653
- package/dist/atheletebridge/pages/admin/AdminAnalyticsPage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/AdminDashboard.js +0 -671
- package/dist/atheletebridge/pages/admin/AdminDashboard.js.map +0 -1
- package/dist/atheletebridge/pages/admin/AdminFinancialPage.js +0 -429
- package/dist/atheletebridge/pages/admin/AdminFinancialPage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/AdminOrdersPage.js +0 -667
- package/dist/atheletebridge/pages/admin/AdminOrdersPage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/AdminProfilePage.js +0 -261
- package/dist/atheletebridge/pages/admin/AdminProfilePage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/AdminSettingsPage.js +0 -653
- package/dist/atheletebridge/pages/admin/AdminSettingsPage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/CartManagementPage.js +0 -876
- package/dist/atheletebridge/pages/admin/CartManagementPage.js.map +0 -1
- package/dist/atheletebridge/pages/admin/UserManagementPage.js +0 -900
- package/dist/atheletebridge/pages/admin/UserManagementPage.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/AnalyticsDashboard.js +0 -839
- package/dist/atheletebridge/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/BusinessSettingsPage.js +0 -704
- package/dist/atheletebridge/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/CartOwnerDashboard.js +0 -792
- package/dist/atheletebridge/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/CartOwnerProfilePage.js +0 -891
- package/dist/atheletebridge/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/CartServiceConfigPage.js +0 -633
- package/dist/atheletebridge/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/InventoryManagementPage.js +0 -913
- package/dist/atheletebridge/pages/cart-owner/InventoryManagementPage.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/OrderManagementPage.js +0 -881
- package/dist/atheletebridge/pages/cart-owner/OrderManagementPage.js.map +0 -1
- package/dist/atheletebridge/pages/cart-owner/StaffManagementPage.js +0 -841
- package/dist/atheletebridge/pages/cart-owner/StaffManagementPage.js.map +0 -1
- package/dist/atheletebridge/pages/common/HelpSupportPage.js +0 -1319
- package/dist/atheletebridge/pages/common/HelpSupportPage.js.map +0 -1
- package/dist/atheletebridge/pages/common/NotificationCenterPage.js +0 -674
- package/dist/atheletebridge/pages/common/NotificationCenterPage.js.map +0 -1
- package/dist/atheletebridge/pages/common/SettingsPage.js +0 -972
- package/dist/atheletebridge/pages/common/SettingsPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/BookingDetailsPage.js +0 -602
- package/dist/atheletebridge/pages/customer/BookingDetailsPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/CartDetailPage.js +0 -656
- package/dist/atheletebridge/pages/customer/CartDetailPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/CartDiscoveryPage.js +0 -558
- package/dist/atheletebridge/pages/customer/CartDiscoveryPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/CheckoutPage.js +0 -536
- package/dist/atheletebridge/pages/customer/CheckoutPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/CustomerDashboard.js +0 -539
- package/dist/atheletebridge/pages/customer/CustomerDashboard.js.map +0 -1
- package/dist/atheletebridge/pages/customer/CustomerProfilePage.js +0 -657
- package/dist/atheletebridge/pages/customer/CustomerProfilePage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/DirectServiceBookingPage.js +0 -635
- package/dist/atheletebridge/pages/customer/DirectServiceBookingPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/FavoritesPage.js +0 -486
- package/dist/atheletebridge/pages/customer/FavoritesPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/OrderHistoryPage.js +0 -447
- package/dist/atheletebridge/pages/customer/OrderHistoryPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/OrderTrackingPage.js +0 -393
- package/dist/atheletebridge/pages/customer/OrderTrackingPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/PaymentMethodsPage.js +0 -494
- package/dist/atheletebridge/pages/customer/PaymentMethodsPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/ReviewsPage.js +0 -615
- package/dist/atheletebridge/pages/customer/ReviewsPage.js.map +0 -1
- package/dist/atheletebridge/pages/customer/SavedAddressesPage.js +0 -451
- package/dist/atheletebridge/pages/customer/SavedAddressesPage.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestmentDetailsPage.js +0 -881
- package/dist/atheletebridge/pages/investor/InvestmentDetailsPage.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestmentOpportunitiesPage.js +0 -945
- package/dist/atheletebridge/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestorAnalyticsPage.js +0 -686
- package/dist/atheletebridge/pages/investor/InvestorAnalyticsPage.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestorDashboard.js +0 -453
- package/dist/atheletebridge/pages/investor/InvestorDashboard.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestorInvestmentsPage.js +0 -405
- package/dist/atheletebridge/pages/investor/InvestorInvestmentsPage.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestorPortfolioPage.js +0 -577
- package/dist/atheletebridge/pages/investor/InvestorPortfolioPage.js.map +0 -1
- package/dist/atheletebridge/pages/investor/InvestorProfilePage.js +0 -1069
- package/dist/atheletebridge/pages/investor/InvestorProfilePage.js.map +0 -1
- package/dist/atheletebridge/pages/worker/TaskDetailsPage.js +0 -530
- package/dist/atheletebridge/pages/worker/TaskDetailsPage.js.map +0 -1
- package/dist/atheletebridge/pages/worker/WorkerDashboard.js +0 -669
- package/dist/atheletebridge/pages/worker/WorkerDashboard.js.map +0 -1
- package/dist/atheletebridge/pages/worker/WorkerOrdersPage.js +0 -681
- package/dist/atheletebridge/pages/worker/WorkerOrdersPage.js.map +0 -1
- package/dist/atheletebridge/pages/worker/WorkerProfilePage.js +0 -691
- package/dist/atheletebridge/pages/worker/WorkerProfilePage.js.map +0 -1
- package/dist/atheletebridge/pages/worker/WorkerSchedulePage.js +0 -588
- package/dist/atheletebridge/pages/worker/WorkerSchedulePage.js.map +0 -1
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { useAthleteBridge as e } from "../../context/AthleteBridgeContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { StatusBadge as n } from "../../components/StatusBadge.js";
|
|
4
|
+
import { PROGRAM_LEVEL_LABELS as r, SPORT_LABELS as i } from "../../components/domain.js";
|
|
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";
|
|
9
|
+
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";
|
|
14
|
+
//#region src/atheletebridge/pages/programs/ProgramDetailPage.tsx
|
|
15
|
+
var F = {
|
|
16
|
+
name: "",
|
|
17
|
+
description: "",
|
|
18
|
+
weekNumber: "1",
|
|
19
|
+
sets: "",
|
|
20
|
+
reps: "",
|
|
21
|
+
durationSec: "",
|
|
22
|
+
restSec: ""
|
|
23
|
+
}, I = (e) => {
|
|
24
|
+
let t = e.trim();
|
|
25
|
+
if (!t) return;
|
|
26
|
+
let n = Number.parseInt(t, 10);
|
|
27
|
+
return Number.isNaN(n) ? void 0 : n;
|
|
28
|
+
};
|
|
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();
|
|
32
|
+
for (let n of e) {
|
|
33
|
+
let e = t.get(n.weekNumber) ?? [];
|
|
34
|
+
e.push(n), t.set(n.weekNumber, e);
|
|
35
|
+
}
|
|
36
|
+
return Array.from(t.entries()).sort(([e], [t]) => e - t).map(([e, t]) => ({
|
|
37
|
+
week: e,
|
|
38
|
+
drills: [...t].sort((e, t) => e.orderIndex - t.orderIndex)
|
|
39
|
+
}));
|
|
40
|
+
}, [z.data]);
|
|
41
|
+
if (z.isLoading) return /* @__PURE__ */ E(t, {
|
|
42
|
+
title: "Program",
|
|
43
|
+
children: /* @__PURE__ */ E(te, { label: "Loading program…" })
|
|
44
|
+
});
|
|
45
|
+
if (z.isError || !z.data) return /* @__PURE__ */ E(t, {
|
|
46
|
+
title: "Program",
|
|
47
|
+
children: /* @__PURE__ */ E(ee, {
|
|
48
|
+
title: "Program not found",
|
|
49
|
+
message: "This program does not exist or you do not have access to it.",
|
|
50
|
+
onRetry: () => R("/programs")
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
let Z = z.data, Q = Z.drills?.length ?? 0, $ = (e) => {
|
|
54
|
+
L && B.mutate({
|
|
55
|
+
id: L,
|
|
56
|
+
status: e
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
return /* @__PURE__ */ D(t, {
|
|
60
|
+
title: Z.name,
|
|
61
|
+
description: `${i[Z.sport]} · ${r[Z.level]} · ${Z.weeks} ${Z.weeks === 1 ? "week" : "weeks"}`,
|
|
62
|
+
actions: /* @__PURE__ */ D("div", {
|
|
63
|
+
className: "flex flex-wrap items-center gap-2",
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ D("button", {
|
|
66
|
+
type: "button",
|
|
67
|
+
onClick: () => R("/programs"),
|
|
68
|
+
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
|
+
}),
|
|
71
|
+
/* @__PURE__ */ D("button", {
|
|
72
|
+
type: "button",
|
|
73
|
+
onClick: () => R(`/programs/${Z.id}/edit`),
|
|
74
|
+
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
|
+
}),
|
|
77
|
+
Z.status !== "ARCHIVED" && /* @__PURE__ */ D("button", {
|
|
78
|
+
type: "button",
|
|
79
|
+
onClick: async () => {
|
|
80
|
+
L && (await V.mutateAsync(L), R("/programs"));
|
|
81
|
+
},
|
|
82
|
+
disabled: V.isPending,
|
|
83
|
+
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
|
+
children: [
|
|
85
|
+
V.isPending ? /* @__PURE__ */ E(y, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ E(h, { className: "h-4 w-4" }),
|
|
86
|
+
" ",
|
|
87
|
+
"Archive"
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
}),
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ D(k, {
|
|
94
|
+
treatment: "insight",
|
|
95
|
+
className: "mb-8 p-6",
|
|
96
|
+
children: [
|
|
97
|
+
/* @__PURE__ */ D("div", {
|
|
98
|
+
className: "flex flex-wrap items-center gap-3",
|
|
99
|
+
children: [/* @__PURE__ */ E(n, { status: Z.status }), Z.isTemplate && /* @__PURE__ */ D("span", {
|
|
100
|
+
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
|
+
})]
|
|
103
|
+
}),
|
|
104
|
+
Z.summary && /* @__PURE__ */ E("p", {
|
|
105
|
+
className: "mt-4 text-sm text-text-secondary",
|
|
106
|
+
children: Z.summary
|
|
107
|
+
}),
|
|
108
|
+
Z.tags.length > 0 && /* @__PURE__ */ E("div", {
|
|
109
|
+
className: "mt-4 flex flex-wrap gap-1.5",
|
|
110
|
+
children: Z.tags.map((e) => /* @__PURE__ */ E("span", {
|
|
111
|
+
className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
|
|
112
|
+
children: e
|
|
113
|
+
}, e))
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ D("div", {
|
|
116
|
+
className: "mt-5 flex flex-wrap items-center gap-2 border-t border-border-seam pt-5",
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ E("span", {
|
|
119
|
+
className: "text-sm font-medium text-text-primary",
|
|
120
|
+
children: "Publish lifecycle:"
|
|
121
|
+
}),
|
|
122
|
+
Z.status === "DRAFT" && /* @__PURE__ */ D("button", {
|
|
123
|
+
type: "button",
|
|
124
|
+
onClick: () => $("PUBLISHED"),
|
|
125
|
+
disabled: B.isPending,
|
|
126
|
+
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
|
+
}),
|
|
129
|
+
Z.status === "PUBLISHED" && /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ D("button", {
|
|
130
|
+
type: "button",
|
|
131
|
+
onClick: () => $("DRAFT"),
|
|
132
|
+
disabled: B.isPending,
|
|
133
|
+
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", {
|
|
136
|
+
type: "button",
|
|
137
|
+
onClick: () => $("ARCHIVED"),
|
|
138
|
+
disabled: B.isPending,
|
|
139
|
+
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
|
+
})] }),
|
|
142
|
+
Z.status === "ARCHIVED" && /* @__PURE__ */ E("span", {
|
|
143
|
+
className: "text-sm text-text-secondary",
|
|
144
|
+
children: "This program is archived."
|
|
145
|
+
}),
|
|
146
|
+
B.isPending && /* @__PURE__ */ E(y, { className: "h-4 w-4 animate-spin text-text-secondary" })
|
|
147
|
+
]
|
|
148
|
+
})
|
|
149
|
+
]
|
|
150
|
+
}),
|
|
151
|
+
/* @__PURE__ */ D("section", { children: [/* @__PURE__ */ D("div", {
|
|
152
|
+
className: "mb-4 flex items-center justify-between",
|
|
153
|
+
children: [/* @__PURE__ */ D("h2", {
|
|
154
|
+
className: "text-lg font-semibold text-text-primary",
|
|
155
|
+
children: [
|
|
156
|
+
"Drills (",
|
|
157
|
+
Q,
|
|
158
|
+
")"
|
|
159
|
+
]
|
|
160
|
+
}), /* @__PURE__ */ D("button", {
|
|
161
|
+
type: "button",
|
|
162
|
+
onClick: () => {
|
|
163
|
+
Y(null), G(!0);
|
|
164
|
+
},
|
|
165
|
+
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
|
+
})]
|
|
168
|
+
}), Q === 0 ? /* @__PURE__ */ E(a, {
|
|
169
|
+
illustration: "empty-data",
|
|
170
|
+
icon: /* @__PURE__ */ E(b, { className: "h-10 w-10" }),
|
|
171
|
+
title: "No drills yet",
|
|
172
|
+
description: "Add drills with sets, reps and durations, organized by week."
|
|
173
|
+
}) : /* @__PURE__ */ E("div", {
|
|
174
|
+
className: "space-y-6",
|
|
175
|
+
children: X.map(({ week: e, drills: t }) => /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ D("h3", {
|
|
176
|
+
className: "mb-3 text-sm font-semibold uppercase tracking-wide text-text-secondary",
|
|
177
|
+
children: ["Week ", e]
|
|
178
|
+
}), /* @__PURE__ */ E(O, { children: /* @__PURE__ */ E("div", {
|
|
179
|
+
className: "divide-y divide-border-seam",
|
|
180
|
+
children: t.map((e) => /* @__PURE__ */ D("div", {
|
|
181
|
+
className: "flex items-start gap-4 py-3",
|
|
182
|
+
children: [/* @__PURE__ */ D("div", {
|
|
183
|
+
className: "min-w-0 flex-1",
|
|
184
|
+
children: [
|
|
185
|
+
/* @__PURE__ */ E("p", {
|
|
186
|
+
className: "text-sm font-medium text-text-primary",
|
|
187
|
+
children: e.name
|
|
188
|
+
}),
|
|
189
|
+
e.description && /* @__PURE__ */ E("p", {
|
|
190
|
+
className: "mt-0.5 text-xs text-text-secondary",
|
|
191
|
+
children: e.description
|
|
192
|
+
}),
|
|
193
|
+
/* @__PURE__ */ D("div", {
|
|
194
|
+
className: "mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-text-secondary",
|
|
195
|
+
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"] })
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
]
|
|
203
|
+
}), /* @__PURE__ */ E("button", {
|
|
204
|
+
type: "button",
|
|
205
|
+
onClick: () => void U.mutate(e.id),
|
|
206
|
+
disabled: U.isPending,
|
|
207
|
+
"aria-label": `Remove ${e.name}`,
|
|
208
|
+
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" })
|
|
210
|
+
})]
|
|
211
|
+
}, e.id))
|
|
212
|
+
}) })] }, e))
|
|
213
|
+
})] }),
|
|
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", {
|
|
218
|
+
onSubmit: async (e) => {
|
|
219
|
+
if (e.preventDefault(), Y(null), !L) return;
|
|
220
|
+
if (!K.name.trim()) {
|
|
221
|
+
Y("Drill name is required.");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
let t = I(K.weekNumber) ?? 1;
|
|
225
|
+
try {
|
|
226
|
+
await H.mutateAsync({
|
|
227
|
+
programId: L,
|
|
228
|
+
name: K.name.trim(),
|
|
229
|
+
description: K.description.trim() || void 0,
|
|
230
|
+
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);
|
|
237
|
+
} catch (e) {
|
|
238
|
+
Y(e instanceof Error ? e.message : "Failed to add drill.");
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
className: "space-y-4 px-1",
|
|
242
|
+
children: [
|
|
243
|
+
/* @__PURE__ */ E(d, {
|
|
244
|
+
label: "Name",
|
|
245
|
+
htmlFor: "drillName",
|
|
246
|
+
required: !0,
|
|
247
|
+
children: /* @__PURE__ */ E("input", {
|
|
248
|
+
id: "drillName",
|
|
249
|
+
className: f,
|
|
250
|
+
value: K.name,
|
|
251
|
+
onChange: (e) => q((t) => ({
|
|
252
|
+
...t,
|
|
253
|
+
name: e.target.value
|
|
254
|
+
})),
|
|
255
|
+
placeholder: "e.g. Box jumps"
|
|
256
|
+
})
|
|
257
|
+
}),
|
|
258
|
+
/* @__PURE__ */ E(d, {
|
|
259
|
+
label: "Description",
|
|
260
|
+
htmlFor: "drillDesc",
|
|
261
|
+
children: /* @__PURE__ */ E("textarea", {
|
|
262
|
+
id: "drillDesc",
|
|
263
|
+
className: f,
|
|
264
|
+
rows: 2,
|
|
265
|
+
value: K.description,
|
|
266
|
+
onChange: (e) => q((t) => ({
|
|
267
|
+
...t,
|
|
268
|
+
description: e.target.value
|
|
269
|
+
})),
|
|
270
|
+
placeholder: "How to perform the drill."
|
|
271
|
+
})
|
|
272
|
+
}),
|
|
273
|
+
/* @__PURE__ */ D("div", {
|
|
274
|
+
className: "grid grid-cols-3 gap-4",
|
|
275
|
+
children: [
|
|
276
|
+
/* @__PURE__ */ E(d, {
|
|
277
|
+
label: "Week",
|
|
278
|
+
htmlFor: "drillWeek",
|
|
279
|
+
children: /* @__PURE__ */ E("input", {
|
|
280
|
+
id: "drillWeek",
|
|
281
|
+
inputMode: "numeric",
|
|
282
|
+
className: f,
|
|
283
|
+
value: K.weekNumber,
|
|
284
|
+
onChange: (e) => q((t) => ({
|
|
285
|
+
...t,
|
|
286
|
+
weekNumber: e.target.value
|
|
287
|
+
}))
|
|
288
|
+
})
|
|
289
|
+
}),
|
|
290
|
+
/* @__PURE__ */ E(d, {
|
|
291
|
+
label: "Sets",
|
|
292
|
+
htmlFor: "drillSets",
|
|
293
|
+
children: /* @__PURE__ */ E("input", {
|
|
294
|
+
id: "drillSets",
|
|
295
|
+
inputMode: "numeric",
|
|
296
|
+
className: f,
|
|
297
|
+
value: K.sets,
|
|
298
|
+
onChange: (e) => q((t) => ({
|
|
299
|
+
...t,
|
|
300
|
+
sets: e.target.value
|
|
301
|
+
}))
|
|
302
|
+
})
|
|
303
|
+
}),
|
|
304
|
+
/* @__PURE__ */ E(d, {
|
|
305
|
+
label: "Reps",
|
|
306
|
+
htmlFor: "drillReps",
|
|
307
|
+
children: /* @__PURE__ */ E("input", {
|
|
308
|
+
id: "drillReps",
|
|
309
|
+
inputMode: "numeric",
|
|
310
|
+
className: f,
|
|
311
|
+
value: K.reps,
|
|
312
|
+
onChange: (e) => q((t) => ({
|
|
313
|
+
...t,
|
|
314
|
+
reps: e.target.value
|
|
315
|
+
}))
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
]
|
|
319
|
+
}),
|
|
320
|
+
/* @__PURE__ */ D("div", {
|
|
321
|
+
className: "grid grid-cols-2 gap-4",
|
|
322
|
+
children: [/* @__PURE__ */ E(d, {
|
|
323
|
+
label: "Work (sec)",
|
|
324
|
+
htmlFor: "drillDuration",
|
|
325
|
+
children: /* @__PURE__ */ E("input", {
|
|
326
|
+
id: "drillDuration",
|
|
327
|
+
inputMode: "numeric",
|
|
328
|
+
className: f,
|
|
329
|
+
value: K.durationSec,
|
|
330
|
+
onChange: (e) => q((t) => ({
|
|
331
|
+
...t,
|
|
332
|
+
durationSec: e.target.value
|
|
333
|
+
}))
|
|
334
|
+
})
|
|
335
|
+
}), /* @__PURE__ */ E(d, {
|
|
336
|
+
label: "Rest (sec)",
|
|
337
|
+
htmlFor: "drillRest",
|
|
338
|
+
children: /* @__PURE__ */ E("input", {
|
|
339
|
+
id: "drillRest",
|
|
340
|
+
inputMode: "numeric",
|
|
341
|
+
className: f,
|
|
342
|
+
value: K.restSec,
|
|
343
|
+
onChange: (e) => q((t) => ({
|
|
344
|
+
...t,
|
|
345
|
+
restSec: e.target.value
|
|
346
|
+
}))
|
|
347
|
+
})
|
|
348
|
+
})]
|
|
349
|
+
}),
|
|
350
|
+
J && /* @__PURE__ */ E("p", {
|
|
351
|
+
className: "text-sm text-status-error-text",
|
|
352
|
+
children: J
|
|
353
|
+
}),
|
|
354
|
+
/* @__PURE__ */ D(M, { children: [/* @__PURE__ */ E("button", {
|
|
355
|
+
type: "button",
|
|
356
|
+
onClick: () => G(!1),
|
|
357
|
+
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
|
+
children: "Cancel"
|
|
359
|
+
}), /* @__PURE__ */ D("button", {
|
|
360
|
+
type: "submit",
|
|
361
|
+
disabled: H.isPending,
|
|
362
|
+
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"]
|
|
364
|
+
})] })
|
|
365
|
+
]
|
|
366
|
+
})] })
|
|
367
|
+
})
|
|
368
|
+
]
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
//#endregion
|
|
372
|
+
export { L as ProgramDetailPage };
|
|
373
|
+
|
|
374
|
+
//# sourceMappingURL=ProgramDetailPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProgramDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/programs/ProgramDetailPage.tsx"],"sourcesContent":["/**\n * Program Detail\n * @module atheletebridge/pages/programs\n *\n * Reads a single program with its drills (athProgram), supports the publish\n * lifecycle (athTransitionProgram), editing/archiving, and adding/removing\n * drills (athAddDrill / athRemoveDrill).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Edit,\n Archive,\n Plus,\n Trash2,\n Loader2,\n Send,\n Undo2,\n Copy,\n} from 'lucide-react';\nimport {\n GlassCard,\n EmphasisPanel,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { SPORT_LABELS, PROGRAM_LEVEL_LABELS } from '../../components/domain';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useProgram,\n useTransitionProgram,\n useArchiveProgram,\n useAddDrill,\n useRemoveDrill,\n} from '../../hooks/useAthleteBridgeApi';\nimport type { AthDrill, AthProgramStatus } from '../../types';\n\ninterface DrillForm {\n name: string;\n description: string;\n weekNumber: string;\n sets: string;\n reps: string;\n durationSec: string;\n restSec: string;\n}\n\nconst EMPTY_DRILL: DrillForm = {\n name: '',\n description: '',\n weekNumber: '1',\n sets: '',\n reps: '',\n durationSec: '',\n restSec: '',\n};\n\nconst numOrUndefined = (v: string): number | undefined => {\n const t = v.trim();\n if (!t) return undefined;\n const n = Number.parseInt(t, 10);\n return Number.isNaN(n) ? undefined : n;\n};\n\nexport function ProgramDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n\n const program = useProgram(id);\n const transition = useTransitionProgram();\n const archive = useArchiveProgram();\n const addDrill = useAddDrill();\n const removeDrill = useRemoveDrill(id);\n\n const [drillOpen, setDrillOpen] = useState(false);\n const [drillForm, setDrillForm] = useState<DrillForm>(EMPTY_DRILL);\n const [drillError, setDrillError] = useState<string | null>(null);\n\n const drillsByWeek = useMemo(() => {\n const drills = program.data?.drills ?? [];\n const groups = new Map<number, AthDrill[]>();\n for (const d of drills) {\n const list = groups.get(d.weekNumber) ?? [];\n list.push(d);\n groups.set(d.weekNumber, list);\n }\n return Array.from(groups.entries())\n .sort(([a], [b]) => a - b)\n .map(([week, list]) => ({\n week,\n drills: [...list].sort((a, b) => a.orderIndex - b.orderIndex),\n }));\n }, [program.data]);\n\n if (program.isLoading) {\n return (\n <PageLayout title=\"Program\">\n <LoadingState label=\"Loading program…\" />\n </PageLayout>\n );\n }\n\n if (program.isError || !program.data) {\n return (\n <PageLayout title=\"Program\">\n <ErrorState\n title=\"Program not found\"\n message=\"This program does not exist or you do not have access to it.\"\n onRetry={() => navigateTo('/programs')}\n />\n </PageLayout>\n );\n }\n\n const p = program.data;\n const drillCount = p.drills?.length ?? 0;\n\n const handleTransition = (status: AthProgramStatus) => {\n if (!id) return;\n void transition.mutate({ id, status });\n };\n\n const handleArchive = async () => {\n if (!id) return;\n await archive.mutateAsync(id);\n navigateTo('/programs');\n };\n\n const handleAddDrill = async (e: React.FormEvent) => {\n e.preventDefault();\n setDrillError(null);\n if (!id) return;\n if (!drillForm.name.trim()) {\n setDrillError('Drill name is required.');\n return;\n }\n const week = numOrUndefined(drillForm.weekNumber) ?? 1;\n try {\n await addDrill.mutateAsync({\n programId: id,\n name: drillForm.name.trim(),\n description: drillForm.description.trim() || undefined,\n weekNumber: week,\n orderIndex: (program.data?.drills?.filter((d) => d.weekNumber === week).length ?? 0),\n sets: numOrUndefined(drillForm.sets),\n reps: numOrUndefined(drillForm.reps),\n durationSec: numOrUndefined(drillForm.durationSec),\n restSec: numOrUndefined(drillForm.restSec),\n });\n setDrillForm(EMPTY_DRILL);\n setDrillOpen(false);\n } catch (err) {\n setDrillError(err instanceof Error ? err.message : 'Failed to add drill.');\n }\n };\n\n return (\n <PageLayout\n title={p.name}\n description={`${SPORT_LABELS[p.sport]} · ${PROGRAM_LEVEL_LABELS[p.level]} · ${p.weeks} ${\n p.weeks === 1 ? 'week' : 'weeks'\n }`}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/programs')}\n 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\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/programs/${p.id}/edit`)}\n 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\"\n >\n <Edit className=\"h-4 w-4\" /> Edit\n </button>\n {p.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archive.isPending}\n 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\"\n >\n {archive.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Archive className=\"h-4 w-4\" />\n )}{' '}\n Archive\n </button>\n )}\n </div>\n }\n >\n {/* Summary + lifecycle */}\n <GlassCard treatment=\"insight\" className=\"mb-8 p-6\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <StatusBadge status={p.status} />\n {p.isTemplate && (\n <span className=\"inline-flex items-center gap-1 text-sm text-text-secondary\">\n <Copy className=\"h-3.5 w-3.5\" /> Template\n </span>\n )}\n </div>\n {p.summary && <p className=\"mt-4 text-sm text-text-secondary\">{p.summary}</p>}\n {p.tags.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {p.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Lifecycle transitions */}\n <div className=\"mt-5 flex flex-wrap items-center gap-2 border-t border-border-seam pt-5\">\n <span className=\"text-sm font-medium text-text-primary\">Publish lifecycle:</span>\n {p.status === 'DRAFT' && (\n <button\n type=\"button\"\n onClick={() => handleTransition('PUBLISHED')}\n disabled={transition.isPending}\n 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\"\n >\n <Send className=\"h-4 w-4\" /> Publish\n </button>\n )}\n {p.status === 'PUBLISHED' && (\n <>\n <button\n type=\"button\"\n onClick={() => handleTransition('DRAFT')}\n disabled={transition.isPending}\n 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\"\n >\n <Undo2 className=\"h-4 w-4\" /> Pull back to draft\n </button>\n <button\n type=\"button\"\n onClick={() => handleTransition('ARCHIVED')}\n disabled={transition.isPending}\n 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\"\n >\n <Archive className=\"h-4 w-4\" /> Archive\n </button>\n </>\n )}\n {p.status === 'ARCHIVED' && (\n <span className=\"text-sm text-text-secondary\">This program is archived.</span>\n )}\n {transition.isPending && (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n )}\n </div>\n </GlassCard>\n\n {/* Drills */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Drills ({drillCount})</h2>\n <button\n type=\"button\"\n onClick={() => {\n setDrillError(null);\n setDrillOpen(true);\n }}\n 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\"\n >\n <Plus className=\"h-4 w-4\" /> Add drill\n </button>\n </div>\n\n {drillCount === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Plus className=\"h-10 w-10\" />}\n title=\"No drills yet\"\n description=\"Add drills with sets, reps and durations, organized by week.\"\n />\n ) : (\n <div className=\"space-y-6\">\n {drillsByWeek.map(({ week, drills }) => (\n <div key={week}>\n <h3 className=\"mb-3 text-sm font-semibold uppercase tracking-wide text-text-secondary\">\n Week {week}\n </h3>\n <EmphasisPanel>\n <div className=\"divide-y divide-border-seam\">\n {drills.map((d) => (\n <div key={d.id} className=\"flex items-start gap-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">{d.name}</p>\n {d.description && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{d.description}</p>\n )}\n <div className=\"mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-text-secondary\">\n {d.sets !== null && <span>{d.sets} sets</span>}\n {d.reps !== null && <span>{d.reps} reps</span>}\n {d.durationSec !== null && <span>{d.durationSec}s work</span>}\n {d.restSec !== null && <span>{d.restSec}s rest</span>}\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void removeDrill.mutate(d.id)}\n disabled={removeDrill.isPending}\n aria-label={`Remove ${d.name}`}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-60\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n </div>\n ))}\n </div>\n )}\n </section>\n\n {/* Add drill dialog */}\n <ResponsiveDialog open={drillOpen} onOpenChange={setDrillOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add drill</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <form onSubmit={handleAddDrill} className=\"space-y-4 px-1\">\n <FormField label=\"Name\" htmlFor=\"drillName\" required>\n <input\n id=\"drillName\"\n className={fieldInputClass}\n value={drillForm.name}\n onChange={(e) => setDrillForm((p) => ({ ...p, name: e.target.value }))}\n placeholder=\"e.g. Box jumps\"\n />\n </FormField>\n <FormField label=\"Description\" htmlFor=\"drillDesc\">\n <textarea\n id=\"drillDesc\"\n className={fieldInputClass}\n rows={2}\n value={drillForm.description}\n onChange={(e) =>\n setDrillForm((p) => ({ ...p, description: e.target.value }))\n }\n placeholder=\"How to perform the drill.\"\n />\n </FormField>\n <div className=\"grid grid-cols-3 gap-4\">\n <FormField label=\"Week\" htmlFor=\"drillWeek\">\n <input\n id=\"drillWeek\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={drillForm.weekNumber}\n onChange={(e) =>\n setDrillForm((p) => ({ ...p, weekNumber: e.target.value }))\n }\n />\n </FormField>\n <FormField label=\"Sets\" htmlFor=\"drillSets\">\n <input\n id=\"drillSets\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={drillForm.sets}\n onChange={(e) => setDrillForm((p) => ({ ...p, sets: e.target.value }))}\n />\n </FormField>\n <FormField label=\"Reps\" htmlFor=\"drillReps\">\n <input\n id=\"drillReps\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={drillForm.reps}\n onChange={(e) => setDrillForm((p) => ({ ...p, reps: e.target.value }))}\n />\n </FormField>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Work (sec)\" htmlFor=\"drillDuration\">\n <input\n id=\"drillDuration\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={drillForm.durationSec}\n onChange={(e) =>\n setDrillForm((p) => ({ ...p, durationSec: e.target.value }))\n }\n />\n </FormField>\n <FormField label=\"Rest (sec)\" htmlFor=\"drillRest\">\n <input\n id=\"drillRest\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={drillForm.restSec}\n onChange={(e) => setDrillForm((p) => ({ ...p, restSec: e.target.value }))}\n />\n </FormField>\n </div>\n {drillError && <p className=\"text-sm text-status-error-text\">{drillError}</p>}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setDrillOpen(false)}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={addDrill.isPending}\n 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\"\n >\n {addDrill.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n Add drill\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { useAthleteBridge as e } from "../../context/AthleteBridgeContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { PROGRAM_LEVEL_LABELS as n, SPORT_LABELS as r } from "../../components/domain.js";
|
|
4
|
+
import { ErrorState as i, LoadingState as a } from "../../components/LoadingState.js";
|
|
5
|
+
import { useCreateProgram as o, useProgram as s, useUpdateProgram as c } from "../../hooks/useAthleteBridgeApi.js";
|
|
6
|
+
import { FormField as l, fieldInputClass as u } from "../../components/form.js";
|
|
7
|
+
import { useEffect as d, useState as f } from "react";
|
|
8
|
+
import { ArrowLeft as p, Loader2 as m, Save as h } from "lucide-react";
|
|
9
|
+
import { useParams as g } from "react-router-dom";
|
|
10
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
11
|
+
//#region src/atheletebridge/pages/programs/ProgramFormPage.tsx
|
|
12
|
+
var y = Object.keys(r), b = Object.keys(n), x = {
|
|
13
|
+
name: "",
|
|
14
|
+
sport: "OTHER",
|
|
15
|
+
level: "BEGINNER",
|
|
16
|
+
summary: "",
|
|
17
|
+
weeks: "4",
|
|
18
|
+
isTemplate: !1,
|
|
19
|
+
tagsText: ""
|
|
20
|
+
};
|
|
21
|
+
function S() {
|
|
22
|
+
let { id: S } = g(), C = !!S, { navigateTo: w } = e(), T = s(S), E = o(), D = c(), [O, k] = f(x), [A, j] = f(null);
|
|
23
|
+
d(() => {
|
|
24
|
+
let e = T.data;
|
|
25
|
+
C && e && k({
|
|
26
|
+
name: e.name,
|
|
27
|
+
sport: e.sport,
|
|
28
|
+
level: e.level,
|
|
29
|
+
summary: e.summary ?? "",
|
|
30
|
+
weeks: String(e.weeks),
|
|
31
|
+
isTemplate: e.isTemplate,
|
|
32
|
+
tagsText: e.tags.join(", ")
|
|
33
|
+
});
|
|
34
|
+
}, [C, T.data]);
|
|
35
|
+
let M = (e, t) => k((n) => ({
|
|
36
|
+
...n,
|
|
37
|
+
[e]: t
|
|
38
|
+
})), N = async (e) => {
|
|
39
|
+
if (e.preventDefault(), j(null), !O.name.trim()) {
|
|
40
|
+
j("Program name is required.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let t = Number.parseInt(O.weeks, 10);
|
|
44
|
+
if (Number.isNaN(t) || t < 1) {
|
|
45
|
+
j("Weeks must be a positive number.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let n = O.tagsText.split(",").map((e) => e.trim()).filter(Boolean);
|
|
49
|
+
try {
|
|
50
|
+
C && S ? (await D.mutateAsync({
|
|
51
|
+
id: S,
|
|
52
|
+
input: {
|
|
53
|
+
name: O.name.trim(),
|
|
54
|
+
sport: O.sport,
|
|
55
|
+
level: O.level,
|
|
56
|
+
summary: O.summary.trim() || void 0,
|
|
57
|
+
weeks: t,
|
|
58
|
+
isTemplate: O.isTemplate,
|
|
59
|
+
tags: n
|
|
60
|
+
}
|
|
61
|
+
}), w(`/programs/${S}`)) : w(`/programs/${(await E.mutateAsync({
|
|
62
|
+
name: O.name.trim(),
|
|
63
|
+
sport: O.sport,
|
|
64
|
+
level: O.level,
|
|
65
|
+
summary: O.summary.trim() || void 0,
|
|
66
|
+
weeks: t,
|
|
67
|
+
isTemplate: O.isTemplate,
|
|
68
|
+
tags: n
|
|
69
|
+
})).id}`);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
j(e instanceof Error ? e.message : "Failed to save program.");
|
|
72
|
+
}
|
|
73
|
+
}, P = E.isPending || D.isPending;
|
|
74
|
+
return C && T.isLoading ? /* @__PURE__ */ _(t, {
|
|
75
|
+
title: "Edit program",
|
|
76
|
+
children: /* @__PURE__ */ _(a, { label: "Loading program…" })
|
|
77
|
+
}) : C && (T.isError || !T.isLoading && !T.data) ? /* @__PURE__ */ _(t, {
|
|
78
|
+
title: "Edit program",
|
|
79
|
+
children: /* @__PURE__ */ _(i, {
|
|
80
|
+
title: "Program not found",
|
|
81
|
+
message: "This program does not exist or you do not have access to it.",
|
|
82
|
+
onRetry: () => w("/programs")
|
|
83
|
+
})
|
|
84
|
+
}) : /* @__PURE__ */ _(t, {
|
|
85
|
+
title: C ? "Edit program" : "New program",
|
|
86
|
+
description: C ? "Update the training plan details." : "Create a new training plan in draft.",
|
|
87
|
+
actions: /* @__PURE__ */ v("button", {
|
|
88
|
+
type: "button",
|
|
89
|
+
onClick: () => w(C && S ? `/programs/${S}` : "/programs"),
|
|
90
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
91
|
+
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), " Cancel"]
|
|
92
|
+
}),
|
|
93
|
+
children: /* @__PURE__ */ v("form", {
|
|
94
|
+
onSubmit: N,
|
|
95
|
+
className: "max-w-2xl space-y-5",
|
|
96
|
+
children: [
|
|
97
|
+
/* @__PURE__ */ _(l, {
|
|
98
|
+
label: "Program name",
|
|
99
|
+
htmlFor: "name",
|
|
100
|
+
required: !0,
|
|
101
|
+
children: /* @__PURE__ */ _("input", {
|
|
102
|
+
id: "name",
|
|
103
|
+
className: u,
|
|
104
|
+
value: O.name,
|
|
105
|
+
onChange: (e) => M("name", e.target.value),
|
|
106
|
+
placeholder: "e.g. Pre-season strength block"
|
|
107
|
+
})
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ v("div", {
|
|
110
|
+
className: "grid grid-cols-1 gap-5 sm:grid-cols-3",
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ _(l, {
|
|
113
|
+
label: "Sport",
|
|
114
|
+
htmlFor: "sport",
|
|
115
|
+
children: /* @__PURE__ */ _("select", {
|
|
116
|
+
id: "sport",
|
|
117
|
+
className: u,
|
|
118
|
+
value: O.sport,
|
|
119
|
+
onChange: (e) => M("sport", e.target.value),
|
|
120
|
+
children: y.map((e) => /* @__PURE__ */ _("option", {
|
|
121
|
+
value: e,
|
|
122
|
+
children: r[e]
|
|
123
|
+
}, e))
|
|
124
|
+
})
|
|
125
|
+
}),
|
|
126
|
+
/* @__PURE__ */ _(l, {
|
|
127
|
+
label: "Level",
|
|
128
|
+
htmlFor: "level",
|
|
129
|
+
children: /* @__PURE__ */ _("select", {
|
|
130
|
+
id: "level",
|
|
131
|
+
className: u,
|
|
132
|
+
value: O.level,
|
|
133
|
+
onChange: (e) => M("level", e.target.value),
|
|
134
|
+
children: b.map((e) => /* @__PURE__ */ _("option", {
|
|
135
|
+
value: e,
|
|
136
|
+
children: n[e]
|
|
137
|
+
}, e))
|
|
138
|
+
})
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ _(l, {
|
|
141
|
+
label: "Weeks",
|
|
142
|
+
htmlFor: "weeks",
|
|
143
|
+
children: /* @__PURE__ */ _("input", {
|
|
144
|
+
id: "weeks",
|
|
145
|
+
inputMode: "numeric",
|
|
146
|
+
className: u,
|
|
147
|
+
value: O.weeks,
|
|
148
|
+
onChange: (e) => M("weeks", e.target.value),
|
|
149
|
+
placeholder: "4"
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
}),
|
|
154
|
+
/* @__PURE__ */ _(l, {
|
|
155
|
+
label: "Summary",
|
|
156
|
+
htmlFor: "summary",
|
|
157
|
+
children: /* @__PURE__ */ _("textarea", {
|
|
158
|
+
id: "summary",
|
|
159
|
+
className: u,
|
|
160
|
+
rows: 3,
|
|
161
|
+
value: O.summary,
|
|
162
|
+
onChange: (e) => M("summary", e.target.value),
|
|
163
|
+
placeholder: "What this program is for and who it's aimed at."
|
|
164
|
+
})
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ _(l, {
|
|
167
|
+
label: "Tags",
|
|
168
|
+
htmlFor: "tags",
|
|
169
|
+
hint: "Comma-separated, e.g. strength, pre-season.",
|
|
170
|
+
children: /* @__PURE__ */ _("input", {
|
|
171
|
+
id: "tags",
|
|
172
|
+
className: u,
|
|
173
|
+
value: O.tagsText,
|
|
174
|
+
onChange: (e) => M("tagsText", e.target.value),
|
|
175
|
+
placeholder: "strength, conditioning"
|
|
176
|
+
})
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ v("label", {
|
|
179
|
+
className: "flex items-center gap-2.5 text-sm text-text-primary",
|
|
180
|
+
children: [/* @__PURE__ */ _("input", {
|
|
181
|
+
type: "checkbox",
|
|
182
|
+
checked: O.isTemplate,
|
|
183
|
+
onChange: (e) => M("isTemplate", e.target.checked),
|
|
184
|
+
className: "h-4 w-4 rounded border-border-seam text-action-primary-bg focus:ring-[var(--color-focus-ring)]"
|
|
185
|
+
}), "Mark as reusable template"]
|
|
186
|
+
}),
|
|
187
|
+
A && /* @__PURE__ */ _("p", {
|
|
188
|
+
className: "text-sm text-status-error-text",
|
|
189
|
+
children: A
|
|
190
|
+
}),
|
|
191
|
+
/* @__PURE__ */ _("div", {
|
|
192
|
+
className: "pt-2",
|
|
193
|
+
children: /* @__PURE__ */ v("button", {
|
|
194
|
+
type: "submit",
|
|
195
|
+
disabled: P,
|
|
196
|
+
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",
|
|
197
|
+
children: [P ? /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(h, { className: "h-4 w-4" }), C ? "Save changes" : "Create program"]
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { S as ProgramFormPage };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=ProgramFormPage.js.map
|