@burdenoff/microfe-atheletebridge 2026.705.2 → 2026.706.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.
Files changed (101) hide show
  1. package/dist/atheletebridge/AthleteBridgeRoot.js +55 -39
  2. package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
  3. package/dist/atheletebridge/AthleteBridgeRoutes.js +18 -6
  4. package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
  5. package/dist/atheletebridge/components/Crest.js +70 -0
  6. package/dist/atheletebridge/components/Crest.js.map +1 -0
  7. package/dist/atheletebridge/components/FixtureCard.js +8 -72
  8. package/dist/atheletebridge/components/FixtureCard.js.map +1 -1
  9. package/dist/atheletebridge/components/MatchupRow.js +74 -0
  10. package/dist/atheletebridge/components/MatchupRow.js.map +1 -0
  11. package/dist/atheletebridge/components/MetricsCard.js +61 -21
  12. package/dist/atheletebridge/components/MetricsCard.js.map +1 -1
  13. package/dist/atheletebridge/components/ProgramCard.js +75 -43
  14. package/dist/atheletebridge/components/ProgramCard.js.map +1 -1
  15. package/dist/atheletebridge/components/Scoreboard.js +163 -0
  16. package/dist/atheletebridge/components/Scoreboard.js.map +1 -0
  17. package/dist/atheletebridge/components/SessionCard.js +64 -41
  18. package/dist/atheletebridge/components/SessionCard.js.map +1 -1
  19. package/dist/atheletebridge/components/SessionStatusTimeline.js +80 -0
  20. package/dist/atheletebridge/components/SessionStatusTimeline.js.map +1 -0
  21. package/dist/atheletebridge/components/StatusBadge.js +5 -5
  22. package/dist/atheletebridge/components/StatusBadge.js.map +1 -1
  23. package/dist/atheletebridge/components/TeamCard.js +65 -35
  24. package/dist/atheletebridge/components/TeamCard.js.map +1 -1
  25. package/dist/atheletebridge/components/UserAvatar.js +33 -0
  26. package/dist/atheletebridge/components/UserAvatar.js.map +1 -0
  27. package/dist/atheletebridge/components/domain.js +5 -2
  28. package/dist/atheletebridge/components/domain.js.map +1 -1
  29. package/dist/atheletebridge/components/viz/AvatarStack.js +2 -0
  30. package/dist/atheletebridge/components/viz/BarChart.js +83 -0
  31. package/dist/atheletebridge/components/viz/BarChart.js.map +1 -0
  32. package/dist/atheletebridge/components/viz/DonutChart.js +109 -0
  33. package/dist/atheletebridge/components/viz/DonutChart.js.map +1 -0
  34. package/dist/atheletebridge/components/viz/LineChart.js +123 -0
  35. package/dist/atheletebridge/components/viz/LineChart.js.map +1 -0
  36. package/dist/atheletebridge/components/viz/ProgressRing.js +6 -0
  37. package/dist/atheletebridge/components/viz/ProgressRing.js.map +1 -0
  38. package/dist/atheletebridge/components/viz/RatingStars.js +92 -0
  39. package/dist/atheletebridge/components/viz/RatingStars.js.map +1 -0
  40. package/dist/atheletebridge/components/viz/Sparkline.js +74 -0
  41. package/dist/atheletebridge/components/viz/Sparkline.js.map +1 -0
  42. package/dist/atheletebridge/components/viz/StatCard.js +60 -0
  43. package/dist/atheletebridge/components/viz/StatCard.js.map +1 -0
  44. package/dist/atheletebridge/components/viz/StepProgress.js +3 -0
  45. package/dist/atheletebridge/components/viz/Timeline.js +66 -0
  46. package/dist/atheletebridge/components/viz/Timeline.js.map +1 -0
  47. package/dist/atheletebridge/components/viz/index.js +13 -0
  48. package/dist/atheletebridge/components/viz/lib/Avatar.js +1 -0
  49. package/dist/atheletebridge/components/viz/lib/CartesianFrame.js +119 -0
  50. package/dist/atheletebridge/components/viz/lib/CartesianFrame.js.map +1 -0
  51. package/dist/atheletebridge/components/viz/lib/ResponsiveSvg.js +35 -0
  52. package/dist/atheletebridge/components/viz/lib/ResponsiveSvg.js.map +1 -0
  53. package/dist/atheletebridge/components/viz/lib/format.js +17 -0
  54. package/dist/atheletebridge/components/viz/lib/format.js.map +1 -0
  55. package/dist/atheletebridge/components/viz/lib/geometry.js +61 -0
  56. package/dist/atheletebridge/components/viz/lib/geometry.js.map +1 -0
  57. package/dist/atheletebridge/components/viz/lib/hooks.js +39 -0
  58. package/dist/atheletebridge/components/viz/lib/hooks.js.map +1 -0
  59. package/dist/atheletebridge/components/viz/lib/palette.js +27 -0
  60. package/dist/atheletebridge/components/viz/lib/palette.js.map +1 -0
  61. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +369 -163
  62. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
  63. package/dist/atheletebridge/pages/DashboardPage.js +182 -117
  64. package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
  65. package/dist/atheletebridge/pages/StandingsPage.js +119 -95
  66. package/dist/atheletebridge/pages/StandingsPage.js.map +1 -1
  67. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js +372 -0
  68. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js.map +1 -0
  69. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js +511 -0
  70. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js.map +1 -0
  71. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +120 -131
  72. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
  73. package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js +264 -176
  74. package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js.map +1 -1
  75. package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +84 -47
  76. package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -1
  77. package/dist/atheletebridge/pages/performance/PerformancePage.js +432 -281
  78. package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -1
  79. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +187 -123
  80. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
  81. package/dist/atheletebridge/pages/programs/ProgramsListPage.js +56 -39
  82. package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -1
  83. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +267 -139
  84. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
  85. package/dist/atheletebridge/pages/sessions/SessionFormPage.js +253 -162
  86. package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -1
  87. package/dist/atheletebridge/pages/sessions/SessionsListPage.js +38 -36
  88. package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -1
  89. package/dist/atheletebridge/pages/teams/TeamDetailPage.js +437 -189
  90. package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
  91. package/dist/atheletebridge/pages/teams/TeamFormPage.js +368 -177
  92. package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -1
  93. package/dist/atheletebridge/pages/teams/TeamsListPage.js +3 -0
  94. package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -1
  95. package/dist/atheletebridge/pages/tournaments/TournamentBracketPage.js +298 -0
  96. package/dist/atheletebridge/pages/tournaments/TournamentBracketPage.js.map +1 -0
  97. package/dist/atheletebridge/utils/identity.js +31 -0
  98. package/dist/atheletebridge/utils/identity.js.map +1 -0
  99. package/dist/atheletebridge/utils/timeAgo.js +20 -0
  100. package/dist/atheletebridge/utils/timeAgo.js.map +1 -0
  101. package/package.json +1 -1
@@ -1,173 +1,303 @@
1
1
  import { useAthleteBridge as e } from "../../context/AthleteBridgeContext.js";
