From 8dfff139e1ea9c6928ff8382f9c9feab93822126 Mon Sep 17 00:00:00 2001 From: yohlo Date: Thu, 11 Sep 2025 14:04:05 -0500 Subject: [PATCH] match status --- pb_migrations/1757615830_updated_matches.js | 31 +++++++++++++++ .../_authed/admin/tournaments/run.$id.tsx | 17 +++----- src/app/routes/refresh-session.tsx | 2 +- .../bracket/components/match-card.tsx | 4 +- src/features/matches/server.ts | 39 ++++++++++++++++++- src/features/matches/types.ts | 4 ++ .../components/seed-tournament.tsx | 11 ++++-- src/lib/pocketbase/util/transform-types.ts | 1 + 8 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 pb_migrations/1757615830_updated_matches.js diff --git a/pb_migrations/1757615830_updated_matches.js b/pb_migrations/1757615830_updated_matches.js new file mode 100644 index 0000000..12ce5a4 --- /dev/null +++ b/pb_migrations/1757615830_updated_matches.js @@ -0,0 +1,31 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_2541054544") + + // add field + collection.fields.addAt(21, new Field({ + "hidden": false, + "id": "select2063623452", + "maxSelect": 1, + "name": "status", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "tbd", + "ready", + "started", + "ended" + ] + })) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_2541054544") + + // remove field + collection.fields.removeById("select2063623452") + + return app.save(collection) +}) diff --git a/src/app/routes/_authed/admin/tournaments/run.$id.tsx b/src/app/routes/_authed/admin/tournaments/run.$id.tsx index b3c91b1..d00930c 100644 --- a/src/app/routes/_authed/admin/tournaments/run.$id.tsx +++ b/src/app/routes/_authed/admin/tournaments/run.$id.tsx @@ -1,5 +1,5 @@ import { createFileRoute, redirect, useRouter } from '@tanstack/react-router' -import { tournamentQueries } from '@/features/tournaments/queries' +import { tournamentQueries, useTournament } from '@/features/tournaments/queries' import { ensureServerQueryData } from '@/lib/tanstack-query/utils/ensure' import SeedTournament from '@/features/tournaments/components/seed-tournament' import { Container, Alert, Text } from '@mantine/core' @@ -32,8 +32,8 @@ export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({ }) function RouteComponent() { - const { tournament } = Route.useRouteContext() - const router = useRouter() + const { id } = Route.useParams(); + const { data: tournament } = useTournament(id) const bracket: BracketData = useMemo(() => { if (!tournament.matches || tournament.matches.length === 0) { @@ -67,18 +67,12 @@ function RouteComponent() { return { winners, losers } }, [tournament.matches]) - - const handleSuccess = () => { - router.navigate({ - to: '/admin/tournaments/$id', - params: { id: tournament.id } - }) - } - const handleStartMatch = (match: Match) => { } + console.log(tournament.matches) + return ( { @@ -88,7 +82,6 @@ function RouteComponent() { ) } diff --git a/src/app/routes/refresh-session.tsx b/src/app/routes/refresh-session.tsx index 615318c..8ab33a8 100644 --- a/src/app/routes/refresh-session.tsx +++ b/src/app/routes/refresh-session.tsx @@ -18,7 +18,7 @@ function RouteComponent() { const urlParams = new URLSearchParams(window.location.search) const redirect = urlParams.get('redirect') - if (redirect) { + if (redirect && !redirect.startsWith('/_serverFn')) { window.location.href = decodeURIComponent(redirect) } else { window.location.href = '/' diff --git a/src/features/bracket/components/match-card.tsx b/src/features/bracket/components/match-card.tsx index ef6b37b..403eef4 100644 --- a/src/features/bracket/components/match-card.tsx +++ b/src/features/bracket/components/match-card.tsx @@ -35,8 +35,8 @@ export const MatchCard: React.FC = ({ ); const showToolbar = useMemo( - () => match.home && match.away && onStartMatch, - [match.home, match.away] + () => match.status === "ready" && onStartMatch, + [match.status, onStartMatch] ); const handleAnnounce = useCallback( diff --git a/src/features/matches/server.ts b/src/features/matches/server.ts index 798e3b9..d094695 100644 --- a/src/features/matches/server.ts +++ b/src/features/matches/server.ts @@ -58,6 +58,7 @@ export const generateTournamentBracket = createServerFn() home_from_loser: match.home_from_loser || false, away_from_loser: match.away_from_loser || false, is_losers_bracket: false, + status: "tbd", tournament: tournamentId, }; @@ -77,6 +78,10 @@ export const generateTournamentBracket = createServerFn() } } + if (matchInput.home && matchInput.away) { + matchInput.status = "ready" + } + matchInputs.push(matchInput); }); }); @@ -97,6 +102,7 @@ export const generateTournamentBracket = createServerFn() home_from_loser: match.home_from_loser || false, away_from_loser: match.away_from_loser || false, is_losers_bracket: true, + status: "tbd", tournament: tournamentId, }; @@ -135,7 +141,38 @@ export const startMatch = createServerFn() } match = await pbAdmin.updateMatch(data, { - start_time: new Date().toISOString() + start_time: new Date().toISOString(), + status: "started" + }); + + return match; + } + )); + +const endMatchSchema = z.object({ + matchId: z.string(), + home_cups: z.number(), + away_cups: z.number(), + ot_count: z.number() +}); +export const endMatch = createServerFn() + .validator(endMatchSchema) + .middleware([superTokensAdminFunctionMiddleware]) + .handler(async ({ data: { matchId, home_cups, away_cups, ot_count } }) => + toServerResult(async () => { + logger.info('Ending match', matchId); + + let match = await pbAdmin.getMatch(matchId); + if (!match) { + throw new Error('Match not found'); + } + + match = await pbAdmin.updateMatch(matchId, { + end_time: new Date().toISOString(), + status: "ended", + home_cups, + away_cups, + ot_count }) } )); \ No newline at end of file diff --git a/src/features/matches/types.ts b/src/features/matches/types.ts index 2174cc6..341165e 100644 --- a/src/features/matches/types.ts +++ b/src/features/matches/types.ts @@ -2,6 +2,8 @@ import { z } from "zod"; import { TeamInfo } from "../teams/types"; import { TournamentInfo } from "../tournaments/types"; +export type MatchStatus = "tbd" | "ready" | "started" | "ended"; + /** * class TMatchSlot(BaseModel): pass @@ -52,6 +54,7 @@ export interface Match { home_from_loser: boolean; away_from_loser: boolean; is_losers_bracket: boolean; + status: MatchStatus; tournament: TournamentInfo; home?: TeamInfo; away?: TeamInfo; @@ -77,6 +80,7 @@ export const matchInputSchema = z.object({ home_from_loser: z.boolean().optional().default(false), away_from_loser: z.boolean().optional().default(false), is_losers_bracket: z.boolean().optional().default(false), + status: z.enum(["tbd", "ready", "started", "ended"]).optional().default("tbd"), tournament: z.string().min(1), home: z.string().min(1).optional(), away: z.string().min(1).optional(), diff --git a/src/features/tournaments/components/seed-tournament.tsx b/src/features/tournaments/components/seed-tournament.tsx index cec071e..74fdd6a 100644 --- a/src/features/tournaments/components/seed-tournament.tsx +++ b/src/features/tournaments/components/seed-tournament.tsx @@ -17,20 +17,21 @@ import Avatar from "@/components/avatar"; import { useBracketPreview } from "@/features/bracket/queries"; import { BracketData } from "@/features/bracket/types"; import BracketView from "@/features/bracket/components/bracket-view"; +import { useQueryClient } from "@tanstack/react-query"; +import { tournamentKeys } from "../queries"; interface SeedTournamentProps { tournamentId: string; teams: TeamInfo[]; - onSuccess?: () => void; } const SeedTournament: React.FC = ({ tournamentId, - teams, - onSuccess, + teams }) => { const [orderedTeams, setOrderedTeams] = useState(teams); const { data: bracketPreview } = useBracketPreview(teams.length); + const queryClient = useQueryClient() const bracket: BracketData = useMemo( () => ({ @@ -56,7 +57,9 @@ const SeedTournament: React.FC = ({ mutationFn: generateTournamentBracket, successMessage: "Tournament bracket generated successfully!", onSuccess: () => { - onSuccess?.(); + queryClient.invalidateQueries({ + queryKey: tournamentKeys.details(tournamentId) + }) }, }); diff --git a/src/lib/pocketbase/util/transform-types.ts b/src/lib/pocketbase/util/transform-types.ts index 9883a36..2f0e5e3 100644 --- a/src/lib/pocketbase/util/transform-types.ts +++ b/src/lib/pocketbase/util/transform-types.ts @@ -43,6 +43,7 @@ export const transformMatch = (record: any): Match => { home_from_loser: record.home_from_loser, away_from_loser: record.away_from_loser, is_losers_bracket: record.is_losers_bracket, + status: record.status || "tbd", tournament: transformTournamentInfo(record.expand?.tournament), home: record.expand?.home ? transformTeamInfo(record.expand.home) : undefined, away: record.expand?.away ? transformTeamInfo(record.expand.away) : undefined,