@burdenoff/microfe-atheletebridge 2026.629.1 → 2026.705.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atheletebridge/AthleteBridgeRoot.js +45 -37
- package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
- package/dist/atheletebridge/AthleteBridgeRoutes.js +10 -6
- package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
- package/dist/atheletebridge/components/AthleteSelect.js +66 -0
- package/dist/atheletebridge/components/AthleteSelect.js.map +1 -0
- package/dist/atheletebridge/components/DrillVideoLink.js +28 -0
- package/dist/atheletebridge/components/DrillVideoLink.js.map +1 -0
- package/dist/atheletebridge/components/MetricsCard.js +20 -46
- package/dist/atheletebridge/components/MetricsCard.js.map +1 -1
- package/dist/atheletebridge/components/UserSearchSelect.js +129 -0
- package/dist/atheletebridge/components/UserSearchSelect.js.map +1 -0
- package/dist/atheletebridge/components/domain.js +10 -1
- package/dist/atheletebridge/components/domain.js.map +1 -1
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +313 -180
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.js +77 -69
- package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.js +496 -0
- package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -0
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +175 -141
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js +91 -98
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js +132 -137
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
- package/dist/atheletebridge/utils/formatters.js +2 -25
- package/dist/atheletebridge/utils/formatters.js.map +1 -1
- package/dist/index.js +4 -4
- package/package.json +5 -5
|
@@ -4,94 +4,102 @@ import { useCallback as n, useEffect as r } from "react";
|
|
|
4
4
|
import { QueryClient as i, QueryClientProvider as a } from "@tanstack/react-query";
|
|
5
5
|
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
6
|
import { FeatureFlagProvider as s } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
7
|
-
import { CalendarDays as c, ClipboardCheck as l, Dumbbell as u,
|
|
8
|
-
import { jsx as
|
|
7
|
+
import { CalendarDays as c, ClipboardCheck as l, Dumbbell as u, Gauge as d, LayoutDashboard as f, Trophy as p, Users as m } from "lucide-react";
|
|
8
|
+
import { jsx as h } from "react/jsx-runtime";
|
|
9
9
|
//#region src/atheletebridge/AthleteBridgeRoot.tsx
|
|
10
|
-
var
|
|
10
|
+
var g = new i({ defaultOptions: { queries: {
|
|
11
11
|
staleTime: 300 * 1e3,
|
|
12
12
|
retry: 1
|
|
13
|
-
} } }),
|
|
14
|
-
let { registerNavItems:
|
|
15
|
-
let n =
|
|
13
|
+
} } }), _ = (i) => {
|
|
14
|
+
let { registerNavItems: _, registerFooterItems: v, basePath: y = "/" } = i, { t: b } = o(), x = n((e, t) => {
|
|
15
|
+
let n = b(e);
|
|
16
16
|
return n === e ? t : n;
|
|
17
|
-
}, [
|
|
17
|
+
}, [b]), S = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
18
18
|
return r(() => {
|
|
19
|
-
if (
|
|
19
|
+
if (_) return _([
|
|
20
20
|
{
|
|
21
21
|
id: "atheletebridge-dashboard",
|
|
22
|
-
label:
|
|
23
|
-
path:
|
|
24
|
-
icon: /* @__PURE__ */
|
|
22
|
+
label: x("atheletebridge.nav.dashboard", "Dashboard"),
|
|
23
|
+
path: S(y, ""),
|
|
24
|
+
icon: /* @__PURE__ */ h(f, { size: 20 }),
|
|
25
25
|
order: 1,
|
|
26
26
|
group: "main"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
id: "atheletebridge-teams",
|
|
30
|
-
label:
|
|
31
|
-
path:
|
|
32
|
-
icon: /* @__PURE__ */ m
|
|
30
|
+
label: x("atheletebridge.nav.teams", "Teams"),
|
|
31
|
+
path: S(y, "teams"),
|
|
32
|
+
icon: /* @__PURE__ */ h(m, { size: 20 }),
|
|
33
33
|
order: 10,
|
|
34
34
|
group: "roster"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
id: "atheletebridge-fixtures",
|
|
38
|
-
label:
|
|
39
|
-
path:
|
|
40
|
-
icon: /* @__PURE__ */
|
|
38
|
+
label: x("atheletebridge.nav.fixtures", "Fixtures"),
|
|
39
|
+
path: S(y, "fixtures"),
|
|
40
|
+
icon: /* @__PURE__ */ h(c, { size: 20 }),
|
|
41
41
|
order: 11,
|
|
42
42
|
group: "roster"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
id: "atheletebridge-standings",
|
|
46
|
-
label:
|
|
47
|
-
path:
|
|
48
|
-
icon: /* @__PURE__ */
|
|
46
|
+
label: x("atheletebridge.nav.standings", "Standings"),
|
|
47
|
+
path: S(y, "standings"),
|
|
48
|
+
icon: /* @__PURE__ */ h(p, { size: 20 }),
|
|
49
49
|
order: 12,
|
|
50
50
|
group: "roster"
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
id: "atheletebridge-programs",
|
|
54
|
-
label:
|
|
55
|
-
path:
|
|
56
|
-
icon: /* @__PURE__ */
|
|
54
|
+
label: x("atheletebridge.nav.programs", "Programs"),
|
|
55
|
+
path: S(y, "programs"),
|
|
56
|
+
icon: /* @__PURE__ */ h(u, { size: 20 }),
|
|
57
57
|
order: 20,
|
|
58
58
|
group: "training"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
id: "atheletebridge-sessions",
|
|
62
|
-
label:
|
|
63
|
-
path:
|
|
64
|
-
icon: /* @__PURE__ */
|
|
62
|
+
label: x("atheletebridge.nav.sessions", "Sessions"),
|
|
63
|
+
path: S(y, "sessions"),
|
|
64
|
+
icon: /* @__PURE__ */ h(l, { size: 20 }),
|
|
65
65
|
order: 21,
|
|
66
66
|
group: "training"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "atheletebridge-performance",
|
|
70
|
+
label: x("atheletebridge.nav.performance", "Performance"),
|
|
71
|
+
path: S(y, "performance"),
|
|
72
|
+
icon: /* @__PURE__ */ h(d, { size: 20 }),
|
|
73
|
+
order: 22,
|
|
74
|
+
group: "training"
|
|
67
75
|
}
|
|
68
76
|
]);
|
|
69
77
|
}, [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
_,
|
|
79
|
+
y,
|
|
80
|
+
x
|
|
73
81
|
]), r(() => {
|
|
74
|
-
if (
|
|
82
|
+
if (v) return v([{
|
|
75
83
|
id: "atheletebridge-status",
|
|
76
|
-
label:
|
|
84
|
+
label: x("atheletebridge.footer.ready", "AthleteBridge Ready"),
|
|
77
85
|
type: "status",
|
|
78
86
|
order: 10,
|
|
79
87
|
section: "left"
|
|
80
88
|
}]);
|
|
81
|
-
}, [
|
|
82
|
-
client:
|
|
83
|
-
children: /* @__PURE__ */
|
|
89
|
+
}, [v, x]), /* @__PURE__ */ h(a, {
|
|
90
|
+
client: g,
|
|
91
|
+
children: /* @__PURE__ */ h(s, {
|
|
84
92
|
workspaceId: null,
|
|
85
93
|
gateway: "global",
|
|
86
94
|
defaultEnabled: !0,
|
|
87
|
-
children: /* @__PURE__ */
|
|
95
|
+
children: /* @__PURE__ */ h(e, {
|
|
88
96
|
...i,
|
|
89
|
-
children: /* @__PURE__ */
|
|
97
|
+
children: /* @__PURE__ */ h(t, {})
|
|
90
98
|
})
|
|
91
99
|
})
|
|
92
100
|
});
|
|
93
101
|
};
|
|
94
102
|
//#endregion
|
|
95
|
-
export {
|
|
103
|
+
export { _ as AthleteBridgeRoot };
|
|
96
104
|
|
|
97
105
|
//# sourceMappingURL=AthleteBridgeRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AthleteBridgeRoot.js","names":[],"sources":["../../src/atheletebridge/AthleteBridgeRoot.tsx"],"sourcesContent":["/**\n * AthleteBridge Root Component\n * @module atheletebridge\n *\n * The single Root export consumed by the host app shell. The export name and\n * props (AthleteBridgeRootProps) are the host contract and must not change —\n * only the internal nav config, provider tree, and routes are product-specific.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useEffect } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n LayoutDashboard,\n Users,\n CalendarDays,\n Trophy,\n Dumbbell,\n ClipboardCheck,\n} from 'lucide-react';\nimport { AthleteBridgeProvider } from './context/AthleteBridgeContext';\nimport { AthleteBridgeRoutes } from './AthleteBridgeRoutes';\nimport type { AthleteBridgeRootProps, NavItem } from './types';\n\n// Create a query client for this microfrontend\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\nexport const AthleteBridgeRoot: FC<AthleteBridgeRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n // Helper to join paths correctly\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with shell on mount\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'atheletebridge-dashboard',\n label: t('atheletebridge.nav.dashboard', 'Dashboard'),\n path: joinPath(basePath, ''),\n icon: <LayoutDashboard size={20} />,\n order: 1,\n group: 'main',\n },\n {\n id: 'atheletebridge-teams',\n label: t('atheletebridge.nav.teams', 'Teams'),\n path: joinPath(basePath, 'teams'),\n icon: <Users size={20} />,\n order: 10,\n group: 'roster',\n },\n {\n id: 'atheletebridge-fixtures',\n label: t('atheletebridge.nav.fixtures', 'Fixtures'),\n path: joinPath(basePath, 'fixtures'),\n icon: <CalendarDays size={20} />,\n order: 11,\n group: 'roster',\n },\n {\n id: 'atheletebridge-standings',\n label: t('atheletebridge.nav.standings', 'Standings'),\n path: joinPath(basePath, 'standings'),\n icon: <Trophy size={20} />,\n order: 12,\n group: 'roster',\n },\n {\n id: 'atheletebridge-programs',\n label: t('atheletebridge.nav.programs', 'Programs'),\n path: joinPath(basePath, 'programs'),\n icon: <Dumbbell size={20} />,\n order: 20,\n group: 'training',\n },\n {\n id: 'atheletebridge-sessions',\n label: t('atheletebridge.nav.sessions', 'Sessions'),\n path: joinPath(basePath, 'sessions'),\n icon: <ClipboardCheck size={20} />,\n order: 21,\n group: 'training',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, t]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'atheletebridge-status',\n label: t('atheletebridge.footer.ready', 'AthleteBridge Ready'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, t]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <AthleteBridgeProvider {...props}>\n <AthleteBridgeRoutes />\n </AthleteBridgeProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"AthleteBridgeRoot.js","names":[],"sources":["../../src/atheletebridge/AthleteBridgeRoot.tsx"],"sourcesContent":["/**\n * AthleteBridge Root Component\n * @module atheletebridge\n *\n * The single Root export consumed by the host app shell. The export name and\n * props (AthleteBridgeRootProps) are the host contract and must not change —\n * only the internal nav config, provider tree, and routes are product-specific.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useEffect } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n LayoutDashboard,\n Users,\n CalendarDays,\n Trophy,\n Dumbbell,\n ClipboardCheck,\n Gauge,\n} from 'lucide-react';\nimport { AthleteBridgeProvider } from './context/AthleteBridgeContext';\nimport { AthleteBridgeRoutes } from './AthleteBridgeRoutes';\nimport type { AthleteBridgeRootProps, NavItem } from './types';\n\n// Create a query client for this microfrontend\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\nexport const AthleteBridgeRoot: FC<AthleteBridgeRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n // Helper to join paths correctly\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with shell on mount\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'atheletebridge-dashboard',\n label: t('atheletebridge.nav.dashboard', 'Dashboard'),\n path: joinPath(basePath, ''),\n icon: <LayoutDashboard size={20} />,\n order: 1,\n group: 'main',\n },\n {\n id: 'atheletebridge-teams',\n label: t('atheletebridge.nav.teams', 'Teams'),\n path: joinPath(basePath, 'teams'),\n icon: <Users size={20} />,\n order: 10,\n group: 'roster',\n },\n {\n id: 'atheletebridge-fixtures',\n label: t('atheletebridge.nav.fixtures', 'Fixtures'),\n path: joinPath(basePath, 'fixtures'),\n icon: <CalendarDays size={20} />,\n order: 11,\n group: 'roster',\n },\n {\n id: 'atheletebridge-standings',\n label: t('atheletebridge.nav.standings', 'Standings'),\n path: joinPath(basePath, 'standings'),\n icon: <Trophy size={20} />,\n order: 12,\n group: 'roster',\n },\n {\n id: 'atheletebridge-programs',\n label: t('atheletebridge.nav.programs', 'Programs'),\n path: joinPath(basePath, 'programs'),\n icon: <Dumbbell size={20} />,\n order: 20,\n group: 'training',\n },\n {\n id: 'atheletebridge-sessions',\n label: t('atheletebridge.nav.sessions', 'Sessions'),\n path: joinPath(basePath, 'sessions'),\n icon: <ClipboardCheck size={20} />,\n order: 21,\n group: 'training',\n },\n {\n id: 'atheletebridge-performance',\n label: t('atheletebridge.nav.performance', 'Performance'),\n path: joinPath(basePath, 'performance'),\n icon: <Gauge size={20} />,\n order: 22,\n group: 'training',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, t]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'atheletebridge-status',\n label: t('atheletebridge.footer.ready', 'AthleteBridge Ready'),\n type: 'status' as const,\n order: 10,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, t]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <AthleteBridgeProvider {...props}>\n <AthleteBridgeRoutes />\n </AthleteBridgeProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n"],"mappings":";;;;;;;;;AA4BA,IAAM,IAAc,IAAI,EAAY,EAClC,gBAAgB,EACd,SAAS;CACP,WAAW,MAAS;CACpB,OAAO;CACR,EACF,EACF,CAAC,EAEW,KAAiD,MAAU;CACtE,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,QAAQ,GAC5D,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAGK,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AAyFtD,QApFA,QAAgB;AACT,QA8DL,QADgB,EA3DY;GAC1B;IACE,IAAI;IACJ,OAAO,EAAE,gCAAgC,YAAY;IACrD,MAAM,EAAS,GAAU,GAAG;IAC5B,MAAM,kBAAC,GAAD,EAAiB,MAAM,IAAM,CAAA;IACnC,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,4BAA4B,QAAQ;IAC7C,MAAM,EAAS,GAAU,QAAQ;IACjC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,+BAA+B,WAAW;IACnD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;IAChC,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,gCAAgC,YAAY;IACrD,MAAM,EAAS,GAAU,YAAY;IACrC,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;IAC1B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,+BAA+B,WAAW;IACnD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAC5B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,+BAA+B,WAAW;IACnD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAgB,MAAM,IAAM,CAAA;IAClC,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAE,kCAAkC,cAAc;IACzD,MAAM,EAAS,GAAU,cAAc;IACvC,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OAAO;IACP,OAAO;IACR;GACF,CAEyC;IAEzC;EAAC;EAAkB;EAAU;EAAE,CAAC,EAGnC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO,EAAE,+BAA+B,sBAAsB;GAC9D,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,GAAqB,EAAE,CAAC,EAG1B,kBAAC,GAAD;EAAqB,QAAQ;YAC3B,kBAAC,GAAD;GAAqB,aAAa;GAAM,SAAQ;GAAS,gBAAgB;aACvE,kBAAC,GAAD;IAAuB,GAAI;cACzB,kBAAC,GAAD,EAAuB,CAAA;IACD,CAAA;GACJ,CAAA;EACF,CAAA"}
|
|
@@ -5,15 +5,15 @@ import { Loader2 as i } from "lucide-react";
|
|
|
5
5
|
import { Navigate as a, Route as o, Routes as s, useLocation as c } from "react-router-dom";
|
|
6
6
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
7
7
|
//#region src/atheletebridge/AthleteBridgeRoutes.tsx
|
|
8
|
-
var d = r(() => import("./pages/teams/TeamsListPage.js").then((e) => ({ default: e.TeamsListPage }))), f = r(() => import("./pages/teams/TeamDetailPage.js").then((e) => ({ default: e.TeamDetailPage }))), p = r(() => import("./pages/teams/TeamFormPage.js").then((e) => ({ default: e.TeamFormPage }))), m = r(() => import("./pages/fixtures/FixturesListPage.js").then((e) => ({ default: e.FixturesListPage }))), h = r(() => import("./pages/fixtures/FixtureDetailPage.js").then((e) => ({ default: e.FixtureDetailPage }))), g = r(() => import("./pages/fixtures/FixtureFormPage.js").then((e) => ({ default: e.FixtureFormPage }))), _ = r(() => import("./pages/StandingsPage.js").then((e) => ({ default: e.StandingsPage }))), v = r(() => import("./pages/programs/ProgramsListPage.js").then((e) => ({ default: e.ProgramsListPage }))), y = r(() => import("./pages/programs/ProgramDetailPage.js").then((e) => ({ default: e.ProgramDetailPage }))), b = r(() => import("./pages/programs/ProgramFormPage.js").then((e) => ({ default: e.ProgramFormPage }))), x = r(() => import("./pages/sessions/SessionsListPage.js").then((e) => ({ default: e.SessionsListPage }))), S = r(() => import("./pages/sessions/SessionDetailPage.js").then((e) => ({ default: e.SessionDetailPage }))), C = r(() => import("./pages/sessions/SessionFormPage.js").then((e) => ({ default: e.SessionFormPage }))), w = () => /* @__PURE__ */ l("div", {
|
|
8
|
+
var d = r(() => import("./pages/teams/TeamsListPage.js").then((e) => ({ default: e.TeamsListPage }))), f = r(() => import("./pages/teams/TeamDetailPage.js").then((e) => ({ default: e.TeamDetailPage }))), p = r(() => import("./pages/teams/TeamFormPage.js").then((e) => ({ default: e.TeamFormPage }))), m = r(() => import("./pages/fixtures/FixturesListPage.js").then((e) => ({ default: e.FixturesListPage }))), h = r(() => import("./pages/fixtures/FixtureDetailPage.js").then((e) => ({ default: e.FixtureDetailPage }))), g = r(() => import("./pages/fixtures/FixtureFormPage.js").then((e) => ({ default: e.FixtureFormPage }))), _ = r(() => import("./pages/StandingsPage.js").then((e) => ({ default: e.StandingsPage }))), v = r(() => import("./pages/programs/ProgramsListPage.js").then((e) => ({ default: e.ProgramsListPage }))), y = r(() => import("./pages/programs/ProgramDetailPage.js").then((e) => ({ default: e.ProgramDetailPage }))), b = r(() => import("./pages/programs/ProgramFormPage.js").then((e) => ({ default: e.ProgramFormPage }))), x = r(() => import("./pages/sessions/SessionsListPage.js").then((e) => ({ default: e.SessionsListPage }))), S = r(() => import("./pages/sessions/SessionDetailPage.js").then((e) => ({ default: e.SessionDetailPage }))), C = r(() => import("./pages/sessions/SessionFormPage.js").then((e) => ({ default: e.SessionFormPage }))), w = r(() => import("./pages/performance/PerformancePage.js").then((e) => ({ default: e.PerformancePage }))), T = () => /* @__PURE__ */ l("div", {
|
|
9
9
|
className: "flex min-h-[400px] items-center justify-center",
|
|
10
10
|
children: /* @__PURE__ */ l(i, { className: "h-8 w-8 animate-spin text-text-secondary" })
|
|
11
|
-
}),
|
|
12
|
-
let { basePath: r, isStandalone: i } = e(),
|
|
13
|
-
return
|
|
11
|
+
}), E = () => {
|
|
12
|
+
let { basePath: r, isStandalone: i } = e(), E = c(), D = r === "/" ? "" : (r ?? "").replace(/\/$/, "");
|
|
13
|
+
return D && !i && !(E.pathname === D || E.pathname.startsWith(D + "/")) ? null : /* @__PURE__ */ l("div", {
|
|
14
14
|
className: "flex min-h-full flex-col",
|
|
15
15
|
children: /* @__PURE__ */ l(n, {
|
|
16
|
-
fallback: /* @__PURE__ */ l(
|
|
16
|
+
fallback: /* @__PURE__ */ l(T, {}),
|
|
17
17
|
children: /* @__PURE__ */ u(s, { children: [
|
|
18
18
|
/* @__PURE__ */ l(o, {
|
|
19
19
|
index: !0,
|
|
@@ -79,6 +79,10 @@ var d = r(() => import("./pages/teams/TeamsListPage.js").then((e) => ({ default:
|
|
|
79
79
|
path: "sessions/:id",
|
|
80
80
|
element: /* @__PURE__ */ l(S, {})
|
|
81
81
|
}),
|
|
82
|
+
/* @__PURE__ */ l(o, {
|
|
83
|
+
path: "performance",
|
|
84
|
+
element: /* @__PURE__ */ l(w, {})
|
|
85
|
+
}),
|
|
82
86
|
/* @__PURE__ */ l(o, {
|
|
83
87
|
path: "*",
|
|
84
88
|
element: /* @__PURE__ */ l(a, {
|
|
@@ -91,6 +95,6 @@ var d = r(() => import("./pages/teams/TeamsListPage.js").then((e) => ({ default:
|
|
|
91
95
|
});
|
|
92
96
|
};
|
|
93
97
|
//#endregion
|
|
94
|
-
export {
|
|
98
|
+
export { E as AthleteBridgeRoutes };
|
|
95
99
|
|
|
96
100
|
//# sourceMappingURL=AthleteBridgeRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AthleteBridgeRoutes.js","names":[],"sources":["../../src/atheletebridge/AthleteBridgeRoutes.tsx"],"sourcesContent":["/**\n * AthleteBridge Routes\n * @module atheletebridge\n *\n * Real sports-domain routes wired to the deployed `wspace-atheletebridge-svc`\n * GraphQL contract. Dashboard is eager; entity pages are lazy.\n */\n\nimport { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { Loader2 } from 'lucide-react';\nimport { useAthleteBridge } from './context/AthleteBridgeContext';\n\n// Eager — primary landing\nimport { DashboardPage } from './pages/DashboardPage';\n\n// Teams\nconst TeamsListPage = lazy(() =>\n import('./pages/teams/TeamsListPage').then((m) => ({ default: m.TeamsListPage }))\n);\nconst TeamDetailPage = lazy(() =>\n import('./pages/teams/TeamDetailPage').then((m) => ({ default: m.TeamDetailPage }))\n);\nconst TeamFormPage = lazy(() =>\n import('./pages/teams/TeamFormPage').then((m) => ({ default: m.TeamFormPage }))\n);\n\n// Fixtures\nconst FixturesListPage = lazy(() =>\n import('./pages/fixtures/FixturesListPage').then((m) => ({ default: m.FixturesListPage }))\n);\nconst FixtureDetailPage = lazy(() =>\n import('./pages/fixtures/FixtureDetailPage').then((m) => ({ default: m.FixtureDetailPage }))\n);\nconst FixtureFormPage = lazy(() =>\n import('./pages/fixtures/FixtureFormPage').then((m) => ({ default: m.FixtureFormPage }))\n);\n\n// Standings\nconst StandingsPage = lazy(() =>\n import('./pages/StandingsPage').then((m) => ({ default: m.StandingsPage }))\n);\n\n// Programs\nconst ProgramsListPage = lazy(() =>\n import('./pages/programs/ProgramsListPage').then((m) => ({ default: m.ProgramsListPage }))\n);\nconst ProgramDetailPage = lazy(() =>\n import('./pages/programs/ProgramDetailPage').then((m) => ({ default: m.ProgramDetailPage }))\n);\nconst ProgramFormPage = lazy(() =>\n import('./pages/programs/ProgramFormPage').then((m) => ({ default: m.ProgramFormPage }))\n);\n\n// Sessions\nconst SessionsListPage = lazy(() =>\n import('./pages/sessions/SessionsListPage').then((m) => ({ default: m.SessionsListPage }))\n);\nconst SessionDetailPage = lazy(() =>\n import('./pages/sessions/SessionDetailPage').then((m) => ({ default: m.SessionDetailPage }))\n);\nconst SessionFormPage = lazy(() =>\n import('./pages/sessions/SessionFormPage').then((m) => ({ default: m.SessionFormPage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n);\n\nexport const AthleteBridgeRoutes: FC = () => {\n const { basePath, isStandalone } = useAthleteBridge();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render when the path matches basePath (integrated mode).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Dashboard */}\n <Route index element={<DashboardPage />} />\n\n {/* Teams */}\n <Route path=\"teams\" element={<TeamsListPage />} />\n <Route path=\"teams/new\" element={<TeamFormPage />} />\n <Route path=\"teams/:id\" element={<TeamDetailPage />} />\n <Route path=\"teams/:id/edit\" element={<TeamFormPage />} />\n\n {/* Fixtures */}\n <Route path=\"fixtures\" element={<FixturesListPage />} />\n <Route path=\"fixtures/new\" element={<FixtureFormPage />} />\n <Route path=\"fixtures/:id\" element={<FixtureDetailPage />} />\n\n {/* Standings */}\n <Route path=\"standings\" element={<StandingsPage />} />\n\n {/* Programs */}\n <Route path=\"programs\" element={<ProgramsListPage />} />\n <Route path=\"programs/new\" element={<ProgramFormPage />} />\n <Route path=\"programs/:id\" element={<ProgramDetailPage />} />\n <Route path=\"programs/:id/edit\" element={<ProgramFormPage />} />\n\n {/* Sessions */}\n <Route path=\"sessions\" element={<SessionsListPage />} />\n <Route path=\"sessions/new\" element={<SessionFormPage />} />\n <Route path=\"sessions/:id\" element={<SessionDetailPage />} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\".\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAiBA,IAAM,IAAgB,QACpB,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAClF,EACK,IAAiB,QACrB,OAAO,mCAAgC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACpF,EACK,IAAe,QACnB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAChF,EAGK,IAAmB,QACvB,OAAO,wCAAqC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC3F,EACK,IAAoB,QACxB,OAAO,yCAAsC,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAC7F,EACK,IAAkB,QACtB,OAAO,uCAAoC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CACzF,EAGK,IAAgB,QACpB,OAAO,4BAAyB,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAC5E,EAGK,IAAmB,QACvB,OAAO,wCAAqC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC3F,EACK,IAAoB,QACxB,OAAO,yCAAsC,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAC7F,EACK,IAAkB,QACtB,OAAO,uCAAoC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CACzF,EAGK,IAAmB,QACvB,OAAO,wCAAqC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC3F,EACK,IAAoB,QACxB,OAAO,yCAAsC,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAC7F,EACK,IAAkB,QACtB,OAAO,uCAAoC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CACzF,EAEK,UACJ,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;CAC5D,CAAA,EAGK,UAAgC;CAC3C,IAAM,EAAE,aAAU,oBAAiB,GAAkB,EAC/C,IAAW,GAAa,EAExB,IAAqB,MAAa,MAAM,MAAM,KAAY,IAAI,QAAQ,OAAO,GAAG;AAYtF,QATI,KAAsB,CAAC,KAIrB,EAFF,EAAS,aAAa,KACtB,EAAS,SAAS,WAAW,IAAqB,IAAI,IAE/C,OAKT,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,EAAc,CAAA;aAChC,kBAAC,GAAD,EAAA,UAAA;IAEE,kBAAC,GAAD;KAAO,OAAA;KAAM,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IAG3C,kBAAC,GAAD;KAAO,MAAK;KAAQ,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IAClD,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAgB,CAAA;KAAI,CAAA;IACrD,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAkB,CAAA;KAAI,CAAA;IACvD,kBAAC,GAAD;KAAO,MAAK;KAAiB,SAAS,kBAAC,GAAD,EAAgB,CAAA;KAAI,CAAA;IAG1D,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,EAAoB,CAAA;KAAI,CAAA;IACxD,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IAC3D,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,EAAqB,CAAA;KAAI,CAAA;IAG7D,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IAGtD,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,EAAoB,CAAA;KAAI,CAAA;IACxD,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IAC3D,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,EAAqB,CAAA;KAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAoB,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IAGhE,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,EAAoB,CAAA;KAAI,CAAA;IACxD,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IAC3D,kBAAC,GAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,GAAD,EAAqB,CAAA;KAAI,CAAA;IAG7D,kBAAC,GAAD;KAAO,MAAK;KAAI,SAAS,kBAAC,GAAD;MAAU,IAAG;MAAI,SAAA;MAAU,CAAA;KAAI,CAAA;IACjD,EAAA,CAAA;GACA,CAAA;EACP,CAAA"}
|
|
1
|
+
{"version":3,"file":"AthleteBridgeRoutes.js","names":[],"sources":["../../src/atheletebridge/AthleteBridgeRoutes.tsx"],"sourcesContent":["/**\n * AthleteBridge Routes\n * @module atheletebridge\n *\n * Real sports-domain routes wired to the deployed `wspace-atheletebridge-svc`\n * GraphQL contract. Dashboard is eager; entity pages are lazy.\n */\n\nimport { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { Loader2 } from 'lucide-react';\nimport { useAthleteBridge } from './context/AthleteBridgeContext';\n\n// Eager — primary landing\nimport { DashboardPage } from './pages/DashboardPage';\n\n// Teams\nconst TeamsListPage = lazy(() =>\n import('./pages/teams/TeamsListPage').then((m) => ({ default: m.TeamsListPage }))\n);\nconst TeamDetailPage = lazy(() =>\n import('./pages/teams/TeamDetailPage').then((m) => ({ default: m.TeamDetailPage }))\n);\nconst TeamFormPage = lazy(() =>\n import('./pages/teams/TeamFormPage').then((m) => ({ default: m.TeamFormPage }))\n);\n\n// Fixtures\nconst FixturesListPage = lazy(() =>\n import('./pages/fixtures/FixturesListPage').then((m) => ({ default: m.FixturesListPage }))\n);\nconst FixtureDetailPage = lazy(() =>\n import('./pages/fixtures/FixtureDetailPage').then((m) => ({ default: m.FixtureDetailPage }))\n);\nconst FixtureFormPage = lazy(() =>\n import('./pages/fixtures/FixtureFormPage').then((m) => ({ default: m.FixtureFormPage }))\n);\n\n// Standings\nconst StandingsPage = lazy(() =>\n import('./pages/StandingsPage').then((m) => ({ default: m.StandingsPage }))\n);\n\n// Programs\nconst ProgramsListPage = lazy(() =>\n import('./pages/programs/ProgramsListPage').then((m) => ({ default: m.ProgramsListPage }))\n);\nconst ProgramDetailPage = lazy(() =>\n import('./pages/programs/ProgramDetailPage').then((m) => ({ default: m.ProgramDetailPage }))\n);\nconst ProgramFormPage = lazy(() =>\n import('./pages/programs/ProgramFormPage').then((m) => ({ default: m.ProgramFormPage }))\n);\n\n// Sessions\nconst SessionsListPage = lazy(() =>\n import('./pages/sessions/SessionsListPage').then((m) => ({ default: m.SessionsListPage }))\n);\nconst SessionDetailPage = lazy(() =>\n import('./pages/sessions/SessionDetailPage').then((m) => ({ default: m.SessionDetailPage }))\n);\nconst SessionFormPage = lazy(() =>\n import('./pages/sessions/SessionFormPage').then((m) => ({ default: m.SessionFormPage }))\n);\n\n// Performance\nconst PerformancePage = lazy(() =>\n import('./pages/performance/PerformancePage').then((m) => ({ default: m.PerformancePage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n);\n\nexport const AthleteBridgeRoutes: FC = () => {\n const { basePath, isStandalone } = useAthleteBridge();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render when the path matches basePath (integrated mode).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Dashboard */}\n <Route index element={<DashboardPage />} />\n\n {/* Teams */}\n <Route path=\"teams\" element={<TeamsListPage />} />\n <Route path=\"teams/new\" element={<TeamFormPage />} />\n <Route path=\"teams/:id\" element={<TeamDetailPage />} />\n <Route path=\"teams/:id/edit\" element={<TeamFormPage />} />\n\n {/* Fixtures */}\n <Route path=\"fixtures\" element={<FixturesListPage />} />\n <Route path=\"fixtures/new\" element={<FixtureFormPage />} />\n <Route path=\"fixtures/:id\" element={<FixtureDetailPage />} />\n\n {/* Standings */}\n <Route path=\"standings\" element={<StandingsPage />} />\n\n {/* Programs */}\n <Route path=\"programs\" element={<ProgramsListPage />} />\n <Route path=\"programs/new\" element={<ProgramFormPage />} />\n <Route path=\"programs/:id\" element={<ProgramDetailPage />} />\n <Route path=\"programs/:id/edit\" element={<ProgramFormPage />} />\n\n {/* Sessions */}\n <Route path=\"sessions\" element={<SessionsListPage />} />\n <Route path=\"sessions/new\" element={<SessionFormPage />} />\n <Route path=\"sessions/:id\" element={<SessionDetailPage />} />\n\n {/* Performance */}\n <Route path=\"performance\" element={<PerformancePage />} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\".\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useTeamMembers as e, useTeams as t } from "../hooks/useAthleteBridgeApi.js";
|
|
2
|
+
import { FormField as n, fieldInputClass as r } from "./form.js";
|
|
3
|
+
import { useMemo as i, useState as a } from "react";
|
|
4
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
5
|
+
//#region src/atheletebridge/components/AthleteSelect.tsx
|
|
6
|
+
function c({ athleteId: c, onSelect: l, required: u = !1, teamLabel: d = "Team", athleteLabel: f = "Athlete", idPrefix: p = "athlete" }) {
|
|
7
|
+
let [m, h] = a(""), g = t({ pagination: { limit: 100 } }), _ = e(m || void 0), v = i(() => g.data?.items ?? [], [g.data]), y = i(() => (_.data ?? []).filter((e) => !!e.userId), [_.data]), b = `${p}-team`, x = `${p}-athlete`, S = m ? _.isLoading ? "Loading roster…" : y.length === 0 ? "This team has no members yet — add them from the team page." : void 0 : "Choose a team to load its roster.";
|
|
8
|
+
return /* @__PURE__ */ s("div", {
|
|
9
|
+
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
10
|
+
children: [/* @__PURE__ */ o(n, {
|
|
11
|
+
label: d,
|
|
12
|
+
htmlFor: b,
|
|
13
|
+
required: u,
|
|
14
|
+
children: /* @__PURE__ */ s("select", {
|
|
15
|
+
id: b,
|
|
16
|
+
className: r,
|
|
17
|
+
value: m,
|
|
18
|
+
onChange: (e) => {
|
|
19
|
+
h(e.target.value), l({
|
|
20
|
+
athleteId: "",
|
|
21
|
+
displayName: ""
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
children: [/* @__PURE__ */ o("option", {
|
|
25
|
+
value: "",
|
|
26
|
+
children: "Select a team…"
|
|
27
|
+
}), v.map((e) => /* @__PURE__ */ o("option", {
|
|
28
|
+
value: e.id,
|
|
29
|
+
children: e.name
|
|
30
|
+
}, e.id))]
|
|
31
|
+
})
|
|
32
|
+
}), /* @__PURE__ */ o(n, {
|
|
33
|
+
label: f,
|
|
34
|
+
htmlFor: x,
|
|
35
|
+
required: u,
|
|
36
|
+
hint: S,
|
|
37
|
+
children: /* @__PURE__ */ s("select", {
|
|
38
|
+
id: x,
|
|
39
|
+
className: r,
|
|
40
|
+
value: c,
|
|
41
|
+
disabled: !m || y.length === 0,
|
|
42
|
+
onChange: (e) => {
|
|
43
|
+
let t = y.find((t) => t.userId === e.target.value);
|
|
44
|
+
l(t ? {
|
|
45
|
+
athleteId: t.userId,
|
|
46
|
+
displayName: t.displayName
|
|
47
|
+
} : {
|
|
48
|
+
athleteId: "",
|
|
49
|
+
displayName: ""
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
children: [/* @__PURE__ */ o("option", {
|
|
53
|
+
value: "",
|
|
54
|
+
children: "Select an athlete…"
|
|
55
|
+
}), y.map((e) => /* @__PURE__ */ s("option", {
|
|
56
|
+
value: e.userId,
|
|
57
|
+
children: [e.displayName, e.jerseyNumber == null ? "" : ` · #${e.jerseyNumber}`]
|
|
58
|
+
}, e.id))]
|
|
59
|
+
})
|
|
60
|
+
})]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { c as AthleteSelect };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=AthleteSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AthleteSelect.js","names":[],"sources":["../../../src/atheletebridge/components/AthleteSelect.tsx"],"sourcesContent":["/**\n * Athlete picker\n * @module atheletebridge/components\n *\n * Two coupled selects — pick a team, then pick an athlete from that team's live\n * roster (`athListMembers`). Emits the athlete's user id (used everywhere an\n * `athleteId` is required) plus a display name, so a coach never has to paste a\n * raw UUID. Semantic tokens only.\n */\n\nimport { useMemo, useState } from 'react';\nimport { FormField, fieldInputClass } from './form';\nimport { useTeams, useTeamMembers } from '../hooks/useAthleteBridgeApi';\n\nexport interface AthleteSelectValue {\n athleteId: string;\n displayName: string;\n}\n\ninterface AthleteSelectProps {\n /** Currently selected athlete user id. */\n athleteId: string;\n onSelect: (value: AthleteSelectValue) => void;\n required?: boolean;\n teamLabel?: string;\n athleteLabel?: string;\n /** Prefix for the field ids so multiple instances stay unique/accessible. */\n idPrefix?: string;\n}\n\nexport function AthleteSelect({\n athleteId,\n onSelect,\n required = false,\n teamLabel = 'Team',\n athleteLabel = 'Athlete',\n idPrefix = 'athlete',\n}: AthleteSelectProps) {\n const [teamId, setTeamId] = useState('');\n\n const teams = useTeams({ pagination: { limit: 100 } });\n const members = useTeamMembers(teamId || undefined);\n\n const teamOptions = useMemo(() => teams.data?.items ?? [], [teams.data]);\n const memberOptions = useMemo(\n () => (members.data ?? []).filter((m) => Boolean(m.userId)),\n [members.data]\n );\n\n const teamFieldId = `${idPrefix}-team`;\n const athleteFieldId = `${idPrefix}-athlete`;\n\n const memberHint = teamId\n ? members.isLoading\n ? 'Loading roster…'\n : memberOptions.length === 0\n ? 'This team has no members yet — add them from the team page.'\n : undefined\n : 'Choose a team to load its roster.';\n\n return (\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <FormField label={teamLabel} htmlFor={teamFieldId} required={required}>\n <select\n id={teamFieldId}\n className={fieldInputClass}\n value={teamId}\n onChange={(e) => {\n setTeamId(e.target.value);\n // Clear any prior athlete selection when the team changes.\n onSelect({ athleteId: '', displayName: '' });\n }}\n >\n <option value=\"\">Select a team…</option>\n {teamOptions.map((team) => (\n <option key={team.id} value={team.id}>\n {team.name}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField\n label={athleteLabel}\n htmlFor={athleteFieldId}\n required={required}\n hint={memberHint}\n >\n <select\n id={athleteFieldId}\n className={fieldInputClass}\n value={athleteId}\n disabled={!teamId || memberOptions.length === 0}\n onChange={(e) => {\n const picked = memberOptions.find((m) => m.userId === e.target.value);\n onSelect(\n picked\n ? { athleteId: picked.userId, displayName: picked.displayName }\n : { athleteId: '', displayName: '' }\n );\n }}\n >\n <option value=\"\">Select an athlete…</option>\n {memberOptions.map((member) => (\n <option key={member.id} value={member.userId}>\n {member.displayName}\n {member.jerseyNumber != null ? ` · #${member.jerseyNumber}` : ''}\n </option>\n ))}\n </select>\n </FormField>\n </div>\n );\n}\n"],"mappings":";;;;;AA8BA,SAAgB,EAAc,EAC5B,cACA,aACA,cAAW,IACX,eAAY,QACZ,kBAAe,WACf,cAAW,aACU;CACrB,IAAM,CAAC,GAAQ,KAAa,EAAS,GAAG,EAElC,IAAQ,EAAS,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EAChD,IAAU,EAAe,KAAU,KAAA,EAAU,EAE7C,IAAc,QAAc,EAAM,MAAM,SAAS,EAAE,EAAE,CAAC,EAAM,KAAK,CAAC,EAClE,IAAgB,SACb,EAAQ,QAAQ,EAAE,EAAE,QAAQ,MAAM,EAAQ,EAAE,OAAQ,EAC3D,CAAC,EAAQ,KAAK,CACf,EAEK,IAAc,GAAG,EAAS,QAC1B,IAAiB,GAAG,EAAS,WAE7B,IAAa,IACf,EAAQ,YACN,oBACA,EAAc,WAAW,IACvB,gEACA,KAAA,IACJ;AAEJ,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAW,OAAO;GAAW,SAAS;GAAuB;aAC3D,kBAAC,UAAD;IACE,IAAI;IACJ,WAAW;IACX,OAAO;IACP,WAAW,MAAM;AAGf,KAFA,EAAU,EAAE,OAAO,MAAM,EAEzB,EAAS;MAAE,WAAW;MAAI,aAAa;MAAI,CAAC;;cAPhD,CAUE,kBAAC,UAAD;KAAQ,OAAM;eAAG;KAAuB,CAAA,EACvC,EAAY,KAAK,MAChB,kBAAC,UAAD;KAAsB,OAAO,EAAK;eAC/B,EAAK;KACC,EAFI,EAAK,GAET,CACT,CACK;;GACC,CAAA,EAEZ,kBAAC,GAAD;GACE,OAAO;GACP,SAAS;GACC;GACV,MAAM;aAEN,kBAAC,UAAD;IACE,IAAI;IACJ,WAAW;IACX,OAAO;IACP,UAAU,CAAC,KAAU,EAAc,WAAW;IAC9C,WAAW,MAAM;KACf,IAAM,IAAS,EAAc,MAAM,MAAM,EAAE,WAAW,EAAE,OAAO,MAAM;AACrE,OACE,IACI;MAAE,WAAW,EAAO;MAAQ,aAAa,EAAO;MAAa,GAC7D;MAAE,WAAW;MAAI,aAAa;MAAI,CACvC;;cAXL,CAcE,kBAAC,UAAD;KAAQ,OAAM;eAAG;KAA2B,CAAA,EAC3C,EAAc,KAAK,MAClB,kBAAC,UAAD;KAAwB,OAAO,EAAO;eAAtC,CACG,EAAO,aACP,EAAO,gBAAgB,OAAsC,KAA/B,OAAO,EAAO,eACtC;OAHI,EAAO,GAGX,CACT,CACK;;GACC,CAAA,CACR"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useFile as e } from "../hooks/useAthleteBridgeApi.js";
|
|
2
|
+
import { ExternalLink as t, FileVideo as n, Loader2 as r } from "lucide-react";
|
|
3
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
+
//#region src/atheletebridge/components/DrillVideoLink.tsx
|
|
5
|
+
function o({ fileId: o }) {
|
|
6
|
+
let s = e(o), c = s.data?.downloadUrl ?? void 0, l = s.data?.name ?? "Demo video";
|
|
7
|
+
return c ? /* @__PURE__ */ a("a", {
|
|
8
|
+
href: c,
|
|
9
|
+
target: "_blank",
|
|
10
|
+
rel: "noreferrer",
|
|
11
|
+
className: "mt-1.5 inline-flex items-center gap-1 text-xs font-medium text-primary hover:underline",
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ i(n, { className: "h-3.5 w-3.5" }),
|
|
14
|
+
/* @__PURE__ */ i("span", {
|
|
15
|
+
className: "max-w-[16rem] truncate",
|
|
16
|
+
children: l
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ i(t, { className: "h-3 w-3" })
|
|
19
|
+
]
|
|
20
|
+
}) : /* @__PURE__ */ a("span", {
|
|
21
|
+
className: "mt-1.5 inline-flex items-center gap-1 text-xs text-text-secondary",
|
|
22
|
+
children: [s.isLoading ? /* @__PURE__ */ i(r, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ i(n, { className: "h-3.5 w-3.5" }), "Demo video"]
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { o as DrillVideoLink };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=DrillVideoLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DrillVideoLink.js","names":[],"sources":["../../../src/atheletebridge/components/DrillVideoLink.tsx"],"sourcesContent":["/**\n * Drill demo-video reference\n * @module atheletebridge/components\n *\n * A drill's `video` is a reference into wspace-files-svc (blobs live there). We\n * resolve the playable URL lazily and in isolation, so a missing/expired blob\n * degrades to a quiet badge instead of ever breaking the program page.\n */\n\nimport { ExternalLink, FileVideo, Loader2 } from 'lucide-react';\nimport { useFile } from '../hooks/useAthleteBridgeApi';\n\nexport function DrillVideoLink({ fileId }: { fileId: string }) {\n const file = useFile(fileId);\n const url = file.data?.downloadUrl ?? undefined;\n const label = file.data?.name ?? 'Demo video';\n\n if (url) {\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-1.5 inline-flex items-center gap-1 text-xs font-medium text-primary hover:underline\"\n >\n <FileVideo className=\"h-3.5 w-3.5\" />\n <span className=\"max-w-[16rem] truncate\">{label}</span>\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n );\n }\n\n return (\n <span className=\"mt-1.5 inline-flex items-center gap-1 text-xs text-text-secondary\">\n {file.isLoading ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <FileVideo className=\"h-3.5 w-3.5\" />\n )}\n Demo video\n </span>\n );\n}\n"],"mappings":";;;;AAYA,SAAgB,EAAe,EAAE,aAA8B;CAC7D,IAAM,IAAO,EAAQ,EAAO,EACtB,IAAM,EAAK,MAAM,eAAe,KAAA,GAChC,IAAQ,EAAK,MAAM,QAAQ;AAiBjC,QAfI,IAEA,kBAAC,KAAD;EACE,MAAM;EACN,QAAO;EACP,KAAI;EACJ,WAAU;YAJZ;GAME,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA;GACrC,kBAAC,QAAD;IAAM,WAAU;cAA0B;IAAa,CAAA;GACvD,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;GAClC;MAKN,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACG,EAAK,YACJ,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA,EACrC,aAEG"}
|
|
@@ -1,52 +1,26 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { GlassCard as o } from "@burdenoff/fe-libs/ui";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { GlassCard as r } from "@burdenoff/fe-libs/ui";
|
|
5
4
|
//#region src/atheletebridge/components/MetricsCard.tsx
|
|
6
|
-
var s = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return /* @__PURE__ */ a(o, {
|
|
22
|
-
treatment: u ? "glass" : "metric",
|
|
23
|
-
glow: u,
|
|
24
|
-
className: e("p-6", d),
|
|
25
|
-
children: [/* @__PURE__ */ a("div", {
|
|
26
|
-
className: "relative flex items-center justify-between",
|
|
27
|
-
children: [/* @__PURE__ */ i("div", {
|
|
28
|
-
className: "text-text-secondary",
|
|
29
|
-
children: t
|
|
30
|
-
}), c && f && p && /* @__PURE__ */ a("div", {
|
|
31
|
-
className: e("flex items-center gap-1 text-xs font-medium", f.color),
|
|
32
|
-
children: [/* @__PURE__ */ i(p, { className: "h-3.5 w-3.5" }), l && /* @__PURE__ */ i("span", {
|
|
33
|
-
className: "tabular-nums",
|
|
34
|
-
children: l
|
|
35
|
-
})]
|
|
36
|
-
})]
|
|
37
|
-
}), /* @__PURE__ */ a("div", {
|
|
38
|
-
className: "relative mt-3",
|
|
39
|
-
children: [/* @__PURE__ */ i("p", {
|
|
40
|
-
className: "text-2xl font-bold tabular-nums text-text-primary",
|
|
41
|
-
children: n
|
|
42
|
-
}), /* @__PURE__ */ i("p", {
|
|
43
|
-
className: "mt-1 text-sm text-text-secondary",
|
|
44
|
-
children: r
|
|
45
|
-
})]
|
|
5
|
+
var i = ({ icon: i, value: a, label: o, featured: s = !1, className: c }) => /* @__PURE__ */ n(r, {
|
|
6
|
+
treatment: s ? "glass" : "metric",
|
|
7
|
+
glow: s,
|
|
8
|
+
className: e("p-6", c),
|
|
9
|
+
children: [/* @__PURE__ */ t("div", {
|
|
10
|
+
className: "relative text-text-secondary",
|
|
11
|
+
children: i
|
|
12
|
+
}), /* @__PURE__ */ n("div", {
|
|
13
|
+
className: "relative mt-3",
|
|
14
|
+
children: [/* @__PURE__ */ t("p", {
|
|
15
|
+
className: "text-2xl font-bold tabular-nums text-text-primary",
|
|
16
|
+
children: a
|
|
17
|
+
}), /* @__PURE__ */ t("p", {
|
|
18
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
19
|
+
children: o
|
|
46
20
|
})]
|
|
47
|
-
})
|
|
48
|
-
};
|
|
21
|
+
})]
|
|
22
|
+
});
|
|
49
23
|
//#endregion
|
|
50
|
-
export {
|
|
24
|
+
export { i as MetricsCard };
|
|
51
25
|
|
|
52
26
|
//# sourceMappingURL=MetricsCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MetricsCard.js","names":[],"sources":["../../../src/atheletebridge/components/MetricsCard.tsx"],"sourcesContent":["/**\n * Metrics Card Component\n * @module atheletebridge/components\n *\n * Boff UI 2.0: KPI tiles use the glassmorphic `GlassCard` \"metric\" treatment\n * (accent wash + seam border + soft pop-lift). The hero metric per page can\n * opt into the brighter `glass` treatment with an accent glow via `featured`.\n * Numerals are tabular for clean alignment. No hover movement.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"MetricsCard.js","names":[],"sources":["../../../src/atheletebridge/components/MetricsCard.tsx"],"sourcesContent":["/**\n * Metrics Card Component\n * @module atheletebridge/components\n *\n * Boff UI 2.0: KPI tiles use the glassmorphic `GlassCard` \"metric\" treatment\n * (accent wash + seam border + soft pop-lift). The hero metric per page can\n * opt into the brighter `glass` treatment with an accent glow via `featured`.\n * Numerals are tabular for clean alignment. No hover movement.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface MetricsCardProps {\n icon: ReactNode;\n value: string | number;\n label: string;\n /** Render as the single featured hero metric (glass treatment + glow). */\n featured?: boolean;\n className?: string;\n}\n\nexport const MetricsCard: FC<MetricsCardProps> = ({\n icon,\n value,\n label,\n featured = false,\n className,\n}) => {\n return (\n <GlassCard\n treatment={featured ? 'glass' : 'metric'}\n glow={featured}\n className={cn('p-6', className)}\n >\n <div className=\"relative text-text-secondary\">{icon}</div>\n <div className=\"relative mt-3\">\n <p className=\"text-2xl font-bold tabular-nums text-text-primary\">{value}</p>\n <p className=\"mt-1 text-sm text-text-secondary\">{label}</p>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":";;;;AAuBA,IAAa,KAAqC,EAChD,SACA,UACA,UACA,cAAW,IACX,mBAGE,kBAAC,GAAD;CACE,WAAW,IAAW,UAAU;CAChC,MAAM;CACN,WAAW,EAAG,OAAO,EAAU;WAHjC,CAKE,kBAAC,OAAD;EAAK,WAAU;YAAgC;EAAW,CAAA,EAC1D,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,KAAD;GAAG,WAAU;aAAqD;GAAU,CAAA,EAC5E,kBAAC,KAAD;GAAG,WAAU;aAAoC;GAAU,CAAA,CACvD;IACI"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { directoryUserName as t, useUserSearch as n } from "../hooks/useAthleteBridgeApi.js";
|
|
3
|
+
import { FormField as r, fieldInputClass as i } from "./form.js";
|
|
4
|
+
import { useState as a } from "react";
|
|
5
|
+
import { Check as o, Loader2 as s, Search as c, User as l, X as u } from "lucide-react";
|
|
6
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
7
|
+
//#region src/atheletebridge/components/UserSearchSelect.tsx
|
|
8
|
+
function p({ userId: p, displayName: m, onSelect: h, required: g = !1 }) {
|
|
9
|
+
let [_, v] = a(""), [y, b] = a(!1), x = n(_), S = x.data ?? [], C = _.trim().length >= 2, w = (e) => {
|
|
10
|
+
h({
|
|
11
|
+
userId: e.id,
|
|
12
|
+
displayName: t(e)
|
|
13
|
+
}), v("");
|
|
14
|
+
}, T = x.isError;
|
|
15
|
+
return y || T ? /* @__PURE__ */ f(r, {
|
|
16
|
+
label: "User ID",
|
|
17
|
+
htmlFor: "member-user-id",
|
|
18
|
+
required: g,
|
|
19
|
+
hint: T ? "User search is unavailable here — paste the auth-svc user id." : "Paste the auth-svc user id.",
|
|
20
|
+
children: [/* @__PURE__ */ d("input", {
|
|
21
|
+
id: "member-user-id",
|
|
22
|
+
className: i,
|
|
23
|
+
value: p,
|
|
24
|
+
onChange: (e) => h({
|
|
25
|
+
userId: e.target.value,
|
|
26
|
+
displayName: m
|
|
27
|
+
}),
|
|
28
|
+
placeholder: "user-uuid"
|
|
29
|
+
}), !T && /* @__PURE__ */ d("button", {
|
|
30
|
+
type: "button",
|
|
31
|
+
onClick: () => b(!1),
|
|
32
|
+
className: "mt-1 self-start text-xs font-medium text-primary hover:underline",
|
|
33
|
+
children: "Search the directory instead"
|
|
34
|
+
})]
|
|
35
|
+
}) : /* @__PURE__ */ d(r, {
|
|
36
|
+
label: "Athlete / user",
|
|
37
|
+
required: g,
|
|
38
|
+
children: p ? /* @__PURE__ */ f("div", {
|
|
39
|
+
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ d("span", {
|
|
42
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary",
|
|
43
|
+
children: /* @__PURE__ */ d(l, { className: "h-4 w-4" })
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ f("div", {
|
|
46
|
+
className: "min-w-0 flex-1",
|
|
47
|
+
children: [/* @__PURE__ */ d("p", {
|
|
48
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
49
|
+
children: m || "Selected user"
|
|
50
|
+
}), /* @__PURE__ */ d("p", {
|
|
51
|
+
className: "truncate text-xs text-text-secondary",
|
|
52
|
+
children: p
|
|
53
|
+
})]
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ d("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
"aria-label": "Clear selected user",
|
|
58
|
+
onClick: () => h({
|
|
59
|
+
userId: "",
|
|
60
|
+
displayName: ""
|
|
61
|
+
}),
|
|
62
|
+
className: "rounded-md p-1 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
|
|
63
|
+
children: /* @__PURE__ */ d(u, { className: "h-4 w-4" })
|
|
64
|
+
})
|
|
65
|
+
]
|
|
66
|
+
}) : /* @__PURE__ */ f("div", {
|
|
67
|
+
className: "space-y-2",
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ f("div", {
|
|
70
|
+
className: "relative",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ d(c, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }),
|
|
73
|
+
/* @__PURE__ */ d("input", {
|
|
74
|
+
className: e(i, "pl-9"),
|
|
75
|
+
value: _,
|
|
76
|
+
onChange: (e) => v(e.target.value),
|
|
77
|
+
placeholder: "Search by name or email…",
|
|
78
|
+
"aria-label": "Search users by name or email"
|
|
79
|
+
}),
|
|
80
|
+
x.isFetching && /* @__PURE__ */ d(s, { className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-text-secondary" })
|
|
81
|
+
]
|
|
82
|
+
}),
|
|
83
|
+
C && !x.isFetching && S.length === 0 && /* @__PURE__ */ f("p", {
|
|
84
|
+
className: "text-xs text-text-secondary",
|
|
85
|
+
children: [
|
|
86
|
+
"No users match “",
|
|
87
|
+
_.trim(),
|
|
88
|
+
"”."
|
|
89
|
+
]
|
|
90
|
+
}),
|
|
91
|
+
S.length > 0 && /* @__PURE__ */ d("ul", {
|
|
92
|
+
className: "max-h-56 divide-y divide-border-seam overflow-auto rounded-lg border border-border-seam bg-bg-surface",
|
|
93
|
+
children: S.map((e) => /* @__PURE__ */ d("li", { children: /* @__PURE__ */ f("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
onClick: () => w(e),
|
|
96
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-bg-sunken",
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ d("span", {
|
|
99
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary",
|
|
100
|
+
children: /* @__PURE__ */ d(l, { className: "h-4 w-4" })
|
|
101
|
+
}),
|
|
102
|
+
/* @__PURE__ */ f("span", {
|
|
103
|
+
className: "min-w-0 flex-1",
|
|
104
|
+
children: [/* @__PURE__ */ d("span", {
|
|
105
|
+
className: "block truncate text-sm font-medium text-text-primary",
|
|
106
|
+
children: t(e)
|
|
107
|
+
}), /* @__PURE__ */ d("span", {
|
|
108
|
+
className: "block truncate text-xs text-text-secondary",
|
|
109
|
+
children: e.email
|
|
110
|
+
})]
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ d(o, { className: "h-4 w-4 shrink-0 text-text-secondary opacity-0" })
|
|
113
|
+
]
|
|
114
|
+
}) }, e.id))
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ d("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
onClick: () => b(!0),
|
|
119
|
+
className: "text-xs font-medium text-primary hover:underline",
|
|
120
|
+
children: "Enter a user ID manually"
|
|
121
|
+
})
|
|
122
|
+
]
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { p as UserSearchSelect };
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=UserSearchSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserSearchSelect.js","names":[],"sources":["../../../src/atheletebridge/components/UserSearchSelect.tsx"],"sourcesContent":["/**\n * User picker\n * @module atheletebridge/components\n *\n * Resolves a real workspace/tenant user by name or email (global user\n * directory) so a coach can add a team member without knowing a raw UUID.\n * Degrades gracefully: if the directory is unavailable the coach can still paste\n * a user id manually. Semantic tokens only.\n */\n\nimport { useState } from 'react';\nimport { Check, Loader2, Search, User as UserIcon, X } from 'lucide-react';\nimport { FormField, fieldInputClass } from './form';\nimport { cn } from '../utils/cn';\nimport { directoryUserName, useUserSearch, type DirectoryUser } from '../hooks/useAthleteBridgeApi';\n\nexport interface UserSelectValue {\n userId: string;\n displayName: string;\n}\n\ninterface UserSearchSelectProps {\n /** Currently selected user id. */\n userId: string;\n /** Display name currently chosen (echoed back so the parent can prefill). */\n displayName: string;\n onSelect: (value: UserSelectValue) => void;\n required?: boolean;\n}\n\nexport function UserSearchSelect({\n userId,\n displayName,\n onSelect,\n required = false,\n}: UserSearchSelectProps) {\n const [term, setTerm] = useState('');\n const [manual, setManual] = useState(false);\n\n const search = useUserSearch(term);\n const results = search.data ?? [];\n const active = term.trim().length >= 2;\n\n const pick = (user: DirectoryUser) => {\n onSelect({ userId: user.id, displayName: directoryUserName(user) });\n setTerm('');\n };\n\n // If the directory query fails (e.g. RBAC), offer manual entry automatically.\n const searchUnavailable = search.isError;\n\n if (manual || searchUnavailable) {\n return (\n <FormField\n label=\"User ID\"\n htmlFor=\"member-user-id\"\n required={required}\n hint={\n searchUnavailable\n ? 'User search is unavailable here — paste the auth-svc user id.'\n : 'Paste the auth-svc user id.'\n }\n >\n <input\n id=\"member-user-id\"\n className={fieldInputClass}\n value={userId}\n onChange={(e) => onSelect({ userId: e.target.value, displayName })}\n placeholder=\"user-uuid\"\n />\n {!searchUnavailable && (\n <button\n type=\"button\"\n onClick={() => setManual(false)}\n className=\"mt-1 self-start text-xs font-medium text-primary hover:underline\"\n >\n Search the directory instead\n </button>\n )}\n </FormField>\n );\n }\n\n return (\n <FormField label=\"Athlete / user\" required={required}>\n {userId ? (\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <UserIcon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {displayName || 'Selected user'}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">{userId}</p>\n </div>\n <button\n type=\"button\"\n aria-label=\"Clear selected user\"\n onClick={() => onSelect({ userId: '', displayName: '' })}\n className=\"rounded-md p-1 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <div className=\"space-y-2\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n className={cn(fieldInputClass, 'pl-9')}\n value={term}\n onChange={(e) => setTerm(e.target.value)}\n placeholder=\"Search by name or email…\"\n aria-label=\"Search users by name or email\"\n />\n {search.isFetching && (\n <Loader2 className=\"absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-text-secondary\" />\n )}\n </div>\n\n {active && !search.isFetching && results.length === 0 && (\n <p className=\"text-xs text-text-secondary\">No users match “{term.trim()}”.</p>\n )}\n\n {results.length > 0 && (\n <ul className=\"max-h-56 divide-y divide-border-seam overflow-auto rounded-lg border border-border-seam bg-bg-surface\">\n {results.map((user) => (\n <li key={user.id}>\n <button\n type=\"button\"\n onClick={() => pick(user)}\n className=\"flex w-full items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-bg-sunken\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <UserIcon className=\"h-4 w-4\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-sm font-medium text-text-primary\">\n {directoryUserName(user)}\n </span>\n <span className=\"block truncate text-xs text-text-secondary\">\n {user.email}\n </span>\n </span>\n <Check className=\"h-4 w-4 shrink-0 text-text-secondary opacity-0\" />\n </button>\n </li>\n ))}\n </ul>\n )}\n\n <button\n type=\"button\"\n onClick={() => setManual(true)}\n className=\"text-xs font-medium text-primary hover:underline\"\n >\n Enter a user ID manually\n </button>\n </div>\n )}\n </FormField>\n );\n}\n"],"mappings":";;;;;;;AA8BA,SAAgB,EAAiB,EAC/B,WACA,gBACA,aACA,cAAW,MACa;CACxB,IAAM,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAQ,KAAa,EAAS,GAAM,EAErC,IAAS,EAAc,EAAK,EAC5B,IAAU,EAAO,QAAQ,EAAE,EAC3B,IAAS,EAAK,MAAM,CAAC,UAAU,GAE/B,KAAQ,MAAwB;AAEpC,EADA,EAAS;GAAE,QAAQ,EAAK;GAAI,aAAa,EAAkB,EAAK;GAAE,CAAC,EACnE,EAAQ,GAAG;IAIP,IAAoB,EAAO;AAkCjC,QAhCI,KAAU,IAEV,kBAAC,GAAD;EACE,OAAM;EACN,SAAQ;EACE;EACV,MACE,IACI,kEACA;YAPR,CAUE,kBAAC,SAAD;GACE,IAAG;GACH,WAAW;GACX,OAAO;GACP,WAAW,MAAM,EAAS;IAAE,QAAQ,EAAE,OAAO;IAAO;IAAa,CAAC;GAClE,aAAY;GACZ,CAAA,EACD,CAAC,KACA,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAU,GAAM;GAC/B,WAAU;aACX;GAEQ,CAAA,CAED;MAKd,kBAAC,GAAD;EAAW,OAAM;EAA2B;YACzC,IACC,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,QAAD;KAAM,WAAU;eACd,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;KAC3B,CAAA;IACP,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,KAAe;MACd,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBAAwC;MAAW,CAAA,CAC5D;;IACN,kBAAC,UAAD;KACE,MAAK;KACL,cAAW;KACX,eAAe,EAAS;MAAE,QAAQ;MAAI,aAAa;MAAI,CAAC;KACxD,WAAU;eAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;KAClB,CAAA;IACL;OAEN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAQ,WAAU,4FAA6F,CAAA;MAC/G,kBAAC,SAAD;OACE,WAAW,EAAG,GAAiB,OAAO;OACtC,OAAO;OACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;OACxC,aAAY;OACZ,cAAW;OACX,CAAA;MACD,EAAO,cACN,kBAAC,GAAD,EAAS,WAAU,sFAAuF,CAAA;MAExG;;IAEL,KAAU,CAAC,EAAO,cAAc,EAAQ,WAAW,KAClD,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA2C;MAAiB,EAAK,MAAM;MAAC;MAAM;;IAG/E,EAAQ,SAAS,KAChB,kBAAC,MAAD;KAAI,WAAU;eACX,EAAQ,KAAK,MACZ,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAK,EAAK;MACzB,WAAU;gBAHZ;OAKE,kBAAC,QAAD;QAAM,WAAU;kBACd,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;QAC3B,CAAA;OACP,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAkB,EAAK;SACnB,CAAA,EACP,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAK;SACD,CAAA,CACF;;OACP,kBAAC,GAAD,EAAO,WAAU,kDAAmD,CAAA;OAC7D;SACN,EAnBI,EAAK,GAmBT,CACL;KACC,CAAA;IAGP,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAU,GAAK;KAC9B,WAAU;eACX;KAEQ,CAAA;IACL;;EAEE,CAAA"}
|