2
2
  import { PageLayout as t } from "../../components/PageLayout.js";
3
- import { StatusBadge as n } from "../../components/StatusBadge.js";
4
- import { SESSION_TYPE_LABELS as r } from "../../components/domain.js";
5
- import { formatDate as i, formatDuration as a } from "../../utils/formatters.js";
6
- import { ErrorState as o, LoadingState as s } from "../../components/LoadingState.js";
7
- import { useCancelSession as c, useCompleteSession as l, useConfirmSession as u, useSession as d } from "../../hooks/useAthleteBridgeApi.js";
3
+ import { RatingStars as n } from "../../components/viz/RatingStars.js";
4
+ import "../../components/viz/index.js";
5
+ import { StatusBadge as r } from "../../components/StatusBadge.js";
6
+ import { SESSION_TYPE_LABELS as i } from "../../components/domain.js";
7
+ import { formatDate as a, formatDuration as ee } from "../../utils/formatters.js";
8
+ import { UserAvatar as o } from "../../components/UserAvatar.js";
9
+ import { ErrorState as s, LoadingState as te } from "../../components/LoadingState.js";
10
+ import { useCancelSession as ne, useCompleteSession as c, useConfirmSession as l, useSession as u, useUserDirectory as d } from "../../hooks/useAthleteBridgeApi.js";
8
11
  import { FormField as f, fieldInputClass as p } from "../../components/form.js";
9
- import { safeExternalHref as m } from "../../utils/url.js";
10
- import { useState as h } from "react";
11
- import { ArrowLeft as g, CalendarClock as _, CheckCircle2 as v, Clock as y, Loader2 as b, MapPin as x, Star as S, Video as C, XCircle as w } from "lucide-react";
12
- import { useParams as T } from "react-router-dom";
12
+ import { SessionStatusTimeline as m } from "../../components/SessionStatusTimeline.js";
13
+ import { safeExternalHref as h } from "../../utils/url.js";
14
+ import { useMemo as g, useState as _ } from "react";
15
+ import { ArrowLeft as re, CalendarClock as ie, CheckCircle2 as v, Clock as y, ExternalLink as b, Loader2 as x, Mail as S, MapPin as C, Video as w, XCircle as T } from "lucide-react";
16
+ import { useParams as ae } from "react-router-dom";
13
17
  import { jsx as E, jsxs as D } from "react/jsx-runtime";
14
18
  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";
15
19
  //#region src/atheletebridge/pages/sessions/SessionDetailPage.tsx
