@burdenoff/microfe-atheletebridge 2026.805.1 → 2026.829.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/atheletebridge/components/LoadingState.d.ts.map +1 -1
  2. package/dist/atheletebridge/components/LoadingState.js +39 -32
  3. package/dist/atheletebridge/components/LoadingState.js.map +1 -1
  4. package/dist/atheletebridge/components/TeamCard.d.ts.map +1 -1
  5. package/dist/atheletebridge/components/TeamCard.js +45 -49
  6. package/dist/atheletebridge/components/TeamCard.js.map +1 -1
  7. package/dist/atheletebridge/hooks/index.d.ts +1 -1
  8. package/dist/atheletebridge/hooks/index.d.ts.map +1 -1
  9. package/dist/atheletebridge/hooks/useAthleteBridgeApi.d.ts +41 -1
  10. package/dist/atheletebridge/hooks/useAthleteBridgeApi.d.ts.map +1 -1
  11. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +58 -28
  12. package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
  13. package/dist/atheletebridge/pages/DashboardPage.d.ts.map +1 -1
  14. package/dist/atheletebridge/pages/DashboardPage.js +56 -24
  15. package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
  16. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.d.ts +6 -2
  17. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.d.ts.map +1 -1
  18. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js +330 -174
  19. package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js.map +1 -1
  20. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.d.ts.map +1 -1
  21. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +110 -106
  22. package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
  23. package/dist/atheletebridge/pages/fixtures/FixturesListPage.d.ts.map +1 -1
  24. package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +80 -71
  25. package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -1
  26. package/dist/atheletebridge/pages/onboarding/OnboardingPage.d.ts.map +1 -1
  27. package/dist/atheletebridge/pages/onboarding/OnboardingPage.js +244 -210
  28. package/dist/atheletebridge/pages/onboarding/OnboardingPage.js.map +1 -1
  29. package/dist/atheletebridge/pages/performance/PerformancePage.d.ts +7 -0
  30. package/dist/atheletebridge/pages/performance/PerformancePage.d.ts.map +1 -1
  31. package/dist/atheletebridge/pages/performance/PerformancePage.js +526 -362
  32. package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -1
  33. package/dist/atheletebridge/pages/programs/ProgramDetailPage.d.ts.map +1 -1
  34. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +169 -143
  35. package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
  36. package/dist/atheletebridge/pages/programs/ProgramsListPage.d.ts.map +1 -1
  37. package/dist/atheletebridge/pages/programs/ProgramsListPage.js +77 -72
  38. package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -1
  39. package/dist/atheletebridge/pages/sessions/SessionDetailPage.d.ts.map +1 -1
  40. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +104 -86
  41. package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
  42. package/dist/atheletebridge/pages/sessions/SessionsListPage.d.ts.map +1 -1
  43. package/dist/atheletebridge/pages/sessions/SessionsListPage.js +54 -49
  44. package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -1
  45. package/dist/atheletebridge/pages/teams/TeamDetailPage.d.ts.map +1 -1
  46. package/dist/atheletebridge/pages/teams/TeamDetailPage.js +286 -267
  47. package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
  48. package/dist/atheletebridge/pages/teams/TeamFormPage.d.ts.map +1 -1
  49. package/dist/atheletebridge/pages/teams/TeamFormPage.js +185 -180
  50. package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -1
  51. package/dist/atheletebridge/pages/teams/TeamsListPage.d.ts.map +1 -1
  52. package/dist/atheletebridge/pages/teams/TeamsListPage.js +70 -74
  53. package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -1
  54. package/package.json +1 -1
