@burdenoff/microfe-atheletebridge 2026.1029.1 → 2026.1031.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/atheletebridge/components/ai/AiModelPicker.d.ts.map +1 -1
  2. package/dist/atheletebridge/components/ai/AiModelPicker.js +17 -3
  3. package/dist/atheletebridge/components/ai/AiModelPicker.js.map +1 -1
  4. package/dist/atheletebridge/components/ai/DecisionCard.d.ts.map +1 -1
  5. package/dist/atheletebridge/components/ai/DecisionCard.js +217 -122
  6. package/dist/atheletebridge/components/ai/DecisionCard.js.map +1 -1
  7. package/dist/atheletebridge/components/ai/SubjectDecisions.d.ts +29 -0
  8. package/dist/atheletebridge/components/ai/SubjectDecisions.d.ts.map +1 -0
  9. package/dist/atheletebridge/components/ai/SubjectDecisions.js +69 -0
  10. package/dist/atheletebridge/components/ai/SubjectDecisions.js.map +1 -0
  11. package/dist/atheletebridge/components/ai/index.d.ts +2 -0
  12. package/dist/atheletebridge/components/ai/index.d.ts.map +1 -1
  13. package/dist/atheletebridge/hooks/useAthleteBridgeApi.d.ts +10 -1
  14. package/dist/atheletebridge/hooks/useAthleteBridgeApi.d.ts.map +1 -1
  15. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +25 -16
  16. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
  17. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.d.ts.map +1 -1
  18. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js +135 -118
  19. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js.map +1 -1
  20. package/dist/atheletebridge/pages/programs/ProgramDetailPage.d.ts.map +1 -1
  21. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +42 -34
  22. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
  23. package/dist/atheletebridge/pages/teams/TeamDetailPage.d.ts.map +1 -1
  24. package/dist/atheletebridge/pages/teams/TeamDetailPage.js +65 -57
  25. package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
  26. package/dist/atheletebridge/types/index.d.ts +6 -0
  27. package/dist/atheletebridge/types/index.d.ts.map +1 -1
  28. package/dist/atheletebridge/utils/aiDecision.d.ts +12 -0
  29. package/dist/atheletebridge/utils/aiDecision.d.ts.map +1 -1
  30. package/dist/atheletebridge/utils/aiDecision.js +11 -1
  31. package/dist/atheletebridge/utils/aiDecision.js.map +1 -1
  32. package/package.json +1 -1
@@ -2,23 +2,24 @@ import { useAbTranslate as e } from "../../i18n.js";
2
2
  import { useAthleteBridge as t } from "../../context/AthleteBridgeContext.js";
3
3
  import { PageLayout as n } from "../../components/PageLayout.js";
4
4
  import { StatusBadge as r } from "../../components/StatusBadge.js";
5
- import { MEMBER_ROLE_LABELS as i, SPORT_LABELS as a } from "../../components/domain.js";
6
- import { Crest as o } from "../../components/Crest.js";
7
- import { MatchupRow as ee } from "../../components/MatchupRow.js";
8
- import { UserAvatar as te } from "../../components/UserAvatar.js";
9
- import { EmptyState as s } from "../../components/EmptyState.js";
10
- import { ErrorState as ne, LoadingState as re } from "../../components/LoadingState.js";
11
- import { useAddMember as ie, useArchiveTeam as ae, useRemoveMember as oe, useTeam as se } from "../../hooks/useAthleteBridgeApi.js";
5
+ import { MEMBER_ROLE_LABELS as i, SPORT_LABELS as ee } from "../../components/domain.js";
6
+ import { Crest as te } from "../../components/Crest.js";
7
+ import { MatchupRow as ne } from "../../components/MatchupRow.js";
8
+ import { UserAvatar as a } from "../../components/UserAvatar.js";
9
+ import { EmptyState as o } from "../../components/EmptyState.js";
10
+ import { ErrorState as re, LoadingState as ie } from "../../components/LoadingState.js";
11
+ import { useAddMember as s, useArchiveTeam as ae, useRemoveMember as oe, useTeam as se } from "../../hooks/useAthleteBridgeApi.js";
12
12
  import { FormField as c, fieldInputClass as l } from "../../components/form.js";
13
- import { UserSearchSelect as ce } from "../../components/UserSearchSelect.js";
13
+ import { SubjectDecisions as ce } from "../../components/ai/SubjectDecisions.js";
14
+ import { UserSearchSelect as le } from "../../components/UserSearchSelect.js";
14
15
  import { useState as u } from "react";
15
16
  import { useMutation as d, useQueryClient as f } from "@tanstack/react-query";
16
- import { Archive as le, ArrowLeft as ue, CalendarDays as de, Check as fe, Edit as pe, Loader2 as p, MapPin as me, Pencil as he, Plus as ge, Trash2 as _e, UserPlus as m, X as ve } from "lucide-react";
17
- import { useParams as h } from "react-router";
17
+ import { Archive as ue, ArrowLeft as p, CalendarDays as de, Check as fe, Edit as pe, Loader2 as m, MapPin as me, Pencil as he, Plus as ge, Trash2 as _e, UserPlus as h, X as ve } from "lucide-react";
18
+ import { useParams as ye } from "react-router";
18
19
  import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
19
20
  import { GlassCard as y, ResponsiveDialog as b, ResponsiveDialogContent as x, ResponsiveDialogFooter as S, ResponsiveDialogHeader as C, ResponsiveDialogTitle as w } from "@burdenoff/fe-libs/ui";
20
21
  import { clearGraphqlFetchResponseCache as T, graphqlFetch as E } from "@burdenoff/fe-libs/shared/graphql";
21
- import { useGlobalUi as ye } from "@burdenoff/fe-libs/shared/providers/shell/GlobalUiProvider";
22
+ import { useGlobalUi as be } from "@burdenoff/fe-libs/shared/providers/shell/GlobalUiProvider";
22
23
  //#region src/atheletebridge/pages/teams/TeamDetailPage.tsx
