@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,205 +1,293 @@
1
1
  import { useAthleteBridge as e } from "../../context/AthleteBridgeContext.js";
2
- import { PageLayout as t } from "../../components/PageLayout.js";
3
- import { FIXTURE_TYPE_LABELS as n } from "../../components/domain.js";
4
- import { LoadingState as r } from "../../components/LoadingState.js";
5
- import { useScheduleFixture as i, useTeams as a } from "../../hooks/useAthleteBridgeApi.js";
6
- import { FormField as o, fieldInputClass as s } from "../../components/form.js";
7
- import { toIsoOrUndefined as c } from "../../utils/datetime.js";
8
- import { useState as l } from "react";
9
- import { ArrowLeft as u, Loader2 as d, Save as f } from "lucide-react";
10
- import { useSearchParams as p } from "react-router-dom";
11
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
2
+ import { cn as t } from "../../utils/cn.js";
3
+ import { PageLayout as n } from "../../components/PageLayout.js";
4
+ import { FIXTURE_TYPE_LABELS as r } from "../../components/domain.js";
5
+ import { Crest as i } from "../../components/Crest.js";
6
+ import { LoadingState as a } from "../../components/LoadingState.js";
7
+ import { useFixtures as o, useScheduleFixture as s, useTeams as c } from "../../hooks/useAthleteBridgeApi.js";
8
+ import { FormField as l, fieldInputClass as u } from "../../components/form.js";
9
+ import { toIsoOrUndefined as d } from "../../utils/datetime.js";
10
+ import { useMemo as f, useState as p } from "react";
11
+ import { ArrowLeft as m, Calendar as h, Clock as g, Handshake as _, Loader2 as v, Network as y, Save as b, Shield as x, Swords as S, Trophy as C } from "lucide-react";
12
+ import { useSearchParams as w } from "react-router-dom";
13
+ import { jsx as T, jsxs as E } from "react/jsx-runtime";
14
+ import { GlassCard as D } from "@burdenoff/fe-libs/ui";
12
15
  //#region src/atheletebridge/pages/fixtures/FixtureFormPage.tsx