@@ -65,8 +65,8 @@ function P() {
65
65
  gradientTitle: !0,
66
66
  children: /* @__PURE__ */ O(l, { label: y("atheletebridge.pages.dashboard.loading", "Loading your sporting operation…") })
67
67
  }) : /* @__PURE__ */ k(n, {
68
- title: `Welcome back, ${z}`,
69
- description: "Your teams, fixtures, training and coaching — one connected record.",
68
+ title: `${y("atheletebridge.pages.dashboard.welcomeBack", "Welcome back")}, ${z}`,
69
+ description: y("atheletebridge.pages.dashboard.description", "Your teams, fixtures, training and coaching — one connected record."),
70
70
  hero: !0,
71
71
  gradientTitle: !0,
72
72
  actions: /* @__PURE__ */ k("div", {
@@ -75,18 +75,26 @@ function P() {
75
75
  type: "button",
76
76
  onClick: () => E("/teams/new"),
77
77
  className: M,
78
- children: [/* @__PURE__ */ O(S, { className: "h-4 w-4" }), " New team"]
78
+ children: [
79
+ /* @__PURE__ */ O(S, { className: "h-4 w-4" }),
80
+ " ",
81
+ y("atheletebridge.pages.dashboard.newTeam", "New team")
82
+ ]
79
83
  }), /* @__PURE__ */ k("button", {
80
84
  type: "button",
81
85
  onClick: () => E("/performance"),
82
86
  className: M,
83
- children: [/* @__PURE__ */ O(w, { className: "h-4 w-4" }), " Track performance"]
87
+ children: [
88
+ /* @__PURE__ */ O(w, { className: "h-4 w-4" }),
89
+ " ",
90
+ y("atheletebridge.pages.dashboard.trackPerformance", "Track performance")
91
+ ]
84
92
  })]
85
93
  }),