23
24
  var D = Object.keys(i), O = [
24
25
  "TRIAL",
@@ -33,7 +34,7 @@ var D = Object.keys(i), O = [
33
34
  status: "ACTIVE",
34
35
  position: "",
35
36
  jerseyNumber: ""
36
- }, be = (e) => {
37
+ }, j = (e) => {
37
38
  let n = f(), { apiGatewayUrl: r } = t();
38
39
  return d({
39
40
  mutationFn: async ({ id: e, input: t }) => {
@@ -64,28 +65,28 @@ var D = Object.keys(i), O = [
64
65
  }
65
66
  });
66
67
  };
67
- function j() {
68
- let d = e(), { id: f } = h(), { navigateTo: T } = t(), { showToast: E } = ye(), j = se(f), M = ae(), N = ie(), P = oe(f), F = be(f), [I, L] = u(!1), [R, z] = u(A), [B, V] = u(null), [xe, H] = u(null), [U, W] = u(null), [G, K] = u(null), [q, J] = u(null);
69
- if (j.isLoading) return /* @__PURE__ */ _(n, {
68
+ function M() {
69
+ let d = e(), { id: f } = ye(), { navigateTo: T } = t(), { showToast: E } = be(), M = se(f), N = ae(), P = s(), F = oe(f), I = j(f), [xe, L] = u(!1), [R, z] = u(A), [B, V] = u(null), [Se, H] = u(null), [U, W] = u(null), [G, K] = u(null), [q, J] = u(null);
70
+ if (M.isLoading) return /* @__PURE__ */ _(n, {
70
71
  title: d("atheletebridge.pages.teamDetail.title", "Team"),
71
- children: /* @__PURE__ */ _(re, { label: d("atheletebridge.pages.teamDetail.loading", "Loading team…") })
72
+ children: /* @__PURE__ */ _(ie, { label: d("atheletebridge.pages.teamDetail.loading", "Loading team…") })
72
73
  });
73
- if (j.isError || !j.data) return /* @__PURE__ */ _(n, {
74
+ if (M.isError || !M.data) return /* @__PURE__ */ _(n, {
74
75
  title: d("atheletebridge.pages.teamDetail.title", "Team"),
75
- children: /* @__PURE__ */ _(ne, {
76
+ children: /* @__PURE__ */ _(re, {
76
77
  title: d("atheletebridge.pages.teamDetail.notFoundTitle", "Team not found"),
77
78
  message: d("atheletebridge.pages.teamDetail.notFoundMessage", "This team does not exist or you do not have access to it."),
78
79
  onRetry: () => T("/teams")
79
80
  })
80
81
  });
81
- let Y = j.data, X = Y.members?.items ?? [], Z = Y.fixtures?.items ?? [], Q = [
82
- a[Y.sport],
82
+ let Y = M.data, X = Y.members?.items ?? [], Z = Y.fixtures?.items ?? [], Q = [
83
+ ee[Y.sport],
83
84
  Y.ageGroup ?? void 0,
84
85
  Y.season ?? void 0,
85
86
  Y.homeVenue ?? void 0
86
- ].filter(Boolean), Se = async () => {
87
- f && (await M.mutateAsync(f), T("/teams"));
88
- }, Ce = (e) => {
87
+ ].filter(Boolean), Ce = async () => {
88
+ f && (await N.mutateAsync(f), T("/teams"));
89
+ }, we = (e) => {
89
90
  K(null), H(e.id), W({
90
91
  role: e.role,
91
92
  status: e.status,
@@ -94,7 +95,7 @@ function j() {
94
95
  });
95
96
  }, $ = () => {
96
97
  H(null), W(null), K(null);
97
- }, we = async (e) => {
98
+ }, Te = async (e) => {
98
99
  if (!U) return;
99
100
  K(null);
100
101
  let t = U.jerseyNumber.trim(), n;
@@ -103,7 +104,7 @@ function j() {
103
104
  return;
104
105
  }
105
106
  try {
106
- await F.mutateAsync({
107
+ await I.mutateAsync({
107
108
  id: e,
108
109
  input: {
109
110
  role: U.role,
@@ -115,7 +116,7 @@ function j() {
115
116
  } catch (e) {
116
117
  K(e instanceof Error ? e.message : "Failed to update member.");
117
118
  }
118
- }, Te = async (e) => {
119
+ }, Ee = async (e) => {
119
120
  if (e.preventDefault(), V(null), !f) return;
120
121
  if (!R.userId.trim() || !R.displayName.trim()) {
121
122
  V("User ID and display name are required.");
@@ -127,7 +128,7 @@ function j() {
127
128
  return;
128
129
  }
129
130
  try {
130
- await N.mutateAsync({
131
+ await P.mutateAsync({
131
132
  teamId: f,
132
133
  userId: R.userId.trim(),
133
134
  displayName: R.displayName.trim(),
@@ -139,11 +140,11 @@ function j() {
139
140
  } catch (e) {
140
141
  V(e instanceof Error ? e.message : "Failed to add member.");
141
142
  }
142
- }, Ee = (e) => T(`/teams/${Y.id}/athletes/${e}`), De = async () => {
143
+ }, De = (e) => T(`/teams/${Y.id}/athletes/${e}`), Oe = async () => {
143
144
  if (!q) return;
144
145
  let e = q.displayName;
145
146
  try {
146
- await P.mutateAsync(q.id), E(d("atheletebridge.pages.teamDetail.memberDeletedToast", "\"{name}\" was removed from the team.").replace("{name}", e), "success"), J(null);
147
+ await F.mutateAsync(q.id), E(d("atheletebridge.pages.teamDetail.memberDeletedToast", "\"{name}\" was removed from the team.").replace("{name}", e), "success"), J(null);
147
148
  } catch {
148
149
  E(d("atheletebridge.pages.teamDetail.memberDeleteFailedToast", "Failed to remove \"{name}\". Please try again.").replace("{name}", e), "error"), J(null);
149
150
  }
@@ -160,7 +161,7 @@ function j() {
160
161
  onClick: () => T("/teams"),
161
162
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
162
163
  children: [
163
- /* @__PURE__ */ _(ue, { className: "h-4 w-4" }),
164
+ /* @__PURE__ */ _(p, { className: "h-4 w-4" }),
164
165
  " ",
165
166
  d("atheletebridge.pages.teamDetail.back", "Back")
166
167
  ]
@@ -177,11 +178,11 @@ function j() {
177
178
  }),
178
179
  Y.status !== "ARCHIVED" && /* @__PURE__ */ v("button", {
179
180
  type: "button",
180
- onClick: Se,
181
- disabled: M.isPending,
181
+ onClick: Ce,
182
+ disabled: N.isPending,
182
183
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60",
183
184
  children: [
184
- M.isPending ? /* @__PURE__ */ _(p, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(le, { className: "h-4 w-4" }),
185
+ N.isPending ? /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(ue, { className: "h-4 w-4" }),
185
186
  " ",
186
187
  d("atheletebridge.pages.teamDetail.archive", "Archive")
187
188
  ]
@@ -195,7 +196,7 @@ function j() {
195
196
  children: [
196
197
  /* @__PURE__ */ v("div", {
197
198
  className: "flex flex-col gap-4 sm:flex-row sm:items-center",
198
- children: [/* @__PURE__ */ _(o, {
199
+ children: [/* @__PURE__ */ _(te, {
199
200
  name: Y.name,
200
201
  sport: Y.sport,
201
202
  size: 64
@@ -262,14 +263,14 @@ function j() {
262
263
  },
263
264
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
264
265
  children: [
265
- /* @__PURE__ */ _(m, { className: "h-4 w-4" }),
266
+ /* @__PURE__ */ _(h, { className: "h-4 w-4" }),
266
267
  " ",
267
268
  d("atheletebridge.pages.teamDetail.addMember", "Add member")
268
269
  ]
269
270
  })]
270
- }), X.length === 0 ? /* @__PURE__ */ _(s, {
271
+ }), X.length === 0 ? /* @__PURE__ */ _(o, {
271
272
  illustration: "empty-data",
272
- icon: /* @__PURE__ */ _(m, { className: "h-10 w-10" }),
273
+ icon: /* @__PURE__ */ _(h, { className: "h-10 w-10" }),
273
274
  title: d("atheletebridge.pages.teamDetail.noMembersTitle", "No members yet"),
274
275
  description: d("atheletebridge.pages.teamDetail.noMembersHint", "Add players, coaches and staff to build the roster.")
275
276
  }) : /* @__PURE__ */ v(y, {
@@ -310,7 +311,7 @@ function j() {
310
311
  }) }), /* @__PURE__ */ _("tbody", {
311
312
  className: "divide-y divide-border-seam",
312
313
  children: X.map((e) => {
313
- let t = xe === e.id, n = F.isPending && t;
314
+ let t = Se === e.id, n = I.isPending && t;
314
315
  return /* @__PURE__ */ v("tr", {
315
316
  className: "align-middle transition-colors hover:bg-bg-sunken/60",
316
317
  children: [
@@ -318,10 +319,10 @@ function j() {
318
319
  className: "px-4 py-3",
319
320
  children: /* @__PURE__ */ v("button", {
320
321
  type: "button",
321
- onClick: () => Ee(e.id),
322
+ onClick: () => De(e.id),
322
323
  className: "group flex items-center gap-3 text-left focus-visible:outline-none",
323
324
  title: `${d("atheletebridge.pages.teamDetail.viewProfile", "View profile")}: ${e.displayName}`,
324
- children: [/* @__PURE__ */ _(te, {
325
+ children: [/* @__PURE__ */ _(a, {
325
326
  name: e.displayName,
326
327
  id: e.userId,
327
328
  jersey: e.jerseyNumber,
@@ -412,11 +413,11 @@ function j() {
412
413
  className: "flex items-center justify-end gap-1",
413
414
  children: t ? /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
414
415
  type: "button",
415
- onClick: () => void we(e.id),
416
+ onClick: () => void Te(e.id),
416
417
  disabled: n,
417
418
  "aria-label": d("common.save", "Save"),
418
419
  className: "rounded-lg p-2 text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-60",
419
- children: n ? /* @__PURE__ */ _(p, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(fe, { className: "h-4 w-4" })
420
+ children: n ? /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(fe, { className: "h-4 w-4" })
420
421
  }), /* @__PURE__ */ _("button", {
421
422
  type: "button",
422
423
  onClick: $,
@@ -426,14 +427,14 @@ function j() {
426
427
  children: /* @__PURE__ */ _(ve, { className: "h-4 w-4" })
427
428
  })] }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
428
429
  type: "button",
429
- onClick: () => Ce(e),
430
+ onClick: () => we(e),
430
431
  "aria-label": `${d("common.edit", "Edit")} ${e.displayName}`,
431
432
  className: "rounded-lg p-2 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
432
433
  children: /* @__PURE__ */ _(he, { className: "h-4 w-4" })
433
434
  }), /* @__PURE__ */ _("button", {
434
435
  type: "button",
435
436
  onClick: () => J(e),
436
- disabled: P.isPending,
437
+ disabled: F.isPending,
437
438
  "aria-label": `${d("common.delete", "Delete")} ${e.displayName}`,
438
439
  className: "rounded-lg p-2 text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-60",
439
440
  children: /* @__PURE__ */ _(_e, { className: "h-4 w-4" })
@@ -479,7 +480,7 @@ function j() {
479
480
  ]
480
481
  })]
481
482
  })]
482
- }), Z.length === 0 ? /* @__PURE__ */ _(s, {
483
+ }), Z.length === 0 ? /* @__PURE__ */ _(o, {
483
484
  illustration: "empty-calendar",
484
485
  icon: /* @__PURE__ */ _(de, { className: "h-10 w-10" }),
485
486
  title: d("atheletebridge.pages.teamDetail.noFixturesTitle", "No fixtures scheduled"),
@@ -489,16 +490,23 @@ function j() {
489
490
  className: "px-4 py-1",
490
491
  children: /* @__PURE__ */ _("div", {
491
492
  className: "divide-y divide-border-seam",
492
- children: Z.map((e) => /* @__PURE__ */ _(ee, {
493
+ children: Z.map((e) => /* @__PURE__ */ _(ne, {
493
494
  fixture: e,
494
495
  onClick: () => T(`/fixtures/${e.id}`)
495
496
  }, e.id))
496
497
  })
497
498
  })] }),
499
+ /* @__PURE__ */ _(ce, {
500
+ className: "mt-8",
501
+ subject: {
502
+ kind: "team",
503
+ teamId: Y.id
504
+ }
505
+ }),
498
506
  /* @__PURE__ */ _(b, {
499
507
  open: q !== null,
500
508
  onOpenChange: (e) => {
501
- !e && !P.isPending && J(null);
509
+ !e && !F.isPending && J(null);
502
510
  },
503
511
  children: /* @__PURE__ */ v(x, { children: [
504
512
  /* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, { children: d("atheletebridge.pages.teamDetail.deleteMemberTitle", "Delete player?") }) }),
@@ -509,26 +517,26 @@ function j() {
509
517
  /* @__PURE__ */ v(S, { children: [/* @__PURE__ */ _("button", {
510
518
  type: "button",
511
519
  onClick: () => J(null),
512
- disabled: P.isPending,
520
+ disabled: F.isPending,
513
521
  className: "rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-60",
514
522
  children: d("common.cancel", "Cancel")
515
523
  }), /* @__PURE__ */ v("button", {
516
524
  type: "button",
517
- onClick: () => void De(),
518
- disabled: P.isPending,
525
+ onClick: () => void Oe(),
526
+ disabled: F.isPending,
519
527
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-danger-bg px-4 py-2 text-sm font-semibold text-action-danger-text transition-colors hover:bg-action-danger-bgHover disabled:opacity-60",
520
- children: [P.isPending && /* @__PURE__ */ _(p, { className: "h-4 w-4 animate-spin" }), d("common.delete", "Delete")]
528
+ children: [F.isPending && /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin" }), d("common.delete", "Delete")]
521
529
  })] })
522
530
  ] })
523
531
  }),
524
532
  /* @__PURE__ */ _(b, {
525
- open: I,
533
+ open: xe,
526
534
  onOpenChange: L,
527
535
  children: /* @__PURE__ */ v(x, { children: [/* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, { children: d("atheletebridge.pages.teamDetail.addMember", "Add member") }) }), /* @__PURE__ */ v("form", {
528
- onSubmit: Te,
536
+ onSubmit: Ee,
529
537
  className: "space-y-4 px-1",
530
538
  children: [
531
- /* @__PURE__ */ _(ce, {
539
+ /* @__PURE__ */ _(le, {
532
540
  userId: R.userId,
533
541
  displayName: R.displayName,
534
542
  onSelect: ({ userId: e, displayName: t }) => z((n) => ({
@@ -631,9 +639,9 @@ function j() {
631
639
  children: d("common.cancel", "Cancel")
632
640
  }), /* @__PURE__ */ v("button", {
633
641
  type: "submit",
634
- disabled: N.isPending,
642
+ disabled: P.isPending,
635
643
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
636
- children: [N.isPending && /* @__PURE__ */ _(p, { className: "h-4 w-4 animate-spin" }), d("atheletebridge.pages.teamDetail.addMember", "Add member")]
644
+ children: [P.isPending && /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin" }), d("atheletebridge.pages.teamDetail.addMember", "Add member")]
637
645
  })] })
638
646
  ]
639
647
  })] })
@@ -642,6 +650,6 @@ function j() {
642
650
  });
643
651
  }
644
652
  //#endregion
645
- export { j as TeamDetailPage };
653
+ export { M as TeamDetailPage };
646
654
 
647
655
  //# sourceMappingURL=TeamDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/teams/TeamDetailPage.tsx"],"sourcesContent":["/**\n * Team Detail\n * @module atheletebridge/pages/teams\n *\n * Reads a single team (with members + fixtures) from `athTeam`, supports adding,\n * editing (athUpdateMember — role/position/jersey/status inline per roster row)\n * and removing members (athAddMember / athRemoveMember), editing and archiving\n * the team (athUpdateTeam via the form / athArchiveTeam), links each roster row\n * to the athlete profile, and renders the team's fixtures as crest-vs-crest\n * matchup rows.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n CalendarDays,\n Check,\n Edit,\n Archive,\n MapPin,\n Pencil,\n Plus,\n Trash2,\n UserPlus,\n Loader2,\n X,\n} from 'lucide-react';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { graphqlFetch, clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell/GlobalUiProvider';\nimport {\n GlassCard,\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 { Crest } from '../../components/Crest';\nimport { UserAvatar } from '../../components/UserAvatar';\nimport { MatchupRow } from '../../components/MatchupRow';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { UserSearchSelect } from '../../components/UserSearchSelect';\nimport { SPORT_LABELS, MEMBER_ROLE_LABELS } from '../../components/domain';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useTeam,\n useArchiveTeam,\n useAddMember,\n useRemoveMember,\n} from '../../hooks/useAthleteBridgeApi';\nimport { useAbTranslate } from '../../i18n';\nimport type { AthMember, AthMemberRole, AthMemberStatus, AthUpdateMemberInput } from '../../types';\n\nconst ROLES = Object.keys(MEMBER_ROLE_LABELS) as AthMemberRole[];\nconst MEMBER_STATUSES: AthMemberStatus[] = ['TRIAL', 'ACTIVE', 'INJURED', 'SUSPENDED', 'RELEASED'];\n\nconst memberStatusLabel = (s: AthMemberStatus): string => s.charAt(0) + s.slice(1).toLowerCase();\n\ninterface AddMemberForm {\n userId: string;\n displayName: string;\n role: AthMemberRole;\n status: AthMemberStatus;\n position: string;\n jerseyNumber: string;\n}\n\nconst EMPTY_MEMBER: AddMemberForm = {\n userId: '',\n displayName: '',\n role: 'PLAYER',\n status: 'ACTIVE',\n position: '',\n jerseyNumber: '',\n};\n\ninterface RowDraft {\n role: AthMemberRole;\n status: AthMemberStatus;\n position: string;\n jerseyNumber: string;\n}\n\n/**\n * Inline roster-member update wired directly to the shipped `athUpdateMember`\n * mutation (the shared kit has add/remove hooks but no update hook). Uses the\n * same workspace-token graphqlFetch primitive as the rest of the MFE and clears\n * the short-lived read cache so the team refetch is fresh.\n */\nconst useUpdateMemberInline = (teamId: string | undefined) => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthUpdateMemberInput }) => {\n const res = await graphqlFetch<{ athUpdateMember: AthMember }>({\n gateway: 'workspace',\n baseUrl: apiGatewayUrl,\n query: `mutation AthUpdateMember($id: ID!, $input: AthUpdateMemberInput!) {\n athUpdateMember(id: $id, input: $input) {\n id teamId displayName role status position jerseyNumber\n }\n }`,\n variables: { id, input },\n workspaceToken: true,\n });\n if (res.errors?.length) {\n throw new Error(res.errors[0]?.message ?? 'Failed to update member.');\n }\n clearGraphqlFetchResponseCache();\n if (!res.data) throw new Error('Update did not return data.');\n return res.data.athUpdateMember;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', teamId] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'members', teamId] });\n },\n });\n};\n\nexport function TeamDetailPage() {\n const tr = useAbTranslate();\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n const { showToast } = useGlobalUi();\n\n const team = useTeam(id);\n const archiveTeam = useArchiveTeam();\n const addMember = useAddMember();\n const removeMember = useRemoveMember(id);\n const updateMember = useUpdateMemberInline(id);\n\n const [addOpen, setAddOpen] = useState(false);\n const [memberForm, setMemberForm] = useState<AddMemberForm>(EMPTY_MEMBER);\n const [memberError, setMemberError] = useState<string | null>(null);\n\n const [editingId, setEditingId] = useState<string | null>(null);\n const [draft, setDraft] = useState<RowDraft | null>(null);\n const [rowError, setRowError] = useState<string | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<AthMember | null>(null);\n\n if (team.isLoading) {\n return (\n <PageLayout title={tr('atheletebridge.pages.teamDetail.title', 'Team')}>\n <LoadingState label={tr('atheletebridge.pages.teamDetail.loading', 'Loading team…')} />\n </PageLayout>\n );\n }\n\n if (team.isError || !team.data) {\n return (\n <PageLayout title={tr('atheletebridge.pages.teamDetail.title', 'Team')}>\n <ErrorState\n title={tr('atheletebridge.pages.teamDetail.notFoundTitle', 'Team not found')}\n message={tr(\n 'atheletebridge.pages.teamDetail.notFoundMessage',\n 'This team does not exist or you do not have access to it.'\n )}\n onRetry={() => navigateTo('/teams')}\n />\n </PageLayout>\n );\n }\n\n const t = team.data;\n const members = t.members?.items ?? [];\n const fixtures = t.fixtures?.items ?? [];\n\n const metaParts = [\n SPORT_LABELS[t.sport],\n t.ageGroup ?? undefined,\n t.season ?? undefined,\n t.homeVenue ?? undefined,\n ].filter(Boolean) as string[];\n\n const handleArchive = async () => {\n if (!id) return;\n await archiveTeam.mutateAsync(id);\n navigateTo('/teams');\n };\n\n const startEdit = (m: AthMember) => {\n setRowError(null);\n setEditingId(m.id);\n setDraft({\n role: m.role,\n status: m.status,\n position: m.position ?? '',\n jerseyNumber: m.jerseyNumber != null ? String(m.jerseyNumber) : '',\n });\n };\n\n const cancelEdit = () => {\n setEditingId(null);\n setDraft(null);\n setRowError(null);\n };\n\n const saveEdit = async (memberId: string) => {\n if (!draft) return;\n setRowError(null);\n const trimmedJersey = draft.jerseyNumber.trim();\n let jersey: number | undefined;\n if (trimmedJersey) {\n jersey = Number.parseInt(trimmedJersey, 10);\n if (Number.isNaN(jersey)) {\n setRowError('Jersey number must be a number.');\n return;\n }\n }\n try {\n await updateMember.mutateAsync({\n id: memberId,\n input: {\n role: draft.role,\n status: draft.status,\n position: draft.position.trim() || undefined,\n jerseyNumber: jersey,\n },\n });\n cancelEdit();\n } catch (err) {\n setRowError(err instanceof Error ? err.message : 'Failed to update member.');\n }\n };\n\n const handleAddMember = async (e: React.FormEvent) => {\n e.preventDefault();\n setMemberError(null);\n if (!id) return;\n if (!memberForm.userId.trim() || !memberForm.displayName.trim()) {\n setMemberError('User ID and display name are required.');\n return;\n }\n const jersey = memberForm.jerseyNumber.trim()\n ? Number.parseInt(memberForm.jerseyNumber, 10)\n : undefined;\n if (jersey !== undefined && Number.isNaN(jersey)) {\n setMemberError('Jersey number must be a number.');\n return;\n }\n try {\n await addMember.mutateAsync({\n teamId: id,\n userId: memberForm.userId.trim(),\n displayName: memberForm.displayName.trim(),\n role: memberForm.role,\n status: memberForm.status,\n position: memberForm.position.trim() || undefined,\n jerseyNumber: jersey,\n });\n setMemberForm(EMPTY_MEMBER);\n setAddOpen(false);\n } catch (err) {\n setMemberError(err instanceof Error ? err.message : 'Failed to add member.');\n }\n };\n\n const openProfile = (memberId: string) => navigateTo(`/teams/${t.id}/athletes/${memberId}`);\n\n /**\n * Destructive roster removal (BOFF-7617): only runs after the confirmation\n * dialog's explicit Delete. On success the read cache is invalidated by the\n * shared hook and a success toast is shown; on failure an error toast is\n * shown and the member stays on the roster (no invalidation ran).\n */\n const confirmDeleteMember = async () => {\n if (!deleteTarget) return;\n const name = deleteTarget.displayName;\n try {\n await removeMember.mutateAsync(deleteTarget.id);\n showToast(\n tr(\n 'atheletebridge.pages.teamDetail.memberDeletedToast',\n '\"{name}\" was removed from the team.'\n ).replace('{name}', name),\n 'success'\n );\n setDeleteTarget(null);\n } catch {\n showToast(\n tr(\n 'atheletebridge.pages.teamDetail.memberDeleteFailedToast',\n 'Failed to remove \"{name}\". Please try again.'\n ).replace('{name}', name),\n 'error'\n );\n setDeleteTarget(null);\n }\n };\n\n return (\n <PageLayout\n title={t.name}\n gradientTitle\n description={metaParts.join(' · ')}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> {tr('atheletebridge.pages.teamDetail.back', 'Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/teams/${t.id}/edit`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Edit className=\"h-4 w-4\" /> {tr('atheletebridge.pages.teamDetail.edit', 'Edit')}\n </button>\n {t.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archiveTeam.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {archiveTeam.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Archive className=\"h-4 w-4\" />\n )}{' '}\n {tr('atheletebridge.pages.teamDetail.archive', 'Archive')}\n </button>\n )}\n </div>\n }\n >\n {/* Hero: crest + meta strip */}\n <GlassCard treatment=\"insight\" className=\"mb-8 p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <Crest name={t.name} sport={t.sport} size={64} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{t.name}</h2>\n <StatusBadge status={t.status} />\n </div>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-text-secondary\">\n {metaParts.map((part, i) => (\n <span key={part} className=\"flex items-center gap-2\">\n {i > 0 && (\n <span aria-hidden=\"true\" className=\"text-text-muted\">\n ·\n </span>\n )}\n {part === t.homeVenue ? (\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {part}\n </span>\n ) : (\n part\n )}\n </span>\n ))}\n </div>\n </div>\n </div>\n {t.description && <p className=\"mt-4 text-sm text-text-secondary\">{t.description}</p>}\n {t.tags.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {t.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Roster */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('atheletebridge.pages.teamDetail.rosterTitle', 'Roster')} ({members.length})\n </h2>\n <button\n type=\"button\"\n onClick={() => {\n setMemberError(null);\n setAddOpen(true);\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <UserPlus className=\"h-4 w-4\" />{' '}\n {tr('atheletebridge.pages.teamDetail.addMember', 'Add member')}\n </button>\n </div>\n\n {members.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<UserPlus className=\"h-10 w-10\" />}\n title={tr('atheletebridge.pages.teamDetail.noMembersTitle', 'No members yet')}\n description={tr(\n 'atheletebridge.pages.teamDetail.noMembersHint',\n 'Add players, coaches and staff to build the roster.'\n )}\n />\n ) : (\n <GlassCard treatment=\"insight\" className=\"overflow-hidden p-0\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[720px] border-collapse text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-xs uppercase tracking-wide text-text-secondary\">\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.playerHeader', 'Player')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.roleHeader', 'Role')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.positionHeader', 'Position')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.jerseyHeader', 'Jersey #')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.statusHeader', 'Status')}\n </th>\n <th className=\"px-4 py-3 text-right font-medium\">\n {tr('atheletebridge.pages.teamDetail.actionsHeader', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam\">\n {members.map((m) => {\n const isEditing = editingId === m.id;\n const busy = updateMember.isPending && isEditing;\n return (\n <tr\n key={m.id}\n className=\"align-middle transition-colors hover:bg-bg-sunken/60\"\n >\n {/* Player */}\n <td className=\"px-4 py-3\">\n <button\n type=\"button\"\n onClick={() => openProfile(m.id)}\n className=\"group flex items-center gap-3 text-left focus-visible:outline-none\"\n title={`${tr('atheletebridge.pages.teamDetail.viewProfile', 'View profile')}: ${m.displayName}`}\n >\n <UserAvatar\n name={m.displayName}\n id={m.userId}\n jersey={m.jerseyNumber}\n size=\"sm\"\n />\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium text-text-primary group-hover:text-text-link\">\n {m.displayName}\n </span>\n <span className=\"block truncate text-xs text-text-secondary group-hover:text-text-link\">\n {tr('atheletebridge.pages.teamDetail.viewProfile', 'View profile')}\n </span>\n </span>\n </button>\n </td>\n\n {/* Role */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <select\n aria-label={tr('atheletebridge.pages.teamDetail.roleHeader', 'Role')}\n className={fieldInputClass}\n value={draft.role}\n onChange={(e) =>\n setDraft((d) =>\n d ? { ...d, role: e.target.value as AthMemberRole } : d\n )\n }\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {MEMBER_ROLE_LABELS[r]}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"text-text-secondary\">\n {MEMBER_ROLE_LABELS[m.role]}\n </span>\n )}\n </td>\n\n {/* Position */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <input\n aria-label={tr(\n 'atheletebridge.pages.teamDetail.positionHeader',\n 'Position'\n )}\n className={fieldInputClass}\n value={draft.position}\n onChange={(e) =>\n setDraft((d) => (d ? { ...d, position: e.target.value } : d))\n }\n placeholder=\"e.g. Striker\"\n />\n ) : (\n <span className=\"text-text-secondary\">{m.position || '—'}</span>\n )}\n </td>\n\n {/* Jersey # */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <input\n aria-label={tr(\n 'atheletebridge.pages.teamDetail.jerseyHeader',\n 'Jersey number'\n )}\n inputMode=\"numeric\"\n className={`${fieldInputClass} w-20`}\n value={draft.jerseyNumber}\n onChange={(e) =>\n setDraft((d) => (d ? { ...d, jerseyNumber: e.target.value } : d))\n }\n placeholder=\"9\"\n />\n ) : (\n <span className=\"tabular-nums text-text-primary\">\n {m.jerseyNumber ?? '—'}\n </span>\n )}\n </td>\n\n {/* Status */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <select\n aria-label={tr(\n 'atheletebridge.pages.teamDetail.statusHeader',\n 'Status'\n )}\n className={fieldInputClass}\n value={draft.status}\n onChange={(e) =>\n setDraft((d) =>\n d ? { ...d, status: e.target.value as AthMemberStatus } : d\n )\n }\n >\n {MEMBER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {memberStatusLabel(s)}\n </option>\n ))}\n </select>\n ) : (\n <StatusBadge status={m.status} />\n )}\n </td>\n\n {/* Actions */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-1\">\n {isEditing ? (\n <>\n <button\n type=\"button\"\n onClick={() => void saveEdit(m.id)}\n disabled={busy}\n aria-label={tr('common.save', 'Save')}\n className=\"rounded-lg p-2 text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-60\"\n >\n {busy ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Check className=\"h-4 w-4\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={cancelEdit}\n disabled={busy}\n aria-label={tr('common.cancel', 'Cancel')}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary disabled:opacity-60\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={() => startEdit(m)}\n aria-label={`${tr('common.edit', 'Edit')} ${m.displayName}`}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteTarget(m)}\n disabled={removeMember.isPending}\n aria-label={`${tr('common.delete', 'Delete')} ${m.displayName}`}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-60\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n {rowError && (\n <p className=\"border-t border-border-seam px-4 py-2 text-sm text-status-error-text\">\n {rowError}\n </p>\n )}\n </GlassCard>\n )}\n </section>\n\n {/* Fixtures */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('atheletebridge.pages.teamDetail.fixturesTitle', 'Fixtures')} ({fixtures.length})\n </h2>\n <div className=\"flex items-center gap-2\">\n {fixtures.length > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\n className=\"text-sm font-medium text-text-link transition-colors hover:underline\"\n >\n {tr('atheletebridge.pages.teamDetail.viewAll', 'View all')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/fixtures/new?homeTeamId=${t.id}`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" />{' '}\n {tr('atheletebridge.pages.teamDetail.schedule', 'Schedule')}\n </button>\n </div>\n </div>\n {fixtures.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n icon={<CalendarDays className=\"h-10 w-10\" />}\n title={tr('atheletebridge.pages.teamDetail.noFixturesTitle', 'No fixtures scheduled')}\n description={tr(\n 'atheletebridge.pages.teamDetail.noFixturesHint',\n 'Schedule a match for this team to start tracking results.'\n )}\n />\n ) : (\n <GlassCard treatment=\"insight\" className=\"px-4 py-1\">\n <div className=\"divide-y divide-border-seam\">\n {fixtures.map((f) => (\n <MatchupRow\n key={f.id}\n fixture={f}\n onClick={() => navigateTo(`/fixtures/${f.id}`)}\n />\n ))}\n </div>\n </GlassCard>\n )}\n </section>\n\n {/* Delete member confirmation (BOFF-7617) */}\n <ResponsiveDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open && !removeMember.isPending) setDeleteTarget(null);\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('atheletebridge.pages.teamDetail.deleteMemberTitle', 'Delete player?')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <p className=\"px-1 text-sm text-text-secondary\">\n {tr(\n 'atheletebridge.pages.teamDetail.deleteMemberMessage',\n 'This will remove \"{name}\" from {team}. This action cannot be undone.'\n )\n .replace('{name}', deleteTarget?.displayName ?? '')\n .replace('{team}', t.name)}\n </p>\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setDeleteTarget(null)}\n disabled={removeMember.isPending}\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 disabled:opacity-60\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => void confirmDeleteMember()}\n disabled={removeMember.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-danger-bg px-4 py-2 text-sm font-semibold text-action-danger-text transition-colors hover:bg-action-danger-bgHover disabled:opacity-60\"\n >\n {removeMember.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {tr('common.delete', 'Delete')}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add member dialog */}\n <ResponsiveDialog open={addOpen} onOpenChange={setAddOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('atheletebridge.pages.teamDetail.addMember', 'Add member')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <form onSubmit={handleAddMember} className=\"space-y-4 px-1\">\n <UserSearchSelect\n userId={memberForm.userId}\n displayName={memberForm.displayName}\n onSelect={({ userId, displayName }) =>\n setMemberForm((p) => ({ ...p, userId, displayName: displayName || p.displayName }))\n }\n required\n />\n <FormField label=\"Display name\" htmlFor=\"displayName\" required>\n <input\n id=\"displayName\"\n className={fieldInputClass}\n value={memberForm.displayName}\n onChange={(e) => setMemberForm((p) => ({ ...p, displayName: e.target.value }))}\n placeholder=\"e.g. Alex Morgan\"\n />\n </FormField>\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Role\" htmlFor=\"role\">\n <select\n id=\"role\"\n className={fieldInputClass}\n value={memberForm.role}\n onChange={(e) =>\n setMemberForm((p) => ({ ...p, role: e.target.value as AthMemberRole }))\n }\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {MEMBER_ROLE_LABELS[r]}\n </option>\n ))}\n </select>\n </FormField>\n <FormField label=\"Status\" htmlFor=\"memberStatus\">\n <select\n id=\"memberStatus\"\n className={fieldInputClass}\n value={memberForm.status}\n onChange={(e) =>\n setMemberForm((p) => ({ ...p, status: e.target.value as AthMemberStatus }))\n }\n >\n {MEMBER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {memberStatusLabel(s)}\n </option>\n ))}\n </select>\n </FormField>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Position\" htmlFor=\"position\">\n <input\n id=\"position\"\n className={fieldInputClass}\n value={memberForm.position}\n onChange={(e) => setMemberForm((p) => ({ ...p, position: e.target.value }))}\n placeholder=\"e.g. Striker\"\n />\n </FormField>\n <FormField label=\"Jersey #\" htmlFor=\"jersey\">\n <input\n id=\"jersey\"\n className={fieldInputClass}\n inputMode=\"numeric\"\n value={memberForm.jerseyNumber}\n onChange={(e) => setMemberForm((p) => ({ ...p, jerseyNumber: e.target.value }))}\n placeholder=\"9\"\n />\n </FormField>\n </div>\n {memberError && <p className=\"text-sm text-status-error-text\">{memberError}</p>}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setAddOpen(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 {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={addMember.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {addMember.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {tr('atheletebridge.pages.teamDetail.addMember', 'Add member')}\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TeamDetailPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/teams/TeamDetailPage.tsx"],"sourcesContent":["/**\n * Team Detail\n * @module atheletebridge/pages/teams\n *\n * Reads a single team (with members + fixtures) from `athTeam`, supports adding,\n * editing (athUpdateMember — role/position/jersey/status inline per roster row)\n * and removing members (athAddMember / athRemoveMember), editing and archiving\n * the team (athUpdateTeam via the form / athArchiveTeam), links each roster row\n * to the athlete profile, and renders the team's fixtures as crest-vs-crest\n * matchup rows.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n CalendarDays,\n Check,\n Edit,\n Archive,\n MapPin,\n Pencil,\n Plus,\n Trash2,\n UserPlus,\n Loader2,\n X,\n} from 'lucide-react';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { graphqlFetch, clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell/GlobalUiProvider';\nimport {\n GlassCard,\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 { Crest } from '../../components/Crest';\nimport { UserAvatar } from '../../components/UserAvatar';\nimport { MatchupRow } from '../../components/MatchupRow';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { UserSearchSelect } from '../../components/UserSearchSelect';\nimport { SPORT_LABELS, MEMBER_ROLE_LABELS } from '../../components/domain';\nimport { SubjectDecisions } from '../../components/ai/SubjectDecisions';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useTeam,\n useArchiveTeam,\n useAddMember,\n useRemoveMember,\n} from '../../hooks/useAthleteBridgeApi';\nimport { useAbTranslate } from '../../i18n';\nimport type { AthMember, AthMemberRole, AthMemberStatus, AthUpdateMemberInput } from '../../types';\n\nconst ROLES = Object.keys(MEMBER_ROLE_LABELS) as AthMemberRole[];\nconst MEMBER_STATUSES: AthMemberStatus[] = ['TRIAL', 'ACTIVE', 'INJURED', 'SUSPENDED', 'RELEASED'];\n\nconst memberStatusLabel = (s: AthMemberStatus): string => s.charAt(0) + s.slice(1).toLowerCase();\n\ninterface AddMemberForm {\n userId: string;\n displayName: string;\n role: AthMemberRole;\n status: AthMemberStatus;\n position: string;\n jerseyNumber: string;\n}\n\nconst EMPTY_MEMBER: AddMemberForm = {\n userId: '',\n displayName: '',\n role: 'PLAYER',\n status: 'ACTIVE',\n position: '',\n jerseyNumber: '',\n};\n\ninterface RowDraft {\n role: AthMemberRole;\n status: AthMemberStatus;\n position: string;\n jerseyNumber: string;\n}\n\n/**\n * Inline roster-member update wired directly to the shipped `athUpdateMember`\n * mutation (the shared kit has add/remove hooks but no update hook). Uses the\n * same workspace-token graphqlFetch primitive as the rest of the MFE and clears\n * the short-lived read cache so the team refetch is fresh.\n */\nconst useUpdateMemberInline = (teamId: string | undefined) => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthUpdateMemberInput }) => {\n const res = await graphqlFetch<{ athUpdateMember: AthMember }>({\n gateway: 'workspace',\n baseUrl: apiGatewayUrl,\n query: `mutation AthUpdateMember($id: ID!, $input: AthUpdateMemberInput!) {\n athUpdateMember(id: $id, input: $input) {\n id teamId displayName role status position jerseyNumber\n }\n }`,\n variables: { id, input },\n workspaceToken: true,\n });\n if (res.errors?.length) {\n throw new Error(res.errors[0]?.message ?? 'Failed to update member.');\n }\n clearGraphqlFetchResponseCache();\n if (!res.data) throw new Error('Update did not return data.');\n return res.data.athUpdateMember;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', teamId] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'members', teamId] });\n },\n });\n};\n\nexport function TeamDetailPage() {\n const tr = useAbTranslate();\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAthleteBridge();\n const { showToast } = useGlobalUi();\n\n const team = useTeam(id);\n const archiveTeam = useArchiveTeam();\n const addMember = useAddMember();\n const removeMember = useRemoveMember(id);\n const updateMember = useUpdateMemberInline(id);\n\n const [addOpen, setAddOpen] = useState(false);\n const [memberForm, setMemberForm] = useState<AddMemberForm>(EMPTY_MEMBER);\n const [memberError, setMemberError] = useState<string | null>(null);\n\n const [editingId, setEditingId] = useState<string | null>(null);\n const [draft, setDraft] = useState<RowDraft | null>(null);\n const [rowError, setRowError] = useState<string | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<AthMember | null>(null);\n\n if (team.isLoading) {\n return (\n <PageLayout title={tr('atheletebridge.pages.teamDetail.title', 'Team')}>\n <LoadingState label={tr('atheletebridge.pages.teamDetail.loading', 'Loading team…')} />\n </PageLayout>\n );\n }\n\n if (team.isError || !team.data) {\n return (\n <PageLayout title={tr('atheletebridge.pages.teamDetail.title', 'Team')}>\n <ErrorState\n title={tr('atheletebridge.pages.teamDetail.notFoundTitle', 'Team not found')}\n message={tr(\n 'atheletebridge.pages.teamDetail.notFoundMessage',\n 'This team does not exist or you do not have access to it.'\n )}\n onRetry={() => navigateTo('/teams')}\n />\n </PageLayout>\n );\n }\n\n const t = team.data;\n const members = t.members?.items ?? [];\n const fixtures = t.fixtures?.items ?? [];\n\n const metaParts = [\n SPORT_LABELS[t.sport],\n t.ageGroup ?? undefined,\n t.season ?? undefined,\n t.homeVenue ?? undefined,\n ].filter(Boolean) as string[];\n\n const handleArchive = async () => {\n if (!id) return;\n await archiveTeam.mutateAsync(id);\n navigateTo('/teams');\n };\n\n const startEdit = (m: AthMember) => {\n setRowError(null);\n setEditingId(m.id);\n setDraft({\n role: m.role,\n status: m.status,\n position: m.position ?? '',\n jerseyNumber: m.jerseyNumber != null ? String(m.jerseyNumber) : '',\n });\n };\n\n const cancelEdit = () => {\n setEditingId(null);\n setDraft(null);\n setRowError(null);\n };\n\n const saveEdit = async (memberId: string) => {\n if (!draft) return;\n setRowError(null);\n const trimmedJersey = draft.jerseyNumber.trim();\n let jersey: number | undefined;\n if (trimmedJersey) {\n jersey = Number.parseInt(trimmedJersey, 10);\n if (Number.isNaN(jersey)) {\n setRowError('Jersey number must be a number.');\n return;\n }\n }\n try {\n await updateMember.mutateAsync({\n id: memberId,\n input: {\n role: draft.role,\n status: draft.status,\n position: draft.position.trim() || undefined,\n jerseyNumber: jersey,\n },\n });\n cancelEdit();\n } catch (err) {\n setRowError(err instanceof Error ? err.message : 'Failed to update member.');\n }\n };\n\n const handleAddMember = async (e: React.FormEvent) => {\n e.preventDefault();\n setMemberError(null);\n if (!id) return;\n if (!memberForm.userId.trim() || !memberForm.displayName.trim()) {\n setMemberError('User ID and display name are required.');\n return;\n }\n const jersey = memberForm.jerseyNumber.trim()\n ? Number.parseInt(memberForm.jerseyNumber, 10)\n : undefined;\n if (jersey !== undefined && Number.isNaN(jersey)) {\n setMemberError('Jersey number must be a number.');\n return;\n }\n try {\n await addMember.mutateAsync({\n teamId: id,\n userId: memberForm.userId.trim(),\n displayName: memberForm.displayName.trim(),\n role: memberForm.role,\n status: memberForm.status,\n position: memberForm.position.trim() || undefined,\n jerseyNumber: jersey,\n });\n setMemberForm(EMPTY_MEMBER);\n setAddOpen(false);\n } catch (err) {\n setMemberError(err instanceof Error ? err.message : 'Failed to add member.');\n }\n };\n\n const openProfile = (memberId: string) => navigateTo(`/teams/${t.id}/athletes/${memberId}`);\n\n /**\n * Destructive roster removal (BOFF-7617): only runs after the confirmation\n * dialog's explicit Delete. On success the read cache is invalidated by the\n * shared hook and a success toast is shown; on failure an error toast is\n * shown and the member stays on the roster (no invalidation ran).\n */\n const confirmDeleteMember = async () => {\n if (!deleteTarget) return;\n const name = deleteTarget.displayName;\n try {\n await removeMember.mutateAsync(deleteTarget.id);\n showToast(\n tr(\n 'atheletebridge.pages.teamDetail.memberDeletedToast',\n '\"{name}\" was removed from the team.'\n ).replace('{name}', name),\n 'success'\n );\n setDeleteTarget(null);\n } catch {\n showToast(\n tr(\n 'atheletebridge.pages.teamDetail.memberDeleteFailedToast',\n 'Failed to remove \"{name}\". Please try again.'\n ).replace('{name}', name),\n 'error'\n );\n setDeleteTarget(null);\n }\n };\n\n return (\n <PageLayout\n title={t.name}\n gradientTitle\n description={metaParts.join(' · ')}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> {tr('atheletebridge.pages.teamDetail.back', 'Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/teams/${t.id}/edit`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Edit className=\"h-4 w-4\" /> {tr('atheletebridge.pages.teamDetail.edit', 'Edit')}\n </button>\n {t.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archiveTeam.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {archiveTeam.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Archive className=\"h-4 w-4\" />\n )}{' '}\n {tr('atheletebridge.pages.teamDetail.archive', 'Archive')}\n </button>\n )}\n </div>\n }\n >\n {/* Hero: crest + meta strip */}\n <GlassCard treatment=\"insight\" className=\"mb-8 p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <Crest name={t.name} sport={t.sport} size={64} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{t.name}</h2>\n <StatusBadge status={t.status} />\n </div>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-text-secondary\">\n {metaParts.map((part, i) => (\n <span key={part} className=\"flex items-center gap-2\">\n {i > 0 && (\n <span aria-hidden=\"true\" className=\"text-text-muted\">\n ·\n </span>\n )}\n {part === t.homeVenue ? (\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {part}\n </span>\n ) : (\n part\n )}\n </span>\n ))}\n </div>\n </div>\n </div>\n {t.description && <p className=\"mt-4 text-sm text-text-secondary\">{t.description}</p>}\n {t.tags.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {t.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Roster */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('atheletebridge.pages.teamDetail.rosterTitle', 'Roster')} ({members.length})\n </h2>\n <button\n type=\"button\"\n onClick={() => {\n setMemberError(null);\n setAddOpen(true);\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <UserPlus className=\"h-4 w-4\" />{' '}\n {tr('atheletebridge.pages.teamDetail.addMember', 'Add member')}\n </button>\n </div>\n\n {members.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<UserPlus className=\"h-10 w-10\" />}\n title={tr('atheletebridge.pages.teamDetail.noMembersTitle', 'No members yet')}\n description={tr(\n 'atheletebridge.pages.teamDetail.noMembersHint',\n 'Add players, coaches and staff to build the roster.'\n )}\n />\n ) : (\n <GlassCard treatment=\"insight\" className=\"overflow-hidden p-0\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[720px] border-collapse text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-xs uppercase tracking-wide text-text-secondary\">\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.playerHeader', 'Player')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.roleHeader', 'Role')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.positionHeader', 'Position')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.jerseyHeader', 'Jersey #')}\n </th>\n <th className=\"px-4 py-3 font-medium\">\n {tr('atheletebridge.pages.teamDetail.statusHeader', 'Status')}\n </th>\n <th className=\"px-4 py-3 text-right font-medium\">\n {tr('atheletebridge.pages.teamDetail.actionsHeader', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam\">\n {members.map((m) => {\n const isEditing = editingId === m.id;\n const busy = updateMember.isPending && isEditing;\n return (\n <tr\n key={m.id}\n className=\"align-middle transition-colors hover:bg-bg-sunken/60\"\n >\n {/* Player */}\n <td className=\"px-4 py-3\">\n <button\n type=\"button\"\n onClick={() => openProfile(m.id)}\n className=\"group flex items-center gap-3 text-left focus-visible:outline-none\"\n title={`${tr('atheletebridge.pages.teamDetail.viewProfile', 'View profile')}: ${m.displayName}`}\n >\n <UserAvatar\n name={m.displayName}\n id={m.userId}\n jersey={m.jerseyNumber}\n size=\"sm\"\n />\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium text-text-primary group-hover:text-text-link\">\n {m.displayName}\n </span>\n <span className=\"block truncate text-xs text-text-secondary group-hover:text-text-link\">\n {tr('atheletebridge.pages.teamDetail.viewProfile', 'View profile')}\n </span>\n </span>\n </button>\n </td>\n\n {/* Role */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <select\n aria-label={tr('atheletebridge.pages.teamDetail.roleHeader', 'Role')}\n className={fieldInputClass}\n value={draft.role}\n onChange={(e) =>\n setDraft((d) =>\n d ? { ...d, role: e.target.value as AthMemberRole } : d\n )\n }\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {MEMBER_ROLE_LABELS[r]}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"text-text-secondary\">\n {MEMBER_ROLE_LABELS[m.role]}\n </span>\n )}\n </td>\n\n {/* Position */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <input\n aria-label={tr(\n 'atheletebridge.pages.teamDetail.positionHeader',\n 'Position'\n )}\n className={fieldInputClass}\n value={draft.position}\n onChange={(e) =>\n setDraft((d) => (d ? { ...d, position: e.target.value } : d))\n }\n placeholder=\"e.g. Striker\"\n />\n ) : (\n <span className=\"text-text-secondary\">{m.position || '—'}</span>\n )}\n </td>\n\n {/* Jersey # */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <input\n aria-label={tr(\n 'atheletebridge.pages.teamDetail.jerseyHeader',\n 'Jersey number'\n )}\n inputMode=\"numeric\"\n className={`${fieldInputClass} w-20`}\n value={draft.jerseyNumber}\n onChange={(e) =>\n setDraft((d) => (d ? { ...d, jerseyNumber: e.target.value } : d))\n }\n placeholder=\"9\"\n />\n ) : (\n <span className=\"tabular-nums text-text-primary\">\n {m.jerseyNumber ?? '—'}\n </span>\n )}\n </td>\n\n {/* Status */}\n <td className=\"px-4 py-3\">\n {isEditing && draft ? (\n <select\n aria-label={tr(\n 'atheletebridge.pages.teamDetail.statusHeader',\n 'Status'\n )}\n className={fieldInputClass}\n value={draft.status}\n onChange={(e) =>\n setDraft((d) =>\n d ? { ...d, status: e.target.value as AthMemberStatus } : d\n )\n }\n >\n {MEMBER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {memberStatusLabel(s)}\n </option>\n ))}\n </select>\n ) : (\n <StatusBadge status={m.status} />\n )}\n </td>\n\n {/* Actions */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-1\">\n {isEditing ? (\n <>\n <button\n type=\"button\"\n onClick={() => void saveEdit(m.id)}\n disabled={busy}\n aria-label={tr('common.save', 'Save')}\n className=\"rounded-lg p-2 text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-60\"\n >\n {busy ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Check className=\"h-4 w-4\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={cancelEdit}\n disabled={busy}\n aria-label={tr('common.cancel', 'Cancel')}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary disabled:opacity-60\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={() => startEdit(m)}\n aria-label={`${tr('common.edit', 'Edit')} ${m.displayName}`}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteTarget(m)}\n disabled={removeMember.isPending}\n aria-label={`${tr('common.delete', 'Delete')} ${m.displayName}`}\n className=\"rounded-lg p-2 text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-60\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n {rowError && (\n <p className=\"border-t border-border-seam px-4 py-2 text-sm text-status-error-text\">\n {rowError}\n </p>\n )}\n </GlassCard>\n )}\n </section>\n\n {/* Fixtures */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('atheletebridge.pages.teamDetail.fixturesTitle', 'Fixtures')} ({fixtures.length})\n </h2>\n <div className=\"flex items-center gap-2\">\n {fixtures.length > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\n className=\"text-sm font-medium text-text-link transition-colors hover:underline\"\n >\n {tr('atheletebridge.pages.teamDetail.viewAll', 'View all')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/fixtures/new?homeTeamId=${t.id}`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" />{' '}\n {tr('atheletebridge.pages.teamDetail.schedule', 'Schedule')}\n </button>\n </div>\n </div>\n {fixtures.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n icon={<CalendarDays className=\"h-10 w-10\" />}\n title={tr('atheletebridge.pages.teamDetail.noFixturesTitle', 'No fixtures scheduled')}\n description={tr(\n 'atheletebridge.pages.teamDetail.noFixturesHint',\n 'Schedule a match for this team to start tracking results.'\n )}\n />\n ) : (\n <GlassCard treatment=\"insight\" className=\"px-4 py-1\">\n <div className=\"divide-y divide-border-seam\">\n {fixtures.map((f) => (\n <MatchupRow\n key={f.id}\n fixture={f}\n onClick={() => navigateTo(`/fixtures/${f.id}`)}\n />\n ))}\n </div>\n </GlassCard>\n )}\n </section>\n\n {/* AI programs generated for this team (BOFF-3574) */}\n <SubjectDecisions className=\"mt-8\" subject={{ kind: 'team', teamId: t.id }} />\n\n {/* Delete member confirmation (BOFF-7617) */}\n <ResponsiveDialog\n open={deleteTarget !== null}\n onOpenChange={(open) => {\n if (!open && !removeMember.isPending) setDeleteTarget(null);\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('atheletebridge.pages.teamDetail.deleteMemberTitle', 'Delete player?')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <p className=\"px-1 text-sm text-text-secondary\">\n {tr(\n 'atheletebridge.pages.teamDetail.deleteMemberMessage',\n 'This will remove \"{name}\" from {team}. This action cannot be undone.'\n )\n .replace('{name}', deleteTarget?.displayName ?? '')\n .replace('{team}', t.name)}\n </p>\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setDeleteTarget(null)}\n disabled={removeMember.isPending}\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 disabled:opacity-60\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => void confirmDeleteMember()}\n disabled={removeMember.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-danger-bg px-4 py-2 text-sm font-semibold text-action-danger-text transition-colors hover:bg-action-danger-bgHover disabled:opacity-60\"\n >\n {removeMember.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {tr('common.delete', 'Delete')}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add member dialog */}\n <ResponsiveDialog open={addOpen} onOpenChange={setAddOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('atheletebridge.pages.teamDetail.addMember', 'Add member')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <form onSubmit={handleAddMember} className=\"space-y-4 px-1\">\n <UserSearchSelect\n userId={memberForm.userId}\n displayName={memberForm.displayName}\n onSelect={({ userId, displayName }) =>\n setMemberForm((p) => ({ ...p, userId, displayName: displayName || p.displayName }))\n }\n required\n />\n <FormField label=\"Display name\" htmlFor=\"displayName\" required>\n <input\n id=\"displayName\"\n className={fieldInputClass}\n value={memberForm.displayName}\n onChange={(e) => setMemberForm((p) => ({ ...p, displayName: e.target.value }))}\n placeholder=\"e.g. Alex Morgan\"\n />\n </FormField>\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Role\" htmlFor=\"role\">\n <select\n id=\"role\"\n className={fieldInputClass}\n value={memberForm.role}\n onChange={(e) =>\n setMemberForm((p) => ({ ...p, role: e.target.value as AthMemberRole }))\n }\n >\n {ROLES.map((r) => (\n <option key={r} value={r}>\n {MEMBER_ROLE_LABELS[r]}\n </option>\n ))}\n </select>\n </FormField>\n <FormField label=\"Status\" htmlFor=\"memberStatus\">\n <select\n id=\"memberStatus\"\n className={fieldInputClass}\n value={memberForm.status}\n onChange={(e) =>\n setMemberForm((p) => ({ ...p, status: e.target.value as AthMemberStatus }))\n }\n >\n {MEMBER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {memberStatusLabel(s)}\n </option>\n ))}\n </select>\n </FormField>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Position\" htmlFor=\"position\">\n <input\n id=\"position\"\n className={fieldInputClass}\n value={memberForm.position}\n onChange={(e) => setMemberForm((p) => ({ ...p, position: e.target.value }))}\n placeholder=\"e.g. Striker\"\n />\n </FormField>\n <FormField label=\"Jersey #\" htmlFor=\"jersey\">\n <input\n id=\"jersey\"\n className={fieldInputClass}\n inputMode=\"numeric\"\n value={memberForm.jerseyNumber}\n onChange={(e) => setMemberForm((p) => ({ ...p, jerseyNumber: e.target.value }))}\n placeholder=\"9\"\n />\n </FormField>\n </div>\n {memberError && <p className=\"text-sm text-status-error-text\">{memberError}</p>}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setAddOpen(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 {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={addMember.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {addMember.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {tr('atheletebridge.pages.teamDetail.addMember', 'Add member')}\n </button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -383,6 +383,12 @@ export interface AthAiModelInfo {
383
383
  label: string;
384
384
  isDefault: boolean;
385
385
  capabilities: string[];
386
+ /**
387
+ * Whether this deployment can call the provider right now (adapter
388
+ * registered and API key set). An unconfigured pair can still be saved; its
389
+ * decisions then run on the next configured provider in the chain.
390
+ */
391
+ configured: boolean;
386
392
  }
387
393
  /**
388
394
  * The workspace's saved default AI provider/model. `autoPilotEnabled` is the