match status
This commit is contained in:
31
pb_migrations/1757615830_updated_matches.js
Normal file
31
pb_migrations/1757615830_updated_matches.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
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)
|
||||||
|
})
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createFileRoute, redirect, useRouter } from '@tanstack/react-router'
|
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 { ensureServerQueryData } from '@/lib/tanstack-query/utils/ensure'
|
||||||
import SeedTournament from '@/features/tournaments/components/seed-tournament'
|
import SeedTournament from '@/features/tournaments/components/seed-tournament'
|
||||||
import { Container, Alert, Text } from '@mantine/core'
|
import { Container, Alert, Text } from '@mantine/core'
|
||||||
@@ -32,8 +32,8 @@ export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { tournament } = Route.useRouteContext()
|
const { id } = Route.useParams();
|
||||||
const router = useRouter()
|
const { data: tournament } = useTournament(id)
|
||||||
|
|
||||||
const bracket: BracketData = useMemo(() => {
|
const bracket: BracketData = useMemo(() => {
|
||||||
if (!tournament.matches || tournament.matches.length === 0) {
|
if (!tournament.matches || tournament.matches.length === 0) {
|
||||||
@@ -67,18 +67,12 @@ function RouteComponent() {
|
|||||||
return { winners, losers }
|
return { winners, losers }
|
||||||
}, [tournament.matches])
|
}, [tournament.matches])
|
||||||
|
|
||||||
|
|
||||||
const handleSuccess = () => {
|
|
||||||
router.navigate({
|
|
||||||
to: '/admin/tournaments/$id',
|
|
||||||
params: { id: tournament.id }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleStartMatch = (match: Match) => {
|
const handleStartMatch = (match: Match) => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(tournament.matches)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container size="md">
|
<Container size="md">
|
||||||
{
|
{
|
||||||
@@ -88,7 +82,6 @@ function RouteComponent() {
|
|||||||
<SeedTournament
|
<SeedTournament
|
||||||
tournamentId={tournament.id}
|
tournamentId={tournament.id}
|
||||||
teams={tournament.teams || []}
|
teams={tournament.teams || []}
|
||||||
onSuccess={handleSuccess}
|
|
||||||
/>)
|
/>)
|
||||||
}
|
}
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function RouteComponent() {
|
|||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
const redirect = urlParams.get('redirect')
|
const redirect = urlParams.get('redirect')
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect && !redirect.startsWith('/_serverFn')) {
|
||||||
window.location.href = decodeURIComponent(redirect)
|
window.location.href = decodeURIComponent(redirect)
|
||||||
} else {
|
} else {
|
||||||
window.location.href = '/'
|
window.location.href = '/'
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ export const MatchCard: React.FC<MatchCardProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const showToolbar = useMemo(
|
const showToolbar = useMemo(
|
||||||
() => match.home && match.away && onStartMatch,
|
() => match.status === "ready" && onStartMatch,
|
||||||
[match.home, match.away]
|
[match.status, onStartMatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAnnounce = useCallback(
|
const handleAnnounce = useCallback(
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
home_from_loser: match.home_from_loser || false,
|
home_from_loser: match.home_from_loser || false,
|
||||||
away_from_loser: match.away_from_loser || false,
|
away_from_loser: match.away_from_loser || false,
|
||||||
is_losers_bracket: false,
|
is_losers_bracket: false,
|
||||||
|
status: "tbd",
|
||||||
tournament: tournamentId,
|
tournament: tournamentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,6 +78,10 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matchInput.home && matchInput.away) {
|
||||||
|
matchInput.status = "ready"
|
||||||
|
}
|
||||||
|
|
||||||
matchInputs.push(matchInput);
|
matchInputs.push(matchInput);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -97,6 +102,7 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
home_from_loser: match.home_from_loser || false,
|
home_from_loser: match.home_from_loser || false,
|
||||||
away_from_loser: match.away_from_loser || false,
|
away_from_loser: match.away_from_loser || false,
|
||||||
is_losers_bracket: true,
|
is_losers_bracket: true,
|
||||||
|
status: "tbd",
|
||||||
tournament: tournamentId,
|
tournament: tournamentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,7 +141,38 @@ export const startMatch = createServerFn()
|
|||||||
}
|
}
|
||||||
|
|
||||||
match = await pbAdmin.updateMatch(data, {
|
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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
@@ -2,6 +2,8 @@ import { z } from "zod";
|
|||||||
import { TeamInfo } from "../teams/types";
|
import { TeamInfo } from "../teams/types";
|
||||||
import { TournamentInfo } from "../tournaments/types";
|
import { TournamentInfo } from "../tournaments/types";
|
||||||
|
|
||||||
|
export type MatchStatus = "tbd" | "ready" | "started" | "ended";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class TMatchSlot(BaseModel):
|
* class TMatchSlot(BaseModel):
|
||||||
pass
|
pass
|
||||||
@@ -52,6 +54,7 @@ export interface Match {
|
|||||||
home_from_loser: boolean;
|
home_from_loser: boolean;
|
||||||
away_from_loser: boolean;
|
away_from_loser: boolean;
|
||||||
is_losers_bracket: boolean;
|
is_losers_bracket: boolean;
|
||||||
|
status: MatchStatus;
|
||||||
tournament: TournamentInfo;
|
tournament: TournamentInfo;
|
||||||
home?: TeamInfo;
|
home?: TeamInfo;
|
||||||
away?: TeamInfo;
|
away?: TeamInfo;
|
||||||
@@ -77,6 +80,7 @@ export const matchInputSchema = z.object({
|
|||||||
home_from_loser: z.boolean().optional().default(false),
|
home_from_loser: z.boolean().optional().default(false),
|
||||||
away_from_loser: z.boolean().optional().default(false),
|
away_from_loser: z.boolean().optional().default(false),
|
||||||
is_losers_bracket: 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),
|
tournament: z.string().min(1),
|
||||||
home: z.string().min(1).optional(),
|
home: z.string().min(1).optional(),
|
||||||
away: z.string().min(1).optional(),
|
away: z.string().min(1).optional(),
|
||||||
|
|||||||
@@ -17,20 +17,21 @@ import Avatar from "@/components/avatar";
|
|||||||
import { useBracketPreview } from "@/features/bracket/queries";
|
import { useBracketPreview } from "@/features/bracket/queries";
|
||||||
import { BracketData } from "@/features/bracket/types";
|
import { BracketData } from "@/features/bracket/types";
|
||||||
import BracketView from "@/features/bracket/components/bracket-view";
|
import BracketView from "@/features/bracket/components/bracket-view";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { tournamentKeys } from "../queries";
|
||||||
|
|
||||||
interface SeedTournamentProps {
|
interface SeedTournamentProps {
|
||||||
tournamentId: string;
|
tournamentId: string;
|
||||||
teams: TeamInfo[];
|
teams: TeamInfo[];
|
||||||
onSuccess?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SeedTournament: React.FC<SeedTournamentProps> = ({
|
const SeedTournament: React.FC<SeedTournamentProps> = ({
|
||||||
tournamentId,
|
tournamentId,
|
||||||
teams,
|
teams
|
||||||
onSuccess,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [orderedTeams, setOrderedTeams] = useState<TeamInfo[]>(teams);
|
const [orderedTeams, setOrderedTeams] = useState<TeamInfo[]>(teams);
|
||||||
const { data: bracketPreview } = useBracketPreview(teams.length);
|
const { data: bracketPreview } = useBracketPreview(teams.length);
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const bracket: BracketData = useMemo(
|
const bracket: BracketData = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -56,7 +57,9 @@ const SeedTournament: React.FC<SeedTournamentProps> = ({
|
|||||||
mutationFn: generateTournamentBracket,
|
mutationFn: generateTournamentBracket,
|
||||||
successMessage: "Tournament bracket generated successfully!",
|
successMessage: "Tournament bracket generated successfully!",
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
onSuccess?.();
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: tournamentKeys.details(tournamentId)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const transformMatch = (record: any): Match => {
|
|||||||
home_from_loser: record.home_from_loser,
|
home_from_loser: record.home_from_loser,
|
||||||
away_from_loser: record.away_from_loser,
|
away_from_loser: record.away_from_loser,
|
||||||
is_losers_bracket: record.is_losers_bracket,
|
is_losers_bracket: record.is_losers_bracket,
|
||||||
|
status: record.status || "tbd",
|
||||||
tournament: transformTournamentInfo(record.expand?.tournament),
|
tournament: transformTournamentInfo(record.expand?.tournament),
|
||||||
home: record.expand?.home ? transformTeamInfo(record.expand.home) : undefined,
|
home: record.expand?.home ? transformTeamInfo(record.expand.home) : undefined,
|
||||||
away: record.expand?.away ? transformTeamInfo(record.expand.away) : undefined,
|
away: record.expand?.away ? transformTeamInfo(record.expand.away) : undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user