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 SeedTournament from '@/features/tournaments/components/seed-tournament'
|
||||||
import { Container, Alert, Text } from '@mantine/core'
|
import { Container, Alert, Text } from '@mantine/core'
|
||||||
import { Info } from '@phosphor-icons/react'
|
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')({
|
export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({
|
||||||
beforeLoad: async ({ context, params }) => {
|
beforeLoad: async ({ context, params }) => {
|
||||||
@@ -31,6 +35,50 @@ function RouteComponent() {
|
|||||||
const { tournament } = Route.useRouteContext()
|
const { tournament } = Route.useRouteContext()
|
||||||
const router = useRouter()
|
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 = () => {
|
const handleSuccess = () => {
|
||||||
router.navigate({
|
router.navigate({
|
||||||
to: '/admin/tournaments/$id',
|
to: '/admin/tournaments/$id',
|
||||||
@@ -44,7 +92,7 @@ function RouteComponent() {
|
|||||||
<Container size="md">
|
<Container size="md">
|
||||||
{
|
{
|
||||||
tournament.matches?.length ?
|
tournament.matches?.length ?
|
||||||
<p>Matches</p>
|
<BracketView bracket={bracket} onAnnounce={console.log} />
|
||||||
: (
|
: (
|
||||||
<SeedTournament
|
<SeedTournament
|
||||||
tournamentId={tournament.id}
|
tournamentId={tournament.id}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ActionIcon, Card, Flex, Text } from "@mantine/core";
|
import { ActionIcon, Card, Flex, Text, Stack } from "@mantine/core";
|
||||||
import { PlayIcon } from "@phosphor-icons/react";
|
import { PlayIcon, PencilIcon } from "@phosphor-icons/react";
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
import { MatchSlot } from "./match-slot";
|
import { MatchSlot } from "./match-slot";
|
||||||
import { Match } from "@/features/matches/types";
|
import { Match } from "@/features/matches/types";
|
||||||
@@ -34,65 +34,79 @@ export const MatchCard: React.FC<MatchCardProps> = ({
|
|||||||
[match]
|
[match]
|
||||||
);
|
);
|
||||||
|
|
||||||
const showAnnounce = useMemo(
|
const showToolbar = useMemo(
|
||||||
() => onAnnounce && match.home && match.away,
|
() => match.home && match.away,
|
||||||
[onAnnounce, match.home, match.away]
|
[match.home, match.away]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAnnounce = useCallback(
|
const handleAnnounce = useCallback(
|
||||||
() => onAnnounce?.(match.home, match.away),
|
() => 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 (
|
return (
|
||||||
<Flex direction="row" align="center" justify="end" gap={8}>
|
<Flex direction="row" align="center" justify="end" gap={8}>
|
||||||
<Text c="dimmed" fw="bolder">
|
<Text c="dimmed" fw="bolder">
|
||||||
{match.order}
|
{match.order}
|
||||||
</Text>
|
</Text>
|
||||||
<Card
|
<Flex align="stretch">
|
||||||
withBorder
|
<Card
|
||||||
pos="relative"
|
withBorder
|
||||||
w={200}
|
pos="relative"
|
||||||
style={{ overflow: "visible" }}
|
w={200}
|
||||||
data-match-lid={match.lid}
|
style={{ overflow: "visible" }}
|
||||||
>
|
data-match-lid={match.lid}
|
||||||
<Card.Section withBorder p={0}>
|
>
|
||||||
<MatchSlot {...homeSlot} />
|
<Card.Section withBorder p={0}>
|
||||||
</Card.Section>
|
<MatchSlot {...homeSlot} />
|
||||||
|
</Card.Section>
|
||||||
|
|
||||||
<Card.Section p={0} mb={-16}>
|
<Card.Section p={0} mb={-16}>
|
||||||
<MatchSlot {...awaySlot} />
|
<MatchSlot {...awaySlot} />
|
||||||
</Card.Section>
|
</Card.Section>
|
||||||
|
|
||||||
{match.reset && (
|
{match.reset && (
|
||||||
<Text
|
<Text
|
||||||
pos="absolute"
|
pos="absolute"
|
||||||
top={-20}
|
top={-20}
|
||||||
left={8}
|
left={8}
|
||||||
size="xs"
|
size="xs"
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
fw="bold"
|
fw="bold"
|
||||||
|
>
|
||||||
|
* If necessary
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{showToolbar && (
|
||||||
|
<Flex
|
||||||
|
direction="column"
|
||||||
|
justify="center"
|
||||||
|
align="center"
|
||||||
>
|
>
|
||||||
* If necessary
|
<ActionIcon
|
||||||
</Text>
|
color="green"
|
||||||
|
onClick={handleAnnounce}
|
||||||
|
size="sm"
|
||||||
|
h='100%'
|
||||||
|
radius='sm'
|
||||||
|
ml={-4}
|
||||||
|
style={{
|
||||||
|
borderTopLeftRadius: 0,
|
||||||
|
borderBottomLeftRadius: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayIcon size={14} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
</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,
|
seed,
|
||||||
}) => (
|
}) => (
|
||||||
<Flex align="stretch">
|
<Flex align="stretch">
|
||||||
{seed && <SeedBadge seed={seed} />}
|
{(seed && seed > 0) ? <SeedBadge seed={seed} /> : undefined}
|
||||||
<Flex p="4px 8px">
|
<Flex p="4px 8px">
|
||||||
{team ? (
|
{team ? (
|
||||||
<Text size="xs">{team.name}</Text>
|
<Text size="xs">{team.name}</Text>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function createTournamentsService(pb: PocketBase) {
|
|||||||
return {
|
return {
|
||||||
async getTournament(id: string): Promise<Tournament> {
|
async getTournament(id: string): Promise<Tournament> {
|
||||||
const result = await pb.collection("tournaments").getOne(id, {
|
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);
|
return transformTournament(result);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function transformTeamInfo(record: any): TeamInfo {
|
|||||||
export const transformMatch = (record: any): Match => {
|
export const transformMatch = (record: any): Match => {
|
||||||
return {
|
return {
|
||||||
id: record.id,
|
id: record.id,
|
||||||
order: record.name,
|
order: record.order,
|
||||||
lid: record.lid,
|
lid: record.lid,
|
||||||
reset: record.reset,
|
reset: record.reset,
|
||||||
round: record.round,
|
round: record.round,
|
||||||
|
|||||||
Reference in New Issue
Block a user