seed tournament done
This commit is contained in:
27
pb_migrations/1757263183_updated_matches.js
Normal file
27
pb_migrations/1757263183_updated_matches.js
Normal 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)
|
||||
})
|
||||
@@ -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}
|
||||
|
||||
@@ -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,65 +34,79 @@ 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>
|
||||
<Card
|
||||
withBorder
|
||||
pos="relative"
|
||||
w={200}
|
||||
style={{ overflow: "visible" }}
|
||||
data-match-lid={match.lid}
|
||||
>
|
||||
<Card.Section withBorder p={0}>
|
||||
<MatchSlot {...homeSlot} />
|
||||
</Card.Section>
|
||||
<Flex align="stretch">
|
||||
<Card
|
||||
withBorder
|
||||
pos="relative"
|
||||
w={200}
|
||||
style={{ overflow: "visible" }}
|
||||
data-match-lid={match.lid}
|
||||
>
|
||||
<Card.Section withBorder p={0}>
|
||||
<MatchSlot {...homeSlot} />
|
||||
</Card.Section>
|
||||
|
||||
<Card.Section p={0} mb={-16}>
|
||||
<MatchSlot {...awaySlot} />
|
||||
</Card.Section>
|
||||
<Card.Section p={0} mb={-16}>
|
||||
<MatchSlot {...awaySlot} />
|
||||
</Card.Section>
|
||||
|
||||
{match.reset && (
|
||||
<Text
|
||||
pos="absolute"
|
||||
top={-20}
|
||||
left={8}
|
||||
size="xs"
|
||||
c="dimmed"
|
||||
fw="bold"
|
||||
{match.reset && (
|
||||
<Text
|
||||
pos="absolute"
|
||||
top={-20}
|
||||
left={8}
|
||||
size="xs"
|
||||
c="dimmed"
|
||||
fw="bold"
|
||||
>
|
||||
* If necessary
|
||||
</Text>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{showToolbar && (
|
||||
<Flex
|
||||
direction="column"
|
||||
justify="center"
|
||||
align="center"
|
||||
>
|
||||
* If necessary
|
||||
</Text>
|
||||
<ActionIcon
|
||||
color="green"
|
||||
onClick={handleAnnounce}
|
||||
size="sm"
|
||||
h='100%'
|
||||
radius='sm'
|
||||
ml={-4}
|
||||
style={{
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
}}
|
||||
>
|
||||
<PlayIcon size={14} />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{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>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user