@burdenoff/microfe-atheletebridge 2026.705.1 → 2026.706.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atheletebridge/AthleteBridgeRoot.js +55 -39
- package/dist/atheletebridge/AthleteBridgeRoot.js.map +1 -1
- package/dist/atheletebridge/AthleteBridgeRoutes.js +18 -6
- package/dist/atheletebridge/AthleteBridgeRoutes.js.map +1 -1
- package/dist/atheletebridge/components/Crest.js +70 -0
- package/dist/atheletebridge/components/Crest.js.map +1 -0
- package/dist/atheletebridge/components/FixtureCard.js +8 -72
- package/dist/atheletebridge/components/FixtureCard.js.map +1 -1
- package/dist/atheletebridge/components/MatchupRow.js +74 -0
- package/dist/atheletebridge/components/MatchupRow.js.map +1 -0
- package/dist/atheletebridge/components/MetricsCard.js +61 -21
- package/dist/atheletebridge/components/MetricsCard.js.map +1 -1
- package/dist/atheletebridge/components/ProgramCard.js +75 -43
- package/dist/atheletebridge/components/ProgramCard.js.map +1 -1
- package/dist/atheletebridge/components/Scoreboard.js +163 -0
- package/dist/atheletebridge/components/Scoreboard.js.map +1 -0
- package/dist/atheletebridge/components/SessionCard.js +64 -41
- package/dist/atheletebridge/components/SessionCard.js.map +1 -1
- package/dist/atheletebridge/components/SessionStatusTimeline.js +80 -0
- package/dist/atheletebridge/components/SessionStatusTimeline.js.map +1 -0
- package/dist/atheletebridge/components/StatusBadge.js +5 -5
- package/dist/atheletebridge/components/StatusBadge.js.map +1 -1
- package/dist/atheletebridge/components/TeamCard.js +65 -35
- package/dist/atheletebridge/components/TeamCard.js.map +1 -1
- package/dist/atheletebridge/components/UserAvatar.js +33 -0
- package/dist/atheletebridge/components/UserAvatar.js.map +1 -0
- package/dist/atheletebridge/components/domain.js +5 -2
- package/dist/atheletebridge/components/domain.js.map +1 -1
- package/dist/atheletebridge/components/viz/AvatarStack.js +2 -0
- package/dist/atheletebridge/components/viz/BarChart.js +83 -0
- package/dist/atheletebridge/components/viz/BarChart.js.map +1 -0
- package/dist/atheletebridge/components/viz/DonutChart.js +109 -0
- package/dist/atheletebridge/components/viz/DonutChart.js.map +1 -0
- package/dist/atheletebridge/components/viz/LineChart.js +123 -0
- package/dist/atheletebridge/components/viz/LineChart.js.map +1 -0
- package/dist/atheletebridge/components/viz/ProgressRing.js +6 -0
- package/dist/atheletebridge/components/viz/ProgressRing.js.map +1 -0
- package/dist/atheletebridge/components/viz/RatingStars.js +92 -0
- package/dist/atheletebridge/components/viz/RatingStars.js.map +1 -0
- package/dist/atheletebridge/components/viz/Sparkline.js +74 -0
- package/dist/atheletebridge/components/viz/Sparkline.js.map +1 -0
- package/dist/atheletebridge/components/viz/StatCard.js +60 -0
- package/dist/atheletebridge/components/viz/StatCard.js.map +1 -0
- package/dist/atheletebridge/components/viz/StepProgress.js +3 -0
- package/dist/atheletebridge/components/viz/Timeline.js +66 -0
- package/dist/atheletebridge/components/viz/Timeline.js.map +1 -0
- package/dist/atheletebridge/components/viz/index.js +13 -0
- package/dist/atheletebridge/components/viz/lib/Avatar.js +1 -0
- package/dist/atheletebridge/components/viz/lib/CartesianFrame.js +119 -0
- package/dist/atheletebridge/components/viz/lib/CartesianFrame.js.map +1 -0
- package/dist/atheletebridge/components/viz/lib/ResponsiveSvg.js +35 -0
- package/dist/atheletebridge/components/viz/lib/ResponsiveSvg.js.map +1 -0
- package/dist/atheletebridge/components/viz/lib/format.js +17 -0
- package/dist/atheletebridge/components/viz/lib/format.js.map +1 -0
- package/dist/atheletebridge/components/viz/lib/geometry.js +61 -0
- package/dist/atheletebridge/components/viz/lib/geometry.js.map +1 -0
- package/dist/atheletebridge/components/viz/lib/hooks.js +39 -0
- package/dist/atheletebridge/components/viz/lib/hooks.js.map +1 -0
- package/dist/atheletebridge/components/viz/lib/palette.js +27 -0
- package/dist/atheletebridge/components/viz/lib/palette.js.map +1 -0
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js +369 -163
- package/dist/atheletebridge/hooks/useAthleteBridgeApi.js.map +1 -1
- package/dist/atheletebridge/pages/DashboardPage.js +182 -117
- package/dist/atheletebridge/pages/DashboardPage.js.map +1 -1
- package/dist/atheletebridge/pages/StandingsPage.js +119 -95
- package/dist/atheletebridge/pages/StandingsPage.js.map +1 -1
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js +372 -0
- package/dist/atheletebridge/pages/analytics/CoachAnalyticsPage.js.map +1 -0
- package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js +511 -0
- package/dist/atheletebridge/pages/athletes/AthleteProfilePage.js.map +1 -0
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js +120 -131
- package/dist/atheletebridge/pages/fixtures/FixtureDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js +264 -176
- package/dist/atheletebridge/pages/fixtures/FixtureFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js +84 -47
- package/dist/atheletebridge/pages/fixtures/FixturesListPage.js.map +1 -1
- package/dist/atheletebridge/pages/performance/PerformancePage.js +435 -284
- package/dist/atheletebridge/pages/performance/PerformancePage.js.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js +188 -124
- package/dist/atheletebridge/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js +56 -39
- package/dist/atheletebridge/pages/programs/ProgramsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js +268 -139
- package/dist/atheletebridge/pages/sessions/SessionDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js +254 -162
- package/dist/atheletebridge/pages/sessions/SessionFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js +38 -36
- package/dist/atheletebridge/pages/sessions/SessionsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js +437 -189
- package/dist/atheletebridge/pages/teams/TeamDetailPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamFormPage.js +368 -177
- package/dist/atheletebridge/pages/teams/TeamFormPage.js.map +1 -1
- package/dist/atheletebridge/pages/teams/TeamsListPage.js +3 -0
- package/dist/atheletebridge/pages/teams/TeamsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/tournaments/TournamentBracketPage.js +298 -0
- package/dist/atheletebridge/pages/tournaments/TournamentBracketPage.js.map +1 -0
- package/dist/atheletebridge/utils/identity.js +31 -0
- package/dist/atheletebridge/utils/identity.js.map +1 -0
- package/dist/atheletebridge/utils/timeAgo.js +20 -0
- package/dist/atheletebridge/utils/timeAgo.js.map +1 -0
- package/dist/atheletebridge/utils/url.js +17 -0
- package/dist/atheletebridge/utils/url.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAthleteBridgeApi.js","names":[],"sources":["../../../src/atheletebridge/hooks/useAthleteBridgeApi.ts"],"sourcesContent":["/**\n * AthleteBridge API hooks\n * @module atheletebridge/hooks\n *\n * Thin react-query wrappers over the real `wspace-atheletebridge-svc` federated\n * GraphQL contract, reached through the workspace gateway with the workspace\n * dual-token. Every query/mutation here maps 1:1 to an `ath*` root field in the\n * service's `.hive-schema.graphql`. NO MOCKS — all data is live.\n */\n\nimport { useCallback } from 'react';\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch, clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useAthleteBridge } from '../context';\nimport type {\n AthCompleteSessionInput,\n AthCreateFixtureInput,\n AthCreateProgramInput,\n AthCreateTeamInput,\n AthAddMemberInput,\n AthAddDrillInput,\n AthBookSessionInput,\n AthDrill,\n AthFixture,\n AthFixtureConnection,\n AthFixtureStatus,\n AthLogWorkoutInput,\n AthMember,\n AthMemberStatus,\n AthPerformanceMetric,\n AthProgram,\n AthProgramConnection,\n AthProgramLevel,\n AthProgramStatus,\n AthRecordMetricInput,\n AthRecordResultInput,\n AthSession,\n AthSessionConnection,\n AthSessionStatus,\n AthSport,\n AthStanding,\n AthTeam,\n AthTeamConnection,\n AthTeamStatus,\n AthUpdateProgramInput,\n AthUpdateTeamInput,\n AthWorkoutLog,\n AthWorkoutLogConnection,\n PaginationInput,\n} from '../types';\n\n// ============================================================================\n// Low-level query wrapper\n// ============================================================================\n\nconst athQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n // This MFE bypasses Apollo, so fe-libs' short-lived graphqlFetch read cache\n // would otherwise serve pre-write data to the very next refetch issued by a\n // mutation's onSuccess invalidation. Clear it after any successful mutation so\n // every downstream read is fresh (fleet-wide stale-read-after-write fix).\n if (query.trimStart().startsWith('mutation')) {\n clearGraphqlFetchResponseCache();\n }\n\n return response.data;\n};\n\n// Global-gateway read wrapper — the workspace-scoped MFE reaches the tenant user\n// directory (for real user pickers) through the global gateway.\nconst athGlobalQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'global',\n query,\n variables,\n suppressGlobalErrorEvent: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n return response.data;\n};\n\n// ============================================================================\n// GraphQL fragments (inline strings — exact fields from the svc schema)\n// ============================================================================\n\nconst TEAM_FIELDS = `\n id workspaceId name sport status description ageGroup season homeVenue\n headCoachId tags createdAt updatedAt\n`;\n\nconst MEMBER_FIELDS = `\n id workspaceId teamId userId displayName role status position jerseyNumber\n height weight dominantSide notes joinedAt createdAt updatedAt\n`;\n\nconst FIXTURE_FIELDS = `\n id workspaceId type status competition round venue homeTeamId awayTeamId\n homeScore awayScore scheduledAt kickoffAt notes createdAt updatedAt\n homeTeam { id name sport }\n awayTeam { id name sport }\n`;\n\nconst STANDING_FIELDS = `\n id workspaceId competition teamId played won drawn lost goalsFor goalsAgainst\n points rank createdAt updatedAt\n team { id name sport }\n`;\n\nconst DRILL_FIELDS = `\n id workspaceId programId name description weekNumber orderIndex sets reps\n durationSec restSec createdAt updatedAt\n video { id }\n`;\n\nconst PROGRAM_FIELDS = `\n id workspaceId coachId name sport level status summary weeks isTemplate tags\n createdAt updatedAt\n`;\n\nconst SESSION_FIELDS = `\n id workspaceId coachId athleteId programId title type status location\n meetingUrl scheduledAt durationMin completedAt feedback rating\n createdAt updatedAt\n`;\n\nconst WORKOUT_LOG_FIELDS = `\n id workspaceId athleteId programId title status performedAt durationMin rpe\n totalLoad metrics notes createdAt updatedAt\n`;\n\nconst METRIC_FIELDS = `\n id workspaceId athleteId type label value unit source recordedAt\n createdAt updatedAt\n`;\n\n// ============================================================================\n// Teams\n// ============================================================================\n\nexport interface TeamFilters {\n sport?: AthSport;\n status?: AthTeamStatus;\n pagination?: PaginationInput;\n}\n\nexport const useTeams = (filters: TeamFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'teams', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListTeams: AthTeamConnection }>(\n `query AthListTeams($sport: AthSport, $status: AthTeamStatus, $pagination: PaginationInput) {\n athListTeams(sport: $sport, status: $status, pagination: $pagination) {\n items { ${TEAM_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n sport: filters.sport,\n status: filters.status,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListTeams;\n },\n });\n};\n\nexport const useTeam = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'team', id],\n queryFn: async () => {\n const result = await athQuery<{ athTeam: AthTeam | null }>(\n `query AthTeam($id: ID!) {\n athTeam(id: $id) {\n ${TEAM_FIELDS}\n members { ${MEMBER_FIELDS} }\n fixtures { ${FIXTURE_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athTeam;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useTeamMembers = (teamId: string | undefined, status?: AthMemberStatus) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'members', teamId, status],\n queryFn: async () => {\n const result = await athQuery<{ athListMembers: AthMember[] }>(\n `query AthListMembers($teamId: ID!, $status: AthMemberStatus) {\n athListMembers(teamId: $teamId, status: $status) { ${MEMBER_FIELDS} }\n }`,\n { teamId, status },\n apiGatewayUrl\n );\n return result.athListMembers;\n },\n enabled: Boolean(teamId),\n });\n};\n\nexport const useCreateTeam = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthCreateTeamInput) => {\n const result = await athQuery<{ athCreateTeam: AthTeam }>(\n `mutation AthCreateTeam($input: AthCreateTeamInput!) {\n athCreateTeam(input: $input) { ${TEAM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athCreateTeam;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'teams'] });\n },\n });\n};\n\nexport const useUpdateTeam = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthUpdateTeamInput }) => {\n const result = await athQuery<{ athUpdateTeam: AthTeam }>(\n `mutation AthUpdateTeam($id: ID!, $input: AthUpdateTeamInput!) {\n athUpdateTeam(id: $id, input: $input) { ${TEAM_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athUpdateTeam;\n },\n onSuccess: (team) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'teams'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', team.id] });\n },\n });\n};\n\nexport const useArchiveTeam = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athArchiveTeam: boolean }>(\n `mutation AthArchiveTeam($id: ID!) { athArchiveTeam(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athArchiveTeam;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'teams'] });\n },\n });\n};\n\nexport const useAddMember = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthAddMemberInput) => {\n const result = await athQuery<{ athAddMember: AthMember }>(\n `mutation AthAddMember($input: AthAddMemberInput!) {\n athAddMember(input: $input) { ${MEMBER_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athAddMember;\n },\n onSuccess: (member) => {\n void queryClient.invalidateQueries({\n queryKey: ['atheletebridge', 'members', member.teamId],\n });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', member.teamId] });\n },\n });\n};\n\nexport const useRemoveMember = (teamId?: string) => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athRemoveMember: boolean }>(\n `mutation AthRemoveMember($id: ID!) { athRemoveMember(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athRemoveMember;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'members', teamId] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', teamId] });\n },\n });\n};\n\n// ============================================================================\n// Fixtures & standings\n// ============================================================================\n\nexport interface FixtureFilters {\n status?: AthFixtureStatus;\n teamId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useFixtures = (filters: FixtureFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'fixtures', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListFixtures: AthFixtureConnection }>(\n `query AthListFixtures($status: AthFixtureStatus, $teamId: ID, $pagination: PaginationInput) {\n athListFixtures(status: $status, teamId: $teamId, pagination: $pagination) {\n items { ${FIXTURE_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n teamId: filters.teamId,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListFixtures;\n },\n });\n};\n\nexport const useFixture = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'fixture', id],\n queryFn: async () => {\n const result = await athQuery<{ athFixture: AthFixture | null }>(\n `query AthFixture($id: ID!) {\n athFixture(id: $id) { ${FIXTURE_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athFixture;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useStandings = (competition: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'standings', competition],\n queryFn: async () => {\n const result = await athQuery<{ athStandings: AthStanding[] }>(\n `query AthStandings($competition: String!) {\n athStandings(competition: $competition) { ${STANDING_FIELDS} }\n }`,\n { competition },\n apiGatewayUrl\n );\n return result.athStandings;\n },\n enabled: Boolean(competition),\n });\n};\n\nexport const useScheduleFixture = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthCreateFixtureInput) => {\n const result = await athQuery<{ athScheduleFixture: AthFixture }>(\n `mutation AthScheduleFixture($input: AthCreateFixtureInput!) {\n athScheduleFixture(input: $input) { ${FIXTURE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athScheduleFixture;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'fixtures'] });\n },\n });\n};\n\nexport const useRecordFixtureResult = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthRecordResultInput }) => {\n const result = await athQuery<{ athRecordFixtureResult: AthFixture }>(\n `mutation AthRecordFixtureResult($id: ID!, $input: AthRecordResultInput!) {\n athRecordFixtureResult(id: $id, input: $input) { ${FIXTURE_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athRecordFixtureResult;\n },\n onSuccess: (fixture) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'fixtures'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'fixture', fixture.id] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'standings'] });\n },\n });\n};\n\n// ============================================================================\n// Programs & drills\n// ============================================================================\n\nexport interface ProgramFilters {\n sport?: AthSport;\n level?: AthProgramLevel;\n status?: AthProgramStatus;\n pagination?: PaginationInput;\n}\n\nexport const usePrograms = (filters: ProgramFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'programs', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListPrograms: AthProgramConnection }>(\n `query AthListPrograms($sport: AthSport, $level: AthProgramLevel, $status: AthProgramStatus, $pagination: PaginationInput) {\n athListPrograms(sport: $sport, level: $level, status: $status, pagination: $pagination) {\n items { ${PROGRAM_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n sport: filters.sport,\n level: filters.level,\n status: filters.status,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListPrograms;\n },\n });\n};\n\nexport const useProgram = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'program', id],\n queryFn: async () => {\n const result = await athQuery<{ athProgram: AthProgram | null }>(\n `query AthProgram($id: ID!) {\n athProgram(id: $id) {\n ${PROGRAM_FIELDS}\n drills { ${DRILL_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athProgram;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthCreateProgramInput) => {\n const result = await athQuery<{ athCreateProgram: AthProgram }>(\n `mutation AthCreateProgram($input: AthCreateProgramInput!) {\n athCreateProgram(input: $input) { ${PROGRAM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athCreateProgram;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n },\n });\n};\n\nexport const useUpdateProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthUpdateProgramInput }) => {\n const result = await athQuery<{ athUpdateProgram: AthProgram }>(\n `mutation AthUpdateProgram($id: ID!, $input: AthUpdateProgramInput!) {\n athUpdateProgram(id: $id, input: $input) { ${PROGRAM_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athUpdateProgram;\n },\n onSuccess: (program) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'program', program.id] });\n },\n });\n};\n\nexport const useTransitionProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: AthProgramStatus }) => {\n const result = await athQuery<{ athTransitionProgram: AthProgram }>(\n `mutation AthTransitionProgram($id: ID!, $status: AthProgramStatus!) {\n athTransitionProgram(id: $id, status: $status) { ${PROGRAM_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.athTransitionProgram;\n },\n onSuccess: (program) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'program', program.id] });\n },\n });\n};\n\nexport const useArchiveProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athArchiveProgram: boolean }>(\n `mutation AthArchiveProgram($id: ID!) { athArchiveProgram(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athArchiveProgram;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n },\n });\n};\n\nexport const useAddDrill = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthAddDrillInput) => {\n const result = await athQuery<{ athAddDrill: AthDrill }>(\n `mutation AthAddDrill($input: AthAddDrillInput!) {\n athAddDrill(input: $input) { ${DRILL_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athAddDrill;\n },\n onSuccess: (drill) => {\n void queryClient.invalidateQueries({\n queryKey: ['atheletebridge', 'program', drill.programId],\n });\n },\n });\n};\n\nexport const useRemoveDrill = (programId?: string) => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athRemoveDrill: boolean }>(\n `mutation AthRemoveDrill($id: ID!) { athRemoveDrill(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athRemoveDrill;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'program', programId] });\n },\n });\n};\n\n// ============================================================================\n// Coaching sessions\n// ============================================================================\n\nexport interface SessionFilters {\n athleteId?: string;\n coachId?: string;\n status?: AthSessionStatus;\n pagination?: PaginationInput;\n}\n\nexport const useSessions = (filters: SessionFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'sessions', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListSessions: AthSessionConnection }>(\n `query AthListSessions($athleteId: ID, $coachId: ID, $status: AthSessionStatus, $pagination: PaginationInput) {\n athListSessions(athleteId: $athleteId, coachId: $coachId, status: $status, pagination: $pagination) {\n items { ${SESSION_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n athleteId: filters.athleteId,\n coachId: filters.coachId,\n status: filters.status,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListSessions;\n },\n });\n};\n\nexport const useSession = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'session', id],\n queryFn: async () => {\n const result = await athQuery<{ athSession: AthSession | null }>(\n `query AthSession($id: ID!) {\n athSession(id: $id) { ${SESSION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athSession;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useBookSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthBookSessionInput) => {\n const result = await athQuery<{ athBookSession: AthSession }>(\n `mutation AthBookSession($input: AthBookSessionInput!) {\n athBookSession(input: $input) { ${SESSION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athBookSession;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n },\n });\n};\n\nexport const useConfirmSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athConfirmSession: AthSession }>(\n `mutation AthConfirmSession($id: ID!) {\n athConfirmSession(id: $id) { ${SESSION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athConfirmSession;\n },\n onSuccess: (session) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'session', session.id] });\n },\n });\n};\n\nexport const useCompleteSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input?: AthCompleteSessionInput }) => {\n const result = await athQuery<{ athCompleteSession: AthSession }>(\n `mutation AthCompleteSession($id: ID!, $input: AthCompleteSessionInput) {\n athCompleteSession(id: $id, input: $input) { ${SESSION_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athCompleteSession;\n },\n onSuccess: (session) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'session', session.id] });\n },\n });\n};\n\nexport const useCancelSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athCancelSession: AthSession }>(\n `mutation AthCancelSession($id: ID!) {\n athCancelSession(id: $id) { ${SESSION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athCancelSession;\n },\n onSuccess: (session) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'session', session.id] });\n },\n });\n};\n\n// ============================================================================\n// Workout logs & performance metrics\n// ============================================================================\n\nexport interface WorkoutLogFilters {\n athleteId?: string;\n programId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useWorkoutLogs = (filters: WorkoutLogFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'workout-logs', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListWorkoutLogs: AthWorkoutLogConnection }>(\n `query AthListWorkoutLogs($athleteId: ID, $programId: ID, $pagination: PaginationInput) {\n athListWorkoutLogs(athleteId: $athleteId, programId: $programId, pagination: $pagination) {\n items { ${WORKOUT_LOG_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n athleteId: filters.athleteId,\n programId: filters.programId,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListWorkoutLogs;\n },\n });\n};\n\nexport const usePerformanceMetrics = (athleteId: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'metrics', athleteId],\n queryFn: async () => {\n const result = await athQuery<{ athPerformanceMetrics: AthPerformanceMetric[] }>(\n `query AthPerformanceMetrics($athleteId: ID!) {\n athPerformanceMetrics(athleteId: $athleteId) { ${METRIC_FIELDS} }\n }`,\n { athleteId },\n apiGatewayUrl\n );\n return result.athPerformanceMetrics;\n },\n enabled: Boolean(athleteId),\n });\n};\n\nexport const useLogWorkout = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthLogWorkoutInput) => {\n const result = await athQuery<{ athLogWorkout: AthWorkoutLog }>(\n `mutation AthLogWorkout($input: AthLogWorkoutInput!) {\n athLogWorkout(input: $input) { ${WORKOUT_LOG_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athLogWorkout;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'workout-logs'] });\n },\n });\n};\n\nexport const useRecordMetric = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthRecordMetricInput) => {\n const result = await athQuery<{ athRecordMetric: AthPerformanceMetric }>(\n `mutation AthRecordMetric($input: AthRecordMetricInput!) {\n athRecordMetric(input: $input) { ${METRIC_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athRecordMetric;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'metrics'] });\n },\n });\n};\n\n// ============================================================================\n// Workspace files — drill demo videos live in wspace-files-svc, reached through\n// the same workspace gateway. Blobs never touch the atheletebridge subgraph.\n// ============================================================================\n\nconst fileExtensionOf = (fileName: string): string => {\n const dot = fileName.lastIndexOf('.');\n return dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : 'bin';\n};\n\n/**\n * Upload a file to wspace-files-svc via the standard three-step flow\n * (initiateUpload → presigned PUT → completeUpload) and resolve the new file id,\n * ready to pass as `AthAddDrillInput.videoFileId`.\n */\nexport const useUploadFile = () => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useCallback(\n async (file: File): Promise<string> => {\n const initiated = await athQuery<{\n initiateUpload: { uploadUrl: string | null; session: { id: string } };\n }>(\n `mutation AthInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) { uploadUrl session { id } }\n }`,\n {\n input: {\n fileName: file.name,\n extension: fileExtensionOf(file.name),\n fileSize: file.size,\n mimeType: file.type || 'application/octet-stream',\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n\n const uploadUrl = initiated.initiateUpload.uploadUrl;\n const sessionId = initiated.initiateUpload.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('Upload could not be initialised.');\n }\n\n const put = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!put.ok) {\n throw new Error(`Storage rejected the upload (HTTP ${put.status}).`);\n }\n\n const completed = await athQuery<{ completeUpload: { fileId: string } }>(\n `mutation AthCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) { fileId }\n }`,\n { input: { sessionId } },\n apiGatewayUrl\n );\n return completed.completeUpload.fileId;\n },\n [apiGatewayUrl]\n );\n};\n\nexport interface WorkspaceFileRef {\n id: string;\n name: string;\n downloadUrl: string | null;\n}\n\n/**\n * Resolve a wspace-files File (a drill's demo video) by id, on demand. Kept as a\n * separate lazy read so a missing/expired blob can never break the program page.\n */\nexport const useFile = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'file', id],\n queryFn: async () => {\n const result = await athQuery<{ getFile: WorkspaceFileRef | null }>(\n `query AthGetFile($id: ID!) {\n getFile(id: $id) { id name downloadUrl }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.getFile;\n },\n enabled: Boolean(id),\n staleTime: 60_000,\n });\n};\n\n// ============================================================================\n// User directory (global gateway) — resolve real users for team member add so a\n// coach never has to paste a raw UUID.\n// ============================================================================\n\nexport interface DirectoryUser {\n id: string;\n name: string | null;\n firstName: string | null;\n lastName: string | null;\n email: string;\n username: string;\n}\n\n/** Best display name for a directory user (name → first+last → username → email). */\nexport const directoryUserName = (user: DirectoryUser): string => {\n if (user.name?.trim()) return user.name.trim();\n const full = [user.firstName, user.lastName].filter(Boolean).join(' ').trim();\n if (full) return full;\n if (user.username?.trim()) return user.username.trim();\n return user.email;\n};\n\n/**\n * Search the tenant user directory by name/email. Enabled once the term is at\n * least two characters; failures are surfaced to the caller (which offers a\n * manual-id fallback) rather than as a global error toast.\n */\nexport const useUserSearch = (search: string) => {\n const term = search.trim();\n return useQuery({\n queryKey: ['atheletebridge', 'user-search', term],\n queryFn: async () => {\n const result = await athGlobalQuery<{\n users: { users: DirectoryUser[]; hasMore: boolean };\n }>(\n `query AthUserSearch($filter: UserFilterInput, $pagination: PaginationInput) {\n users(filter: $filter, pagination: $pagination) {\n users { id name firstName lastName email username }\n hasMore\n }\n }`,\n { filter: { search: term }, pagination: { limit: 8, offset: 0 } }\n );\n return result.users.users;\n },\n enabled: term.length >= 2,\n staleTime: 30_000,\n retry: false,\n });\n};\n\n// ============================================================================\n// Health probe\n// ============================================================================\n\nexport const useHealth = () => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'health'],\n queryFn: async () => {\n const result = await athQuery<{ athHealth: string }>(\n `query AthHealth { athHealth }`,\n {},\n apiGatewayUrl\n );\n return result.athHealth;\n },\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAthleteBridgeApi.js","names":[],"sources":["../../../src/atheletebridge/hooks/useAthleteBridgeApi.ts"],"sourcesContent":["/**\n * AthleteBridge API hooks\n * @module atheletebridge/hooks\n *\n * Thin react-query wrappers over the real `wspace-atheletebridge-svc` federated\n * GraphQL contract, reached through the workspace gateway with the workspace\n * dual-token. Every query/mutation here maps 1:1 to an `ath*` root field in the\n * service's `.hive-schema.graphql`. NO MOCKS — all data is live.\n */\n\nimport { useCallback, useMemo } from 'react';\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch, clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useAthleteBridge } from '../context';\nimport type {\n AthCompleteSessionInput,\n AthCreateFixtureInput,\n AthCreateProgramInput,\n AthCreateTeamInput,\n AthAddMemberInput,\n AthAddDrillInput,\n AthBookSessionInput,\n AthDrill,\n AthFixture,\n AthFixtureConnection,\n AthFixtureStatus,\n AthLogWorkoutInput,\n AthMember,\n AthMemberStatus,\n AthPerformanceMetric,\n AthProgram,\n AthProgramConnection,\n AthProgramLevel,\n AthProgramStatus,\n AthRecordMetricInput,\n AthRecordResultInput,\n AthSession,\n AthSessionConnection,\n AthSessionStatus,\n AthSport,\n AthStanding,\n AthTeam,\n AthTeamConnection,\n AthTeamStatus,\n AthUpdateProgramInput,\n AthUpdateTeamInput,\n AthWorkoutLog,\n AthWorkoutLogConnection,\n PaginationInput,\n} from '../types';\n\n// ============================================================================\n// Low-level query wrapper\n// ============================================================================\n\nconst athQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n // This MFE bypasses Apollo, so fe-libs' short-lived graphqlFetch read cache\n // would otherwise serve pre-write data to the very next refetch issued by a\n // mutation's onSuccess invalidation. Clear it after any successful mutation so\n // every downstream read is fresh (fleet-wide stale-read-after-write fix).\n if (query.trimStart().startsWith('mutation')) {\n clearGraphqlFetchResponseCache();\n }\n\n return response.data;\n};\n\n// Global-gateway read wrapper — the workspace-scoped MFE reaches the tenant user\n// directory (for real user pickers) through the global gateway.\nconst athGlobalQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'global',\n query,\n variables,\n suppressGlobalErrorEvent: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n\n return response.data;\n};\n\n// ============================================================================\n// GraphQL fragments (inline strings — exact fields from the svc schema)\n// ============================================================================\n\nconst TEAM_FIELDS = `\n id workspaceId name sport status description ageGroup season homeVenue\n headCoachId tags createdAt updatedAt\n`;\n\nconst MEMBER_FIELDS = `\n id workspaceId teamId userId displayName role status position jerseyNumber\n height weight dominantSide notes joinedAt createdAt updatedAt\n`;\n\nconst FIXTURE_FIELDS = `\n id workspaceId type status competition round venue homeTeamId awayTeamId\n homeScore awayScore scheduledAt kickoffAt notes createdAt updatedAt\n homeTeam { id name sport }\n awayTeam { id name sport }\n`;\n\nconst STANDING_FIELDS = `\n id workspaceId competition teamId played won drawn lost goalsFor goalsAgainst\n points rank createdAt updatedAt\n team { id name sport }\n`;\n\nconst DRILL_FIELDS = `\n id workspaceId programId name description weekNumber orderIndex sets reps\n durationSec restSec createdAt updatedAt\n video { id }\n`;\n\nconst PROGRAM_FIELDS = `\n id workspaceId coachId name sport level status summary weeks isTemplate tags\n createdAt updatedAt\n`;\n\nconst SESSION_FIELDS = `\n id workspaceId coachId athleteId programId title type status location\n meetingUrl scheduledAt durationMin completedAt feedback rating\n createdAt updatedAt\n`;\n\nconst WORKOUT_LOG_FIELDS = `\n id workspaceId athleteId programId title status performedAt durationMin rpe\n totalLoad metrics notes createdAt updatedAt\n`;\n\nconst METRIC_FIELDS = `\n id workspaceId athleteId type label value unit source recordedAt\n createdAt updatedAt\n`;\n\n// ============================================================================\n// Teams\n// ============================================================================\n\nexport interface TeamFilters {\n sport?: AthSport;\n status?: AthTeamStatus;\n pagination?: PaginationInput;\n}\n\nexport const useTeams = (filters: TeamFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'teams', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListTeams: AthTeamConnection }>(\n `query AthListTeams($sport: AthSport, $status: AthTeamStatus, $pagination: PaginationInput) {\n athListTeams(sport: $sport, status: $status, pagination: $pagination) {\n items { ${TEAM_FIELDS} members { id } }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n sport: filters.sport,\n status: filters.status,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListTeams;\n },\n });\n};\n\nexport const useTeam = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'team', id],\n queryFn: async () => {\n const result = await athQuery<{ athTeam: AthTeam | null }>(\n `query AthTeam($id: ID!) {\n athTeam(id: $id) {\n ${TEAM_FIELDS}\n members { ${MEMBER_FIELDS} }\n fixtures { ${FIXTURE_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athTeam;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useTeamMembers = (teamId: string | undefined, status?: AthMemberStatus) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'members', teamId, status],\n queryFn: async () => {\n const result = await athQuery<{ athListMembers: AthMember[] }>(\n `query AthListMembers($teamId: ID!, $status: AthMemberStatus) {\n athListMembers(teamId: $teamId, status: $status) { ${MEMBER_FIELDS} }\n }`,\n { teamId, status },\n apiGatewayUrl\n );\n return result.athListMembers;\n },\n enabled: Boolean(teamId),\n });\n};\n\nexport const useCreateTeam = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthCreateTeamInput) => {\n const result = await athQuery<{ athCreateTeam: AthTeam }>(\n `mutation AthCreateTeam($input: AthCreateTeamInput!) {\n athCreateTeam(input: $input) { ${TEAM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athCreateTeam;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'teams'] });\n },\n });\n};\n\nexport const useUpdateTeam = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthUpdateTeamInput }) => {\n const result = await athQuery<{ athUpdateTeam: AthTeam }>(\n `mutation AthUpdateTeam($id: ID!, $input: AthUpdateTeamInput!) {\n athUpdateTeam(id: $id, input: $input) { ${TEAM_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athUpdateTeam;\n },\n onSuccess: (team) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'teams'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', team.id] });\n },\n });\n};\n\nexport const useArchiveTeam = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athArchiveTeam: boolean }>(\n `mutation AthArchiveTeam($id: ID!) { athArchiveTeam(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athArchiveTeam;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'teams'] });\n },\n });\n};\n\nexport const useAddMember = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthAddMemberInput) => {\n const result = await athQuery<{ athAddMember: AthMember }>(\n `mutation AthAddMember($input: AthAddMemberInput!) {\n athAddMember(input: $input) { ${MEMBER_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athAddMember;\n },\n onSuccess: (member) => {\n void queryClient.invalidateQueries({\n queryKey: ['atheletebridge', 'members', member.teamId],\n });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', member.teamId] });\n },\n });\n};\n\nexport const useRemoveMember = (teamId?: string) => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athRemoveMember: boolean }>(\n `mutation AthRemoveMember($id: ID!) { athRemoveMember(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athRemoveMember;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'members', teamId] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'team', teamId] });\n },\n });\n};\n\n// ============================================================================\n// Fixtures & standings\n// ============================================================================\n\nexport interface FixtureFilters {\n status?: AthFixtureStatus;\n teamId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useFixtures = (filters: FixtureFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'fixtures', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListFixtures: AthFixtureConnection }>(\n `query AthListFixtures($status: AthFixtureStatus, $teamId: ID, $pagination: PaginationInput) {\n athListFixtures(status: $status, teamId: $teamId, pagination: $pagination) {\n items { ${FIXTURE_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n teamId: filters.teamId,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListFixtures;\n },\n });\n};\n\nexport const useFixture = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'fixture', id],\n queryFn: async () => {\n const result = await athQuery<{ athFixture: AthFixture | null }>(\n `query AthFixture($id: ID!) {\n athFixture(id: $id) { ${FIXTURE_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athFixture;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useStandings = (competition: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'standings', competition],\n queryFn: async () => {\n const result = await athQuery<{ athStandings: AthStanding[] }>(\n `query AthStandings($competition: String!) {\n athStandings(competition: $competition) { ${STANDING_FIELDS} }\n }`,\n { competition },\n apiGatewayUrl\n );\n return result.athStandings;\n },\n enabled: Boolean(competition),\n });\n};\n\nexport const useScheduleFixture = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthCreateFixtureInput) => {\n const result = await athQuery<{ athScheduleFixture: AthFixture }>(\n `mutation AthScheduleFixture($input: AthCreateFixtureInput!) {\n athScheduleFixture(input: $input) { ${FIXTURE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athScheduleFixture;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'fixtures'] });\n },\n });\n};\n\nexport const useRecordFixtureResult = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthRecordResultInput }) => {\n const result = await athQuery<{ athRecordFixtureResult: AthFixture }>(\n `mutation AthRecordFixtureResult($id: ID!, $input: AthRecordResultInput!) {\n athRecordFixtureResult(id: $id, input: $input) { ${FIXTURE_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athRecordFixtureResult;\n },\n onSuccess: (fixture) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'fixtures'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'fixture', fixture.id] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'standings'] });\n },\n });\n};\n\n// ============================================================================\n// Programs & drills\n// ============================================================================\n\nexport interface ProgramFilters {\n sport?: AthSport;\n level?: AthProgramLevel;\n status?: AthProgramStatus;\n pagination?: PaginationInput;\n}\n\nexport const usePrograms = (filters: ProgramFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'programs', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListPrograms: AthProgramConnection }>(\n `query AthListPrograms($sport: AthSport, $level: AthProgramLevel, $status: AthProgramStatus, $pagination: PaginationInput) {\n athListPrograms(sport: $sport, level: $level, status: $status, pagination: $pagination) {\n items { ${PROGRAM_FIELDS} drills { id } }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n sport: filters.sport,\n level: filters.level,\n status: filters.status,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListPrograms;\n },\n });\n};\n\nexport const useProgram = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'program', id],\n queryFn: async () => {\n const result = await athQuery<{ athProgram: AthProgram | null }>(\n `query AthProgram($id: ID!) {\n athProgram(id: $id) {\n ${PROGRAM_FIELDS}\n drills { ${DRILL_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athProgram;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthCreateProgramInput) => {\n const result = await athQuery<{ athCreateProgram: AthProgram }>(\n `mutation AthCreateProgram($input: AthCreateProgramInput!) {\n athCreateProgram(input: $input) { ${PROGRAM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athCreateProgram;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n },\n });\n};\n\nexport const useUpdateProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: AthUpdateProgramInput }) => {\n const result = await athQuery<{ athUpdateProgram: AthProgram }>(\n `mutation AthUpdateProgram($id: ID!, $input: AthUpdateProgramInput!) {\n athUpdateProgram(id: $id, input: $input) { ${PROGRAM_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athUpdateProgram;\n },\n onSuccess: (program) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'program', program.id] });\n },\n });\n};\n\nexport const useTransitionProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: AthProgramStatus }) => {\n const result = await athQuery<{ athTransitionProgram: AthProgram }>(\n `mutation AthTransitionProgram($id: ID!, $status: AthProgramStatus!) {\n athTransitionProgram(id: $id, status: $status) { ${PROGRAM_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return result.athTransitionProgram;\n },\n onSuccess: (program) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'program', program.id] });\n },\n });\n};\n\nexport const useArchiveProgram = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athArchiveProgram: boolean }>(\n `mutation AthArchiveProgram($id: ID!) { athArchiveProgram(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athArchiveProgram;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'programs'] });\n },\n });\n};\n\nexport const useAddDrill = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthAddDrillInput) => {\n const result = await athQuery<{ athAddDrill: AthDrill }>(\n `mutation AthAddDrill($input: AthAddDrillInput!) {\n athAddDrill(input: $input) { ${DRILL_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athAddDrill;\n },\n onSuccess: (drill) => {\n void queryClient.invalidateQueries({\n queryKey: ['atheletebridge', 'program', drill.programId],\n });\n },\n });\n};\n\nexport const useRemoveDrill = (programId?: string) => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athRemoveDrill: boolean }>(\n `mutation AthRemoveDrill($id: ID!) { athRemoveDrill(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.athRemoveDrill;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'program', programId] });\n },\n });\n};\n\n// ============================================================================\n// Coaching sessions\n// ============================================================================\n\nexport interface SessionFilters {\n athleteId?: string;\n coachId?: string;\n status?: AthSessionStatus;\n pagination?: PaginationInput;\n}\n\nexport const useSessions = (filters: SessionFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'sessions', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListSessions: AthSessionConnection }>(\n `query AthListSessions($athleteId: ID, $coachId: ID, $status: AthSessionStatus, $pagination: PaginationInput) {\n athListSessions(athleteId: $athleteId, coachId: $coachId, status: $status, pagination: $pagination) {\n items { ${SESSION_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n athleteId: filters.athleteId,\n coachId: filters.coachId,\n status: filters.status,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListSessions;\n },\n });\n};\n\nexport const useSession = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'session', id],\n queryFn: async () => {\n const result = await athQuery<{ athSession: AthSession | null }>(\n `query AthSession($id: ID!) {\n athSession(id: $id) { ${SESSION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athSession;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useBookSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthBookSessionInput) => {\n const result = await athQuery<{ athBookSession: AthSession }>(\n `mutation AthBookSession($input: AthBookSessionInput!) {\n athBookSession(input: $input) { ${SESSION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athBookSession;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n },\n });\n};\n\nexport const useConfirmSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athConfirmSession: AthSession }>(\n `mutation AthConfirmSession($id: ID!) {\n athConfirmSession(id: $id) { ${SESSION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athConfirmSession;\n },\n onSuccess: (session) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'session', session.id] });\n },\n });\n};\n\nexport const useCompleteSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input?: AthCompleteSessionInput }) => {\n const result = await athQuery<{ athCompleteSession: AthSession }>(\n `mutation AthCompleteSession($id: ID!, $input: AthCompleteSessionInput) {\n athCompleteSession(id: $id, input: $input) { ${SESSION_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return result.athCompleteSession;\n },\n onSuccess: (session) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'session', session.id] });\n },\n });\n};\n\nexport const useCancelSession = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await athQuery<{ athCancelSession: AthSession }>(\n `mutation AthCancelSession($id: ID!) {\n athCancelSession(id: $id) { ${SESSION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.athCancelSession;\n },\n onSuccess: (session) => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'sessions'] });\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'session', session.id] });\n },\n });\n};\n\n// ============================================================================\n// Workout logs & performance metrics\n// ============================================================================\n\nexport interface WorkoutLogFilters {\n athleteId?: string;\n programId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useWorkoutLogs = (filters: WorkoutLogFilters = {}) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'workout-logs', filters],\n queryFn: async () => {\n const result = await athQuery<{ athListWorkoutLogs: AthWorkoutLogConnection }>(\n `query AthListWorkoutLogs($athleteId: ID, $programId: ID, $pagination: PaginationInput) {\n athListWorkoutLogs(athleteId: $athleteId, programId: $programId, pagination: $pagination) {\n items { ${WORKOUT_LOG_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n athleteId: filters.athleteId,\n programId: filters.programId,\n pagination: filters.pagination,\n },\n apiGatewayUrl\n );\n return result.athListWorkoutLogs;\n },\n });\n};\n\nexport const usePerformanceMetrics = (athleteId: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'metrics', athleteId],\n queryFn: async () => {\n const result = await athQuery<{ athPerformanceMetrics: AthPerformanceMetric[] }>(\n `query AthPerformanceMetrics($athleteId: ID!) {\n athPerformanceMetrics(athleteId: $athleteId) { ${METRIC_FIELDS} }\n }`,\n { athleteId },\n apiGatewayUrl\n );\n return result.athPerformanceMetrics;\n },\n enabled: Boolean(athleteId),\n });\n};\n\nexport const useLogWorkout = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthLogWorkoutInput) => {\n const result = await athQuery<{ athLogWorkout: AthWorkoutLog }>(\n `mutation AthLogWorkout($input: AthLogWorkoutInput!) {\n athLogWorkout(input: $input) { ${WORKOUT_LOG_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athLogWorkout;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'workout-logs'] });\n },\n });\n};\n\nexport const useRecordMetric = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useAthleteBridge();\n return useMutation({\n mutationFn: async (input: AthRecordMetricInput) => {\n const result = await athQuery<{ athRecordMetric: AthPerformanceMetric }>(\n `mutation AthRecordMetric($input: AthRecordMetricInput!) {\n athRecordMetric(input: $input) { ${METRIC_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.athRecordMetric;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: ['atheletebridge', 'metrics'] });\n },\n });\n};\n\n// ============================================================================\n// Workspace files — drill demo videos live in wspace-files-svc, reached through\n// the same workspace gateway. Blobs never touch the atheletebridge subgraph.\n// ============================================================================\n\nconst fileExtensionOf = (fileName: string): string => {\n const dot = fileName.lastIndexOf('.');\n return dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : 'bin';\n};\n\n/**\n * Upload a file to wspace-files-svc via the standard three-step flow\n * (initiateUpload → presigned PUT → completeUpload) and resolve the new file id,\n * ready to pass as `AthAddDrillInput.videoFileId`.\n */\nexport const useUploadFile = () => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useCallback(\n async (file: File): Promise<string> => {\n const initiated = await athQuery<{\n initiateUpload: { uploadUrl: string | null; session: { id: string } };\n }>(\n `mutation AthInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) { uploadUrl session { id } }\n }`,\n {\n input: {\n fileName: file.name,\n extension: fileExtensionOf(file.name),\n fileSize: file.size,\n mimeType: file.type || 'application/octet-stream',\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n\n const uploadUrl = initiated.initiateUpload.uploadUrl;\n const sessionId = initiated.initiateUpload.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('Upload could not be initialised.');\n }\n\n const put = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!put.ok) {\n throw new Error(`Storage rejected the upload (HTTP ${put.status}).`);\n }\n\n const completed = await athQuery<{ completeUpload: { fileId: string } }>(\n `mutation AthCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) { fileId }\n }`,\n { input: { sessionId } },\n apiGatewayUrl\n );\n return completed.completeUpload.fileId;\n },\n [apiGatewayUrl]\n );\n};\n\nexport interface WorkspaceFileRef {\n id: string;\n name: string;\n downloadUrl: string | null;\n}\n\n/**\n * Resolve a wspace-files File (a drill's demo video) by id, on demand. Kept as a\n * separate lazy read so a missing/expired blob can never break the program page.\n */\nexport const useFile = (id: string | undefined) => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'file', id],\n queryFn: async () => {\n const result = await athQuery<{ getFile: WorkspaceFileRef | null }>(\n `query AthGetFile($id: ID!) {\n getFile(id: $id) { id name downloadUrl }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.getFile;\n },\n enabled: Boolean(id),\n staleTime: 60_000,\n });\n};\n\n// ============================================================================\n// User directory (global gateway) — resolve real users for team member add so a\n// coach never has to paste a raw UUID.\n// ============================================================================\n\nexport interface DirectoryUser {\n id: string;\n name: string | null;\n firstName: string | null;\n lastName: string | null;\n email: string;\n username: string;\n}\n\n/** Best display name for a directory user (name → first+last → username → email). */\nexport const directoryUserName = (user: DirectoryUser): string => {\n if (user.name?.trim()) return user.name.trim();\n const full = [user.firstName, user.lastName].filter(Boolean).join(' ').trim();\n if (full) return full;\n if (user.username?.trim()) return user.username.trim();\n return user.email;\n};\n\n/**\n * Search the tenant user directory by name/email. Enabled once the term is at\n * least two characters; failures are surfaced to the caller (which offers a\n * manual-id fallback) rather than as a global error toast.\n */\nexport const useUserSearch = (search: string) => {\n const term = search.trim();\n return useQuery({\n queryKey: ['atheletebridge', 'user-search', term],\n queryFn: async () => {\n const result = await athGlobalQuery<{\n users: { users: DirectoryUser[]; hasMore: boolean };\n }>(\n `query AthUserSearch($filter: UserFilterInput, $pagination: PaginationInput) {\n users(filter: $filter, pagination: $pagination) {\n users { id name firstName lastName email username }\n hasMore\n }\n }`,\n { filter: { search: term }, pagination: { limit: 8, offset: 0 } }\n );\n return result.users.users;\n },\n enabled: term.length >= 2,\n staleTime: 30_000,\n retry: false,\n });\n};\n\n// ============================================================================\n// User id resolution (global gateway) — resolve a set of user ids to real\n// names/emails for identity chips (coach/athlete), mirroring the UserSearchSelect\n// directory pattern. Batched into one aliased request, cached, and RBAC-safe:\n// on any error it degrades to an empty map so callers fall back to id-seeded\n// monograms rather than surfacing an error toast.\n// ============================================================================\n\nexport interface ResolvedUser {\n id: string;\n name: string;\n email: string | null;\n}\n\n/**\n * Resolve the given user ids to `{ id, name, email }` via the global `user(id)`\n * query (one aliased round-trip). Unknown / forbidden ids are simply absent from\n * the returned map.\n */\nexport const useUsersByIds = (ids: ReadonlyArray<string | null | undefined>) => {\n const unique = useMemo(\n () =>\n Array.from(new Set(ids.filter((value): value is string => Boolean(value && value.trim())))).sort(),\n [ids]\n );\n\n return useQuery({\n queryKey: ['atheletebridge', 'users-by-id', unique],\n queryFn: async (): Promise<Record<string, ResolvedUser>> => {\n if (unique.length === 0) {\n return {};\n }\n const varDefs = unique.map((_, i) => `$id${i}: ID!`).join(', ');\n const selections = unique\n .map((_, i) => `u${i}: user(id: $id${i}) { id name firstName lastName email username }`)\n .join('\\n');\n const variables: Record<string, string> = {};\n unique.forEach((id, i) => {\n variables[`id${i}`] = id;\n });\n\n // Read tolerantly: don't throw on partial/RBAC errors — keep whatever\n // aliases resolved so a single forbidden id can't blank the whole feed.\n const response = await graphqlFetch<Record<string, DirectoryUser | null>>({\n gateway: 'global',\n query: `query AthUsersByIds(${varDefs}) {\\n${selections}\\n}`,\n variables,\n suppressGlobalErrorEvent: true,\n });\n\n const data = response.data ?? {};\n const map: Record<string, ResolvedUser> = {};\n for (const value of Object.values(data)) {\n if (value && value.id) {\n map[value.id] = {\n id: value.id,\n name: directoryUserName(value),\n email: value.email ?? null,\n };\n }\n }\n return map;\n },\n enabled: unique.length > 0,\n staleTime: 5 * 60_000,\n retry: false,\n });\n};\n\nexport interface UserDirectory {\n byId: Record<string, ResolvedUser>;\n isLoading: boolean;\n isError: boolean;\n /** Resolved display name for an id, or undefined while unknown. */\n nameOf: (id: string | null | undefined) => string | undefined;\n /** Resolved email for an id, or undefined while unknown. */\n emailOf: (id: string | null | undefined) => string | null | undefined;\n}\n\n/**\n * Convenience wrapper over {@link useUsersByIds} returning name/email lookup\n * helpers, so identity components can resolve ids without touching the query.\n */\nexport const useUserDirectory = (ids: ReadonlyArray<string | null | undefined>): UserDirectory => {\n const query = useUsersByIds(ids);\n const byId = query.data ?? {};\n return {\n byId,\n isLoading: query.isLoading,\n isError: query.isError,\n nameOf: (id) => (id ? byId[id]?.name : undefined),\n emailOf: (id) => (id ? byId[id]?.email : undefined),\n };\n};\n\n// ============================================================================\n// Recent activity — a unified, time-ordered feed derived PURELY from records\n// already loaded on the page (teams/fixtures/sessions/programs). No extra\n// network, no fabricated events: every item maps to a real timestamp on a real\n// row.\n// ============================================================================\n\nexport type ActivityType =\n | 'team-created'\n | 'member-added'\n | 'fixture-updated'\n | 'program-published'\n | 'session-completed';\n\nexport interface ActivityItem {\n id: string;\n type: ActivityType;\n title: string;\n subtitle?: string;\n /** ISO timestamp the event occurred at. */\n at: string;\n entityId: string;\n entityKind: 'team' | 'fixture' | 'session' | 'program';\n}\n\nexport interface RecentActivityInput {\n teams?: AthTeam[];\n fixtures?: AthFixture[];\n sessions?: AthSession[];\n programs?: AthProgram[];\n /** Max items to return (newest first). Default 8. */\n limit?: number;\n}\n\n/** Derive a typed, time-ordered activity feed from already-loaded records. */\nexport const useRecentActivity = ({\n teams = [],\n fixtures = [],\n sessions = [],\n programs = [],\n limit = 8,\n}: RecentActivityInput): ActivityItem[] =>\n useMemo(() => {\n const items: ActivityItem[] = [];\n\n for (const team of teams) {\n items.push({\n id: `team-${team.id}`,\n type: 'team-created',\n title: `Team ${team.name} created`,\n at: team.createdAt,\n entityId: team.id,\n entityKind: 'team',\n });\n for (const member of team.members ?? []) {\n items.push({\n id: `member-${member.id}`,\n type: 'member-added',\n title: `${member.displayName} added to ${team.name}`,\n at: member.joinedAt ?? member.createdAt,\n entityId: team.id,\n entityKind: 'team',\n });\n }\n }\n\n for (const program of programs) {\n if (program.status === 'PUBLISHED') {\n items.push({\n id: `program-${program.id}`,\n type: 'program-published',\n title: `${program.name} published`,\n at: program.updatedAt,\n entityId: program.id,\n entityKind: 'program',\n });\n }\n }\n\n for (const fixture of fixtures) {\n const home = fixture.homeTeam?.name ?? 'Home';\n const away = fixture.awayTeam?.name ?? (fixture.awayTeamId ? 'Away' : 'TBD');\n const hasScore = fixture.homeScore !== null && fixture.awayScore !== null;\n items.push({\n id: `fixture-${fixture.id}`,\n type: 'fixture-updated',\n title: hasScore\n ? `${home} ${fixture.homeScore}–${fixture.awayScore} ${away}`\n : `Fixture ${home} vs ${away} updated`,\n subtitle: fixture.competition ?? undefined,\n at: fixture.updatedAt,\n entityId: fixture.id,\n entityKind: 'fixture',\n });\n }\n\n for (const session of sessions) {\n if (session.status === 'COMPLETED') {\n items.push({\n id: `session-${session.id}`,\n type: 'session-completed',\n title: `Session \"${session.title}\" completed`,\n subtitle: session.rating !== null ? `Rated ${session.rating}/5` : undefined,\n at: session.completedAt ?? session.updatedAt,\n entityId: session.id,\n entityKind: 'session',\n });\n }\n }\n\n return items\n .filter((item) => Boolean(item.at) && !Number.isNaN(new Date(item.at).getTime()))\n .sort((a, b) => new Date(b.at).getTime() - new Date(a.at).getTime())\n .slice(0, limit);\n }, [teams, fixtures, sessions, programs, limit]);\n\n// ============================================================================\n// Athlete profile — compose a team member with that athlete's real training\n// data (performance metrics + workout logs + coaching sessions), keyed off the\n// member's userId (the athleteId used across the training tables).\n// ============================================================================\n\nexport interface AthleteProfile {\n /** The resolved team member, or null while loading / not found. */\n member: AthMember | null;\n /** The user id used across sessions / metrics / workouts. */\n athleteId: string | undefined;\n metrics: AthPerformanceMetric[];\n workouts: AthWorkoutLog[];\n sessions: AthSession[];\n isLoading: boolean;\n isError: boolean;\n}\n\n/** Compose a member + their metrics, workout logs and sessions into a profile. */\nexport const useAthleteProfile = (\n teamId: string | undefined,\n memberId: string | undefined\n): AthleteProfile => {\n const membersQuery = useTeamMembers(teamId);\n const member = useMemo(\n () => (membersQuery.data ?? []).find((m) => m.id === memberId) ?? null,\n [membersQuery.data, memberId]\n );\n const athleteId = member?.userId;\n\n const metricsQuery = usePerformanceMetrics(athleteId);\n const workoutsQuery = useWorkoutLogs({ athleteId, pagination: { limit: 100 } });\n const sessionsQuery = useSessions({ athleteId, pagination: { limit: 100 } });\n\n return {\n member,\n athleteId,\n metrics: metricsQuery.data ?? [],\n workouts: workoutsQuery.data?.items ?? [],\n sessions: sessionsQuery.data?.items ?? [],\n isLoading:\n membersQuery.isLoading ||\n (Boolean(athleteId) &&\n (metricsQuery.isLoading || workoutsQuery.isLoading || sessionsQuery.isLoading)),\n isError:\n membersQuery.isError ||\n metricsQuery.isError ||\n workoutsQuery.isError ||\n sessionsQuery.isError,\n };\n};\n\n// ============================================================================\n// Coach analytics — honest aggregates over real sessions / workouts / fixtures\n// / standings. No fabricated wellness or injury data.\n// ============================================================================\n\nexport interface SessionStatusCount {\n status: AthSessionStatus;\n count: number;\n}\n\nexport interface RatingByCoach {\n coachId: string;\n avgRating: number;\n count: number;\n}\n\nexport interface AnalyticsTrendPoint {\n label: string;\n value: number;\n}\n\nexport interface CoachAnalytics {\n totalSessions: number;\n ratedCount: number;\n /** Mean of session.rating over rated sessions, or null when none rated. */\n avgRating: number | null;\n completedCount: number;\n completedPct: number;\n sessionsByStatus: SessionStatusCount[];\n ratingByCoach: RatingByCoach[];\n coachIds: string[];\n /** Acute (last 7d) vs chronic (avg weekly over 28d) workout load + ratio. */\n trainingLoad: { acute: number; chronic: number; ratio: number | null };\n /** Weekly total workout load, oldest → newest (from real totalLoad). */\n loadTrend: AnalyticsTrendPoint[];\n /** Weekly session count, oldest → newest. */\n sessionTrend: AnalyticsTrendPoint[];\n /** Standings snapshot for the resolved competition. */\n standings: AthStanding[];\n competition: string | null;\n isLoading: boolean;\n isError: boolean;\n}\n\nexport interface CoachAnalyticsOptions {\n /** Competition for the standings snapshot; else the most-used one in fixtures. */\n competition?: string;\n /** Rows to sample from each list query. Default 200. */\n sampleLimit?: number;\n}\n\n/** Bucket rows into `weeks` weekly totals ending at \"now\" (oldest → newest). */\nfunction weeklyTrend<T>(\n rows: readonly T[],\n getDate: (row: T) => string,\n getValue: (row: T) => number,\n weeks = 8\n): AnalyticsTrendPoint[] {\n const MS_WEEK = 7 * 86_400_000;\n const now = Date.now();\n const buckets = Array.from({ length: weeks }, () => 0);\n for (const row of rows) {\n const time = new Date(getDate(row)).getTime();\n if (Number.isNaN(time)) {\n continue;\n }\n const age = now - time;\n if (age < 0) {\n continue;\n }\n const weeksAgo = Math.floor(age / MS_WEEK);\n if (weeksAgo >= 0 && weeksAgo < weeks) {\n buckets[weeks - 1 - weeksAgo] += getValue(row);\n }\n }\n return buckets.map((value, i) => {\n const ago = weeks - 1 - i;\n return { label: ago === 0 ? 'Now' : `${ago}w`, value };\n });\n}\n\n/** Aggregate real coaching data for the analytics dashboard. */\nexport const useCoachAnalytics = (options: CoachAnalyticsOptions = {}): CoachAnalytics => {\n const { competition: competitionOpt, sampleLimit = 200 } = options;\n\n const sessionsQuery = useSessions({ pagination: { limit: sampleLimit } });\n const workoutsQuery = useWorkoutLogs({ pagination: { limit: sampleLimit } });\n const fixturesQuery = useFixtures({ pagination: { limit: sampleLimit } });\n\n const sessions = useMemo(() => sessionsQuery.data?.items ?? [], [sessionsQuery.data]);\n const workouts = useMemo(() => workoutsQuery.data?.items ?? [], [workoutsQuery.data]);\n const fixtures = useMemo(() => fixturesQuery.data?.items ?? [], [fixturesQuery.data]);\n\n // Resolve the competition for the standings snapshot.\n const competition = useMemo(() => {\n if (competitionOpt) {\n return competitionOpt;\n }\n const counts = new Map<string, number>();\n for (const fixture of fixtures) {\n if (fixture.competition) {\n counts.set(fixture.competition, (counts.get(fixture.competition) ?? 0) + 1);\n }\n }\n let top: string | null = null;\n let best = 0;\n for (const [comp, n] of counts) {\n if (n > best) {\n best = n;\n top = comp;\n }\n }\n return top;\n }, [competitionOpt, fixtures]);\n\n const standingsQuery = useStandings(competition ?? undefined);\n\n return useMemo<CoachAnalytics>(() => {\n const totalSessions = sessions.length;\n const rated = sessions.filter((s) => s.rating !== null);\n const ratedCount = rated.length;\n const avgRating =\n ratedCount > 0 ? rated.reduce((acc, s) => acc + (s.rating ?? 0), 0) / ratedCount : null;\n const completedCount = sessions.filter((s) => s.status === 'COMPLETED').length;\n const completedPct = totalSessions > 0 ? (completedCount / totalSessions) * 100 : 0;\n\n const statusOrder: AthSessionStatus[] = [\n 'REQUESTED',\n 'CONFIRMED',\n 'COMPLETED',\n 'CANCELLED',\n 'NO_SHOW',\n ];\n const statusCounts = new Map<AthSessionStatus, number>();\n for (const s of sessions) {\n statusCounts.set(s.status, (statusCounts.get(s.status) ?? 0) + 1);\n }\n const sessionsByStatus = statusOrder\n .map((status) => ({ status, count: statusCounts.get(status) ?? 0 }))\n .filter((entry) => entry.count > 0);\n\n const byCoach = new Map<string, { sum: number; count: number }>();\n for (const s of rated) {\n const entry = byCoach.get(s.coachId) ?? { sum: 0, count: 0 };\n entry.sum += s.rating ?? 0;\n entry.count += 1;\n byCoach.set(s.coachId, entry);\n }\n const ratingByCoach = Array.from(byCoach.entries())\n .map(([coachId, { sum, count }]) => ({ coachId, avgRating: sum / count, count }))\n .sort((a, b) => b.avgRating - a.avgRating);\n\n const coachIds = Array.from(new Set(sessions.map((s) => s.coachId)));\n\n // Training load (ACWR) from real workout totalLoad.\n const now = Date.now();\n const DAY = 86_400_000;\n let acute = 0;\n let chronic28 = 0;\n for (const w of workouts) {\n const load = w.totalLoad ?? 0;\n const age = now - new Date(w.performedAt).getTime();\n if (Number.isNaN(age)) {\n continue;\n }\n if (age <= 7 * DAY) {\n acute += load;\n }\n if (age <= 28 * DAY) {\n chronic28 += load;\n }\n }\n const chronic = chronic28 / 4;\n const ratio = chronic > 0 ? acute / chronic : null;\n\n const loadTrend = weeklyTrend(\n workouts,\n (w) => w.performedAt,\n (w) => w.totalLoad ?? 0\n );\n const sessionTrend = weeklyTrend(\n sessions,\n (s) => s.scheduledAt,\n () => 1\n );\n\n return {\n totalSessions,\n ratedCount,\n avgRating,\n completedCount,\n completedPct,\n sessionsByStatus,\n ratingByCoach,\n coachIds,\n trainingLoad: { acute, chronic, ratio },\n loadTrend,\n sessionTrend,\n standings: standingsQuery.data ?? [],\n competition,\n isLoading:\n sessionsQuery.isLoading ||\n workoutsQuery.isLoading ||\n fixturesQuery.isLoading ||\n (Boolean(competition) && standingsQuery.isLoading),\n isError:\n sessionsQuery.isError ||\n workoutsQuery.isError ||\n fixturesQuery.isError ||\n standingsQuery.isError,\n };\n }, [\n sessions,\n workouts,\n competition,\n standingsQuery.data,\n standingsQuery.isLoading,\n standingsQuery.isError,\n sessionsQuery.isLoading,\n sessionsQuery.isError,\n workoutsQuery.isLoading,\n workoutsQuery.isError,\n fixturesQuery.isLoading,\n fixturesQuery.isError,\n ]);\n};\n\n// ============================================================================\n// Health probe\n// ============================================================================\n\nexport const useHealth = () => {\n const { apiGatewayUrl } = useAthleteBridge();\n return useQuery({\n queryKey: ['atheletebridge', 'health'],\n queryFn: async () => {\n const result = await athQuery<{ athHealth: string }>(\n `query AthHealth { athHealth }`,\n {},\n apiGatewayUrl\n );\n return result.athHealth;\n },\n });\n};\n"],"mappings":"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"}
|