86
94
  children: [
87
95
  /* @__PURE__ */ O(j, {
88
96
  className: "mb-6",
89
- 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."
97
+ children: y("atheletebridge.pages.dashboard.purpose", "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.")
90
98
  }),
91
99
  /* @__PURE__ */ k("div", {
92
100
  "data-tour": "dashboard-kpis",
@@ -95,26 +103,26 @@ function P() {
95
103
  /* @__PURE__ */ O(i, {
96
104
  icon: /* @__PURE__ */ O(D, { className: "h-5 w-5" }),
97
105
  value: B.length,
98
- label: "Teams",
106
+ label: y("atheletebridge.pages.dashboard.kpi.teams", "Teams"),
99
107
  tone: "accent",
100
108
  featured: !0
101
109
  }),
102
110
  /* @__PURE__ */ O(i, {
103
111
  icon: /* @__PURE__ */ O(v, { className: "h-5 w-5" }),
104
112
  value: V.length,
105
- label: "Fixtures",
113
+ label: y("atheletebridge.pages.dashboard.kpi.fixtures", "Fixtures"),
106
114
  tone: "accent"
107
115
  }),
108
116
  /* @__PURE__ */ O(i, {
109
117
  icon: /* @__PURE__ */ O(x, { className: "h-5 w-5" }),
110
118
  value: `${q}/${H.length}`,
111
- label: "Programs published",
119
+ label: y("atheletebridge.pages.dashboard.kpi.programsPublished", "Programs published"),
112
120
  tone: "accent"
113
121
  }),
114
122
  /* @__PURE__ */ O(i, {
115
123
  icon: /* @__PURE__ */ O(b, { className: "h-5 w-5" }),
116
124
  value: U.length,
117
- label: "Sessions",
125
+ label: y("atheletebridge.pages.dashboard.kpi.sessions", "Sessions"),
118
126
  tone: "accent"
119
127
  })
120
128
  ]
@@ -148,7 +156,11 @@ function P() {
148
156
  type: "button",
149
157
  onClick: () => E("/fixtures/new"),
150
158
  className: M,
151
- children: [/* @__PURE__ */ O(S, { className: "h-4 w-4" }), " Schedule fixture"]
159
+ children: [
160
+ /* @__PURE__ */ O(S, { className: "h-4 w-4" }),
161
+ " ",
162
+ y("atheletebridge.pages.dashboard.scheduleFixture", "Schedule fixture")
163
+ ]
152
164
  })
153
165
  }) : /* @__PURE__ */ O(A, {
154
166
  treatment: "glass",
@@ -164,7 +176,7 @@ function P() {
164
176
  className: "mb-4 flex items-center min-h-[1.75rem]",
165
177
  children: /* @__PURE__ */ O("h2", {
166
178
  className: "text-lg font-semibold text-text-primary",
167
- children: "Recent activity"
179
+ children: y("atheletebridge.pages.dashboard.recentActivity", "Recent activity")
168
180
  })
169
181
  }), Y.length === 0 ? /* @__PURE__ */ O(A, {
170
182
  treatment: "metric",
@@ -172,8 +184,8 @@ function P() {
172
184
  children: /* @__PURE__ */ O(c, {
173
185
  illustration: "empty-data",
174
186
  icon: /* @__PURE__ */ O(w, { className: "h-10 w-10" }),
175
- title: "No recent activity",
176
- description: "Create teams, publish programs or record results — the latest moves land here."
187
+ title: y("atheletebridge.pages.dashboard.noRecentActivity", "No recent activity"),
188
+ description: y("atheletebridge.pages.dashboard.noRecentActivityHint", "Create teams, publish programs or record results — the latest moves land here.")
177
189
  })
178
190
  }) : /* @__PURE__ */ O(A, {
179
191
  treatment: "metric",
@@ -189,23 +201,31 @@ function P() {
189
201
  className: "mb-4 flex items-center justify-between",
190
202
  children: [/* @__PURE__ */ O("h2", {
191
203
  className: "text-lg font-semibold text-text-primary",
192
- children: "Upcoming sessions"
204
+ children: y("atheletebridge.pages.dashboard.upcomingSessions", "Upcoming sessions")
193
205
  }), /* @__PURE__ */ k("button", {
194
206
  type: "button",
195
207
  onClick: () => E("/sessions"),
196
208
  className: "flex items-center gap-1 text-sm font-medium text-text-link hover:underline",
197
- children: ["View all ", /* @__PURE__ */ O(_, { className: "h-3.5 w-3.5" })]
209
+ children: [
210
+ y("atheletebridge.pages.dashboard.viewAll", "View all"),
211
+ " ",
212
+ /* @__PURE__ */ O(_, { className: "h-3.5 w-3.5" })
213
+ ]
198
214
  })]
199
215
  }), G.length === 0 ? /* @__PURE__ */ O(c, {
200
216
  illustration: "empty-calendar",
201
217
  icon: /* @__PURE__ */ O(b, { className: "h-10 w-10" }),
202
- title: "No upcoming sessions",
203
- description: "Book a coaching session with an athlete to get started.",
218
+ title: y("atheletebridge.pages.dashboard.noSessions", "No upcoming sessions"),
219
+ description: y("atheletebridge.pages.dashboard.noSessionsHint", "Book a coaching session with an athlete to get started."),
204
220
  action: /* @__PURE__ */ k("button", {
205
221
  type: "button",
206
222
  onClick: () => E("/sessions/new"),
207
223
  className: M,
208
- children: [/* @__PURE__ */ O(S, { className: "h-4 w-4" }), " Book session"]
224
+ children: [
225
+ /* @__PURE__ */ O(S, { className: "h-4 w-4" }),
226
+ " ",
227
+ y("atheletebridge.pages.dashboard.bookSession", "Book session")
228
+ ]
209
229
  })
210
230
  }) : /* @__PURE__ */ O("div", {
211
231
  className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",
@@ -221,30 +241,42 @@ function P() {
221
241
  className: "mb-4 flex items-center justify-between",
222
242
  children: [/* @__PURE__ */ O("h2", {
223
243
  className: "text-lg font-semibold text-text-primary",
224
- children: "Your teams"
244
+ children: y("atheletebridge.pages.dashboard.yourTeams", "Your teams")
225
245
  }), /* @__PURE__ */ k("button", {
226
246
  type: "button",
227
247
  onClick: () => E("/teams"),
228
248
  className: "flex items-center gap-1 text-sm font-medium text-text-link hover:underline",
229
- children: ["View all ", /* @__PURE__ */ O(_, { className: "h-3.5 w-3.5" })]
249
+ children: [
250
+ y("atheletebridge.pages.dashboard.viewAll", "View all"),
251
+ " ",
252
+ /* @__PURE__ */ O(_, { className: "h-3.5 w-3.5" })
253
+ ]
230
254
  })]
231
255
  }), K.length === 0 ? /* @__PURE__ */ O(c, {
232
256
  illustration: "empty-data",
233
257
  icon: /* @__PURE__ */ O(T, { className: "h-10 w-10" }),
234
- title: "No teams yet",
235
- description: "Set up AthleteBridge in about a minute — pick your sport, create your first team and add yourself to the roster.",
258
+ title: y("atheletebridge.pages.dashboard.noTeams", "No teams yet"),
259
+ description: y("atheletebridge.pages.dashboard.noTeamsHint", "Set up AthleteBridge in about a minute — pick your sport, create your first team and add yourself to the roster."),
236
260
  action: /* @__PURE__ */ k("div", {
237
261
  className: "flex flex-wrap items-center justify-center gap-2",
238
262
  children: [/* @__PURE__ */ k("button", {
239
263
  type: "button",
240
264
  onClick: () => E("/onboarding"),
241
265
  className: M,
242
- children: [/* @__PURE__ */ O(C, { className: "h-4 w-4" }), " Get started"]
266
+ children: [
267
+ /* @__PURE__ */ O(C, { className: "h-4 w-4" }),
268
+ " ",
269
+ y("atheletebridge.pages.dashboard.getStarted", "Get started")
270
+ ]
243
271
  }), /* @__PURE__ */ k("button", {
244
272
  type: "button",
245
273
  onClick: () => E("/teams/new"),
246
274
  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",
247
- children: [/* @__PURE__ */ O(S, { className: "h-4 w-4" }), " Create team"]
275
+ children: [
276
+ /* @__PURE__ */ O(S, { className: "h-4 w-4" }),
277
+ " ",
278
+ y("atheletebridge.pages.dashboard.createTeam", "Create team")
279
+ ]
248
280
  })]
249
281
  })
250
282
  }) : /* @__PURE__ */ O("div", {
@@ -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 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 Rocket,\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';\nimport { useAbTranslate } from '../i18n';\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 tr = useAbTranslate();\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\n label={tr('atheletebridge.pages.dashboard.loading', 'Loading your sporting operation…')}\n />\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 type=\"button\" onClick={() => navigateTo('/performance')} className={GRADIENT_CTA}>\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 data-tour=\"dashboard-kpis\" 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 data-tour=\"dashboard-fixtures\" 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\">\n {tr('atheletebridge.pages.dashboard.upcomingFixtures', 'Upcoming fixtures')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\n className=\"flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('atheletebridge.pages.dashboard.viewAllFixtures', 'View all fixtures')}{' '}\n <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={tr('atheletebridge.pages.dashboard.noFixtures', 'No upcoming fixtures')}\n description={tr(\n 'atheletebridge.pages.dashboard.noFixturesHint',\n 'Schedule a match to start tracking results and standings.'\n )}\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 data-tour=\"dashboard-activity\">\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 data-tour=\"dashboard-sessions\" 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-text-link 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 data-tour=\"dashboard-teams\">\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-text-link 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=\"Set up AthleteBridge in about a minute — pick your sport, create your first team and add yourself to the roster.\"\n action={\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/onboarding')}\n className={GRADIENT_CTA}\n >\n <Rocket className=\"h-4 w-4\" /> Get started\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams/new')}\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 <Plus className=\"h-4 w-4\" /> Create team\n </button>\n </div>\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 Rocket,\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';\nimport { useAbTranslate } from '../i18n';\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 tr = useAbTranslate();\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\n label={tr('atheletebridge.pages.dashboard.loading', 'Loading your sporting operation…')}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={`${tr('atheletebridge.pages.dashboard.welcomeBack', 'Welcome back')}, ${firstName}`}\n description={tr(\n 'atheletebridge.pages.dashboard.description',\n 'Your teams, fixtures, training and coaching — one connected record.'\n )}\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\" /> {tr('atheletebridge.pages.dashboard.newTeam', 'New team')}\n </button>\n <button type=\"button\" onClick={() => navigateTo('/performance')} className={GRADIENT_CTA}>\n <TrendingUp className=\"h-4 w-4\" /> {tr('atheletebridge.pages.dashboard.trackPerformance', 'Track performance')}\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n {tr(\n 'atheletebridge.pages.dashboard.purpose',\n '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.'\n )}\n </PagePurpose>\n\n {/* KPIs — glass tiles with teal stat values (2-col mobile → 4-col lg) */}\n <div data-tour=\"dashboard-kpis\" 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={tr('atheletebridge.pages.dashboard.kpi.teams', 'Teams')}\n tone=\"accent\"\n featured\n />\n <MetricsCard\n icon={<CalendarDays className=\"h-5 w-5\" />}\n value={fixtureItems.length}\n label={tr('atheletebridge.pages.dashboard.kpi.fixtures', 'Fixtures')}\n tone=\"accent\"\n />\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={`${publishedPrograms}/${programItems.length}`}\n label={tr('atheletebridge.pages.dashboard.kpi.programsPublished', 'Programs published')}\n tone=\"accent\"\n />\n <MetricsCard\n icon={<ClipboardCheck className=\"h-5 w-5\" />}\n value={sessionItems.length}\n label={tr('atheletebridge.pages.dashboard.kpi.sessions', '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 data-tour=\"dashboard-fixtures\" 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\">\n {tr('atheletebridge.pages.dashboard.upcomingFixtures', 'Upcoming fixtures')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures')}\n className=\"flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('atheletebridge.pages.dashboard.viewAllFixtures', 'View all fixtures')}{' '}\n <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={tr('atheletebridge.pages.dashboard.noFixtures', 'No upcoming fixtures')}\n description={tr(\n 'atheletebridge.pages.dashboard.noFixturesHint',\n 'Schedule a match to start tracking results and standings.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/fixtures/new')}\n className={GRADIENT_CTA}\n >\n <Plus className=\"h-4 w-4\" /> {tr('atheletebridge.pages.dashboard.scheduleFixture', '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 data-tour=\"dashboard-activity\">\n <div className=\"mb-4 flex items-center min-h-[1.75rem]\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('atheletebridge.pages.dashboard.recentActivity', 'Recent activity')}\n </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={tr('atheletebridge.pages.dashboard.noRecentActivity', 'No recent activity')}\n description={tr(\n 'atheletebridge.pages.dashboard.noRecentActivityHint',\n 'Create teams, publish programs or record results — the latest moves land here.'\n )}\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 data-tour=\"dashboard-sessions\" 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.dashboard.upcomingSessions', 'Upcoming sessions')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions')}\n className=\"flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('atheletebridge.pages.dashboard.viewAll', '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={tr('atheletebridge.pages.dashboard.noSessions', 'No upcoming sessions')}\n description={tr(\n 'atheletebridge.pages.dashboard.noSessionsHint',\n 'Book a coaching session with an athlete to get started.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/sessions/new')}\n className={GRADIENT_CTA}\n >\n <Plus className=\"h-4 w-4\" /> {tr('atheletebridge.pages.dashboard.bookSession', '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 data-tour=\"dashboard-teams\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('atheletebridge.pages.dashboard.yourTeams', 'Your teams')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams')}\n className=\"flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('atheletebridge.pages.dashboard.viewAll', '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={tr('atheletebridge.pages.dashboard.noTeams', 'No teams yet')}\n description={tr(\n 'atheletebridge.pages.dashboard.noTeamsHint',\n 'Set up AthleteBridge in about a minute — pick your sport, create your first team and add yourself to the roster.'\n )}\n action={\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/onboarding')}\n className={GRADIENT_CTA}\n >\n <Rocket className=\"h-4 w-4\" /> {tr('atheletebridge.pages.dashboard.getStarted', 'Get started')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/teams/new')}\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 <Plus className=\"h-4 w-4\" /> {tr('atheletebridge.pages.dashboard.createTeam', 'Create team')}\n </button>\n </div>\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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"}
@@ -5,13 +5,17 @@
5
5
  * An honest coaching-analytics dashboard built ENTIRELY from real aggregates via
6
6
  * `useCoachAnalytics()` — total sessions, average session rating, completion
7
7
  * rate, session-status mix, average rating by coach, weekly training-load /
8
- * session trends, and a league-standings snapshot.
8
+ * session trends, a per-athlete acute:chronic training-load table, and a
9
+ * league-standings snapshot.
9
10
  *
10
11
  * Deliberately OMITS the mock's fabricated composite indices (Form Index,
11
12
  * Fitness Score, Fatigue Index, Injury Status, Availability %) — there is no
12
13
  * wellness / injury / availability data in the schema, so those are not faked.
13
14
  * The one derived metric shown (ACWR training load) uses standard sports-science
14
- * maths over real `workout.totalLoad` and carries an explanatory tooltip.
15
+ * maths over real `workout.totalLoad` and carries an explanatory tooltip — both
16
+ * workspace-wide and, in the per-athlete table, grouped by each log's real
17
+ * `athleteId`. Where an athlete's workouts carry no recorded load the table
18
+ * says so rather than showing a fabricated zero.
15
19
  * Semantic tokens only; responsive.
16
20
  */
17
21
  export declare function CoachAnalyticsPage(): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"CoachAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/analytics/CoachAnalyticsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA6DH,wBAAgB,kBAAkB,gCAmUjC"}
1
+ {"version":3,"file":"CoachAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/analytics/CoachAnalyticsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2FH,wBAAgB,kBAAkB,gCAuVjC"}