16
- function F() {
17
- let { id: F } = T(), { navigateTo: I } = e(), L = d(F), R = u(), z = l(), B = c(), [V, H] = h(!1), [U, W] = h(""), [G, K] = h(""), [q, J] = h(null);
18
- if (L.isLoading) return /* @__PURE__ */ E(t, {
20
+ function F({ role: e, name: t, email: n, id: r }) {
21
+ let i = t?.trim() || `User ${r.slice(0, 6)}`;
22
+ return /* @__PURE__ */ D(k, {
23
+ treatment: "metric",
24
+ className: "p-4",
25
+ children: [
26
+ /* @__PURE__ */ E("p", {
27
+ className: "text-xs font-medium uppercase tracking-wide text-text-muted",
28
+ children: e
29
+ }),
30
+ /* @__PURE__ */ D("div", {
31
+ className: "mt-3 flex items-center gap-3",
32
+ children: [/* @__PURE__ */ E(o, {
33
+ name: t,
34
+ id: r,
35
+ size: "lg"
36
+ }), /* @__PURE__ */ D("div", {
37
+ className: "min-w-0",
38
+ children: [/* @__PURE__ */ E("p", {
39
+ className: "truncate text-sm font-semibold text-text-primary",
40
+ children: i
41
+ }), /* @__PURE__ */ E("p", {
42
+ className: "truncate text-xs text-text-secondary",
43
+ children: e
44
+ })]
45
+ })]
46
+ }),
47
+ n ? /* @__PURE__ */ D("a", {
48
+ href: `mailto:${n}`,
49
+ className: "mt-3 flex items-center gap-1.5 text-xs text-text-secondary hover:text-text-primary hover:underline",
50
+ children: [/* @__PURE__ */ E(S, {
51
+ className: "h-3.5 w-3.5 shrink-0",
52
+ "aria-hidden": "true"
53
+ }), /* @__PURE__ */ E("span", {
54
+ className: "truncate",
55
+ children: n
56
+ })]
57
+ }) : null
58
+ ]
59
+ });
60
+ }
61
+ function I() {
62
+ let { id: o } = ae(), { navigateTo: S } = e(), I = u(o), L = l(), R = c(), z = ne(), [B, V] = _(!1), [H, U] = _(""), [W, G] = _(0), [K, q] = _(null), J = d(g(() => I.data ? [I.data.coachId, I.data.athleteId] : [], [I.data]));
63
+ if (I.isLoading) return /* @__PURE__ */ E(t, {
19
64
  title: "Session",
20
- children: /* @__PURE__ */ E(s, { label: "Loading session…" })
65
+ children: /* @__PURE__ */ E(te, { label: "Loading session…" })
21
66
  });
22
- if (L.isError || !L.data) return /* @__PURE__ */ E(t, {
67
+ if (I.isError || !I.data) return /* @__PURE__ */ E(t, {
23
68
  title: "Session",
24
- children: /* @__PURE__ */ E(o, {
69
+ children: /* @__PURE__ */ E(s, {
25
70
  title: "Session not found",
26
71
  message: "This session does not exist or you do not have access to it.",
27
- onRetry: () => I("/sessions")
72
+ onRetry: () => S("/sessions")
28
73
  })
29
74
  });
30
- let Y = L.data, X = Y.status === "REQUESTED", Z = Y.status === "CONFIRMED", Q = Y.status === "REQUESTED" || Y.status === "CONFIRMED", $ = m(Y.meetingUrl);
75
+ let Y = I.data, X = Y.status === "REQUESTED", Z = Y.status === "CONFIRMED", Q = Y.status === "REQUESTED" || Y.status === "CONFIRMED", $ = h(Y.meetingUrl);
31
76
  return /* @__PURE__ */ D(t, {
32
77
  title: Y.title,
33
- description: r[Y.type],
78
+ description: i[Y.type],
34
79
  actions: /* @__PURE__ */ D("button", {
35
80
  type: "button",
36
- onClick: () => I("/sessions"),
81
+ onClick: () => S("/sessions"),
37
82
  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",
38
- children: [/* @__PURE__ */ E(g, { className: "h-4 w-4" }), " Back"]
83
+ children: [/* @__PURE__ */ E(re, { className: "h-4 w-4" }), " Back"]
39
84
  }),
40
85
  children: [
41
86
  /* @__PURE__ */ D(k, {
42
87
  treatment: "insight",
43
- className: "mb-8 p-6",
88
+ className: "mb-6 p-5 sm:p-6",
89
+ children: [/* @__PURE__ */ D("div", {
90
+ className: "mb-5 flex items-center justify-between gap-3",
91
+ children: [/* @__PURE__ */ E("span", {
92
+ className: "text-xs font-medium uppercase tracking-wide text-text-muted",
93
+ children: "Lifecycle"
94
+ }), /* @__PURE__ */ E(r, { status: Y.status })]
95
+ }), /* @__PURE__ */ E(m, {
96
+ status: Y.status,
97
+ createdAt: Y.createdAt,
98
+ completedAt: Y.completedAt
99
+ })]
100
+ }),
101
+ /* @__PURE__ */ D("div", {
102
+ className: "mb-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
44
103
  children: [
45
- /* @__PURE__ */ E("div", {
46
- className: "flex flex-wrap items-center gap-3",
47
- children: /* @__PURE__ */ E(n, { status: Y.status })
104
+ /* @__PURE__ */ E(F, {
105
+ role: "Coach",
106
+ id: Y.coachId,
107
+ name: J.nameOf(Y.coachId),
108
+ email: J.emailOf(Y.coachId)
109
+ }),
110
+ /* @__PURE__ */ E(F, {
111
+ role: "Athlete",
112
+ id: Y.athleteId,
113
+ name: J.nameOf(Y.athleteId),
114
+ email: J.emailOf(Y.athleteId)
48
115
  }),
49
- /* @__PURE__ */ D("div", {
50
- className: "mt-4 grid grid-cols-1 gap-3 text-sm text-text-secondary sm:grid-cols-2",
116
+ /* @__PURE__ */ D(k, {
117
+ treatment: "metric",
118
+ className: "p-4",
119
+ children: [/* @__PURE__ */ E("p", {
120
+ className: "text-xs font-medium uppercase tracking-wide text-text-muted",
121
+ children: "Schedule"
122
+ }), /* @__PURE__ */ D("div", {
123
+ className: "mt-3 space-y-2.5 text-sm text-text-primary",
124
+ children: [
125
+ /* @__PURE__ */ D("span", {
126
+ className: "flex items-center gap-2",
127
+ children: [/* @__PURE__ */ E(ie, {
128
+ className: "h-4 w-4 shrink-0 text-text-secondary",
129
+ "aria-hidden": "true"
130
+ }), a(Y.scheduledAt)]
131
+ }),
132
+ /* @__PURE__ */ D("span", {
133
+ className: "flex items-center gap-2 text-text-secondary",
134
+ children: [/* @__PURE__ */ E(y, {
135
+ className: "h-4 w-4 shrink-0",
136
+ "aria-hidden": "true"
137
+ }), ee(Y.durationMin)]
138
+ }),
139
+ Y.type === "IN_PERSON" && Y.location ? /* @__PURE__ */ D("span", {
140
+ className: "flex items-center gap-2 text-text-secondary",
141
+ children: [
142
+ /* @__PURE__ */ E(C, {
143
+ className: "h-4 w-4 shrink-0",
144
+ "aria-hidden": "true"
145
+ }),
146
+ " ",
147
+ Y.location
148
+ ]
149
+ }) : null
150
+ ]
151
+ })]
152
+ })
153
+ ]
154
+ }),
155
+ /* @__PURE__ */ D("div", {
156
+ className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
157
+ children: [/* @__PURE__ */ E("div", {
158
+ className: "space-y-6",
159
+ children: /* @__PURE__ */ D(k, {
160
+ treatment: "glass",
161
+ className: "p-5 sm:p-6",
51
162
  children: [
52
- /* @__PURE__ */ D("span", {
53
- className: "flex items-center gap-2",
54
- children: [
55
- /* @__PURE__ */ E(_, { className: "h-4 w-4" }),
56
- " ",
57
- i(Y.scheduledAt)
58
- ]
59
- }),
60
- /* @__PURE__ */ D("span", {
61
- className: "flex items-center gap-2",
62
- children: [
63
- /* @__PURE__ */ E(y, { className: "h-4 w-4" }),
64
- " ",
65
- a(Y.durationMin)
66
- ]
163
+ /* @__PURE__ */ E("h2", {
164
+ className: "text-sm font-semibold text-text-primary",
165
+ children: "Session details"
67
166
  }),
68
- Y.location && /* @__PURE__ */ D("span", {
69
- className: "flex items-center gap-2",
167
+ /* @__PURE__ */ D("p", {
168
+ className: "mt-2 text-sm text-text-secondary",
70
169
  children: [
71
- /* @__PURE__ */ E(x, { className: "h-4 w-4" }),
72
- " ",
73
- Y.location
170
+ i[Y.type],
171
+ " session",
172
+ Y.location ? ` · ${Y.location}` : "",
173
+ "."
74
174
  ]
75
175
  }),
76
- $ && /* @__PURE__ */ D("a", {
176
+ $ ? /* @__PURE__ */ D("a", {
77
177
  href: $,
78
178
  target: "_blank",
79
179
  rel: "noopener noreferrer",
80
- className: "flex items-center gap-2 text-primary hover:underline",
81
- children: [/* @__PURE__ */ E(C, { className: "h-4 w-4" }), " Join meeting"]
82
- })
83
- ]
84
- }),
85
- Y.completedAt && /* @__PURE__ */ D("div", {
86
- className: "mt-5 border-t border-border-seam pt-5",
87
- children: [
88
- /* @__PURE__ */ E("h2", {
89
- className: "mb-2 text-sm font-semibold text-text-primary",
90
- children: "Outcome"
91
- }),
92
- /* @__PURE__ */ D("p", {
93
- className: "text-sm text-text-secondary",
180
+ className: "mt-4 flex items-center gap-3 rounded-xl border border-border-seam bg-bg-sunken p-3 transition-colors hover:bg-bg-surface focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
94
181
  children: [
95
- "Completed ",
96
- i(Y.completedAt),
97
- Y.rating !== null && /* @__PURE__ */ D("span", {
98
- className: "ml-2 inline-flex items-center gap-1",
99
- children: [
100
- /* @__PURE__ */ E(S, { className: "h-3.5 w-3.5" }),
101
- " ",
102
- Y.rating,
103
- "/5"
104
- ]
182
+ /* @__PURE__ */ E("span", {
183
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
184
+ children: /* @__PURE__ */ E(w, {
185
+ className: "h-5 w-5",
186
+ "aria-hidden": "true"
187
+ })
188
+ }),
189
+ /* @__PURE__ */ D("span", {
190
+ className: "min-w-0 flex-1",
191
+ children: [/* @__PURE__ */ E("span", {
192
+ className: "block text-sm font-medium text-text-primary",
193
+ children: "Meeting link"
194
+ }), /* @__PURE__ */ E("span", {
195
+ className: "block truncate text-xs text-text-secondary",
196
+ children: "Online session — tap to join"
197
+ })]
198
+ }),
199
+ /* @__PURE__ */ D("span", {
200
+ className: "inline-flex items-center gap-1 text-sm font-medium text-primary",
201
+ children: ["Join ", /* @__PURE__ */ E(b, {
202
+ className: "h-4 w-4",
203
+ "aria-hidden": "true"
204
+ })]
105
205
  })
106
206
  ]
107
- }),
108
- Y.feedback && /* @__PURE__ */ D("p", {
109
- className: "mt-2 text-sm text-text-secondary",
207
+ }) : null,
208
+ Y.completedAt ? /* @__PURE__ */ D("div", {
209
+ className: "mt-5 border-t border-border-seam pt-5",
110
210
  children: [
111
- "“",
112
- Y.feedback,
113
- "”"
211
+ /* @__PURE__ */ E("h3", {
212
+ className: "mb-2 text-sm font-semibold text-text-primary",
213
+ children: "Outcome"
214
+ }),
215
+ /* @__PURE__ */ D("p", {
216
+ className: "text-sm text-text-secondary",
217
+ children: ["Completed ", a(Y.completedAt)]
218
+ }),
219
+ Y.rating === null ? null : /* @__PURE__ */ E("div", {
220
+ className: "mt-2",
221
+ children: /* @__PURE__ */ E(n, {
222
+ value: Y.rating,
223
+ size: 18,
224
+ showValue: !0
225
+ })
226
+ }),
227
+ Y.feedback ? /* @__PURE__ */ D("p", {
228
+ className: "mt-3 rounded-lg bg-bg-sunken p-3 text-sm italic text-text-secondary",
229
+ children: [
230
+ "“",
231
+ Y.feedback,
232
+ "”"
233
+ ]
234
+ }) : null
114
235
  ]
115
- })
236
+ }) : null
116
237
  ]
117
238
  })
118
- ]
239
+ }), X || Z || Q ? /* @__PURE__ */ D(O, {
240
+ className: "h-fit",
241
+ children: [
242
+ /* @__PURE__ */ E("h2", {
243
+ className: "mb-4 text-sm font-semibold text-text-primary",
244
+ children: "Actions"
245
+ }),
246
+ /* @__PURE__ */ D("div", {
247
+ className: "flex flex-col gap-2.5",
248
+ children: [
249
+ X && /* @__PURE__ */ D("button", {
250
+ type: "button",
251
+ onClick: () => o && L.mutate(o),
252
+ disabled: L.isPending,
253
+ className: "inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
254
+ children: [L.isPending ? /* @__PURE__ */ E(x, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ E(v, { className: "h-4 w-4" }), "Confirm"]
255
+ }),
256
+ Z && /* @__PURE__ */ D("button", {
257
+ type: "button",
258
+ onClick: () => {
259
+ q(null), U(Y.feedback ?? ""), G(Y.rating ?? 0), V(!0);
260
+ },
261
+ className: "inline-flex w-full items-center justify-center gap-2 rounded-lg bg-status-success-bg-subtle px-4 py-2.5 text-sm font-medium text-status-success-text ring-1 ring-inset ring-status-success-border transition-colors hover:bg-status-success-bg-subtle/70",
262
+ children: [/* @__PURE__ */ E(v, { className: "h-4 w-4" }), " Complete"]
263
+ }),
264
+ Q && /* @__PURE__ */ D("button", {
265
+ type: "button",
266
+ onClick: () => o && z.mutate(o),
267
+ disabled: z.isPending,
268
+ className: "inline-flex w-full items-center justify-center gap-2 rounded-lg border border-status-error-border px-4 py-2.5 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60",
269
+ children: [z.isPending ? /* @__PURE__ */ E(x, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ E(T, { className: "h-4 w-4" }), "Cancel session"]
270
+ })
271
+ ]
272
+ }),
273
+ Z ? /* @__PURE__ */ E("p", {
274
+ className: "mt-3 text-xs text-text-muted",
275
+ children: "Completing lets you leave feedback and a 1–5 star rating for the athlete."
276
+ }) : null
277
+ ]
278
+ }) : null]
119
279
  }),
120
- (X || Z || Q) && /* @__PURE__ */ D(O, { children: [/* @__PURE__ */ E("h2", {
121
- className: "mb-4 text-sm font-semibold text-text-primary",
122
- children: "Actions"
123
- }), /* @__PURE__ */ D("div", {
124
- className: "flex flex-wrap items-center gap-2",
125
- children: [
126
- X && /* @__PURE__ */ D("button", {
127
- type: "button",
128
- onClick: () => F && R.mutate(F),
129
- disabled: R.isPending,
130
- 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",
131
- children: [R.isPending ? /* @__PURE__ */ E(b, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ E(v, { className: "h-4 w-4" }), "Confirm"]
132
- }),
133
- Z && /* @__PURE__ */ D("button", {
134
- type: "button",
135
- onClick: () => {
136
- J(null), H(!0);
137
- },
138
- 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",
139
- children: [/* @__PURE__ */ E(v, { className: "h-4 w-4" }), " Complete"]
140
- }),
141
- Q && /* @__PURE__ */ D("button", {
142
- type: "button",
143
- onClick: () => F && B.mutate(F),
144
- disabled: B.isPending,
145
- className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60",
146
- children: [B.isPending ? /* @__PURE__ */ E(b, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ E(w, { className: "h-4 w-4" }), "Cancel"]
147
- })
148
- ]
149
- })] }),
150
280
  /* @__PURE__ */ E(A, {
151
- open: V,
152
- onOpenChange: H,
281
+ open: B,
282
+ onOpenChange: V,
153
283
  children: /* @__PURE__ */ D(j, { children: [/* @__PURE__ */ E(N, { children: /* @__PURE__ */ E(P, { children: "Complete session" }) }), /* @__PURE__ */ D("form", {
154
284
  onSubmit: async (e) => {
155
- if (e.preventDefault(), J(null), !F) return;
156
- let t;
157
- if (G.trim() && (t = Number.parseInt(G, 10), Number.isNaN(t) || t < 1 || t > 5)) {
158
- J("Rating must be between 1 and 5.");
285
+ if (e.preventDefault(), q(null), !o) return;
286
+ let t = W > 0 ? W : void 0;
287
+ if (t !== void 0 && (t < 1 || t > 5)) {
288
+ q("Rating must be between 1 and 5.");
159
289
  return;
160
290
  }
161
291
  try {
162
- await z.mutateAsync({
163
- id: F,
292
+ await R.mutateAsync({
293
+ id: o,
164
294
  input: {
165
- feedback: U.trim() || void 0,
295
+ feedback: H.trim() || void 0,
166
296
  rating: t
167
297
  }
168
- }), H(!1);
298
+ }), V(!1);
169
299
  } catch (e) {
170
- J(e instanceof Error ? e.message : "Failed to complete session.");
300
+ q(e instanceof Error ? e.message : "Failed to complete session.");
171
301
  }
172
302
  },
173
303
  className: "space-y-4 px-1",
@@ -180,38 +310,36 @@ function F() {
180
310
  id: "feedback",
181
311
  className: p,
182
312
  rows: 3,
183
- value: U,
184
- onChange: (e) => W(e.target.value),
313
+ value: H,
314
+ onChange: (e) => U(e.target.value),
185
315
  placeholder: "How did the session go?"
186
316
  })
187
317
  }),
188
318
  /* @__PURE__ */ E(f, {
189
- label: "Rating (1–5)",
190
- htmlFor: "rating",
191
- hint: "Optional.",
192
- children: /* @__PURE__ */ E("input", {
193
- id: "rating",
194
- inputMode: "numeric",
195
- className: p,
196
- value: G,
197
- onChange: (e) => K(e.target.value),
198
- placeholder: "5"
319
+ label: "Rating",
320
+ hint: "Optional — tap a star.",
321
+ children: /* @__PURE__ */ E(n, {
322
+ value: W,
323
+ onChange: G,
324
+ size: 30,
325
+ showLabels: !0,
326
+ ariaLabel: "Rate this session out of 5"
199
327
  })
200
328
  }),
201
- q && /* @__PURE__ */ E("p", {
329
+ K && /* @__PURE__ */ E("p", {
202
330
  className: "text-sm text-status-error-text",
203
- children: q
331
+ children: K
204
332
  }),
205
333
  /* @__PURE__ */ D(M, { children: [/* @__PURE__ */ E("button", {
206
334
  type: "button",
207
- onClick: () => H(!1),
335
+ onClick: () => V(!1),
208
336
  className: "rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
209
337
  children: "Cancel"
210
338
  }), /* @__PURE__ */ D("button", {
211
339
  type: "submit",
212
- disabled: z.isPending,
213
- 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",
214
- children: [z.isPending && /* @__PURE__ */ E(b, { className: "h-4 w-4 animate-spin" }), "Complete session"]
340
+ disabled: R.isPending,
341
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-status-success-bg-subtle px-4 py-2 text-sm font-medium text-status-success-text ring-1 ring-inset ring-status-success-border transition-colors hover:bg-status-success-bg-subtle/70 disabled:opacity-60",
342
+ children: [R.isPending && /* @__PURE__ */ E(x, { className: "h-4 w-4 animate-spin" }), "Complete session"]
215
343
  })] })
216
344
  ]
217
345
  })] })
@@ -220,6 +348,6 @@ function F() {
220
348
  });
221
349
  }
222
350
  //#endregion
223
- export { F as SessionDetailPage };
351
+ export { I as SessionDetailPage };
224
352
 
225
353
  //# sourceMappingURL=SessionDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SessionDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/sessions/SessionDetailPage.tsx"],"sourcesContent":["/**\n * Session Detail\n * @module atheletebridge/pages/sessions\n *\n * Reads a single session (athSession) and drives its lifecycle:\n * confirm (athConfirmSession), complete with feedback + rating\n * (athCompleteSession) and cancel (athCancelSession).\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n CalendarClock,\n Clock,\n MapPin,\n Video,\n Star,\n CheckCircle2,\n XCircle,\n Loader2,\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 { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { SESSION_TYPE_LABELS } from '../../components/domain';\nimport { formatDate, formatDuration } from '../../utils/formatters';\nimport { safeExternalHref } from '../../utils/url';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useSession,\n useConfirmSession,\n useCompleteSession,\n useCancelSession,\n} from '../../hooks/useAthleteBridgeApi';\n\nexport function SessionDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n\n const session = useSession(id);\n const confirmSession = useConfirmSession();\n const completeSession = useCompleteSession();\n const cancelSession = useCancelSession();\n\n const [completeOpen, setCompleteOpen] = useState(false);\n const [feedback, setFeedback] = useState('');\n const [rating, setRating] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n if (session.isLoading) {\n return (\n <PageLayout title=\"Session\">\n <LoadingState label=\"Loading session…\" />\n </PageLayout>\n );\n }\n\n if (session.isError || !session.data) {\n return (\n <PageLayout title=\"Session\">\n <ErrorState\n title=\"Session not found\"\n message=\"This session does not exist or you do not have access to it.\"\n onRetry={() => navigateTo('/sessions')}\n />\n </PageLayout>\n );\n }\n\n const s = session.data;\n const canConfirm = s.status === 'REQUESTED';\n const canComplete = s.status === 'CONFIRMED';\n const canCancel = s.status === 'REQUESTED' || s.status === 'CONFIRMED';\n // Sanitize the user-controlled meeting URL before it becomes a clickable href.\n const meetingHref = safeExternalHref(s.meetingUrl);\n\n const handleComplete = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!id) return;\n let ratingNum: number | undefined;\n if (rating.trim()) {\n ratingNum = Number.parseInt(rating, 10);\n if (Number.isNaN(ratingNum) || ratingNum < 1 || ratingNum > 5) {\n setError('Rating must be between 1 and 5.');\n return;\n }\n }\n try {\n await completeSession.mutateAsync({\n id,\n input: {\n feedback: feedback.trim() || undefined,\n rating: ratingNum,\n },\n });\n setCompleteOpen(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to complete session.');\n }\n };\n\n return (\n <PageLayout\n title={s.title}\n description={SESSION_TYPE_LABELS[s.type]}\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\" /> Back\n </button>\n }\n >\n <GlassCard treatment=\"insight\" className=\"mb-8 p-6\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <StatusBadge status={s.status} />\n </div>\n <div className=\"mt-4 grid grid-cols-1 gap-3 text-sm text-text-secondary sm:grid-cols-2\">\n <span className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4\" /> {formatDate(s.scheduledAt)}\n </span>\n <span className=\"flex items-center gap-2\">\n <Clock className=\"h-4 w-4\" /> {formatDuration(s.durationMin)}\n </span>\n {s.location && (\n <span className=\"flex items-center gap-2\">\n <MapPin className=\"h-4 w-4\" /> {s.location}\n </span>\n )}\n {meetingHref && (\n <a\n href={meetingHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-2 text-primary hover:underline\"\n >\n <Video className=\"h-4 w-4\" /> Join meeting\n </a>\n )}\n </div>\n\n {s.completedAt && (\n <div className=\"mt-5 border-t border-border-seam pt-5\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">Outcome</h2>\n <p className=\"text-sm text-text-secondary\">\n Completed {formatDate(s.completedAt)}\n {s.rating !== null && (\n <span className=\"ml-2 inline-flex items-center gap-1\">\n <Star className=\"h-3.5 w-3.5\" /> {s.rating}/5\n </span>\n )}\n </p>\n {s.feedback && <p className=\"mt-2 text-sm text-text-secondary\">“{s.feedback}”</p>}\n </div>\n )}\n </GlassCard>\n\n {/* Lifecycle actions */}\n {(canConfirm || canComplete || canCancel) && (\n <EmphasisPanel>\n <h2 className=\"mb-4 text-sm font-semibold text-text-primary\">Actions</h2>\n <div className=\"flex flex-wrap items-center gap-2\">\n {canConfirm && (\n <button\n type=\"button\"\n onClick={() => id && confirmSession.mutate(id)}\n disabled={confirmSession.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 {confirmSession.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"h-4 w-4\" />\n )}\n Confirm\n </button>\n )}\n {canComplete && (\n <button\n type=\"button\"\n onClick={() => {\n setError(null);\n setCompleteOpen(true);\n }}\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\"\n >\n <CheckCircle2 className=\"h-4 w-4\" /> Complete\n </button>\n )}\n {canCancel && (\n <button\n type=\"button\"\n onClick={() => id && cancelSession.mutate(id)}\n disabled={cancelSession.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {cancelSession.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n Cancel\n </button>\n )}\n </div>\n </EmphasisPanel>\n )}\n\n {/* Complete dialog */}\n <ResponsiveDialog open={completeOpen} onOpenChange={setCompleteOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Complete session</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <form onSubmit={handleComplete} className=\"space-y-4 px-1\">\n <FormField label=\"Feedback\" htmlFor=\"feedback\" hint=\"Optional post-session notes.\">\n <textarea\n id=\"feedback\"\n className={fieldInputClass}\n rows={3}\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n placeholder=\"How did the session go?\"\n />\n </FormField>\n <FormField label=\"Rating (1–5)\" htmlFor=\"rating\" hint=\"Optional.\">\n <input\n id=\"rating\"\n inputMode=\"numeric\"\n className={fieldInputClass}\n value={rating}\n onChange={(e) => setRating(e.target.value)}\n placeholder=\"5\"\n />\n </FormField>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setCompleteOpen(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={completeSession.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 {completeSession.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n Complete session\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SessionDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/sessions/SessionDetailPage.tsx"],"sourcesContent":["/**\n * Session Detail\n * @module atheletebridge/pages/sessions\n *\n * Reads a single session (athSession) and drives its lifecycle:\n * confirm (athConfirmSession), complete with feedback + rating\n * (athCompleteSession) and cancel (athCancelSession).\n *\n * The layout mirrors the concept mock: a lifecycle timeline, paired\n * Coach / Athlete / Schedule cards (identities resolved via the platform User\n * federation lookup), an iconized meeting-link card, and an Actions panel with\n * an interactive 5-star rating in the Complete flow. Semantic tokens only,\n * responsive down to a single mobile column (with bottom-sheet dialogs).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n CalendarClock,\n Clock,\n MapPin,\n Video,\n CheckCircle2,\n XCircle,\n Loader2,\n Mail,\n ExternalLink,\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 { UserAvatar } from '../../components/UserAvatar';\nimport { SessionStatusTimeline } from '../../components/SessionStatusTimeline';\nimport { RatingStars } from '../../components/viz';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { SESSION_TYPE_LABELS } from '../../components/domain';\nimport { formatDate, formatDuration } from '../../utils/formatters';\nimport { safeExternalHref } from '../../utils/url';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useSession,\n useUserDirectory,\n useConfirmSession,\n useCompleteSession,\n useCancelSession,\n} from '../../hooks/useAthleteBridgeApi';\n\n/** A labelled identity tile (Coach / Athlete) — monogram avatar + name + email. */\nfunction PartyCard({\n role,\n name,\n email,\n id,\n}: {\n role: string;\n name?: string | null;\n email?: string | null;\n id: string;\n}) {\n const displayName = name?.trim() || `User ${id.slice(0, 6)}`;\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">{role}</p>\n <div className=\"mt-3 flex items-center gap-3\">\n <UserAvatar name={name} id={id} size=\"lg\" />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{displayName}</p>\n <p className=\"truncate text-xs text-text-secondary\">{role}</p>\n </div>\n </div>\n {email ? (\n <a\n href={`mailto:${email}`}\n className=\"mt-3 flex items-center gap-1.5 text-xs text-text-secondary hover:text-text-primary hover:underline\"\n >\n <Mail className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{email}</span>\n </a>\n ) : null}\n </GlassCard>\n );\n}\n\nexport function SessionDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n\n const session = useSession(id);\n const confirmSession = useConfirmSession();\n const completeSession = useCompleteSession();\n const cancelSession = useCancelSession();\n\n const [completeOpen, setCompleteOpen] = useState(false);\n const [feedback, setFeedback] = useState('');\n const [rating, setRating] = useState<number>(0);\n const [error, setError] = useState<string | null>(null);\n\n // Resolve coach + athlete identities in one batched federation call.\n const directoryIds = useMemo(\n () => (session.data ? [session.data.coachId, session.data.athleteId] : []),\n [session.data]\n );\n const directory = useUserDirectory(directoryIds);\n\n if (session.isLoading) {\n return (\n <PageLayout title=\"Session\">\n <LoadingState label=\"Loading session…\" />\n </PageLayout>\n );\n }\n\n if (session.isError || !session.data) {\n return (\n <PageLayout title=\"Session\">\n <ErrorState\n title=\"Session not found\"\n message=\"This session does not exist or you do not have access to it.\"\n onRetry={() => navigateTo('/sessions')}\n />\n </PageLayout>\n );\n }\n\n const s = session.data;\n const canConfirm = s.status === 'REQUESTED';\n const canComplete = s.status === 'CONFIRMED';\n const canCancel = s.status === 'REQUESTED' || s.status === 'CONFIRMED';\n // Sanitize the user-controlled meeting URL before it becomes a clickable href.\n const meetingHref = safeExternalHref(s.meetingUrl);\n\n const handleComplete = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!id) return;\n const ratingNum = rating > 0 ? rating : undefined;\n if (ratingNum !== undefined && (ratingNum < 1 || ratingNum > 5)) {\n setError('Rating must be between 1 and 5.');\n return;\n }\n try {\n await completeSession.mutateAsync({\n id,\n input: {\n feedback: feedback.trim() || undefined,\n rating: ratingNum,\n },\n });\n setCompleteOpen(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to complete session.');\n }\n };\n\n return (\n <PageLayout\n title={s.title}\n description={SESSION_TYPE_LABELS[s.type]}\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\" /> Back\n </button>\n }\n >\n {/* Lifecycle timeline + prominent status */}\n <GlassCard treatment=\"insight\" className=\"mb-6 p-5 sm:p-6\">\n <div className=\"mb-5 flex items-center justify-between gap-3\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">\n Lifecycle\n </span>\n <StatusBadge status={s.status} />\n </div>\n <SessionStatusTimeline\n status={s.status}\n createdAt={s.createdAt}\n completedAt={s.completedAt}\n />\n </GlassCard>\n\n {/* Coach / Athlete / Schedule — three-up on desktop, stacked on mobile */}\n <div className=\"mb-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <PartyCard\n role=\"Coach\"\n id={s.coachId}\n name={directory.nameOf(s.coachId)}\n email={directory.emailOf(s.coachId)}\n />\n <PartyCard\n role=\"Athlete\"\n id={s.athleteId}\n name={directory.nameOf(s.athleteId)}\n email={directory.emailOf(s.athleteId)}\n />\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">Schedule</p>\n <div className=\"mt-3 space-y-2.5 text-sm text-text-primary\">\n <span className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n {formatDate(s.scheduledAt)}\n </span>\n <span className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n {formatDuration(s.durationMin)}\n </span>\n {s.type === 'IN_PERSON' && s.location ? (\n <span className=\"flex items-center gap-2 text-text-secondary\">\n <MapPin className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" /> {s.location}\n </span>\n ) : null}\n </div>\n </GlassCard>\n </div>\n\n {/* Details + Actions */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Left: session details, meeting link, outcome */}\n <div className=\"space-y-6\">\n <GlassCard treatment=\"glass\" className=\"p-5 sm:p-6\">\n <h2 className=\"text-sm font-semibold text-text-primary\">Session details</h2>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {SESSION_TYPE_LABELS[s.type]} session\n {s.location ? ` · ${s.location}` : ''}.\n </p>\n\n {meetingHref ? (\n <a\n href={meetingHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mt-4 flex items-center gap-3 rounded-xl border border-border-seam bg-bg-sunken p-3 transition-colors hover:bg-bg-surface focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Video className=\"h-5 w-5\" aria-hidden=\"true\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-sm font-medium text-text-primary\">Meeting link</span>\n <span className=\"block truncate text-xs text-text-secondary\">\n Online session — tap to join\n </span>\n </span>\n <span className=\"inline-flex items-center gap-1 text-sm font-medium text-primary\">\n Join <ExternalLink className=\"h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </a>\n ) : null}\n\n {s.completedAt ? (\n <div className=\"mt-5 border-t border-border-seam pt-5\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Outcome</h3>\n <p className=\"text-sm text-text-secondary\">Completed {formatDate(s.completedAt)}</p>\n {s.rating !== null ? (\n <div className=\"mt-2\">\n <RatingStars value={s.rating} size={18} showValue />\n </div>\n ) : null}\n {s.feedback ? (\n <p className=\"mt-3 rounded-lg bg-bg-sunken p-3 text-sm italic text-text-secondary\">\n “{s.feedback}”\n </p>\n ) : null}\n </div>\n ) : null}\n </GlassCard>\n </div>\n\n {/* Right: lifecycle actions */}\n {canConfirm || canComplete || canCancel ? (\n <EmphasisPanel className=\"h-fit\">\n <h2 className=\"mb-4 text-sm font-semibold text-text-primary\">Actions</h2>\n <div className=\"flex flex-col gap-2.5\">\n {canConfirm && (\n <button\n type=\"button\"\n onClick={() => id && confirmSession.mutate(id)}\n disabled={confirmSession.isPending}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {confirmSession.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"h-4 w-4\" />\n )}\n Confirm\n </button>\n )}\n {canComplete && (\n <button\n type=\"button\"\n onClick={() => {\n setError(null);\n setFeedback(s.feedback ?? '');\n setRating(s.rating ?? 0);\n setCompleteOpen(true);\n }}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-status-success-bg-subtle px-4 py-2.5 text-sm font-medium text-status-success-text ring-1 ring-inset ring-status-success-border transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <CheckCircle2 className=\"h-4 w-4\" /> Complete\n </button>\n )}\n {canCancel && (\n <button\n type=\"button\"\n onClick={() => id && cancelSession.mutate(id)}\n disabled={cancelSession.isPending}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg border border-status-error-border px-4 py-2.5 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {cancelSession.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n Cancel session\n </button>\n )}\n </div>\n {canComplete ? (\n <p className=\"mt-3 text-xs text-text-muted\">\n Completing lets you leave feedback and a 1–5 star rating for the athlete.\n </p>\n ) : null}\n </EmphasisPanel>\n ) : null}\n </div>\n\n {/* Complete dialog */}\n <ResponsiveDialog open={completeOpen} onOpenChange={setCompleteOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Complete session</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <form onSubmit={handleComplete} className=\"space-y-4 px-1\">\n <FormField label=\"Feedback\" htmlFor=\"feedback\" hint=\"Optional post-session notes.\">\n <textarea\n id=\"feedback\"\n className={fieldInputClass}\n rows={3}\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n placeholder=\"How did the session go?\"\n />\n </FormField>\n <FormField label=\"Rating\" hint=\"Optional — tap a star.\">\n <RatingStars\n value={rating}\n onChange={setRating}\n size={30}\n showLabels\n ariaLabel=\"Rate this session out of 5\"\n />\n </FormField>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setCompleteOpen(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={completeSession.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-status-success-bg-subtle px-4 py-2 text-sm font-medium text-status-success-text ring-1 ring-inset ring-status-success-border transition-colors hover:bg-status-success-bg-subtle/70 disabled:opacity-60\"\n >\n {completeSession.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n Complete session\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0DA,SAAS,EAAU,EACjB,SACA,SACA,UACA,SAMC;CACD,IAAM,IAAc,GAAM,MAAM,IAAI,QAAQ,EAAG,MAAM,GAAG,EAAE;AAC1D,QACE,kBAAC,GAAD;EAAW,WAAU;EAAS,WAAU;YAAxC;GACE,kBAAC,KAAD;IAAG,WAAU;cAA+D;IAAS,CAAA;GACrF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAkB;KAAU;KAAI,MAAK;KAAO,CAAA,EAC5C,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAoD;MAAgB,CAAA,EACjF,kBAAC,KAAD;MAAG,WAAU;gBAAwC;MAAS,CAAA,CAC1D;OACF;;GACL,IACC,kBAAC,KAAD;IACE,MAAM,UAAU;IAChB,WAAU;cAFZ,CAIE,kBAAC,GAAD;KAAM,WAAU;KAAuB,eAAY;KAAS,CAAA,EAC5D,kBAAC,QAAD;KAAM,WAAU;eAAY;KAAa,CAAA,CACvC;QACF;GACM;;;AAIhB,SAAgB,IAAoB;CAClC,IAAM,EAAE,UAAO,IAA2B,EACpC,EAAE,kBAAe,GAAkB,EAEnC,IAAU,EAAW,EAAG,EACxB,IAAiB,GAAmB,EACpC,IAAkB,GAAoB,EACtC,IAAgB,IAAkB,EAElC,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAQ,KAAa,EAAiB,EAAE,EACzC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAOjD,IAAY,EAJG,QACZ,EAAQ,OAAO,CAAC,EAAQ,KAAK,SAAS,EAAQ,KAAK,UAAU,GAAG,EAAE,EACzE,CAAC,EAAQ,KAAK,CACf,CAC+C;AAEhD,KAAI,EAAQ,UACV,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,IAAD,EAAc,OAAM,oBAAqB,CAAA;EAC9B,CAAA;AAIjB,KAAI,EAAQ,WAAW,CAAC,EAAQ,KAC9B,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GACE,OAAM;GACN,SAAQ;GACR,eAAe,EAAW,YAAY;GACtC,CAAA;EACS,CAAA;CAIjB,IAAM,IAAI,EAAQ,MACZ,IAAa,EAAE,WAAW,aAC1B,IAAc,EAAE,WAAW,aAC3B,IAAY,EAAE,WAAW,eAAe,EAAE,WAAW,aAErD,IAAc,EAAiB,EAAE,WAAW;AAyBlD,QACE,kBAAC,GAAD;EACE,OAAO,EAAE;EACT,aAAa,EAAoB,EAAE;EACnC,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,YAAY;GACtC,WAAU;aAHZ,CAKE,kBAAC,IAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAC1B;;YAVb;GAcE,kBAAC,GAAD;IAAW,WAAU;IAAU,WAAU;cAAzC,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAA8D;MAEvE,CAAA,EACP,kBAAC,GAAD,EAAa,QAAQ,EAAE,QAAU,CAAA,CAC7B;QACN,kBAAC,GAAD;KACE,QAAQ,EAAE;KACV,WAAW,EAAE;KACb,aAAa,EAAE;KACf,CAAA,CACQ;;GAGZ,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,MAAK;MACL,IAAI,EAAE;MACN,MAAM,EAAU,OAAO,EAAE,QAAQ;MACjC,OAAO,EAAU,QAAQ,EAAE,QAAQ;MACnC,CAAA;KACF,kBAAC,GAAD;MACE,MAAK;MACL,IAAI,EAAE;MACN,MAAM,EAAU,OAAO,EAAE,UAAU;MACnC,OAAO,EAAU,QAAQ,EAAE,UAAU;MACrC,CAAA;KACF,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8D;OAAY,CAAA,EACvF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACE,kBAAC,IAAD;UAAe,WAAU;UAAuC,eAAY;UAAS,CAAA,EACpF,EAAW,EAAE,YAAY,CACrB;;QACP,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACE,kBAAC,GAAD;UAAO,WAAU;UAAmB,eAAY;UAAS,CAAA,EACxD,GAAe,EAAE,YAAY,CACzB;;QACN,EAAE,SAAS,eAAe,EAAE,WAC3B,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACE,kBAAC,GAAD;WAAQ,WAAU;WAAmB,eAAY;WAAS,CAAA;;UAAE,EAAE;UACzD;aACL;QACA;SACI;;KACR;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC;OACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;QAAoB,CAAA;OAC5E,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAoB,EAAE;SAAM;SAC5B,EAAE,WAAW,MAAM,EAAE,aAAa;SAAG;SACpC;;OAEH,IACC,kBAAC,KAAD;QACE,MAAM;QACN,QAAO;QACP,KAAI;QACJ,WAAU;kBAJZ;SAME,kBAAC,QAAD;UAAM,WAAU;oBACd,kBAAC,GAAD;WAAO,WAAU;WAAU,eAAY;WAAS,CAAA;UAC3C,CAAA;SACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8C;WAAmB,CAAA,EACjF,kBAAC,QAAD;WAAM,WAAU;qBAA6C;WAEtD,CAAA,CACF;;SACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAAkF,SAC3E,kBAAC,GAAD;WAAc,WAAU;WAAU,eAAY;WAAS,CAAA,CACvD;;SACL;YACF;OAEH,EAAE,cACD,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,MAAD;UAAI,WAAU;oBAA+C;UAAY,CAAA;SACzE,kBAAC,KAAD;UAAG,WAAU;oBAAb,CAA2C,cAAW,EAAW,EAAE,YAAY,CAAK;;SACnF,EAAE,WAAW,OAIV,OAHF,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD;WAAa,OAAO,EAAE;WAAQ,MAAM;WAAI,WAAA;WAAY,CAAA;UAChD,CAAA;SAEP,EAAE,WACD,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAmF;WAC/E,EAAE;WAAS;WACX;cACF;SACA;YACJ;OACM;;KACR,CAAA,EAGL,KAAc,KAAe,IAC5B,kBAAC,GAAD;KAAe,WAAU;eAAzB;MACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAAY,CAAA;MACzE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,KAAM,EAAe,OAAO,EAAG;SAC9C,UAAU,EAAe;SACzB,WAAU;mBAJZ,CAMG,EAAe,YACd,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,EACpC,UAEK;;QAEV,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAe;AAIb,UAHA,EAAS,KAAK,EACd,EAAY,EAAE,YAAY,GAAG,EAC7B,EAAU,EAAE,UAAU,EAAE,EACxB,EAAgB,GAAK;;SAEvB,WAAU;mBARZ,CAUE,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,EAAA,YAC7B;;QAEV,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,KAAM,EAAc,OAAO,EAAG;SAC7C,UAAU,EAAc;SACxB,WAAU;mBAJZ,CAMG,EAAc,YACb,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA,EAC/B,iBAEK;;QAEP;;MACL,IACC,kBAAC,KAAD;OAAG,WAAU;iBAA+B;OAExC,CAAA,GACF;MACU;SACd,KACA;;GAGN,kBAAC,GAAD;IAAkB,MAAM;IAAc,cAAc;cAClD,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAuB,oBAAwC,CAAA,EACxC,CAAA,EACzB,kBAAC,QAAD;KAAM,UA3MS,OAAO,MAAuB;AAGnD,UAFA,EAAE,gBAAgB,EAClB,EAAS,KAAK,EACV,CAAC,EAAI;MACT,IAAM,IAAY,IAAS,IAAI,IAAS,KAAA;AACxC,UAAI,MAAc,KAAA,MAAc,IAAY,KAAK,IAAY,IAAI;AAC/D,SAAS,kCAAkC;AAC3C;;AAEF,UAAI;AAQF,OAPA,MAAM,EAAgB,YAAY;QAChC;QACA,OAAO;SACL,UAAU,EAAS,MAAM,IAAI,KAAA;SAC7B,QAAQ;SACT;QACF,CAAC,EACF,EAAgB,GAAM;eACf,GAAK;AACZ,SAAS,aAAe,QAAQ,EAAI,UAAU,8BAA8B;;;KAwLxC,WAAU;eAA1C;MACE,kBAAC,GAAD;OAAW,OAAM;OAAW,SAAQ;OAAW,MAAK;iBAClD,kBAAC,YAAD;QACE,IAAG;QACH,WAAW;QACX,MAAM;QACN,OAAO;QACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;QAC5C,aAAY;QACZ,CAAA;OACQ,CAAA;MACZ,kBAAC,GAAD;OAAW,OAAM;OAAS,MAAK;iBAC7B,kBAAC,GAAD;QACE,OAAO;QACP,UAAU;QACV,MAAM;QACN,YAAA;QACA,WAAU;QACV,CAAA;OACQ,CAAA;MACX,KAAS,kBAAC,KAAD;OAAG,WAAU;iBAAkC;OAAU,CAAA;MACnE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAgB,GAAM;OACrC,WAAU;iBACX;OAEQ,CAAA,EACT,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,EAAgB;OAC1B,WAAU;iBAHZ,CAKG,EAAgB,aAAa,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,EAAC,mBAEpE;SACc,EAAA,CAAA;MACpB;OACiB,EAAA,CAAA;IACT,CAAA;GACR"}