@burdenoff/microfe-atheletebridge 2026.705.2 → 2026.706.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/atheletebridge/AthleteBridgeRoot.js +55 -39
  2. package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
  3. package/dist/atheletebridge/AthleteBridgeRoutes.js +18 -6
  4. package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
  5. package/dist/atheletebridge/components/Crest.js +70 -0
  6. package/dist/atheletebridge/components/Crest.js.map +1 -0
  7. package/dist/atheletebridge/components/FixtureCard.js +8 -72
  8. package/dist/atheletebridge/components/FixtureCard.js.map +1 -1
  9. package/dist/atheletebridge/components/MatchupRow.js +74 -0
  10. package/dist/atheletebridge/components/MatchupRow.js.map +1 -0
  11. package/dist/atheletebridge/components/MetricsCard.js +61 -21
  12. package/dist/atheletebridge/components/MetricsCard.js.map +1 -1
  13. package/dist/atheletebridge/components/ProgramCard.js +75 -43
  14. package/dist/atheletebridge/components/ProgramCard.js.map +1 -1
  15. package/dist/atheletebridge/components/Scoreboard.js +163 -0
  16. package/dist/atheletebridge/components/Scoreboard.js.map +1 -0
  17. package/dist/atheletebridge/components/SessionCard.js +64 -41
  18. package/dist/atheletebridge/components/SessionCard.js.map +1 -1
  19. package/dist/atheletebridge/components/SessionStatusTimeline.js +80 -0
  20. package/dist/atheletebridge/components/SessionStatusTimeline.js.map +1 -0
  21. package/dist/atheletebridge/components/StatusBadge.js +5 -5
  22. package/dist/atheletebridge/components/StatusBadge.js.map +1 -1
  23. package/dist/atheletebridge/components/TeamCard.js +65 -35
  24. package/dist/atheletebridge/components/TeamCard.js.map +1 -1
  25. package/dist/atheletebridge/components/UserAvatar.js +33 -0
  26. package/dist/atheletebridge/components/UserAvatar.js.map +1 -0
  27. package/dist/atheletebridge/components/domain.js +5 -2
  28. package/dist/atheletebridge/components/domain.js.map +1 -1
  29. package/dist/atheletebridge/components/viz/AvatarStack.js +2 -0
  30. package/dist/atheletebridge/components/viz/BarChart.js +83 -0
  31. package/dist/atheletebridge/components/viz/BarChart.js.map +1 -0
  32. package/dist/atheletebridge/components/viz/DonutChart.js +109 -0
  33. package/dist/atheletebridge/components/viz/DonutChart.js.map +1 -0
  34. package/dist/atheletebridge/components/viz/LineChart.js +123 -0
  35. package/dist/atheletebridge/components/viz/LineChart.js.map +1 -0
  36. package/dist/atheletebridge/components/viz/ProgressRing.js +6 -0
  37. package/dist/atheletebridge/components/viz/ProgressRing.js.map +1 -0
  38. package/dist/atheletebridge/components/viz/RatingStars.js +92 -0
  39. package/dist/atheletebridge/components/viz/RatingStars.js.map +1 -0
  40. package/dist/atheletebridge/components/viz/Sparkline.js +74 -0
  41. package/dist/atheletebridge/components/viz/Sparkline.js.map +1 -0
  42. package/dist/atheletebridge/components/viz/StatCard.js +60 -0
  43. package/dist/atheletebridge/components/viz/StatCard.js.map +1 -0
  44. package/dist/atheletebridge/components/viz/StepProgress.js +3 -0
  45. package/dist/atheletebridge/components/viz/Timeline.js +66 -0
  46. package/dist/atheletebridge/components/viz/Timeline.js.map +1 -0
  47. package/dist/atheletebridge/components/viz/index.js +13 -0
  48. package/dist/atheletebridge/components/viz/lib/Avatar.js +1 -0
  49. package/dist/atheletebridge/components/viz/lib/CartesianFrame.js +119 -0
  50. package/dist/atheletebridge/components/viz/lib/CartesianFrame.js.map +1 -0
  51. package/dist/atheletebridge/components/viz/lib/ResponsiveSvg.js +35 -0
  52. package/dist/atheletebridge/components/viz/lib/ResponsiveSvg.js.map +1 -0
  53. package/dist/atheletebridge/components/viz/lib/format.js +17 -0
  54. package/dist/atheletebridge/components/viz/lib/format.js.map +1 -0
  55. package/dist/atheletebridge/components/viz/lib/geometry.js +61 -0
  56. package/dist/atheletebridge/components/viz/lib/geometry.js.map +1 -0
  57. package/dist/atheletebridge/components/viz/lib/hooks.js +39 -0
  58. package/dist/atheletebridge/components/viz/lib/hooks.js.map +1 -0
  59. package/dist/atheletebridge/components/viz/lib/palette.js +27 -0
  60. package/dist/atheletebridge/components/viz/lib/palette.js.map +1 -0
  61. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +369 -163
  62. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
  63. package/dist/atheletebridge/pages/DashboardPage.js +182 -117
  64. package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
  65. package/dist/atheletebridge/pages/StandingsPage.js +119 -95
  66. package/dist/atheletebridge/pages/StandingsPage.js.map +1 -1
  67. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js +372 -0
  68. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js.map +1 -0
  69. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js +511 -0
  70. package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js.map +1 -0
  71. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +120 -131
  72. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
  73. package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js +264 -176
  74. package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js.map +1 -1
  75. package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +84 -47
  76. package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -1
  77. package/dist/atheletebridge/pages/performance/PerformancePage.js +432 -281
  78. package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -1
  79. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +187 -123
  80. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
  81. package/dist/atheletebridge/pages/programs/ProgramsListPage.js +56 -39
  82. package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -1
  83. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +267 -139
  84. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
  85. package/dist/atheletebridge/pages/sessions/SessionFormPage.js +253 -162
  86. package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -1
  87. package/dist/atheletebridge/pages/sessions/SessionsListPage.js +38 -36
  88. package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -1
  89. package/dist/atheletebridge/pages/teams/TeamDetailPage.js +437 -189
  90. package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
  91. package/dist/atheletebridge/pages/teams/TeamFormPage.js +368 -177
  92. package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -1
  93. package/dist/atheletebridge/pages/teams/TeamsListPage.js +3 -0
  94. package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -1
  95. package/dist/atheletebridge/pages/tournaments/TournamentBracketPage.js +298 -0
  96. package/dist/atheletebridge/pages/tournaments/TournamentBracketPage.js.map +1 -0
  97. package/dist/atheletebridge/utils/identity.js +31 -0
  98. package/dist/atheletebridge/utils/identity.js.map +1 -0
  99. package/dist/atheletebridge/utils/timeAgo.js +20 -0
  100. package/dist/atheletebridge/utils/timeAgo.js.map +1 -0
  101. package/package.json +1 -1
