@burdenoff/microfe-atheletebridge 2026.827.1 → 2026.829.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atheletebridge/components/LoadingState.d.ts.map +1 -1
- package/dist/atheletebridge/components/LoadingState.js +39 -32
- package/dist/atheletebridge/components/LoadingState.js.map +1 -1
- package/dist/atheletebridge/components/TeamCard.d.ts.map +1 -1
- package/dist/atheletebridge/components/TeamCard.js +45 -49
- package/dist/atheletebridge/components/TeamCard.js.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.js +56 -24
- package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js +58 -56
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +110 -106
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +80 -71
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -1
- package/dist/atheletebridge/pages/onboarding/OnboardingPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/onboarding/OnboardingPage.js +244 -210
- package/dist/atheletebridge/pages/onboarding/OnboardingPage.js.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.js +197 -148
- package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +169 -143
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js +77 -72
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +104 -86
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js +54 -49
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js +286 -267
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamFormPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamFormPage.js +185 -180
- package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamsListPage.js +70 -74
- package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,202 +1,206 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
1
|
+
import { useAbTranslate as e } from "../../i18n.js";
|
|
2
|
+
import { useAthleteBridge as t } from "../../context/AthleteBridgeContext.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 { Scoreboard as a } from "../../components/Scoreboard.js";
|
|
7
|
+
import { ErrorState as o, LoadingState as s } from "../../components/LoadingState.js";
|
|
8
|
+
import { useFixture as c, useRecordFixtureResult as l } from "../../hooks/useAthleteBridgeApi.js";
|
|
9
|
+
import { FormField as u, fieldInputClass as d } from "../../components/form.js";
|
|
10
|
+
import { AvailabilityRsvpControl as f } from "../../components/availability/AvailabilityRsvpControl.js";
|
|
11
|
+
import { AvailabilityRosterPanel as p } from "../../components/availability/AvailabilityRosterPanel.js";
|
|
12
|
+
import { useState as m } from "react";
|
|
13
|
+
import { ArrowLeft as h, Flag as g, Loader2 as _, Minus as v, Trophy as y } from "lucide-react";
|
|
14
|
+
import { useParams as b } from "react-router";
|
|
15
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
16
|
+
import { EmphasisPanel as C, GlassCard as w } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/atheletebridge/pages/fixtures/FixtureDetailPage.tsx
|
|
17
|
-
function
|
|
18
|
-
let { id:
|
|
19
|
-
if (
|
|
20
|
-
title: "Fixture",
|
|
21
|
-
children: /* @__PURE__ */
|
|
18
|
+
function T() {
|
|
19
|
+
let T = e(), { id: E } = b(), { navigateTo: D } = t(), O = c(E), k = l(), [A, j] = m(""), [M, N] = m(""), [P, F] = m(null);
|
|
20
|
+
if (O.isLoading) return /* @__PURE__ */ x(n, {
|
|
21
|
+
title: T("atheletebridge.pages.fixtureDetail.title", "Fixture"),
|
|
22
|
+
children: /* @__PURE__ */ x(s, { label: T("atheletebridge.pages.fixtureDetail.loading", "Loading fixture…") })
|
|
22
23
|
});
|
|
23
|
-
if (
|
|
24
|
-
title: "Fixture",
|
|
25
|
-
children: /* @__PURE__ */
|
|
26
|
-
title: "Fixture not found",
|
|
27
|
-
message: "This fixture does not exist or you do not have access to it.",
|
|
28
|
-
onRetry: () =>
|
|
24
|
+
if (O.isError || !O.data) return /* @__PURE__ */ x(n, {
|
|
25
|
+
title: T("atheletebridge.pages.fixtureDetail.title", "Fixture"),
|
|
26
|
+
children: /* @__PURE__ */ x(o, {
|
|
27
|
+
title: T("atheletebridge.pages.fixtureDetail.notFoundTitle", "Fixture not found"),
|
|
28
|
+
message: T("atheletebridge.pages.fixtureDetail.notFoundMessage", "This fixture does not exist or you do not have access to it."),
|
|
29
|
+
onRetry: () => D("/fixtures")
|
|
29
30
|
})
|
|
30
31
|
});
|
|
31
|
-
let
|
|
32
|
-
return /* @__PURE__ */
|
|
33
|
-
title: `${
|
|
34
|
-
description: `${
|
|
35
|
-
actions: /* @__PURE__ */
|
|
32
|
+
let I = O.data, L = I.homeTeam?.name ?? T("atheletebridge.pages.fixtureDetail.home", "Home"), R = I.awayTeam?.name ?? (I.awayTeamId ? T("atheletebridge.pages.fixtureDetail.away", "Away") : "TBD"), z = !!I.awayTeamId && (I.status === "SCHEDULED" || I.status === "LIVE");
|
|
33
|
+
return /* @__PURE__ */ S(n, {
|
|
34
|
+
title: `${L} vs ${R}`,
|
|
35
|
+
description: `${r[I.type]}${I.competition ? ` · ${I.competition}` : ""}`,
|
|
36
|
+
actions: /* @__PURE__ */ S("button", {
|
|
36
37
|
type: "button",
|
|
37
|
-
onClick: () =>
|
|
38
|
+
onClick: () => D("/fixtures"),
|
|
38
39
|
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",
|
|
39
|
-
children: [
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ x(h, { className: "h-4 w-4" }),
|
|
42
|
+
" ",
|
|
43
|
+
T("atheletebridge.pages.fixtureDetail.back", "Back")
|
|
44
|
+
]
|
|
40
45
|
}),
|
|
41
46
|
children: [
|
|
42
|
-
/* @__PURE__ */
|
|
43
|
-
fixture:
|
|
47
|
+
/* @__PURE__ */ x(a, {
|
|
48
|
+
fixture: I,
|
|
44
49
|
variant: "hero",
|
|
45
50
|
className: "mb-8"
|
|
46
51
|
}),
|
|
47
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ S("div", {
|
|
48
53
|
className: "mb-8 grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1fr)_2fr]",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
50
|
-
teamId:
|
|
51
|
-
fixtureId:
|
|
52
|
-
}), /* @__PURE__ */
|
|
53
|
-
teamId:
|
|
54
|
-
fixtureId:
|
|
54
|
+
children: [/* @__PURE__ */ x(f, {
|
|
55
|
+
teamId: I.homeTeamId,
|
|
56
|
+
fixtureId: I.id
|
|
57
|
+
}), /* @__PURE__ */ x(p, {
|
|
58
|
+
teamId: I.homeTeamId,
|
|
59
|
+
fixtureId: I.id
|
|
55
60
|
})]
|
|
56
61
|
}),
|
|
57
|
-
|
|
62
|
+
I.notes && /* @__PURE__ */ S(C, {
|
|
58
63
|
className: "mb-8",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
64
|
+
children: [/* @__PURE__ */ x("h2", {
|
|
60
65
|
className: "mb-2 text-sm font-semibold text-text-primary",
|
|
61
|
-
children: "Notes"
|
|
62
|
-
}), /* @__PURE__ */
|
|
66
|
+
children: T("atheletebridge.pages.fixtureDetail.notes", "Notes")
|
|
67
|
+
}), /* @__PURE__ */ x("p", {
|
|
63
68
|
className: "text-sm text-text-secondary",
|
|
64
|
-
children:
|
|
69
|
+
children: I.notes
|
|
65
70
|
})]
|
|
66
71
|
}),
|
|
67
|
-
|
|
72
|
+
z && /* @__PURE__ */ S("section", {
|
|
68
73
|
className: "mb-8",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
74
|
+
children: [/* @__PURE__ */ S("h2", {
|
|
70
75
|
className: "mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary",
|
|
71
|
-
children: [
|
|
72
|
-
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ x(g, { className: "h-5 w-5" }),
|
|
78
|
+
" ",
|
|
79
|
+
T("atheletebridge.pages.fixtureDetail.recordResult", "Record result")
|
|
80
|
+
]
|
|
81
|
+
}), /* @__PURE__ */ S(w, {
|
|
73
82
|
treatment: "insight",
|
|
74
83
|
className: "p-5 sm:p-6",
|
|
75
84
|
children: [
|
|
76
85
|
/* @__PURE__ */ x("p", {
|
|
77
86
|
className: "mb-5 text-sm text-text-secondary",
|
|
78
|
-
children:
|
|
79
|
-
"Recording a final score marks this fixture ",
|
|
80
|
-
/* @__PURE__ */ b("strong", { children: "Completed" }),
|
|
81
|
-
" and recomputes the competition standings for both teams."
|
|
82
|
-
]
|
|
87
|
+
children: T("atheletebridge.pages.fixtureDetail.recordResultDescription", "Recording a final score marks this fixture Completed and recomputes the competition standings for both teams.")
|
|
83
88
|
}),
|
|
84
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ S("form", {
|
|
85
90
|
onSubmit: async (e) => {
|
|
86
|
-
if (e.preventDefault(),
|
|
87
|
-
let t = Number.parseInt(
|
|
91
|
+
if (e.preventDefault(), F(null), !E) return;
|
|
92
|
+
let t = Number.parseInt(A, 10), n = Number.parseInt(M, 10);
|
|
88
93
|
if (Number.isNaN(t) || Number.isNaN(n)) {
|
|
89
|
-
|
|
94
|
+
F("Both scores are required and must be numbers.");
|
|
90
95
|
return;
|
|
91
96
|
}
|
|
92
97
|
if (t < 0 || n < 0) {
|
|
93
|
-
|
|
98
|
+
F("Scores cannot be negative.");
|
|
94
99
|
return;
|
|
95
100
|
}
|
|
96
101
|
try {
|
|
97
|
-
await
|
|
98
|
-
id:
|
|
102
|
+
await k.mutateAsync({
|
|
103
|
+
id: E,
|
|
99
104
|
input: {
|
|
100
105
|
homeScore: t,
|
|
101
106
|
awayScore: n
|
|
102
107
|
}
|
|
103
|
-
}),
|
|
108
|
+
}), j(""), N("");
|
|
104
109
|
} catch (e) {
|
|
105
|
-
|
|
110
|
+
F(e instanceof Error ? e.message : "Failed to record result.");
|
|
106
111
|
}
|
|
107
112
|
},
|
|
108
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ S("div", {
|
|
109
114
|
className: "flex flex-wrap items-end justify-center gap-4 sm:gap-6",
|
|
110
115
|
children: [
|
|
111
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ S("div", {
|
|
112
117
|
className: "flex items-end gap-3",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
118
|
+
children: [/* @__PURE__ */ S("div", {
|
|
114
119
|
className: "flex flex-col items-center gap-2",
|
|
115
|
-
children: [/* @__PURE__ */
|
|
116
|
-
name:
|
|
117
|
-
sport:
|
|
120
|
+
children: [/* @__PURE__ */ x(i, {
|
|
121
|
+
name: L,
|
|
122
|
+
sport: I.homeTeam?.sport,
|
|
118
123
|
size: 40
|
|
119
|
-
}), /* @__PURE__ */
|
|
124
|
+
}), /* @__PURE__ */ x("span", {
|
|
120
125
|
className: "max-w-[8rem] truncate text-center text-xs font-medium text-text-secondary",
|
|
121
|
-
children:
|
|
126
|
+
children: L
|
|
122
127
|
})]
|
|
123
|
-
}), /* @__PURE__ */
|
|
124
|
-
label: "Home",
|
|
128
|
+
}), /* @__PURE__ */ x(u, {
|
|
129
|
+
label: T("atheletebridge.pages.fixtureDetail.home", "Home"),
|
|
125
130
|
htmlFor: "homeScore",
|
|
126
131
|
className: "w-24",
|
|
127
|
-
children: /* @__PURE__ */
|
|
132
|
+
children: /* @__PURE__ */ x("input", {
|
|
128
133
|
id: "homeScore",
|
|
129
134
|
inputMode: "numeric",
|
|
130
|
-
className: `${
|
|
131
|
-
value:
|
|
132
|
-
onChange: (e) =>
|
|
135
|
+
className: `${d} text-center text-lg font-bold tabular-nums`,
|
|
136
|
+
value: A,
|
|
137
|
+
onChange: (e) => j(e.target.value),
|
|
133
138
|
placeholder: "0"
|
|
134
139
|
})
|
|
135
140
|
})]
|
|
136
141
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ x(v, {
|
|
138
143
|
className: "mb-3 h-5 w-5 shrink-0 text-text-muted",
|
|
139
144
|
"aria-hidden": "true"
|
|
140
145
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ S("div", {
|
|
142
147
|
className: "flex items-end gap-3",
|
|
143
|
-
children: [/* @__PURE__ */
|
|
144
|
-
label: "Away",
|
|
148
|
+
children: [/* @__PURE__ */ x(u, {
|
|
149
|
+
label: T("atheletebridge.pages.fixtureDetail.away", "Away"),
|
|
145
150
|
htmlFor: "awayScore",
|
|
146
151
|
className: "w-24",
|
|
147
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ x("input", {
|
|
148
153
|
id: "awayScore",
|
|
149
154
|
inputMode: "numeric",
|
|
150
|
-
className: `${
|
|
151
|
-
value:
|
|
152
|
-
onChange: (e) =>
|
|
155
|
+
className: `${d} text-center text-lg font-bold tabular-nums`,
|
|
156
|
+
value: M,
|
|
157
|
+
onChange: (e) => N(e.target.value),
|
|
153
158
|
placeholder: "0"
|
|
154
159
|
})
|
|
155
|
-
}), /* @__PURE__ */
|
|
160
|
+
}), /* @__PURE__ */ S("div", {
|
|
156
161
|
className: "flex flex-col items-center gap-2",
|
|
157
|
-
children: [/* @__PURE__ */
|
|
158
|
-
name:
|
|
159
|
-
sport:
|
|
162
|
+
children: [/* @__PURE__ */ x(i, {
|
|
163
|
+
name: R,
|
|
164
|
+
sport: I.awayTeam?.sport,
|
|
160
165
|
size: 40
|
|
161
|
-
}), /* @__PURE__ */
|
|
166
|
+
}), /* @__PURE__ */ x("span", {
|
|
162
167
|
className: "max-w-[8rem] truncate text-center text-xs font-medium text-text-secondary",
|
|
163
|
-
children:
|
|
168
|
+
children: R
|
|
164
169
|
})]
|
|
165
170
|
})]
|
|
166
171
|
})
|
|
167
172
|
]
|
|
168
|
-
}), /* @__PURE__ */
|
|
173
|
+
}), /* @__PURE__ */ x("div", {
|
|
169
174
|
className: "mt-6 flex justify-center",
|
|
170
|
-
children: /* @__PURE__ */
|
|
175
|
+
children: /* @__PURE__ */ S("button", {
|
|
171
176
|
type: "submit",
|
|
172
|
-
disabled:
|
|
177
|
+
disabled: k.isPending,
|
|
173
178
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-6 py-2.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
|
|
174
|
-
children: [
|
|
179
|
+
children: [k.isPending ? /* @__PURE__ */ x(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ x(y, { className: "h-4 w-4" }), T("atheletebridge.pages.fixtureDetail.recordResultAction", "Record result")]
|
|
175
180
|
})
|
|
176
181
|
})]
|
|
177
182
|
}),
|
|
178
|
-
|
|
183
|
+
P && /* @__PURE__ */ x("p", {
|
|
179
184
|
className: "mt-3 text-center text-sm text-status-error-text",
|
|
180
|
-
children:
|
|
185
|
+
children: P
|
|
181
186
|
})
|
|
182
187
|
]
|
|
183
188
|
})]
|
|
184
189
|
}),
|
|
185
|
-
|
|
190
|
+
I.competition && /* @__PURE__ */ S("button", {
|
|
186
191
|
type: "button",
|
|
187
|
-
onClick: () =>
|
|
192
|
+
onClick: () => D(`/standings?competition=${encodeURIComponent(I.competition ?? "")}`),
|
|
188
193
|
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",
|
|
189
194
|
children: [
|
|
190
|
-
/* @__PURE__ */
|
|
191
|
-
"
|
|
192
|
-
|
|
193
|
-
" standings"
|
|
195
|
+
/* @__PURE__ */ x(y, { className: "h-4 w-4" }),
|
|
196
|
+
" ",
|
|
197
|
+
T("atheletebridge.pages.fixtureDetail.viewStandings", `View ${I.competition} standings`)
|
|
194
198
|
]
|
|
195
199
|
})
|
|
196
200
|
]
|
|
197
201
|
});
|
|
198
202
|
}
|
|
199
203
|
//#endregion
|
|
200
|
-
export {
|
|
204
|
+
export { T as FixtureDetailPage };
|
|
201
205
|
|
|
202
206
|
//# sourceMappingURL=FixtureDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FixtureDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/fixtures/FixtureDetailPage.tsx"],"sourcesContent":["/**\n * Fixture Detail\n * @module atheletebridge/pages/fixtures\n *\n * Reads a single fixture (athFixture) and lets the user record a final result\n * (athRecordFixtureResult) which marks it COMPLETED and recomputes standings.\n *\n * The header is a crest-vs-crest Scoreboard hero; the record-result panel shows\n * each team's crest beside its score input. Semantic tokens only, responsive.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, Trophy, Loader2, Flag, Minus } from 'lucide-react';\nimport { GlassCard, EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Scoreboard } from '../../components/Scoreboard';\nimport { Crest } from '../../components/Crest';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { FIXTURE_TYPE_LABELS } from '../../components/domain';\nimport { AvailabilityRsvpControl } from '../../components/availability/AvailabilityRsvpControl';\nimport { AvailabilityRosterPanel } from '../../components/availability/AvailabilityRosterPanel';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useFixture, useRecordFixtureResult } from '../../hooks/useAthleteBridgeApi';\n\nexport function FixtureDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n\n const fixture = useFixture(id);\n const recordResult = useRecordFixtureResult();\n\n const [homeScore, setHomeScore] = useState('');\n const [awayScore, setAwayScore] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n if (fixture.isLoading) {\n return (\n <PageLayout title=\"Fixture\">\n <LoadingState label=\"Loading fixture…\" />\n </PageLayout>\n );\n }\n\n if (fixture.isError || !fixture.data) {\n return (\n <PageLayout title=\"Fixture\">\n <ErrorState\n title=\"Fixture not found\"\n message=\"This fixture does not exist or you do not have access to it.\"\n onRetry={() => navigateTo('/fixtures')}\n />\n </PageLayout>\n );\n }\n\n const f = fixture.data;\n const homeName = f.homeTeam?.name ?? 'Home';\n const awayName = f.awayTeam?.name ?? (f.awayTeamId ? 'Away' : 'TBD');\n const canRecordResult =\n Boolean(f.awayTeamId) && (f.status === 'SCHEDULED' || f.status === 'LIVE');\n\n const handleRecord = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!id) return;\n const home = Number.parseInt(homeScore, 10);\n const away = Number.parseInt(awayScore, 10);\n if (Number.isNaN(home) || Number.isNaN(away)) {\n setError('Both scores are required and must be numbers.');\n return;\n }\n if (home < 0 || away < 0) {\n setError('Scores cannot be negative.');\n return;\n }\n try {\n await recordResult.mutateAsync({ id, input: { homeScore: home, awayScore: away } });\n setHomeScore('');\n setAwayScore('');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to record result.');\n }\n };\n\n return (\n <PageLayout\n title={`${homeName} vs ${awayName}`}\n description={`${FIXTURE_TYPE_LABELS[f.type]}${f.competition ? ` · ${f.competition}` : ''}`}\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\" /> Back\n </button>\n }\n >\n {/* Crest-vs-crest hero scoreboard */}\n <Scoreboard fixture={f} variant=\"hero\" className=\"mb-8\" />\n\n {/* Availability / RSVP (BOFF-4134) — athlete's own response + coach roster view */}\n <div className=\"mb-8 grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1fr)_2fr]\">\n <AvailabilityRsvpControl teamId={f.homeTeamId} fixtureId={f.id} />\n <AvailabilityRosterPanel teamId={f.homeTeamId} fixtureId={f.id} />\n </div>\n\n {f.notes && (\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">Notes</h2>\n <p className=\"text-sm text-text-secondary\">{f.notes}</p>\n </EmphasisPanel>\n )}\n\n {/* Record result */}\n {canRecordResult && (\n <section className=\"mb-8\">\n <h2 className=\"mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Flag className=\"h-5 w-5\" /> Record result\n </h2>\n <GlassCard treatment=\"insight\" className=\"p-5 sm:p-6\">\n <p className=\"mb-5 text-sm text-text-secondary\">\n Recording a final score marks this fixture <strong>Completed</strong> and recomputes\n the competition standings for both teams.\n </p>\n <form onSubmit={handleRecord}>\n <div className=\"flex flex-wrap items-end justify-center gap-4 sm:gap-6\">\n <div className=\"flex items-end gap-3\">\n <div className=\"flex flex-col items-center gap-2\">\n <Crest name={homeName} sport={f.homeTeam?.sport} size={40} />\n <span className=\"max-w-[8rem] truncate text-center text-xs font-medium text-text-secondary\">\n {homeName}\n </span>\n </div>\n <FormField label=\"Home\" htmlFor=\"homeScore\" className=\"w-24\">\n <input\n id=\"homeScore\"\n inputMode=\"numeric\"\n className={`${fieldInputClass} text-center text-lg font-bold tabular-nums`}\n value={homeScore}\n onChange={(e) => setHomeScore(e.target.value)}\n placeholder=\"0\"\n />\n </FormField>\n </div>\n\n <Minus className=\"mb-3 h-5 w-5 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n\n <div className=\"flex items-end gap-3\">\n <FormField label=\"Away\" htmlFor=\"awayScore\" className=\"w-24\">\n <input\n id=\"awayScore\"\n inputMode=\"numeric\"\n className={`${fieldInputClass} text-center text-lg font-bold tabular-nums`}\n value={awayScore}\n onChange={(e) => setAwayScore(e.target.value)}\n placeholder=\"0\"\n />\n </FormField>\n <div className=\"flex flex-col items-center gap-2\">\n <Crest name={awayName} sport={f.awayTeam?.sport} size={40} />\n <span className=\"max-w-[8rem] truncate text-center text-xs font-medium text-text-secondary\">\n {awayName}\n </span>\n </div>\n </div>\n </div>\n\n <div className=\"mt-6 flex justify-center\">\n <button\n type=\"submit\"\n disabled={recordResult.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-6 py-2.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {recordResult.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trophy className=\"h-4 w-4\" />\n )}\n Record result\n </button>\n </div>\n </form>\n {error && <p className=\"mt-3 text-center text-sm text-status-error-text\">{error}</p>}\n </GlassCard>\n </section>\n )}\n\n {f.competition && (\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(`/standings?competition=${encodeURIComponent(f.competition ?? '')}`)\n }\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 <Trophy className=\"h-4 w-4\" /> View {f.competition} standings\n </button>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FixtureDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/fixtures/FixtureDetailPage.tsx"],"sourcesContent":["/**\n * Fixture Detail\n * @module atheletebridge/pages/fixtures\n *\n * Reads a single fixture (athFixture) and lets the user record a final result\n * (athRecordFixtureResult) which marks it COMPLETED and recomputes standings.\n *\n * The header is a crest-vs-crest Scoreboard hero; the record-result panel shows\n * each team's crest beside its score input. Semantic tokens only, responsive.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, Trophy, Loader2, Flag, Minus } from 'lucide-react';\nimport { GlassCard, EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Scoreboard } from '../../components/Scoreboard';\nimport { Crest } from '../../components/Crest';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { FIXTURE_TYPE_LABELS } from '../../components/domain';\nimport { AvailabilityRsvpControl } from '../../components/availability/AvailabilityRsvpControl';\nimport { AvailabilityRosterPanel } from '../../components/availability/AvailabilityRosterPanel';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useFixture, useRecordFixtureResult } from '../../hooks/useAthleteBridgeApi';\nimport { useAbTranslate } from '../../i18n';\n\nexport function FixtureDetailPage() {\n const tr = useAbTranslate();\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n\n const fixture = useFixture(id);\n const recordResult = useRecordFixtureResult();\n\n const [homeScore, setHomeScore] = useState('');\n const [awayScore, setAwayScore] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n if (fixture.isLoading) {\n return (\n <PageLayout title={tr('atheletebridge.pages.fixtureDetail.title', 'Fixture')}>\n <LoadingState label={tr('atheletebridge.pages.fixtureDetail.loading', 'Loading fixture…')} />\n </PageLayout>\n );\n }\n\n if (fixture.isError || !fixture.data) {\n return (\n <PageLayout title={tr('atheletebridge.pages.fixtureDetail.title', 'Fixture')}>\n <ErrorState\n title={tr('atheletebridge.pages.fixtureDetail.notFoundTitle', 'Fixture not found')}\n message={tr(\n 'atheletebridge.pages.fixtureDetail.notFoundMessage',\n 'This fixture does not exist or you do not have access to it.'\n )}\n onRetry={() => navigateTo('/fixtures')}\n />\n </PageLayout>\n );\n }\n\n const f = fixture.data;\n const homeName = f.homeTeam?.name ?? tr('atheletebridge.pages.fixtureDetail.home', 'Home');\n const awayName = f.awayTeam?.name ?? (f.awayTeamId ? tr('atheletebridge.pages.fixtureDetail.away', 'Away') : 'TBD');\n const canRecordResult =\n Boolean(f.awayTeamId) && (f.status === 'SCHEDULED' || f.status === 'LIVE');\n\n const handleRecord = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!id) return;\n const home = Number.parseInt(homeScore, 10);\n const away = Number.parseInt(awayScore, 10);\n if (Number.isNaN(home) || Number.isNaN(away)) {\n setError('Both scores are required and must be numbers.');\n return;\n }\n if (home < 0 || away < 0) {\n setError('Scores cannot be negative.');\n return;\n }\n try {\n await recordResult.mutateAsync({ id, input: { homeScore: home, awayScore: away } });\n setHomeScore('');\n setAwayScore('');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to record result.');\n }\n };\n\n return (\n <PageLayout\n title={`${homeName} vs ${awayName}`}\n description={`${FIXTURE_TYPE_LABELS[f.type]}${f.competition ? ` · ${f.competition}` : ''}`}\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\" /> {tr('atheletebridge.pages.fixtureDetail.back', 'Back')}\n </button>\n }\n >\n {/* Crest-vs-crest hero scoreboard */}\n <Scoreboard fixture={f} variant=\"hero\" className=\"mb-8\" />\n\n {/* Availability / RSVP (BOFF-4134) — athlete's own response + coach roster view */}\n <div className=\"mb-8 grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1fr)_2fr]\">\n <AvailabilityRsvpControl teamId={f.homeTeamId} fixtureId={f.id} />\n <AvailabilityRosterPanel teamId={f.homeTeamId} fixtureId={f.id} />\n </div>\n\n {f.notes && (\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-2 text-sm font-semibold text-text-primary\">{tr('atheletebridge.pages.fixtureDetail.notes', 'Notes')}</h2>\n <p className=\"text-sm text-text-secondary\">{f.notes}</p>\n </EmphasisPanel>\n )}\n\n {/* Record result */}\n {canRecordResult && (\n <section className=\"mb-8\">\n <h2 className=\"mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Flag className=\"h-5 w-5\" /> {tr('atheletebridge.pages.fixtureDetail.recordResult', 'Record result')}\n </h2>\n <GlassCard treatment=\"insight\" className=\"p-5 sm:p-6\">\n <p className=\"mb-5 text-sm text-text-secondary\">\n {tr(\n 'atheletebridge.pages.fixtureDetail.recordResultDescription',\n 'Recording a final score marks this fixture Completed and recomputes the competition standings for both teams.'\n )}\n </p>\n <form onSubmit={handleRecord}>\n <div className=\"flex flex-wrap items-end justify-center gap-4 sm:gap-6\">\n <div className=\"flex items-end gap-3\">\n <div className=\"flex flex-col items-center gap-2\">\n <Crest name={homeName} sport={f.homeTeam?.sport} size={40} />\n <span className=\"max-w-[8rem] truncate text-center text-xs font-medium text-text-secondary\">\n {homeName}\n </span>\n </div>\n <FormField label={tr('atheletebridge.pages.fixtureDetail.home', 'Home')} htmlFor=\"homeScore\" className=\"w-24\">\n <input\n id=\"homeScore\"\n inputMode=\"numeric\"\n className={`${fieldInputClass} text-center text-lg font-bold tabular-nums`}\n value={homeScore}\n onChange={(e) => setHomeScore(e.target.value)}\n placeholder=\"0\"\n />\n </FormField>\n </div>\n\n <Minus className=\"mb-3 h-5 w-5 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n\n <div className=\"flex items-end gap-3\">\n <FormField label={tr('atheletebridge.pages.fixtureDetail.away', 'Away')} htmlFor=\"awayScore\" className=\"w-24\">\n <input\n id=\"awayScore\"\n inputMode=\"numeric\"\n className={`${fieldInputClass} text-center text-lg font-bold tabular-nums`}\n value={awayScore}\n onChange={(e) => setAwayScore(e.target.value)}\n placeholder=\"0\"\n />\n </FormField>\n <div className=\"flex flex-col items-center gap-2\">\n <Crest name={awayName} sport={f.awayTeam?.sport} size={40} />\n <span className=\"max-w-[8rem] truncate text-center text-xs font-medium text-text-secondary\">\n {awayName}\n </span>\n </div>\n </div>\n </div>\n\n <div className=\"mt-6 flex justify-center\">\n <button\n type=\"submit\"\n disabled={recordResult.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-6 py-2.5 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {recordResult.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trophy className=\"h-4 w-4\" />\n )}\n {tr('atheletebridge.pages.fixtureDetail.recordResultAction', 'Record result')}\n </button>\n </div>\n </form>\n {error && <p className=\"mt-3 text-center text-sm text-status-error-text\">{error}</p>}\n </GlassCard>\n </section>\n )}\n\n {f.competition && (\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(`/standings?competition=${encodeURIComponent(f.competition ?? '')}`)\n }\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 <Trophy className=\"h-4 w-4\" /> {tr('atheletebridge.pages.fixtureDetail.viewStandings', `View ${f.competition} standings`)}\n </button>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FixturesListPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/fixtures/FixturesListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"FixturesListPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/fixtures/FixturesListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA8BH,wBAAgB,gBAAgB,gCA0I/B"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { useAbTranslate as e } from "../../i18n.js";
|
|
2
|
+
import { useAthleteBridge as t } from "../../context/AthleteBridgeContext.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { FIXTURE_TYPE_LABELS as r, FIXTURE_TYPE_OPTIONS as i } from "../../components/domain.js";
|
|
5
|
+
import { EmptyState as a } from "../../components/EmptyState.js";
|
|
6
|
+
import { ErrorState as o, LoadingState as s } from "../../components/LoadingState.js";
|
|
7
|
+
import { useFixtures as c, useTeams as l } from "../../hooks/useAthleteBridgeApi.js";
|
|
8
|
+
import { CursorPager as u } from "../../components/CursorPager.js";
|
|
9
|
+
import { FilterSelect as d } from "../../components/FilterSelect.js";
|
|
10
|
+
import { FixtureCard as f } from "../../components/FixtureCard.js";
|
|
11
|
+
import { useMemo as p, useState as m } from "react";
|
|
12
|
+
import { CalendarDays as h, Plus as g, Trophy as _ } from "lucide-react";
|
|
13
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
13
14
|
//#region src/atheletebridge/pages/fixtures/FixturesListPage.tsx
|
|
14
|
-
var
|
|
15
|
+
var x = 12, S = [
|
|
15
16
|
{
|
|
16
17
|
value: "SCHEDULED",
|
|
17
18
|
label: "Scheduled"
|
|
@@ -32,100 +33,108 @@ var b = 12, x = [
|
|
|
32
33
|
value: "CANCELLED",
|
|
33
34
|
label: "Cancelled"
|
|
34
35
|
}
|
|
35
|
-
],
|
|
36
|
+
], C = i.map((e) => ({
|
|
36
37
|
value: e,
|
|
37
|
-
label:
|
|
38
|
+
label: r[e]
|
|
38
39
|
}));
|
|
39
|
-
function
|
|
40
|
-
let { navigateTo:
|
|
40
|
+
function w() {
|
|
41
|
+
let r = e(), { navigateTo: i } = t(), [w, T] = m(""), [E, D] = m(""), [O, k] = m(""), [A, j] = m(0), M = l({ pagination: { limit: 100 } }), N = p(() => (M.data?.items ?? []).map((e) => ({
|
|
41
42
|
value: e.id,
|
|
42
43
|
label: e.name
|
|
43
|
-
})), [
|
|
44
|
-
status:
|
|
45
|
-
teamId:
|
|
44
|
+
})), [M.data]), P = c({
|
|
45
|
+
status: w || void 0,
|
|
46
|
+
teamId: E || void 0,
|
|
46
47
|
pagination: {
|
|
47
|
-
limit:
|
|
48
|
-
offset:
|
|
48
|
+
limit: x,
|
|
49
|
+
offset: A * x
|
|
49
50
|
}
|
|
50
|
-
}),
|
|
51
|
-
let e =
|
|
52
|
-
return
|
|
53
|
-
}, [
|
|
54
|
-
return /* @__PURE__ */
|
|
55
|
-
title: "Fixtures",
|
|
56
|
-
description: "Matches across friendlies, league games, cups, tournaments and scrimmages.",
|
|
57
|
-
actions: /* @__PURE__ */
|
|
51
|
+
}), F = p(() => {
|
|
52
|
+
let e = P.data?.items ?? [];
|
|
53
|
+
return O ? e.filter((e) => e.type === O) : e;
|
|
54
|
+
}, [P.data, O]), I = P.data?.pageInfo.hasMore ?? !1, L = !!(w || E || O), R = () => j(0);
|
|
55
|
+
return /* @__PURE__ */ b(n, {
|
|
56
|
+
title: r("atheletebridge.pages.fixtures.title", "Fixtures"),
|
|
57
|
+
description: r("atheletebridge.pages.fixtures.description", "Matches across friendlies, league games, cups, tournaments and scrimmages."),
|
|
58
|
+
actions: /* @__PURE__ */ b("div", {
|
|
58
59
|
className: "flex flex-wrap items-center gap-2",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
60
|
+
children: [/* @__PURE__ */ b("button", {
|
|
60
61
|
type: "button",
|
|
61
|
-
onClick: () =>
|
|
62
|
+
onClick: () => i("/tournaments"),
|
|
62
63
|
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",
|
|
63
|
-
children: [
|
|
64
|
-
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ y(_, { className: "h-4 w-4" }),
|
|
66
|
+
" ",
|
|
67
|
+
r("atheletebridge.pages.fixtures.tournamentBracket", "Tournament bracket")
|
|
68
|
+
]
|
|
69
|
+
}), /* @__PURE__ */ b("button", {
|
|
65
70
|
type: "button",
|
|
66
71
|
"data-tour": "fixtures-create",
|
|
67
|
-
onClick: () =>
|
|
72
|
+
onClick: () => i("/fixtures/new"),
|
|
68
73
|
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",
|
|
69
|
-
children: [
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ y(g, { className: "h-4 w-4" }),
|
|
76
|
+
" ",
|
|
77
|
+
r("atheletebridge.pages.fixtures.scheduleFixture", "Schedule fixture")
|
|
78
|
+
]
|
|
70
79
|
})]
|
|
71
80
|
}),
|
|
72
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ b("div", {
|
|
73
82
|
"data-tour": "fixtures-filters",
|
|
74
83
|
className: "mb-6 flex flex-wrap items-center gap-3",
|
|
75
84
|
children: [
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
-
ariaLabel: "Filter by status",
|
|
78
|
-
allLabel: "All statuses",
|
|
79
|
-
value:
|
|
85
|
+
/* @__PURE__ */ y(d, {
|
|
86
|
+
ariaLabel: r("atheletebridge.pages.fixtures.filterStatus", "Filter by status"),
|
|
87
|
+
allLabel: r("atheletebridge.pages.fixtures.allStatuses", "All statuses"),
|
|
88
|
+
value: w,
|
|
80
89
|
onChange: (e) => {
|
|
81
|
-
|
|
90
|
+
T(e), R();
|
|
82
91
|
},
|
|
83
|
-
options:
|
|
92
|
+
options: S
|
|
84
93
|
}),
|
|
85
|
-
/* @__PURE__ */
|
|
86
|
-
ariaLabel: "Filter by team",
|
|
87
|
-
allLabel: "All teams",
|
|
88
|
-
value:
|
|
94
|
+
/* @__PURE__ */ y(d, {
|
|
95
|
+
ariaLabel: r("atheletebridge.pages.fixtures.filterTeam", "Filter by team"),
|
|
96
|
+
allLabel: r("atheletebridge.pages.fixtures.allTeams", "All teams"),
|
|
97
|
+
value: E,
|
|
89
98
|
onChange: (e) => {
|
|
90
|
-
|
|
99
|
+
D(e), R();
|
|
91
100
|
},
|
|
92
|
-
options:
|
|
101
|
+
options: N
|
|
93
102
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
95
|
-
ariaLabel: "Filter by type",
|
|
96
|
-
allLabel: "All types",
|
|
97
|
-
value:
|
|
98
|
-
onChange: (e) =>
|
|
99
|
-
options:
|
|
103
|
+
/* @__PURE__ */ y(d, {
|
|
104
|
+
ariaLabel: r("atheletebridge.pages.fixtures.filterType", "Filter by type"),
|
|
105
|
+
allLabel: r("atheletebridge.pages.fixtures.allTypes", "All types"),
|
|
106
|
+
value: O,
|
|
107
|
+
onChange: (e) => k(e),
|
|
108
|
+
options: C
|
|
100
109
|
})
|
|
101
110
|
]
|
|
102
|
-
}),
|
|
111
|
+
}), P.isLoading ? /* @__PURE__ */ y(s, { label: r("atheletebridge.pages.fixtures.loading", "Loading fixtures…") }) : P.isError ? /* @__PURE__ */ y(o, { onRetry: () => void P.refetch() }) : F.length === 0 ? /* @__PURE__ */ y(a, {
|
|
103
112
|
illustration: "empty-calendar",
|
|
104
|
-
icon: /* @__PURE__ */
|
|
105
|
-
title: "No fixtures found",
|
|
106
|
-
description:
|
|
107
|
-
action: /* @__PURE__ */
|
|
113
|
+
icon: /* @__PURE__ */ y(h, { className: "h-10 w-10" }),
|
|
114
|
+
title: r("atheletebridge.pages.fixtures.noFixtures", "No fixtures found"),
|
|
115
|
+
description: L ? r("atheletebridge.pages.fixtures.noFixturesFilterHint", "Try clearing your filters to see all fixtures.") : r("atheletebridge.pages.fixtures.noFixturesHint", "Schedule a match to start tracking results and standings."),
|
|
116
|
+
action: /* @__PURE__ */ y("button", {
|
|
108
117
|
type: "button",
|
|
109
|
-
onClick: () =>
|
|
118
|
+
onClick: () => i("/fixtures/new"),
|
|
110
119
|
className: "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",
|
|
111
|
-
children: "Schedule fixture"
|
|
120
|
+
children: r("atheletebridge.pages.fixtures.scheduleFixtureAction", "Schedule fixture")
|
|
112
121
|
})
|
|
113
|
-
}) : /* @__PURE__ */
|
|
122
|
+
}) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("div", {
|
|
114
123
|
className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
115
|
-
children:
|
|
124
|
+
children: F.map((e) => /* @__PURE__ */ y(f, {
|
|
116
125
|
fixture: e,
|
|
117
|
-
onClick: () =>
|
|
126
|
+
onClick: () => i(`/fixtures/${e.id}`)
|
|
118
127
|
}, e.id))
|
|
119
|
-
}), /* @__PURE__ */
|
|
128
|
+
}), /* @__PURE__ */ y(u, {
|
|
120
129
|
className: "mt-6",
|
|
121
|
-
page:
|
|
122
|
-
hasMore:
|
|
123
|
-
onPrev: () =>
|
|
124
|
-
onNext: () =>
|
|
130
|
+
page: A,
|
|
131
|
+
hasMore: I,
|
|
132
|
+
onPrev: () => j((e) => Math.max(0, e - 1)),
|
|
133
|
+
onNext: () => j((e) => e + 1)
|
|
125
134
|
})] })]
|
|
126
135
|
});
|
|
127
136
|
}
|
|
128
137
|
//#endregion
|
|
129
|
-
export {
|
|
138
|
+
export { w as FixturesListPage };
|
|
130
139
|
|
|
131
140
|
//# sourceMappingURL=FixturesListPage.js.map
|