13
- var g = Object.keys(n);
14
- function _() {
15
- let [_] = p(), { navigateTo: v } = e(), y = a({ pagination: { limit: 100 } }), b = i(), [x, S] = l(_.get("homeTeamId") ?? ""), [C, w] = l(""), [T, E] = l("FRIENDLY"), [D, O] = l(""), [k, A] = l(""), [j, M] = l(""), [N, P] = l(""), [F, I] = l(""), [L, R] = l(""), [z, B] = l(null), V = y.data?.items ?? [];
16
- return y.isLoading ? /* @__PURE__ */ m(t, {
16
+ var O = {
17
+ FRIENDLY: _,
18
+ LEAGUE: x,
19
+ CUP: C,
20
+ TOURNAMENT: y,
21
+ SCRIMMAGE: S
22
+ }, k = Object.keys(r);
23
+ function A() {
24
+ let [_] = w(), { navigateTo: y } = e(), x = c({ pagination: { limit: 100 } }), S = o({ pagination: { limit: 100 } }), C = s(), [A, j] = p(_.get("homeTeamId") ?? ""), [M, N] = p(""), [P, F] = p("FRIENDLY"), [I, L] = p(""), [R, z] = p(""), [B, V] = p(""), [H, U] = p(""), [W, G] = p(""), [K, q] = p(""), [J, Y] = p(null), X = x.data?.items ?? [], Z = X.find((e) => e.id === A), Q = X.find((e) => e.id === M), { competitions: $, venues: ee } = f(() => {
25
+ let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Set();
26
+ for (let n of S.data?.items ?? []) n.competition && e.add(n.competition), n.venue && t.add(n.venue);
27
+ return {
28
+ competitions: Array.from(e).sort(),
29
+ venues: Array.from(t).sort()
30
+ };
31
+ }, [S.data]);
32
+ return x.isLoading ? /* @__PURE__ */ T(n, {
17
33
  title: "Schedule fixture",
18
- children: /* @__PURE__ */ m(r, { label: "Loading teams…" })
19
- }) : /* @__PURE__ */ m(t, {
34
+ children: /* @__PURE__ */ T(a, { label: "Loading teams…" })
35
+ }) : /* @__PURE__ */ T(n, {
20
36
  title: "Schedule fixture",
21
37
  description: "Set up a match between a home team and an optional away team.",
22
- actions: /* @__PURE__ */ h("button", {
38
+ actions: /* @__PURE__ */ E("button", {
23
39
  type: "button",
24
- onClick: () => v("/fixtures"),
40
+ onClick: () => y("/fixtures"),
25
41
  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",
26
- children: [/* @__PURE__ */ m(u, { className: "h-4 w-4" }), " Cancel"]
42
+ children: [/* @__PURE__ */ T(m, { className: "h-4 w-4" }), " Cancel"]
27
43
  }),
28
- children: /* @__PURE__ */ h("form", {
44
+ children: /* @__PURE__ */ T("form", {
29
45
  onSubmit: async (e) => {
30
- if (e.preventDefault(), B(null), !x) {
31
- B("A home team is required.");
46
+ if (e.preventDefault(), Y(null), !A) {
47
+ Y("A home team is required.");
32
48
  return;
33
49
  }
34
- if (!N) {
35
- B("A scheduled date and time is required.");
50
+ if (!H) {
51
+ Y("A scheduled date and time is required.");
36
52
  return;
37
53
  }
38
- if (C && C === x) {
39
- B("Home and away teams must be different.");
54
+ if (M && M === A) {
55
+ Y("Home and away teams must be different.");
40
56
  return;
41
57
  }
42
58
  try {
43
- v(`/fixtures/${(await b.mutateAsync({
44
- homeTeamId: x,
45
- awayTeamId: C || void 0,
46
- type: T,
47
- competition: D.trim() || void 0,
48
- round: k.trim() || void 0,
49
- venue: j.trim() || void 0,
50
- scheduledAt: new Date(N).toISOString(),
51
- kickoffAt: c(F),
52
- notes: L.trim() || void 0
59
+ y(`/fixtures/${(await C.mutateAsync({
60
+ homeTeamId: A,
61
+ awayTeamId: M || void 0,
62
+ type: P,
63
+ competition: I.trim() || void 0,
64
+ round: R.trim() || void 0,
65
+ venue: B.trim() || void 0,
66
+ scheduledAt: new Date(H).toISOString(),
67
+ kickoffAt: d(W),
68
+ notes: K.trim() || void 0
53
69
  })).id}`);
54
70
  } catch (e) {
55
- B(e instanceof Error ? e.message : "Failed to schedule fixture.");
71
+ Y(e instanceof Error ? e.message : "Failed to schedule fixture.");
56
72
  }
57
73
  },
58
- className: "max-w-2xl space-y-5",
59
- children: [
60
- /* @__PURE__ */ h("div", {
61
- className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
62
- children: [/* @__PURE__ */ m(o, {
63
- label: "Home team",
64
- htmlFor: "homeTeam",
65
- required: !0,
66
- children: /* @__PURE__ */ h("select", {
67
- id: "homeTeam",
68
- className: s,
69
- value: x,
70
- onChange: (e) => S(e.target.value),
71
- children: [/* @__PURE__ */ m("option", {
72
- value: "",
73
- children: "Select a team"
74
- }), V.map((e) => /* @__PURE__ */ m("option", {
75
- value: e.id,
76
- children: e.name
77
- }, e.id))]
74
+ className: "mx-auto max-w-3xl",
75
+ children: /* @__PURE__ */ E(D, {
76
+ treatment: "insight",
77
+ className: "p-5 sm:p-7",
78
+ children: [
79
+ /* @__PURE__ */ E("div", {
80
+ className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
81
+ children: [/* @__PURE__ */ E(l, {
82
+ label: "Home team",
83
+ htmlFor: "homeTeam",
84
+ required: !0,
85
+ children: [/* @__PURE__ */ E("select", {
86
+ id: "homeTeam",
87
+ className: u,
88
+ value: A,
89
+ onChange: (e) => j(e.target.value),
90
+ children: [/* @__PURE__ */ T("option", {
91
+ value: "",
92
+ children: "Select a team"
93
+ }), X.map((e) => /* @__PURE__ */ T("option", {
94
+ value: e.id,
95
+ children: e.name
96
+ }, e.id))]
97
+ }), Z ? /* @__PURE__ */ E("div", {
98
+ className: "mt-2 flex items-center gap-2",
99
+ children: [/* @__PURE__ */ T(i, {
100
+ name: Z.name,
101
+ sport: Z.sport,
102
+ size: 28
103
+ }), /* @__PURE__ */ T("span", {
104
+ className: "truncate text-xs font-medium text-text-secondary",
105
+ children: Z.name
106
+ })]
107
+ }) : null]
108
+ }), /* @__PURE__ */ E(l, {
109
+ label: "Away team",
110
+ htmlFor: "awayTeam",
111
+ hint: "Optional for trials / open events.",
112
+ children: [/* @__PURE__ */ E("select", {
113
+ id: "awayTeam",
114
+ className: u,
115
+ value: M,
116
+ onChange: (e) => N(e.target.value),
117
+ children: [/* @__PURE__ */ T("option", {
118
+ value: "",
119
+ children: "No away team"
120
+ }), X.map((e) => /* @__PURE__ */ T("option", {
121
+ value: e.id,
122
+ children: e.name
123
+ }, e.id))]
124
+ }), Q ? /* @__PURE__ */ E("div", {
125
+ className: "mt-2 flex items-center gap-2",
126
+ children: [/* @__PURE__ */ T(i, {
127
+ name: Q.name,
128
+ sport: Q.sport,
129
+ size: 28
130
+ }), /* @__PURE__ */ T("span", {
131
+ className: "truncate text-xs font-medium text-text-secondary",
132
+ children: Q.name
133
+ })]
134
+ }) : null]
135
+ })]
136
+ }),
137
+ /* @__PURE__ */ E("div", {
138
+ className: "mt-5",
139
+ children: [/* @__PURE__ */ T("span", {
140
+ className: "mb-1.5 block text-sm font-medium text-text-primary",
141
+ children: "Type"
142
+ }), /* @__PURE__ */ T("div", {
143
+ role: "radiogroup",
144
+ "aria-label": "Fixture type",
145
+ onKeyDown: (e) => {
146
+ let t = k.indexOf(P);
147
+ e.key === "ArrowRight" || e.key === "ArrowDown" ? (e.preventDefault(), F(k[(t + 1) % k.length])) : (e.key === "ArrowLeft" || e.key === "ArrowUp") && (e.preventDefault(), F(k[(t - 1 + k.length) % k.length]));
148
+ },
149
+ className: "grid grid-cols-2 gap-2 sm:grid-cols-5",
150
+ children: k.map((e) => {
151
+ let n = O[e], i = P === e;
152
+ return /* @__PURE__ */ E("button", {
153
+ type: "button",
154
+ role: "radio",
155
+ "aria-checked": i,
156
+ tabIndex: i ? 0 : -1,
157
+ onClick: () => F(e),
158
+ className: t("flex items-center justify-center gap-1.5 rounded-lg border px-2.5 py-2.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", i ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary ring-1 ring-inset ring-action-primary-bg" : "border-border-seam text-text-secondary hover:bg-bg-sunken"),
159
+ children: [/* @__PURE__ */ T(n, {
160
+ className: "h-4 w-4 shrink-0",
161
+ "aria-hidden": "true"
162
+ }), r[e]]
163
+ }, e);
164
+ })
165
+ })]
166
+ }),
167
+ /* @__PURE__ */ E("div", {
168
+ className: "mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2",
169
+ children: [/* @__PURE__ */ E(l, {
170
+ label: "Competition",
171
+ htmlFor: "competition",
172
+ hint: "Used to group standings.",
173
+ children: [/* @__PURE__ */ T("input", {
174
+ id: "competition",
175
+ list: "ath-fixture-competitions",
176
+ className: u,
177
+ value: I,
178
+ onChange: (e) => L(e.target.value),
179
+ placeholder: "e.g. Spring League 2026"
180
+ }), /* @__PURE__ */ T("datalist", {
181
+ id: "ath-fixture-competitions",
182
+ children: $.map((e) => /* @__PURE__ */ T("option", { value: e }, e))
183
+ })]
184
+ }), /* @__PURE__ */ T(l, {
185
+ label: "Round",
186
+ htmlFor: "round",
187
+ children: /* @__PURE__ */ T("input", {
188
+ id: "round",
189
+ className: u,
190
+ value: R,
191
+ onChange: (e) => z(e.target.value),
192
+ placeholder: "e.g. Round 4"
193
+ })
194
+ })]
195
+ }),
196
+ /* @__PURE__ */ T("div", {
197
+ className: "mt-5",
198
+ children: /* @__PURE__ */ E(l, {
199
+ label: "Venue",
200
+ htmlFor: "venue",
201
+ children: [/* @__PURE__ */ T("input", {
202
+ id: "venue",
203
+ list: "ath-fixture-venues",
204
+ className: u,
205
+ value: B,
206
+ onChange: (e) => V(e.target.value),
207
+ placeholder: "e.g. Riverside Stadium"
208
+ }), /* @__PURE__ */ T("datalist", {
209
+ id: "ath-fixture-venues",
210
+ children: ee.map((e) => /* @__PURE__ */ T("option", { value: e }, e))
211
+ })]
78
212
  })
79
- }), /* @__PURE__ */ m(o, {
80
- label: "Away team",
81
- htmlFor: "awayTeam",
82
- hint: "Optional for trials / open events.",
83
- children: /* @__PURE__ */ h("select", {
84
- id: "awayTeam",
85
- className: s,
86
- value: C,
87
- onChange: (e) => w(e.target.value),
88
- children: [/* @__PURE__ */ m("option", {
89
- value: "",
90
- children: "No away team"
91
- }), V.map((e) => /* @__PURE__ */ m("option", {
92
- value: e.id,
93
- children: e.name
94
- }, e.id))]
213
+ }),
214
+ /* @__PURE__ */ E("div", {
215
+ className: "mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2",
216
+ children: [/* @__PURE__ */ T(l, {
217
+ label: "Scheduled at",
218
+ htmlFor: "scheduledAt",
219
+ required: !0,
220
+ children: /* @__PURE__ */ E("div", {
221
+ className: "relative",
222
+ children: [/* @__PURE__ */ T(h, {
223
+ className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary",
224
+ "aria-hidden": "true"
225
+ }), /* @__PURE__ */ T("input", {
226
+ id: "scheduledAt",
227
+ type: "datetime-local",
228
+ className: t(u, "pl-9"),
229
+ value: H,
230
+ onChange: (e) => U(e.target.value)
231
+ })]
232
+ })
233
+ }), /* @__PURE__ */ T(l, {
234
+ label: "Kick-off at",
235
+ htmlFor: "kickoffAt",
236
+ hint: "Optional refinement.",
237
+ children: /* @__PURE__ */ E("div", {
238
+ className: "relative",
239
+ children: [/* @__PURE__ */ T(g, {
240
+ className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary",
241
+ "aria-hidden": "true"
242
+ }), /* @__PURE__ */ T("input", {
243
+ id: "kickoffAt",
244
+ type: "datetime-local",
245
+ className: t(u, "pl-9"),
246
+ value: W,
247
+ onChange: (e) => G(e.target.value)
248
+ })]
249
+ })
250
+ })]
251
+ }),
252
+ /* @__PURE__ */ T("div", {
253
+ className: "mt-5",
254
+ children: /* @__PURE__ */ T(l, {
255
+ label: "Notes",
256
+ htmlFor: "notes",
257
+ children: /* @__PURE__ */ T("textarea", {
258
+ id: "notes",
259
+ className: u,
260
+ rows: 3,
261
+ value: K,
262
+ onChange: (e) => q(e.target.value),
263
+ placeholder: "Any extra context for this fixture."
264
+ })
95
265
  })
96
- })]
97
- }),
98
- /* @__PURE__ */ m(o, {
99
- label: "Type",
100
- htmlFor: "type",
101
- children: /* @__PURE__ */ m("select", {
102
- id: "type",
103
- className: s,
104
- value: T,
105
- onChange: (e) => E(e.target.value),
106
- children: g.map((e) => /* @__PURE__ */ m("option", {
107
- value: e,
108
- children: n[e]
109
- }, e))
266
+ }),
267
+ J && /* @__PURE__ */ T("p", {
268
+ className: "mt-5 text-sm text-status-error-text",
269
+ children: J
270
+ }),
271
+ /* @__PURE__ */ E("div", {
272
+ className: "mt-6 flex items-center justify-end gap-3 border-t border-border-seam pt-5",
273
+ children: [/* @__PURE__ */ T("button", {
274
+ type: "button",
275
+ onClick: () => y("/fixtures"),
276
+ className: "rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
277
+ children: "Cancel"
278
+ }), /* @__PURE__ */ E("button", {
279
+ type: "submit",
280
+ disabled: C.isPending,
281
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
282
+ children: [C.isPending ? /* @__PURE__ */ T(v, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ T(b, { className: "h-4 w-4" }), "Schedule fixture"]
283
+ })]
110
284
  })
111
- }),
112
- /* @__PURE__ */ h("div", {
113
- className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
114
- children: [/* @__PURE__ */ m(o, {
115
- label: "Competition",
116
- htmlFor: "competition",
117
- hint: "Used to group standings.",
118
- children: /* @__PURE__ */ m("input", {
119
- id: "competition",
120
- className: s,
121
- value: D,
122
- onChange: (e) => O(e.target.value),
123
- placeholder: "e.g. Spring League 2026"
124
- })
125
- }), /* @__PURE__ */ m(o, {
126
- label: "Round",
127
- htmlFor: "round",
128
- children: /* @__PURE__ */ m("input", {
129
- id: "round",
130
- className: s,
131
- value: k,
132
- onChange: (e) => A(e.target.value),
133
- placeholder: "e.g. Round 4"
134
- })
135
- })]
136
- }),
137
- /* @__PURE__ */ m(o, {
138
- label: "Venue",
139
- htmlFor: "venue",
140
- children: /* @__PURE__ */ m("input", {
141
- id: "venue",
142
- className: s,
143
- value: j,
144
- onChange: (e) => M(e.target.value),
145
- placeholder: "e.g. Riverside Stadium"
146
- })
147
- }),
148
- /* @__PURE__ */ h("div", {
149
- className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
150
- children: [/* @__PURE__ */ m(o, {
151
- label: "Scheduled at",
152
- htmlFor: "scheduledAt",
153
- required: !0,
154
- children: /* @__PURE__ */ m("input", {
155
- id: "scheduledAt",
156
- type: "datetime-local",
157
- className: s,
158
- value: N,
159
- onChange: (e) => P(e.target.value)
160
- })
161
- }), /* @__PURE__ */ m(o, {
162
- label: "Kick-off at",
163
- htmlFor: "kickoffAt",
164
- children: /* @__PURE__ */ m("input", {
165
- id: "kickoffAt",
166
- type: "datetime-local",
167
- className: s,
168
- value: F,
169
- onChange: (e) => I(e.target.value)
170
- })
171
- })]
172
- }),
173
- /* @__PURE__ */ m(o, {
174
- label: "Notes",
175
- htmlFor: "notes",
176
- children: /* @__PURE__ */ m("textarea", {
177
- id: "notes",
178
- className: s,
179
- rows: 3,
180
- value: L,
181
- onChange: (e) => R(e.target.value),
182
- placeholder: "Any extra context for this fixture."
183
- })
184
- }),
185
- z && /* @__PURE__ */ m("p", {
186
- className: "text-sm text-status-error-text",
187
- children: z
188
- }),
189
- /* @__PURE__ */ m("div", {
190
- className: "pt-2",
191
- children: /* @__PURE__ */ h("button", {
192
- type: "submit",
193
- disabled: b.isPending,
194
- 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",
195
- children: [b.isPending ? /* @__PURE__ */ m(d, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ m(f, { className: "h-4 w-4" }), "Schedule fixture"]
196
- })
197
- })
198
- ]
285
+ ]
286
+ })
199
287
  })
200
288
  });
201
289
  }
202
290
  //#endregion
203
- export { _ as FixtureFormPage };
291
+ export { A as FixtureFormPage };
204
292
 
205
293
  //# sourceMappingURL=FixtureFormPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FixtureFormPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/fixtures/FixtureFormPage.tsx"],"sourcesContent":["/**\n * Fixture Schedule Form\n * @module atheletebridge/pages/fixtures\n */\n\nimport { useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { LoadingState } from '../../components/LoadingState';\nimport { FIXTURE_TYPE_LABELS } from '../../components/domain';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useScheduleFixture, useTeams } from '../../hooks/useAthleteBridgeApi';\nimport { toIsoOrUndefined } from '../../utils/datetime';\nimport type { AthFixtureType } from '../../types';\n\nconst TYPES = Object.keys(FIXTURE_TYPE_LABELS) as AthFixtureType[];\n\nexport function FixtureFormPage() {\n const [searchParams] = useSearchParams();\n const { navigateTo } = useAthleteBridge();\n\n const teams = useTeams({ pagination: { limit: 100 } });\n const scheduleFixture = useScheduleFixture();\n\n const [homeTeamId, setHomeTeamId] = useState(searchParams.get('homeTeamId') ?? '');\n const [awayTeamId, setAwayTeamId] = useState('');\n const [type, setType] = useState<AthFixtureType>('FRIENDLY');\n const [competition, setCompetition] = useState('');\n const [round, setRound] = useState('');\n const [venue, setVenue] = useState('');\n const [scheduledAt, setScheduledAt] = useState('');\n const [kickoffAt, setKickoffAt] = useState('');\n const [notes, setNotes] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const teamOptions = teams.data?.items ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!homeTeamId) {\n setError('A home team is required.');\n return;\n }\n if (!scheduledAt) {\n setError('A scheduled date and time is required.');\n return;\n }\n if (awayTeamId && awayTeamId === homeTeamId) {\n setError('Home and away teams must be different.');\n return;\n }\n\n try {\n const created = await scheduleFixture.mutateAsync({\n homeTeamId,\n awayTeamId: awayTeamId || undefined,\n type,\n competition: competition.trim() || undefined,\n round: round.trim() || undefined,\n venue: venue.trim() || undefined,\n scheduledAt: new Date(scheduledAt).toISOString(),\n kickoffAt: toIsoOrUndefined(kickoffAt),\n notes: notes.trim() || undefined,\n });\n navigateTo(`/fixtures/${created.id}`);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to schedule fixture.');\n }\n };\n\n if (teams.isLoading) {\n return (\n <PageLayout title=\"Schedule fixture\">\n <LoadingState label=\"Loading teams…\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Schedule fixture\"\n description=\"Set up a match between a home team and an optional away team.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\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=\"Home team\" htmlFor=\"homeTeam\" required>\n <select\n id=\"homeTeam\"\n className={fieldInputClass}\n value={homeTeamId}\n onChange={(e) => setHomeTeamId(e.target.value)}\n >\n <option value=\"\">Select a team</option>\n {teamOptions.map((tm) => (\n <option key={tm.id} value={tm.id}>\n {tm.name}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField label=\"Away team\" htmlFor=\"awayTeam\" hint=\"Optional for trials / open events.\">\n <select\n id=\"awayTeam\"\n className={fieldInputClass}\n value={awayTeamId}\n onChange={(e) => setAwayTeamId(e.target.value)}\n >\n <option value=\"\">No away team</option>\n {teamOptions.map((tm) => (\n <option key={tm.id} value={tm.id}>\n {tm.name}\n </option>\n ))}\n </select>\n </FormField>\n </div>\n\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 AthFixtureType)}\n >\n {TYPES.map((tp) => (\n <option key={tp} value={tp}>\n {FIXTURE_TYPE_LABELS[tp]}\n </option>\n ))}\n </select>\n </FormField>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Competition\" htmlFor=\"competition\" hint=\"Used to group standings.\">\n <input\n id=\"competition\"\n className={fieldInputClass}\n value={competition}\n onChange={(e) => setCompetition(e.target.value)}\n placeholder=\"e.g. Spring League 2026\"\n />\n </FormField>\n\n <FormField label=\"Round\" htmlFor=\"round\">\n <input\n id=\"round\"\n className={fieldInputClass}\n value={round}\n onChange={(e) => setRound(e.target.value)}\n placeholder=\"e.g. Round 4\"\n />\n </FormField>\n </div>\n\n <FormField label=\"Venue\" htmlFor=\"venue\">\n <input\n id=\"venue\"\n className={fieldInputClass}\n value={venue}\n onChange={(e) => setVenue(e.target.value)}\n placeholder=\"e.g. Riverside Stadium\"\n />\n </FormField>\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\n <FormField label=\"Kick-off at\" htmlFor=\"kickoffAt\">\n <input\n id=\"kickoffAt\"\n type=\"datetime-local\"\n className={fieldInputClass}\n value={kickoffAt}\n onChange={(e) => setKickoffAt(e.target.value)}\n />\n </FormField>\n </div>\n\n <FormField label=\"Notes\" htmlFor=\"notes\">\n <textarea\n id=\"notes\"\n className={fieldInputClass}\n rows={3}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Any extra context for this fixture.\"\n />\n </FormField>\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={scheduleFixture.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 {scheduleFixture.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Schedule fixture\n </button>\n </div>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FixtureFormPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/fixtures/FixtureFormPage.tsx"],"sourcesContent":["/**\n * Fixture Schedule Form\n * @module atheletebridge/pages/fixtures\n *\n * Mirrors the concept mock: a GlassCard-framed two-column form with a segmented\n * fixture-type control (all five AthFixtureType values incl. SCRIMMAGE), team\n * crest previews, competition/venue comboboxes seeded from existing fixtures,\n * and iconized date/time inputs. Every field maps to AthScheduleFixtureInput.\n * Semantic tokens only, responsive.\n */\n\nimport { type KeyboardEvent, useMemo, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Loader2,\n Save,\n Calendar,\n Clock,\n Handshake,\n Shield,\n Trophy,\n Network,\n Swords,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { LoadingState } from '../../components/LoadingState';\nimport { Crest } from '../../components/Crest';\nimport { FIXTURE_TYPE_LABELS } from '../../components/domain';\nimport { cn } from '../../utils/cn';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useScheduleFixture, useTeams, useFixtures } from '../../hooks/useAthleteBridgeApi';\nimport { toIsoOrUndefined } from '../../utils/datetime';\nimport type { AthFixtureType } from '../../types';\n\nconst TYPE_ICONS: Record<AthFixtureType, LucideIcon> = {\n FRIENDLY: Handshake,\n LEAGUE: Shield,\n CUP: Trophy,\n TOURNAMENT: Network,\n SCRIMMAGE: Swords,\n};\nconst TYPES = Object.keys(FIXTURE_TYPE_LABELS) as AthFixtureType[];\n\nexport function FixtureFormPage() {\n const [searchParams] = useSearchParams();\n const { navigateTo } = useAthleteBridge();\n\n const teams = useTeams({ pagination: { limit: 100 } });\n const existingFixtures = useFixtures({ pagination: { limit: 100 } });\n const scheduleFixture = useScheduleFixture();\n\n const [homeTeamId, setHomeTeamId] = useState(searchParams.get('homeTeamId') ?? '');\n const [awayTeamId, setAwayTeamId] = useState('');\n const [type, setType] = useState<AthFixtureType>('FRIENDLY');\n const [competition, setCompetition] = useState('');\n const [round, setRound] = useState('');\n const [venue, setVenue] = useState('');\n const [scheduledAt, setScheduledAt] = useState('');\n const [kickoffAt, setKickoffAt] = useState('');\n const [notes, setNotes] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const teamOptions = teams.data?.items ?? [];\n const homeTeam = teamOptions.find((t) => t.id === homeTeamId);\n const awayTeam = teamOptions.find((t) => t.id === awayTeamId);\n\n // Seed comboboxes from distinct values on existing fixtures (honest, derived).\n const { competitions, venues } = useMemo(() => {\n const comp = new Set<string>();\n const ven = new Set<string>();\n for (const f of existingFixtures.data?.items ?? []) {\n if (f.competition) comp.add(f.competition);\n if (f.venue) ven.add(f.venue);\n }\n return {\n competitions: Array.from(comp).sort(),\n venues: Array.from(ven).sort(),\n };\n }, [existingFixtures.data]);\n\n const handleTypeKey = (e: KeyboardEvent<HTMLDivElement>) => {\n const idx = TYPES.indexOf(type);\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault();\n setType(TYPES[(idx + 1) % TYPES.length]);\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault();\n setType(TYPES[(idx - 1 + TYPES.length) % TYPES.length]);\n }\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!homeTeamId) {\n setError('A home team is required.');\n return;\n }\n if (!scheduledAt) {\n setError('A scheduled date and time is required.');\n return;\n }\n if (awayTeamId && awayTeamId === homeTeamId) {\n setError('Home and away teams must be different.');\n return;\n }\n\n try {\n const created = await scheduleFixture.mutateAsync({\n homeTeamId,\n awayTeamId: awayTeamId || undefined,\n type,\n competition: competition.trim() || undefined,\n round: round.trim() || undefined,\n venue: venue.trim() || undefined,\n scheduledAt: new Date(scheduledAt).toISOString(),\n kickoffAt: toIsoOrUndefined(kickoffAt),\n notes: notes.trim() || undefined,\n });\n navigateTo(`/fixtures/${created.id}`);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to schedule fixture.');\n }\n };\n\n if (teams.isLoading) {\n return (\n <PageLayout title=\"Schedule fixture\">\n <LoadingState label=\"Loading teams…\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Schedule fixture\"\n description=\"Set up a match between a home team and an optional away team.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\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=\"mx-auto max-w-3xl\">\n <GlassCard treatment=\"insight\" className=\"p-5 sm:p-7\">\n {/* Home / away teams with crest preview */}\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Home team\" htmlFor=\"homeTeam\" required>\n <select\n id=\"homeTeam\"\n className={fieldInputClass}\n value={homeTeamId}\n onChange={(e) => setHomeTeamId(e.target.value)}\n >\n <option value=\"\">Select a team</option>\n {teamOptions.map((tm) => (\n <option key={tm.id} value={tm.id}>\n {tm.name}\n </option>\n ))}\n </select>\n {homeTeam ? (\n <div className=\"mt-2 flex items-center gap-2\">\n <Crest name={homeTeam.name} sport={homeTeam.sport} size={28} />\n <span className=\"truncate text-xs font-medium text-text-secondary\">\n {homeTeam.name}\n </span>\n </div>\n ) : null}\n </FormField>\n\n <FormField label=\"Away team\" htmlFor=\"awayTeam\" hint=\"Optional for trials / open events.\">\n <select\n id=\"awayTeam\"\n className={fieldInputClass}\n value={awayTeamId}\n onChange={(e) => setAwayTeamId(e.target.value)}\n >\n <option value=\"\">No away team</option>\n {teamOptions.map((tm) => (\n <option key={tm.id} value={tm.id}>\n {tm.name}\n </option>\n ))}\n </select>\n {awayTeam ? (\n <div className=\"mt-2 flex items-center gap-2\">\n <Crest name={awayTeam.name} sport={awayTeam.sport} size={28} />\n <span className=\"truncate text-xs font-medium text-text-secondary\">\n {awayTeam.name}\n </span>\n </div>\n ) : null}\n </FormField>\n </div>\n\n {/* Type — segmented control (all five values incl. SCRIMMAGE) */}\n <div className=\"mt-5\">\n <span className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</span>\n <div\n role=\"radiogroup\"\n aria-label=\"Fixture type\"\n onKeyDown={handleTypeKey}\n className=\"grid grid-cols-2 gap-2 sm:grid-cols-5\"\n >\n {TYPES.map((tp) => {\n const Icon = TYPE_ICONS[tp];\n const active = type === tp;\n return (\n <button\n key={tp}\n type=\"button\"\n role=\"radio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => setType(tp)}\n className={cn(\n 'flex items-center justify-center gap-1.5 rounded-lg border px-2.5 py-2.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n active\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary ring-1 ring-inset ring-action-primary-bg'\n : 'border-border-seam text-text-secondary hover:bg-bg-sunken'\n )}\n >\n <Icon className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n {FIXTURE_TYPE_LABELS[tp]}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Competition / round */}\n <div className=\"mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Competition\" htmlFor=\"competition\" hint=\"Used to group standings.\">\n <input\n id=\"competition\"\n list=\"ath-fixture-competitions\"\n className={fieldInputClass}\n value={competition}\n onChange={(e) => setCompetition(e.target.value)}\n placeholder=\"e.g. Spring League 2026\"\n />\n <datalist id=\"ath-fixture-competitions\">\n {competitions.map((c) => (\n <option key={c} value={c} />\n ))}\n </datalist>\n </FormField>\n\n <FormField label=\"Round\" htmlFor=\"round\">\n <input\n id=\"round\"\n className={fieldInputClass}\n value={round}\n onChange={(e) => setRound(e.target.value)}\n placeholder=\"e.g. Round 4\"\n />\n </FormField>\n </div>\n\n {/* Venue */}\n <div className=\"mt-5\">\n <FormField label=\"Venue\" htmlFor=\"venue\">\n <input\n id=\"venue\"\n list=\"ath-fixture-venues\"\n className={fieldInputClass}\n value={venue}\n onChange={(e) => setVenue(e.target.value)}\n placeholder=\"e.g. Riverside Stadium\"\n />\n <datalist id=\"ath-fixture-venues\">\n {venues.map((v) => (\n <option key={v} value={v} />\n ))}\n </datalist>\n </FormField>\n </div>\n\n {/* Schedule / kickoff */}\n <div className=\"mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label=\"Scheduled at\" htmlFor=\"scheduledAt\" required>\n <div className=\"relative\">\n <Calendar\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <input\n id=\"scheduledAt\"\n type=\"datetime-local\"\n className={cn(fieldInputClass, 'pl-9')}\n value={scheduledAt}\n onChange={(e) => setScheduledAt(e.target.value)}\n />\n </div>\n </FormField>\n\n <FormField label=\"Kick-off at\" htmlFor=\"kickoffAt\" hint=\"Optional refinement.\">\n <div className=\"relative\">\n <Clock\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <input\n id=\"kickoffAt\"\n type=\"datetime-local\"\n className={cn(fieldInputClass, 'pl-9')}\n value={kickoffAt}\n onChange={(e) => setKickoffAt(e.target.value)}\n />\n </div>\n </FormField>\n </div>\n\n {/* Notes */}\n <div className=\"mt-5\">\n <FormField label=\"Notes\" htmlFor=\"notes\">\n <textarea\n id=\"notes\"\n className={fieldInputClass}\n rows={3}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Any extra context for this fixture.\"\n />\n </FormField>\n </div>\n\n {error && <p className=\"mt-5 text-sm text-status-error-text\">{error}</p>}\n\n {/* Footer */}\n <div className=\"mt-6 flex items-center justify-end gap-3 border-t border-border-seam pt-5\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\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={scheduleFixture.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {scheduleFixture.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n Schedule fixture\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}