@@ -1,177 +1,242 @@
1
1
  import { useAthleteBridge as e } from "../context/AthleteBridgeContext.js";
2
2
  import { PageLayout as t } from "../components/PageLayout.js";
3
- import { MetricsCard as n } from "../components/MetricsCard.js";
4
- import { TeamCard as r } from "../components/TeamCard.js";
5
- import { FixtureCard as i } from "../components/FixtureCard.js";
6
- import { SessionCard as a } from "../components/SessionCard.js";
7
- import { EmptyState as o } from "../components/EmptyState.js";
8
- import { LoadingState as s } from "../components/LoadingState.js";
9
- import { useFixtures as c, usePrograms as l, useSessions as u, useTeams as d } from "../hooks/useAthleteBridgeApi.js";
10
- import { useMemo as f } from "react";
11
- import { ArrowRight as p, CalendarDays as m, ClipboardCheck as h, Dumbbell as g, Gauge as _, Plus as v, Trophy as y, Users as b } from "lucide-react";
12
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
13
- import { EmphasisPanel as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
3
+ import { Timeline as n } from "../components/viz/Timeline.js";
4
+ import "../components/viz/index.js";
5
+ import { MetricsCard as r } from "../components/MetricsCard.js";
6
+ import { TeamCard as i } from "../components/TeamCard.js";
7
+ import { MatchupRow as a } from "../components/MatchupRow.js";
8
+ import { SessionCard as o } from "../components/SessionCard.js";
9
+ import { EmptyState as s } from "../components/EmptyState.js";
10
+ import { LoadingState as c } from "../components/LoadingState.js";
11
+ import { useFixtures as l, usePrograms as u, useRecentActivity as d, useSessions as f, useTeams as p } from "../hooks/useAthleteBridgeApi.js";
12
+ import { timeAgo as m } from "../utils/timeAgo.js";
13
+ import { useMemo as h } from "react";
14
+ import { ArrowRight as g, CalendarDays as _, CheckCircle2 as v, ClipboardCheck as y, Dumbbell as b, Plus as x, TrendingUp as S, Trophy as C, UserPlus as w, Users as T } from "lucide-react";
15
+ import { jsx as E, jsxs as D } from "react/jsx-runtime";
16
+ import { GlassCard as O, PagePurpose as k } from "@burdenoff/fe-libs/ui";
14
17
  //#region src/atheletebridge/pages/DashboardPage.tsx
15
- function T() {
16
- let { navigateTo: T, currentUser: E } = e(), D = d({ pagination: { limit: 50 } }), O = c({ pagination: { limit: 50 } }), k = l({ pagination: { limit: 50 } }), A = u({ pagination: { limit: 50 } }), j = E?.firstName ?? E?.name ?? "coach", M = f(() => D.data?.items ?? [], [D.data]), N = f(() => O.data?.items ?? [], [O.data]), P = f(() => k.data?.items ?? [], [k.data]), F = f(() => A.data?.items ?? [], [A.data]), I = f(() => [...N].filter((e) => e.status === "SCHEDULED" || e.status === "LIVE").sort((e, t) => new Date(e.scheduledAt).getTime() - new Date(t.scheduledAt).getTime()).slice(0, 3), [N]), L = f(() => [...F].filter((e) => e.status === "REQUESTED" || e.status === "CONFIRMED").sort((e, t) => new Date(e.scheduledAt).getTime() - new Date(t.scheduledAt).getTime()).slice(0, 3), [F]), R = f(() => M.slice(0, 6), [M]), z = P.filter((e) => e.status === "PUBLISHED").length;
17
- return D.isLoading || O.isLoading || k.isLoading || A.isLoading ? /* @__PURE__ */ x(t, {
18
+ var A = "inline-flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-semibold text-action-primary-text shadow-elevation-1 transition-[filter,box-shadow] hover:shadow-elevation-2 hover:brightness-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] bg-[linear-gradient(120deg,var(--color-action-primary-bg),color-mix(in_oklab,var(--color-action-primary-bg)_62%,var(--color-accent-purple)),var(--color-action-primary-bg))]", j = {
19
+ "program-published": {
20
+ tone: "success",
21
+ Icon: y
22
+ },
23
+ "member-added": {
24
+ tone: "accent",
25
+ Icon: w
26
+ },
27
+ "fixture-updated": {
28
+ tone: "warning",
29
+ Icon: _
30
+ },
31
+ "session-completed": {
32
+ tone: "info",
33
+ Icon: v
34
+ },
35
+ "team-created": {
36
+ tone: "accent",
37
+ Icon: T
38
+ }
39
+ };
40
+ function M() {
41
+ let { navigateTo: v, currentUser: w } = e(), M = p({ pagination: { limit: 50 } }), N = l({ pagination: { limit: 50 } }), P = u({ pagination: { limit: 50 } }), F = f({ pagination: { limit: 50 } }), I = w?.firstName ?? w?.name ?? "coach", L = h(() => M.data?.items ?? [], [M.data]), R = h(() => N.data?.items ?? [], [N.data]), z = h(() => P.data?.items ?? [], [P.data]), B = h(() => F.data?.items ?? [], [F.data]), V = h(() => [...R].filter((e) => e.status === "SCHEDULED" || e.status === "LIVE").sort((e, t) => new Date(e.scheduledAt).getTime() - new Date(t.scheduledAt).getTime()).slice(0, 5), [R]), H = h(() => [...B].filter((e) => e.status === "REQUESTED" || e.status === "CONFIRMED").sort((e, t) => new Date(e.scheduledAt).getTime() - new Date(t.scheduledAt).getTime()).slice(0, 3), [B]), U = h(() => L.slice(0, 6), [L]), W = z.filter((e) => e.status === "PUBLISHED").length, G = d({
42
+ teams: L,
43
+ fixtures: R,
44
+ sessions: B,
45
+ programs: z,
46
+ limit: 6
47
+ }), K = h(() => G.map((e) => {
48
+ let t = j[e.type], n = t.Icon;
49
+ return {
50
+ id: e.id,
51
+ tone: t.tone,
52
+ icon: /* @__PURE__ */ E(n, {
53
+ className: "h-3 w-3",
54
+ "aria-hidden": "true"
55
+ }),
56
+ title: e.title,
57
+ meta: e.subtitle,
58
+ time: m(e.at)
59
+ };
60
+ }), [G]);
61
+ return M.isLoading || N.isLoading || P.isLoading || F.isLoading ? /* @__PURE__ */ E(t, {
18
62
  title: "AthleteBridge",
19
63
  hero: !0,
20
64
  gradientTitle: !0,
21
- children: /* @__PURE__ */ x(s, { label: "Loading your sporting operation…" })
22
- }) : /* @__PURE__ */ S(t, {
23
- title: `Welcome back, ${j}`,
65
+ children: /* @__PURE__ */ E(c, { label: "Loading your sporting operation…" })
66
+ }) : /* @__PURE__ */ D(t, {
67
+ title: `Welcome back, ${I}`,
24
68
  description: "Your teams, fixtures, training and coaching — one connected record.",
25
69
  hero: !0,
26
70
  gradientTitle: !0,
27
- actions: /* @__PURE__ */ S("div", {
71
+ actions: /* @__PURE__ */ D("div", {
28
72
  className: "flex flex-wrap items-center gap-2",
29
- children: [/* @__PURE__ */ S("button", {
73
+ children: [/* @__PURE__ */ D("button", {
30
74
  type: "button",
31
- onClick: () => T("/performance"),
32
- 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",
33
- children: [/* @__PURE__ */ x(_, { className: "h-4 w-4" }), " Track performance"]
34
- }), /* @__PURE__ */ S("button", {
75
+ onClick: () => v("/teams/new"),
76
+ className: A,
77
+ children: [/* @__PURE__ */ E(x, { className: "h-4 w-4" }), " New team"]
78
+ }), /* @__PURE__ */ D("button", {
35
79
  type: "button",
36
- onClick: () => T("/teams/new"),
37
- 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",
38
- children: [/* @__PURE__ */ x(v, { className: "h-4 w-4" }), " New team"]
80
+ onClick: () => v("/performance"),
81
+ className: A,
82
+ children: [/* @__PURE__ */ E(S, { className: "h-4 w-4" }), " Track performance"]
39
83
  })]
40
84
  }),
41
85
  children: [
42
- /* @__PURE__ */ x(w, {
86
+ /* @__PURE__ */ E(k, {
43
87
  className: "mb-6",
44
88
  children: "AthleteBridge is the operating system for sports, coaching and training. From here you can assemble your roster, schedule fixtures, build training programs, run coaching sessions and track athlete performance — all reading and writing the same workspace-scoped source of truth."
45
89
  }),
46
- /* @__PURE__ */ S(C, {
47
- className: "mb-8",
48
- children: [/* @__PURE__ */ x("h2", {
49
- className: "mb-4 text-lg font-semibold text-text-primary",
50
- children: "At a glance"
51
- }), /* @__PURE__ */ S("div", {
52
- className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
53
- children: [
54
- /* @__PURE__ */ x(n, {
55
- icon: /* @__PURE__ */ x(b, { className: "h-5 w-5" }),
56
- value: M.length,
57
- label: "Teams",
58
- featured: !0
59
- }),
60
- /* @__PURE__ */ x(n, {
61
- icon: /* @__PURE__ */ x(m, { className: "h-5 w-5" }),
62
- value: N.length,
63
- label: "Fixtures"
64
- }),
65
- /* @__PURE__ */ x(n, {
66
- icon: /* @__PURE__ */ x(g, { className: "h-5 w-5" }),
67
- value: `${z}/${P.length}`,
68
- label: "Programs published"
69
- }),
70
- /* @__PURE__ */ x(n, {
71
- icon: /* @__PURE__ */ x(h, { className: "h-5 w-5" }),
72
- value: F.length,
73
- label: "Sessions"
74
- })
75
- ]
76
- })]
90
+ /* @__PURE__ */ D("div", {
91
+ className: "mb-8 grid grid-cols-2 gap-4 lg:grid-cols-4",
92
+ children: [
93
+ /* @__PURE__ */ E(r, {
94
+ icon: /* @__PURE__ */ E(T, { className: "h-5 w-5" }),
95
+ value: L.length,
96
+ label: "Teams",
97
+ tone: "accent",
98
+ featured: !0
99
+ }),
100
+ /* @__PURE__ */ E(r, {
101
+ icon: /* @__PURE__ */ E(_, { className: "h-5 w-5" }),
102
+ value: R.length,
103
+ label: "Fixtures",
104
+ tone: "accent"
105
+ }),
106
+ /* @__PURE__ */ E(r, {
107
+ icon: /* @__PURE__ */ E(b, { className: "h-5 w-5" }),
108
+ value: `${W}/${z.length}`,
109
+ label: "Programs published",
110
+ tone: "accent"
111
+ }),
112
+ /* @__PURE__ */ E(r, {
113
+ icon: /* @__PURE__ */ E(y, { className: "h-5 w-5" }),
114
+ value: B.length,
115
+ label: "Sessions",
116
+ tone: "accent"
117
+ })
118
+ ]
77
119
  }),
78
- /* @__PURE__ */ S("section", {
79
- className: "mb-8",
80
- children: [/* @__PURE__ */ S("div", {
81
- className: "mb-4 flex items-center justify-between",
82
- children: [/* @__PURE__ */ x("h2", {
83
- className: "text-lg font-semibold text-text-primary",
84
- children: "Upcoming fixtures"
85
- }), /* @__PURE__ */ S("button", {
86
- type: "button",
87
- onClick: () => T("/fixtures"),
88
- className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
89
- children: ["View all ", /* @__PURE__ */ x(p, { className: "h-3.5 w-3.5" })]
120
+ /* @__PURE__ */ D("div", {
121
+ className: "mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3",
122
+ children: [/* @__PURE__ */ D("section", {
123
+ className: "lg:col-span-2",
124
+ children: [/* @__PURE__ */ D("div", {
125
+ className: "mb-4 flex items-center justify-between",
126
+ children: [/* @__PURE__ */ E("h2", {
127
+ className: "text-lg font-semibold text-text-primary",
128
+ children: "Upcoming fixtures"
129
+ }), /* @__PURE__ */ D("button", {
130
+ type: "button",
131
+ onClick: () => v("/fixtures"),
132
+ className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
133
+ children: ["View all fixtures ", /* @__PURE__ */ E(g, { className: "h-3.5 w-3.5" })]
134
+ })]
135
+ }), V.length === 0 ? /* @__PURE__ */ E(s, {
136
+ illustration: "empty-calendar",
137
+ icon: /* @__PURE__ */ E(_, { className: "h-10 w-10" }),
138
+ title: "No upcoming fixtures",
139
+ description: "Schedule a match to start tracking results and standings.",
140
+ action: /* @__PURE__ */ D("button", {
141
+ type: "button",
142
+ onClick: () => v("/fixtures/new"),
143
+ className: A,
144
+ children: [/* @__PURE__ */ E(x, { className: "h-4 w-4" }), " Schedule fixture"]
145
+ })
146
+ }) : /* @__PURE__ */ E(O, {
147
+ treatment: "glass",
148
+ className: "divide-y divide-border-seam px-4 py-1 sm:px-5",
149
+ children: V.map((e) => /* @__PURE__ */ E(a, {
150
+ fixture: e,
151
+ onClick: () => v(`/fixtures/${e.id}`)
152
+ }, e.id))
90
153
  })]
91
- }), I.length === 0 ? /* @__PURE__ */ x(o, {
92
- illustration: "empty-calendar",
93
- icon: /* @__PURE__ */ x(m, { className: "h-10 w-10" }),
94
- title: "No upcoming fixtures",
95
- description: "Schedule a match to start tracking results and standings.",
96
- action: /* @__PURE__ */ x("button", {
97
- type: "button",
98
- onClick: () => T("/fixtures/new"),
99
- 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",
100
- children: "Schedule fixture"
154
+ }), /* @__PURE__ */ D("section", { children: [/* @__PURE__ */ E("div", {
155
+ className: "mb-4 flex items-center min-h-[1.75rem]",
156
+ children: /* @__PURE__ */ E("h2", {
157
+ className: "text-lg font-semibold text-text-primary",
158
+ children: "Recent activity"
101
159
  })
102
- }) : /* @__PURE__ */ x("div", {
103
- className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",
104
- children: I.map((e) => /* @__PURE__ */ x(i, {
105
- fixture: e,
106
- onClick: () => T(`/fixtures/${e.id}`)
107
- }, e.id))
108
- })]
160
+ }), K.length === 0 ? /* @__PURE__ */ E(O, {
161
+ treatment: "metric",
162
+ className: "p-6",
163
+ children: /* @__PURE__ */ E(s, {
164
+ illustration: "empty-data",
165
+ icon: /* @__PURE__ */ E(S, { className: "h-10 w-10" }),
166
+ title: "No recent activity",
167
+ description: "Create teams, publish programs or record results — the latest moves land here."
168
+ })
169
+ }) : /* @__PURE__ */ E(O, {
170
+ treatment: "metric",
171
+ className: "p-5",
172
+ children: /* @__PURE__ */ E(n, { items: K })
173
+ })] })]
109
174
  }),
110
- /* @__PURE__ */ S("section", {
175
+ /* @__PURE__ */ D("section", {
111
176
  className: "mb-8",
112
- children: [/* @__PURE__ */ S("div", {
177
+ children: [/* @__PURE__ */ D("div", {
113
178
  className: "mb-4 flex items-center justify-between",
114
- children: [/* @__PURE__ */ x("h2", {
179
+ children: [/* @__PURE__ */ E("h2", {
115
180
  className: "text-lg font-semibold text-text-primary",
116
181
  children: "Upcoming sessions"
117
- }), /* @__PURE__ */ S("button", {
182
+ }), /* @__PURE__ */ D("button", {
118
183
  type: "button",
119
- onClick: () => T("/sessions"),
184
+ onClick: () => v("/sessions"),
120
185
  className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
121
- children: ["View all ", /* @__PURE__ */ x(p, { className: "h-3.5 w-3.5" })]
186
+ children: ["View all ", /* @__PURE__ */ E(g, { className: "h-3.5 w-3.5" })]
122
187
  })]
123
- }), L.length === 0 ? /* @__PURE__ */ x(o, {
188
+ }), H.length === 0 ? /* @__PURE__ */ E(s, {
124
189
  illustration: "empty-calendar",
125
- icon: /* @__PURE__ */ x(h, { className: "h-10 w-10" }),
190
+ icon: /* @__PURE__ */ E(y, { className: "h-10 w-10" }),
126
191
  title: "No upcoming sessions",
127
192
  description: "Book a coaching session with an athlete to get started.",
128
- action: /* @__PURE__ */ x("button", {
193
+ action: /* @__PURE__ */ D("button", {
129
194
  type: "button",
130
- onClick: () => T("/sessions/new"),
131
- 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",
132
- children: "Book session"
195
+ onClick: () => v("/sessions/new"),
196
+ className: A,
197
+ children: [/* @__PURE__ */ E(x, { className: "h-4 w-4" }), " Book session"]
133
198
  })
134
- }) : /* @__PURE__ */ x("div", {
199
+ }) : /* @__PURE__ */ E("div", {
135
200
  className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",
136
- children: L.map((e) => /* @__PURE__ */ x(a, {
201
+ children: H.map((e) => /* @__PURE__ */ E(o, {
137
202
  session: e,
138
- onClick: () => T(`/sessions/${e.id}`)
203
+ onClick: () => v(`/sessions/${e.id}`)
139
204
  }, e.id))
140
205
  })]
141
206
  }),
142
- /* @__PURE__ */ S("section", { children: [/* @__PURE__ */ S("div", {
207
+ /* @__PURE__ */ D("section", { children: [/* @__PURE__ */ D("div", {
143
208
  className: "mb-4 flex items-center justify-between",
144
- children: [/* @__PURE__ */ x("h2", {
209
+ children: [/* @__PURE__ */ E("h2", {
145
210
  className: "text-lg font-semibold text-text-primary",
146
211
  children: "Your teams"
147
- }), /* @__PURE__ */ S("button", {
212
+ }), /* @__PURE__ */ D("button", {
148
213
  type: "button",
149
- onClick: () => T("/teams"),
214
+ onClick: () => v("/teams"),
150
215
  className: "flex items-center gap-1 text-sm font-medium text-primary hover:underline",
151
- children: ["View all ", /* @__PURE__ */ x(p, { className: "h-3.5 w-3.5" })]
216
+ children: ["View all ", /* @__PURE__ */ E(g, { className: "h-3.5 w-3.5" })]
152
217
  })]
153
- }), R.length === 0 ? /* @__PURE__ */ x(o, {
218
+ }), U.length === 0 ? /* @__PURE__ */ E(s, {
154
219
  illustration: "empty-data",
155
- icon: /* @__PURE__ */ x(y, { className: "h-10 w-10" }),
220
+ icon: /* @__PURE__ */ E(C, { className: "h-10 w-10" }),
156
221
  title: "No teams yet",
157
222
  description: "Create your first team to build a roster and schedule fixtures.",
158
- action: /* @__PURE__ */ x("button", {
223
+ action: /* @__PURE__ */ D("button", {
159
224
  type: "button",
160
- onClick: () => T("/teams/new"),
161
- 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",
162
- children: "Create team"
225
+ onClick: () => v("/teams/new"),
226
+ className: A,
227
+ children: [/* @__PURE__ */ E(x, { className: "h-4 w-4" }), " Create team"]
163
228
  })
164
- }) : /* @__PURE__ */ x("div", {
229
+ }) : /* @__PURE__ */ E("div", {
165
230
  className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",
166
- children: R.map((e) => /* @__PURE__ */ x(r, {
231
+ children: U.map((e) => /* @__PURE__ */ E(i, {
167
232
  team: e,
168
- onClick: () => T(`/teams/${e.id}`)
233
+ onClick: () => v(`/teams/${e.id}`)
169
234
  }, e.id))
170
235
  })] })
171
236
  ]
172
237
  });
173
238
  }
174
239
  //#endregion
175
- export { T as DashboardPage };
240
+ export { M as DashboardPage };
176
241
 
177
242
  //# sourceMappingURL=DashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/atheletebridge/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard / Overview\n * @module atheletebridge/pages\n *\n * The AthleteBridge landing page: a workspace-wide command center that reads\n * live counts and recent items across teams, fixtures, programs and sessions\n * from the real `ath*` API.\n */\n\nimport { useMemo } from 'react';\nimport {\n Users,\n CalendarDays,\n Dumbbell,\n ClipboardCheck,\n ArrowRight,\n Plus,\n Trophy,\n Gauge,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { TeamCard } from '../components/TeamCard';\nimport { FixtureCard } from '../components/FixtureCard';\nimport { SessionCard } from '../components/SessionCard';\nimport { EmptyState } from '../components/EmptyState';\nimport { LoadingState } from '../components/LoadingState';\nimport { useAthleteBridge } from '../context/AthleteBridgeContext';\nimport { useTeams, useFixtures, usePrograms, useSessions } from '../hooks/useAthleteBridgeApi';\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useAthleteBridge();\n\n const teams = useTeams({ pagination: { limit: 50 } });\n const fixtures = useFixtures({ pagination: { limit: 50 } });\n const programs = usePrograms({ pagination: { limit: 50 } });\n const sessions = useSessions({ pagination: { limit: 50 } });\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'coach';\n\n const teamItems = useMemo(() => teams.data?.items ?? [], [teams.data]);\n const fixtureItems = useMemo(() => fixtures.data?.items ?? [], [fixtures.data]);\n const programItems = useMemo(() => programs.data?.items ?? [], [programs.data]);\n const sessionItems = useMemo(() => sessions.data?.items ?? [], [sessions.data]);\n\n const upcomingFixtures = useMemo(\n () =>\n [...fixtureItems]\n .filter((f) => f.status === 'SCHEDULED' || f.status === 'LIVE')\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime())\n .slice(0, 3),\n [fixtureItems]\n );\n\n const upcomingSessions = useMemo(\n () =>\n [...sessionItems]\n .filter((s) => s.status === 'REQUESTED' || s.status === 'CONFIRMED')\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime())\n .slice(0, 3),\n [sessionItems]\n );\n\n const recentTeams = useMemo(() => teamItems.slice(0, 6), [teamItems]);\n\n const publishedPrograms = programItems.filter((p) => p.status === 'PUBLISHED').length;\n\n const isLoading =\n teams.isLoading || fixtures.isLoading || programs.isLoading || sessions.isLoading;\n\n if (isLoading) {\n return (\n <PageLayout title=\"AthleteBridge\" hero gradientTitle>\n <LoadingState label=\"Loading your sporting operation…\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={`Welcome back, ${firstName}`}\n description=\"Your teams, fixtures, training and coaching — one connected record.\"\n hero\n gradientTitle\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/performance')}\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 <Gauge className=\"h-4 w-4\" /> Track performance\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> New team\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n AthleteBridge is the operating system for sports, coaching and training. From here you can\n assemble your roster, schedule fixtures, build training programs, run coaching sessions and\n track athlete performance — all reading and writing the same workspace-scoped source of\n truth.\n </PagePurpose>\n\n {/* KPIs */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={teamItems.length}\n label=\"Teams\"\n featured\n />\n <MetricsCard\n icon={<CalendarDays className=\"h-5 w-5\" />}\n value={fixtureItems.length}\n label=\"Fixtures\"\n />\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={`${publishedPrograms}/${programItems.length}`}\n label=\"Programs published\"\n />\n <MetricsCard\n icon={<ClipboardCheck className=\"h-5 w-5\" />}\n value={sessionItems.length}\n label=\"Sessions\"\n />\n </div>\n </EmphasisPanel>\n\n {/* Upcoming fixtures */}\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\">Upcoming fixtures</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {upcomingFixtures.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n icon={<CalendarDays className=\"h-10 w-10\" />}\n title=\"No upcoming fixtures\"\n description=\"Schedule a match to start tracking results and standings.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures/new')}\n 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\"\n >\n Schedule fixture\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {upcomingFixtures.map((fixture) => (\n <FixtureCard\n key={fixture.id}\n fixture={fixture}\n onClick={() => navigateTo(`/fixtures/${fixture.id}`)}\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Upcoming sessions */}\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\">Upcoming sessions</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {upcomingSessions.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n icon={<ClipboardCheck className=\"h-10 w-10\" />}\n title=\"No upcoming sessions\"\n description=\"Book a coaching session with an athlete to get started.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions/new')}\n 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\"\n >\n Book session\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {upcomingSessions.map((session) => (\n <SessionCard\n key={session.id}\n session={session}\n onClick={() => navigateTo(`/sessions/${session.id}`)}\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Teams */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Your teams</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {recentTeams.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Trophy className=\"h-10 w-10\" />}\n title=\"No teams yet\"\n description=\"Create your first team to build a roster and schedule fixtures.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams/new')}\n 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\"\n >\n Create team\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {recentTeams.map((team) => (\n <TeamCard key={team.id} team={team} onClick={() => navigateTo(`/teams/${team.id}`)} />\n ))}\n </div>\n )}\n </section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/atheletebridge/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard / Overview\n * @module atheletebridge/pages\n *\n * The AthleteBridge landing page: a coach command-center that reads live counts\n * and recent items across teams, fixtures, programs and sessions from the real\n * `ath*` API. Elevated to the concept bar: glass teal-stat KPI tiles, a\n * crest-vs-crest upcoming-fixtures rail, and a real (derived) recent-activity\n * timeline — every item mapped to a real record, nothing fabricated.\n */\n\nimport { useMemo } from 'react';\nimport {\n Users,\n CalendarDays,\n Dumbbell,\n ClipboardCheck,\n ArrowRight,\n Plus,\n Trophy,\n TrendingUp,\n UserPlus,\n CheckCircle2,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { TeamCard } from '../components/TeamCard';\nimport { MatchupRow } from '../components/MatchupRow';\nimport { SessionCard } from '../components/SessionCard';\nimport { EmptyState } from '../components/EmptyState';\nimport { LoadingState } from '../components/LoadingState';\nimport { Timeline, type TimelineItem, type TimelineTone } from '../components/viz';\nimport { useAthleteBridge } from '../context/AthleteBridgeContext';\nimport {\n useTeams,\n useFixtures,\n usePrograms,\n useSessions,\n useRecentActivity,\n type ActivityType,\n} from '../hooks/useAthleteBridgeApi';\nimport { timeAgo } from '../utils/timeAgo';\n\n/**\n * Brand gradient CTA — orange action-primary blended toward accent-purple, the\n * same brand gradient the hero title uses. Token-only (no raw colors).\n */\nconst GRADIENT_CTA =\n 'inline-flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-semibold text-action-primary-text shadow-elevation-1 transition-[filter,box-shadow] hover:shadow-elevation-2 hover:brightness-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] bg-[linear-gradient(120deg,var(--color-action-primary-bg),color-mix(in_oklab,var(--color-action-primary-bg)_62%,var(--color-accent-purple)),var(--color-action-primary-bg))]';\n\n/** Per-activity-type node styling for the recent-activity timeline. */\nconst ACTIVITY_VISUAL: Record<ActivityType, { tone: TimelineTone; Icon: LucideIcon }> = {\n 'program-published': { tone: 'success', Icon: ClipboardCheck },\n 'member-added': { tone: 'accent', Icon: UserPlus },\n 'fixture-updated': { tone: 'warning', Icon: CalendarDays },\n 'session-completed': { tone: 'info', Icon: CheckCircle2 },\n 'team-created': { tone: 'accent', Icon: Users },\n};\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useAthleteBridge();\n\n const teams = useTeams({ pagination: { limit: 50 } });\n const fixtures = useFixtures({ pagination: { limit: 50 } });\n const programs = usePrograms({ pagination: { limit: 50 } });\n const sessions = useSessions({ pagination: { limit: 50 } });\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'coach';\n\n const teamItems = useMemo(() => teams.data?.items ?? [], [teams.data]);\n const fixtureItems = useMemo(() => fixtures.data?.items ?? [], [fixtures.data]);\n const programItems = useMemo(() => programs.data?.items ?? [], [programs.data]);\n const sessionItems = useMemo(() => sessions.data?.items ?? [], [sessions.data]);\n\n const upcomingFixtures = useMemo(\n () =>\n [...fixtureItems]\n .filter((f) => f.status === 'SCHEDULED' || f.status === 'LIVE')\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime())\n .slice(0, 5),\n [fixtureItems]\n );\n\n const upcomingSessions = useMemo(\n () =>\n [...sessionItems]\n .filter((s) => s.status === 'REQUESTED' || s.status === 'CONFIRMED')\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime())\n .slice(0, 3),\n [sessionItems]\n );\n\n const recentTeams = useMemo(() => teamItems.slice(0, 6), [teamItems]);\n\n const publishedPrograms = programItems.filter((p) => p.status === 'PUBLISHED').length;\n\n const activity = useRecentActivity({\n teams: teamItems,\n fixtures: fixtureItems,\n sessions: sessionItems,\n programs: programItems,\n limit: 6,\n });\n\n const activityTimeline = useMemo<TimelineItem[]>(\n () =>\n activity.map((item) => {\n const visual = ACTIVITY_VISUAL[item.type];\n const Icon = visual.Icon;\n return {\n id: item.id,\n tone: visual.tone,\n icon: <Icon className=\"h-3 w-3\" aria-hidden=\"true\" />,\n title: item.title,\n meta: item.subtitle,\n time: timeAgo(item.at),\n };\n }),\n [activity]\n );\n\n const isLoading =\n teams.isLoading || fixtures.isLoading || programs.isLoading || sessions.isLoading;\n\n if (isLoading) {\n return (\n <PageLayout title=\"AthleteBridge\" hero gradientTitle>\n <LoadingState label=\"Loading your sporting operation…\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={`Welcome back, ${firstName}`}\n description=\"Your teams, fixtures, training and coaching — one connected record.\"\n hero\n gradientTitle\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button type=\"button\" onClick={() => navigateTo('/teams/new')} className={GRADIENT_CTA}>\n <Plus className=\"h-4 w-4\" /> New team\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/performance')}\n className={GRADIENT_CTA}\n >\n <TrendingUp className=\"h-4 w-4\" /> Track performance\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n AthleteBridge is the operating system for sports, coaching and training. From here you can\n assemble your roster, schedule fixtures, build training programs, run coaching sessions and\n track athlete performance — all reading and writing the same workspace-scoped source of\n truth.\n </PagePurpose>\n\n {/* KPIs — glass tiles with teal stat values (2-col mobile → 4-col lg) */}\n <div className=\"mb-8 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={teamItems.length}\n label=\"Teams\"\n tone=\"accent\"\n featured\n />\n <MetricsCard\n icon={<CalendarDays className=\"h-5 w-5\" />}\n value={fixtureItems.length}\n label=\"Fixtures\"\n tone=\"accent\"\n />\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={`${publishedPrograms}/${programItems.length}`}\n label=\"Programs published\"\n tone=\"accent\"\n />\n <MetricsCard\n icon={<ClipboardCheck className=\"h-5 w-5\" />}\n value={sessionItems.length}\n label=\"Sessions\"\n tone=\"accent\"\n />\n </div>\n\n {/* Upcoming fixtures (matchup rail) + Recent activity (timeline) */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Fixtures — spans two columns on large screens */}\n <section className=\"lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Upcoming fixtures</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all fixtures <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {upcomingFixtures.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n icon={<CalendarDays className=\"h-10 w-10\" />}\n title=\"No upcoming fixtures\"\n description=\"Schedule a match to start tracking results and standings.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures/new')}\n className={GRADIENT_CTA}\n >\n <Plus className=\"h-4 w-4\" /> Schedule fixture\n </button>\n }\n />\n ) : (\n <GlassCard treatment=\"glass\" className=\"divide-y divide-border-seam px-4 py-1 sm:px-5\">\n {upcomingFixtures.map((fixture) => (\n <MatchupRow\n key={fixture.id}\n fixture={fixture}\n onClick={() => navigateTo(`/fixtures/${fixture.id}`)}\n />\n ))}\n </GlassCard>\n )}\n </section>\n\n {/* Recent activity */}\n <section>\n <div className=\"mb-4 flex items-center min-h-[1.75rem]\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recent activity</h2>\n </div>\n {activityTimeline.length === 0 ? (\n <GlassCard treatment=\"metric\" className=\"p-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<TrendingUp className=\"h-10 w-10\" />}\n title=\"No recent activity\"\n description=\"Create teams, publish programs or record results — the latest moves land here.\"\n />\n </GlassCard>\n ) : (\n <GlassCard treatment=\"metric\" className=\"p-5\">\n <Timeline items={activityTimeline} />\n </GlassCard>\n )}\n </section>\n </div>\n\n {/* Upcoming sessions */}\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\">Upcoming sessions</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {upcomingSessions.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n icon={<ClipboardCheck className=\"h-10 w-10\" />}\n title=\"No upcoming sessions\"\n description=\"Book a coaching session with an athlete to get started.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions/new')}\n className={GRADIENT_CTA}\n >\n <Plus className=\"h-4 w-4\" /> Book session\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {upcomingSessions.map((session) => (\n <SessionCard\n key={session.id}\n session={session}\n onClick={() => navigateTo(`/sessions/${session.id}`)}\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Teams */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Your teams</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {recentTeams.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Trophy className=\"h-10 w-10\" />}\n title=\"No teams yet\"\n description=\"Create your first team to build a roster and schedule fixtures.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams/new')}\n className={GRADIENT_CTA}\n >\n <Plus className=\"h-4 w-4\" /> Create team\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {recentTeams.map((team) => (\n <TeamCard key={team.id} team={team} onClick={() => navigateTo(`/teams/${team.id}`)} />\n ))}\n </div>\n )}\n </section>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiDA,IAAM,IACJ,kdAGI,IAAkF;CACtF,qBAAqB;EAAE,MAAM;EAAW,MAAM;EAAgB;CAC9D,gBAAgB;EAAE,MAAM;EAAU,MAAM;EAAU;CAClD,mBAAmB;EAAE,MAAM;EAAW,MAAM;EAAc;CAC1D,qBAAqB;EAAE,MAAM;EAAQ,MAAM;EAAc;CACzD,gBAAgB;EAAE,MAAM;EAAU,MAAM;EAAO;CAChD;AAED,SAAgB,IAAgB;CAC9B,IAAM,EAAE,eAAY,mBAAgB,GAAkB,EAEhD,IAAQ,EAAS,EAAE,YAAY,EAAE,OAAO,IAAI,EAAE,CAAC,EAC/C,IAAW,EAAY,EAAE,YAAY,EAAE,OAAO,IAAI,EAAE,CAAC,EACrD,IAAW,EAAY,EAAE,YAAY,EAAE,OAAO,IAAI,EAAE,CAAC,EACrD,IAAW,EAAY,EAAE,YAAY,EAAE,OAAO,IAAI,EAAE,CAAC,EAErD,IAAY,GAAa,aAAa,GAAa,QAAQ,SAE3D,IAAY,QAAc,EAAM,MAAM,SAAS,EAAE,EAAE,CAAC,EAAM,KAAK,CAAC,EAChE,IAAe,QAAc,EAAS,MAAM,SAAS,EAAE,EAAE,CAAC,EAAS,KAAK,CAAC,EACzE,IAAe,QAAc,EAAS,MAAM,SAAS,EAAE,EAAE,CAAC,EAAS,KAAK,CAAC,EACzE,IAAe,QAAc,EAAS,MAAM,SAAS,EAAE,EAAE,CAAC,EAAS,KAAK,CAAC,EAEzE,IAAmB,QAErB,CAAC,GAAG,EAAa,CACd,QAAQ,MAAM,EAAE,WAAW,eAAe,EAAE,WAAW,OAAO,CAC9D,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,YAAY,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,CACrF,MAAM,GAAG,EAAE,EAChB,CAAC,EAAa,CACf,EAEK,IAAmB,QAErB,CAAC,GAAG,EAAa,CACd,QAAQ,MAAM,EAAE,WAAW,eAAe,EAAE,WAAW,YAAY,CACnE,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,YAAY,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,CACrF,MAAM,GAAG,EAAE,EAChB,CAAC,EAAa,CACf,EAEK,IAAc,QAAc,EAAU,MAAM,GAAG,EAAE,EAAE,CAAC,EAAU,CAAC,EAE/D,IAAoB,EAAa,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC,QAEzE,IAAW,EAAkB;EACjC,OAAO;EACP,UAAU;EACV,UAAU;EACV,UAAU;EACV,OAAO;EACR,CAAC,EAEI,IAAmB,QAErB,EAAS,KAAK,MAAS;EACrB,IAAM,IAAS,EAAgB,EAAK,OAC9B,IAAO,EAAO;AACpB,SAAO;GACL,IAAI,EAAK;GACT,MAAM,EAAO;GACb,MAAM,kBAAC,GAAD;IAAM,WAAU;IAAU,eAAY;IAAS,CAAA;GACrD,OAAO,EAAK;GACZ,MAAM,EAAK;GACX,MAAM,EAAQ,EAAK,GAAG;GACvB;GACD,EACJ,CAAC,EAAS,CACX;AAaD,QAVE,EAAM,aAAa,EAAS,aAAa,EAAS,aAAa,EAAS,YAItE,kBAAC,GAAD;EAAY,OAAM;EAAgB,MAAA;EAAK,eAAA;YACrC,kBAAC,GAAD,EAAc,OAAM,oCAAqC,CAAA;EAC9C,CAAA,GAKf,kBAAC,GAAD;EACE,OAAO,iBAAiB;EACxB,aAAY;EACZ,MAAA;EACA,eAAA;EACA,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IAAQ,MAAK;IAAS,eAAe,EAAW,aAAa;IAAE,WAAW;cAA1E,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,YACrB;OACT,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,eAAe;IACzC,WAAW;cAHb,CAKE,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,EAAA,qBAC3B;MACL;;YAjBV;GAoBE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAKhB,CAAA;GAGd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,OAAO,EAAU;MACjB,OAAM;MACN,MAAK;MACL,UAAA;MACA,CAAA;KACF,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;MAC1C,OAAO,EAAa;MACpB,OAAM;MACN,MAAK;MACL,CAAA;KACF,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;MACtC,OAAO,GAAG,EAAkB,GAAG,EAAa;MAC5C,OAAM;MACN,MAAK;MACL,CAAA;KACF,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;MAC5C,OAAO,EAAa;MACpB,OAAM;MACN,MAAK;MACL,CAAA;KACE;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,WAAD;KAAS,WAAU;eAAnB,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA0C;OAAsB,CAAA,EAC9E,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,YAAY;OACtC,WAAU;iBAHZ,CAIC,sBACmB,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,CACjD;SACL;SACL,EAAiB,WAAW,IAC3B,kBAAC,GAAD;MACE,cAAa;MACb,MAAM,kBAAC,GAAD,EAAc,WAAU,aAAc,CAAA;MAC5C,OAAM;MACN,aAAY;MACZ,QACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,gBAAgB;OAC1C,WAAW;iBAHb,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,oBACrB;;MAEX,CAAA,GAEF,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBACpC,EAAiB,KAAK,MACrB,kBAAC,GAAD;OAEW;OACT,eAAe,EAAW,aAAa,EAAQ,KAAK;OACpD,EAHK,EAAQ,GAGb,CACF;MACQ,CAAA,CAEN;QAGV,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAoB,CAAA;KACxE,CAAA,EACL,EAAiB,WAAW,IAC3B,kBAAC,GAAD;KAAW,WAAU;KAAS,WAAU;eACtC,kBAAC,GAAD;MACE,cAAa;MACb,MAAM,kBAAC,GAAD,EAAY,WAAU,aAAc,CAAA;MAC1C,OAAM;MACN,aAAY;MACZ,CAAA;KACQ,CAAA,GAEZ,kBAAC,GAAD;KAAW,WAAU;KAAS,WAAU;eACtC,kBAAC,GAAD,EAAU,OAAO,GAAoB,CAAA;KAC3B,CAAA,CAEN,EAAA,CAAA,CACN;;GAGN,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAsB,CAAA,EAC9E,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,YAAY;MACtC,WAAU;gBAHZ,CAIC,aACU,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,CACxC;QACL;QACL,EAAiB,WAAW,IAC3B,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAgB,WAAU,aAAc,CAAA;KAC9C,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,gBAAgB;MAC1C,WAAW;gBAHb,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBACrB;;KAEX,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAiB,KAAK,MACrB,kBAAC,GAAD;MAEW;MACT,eAAe,EAAW,aAAa,EAAQ,KAAK;MACpD,EAHK,EAAQ,GAGb,CACF;KACE,CAAA,CAEA;;GAGV,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAAe,CAAA,EACvE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,SAAS;KACnC,WAAU;eAHZ,CAIC,aACU,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,CACxC;OACL;OACL,EAAY,WAAW,IACtB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;IACtC,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,aAAa;KACvC,WAAW;eAHb,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,eACrB;;IAEX,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAY,KAAK,MAChB,kBAAC,GAAD;KAA8B;KAAM,eAAe,EAAW,UAAU,EAAK,KAAK;KAAI,EAAvE,EAAK,GAAkE,CACtF;IACE,CAAA,CAEA,EAAA,CAAA;GACC"}