seed tournament done

This commit is contained in:
yohlo
2025-09-07 11:55:41 -05:00
parent 2396464a19
commit c5d69f1a19
6 changed files with 139 additions and 50 deletions

View File

@@ -0,0 +1,27 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2541054544")
// add field
collection.fields.addAt(20, new Field({
"hidden": false,
"id": "number3320769076",
"max": null,
"min": null,
"name": "round",
"onlyInt": false,
"presentable": false,
"required": false,
"system": false,
"type": "number"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2541054544")
// remove field
collection.fields.removeById("number3320769076")
return app.save(collection)
})

View File

@@ -4,6 +4,10 @@ import { ensureServerQueryData } from '@/lib/tanstack-query/utils/ensure'
import SeedTournament from '@/features/tournaments/components/seed-tournament'
import { Container, Alert, Text } from '@mantine/core'
import { Info } from '@phosphor-icons/react'
import { useMemo } from 'react'
import { BracketData } from '@/features/bracket/types'
import { Match } from '@/features/matches/types'
import BracketView from '@/features/bracket/components/bracket-view'
export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({
beforeLoad: async ({ context, params }) => {
@@ -31,6 +35,50 @@ function RouteComponent() {
const { tournament } = Route.useRouteContext()
const router = useRouter()
const bracket: BracketData = useMemo(() => {
if (!tournament.matches || tournament.matches.length === 0) {
return { winners: [], losers: [] }
}
console.log('Tournament Matches:', tournament.matches)
const winnersMap = new Map<number, Match[]>()
const losersMap = new Map<number, Match[]>()
tournament.matches.sort((a, b) => a.lid - b.lid).forEach((match) => {
console.log('Processing Match:', match)
if (!match.is_losers_bracket) {
if (!winnersMap.has(match.round)) {
winnersMap.set(match.round, [])
}
winnersMap.get(match.round)!.push(match)
console.log('Added to Winners Bracket:', match)
} else {
if (!losersMap.has(match.round)) {
losersMap.set(match.round, [])
}
losersMap.get(match.round)!.push(match)
console.log('Added to Losers Bracket:', match)
}
})
// Convert to dense arrays sorted by round (0, 1, 2, 3...)
const winners = Array.from(winnersMap.entries())
.sort(([a], [b]) => a - b)
.map(([, matches]) => matches)
const losers = Array.from(losersMap.entries())
.sort(([a], [b]) => a - b)
.map(([, matches]) => matches)
console.log('Winners Bracket (fixed):', winners)
console.log('Losers Bracket (fixed):', losers)
return { winners, losers }
}, [tournament.matches])
console.log('Bracket Data:', bracket)
const handleSuccess = () => {
router.navigate({
to: '/admin/tournaments/$id',
@@ -44,7 +92,7 @@ function RouteComponent() {
<Container size="md">
{
tournament.matches?.length ?
<p>Matches</p>
<BracketView bracket={bracket} onAnnounce={console.log} />
: (
<SeedTournament
tournamentId={tournament.id}

View File

@@ -1,5 +1,5 @@
import { ActionIcon, Card, Flex, Text } from "@mantine/core";
import { PlayIcon } from "@phosphor-icons/react";
import { ActionIcon, Card, Flex, Text, Stack } from "@mantine/core";
import { PlayIcon, PencilIcon } from "@phosphor-icons/react";
import React, { useCallback, useMemo } from "react";
import { MatchSlot } from "./match-slot";
import { Match } from "@/features/matches/types";
@@ -34,21 +34,27 @@ export const MatchCard: React.FC<MatchCardProps> = ({
[match]
);
const showAnnounce = useMemo(
() => onAnnounce && match.home && match.away,
[onAnnounce, match.home, match.away]
const showToolbar = useMemo(
() => match.home && match.away,
[match.home, match.away]
);
const handleAnnounce = useCallback(
() => onAnnounce?.(match.home, match.away),
[match.home, match.away]
[onAnnounce, match.home, match.away]
);
const handleEdit = useCallback(() => {
// TODO: implement edit functionality
console.log('Edit match:', match);
}, [match]);
return (
<Flex direction="row" align="center" justify="end" gap={8}>
<Text c="dimmed" fw="bolder">
{match.order}
</Text>
<Flex align="stretch">
<Card
withBorder
pos="relative"
@@ -76,23 +82,31 @@ export const MatchCard: React.FC<MatchCardProps> = ({
* If necessary
</Text>
)}
{showAnnounce && (
<ActionIcon
pos="absolute"
variant="filled"
color="green"
top={-20}
right={-12}
onClick={handleAnnounce}
bd="none"
style={{ boxShadow: "none" }}
size="xs"
>
<PlayIcon size={12} />
</ActionIcon>
)}
</Card>
{showToolbar && (
<Flex
direction="column"
justify="center"
align="center"
>
<ActionIcon
color="green"
onClick={handleAnnounce}
size="sm"
h='100%'
radius='sm'
ml={-4}
style={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
}}
>
<PlayIcon size={14} />
</ActionIcon>
</Flex>
)}
</Flex>
</Flex>
);
};

View File

@@ -17,7 +17,7 @@ export const MatchSlot: React.FC<MatchSlotProps> = ({
seed,
}) => (
<Flex align="stretch">
{seed && <SeedBadge seed={seed} />}
{(seed && seed > 0) ? <SeedBadge seed={seed} /> : undefined}
<Flex p="4px 8px">
{team ? (
<Text size="xs">{team.name}</Text>

View File

@@ -14,7 +14,7 @@ export function createTournamentsService(pb: PocketBase) {
return {
async getTournament(id: string): Promise<Tournament> {
const result = await pb.collection("tournaments").getOne(id, {
expand: "teams, teams.players, matches, matches.tournament",
expand: "teams, teams.players, matches, matches.tournament, matches.home, matches.away",
});
return transformTournament(result);
},

View File

@@ -28,7 +28,7 @@ export function transformTeamInfo(record: any): TeamInfo {
export const transformMatch = (record: any): Match => {
return {
id: record.id,
order: record.name,
order: record.order,
lid: record.lid,
reset: record.reset,
round: record.round,