@burdenoff/microfe-atheletebridge 2026.827.1 → 2026.829.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.
- package/dist/atheletebridge/components/LoadingState.d.ts.map +1 -1
- package/dist/atheletebridge/components/LoadingState.js +39 -32
- package/dist/atheletebridge/components/LoadingState.js.map +1 -1
- package/dist/atheletebridge/components/TeamCard.d.ts.map +1 -1
- package/dist/atheletebridge/components/TeamCard.js +45 -49
- package/dist/atheletebridge/components/TeamCard.js.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.js +56 -24
- package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js +58 -56
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +110 -106
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +80 -71
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -1
- package/dist/atheletebridge/pages/onboarding/OnboardingPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/onboarding/OnboardingPage.js +244 -210
- package/dist/atheletebridge/pages/onboarding/OnboardingPage.js.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.js +197 -148
- package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +169 -143
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js +77 -72
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +104 -86
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js +54 -49
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js +286 -267
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamFormPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamFormPage.js +185 -180
- package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamsListPage.js +70 -74
- package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingState.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/components/LoadingState.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"LoadingState.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/components/LoadingState.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAKhC,UAAU,iBAAiB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAY9C,CAAC;AAEF,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CA2B1C,CAAC"}
|
|
@@ -1,36 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useAbTranslate as e } from "../i18n.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { AlertCircle as n, Loader2 as r } from "lucide-react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
5
|
//#region src/atheletebridge/components/LoadingState.tsx
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
children:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
6
|
+
var o = ({ label: n, className: o }) => {
|
|
7
|
+
let s = e();
|
|
8
|
+
return /* @__PURE__ */ a("div", {
|
|
9
|
+
className: t("flex min-h-[40vh] flex-col items-center justify-center gap-3", o),
|
|
10
|
+
role: "status",
|
|
11
|
+
"aria-live": "polite",
|
|
12
|
+
children: [/* @__PURE__ */ i(r, { className: "h-8 w-8 animate-spin text-text-secondary" }), /* @__PURE__ */ i("p", {
|
|
13
|
+
className: "text-sm text-text-secondary",
|
|
14
|
+
children: n ?? s("common.loading", "Loading…")
|
|
15
|
+
})]
|
|
16
|
+
});
|
|
17
|
+
}, s = ({ title: r, message: o, onRetry: s, className: c }) => {
|
|
18
|
+
let l = e();
|
|
19
|
+
return /* @__PURE__ */ a("div", {
|
|
20
|
+
className: t("flex min-h-[40vh] flex-col items-center justify-center gap-3 text-center", c),
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ i(n, { className: "h-10 w-10 text-status-error-text" }),
|
|
23
|
+
/* @__PURE__ */ i("h3", {
|
|
24
|
+
className: "text-lg font-semibold text-text-primary",
|
|
25
|
+
children: r ?? l("common.errorTitle", "Something went wrong")
|
|
26
|
+
}),
|
|
27
|
+
/* @__PURE__ */ i("p", {
|
|
28
|
+
className: "max-w-sm text-sm text-text-secondary",
|
|
29
|
+
children: o ?? l("common.errorMessage", "We could not load this data. Please try again.")
|
|
30
|
+
}),
|
|
31
|
+
s && /* @__PURE__ */ i("button", {
|
|
32
|
+
type: "button",
|
|
33
|
+
onClick: s,
|
|
34
|
+
className: "mt-2 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
35
|
+
children: l("common.tryAgain", "Try again")
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
};
|
|
33
40
|
//#endregion
|
|
34
|
-
export {
|
|
41
|
+
export { s as ErrorState, o as LoadingState };
|
|
35
42
|
|
|
36
43
|
//# sourceMappingURL=LoadingState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingState.js","names":[],"sources":["../../../src/atheletebridge/components/LoadingState.tsx"],"sourcesContent":["/**\n * Loading / Error State Components\n * @module atheletebridge/components\n *\n * Small calm, centered states shared across list and detail pages.\n */\n\nimport type { FC } from 'react';\nimport { AlertCircle, Loader2 } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\ninterface LoadingStateProps {\n label?: string;\n className?: string;\n}\n\nexport const LoadingState: FC<LoadingStateProps> = ({ label
|
|
1
|
+
{"version":3,"file":"LoadingState.js","names":[],"sources":["../../../src/atheletebridge/components/LoadingState.tsx"],"sourcesContent":["/**\n * Loading / Error State Components\n * @module atheletebridge/components\n *\n * Small calm, centered states shared across list and detail pages.\n */\n\nimport type { FC } from 'react';\nimport { AlertCircle, Loader2 } from 'lucide-react';\nimport { cn } from '../utils/cn';\nimport { useAbTranslate } from '../i18n';\n\ninterface LoadingStateProps {\n label?: string;\n className?: string;\n}\n\nexport const LoadingState: FC<LoadingStateProps> = ({ label, className }) => {\n const tr = useAbTranslate();\n return (\n <div\n className={cn('flex min-h-[40vh] flex-col items-center justify-center gap-3', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">{label ?? tr('common.loading', 'Loading…')}</p>\n </div>\n );\n};\n\ninterface ErrorStateProps {\n title?: string;\n message?: string;\n onRetry?: () => void;\n className?: string;\n}\n\nexport const ErrorState: FC<ErrorStateProps> = ({ title, message, onRetry, className }) => {\n const tr = useAbTranslate();\n return (\n <div\n className={cn(\n 'flex min-h-[40vh] flex-col items-center justify-center gap-3 text-center',\n className\n )}\n >\n <AlertCircle className=\"h-10 w-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {title ?? tr('common.errorTitle', 'Something went wrong')}\n </h3>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n {message ?? tr('common.errorMessage', 'We could not load this data. Please try again.')}\n </p>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-2 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.tryAgain', 'Try again')}\n </button>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;AAiBA,IAAa,KAAuC,EAAE,UAAO,mBAAgB;CAC3E,IAAM,IAAK,EAAe;CAC1B,OACE,kBAAC,OAAD;EACE,WAAW,EAAG,gEAAgE,CAAS;EACvF,MAAK;EACL,aAAU;YAHZ,CAKE,kBAAC,GAAD,EAAS,WAAU,2CAA4C,CAAA,GAC/D,kBAAC,KAAD;GAAG,WAAU;aAA+B,KAAS,EAAG,kBAAkB,UAAU;EAAK,CAAA,CACtF;;AAET,GASa,KAAmC,EAAE,UAAO,YAAS,YAAS,mBAAgB;CACzF,IAAM,IAAK,EAAe;CAC1B,OACE,kBAAC,OAAD;EACE,WAAW,EACT,4EACA,CACF;YAJF;GAME,kBAAC,GAAD,EAAa,WAAU,mCAAoC,CAAA;GAC3D,kBAAC,MAAD;IAAI,WAAU;cACX,KAAS,EAAG,qBAAqB,sBAAsB;GACtD,CAAA;GACJ,kBAAC,KAAD;IAAG,WAAU;cACV,KAAW,EAAG,uBAAuB,gDAAgD;GACrF,CAAA;GACF,KACC,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAET,EAAG,mBAAmB,WAAW;GAC5B,CAAA;EAEP;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamCard.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/components/TeamCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"TeamCard.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/components/TeamCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAQhC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,UAAU,aAAa;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,aAAa,CAuFtC,CAAC"}
|
|
@@ -1,111 +1,107 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { useAbTranslate as e } from "../i18n.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { StatusBadge as n } from "./StatusBadge.js";
|
|
4
|
+
import { SPORT_ICONS as r, SPORT_LABELS as i } from "./domain.js";
|
|
5
|
+
import { Crest as a } from "./Crest.js";
|
|
6
|
+
import { CalendarDays as o, MapPin as s, Users as c } from "lucide-react";
|
|
7
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
8
|
+
import { Badge as d, GlassCard as f } from "@burdenoff/fe-libs/ui";
|
|
8
9
|
//#region src/atheletebridge/components/TeamCard.tsx
|
|
9
|
-
var
|
|
10
|
-
let
|
|
11
|
-
return /* @__PURE__ */
|
|
10
|
+
var p = ({ team: p, onClick: m, memberCount: h }) => {
|
|
11
|
+
let g = e(), _ = r[p.sport], v = h ?? p.members?.items.length, y = v === 1 ? g("atheletebridge.team.memberCount.one", "{{count}} member").replace("{{count}}", String(v)) : g("atheletebridge.team.memberCount.other", "{{count}} members").replace("{{count}}", String(v ?? 0));
|
|
12
|
+
return /* @__PURE__ */ u(f, {
|
|
12
13
|
treatment: "metric",
|
|
13
14
|
role: "button",
|
|
14
15
|
tabIndex: 0,
|
|
15
|
-
onClick:
|
|
16
|
+
onClick: m,
|
|
16
17
|
onKeyDown: (e) => {
|
|
17
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
18
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), m?.());
|
|
18
19
|
},
|
|
19
|
-
className:
|
|
20
|
+
className: t("cursor-pointer p-5 transition-transform duration-200 hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]"),
|
|
20
21
|
children: [
|
|
21
|
-
/* @__PURE__ */
|
|
22
|
+
/* @__PURE__ */ u("div", {
|
|
22
23
|
className: "flex items-start justify-between gap-3",
|
|
23
|
-
children: [/* @__PURE__ */
|
|
24
|
+
children: [/* @__PURE__ */ u("div", {
|
|
24
25
|
className: "flex min-w-0 items-center gap-3",
|
|
25
|
-
children: [/* @__PURE__ */
|
|
26
|
-
name:
|
|
27
|
-
sport:
|
|
26
|
+
children: [/* @__PURE__ */ l(a, {
|
|
27
|
+
name: p.name,
|
|
28
|
+
sport: p.sport,
|
|
28
29
|
size: 44
|
|
29
|
-
}), /* @__PURE__ */
|
|
30
|
+
}), /* @__PURE__ */ u("div", {
|
|
30
31
|
className: "min-w-0",
|
|
31
|
-
children: [/* @__PURE__ */
|
|
32
|
+
children: [/* @__PURE__ */ l("h3", {
|
|
32
33
|
className: "truncate text-base font-semibold text-text-primary",
|
|
33
|
-
children:
|
|
34
|
-
}), /* @__PURE__ */
|
|
34
|
+
children: p.name
|
|
35
|
+
}), /* @__PURE__ */ l("div", {
|
|
35
36
|
className: "mt-1",
|
|
36
|
-
children: /* @__PURE__ */
|
|
37
|
+
children: /* @__PURE__ */ u(d, {
|
|
37
38
|
variant: "secondary",
|
|
38
39
|
className: "gap-1",
|
|
39
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ l(_, {
|
|
40
41
|
className: "h-3 w-3",
|
|
41
42
|
"aria-hidden": "true"
|
|
42
|
-
}),
|
|
43
|
+
}), i[p.sport]]
|
|
43
44
|
})
|
|
44
45
|
})]
|
|
45
46
|
})]
|
|
46
|
-
}), /* @__PURE__ */
|
|
47
|
+
}), /* @__PURE__ */ l(n, { status: p.status })]
|
|
47
48
|
}),
|
|
48
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ u("div", {
|
|
49
50
|
className: "mt-4 flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-secondary",
|
|
50
51
|
children: [
|
|
51
|
-
|
|
52
|
+
p.ageGroup && /* @__PURE__ */ u("span", {
|
|
52
53
|
className: "flex items-center gap-1",
|
|
53
54
|
children: [
|
|
54
|
-
/* @__PURE__ */ c
|
|
55
|
+
/* @__PURE__ */ l(c, {
|
|
55
56
|
className: "h-3.5 w-3.5",
|
|
56
57
|
"aria-hidden": "true"
|
|
57
58
|
}),
|
|
58
59
|
" ",
|
|
59
|
-
|
|
60
|
+
p.ageGroup
|
|
60
61
|
]
|
|
61
62
|
}),
|
|
62
|
-
|
|
63
|
+
p.season && /* @__PURE__ */ u("span", {
|
|
63
64
|
className: "flex items-center gap-1",
|
|
64
65
|
children: [
|
|
65
|
-
/* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ l(o, {
|
|
66
67
|
className: "h-3.5 w-3.5",
|
|
67
68
|
"aria-hidden": "true"
|
|
68
69
|
}),
|
|
69
70
|
" ",
|
|
70
|
-
|
|
71
|
+
p.season
|
|
71
72
|
]
|
|
72
73
|
}),
|
|
73
|
-
|
|
74
|
+
p.homeVenue && /* @__PURE__ */ u("span", {
|
|
74
75
|
className: "flex items-center gap-1 truncate",
|
|
75
76
|
children: [
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ l(s, {
|
|
77
78
|
className: "h-3.5 w-3.5 shrink-0",
|
|
78
79
|
"aria-hidden": "true"
|
|
79
80
|
}),
|
|
80
81
|
" ",
|
|
81
|
-
|
|
82
|
+
p.homeVenue
|
|
82
83
|
]
|
|
83
84
|
})
|
|
84
85
|
]
|
|
85
86
|
}),
|
|
86
|
-
|
|
87
|
+
p.tags.length > 0 && /* @__PURE__ */ l("div", {
|
|
87
88
|
className: "mt-3 flex flex-wrap gap-1.5",
|
|
88
|
-
children:
|
|
89
|
+
children: p.tags.slice(0, 4).map((e) => /* @__PURE__ */ l("span", {
|
|
89
90
|
className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
|
|
90
91
|
children: e
|
|
91
92
|
}, e))
|
|
92
93
|
}),
|
|
93
|
-
|
|
94
|
+
v !== void 0 && /* @__PURE__ */ u("div", {
|
|
94
95
|
className: "mt-4 flex items-center gap-1.5 border-t border-border-seam pt-3 text-xs font-medium text-text-secondary",
|
|
95
|
-
children: [
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}),
|
|
100
|
-
g,
|
|
101
|
-
" ",
|
|
102
|
-
g === 1 ? "member" : "members"
|
|
103
|
-
]
|
|
96
|
+
children: [/* @__PURE__ */ l(c, {
|
|
97
|
+
className: "h-3.5 w-3.5",
|
|
98
|
+
"aria-hidden": "true"
|
|
99
|
+
}), y]
|
|
104
100
|
})
|
|
105
101
|
]
|
|
106
102
|
});
|
|
107
103
|
};
|
|
108
104
|
//#endregion
|
|
109
|
-
export {
|
|
105
|
+
export { p as TeamCard };
|
|
110
106
|
|
|
111
107
|
//# sourceMappingURL=TeamCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamCard.js","names":[],"sources":["../../../src/atheletebridge/components/TeamCard.tsx"],"sourcesContent":["/**\n * Team Card\n * @module atheletebridge/components\n *\n * A team tile fronted by a deterministic gradient monogram crest, with a sport\n * Badge, a StatusBadge (green Active / amber Forming / grey Inactive·Archived),\n * a member-count chip and a hover lift. Semantic tokens only.\n */\n\nimport type { FC } from 'react';\nimport { Users, MapPin, CalendarDays } from 'lucide-react';\nimport { GlassCard, Badge } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { Crest } from './Crest';\nimport { SPORT_ICONS, SPORT_LABELS } from './domain';\nimport { cn } from '../utils/cn';\nimport type { AthTeam } from '../types';\n\ninterface TeamCardProps {\n team: AthTeam;\n onClick?: () => void;\n /**\n * Member count for the footer chip. Falls back to `team.members?.length`\n * when the list query selected members; omit the chip entirely if neither is\n * available (never guess).\n */\n memberCount?: number;\n}\n\nexport const TeamCard: FC<TeamCardProps> = ({ team, onClick, memberCount }) => {\n const SportIcon = SPORT_ICONS[team.sport];\n const count = memberCount ?? team.members?.items.length;\n\n return (\n <GlassCard\n treatment=\"metric\"\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }}\n className={cn(\n 'cursor-pointer p-5 transition-transform duration-200 hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <Crest name={team.name} sport={team.sport} size={44} />\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{team.name}</h3>\n <div className=\"mt-1\">\n <Badge variant=\"secondary\" className=\"gap-1\">\n <SportIcon className=\"h-3 w-3\" aria-hidden=\"true\" />\n {SPORT_LABELS[team.sport]}\n </Badge>\n </div>\n </div>\n </div>\n <StatusBadge status={team.status} />\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-secondary\">\n {team.ageGroup && (\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {team.ageGroup}\n </span>\n )}\n {team.season && (\n <span className=\"flex items-center gap-1\">\n <CalendarDays className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {team.season}\n </span>\n )}\n {team.homeVenue && (\n <span className=\"flex items-center gap-1 truncate\">\n <MapPin className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" /> {team.homeVenue}\n </span>\n )}\n </div>\n\n {team.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {team.tags.slice(0, 4).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\n {count !== undefined && (\n <div className=\"mt-4 flex items-center gap-1.5 border-t border-border-seam pt-3 text-xs font-medium text-text-secondary\">\n <Users className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {
|
|
1
|
+
{"version":3,"file":"TeamCard.js","names":[],"sources":["../../../src/atheletebridge/components/TeamCard.tsx"],"sourcesContent":["/**\n * Team Card\n * @module atheletebridge/components\n *\n * A team tile fronted by a deterministic gradient monogram crest, with a sport\n * Badge, a StatusBadge (green Active / amber Forming / grey Inactive·Archived),\n * a member-count chip and a hover lift. Semantic tokens only.\n */\n\nimport type { FC } from 'react';\nimport { Users, MapPin, CalendarDays } from 'lucide-react';\nimport { GlassCard, Badge } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { Crest } from './Crest';\nimport { SPORT_ICONS, SPORT_LABELS } from './domain';\nimport { cn } from '../utils/cn';\nimport { useAbTranslate } from '../i18n';\nimport type { AthTeam } from '../types';\n\ninterface TeamCardProps {\n team: AthTeam;\n onClick?: () => void;\n /**\n * Member count for the footer chip. Falls back to `team.members?.length`\n * when the list query selected members; omit the chip entirely if neither is\n * available (never guess).\n */\n memberCount?: number;\n}\n\nexport const TeamCard: FC<TeamCardProps> = ({ team, onClick, memberCount }) => {\n const tr = useAbTranslate();\n const SportIcon = SPORT_ICONS[team.sport];\n const count = memberCount ?? team.members?.items.length;\n\n const memberLabel =\n count === 1\n ? tr('atheletebridge.team.memberCount.one', '{{count}} member').replace(\n '{{count}}',\n String(count)\n )\n : tr('atheletebridge.team.memberCount.other', '{{count}} members').replace(\n '{{count}}',\n String(count ?? 0)\n );\n\n return (\n <GlassCard\n treatment=\"metric\"\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }}\n className={cn(\n 'cursor-pointer p-5 transition-transform duration-200 hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <Crest name={team.name} sport={team.sport} size={44} />\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{team.name}</h3>\n <div className=\"mt-1\">\n <Badge variant=\"secondary\" className=\"gap-1\">\n <SportIcon className=\"h-3 w-3\" aria-hidden=\"true\" />\n {SPORT_LABELS[team.sport]}\n </Badge>\n </div>\n </div>\n </div>\n <StatusBadge status={team.status} />\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-secondary\">\n {team.ageGroup && (\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {team.ageGroup}\n </span>\n )}\n {team.season && (\n <span className=\"flex items-center gap-1\">\n <CalendarDays className=\"h-3.5 w-3.5\" aria-hidden=\"true\" /> {team.season}\n </span>\n )}\n {team.homeVenue && (\n <span className=\"flex items-center gap-1 truncate\">\n <MapPin className=\"h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" /> {team.homeVenue}\n </span>\n )}\n </div>\n\n {team.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {team.tags.slice(0, 4).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\n {count !== undefined && (\n <div className=\"mt-4 flex items-center gap-1.5 border-t border-border-seam pt-3 text-xs font-medium text-text-secondary\">\n <Users className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {memberLabel}\n </div>\n )}\n </GlassCard>\n );\n};\n"],"mappings":";;;;;;;;;AA8BA,IAAa,KAA+B,EAAE,SAAM,YAAS,qBAAkB;CAC7E,IAAM,IAAK,EAAe,GACpB,IAAY,EAAY,EAAK,QAC7B,IAAQ,KAAe,EAAK,SAAS,MAAM,QAE3C,IACJ,MAAU,IACN,EAAG,uCAAuC,kBAAkB,EAAE,QAC5D,aACA,OAAO,CAAK,CACd,IACA,EAAG,yCAAyC,mBAAmB,EAAE,QAC/D,aACA,OAAO,KAAS,CAAC,CACnB;CAEN,OACE,kBAAC,GAAD;EACE,WAAU;EACV,MAAK;EACL,UAAU;EACD;EACT,YAAY,MAAM;GAChB,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,GACjB,IAAU;EAEd;EACA,WAAW,EACT,0KACF;YAbF;GAeE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAO,MAAM,EAAK;MAAM,OAAO,EAAK;MAAO,MAAM;KAAK,CAAA,GACtD,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAsD,EAAK;MAAS,CAAA,GAClF,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAO,SAAQ;QAAY,WAAU;kBAArC,CACE,kBAAC,GAAD;SAAW,WAAU;SAAU,eAAY;QAAQ,CAAA,GAClD,EAAa,EAAK,MACd;;MACJ,CAAA,CACF;OACF;QACL,kBAAC,GAAD,EAAa,QAAQ,EAAK,OAAS,CAAA,CAChC;;GAEL,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,EAAK,YACJ,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD;QAAO,WAAU;QAAc,eAAY;OAAQ,CAAA;OAAC;OAAE,EAAK;MACvD;;KAEP,EAAK,UACJ,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD;QAAc,WAAU;QAAc,eAAY;OAAQ,CAAA;OAAC;OAAE,EAAK;MAC9D;;KAEP,EAAK,aACJ,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD;QAAQ,WAAU;QAAuB,eAAY;OAAQ,CAAA;OAAC;OAAE,EAAK;MACjE;;IAEL;;GAEJ,EAAK,KAAK,SAAS,KAClB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAK,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,MAC1B,kBAAC,QAAD;KAEE,WAAU;eAET;IACG,GAJC,CAID,CACP;GACE,CAAA;GAGN,MAAU,KAAA,KACT,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAO,WAAU;KAAc,eAAY;IAAQ,CAAA,GAClD,CACE;;EAEE;;AAEf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/pages/DashboardPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH,wBAAgB,aAAa,
|
|
1
|
+
{"version":3,"file":"DashboardPage.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/pages/DashboardPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH,wBAAgB,aAAa,gCAyT5B"}
|
|
@@ -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:
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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\" />{' '}\n {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\" />{' '}\n {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')}{' '}\n <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\" />{' '}\n {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')}{' '}\n <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\" />{' '}\n {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\" />{' '}\n {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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoachAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/analytics/CoachAnalyticsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"CoachAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/analytics/CoachAnalyticsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA0GH,wBAAgB,kBAAkB,gCAkZjC"}
|