@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.
- package/dist/atheletebridge/AthleteBridgeRoot.js +45 -37
- package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
- package/dist/atheletebridge/AthleteBridgeRoutes.js +10 -6
- package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
- package/dist/atheletebridge/components/AthleteSelect.js +66 -0
- package/dist/atheletebridge/components/AthleteSelect.js.map +1 -0
- package/dist/atheletebridge/components/DrillVideoLink.js +28 -0
- package/dist/atheletebridge/components/DrillVideoLink.js.map +1 -0
- package/dist/atheletebridge/components/MetricsCard.js +20 -46
- package/dist/atheletebridge/components/MetricsCard.js.map +1 -1
- package/dist/atheletebridge/components/UserSearchSelect.js +129 -0
- package/dist/atheletebridge/components/UserSearchSelect.js.map +1 -0
- package/dist/atheletebridge/components/domain.js +10 -1
- package/dist/atheletebridge/components/domain.js.map +1 -1
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +313 -180
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.js +77 -69
- package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +4 -4
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.js +496 -0
- package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -0
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +176 -142
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +86 -85
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js +92 -98
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js +132 -137
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
- package/dist/atheletebridge/utils/formatters.js +2 -25
- package/dist/atheletebridge/utils/formatters.js.map +1 -1
- package/dist/atheletebridge/utils/url.js +17 -0
- package/dist/atheletebridge/utils/url.js.map +1 -0
- package/dist/index.js +4 -4
- package/package.json +5 -5
|
@@ -3,197 +3,191 @@ import { PageLayout as t } from "../../components/PageLayout.js";
|
|
|
3
3
|
import { SESSION_TYPE_LABELS as n } from "../../components/domain.js";
|
|
4
4
|
import { useBookSession as r, usePrograms as i } from "../../hooks/useAthleteBridgeApi.js";
|
|
5
5
|
import { FormField as a, fieldInputClass as o } from "../../components/form.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { AthleteSelect as s } from "../../components/AthleteSelect.js";
|
|
7
|
+
import { useState as c } from "react";
|
|
8
|
+
import { ArrowLeft as l, Loader2 as u, Save as d, User as f } from "lucide-react";
|
|
9
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
9
10
|
//#region src/atheletebridge/pages/sessions/SessionFormPage.tsx
|
|
10
|
-
var
|
|
11
|
-
function
|
|
12
|
-
let { navigateTo:
|
|
11
|
+
var h = Object.keys(n);
|
|
12
|
+
function g() {
|
|
13
|
+
let { navigateTo: g, currentUser: _ } = e(), v = i({
|
|
13
14
|
status: "PUBLISHED",
|
|
14
15
|
pagination: { limit: 100 }
|
|
15
|
-
}),
|
|
16
|
-
return /* @__PURE__ */
|
|
16
|
+
}), y = r(), b = _?.id ?? "", x = _?.name?.trim() || [_?.firstName, _?.lastName].filter(Boolean).join(" ").trim() || _?.email || "You", [S, C] = c(""), [w, T] = c(""), [E, D] = c(""), [O, k] = c("IN_PERSON"), [A, j] = c(""), [M, N] = c(""), [P, F] = c(""), [I, L] = c("60"), [R, z] = c(null), B = v.data?.items ?? [];
|
|
17
|
+
return /* @__PURE__ */ p(t, {
|
|
17
18
|
title: "Book session",
|
|
18
19
|
description: "Schedule a coaching session between a coach and an athlete.",
|
|
19
|
-
actions: /* @__PURE__ */
|
|
20
|
+
actions: /* @__PURE__ */ m("button", {
|
|
20
21
|
type: "button",
|
|
21
|
-
onClick: () =>
|
|
22
|
+
onClick: () => g("/sessions"),
|
|
22
23
|
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",
|
|
23
|
-
children: [/* @__PURE__ */
|
|
24
|
+
children: [/* @__PURE__ */ p(l, { className: "h-4 w-4" }), " Cancel"]
|
|
24
25
|
}),
|
|
25
|
-
children: /* @__PURE__ */
|
|
26
|
+
children: /* @__PURE__ */ m("form", {
|
|
26
27
|
onSubmit: async (e) => {
|
|
27
|
-
if (e.preventDefault(),
|
|
28
|
-
|
|
28
|
+
if (e.preventDefault(), z(null), !b.trim()) {
|
|
29
|
+
z("Unable to identify you as the coach — please refresh and try again.");
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
|
-
if (!
|
|
32
|
-
|
|
32
|
+
if (!S.trim()) {
|
|
33
|
+
z("Athlete ID is required.");
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
|
-
if (!
|
|
36
|
-
|
|
36
|
+
if (!E.trim()) {
|
|
37
|
+
z("A session title is required.");
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
|
-
if (!
|
|
40
|
-
|
|
40
|
+
if (!P) {
|
|
41
|
+
z("A scheduled date and time is required.");
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
|
-
let t = Number.parseInt(
|
|
44
|
+
let t = Number.parseInt(I, 10);
|
|
44
45
|
if (Number.isNaN(t) || t <= 0) {
|
|
45
|
-
|
|
46
|
+
z("Duration must be a positive number of minutes.");
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
try {
|
|
49
|
-
|
|
50
|
-
coachId:
|
|
51
|
-
athleteId:
|
|
52
|
-
programId:
|
|
53
|
-
title:
|
|
54
|
-
type:
|
|
55
|
-
location:
|
|
56
|
-
meetingUrl:
|
|
57
|
-
scheduledAt: new Date(
|
|
50
|
+
g(`/sessions/${(await y.mutateAsync({
|
|
51
|
+
coachId: b.trim(),
|
|
52
|
+
athleteId: S.trim(),
|
|
53
|
+
programId: w || void 0,
|
|
54
|
+
title: E.trim(),
|
|
55
|
+
type: O,
|
|
56
|
+
location: O === "ONLINE" ? void 0 : A.trim() || void 0,
|
|
57
|
+
meetingUrl: O === "IN_PERSON" ? void 0 : M.trim() || void 0,
|
|
58
|
+
scheduledAt: new Date(P).toISOString(),
|
|
58
59
|
durationMin: t
|
|
59
60
|
})).id}`);
|
|
60
61
|
} catch (e) {
|
|
61
|
-
|
|
62
|
+
z(e instanceof Error ? e.message : "Failed to book session.");
|
|
62
63
|
}
|
|
63
64
|
},
|
|
64
65
|
className: "max-w-2xl space-y-5",
|
|
65
66
|
children: [
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
className:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
})
|
|
80
|
-
}), /* @__PURE__ */ d(a, {
|
|
81
|
-
label: "Athlete ID",
|
|
82
|
-
htmlFor: "athleteId",
|
|
83
|
-
required: !0,
|
|
84
|
-
children: /* @__PURE__ */ d("input", {
|
|
85
|
-
id: "athleteId",
|
|
86
|
-
className: o,
|
|
87
|
-
value: b,
|
|
88
|
-
onChange: (e) => x(e.target.value),
|
|
89
|
-
placeholder: "athlete-uuid"
|
|
90
|
-
})
|
|
91
|
-
})]
|
|
67
|
+
/* @__PURE__ */ p(a, {
|
|
68
|
+
label: "Coach",
|
|
69
|
+
hint: "You're booking this session as the coach.",
|
|
70
|
+
children: /* @__PURE__ */ m("div", {
|
|
71
|
+
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 text-sm text-text-primary",
|
|
72
|
+
children: [/* @__PURE__ */ p("span", {
|
|
73
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-surface text-text-secondary",
|
|
74
|
+
children: /* @__PURE__ */ p(f, { className: "h-4 w-4" })
|
|
75
|
+
}), /* @__PURE__ */ p("span", {
|
|
76
|
+
className: "min-w-0 truncate font-medium",
|
|
77
|
+
children: x
|
|
78
|
+
})]
|
|
79
|
+
})
|
|
92
80
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ p(s, {
|
|
82
|
+
athleteId: S,
|
|
83
|
+
onSelect: ({ athleteId: e }) => C(e),
|
|
84
|
+
required: !0,
|
|
85
|
+
idPrefix: "session-athlete"
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ p(a, {
|
|
94
88
|
label: "Title",
|
|
95
89
|
htmlFor: "title",
|
|
96
90
|
required: !0,
|
|
97
|
-
children: /* @__PURE__ */
|
|
91
|
+
children: /* @__PURE__ */ p("input", {
|
|
98
92
|
id: "title",
|
|
99
93
|
className: o,
|
|
100
|
-
value:
|
|
101
|
-
onChange: (e) =>
|
|
94
|
+
value: E,
|
|
95
|
+
onChange: (e) => D(e.target.value),
|
|
102
96
|
placeholder: "e.g. Sprint technique review"
|
|
103
97
|
})
|
|
104
98
|
}),
|
|
105
|
-
/* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ m("div", {
|
|
106
100
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
107
|
-
children: [/* @__PURE__ */
|
|
101
|
+
children: [/* @__PURE__ */ p(a, {
|
|
108
102
|
label: "Type",
|
|
109
103
|
htmlFor: "type",
|
|
110
|
-
children: /* @__PURE__ */
|
|
104
|
+
children: /* @__PURE__ */ p("select", {
|
|
111
105
|
id: "type",
|
|
112
106
|
className: o,
|
|
113
|
-
value:
|
|
114
|
-
onChange: (e) =>
|
|
115
|
-
children:
|
|
107
|
+
value: O,
|
|
108
|
+
onChange: (e) => k(e.target.value),
|
|
109
|
+
children: h.map((e) => /* @__PURE__ */ p("option", {
|
|
116
110
|
value: e,
|
|
117
111
|
children: n[e]
|
|
118
112
|
}, e))
|
|
119
113
|
})
|
|
120
|
-
}), /* @__PURE__ */
|
|
114
|
+
}), /* @__PURE__ */ p(a, {
|
|
121
115
|
label: "Program",
|
|
122
116
|
htmlFor: "program",
|
|
123
117
|
hint: "Optional — tie to a published plan.",
|
|
124
|
-
children: /* @__PURE__ */
|
|
118
|
+
children: /* @__PURE__ */ m("select", {
|
|
125
119
|
id: "program",
|
|
126
120
|
className: o,
|
|
127
|
-
value:
|
|
128
|
-
onChange: (e) =>
|
|
129
|
-
children: [/* @__PURE__ */
|
|
121
|
+
value: w,
|
|
122
|
+
onChange: (e) => T(e.target.value),
|
|
123
|
+
children: [/* @__PURE__ */ p("option", {
|
|
130
124
|
value: "",
|
|
131
125
|
children: "No program"
|
|
132
|
-
}),
|
|
126
|
+
}), B.map((e) => /* @__PURE__ */ p("option", {
|
|
133
127
|
value: e.id,
|
|
134
128
|
children: e.name
|
|
135
129
|
}, e.id))]
|
|
136
130
|
})
|
|
137
131
|
})]
|
|
138
132
|
}),
|
|
139
|
-
|
|
133
|
+
O !== "ONLINE" && /* @__PURE__ */ p(a, {
|
|
140
134
|
label: "Location",
|
|
141
135
|
htmlFor: "location",
|
|
142
|
-
children: /* @__PURE__ */
|
|
136
|
+
children: /* @__PURE__ */ p("input", {
|
|
143
137
|
id: "location",
|
|
144
138
|
className: o,
|
|
145
|
-
value:
|
|
146
|
-
onChange: (e) =>
|
|
139
|
+
value: A,
|
|
140
|
+
onChange: (e) => j(e.target.value),
|
|
147
141
|
placeholder: "e.g. Main gym, Court 2"
|
|
148
142
|
})
|
|
149
143
|
}),
|
|
150
|
-
|
|
144
|
+
O !== "IN_PERSON" && /* @__PURE__ */ p(a, {
|
|
151
145
|
label: "Meeting URL",
|
|
152
146
|
htmlFor: "meetingUrl",
|
|
153
|
-
children: /* @__PURE__ */
|
|
147
|
+
children: /* @__PURE__ */ p("input", {
|
|
154
148
|
id: "meetingUrl",
|
|
155
149
|
className: o,
|
|
156
|
-
value:
|
|
157
|
-
onChange: (e) =>
|
|
150
|
+
value: M,
|
|
151
|
+
onChange: (e) => N(e.target.value),
|
|
158
152
|
placeholder: "https://meet.example.com/…"
|
|
159
153
|
})
|
|
160
154
|
}),
|
|
161
|
-
/* @__PURE__ */
|
|
155
|
+
/* @__PURE__ */ m("div", {
|
|
162
156
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
163
|
-
children: [/* @__PURE__ */
|
|
157
|
+
children: [/* @__PURE__ */ p(a, {
|
|
164
158
|
label: "Scheduled at",
|
|
165
159
|
htmlFor: "scheduledAt",
|
|
166
160
|
required: !0,
|
|
167
|
-
children: /* @__PURE__ */
|
|
161
|
+
children: /* @__PURE__ */ p("input", {
|
|
168
162
|
id: "scheduledAt",
|
|
169
163
|
type: "datetime-local",
|
|
170
164
|
className: o,
|
|
171
|
-
value:
|
|
172
|
-
onChange: (e) =>
|
|
165
|
+
value: P,
|
|
166
|
+
onChange: (e) => F(e.target.value)
|
|
173
167
|
})
|
|
174
|
-
}), /* @__PURE__ */
|
|
168
|
+
}), /* @__PURE__ */ p(a, {
|
|
175
169
|
label: "Duration (minutes)",
|
|
176
170
|
htmlFor: "duration",
|
|
177
|
-
children: /* @__PURE__ */
|
|
171
|
+
children: /* @__PURE__ */ p("input", {
|
|
178
172
|
id: "duration",
|
|
179
173
|
inputMode: "numeric",
|
|
180
174
|
className: o,
|
|
181
|
-
value:
|
|
182
|
-
onChange: (e) =>
|
|
175
|
+
value: I,
|
|
176
|
+
onChange: (e) => L(e.target.value)
|
|
183
177
|
})
|
|
184
178
|
})]
|
|
185
179
|
}),
|
|
186
|
-
|
|
180
|
+
R && /* @__PURE__ */ p("p", {
|
|
187
181
|
className: "text-sm text-status-error-text",
|
|
188
|
-
children:
|
|
182
|
+
children: R
|
|
189
183
|
}),
|
|
190
|
-
/* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ p("div", {
|
|
191
185
|
className: "pt-2",
|
|
192
|
-
children: /* @__PURE__ */
|
|
186
|
+
children: /* @__PURE__ */ m("button", {
|
|
193
187
|
type: "submit",
|
|
194
|
-
disabled:
|
|
188
|
+
disabled: y.isPending,
|
|
195
189
|
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",
|
|
196
|
-
children: [
|
|
190
|
+
children: [y.isPending ? /* @__PURE__ */ p(u, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ p(d, { className: "h-4 w-4" }), "Book session"]
|
|
197
191
|
})
|
|
198
192
|
})
|
|
199
193
|
]
|
|
@@ -201,6 +195,6 @@ function m() {
|
|
|
201
195
|
});
|
|
202
196
|
}
|
|
203
197
|
//#endregion
|
|
204
|
-
export {
|
|
198
|
+
export { g as SessionFormPage };
|
|
205
199
|
|
|
206
200
|
//# sourceMappingURL=SessionFormPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionFormPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/sessions/SessionFormPage.tsx"],"sourcesContent":["/**\n * Book Session Form\n * @module atheletebridge/pages/sessions\n */\n\nimport { useState } from 'react';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { SESSION_TYPE_LABELS } from '../../components/domain';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useBookSession, usePrograms } from '../../hooks/useAthleteBridgeApi';\nimport type { AthSessionType } from '../../types';\n\nconst TYPES = Object.keys(SESSION_TYPE_LABELS) as AthSessionType[];\n\nexport function SessionFormPage() {\n const { navigateTo, currentUser } = useAthleteBridge();\n\n const programs = usePrograms({ status: 'PUBLISHED', pagination: { limit: 100 } });\n const bookSession = useBookSession();\n\n const [coachId, setCoachId] = useState(currentUser?.id ?? '');\n const [athleteId, setAthleteId] = useState('');\n const [programId, setProgramId] = useState('');\n const [title, setTitle] = useState('');\n const [type, setType] = useState<AthSessionType>('IN_PERSON');\n const [location, setLocation] = useState('');\n const [meetingUrl, setMeetingUrl] = useState('');\n const [scheduledAt, setScheduledAt] = useState('');\n const [durationMin, setDurationMin] = useState('60');\n const [error, setError] = useState<string | null>(null);\n\n const programOptions = programs.data?.items ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!coachId.trim()) {\n setError('Coach ID is required.');\n return;\n }\n if (!athleteId.trim()) {\n setError('Athlete ID is required.');\n return;\n }\n if (!title.trim()) {\n setError('A session title is required.');\n return;\n }\n if (!scheduledAt) {\n setError('A scheduled date and time is required.');\n return;\n }\n const duration = Number.parseInt(durationMin, 10);\n if (Number.isNaN(duration) || duration <= 0) {\n setError('Duration must be a positive number of minutes.');\n return;\n }\n\n try {\n const created = await bookSession.mutateAsync({\n coachId: coachId.trim(),\n athleteId: athleteId.trim(),\n programId: programId || undefined,\n title: title.trim(),\n type,\n location: type !== 'ONLINE' ? location.trim() || undefined : undefined,\n meetingUrl: type !== 'IN_PERSON' ? meetingUrl.trim() || undefined : undefined,\n scheduledAt: new Date(scheduledAt).toISOString(),\n durationMin: duration,\n });\n navigateTo(`/sessions/${created.id}`);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to book session.');\n }\n };\n\n return (\n <PageLayout\n title=\"Book session\"\n description=\"Schedule a coaching session between a coach and an athlete.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions')}\n 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\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Coach ID\" htmlFor=\"coachId\" required hint=\"Defaults to you.\">\n <input\n id=\"coachId\"\n className={fieldInputClass}\n value={coachId}\n onChange={(e) => setCoachId(e.target.value)}\n placeholder=\"coach-uuid\"\n />\n </FormField>\n <FormField label=\"Athlete ID\" htmlFor=\"athleteId\" required>\n <input\n id=\"athleteId\"\n className={fieldInputClass}\n value={athleteId}\n onChange={(e) => setAthleteId(e.target.value)}\n placeholder=\"athlete-uuid\"\n />\n </FormField>\n </div>\n\n <FormField label=\"Title\" htmlFor=\"title\" required>\n <input\n id=\"title\"\n className={fieldInputClass}\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Sprint technique review\"\n />\n </FormField>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Type\" htmlFor=\"type\">\n <select\n id=\"type\"\n className={fieldInputClass}\n value={type}\n onChange={(e) => setType(e.target.value as AthSessionType)}\n >\n {TYPES.map((tp) => (\n <option key={tp} value={tp}>\n {SESSION_TYPE_LABELS[tp]}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField label=\"Program\" htmlFor=\"program\" hint=\"Optional — tie to a published plan.\">\n <select\n id=\"program\"\n className={fieldInputClass}\n value={programId}\n onChange={(e) => setProgramId(e.target.value)}\n >\n <option value=\"\">No program</option>\n {programOptions.map((pr) => (\n <option key={pr.id} value={pr.id}>\n {pr.name}\n </option>\n ))}\n </select>\n </FormField>\n </div>\n\n {type !== 'ONLINE' && (\n <FormField label=\"Location\" htmlFor=\"location\">\n <input\n id=\"location\"\n className={fieldInputClass}\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder=\"e.g. Main gym, Court 2\"\n />\n </FormField>\n )}\n\n {type !== 'IN_PERSON' && (\n <FormField label=\"Meeting URL\" htmlFor=\"meetingUrl\">\n <input\n id=\"meetingUrl\"\n className={fieldInputClass}\n value={meetingUrl}\n onChange={(e) => setMeetingUrl(e.target.value)}\n placeholder=\"https://meet.example.com/…\"\n />\n </FormField>\n )}\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Scheduled at\" htmlFor=\"scheduledAt\" required>\n <input\n id=\"scheduledAt\"\n type=\"datetime-local\"\n className={fieldInputClass}\n value={scheduledAt}\n onChange={(e) => setScheduledAt(e.target.value)}\n />\n </FormField>\n <FormField label=\"Duration (minutes)\" htmlFor=\"duration\">\n <input\n id=\"duration\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={durationMin}\n onChange={(e) => setDurationMin(e.target.value)}\n />\n </FormField>\n </div>\n\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n\n <div className=\"pt-2\">\n <button\n type=\"submit\"\n disabled={bookSession.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 {bookSession.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Book session\n </button>\n </div>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionFormPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/sessions/SessionFormPage.tsx"],"sourcesContent":["/**\n * Book Session Form\n * @module atheletebridge/pages/sessions\n */\n\nimport { useState } from 'react';\nimport { ArrowLeft, Loader2, Save, User as UserIcon } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { AthleteSelect } from '../../components/AthleteSelect';\nimport { SESSION_TYPE_LABELS } from '../../components/domain';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useBookSession, usePrograms } from '../../hooks/useAthleteBridgeApi';\nimport type { AthSessionType } from '../../types';\n\nconst TYPES = Object.keys(SESSION_TYPE_LABELS) as AthSessionType[];\n\nexport function SessionFormPage() {\n const { navigateTo, currentUser } = useAthleteBridge();\n\n const programs = usePrograms({ status: 'PUBLISHED', pagination: { limit: 100 } });\n const bookSession = useBookSession();\n\n // The coach is always the current user — sent under the hood, shown read-only.\n const coachId = currentUser?.id ?? '';\n const coachName =\n currentUser?.name?.trim() ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ').trim() ||\n currentUser?.email ||\n 'You';\n\n const [athleteId, setAthleteId] = useState('');\n const [programId, setProgramId] = useState('');\n const [title, setTitle] = useState('');\n const [type, setType] = useState<AthSessionType>('IN_PERSON');\n const [location, setLocation] = useState('');\n const [meetingUrl, setMeetingUrl] = useState('');\n const [scheduledAt, setScheduledAt] = useState('');\n const [durationMin, setDurationMin] = useState('60');\n const [error, setError] = useState<string | null>(null);\n\n const programOptions = programs.data?.items ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!coachId.trim()) {\n setError('Unable to identify you as the coach — please refresh and try again.');\n return;\n }\n if (!athleteId.trim()) {\n setError('Athlete ID is required.');\n return;\n }\n if (!title.trim()) {\n setError('A session title is required.');\n return;\n }\n if (!scheduledAt) {\n setError('A scheduled date and time is required.');\n return;\n }\n const duration = Number.parseInt(durationMin, 10);\n if (Number.isNaN(duration) || duration <= 0) {\n setError('Duration must be a positive number of minutes.');\n return;\n }\n\n try {\n const created = await bookSession.mutateAsync({\n coachId: coachId.trim(),\n athleteId: athleteId.trim(),\n programId: programId || undefined,\n title: title.trim(),\n type,\n location: type !== 'ONLINE' ? location.trim() || undefined : undefined,\n meetingUrl: type !== 'IN_PERSON' ? meetingUrl.trim() || undefined : undefined,\n scheduledAt: new Date(scheduledAt).toISOString(),\n durationMin: duration,\n });\n navigateTo(`/sessions/${created.id}`);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to book session.');\n }\n };\n\n return (\n <PageLayout\n title=\"Book session\"\n description=\"Schedule a coaching session between a coach and an athlete.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions')}\n 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\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-5\">\n <FormField label=\"Coach\" hint=\"You're booking this session as the coach.\">\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 text-sm text-text-primary\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-surface text-text-secondary\">\n <UserIcon className=\"h-4 w-4\" />\n </span>\n <span className=\"min-w-0 truncate font-medium\">{coachName}</span>\n </div>\n </FormField>\n\n <AthleteSelect\n athleteId={athleteId}\n onSelect={({ athleteId: id }) => setAthleteId(id)}\n required\n idPrefix=\"session-athlete\"\n />\n\n <FormField label=\"Title\" htmlFor=\"title\" required>\n <input\n id=\"title\"\n className={fieldInputClass}\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Sprint technique review\"\n />\n </FormField>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Type\" htmlFor=\"type\">\n <select\n id=\"type\"\n className={fieldInputClass}\n value={type}\n onChange={(e) => setType(e.target.value as AthSessionType)}\n >\n {TYPES.map((tp) => (\n <option key={tp} value={tp}>\n {SESSION_TYPE_LABELS[tp]}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField label=\"Program\" htmlFor=\"program\" hint=\"Optional — tie to a published plan.\">\n <select\n id=\"program\"\n className={fieldInputClass}\n value={programId}\n onChange={(e) => setProgramId(e.target.value)}\n >\n <option value=\"\">No program</option>\n {programOptions.map((pr) => (\n <option key={pr.id} value={pr.id}>\n {pr.name}\n </option>\n ))}\n </select>\n </FormField>\n </div>\n\n {type !== 'ONLINE' && (\n <FormField label=\"Location\" htmlFor=\"location\">\n <input\n id=\"location\"\n className={fieldInputClass}\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder=\"e.g. Main gym, Court 2\"\n />\n </FormField>\n )}\n\n {type !== 'IN_PERSON' && (\n <FormField label=\"Meeting URL\" htmlFor=\"meetingUrl\">\n <input\n id=\"meetingUrl\"\n className={fieldInputClass}\n value={meetingUrl}\n onChange={(e) => setMeetingUrl(e.target.value)}\n placeholder=\"https://meet.example.com/…\"\n />\n </FormField>\n )}\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Scheduled at\" htmlFor=\"scheduledAt\" required>\n <input\n id=\"scheduledAt\"\n type=\"datetime-local\"\n className={fieldInputClass}\n value={scheduledAt}\n onChange={(e) => setScheduledAt(e.target.value)}\n />\n </FormField>\n <FormField label=\"Duration (minutes)\" htmlFor=\"duration\">\n <input\n id=\"duration\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={durationMin}\n onChange={(e) => setDurationMin(e.target.value)}\n />\n </FormField>\n </div>\n\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n\n <div className=\"pt-2\">\n <button\n type=\"submit\"\n disabled={bookSession.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 {bookSession.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Book session\n </button>\n </div>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|