diff --git a/pb_migrations/1757211840_updated_matches.js b/pb_migrations/1757211840_updated_matches.js new file mode 100644 index 0000000..73755aa --- /dev/null +++ b/pb_migrations/1757211840_updated_matches.js @@ -0,0 +1,44 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_2541054544") + + // add field + collection.fields.addAt(18, new Field({ + "hidden": false, + "id": "number1705071305", + "max": null, + "min": null, + "name": "home_seed", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + })) + + // add field + collection.fields.addAt(19, new Field({ + "hidden": false, + "id": "number3588777624", + "max": null, + "min": null, + "name": "away_seed", + "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("number1705071305") + + // remove field + collection.fields.removeById("number3588777624") + + return app.save(collection) +}) diff --git a/pb_migrations/1757211934_updated_tournaments.js b/pb_migrations/1757211934_updated_tournaments.js new file mode 100644 index 0000000..a5d31c6 --- /dev/null +++ b/pb_migrations/1757211934_updated_tournaments.js @@ -0,0 +1,28 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_340646327") + + // add field + collection.fields.addAt(10, new Field({ + "cascadeDelete": false, + "collectionId": "pbc_2541054544", + "hidden": false, + "id": "relation103159226", + "maxSelect": 999, + "minSelect": 0, + "name": "matches", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + })) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_340646327") + + // remove field + collection.fields.removeById("relation103159226") + + return app.save(collection) +}) diff --git a/src/app/routes/_authed/admin/preview.tsx b/src/app/routes/_authed/admin/preview.tsx index 629824a..b13c1d2 100644 --- a/src/app/routes/_authed/admin/preview.tsx +++ b/src/app/routes/_authed/admin/preview.tsx @@ -1,4 +1,4 @@ -import BracketPreview from "@/features/bracket/components/preview"; +import BracketPreview from "@/features/admin/components/preview"; import { NumberInput } from "@mantine/core"; import { createFileRoute } from "@tanstack/react-router"; import { useState } from "react"; diff --git a/src/app/routes/_authed/admin/tournaments/run.$id.tsx b/src/app/routes/_authed/admin/tournaments/run.$id.tsx index 5a864b4..d348674 100644 --- a/src/app/routes/_authed/admin/tournaments/run.$id.tsx +++ b/src/app/routes/_authed/admin/tournaments/run.$id.tsx @@ -1,6 +1,9 @@ -import { createFileRoute, redirect } from '@tanstack/react-router' +import { createFileRoute, redirect, useRouter } from '@tanstack/react-router' import { tournamentQueries } 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' +import { Info } from '@phosphor-icons/react' export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({ beforeLoad: async ({ context, params }) => { @@ -25,6 +28,30 @@ export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({ }) function RouteComponent() { - const { id } = Route.useParams() - return

Run tournament

+ const { tournament } = Route.useRouteContext() + const router = useRouter() + + const handleSuccess = () => { + router.navigate({ + to: '/admin/tournaments/$id', + params: { id: tournament.id } + }) + } + + console.log('Tournament:', tournament) + + return ( + + { + tournament.matches?.length ? +

Matches

+ : ( + ) + } +
+ ) } diff --git a/src/features/_bracket/components/bracket-round.tsx b/src/features/_bracket/components/bracket-round.tsx deleted file mode 100644 index af770ac..0000000 --- a/src/features/_bracket/components/bracket-round.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Flex, Text } from '@mantine/core'; -import React from 'react'; -import { MatchCard } from './match-card'; -import { Match } from '../types'; - -interface BracketRoundProps { - matches: Match[]; - roundIndex: number; - getParentMatchOrder: (parentLid: number) => number | string; - onAnnounce?: (teamOne: any, teamTwo: any) => void; -} - -export const BracketRound: React.FC = ({ - matches, - roundIndex, - getParentMatchOrder, - onAnnounce, -}) => { - const isBye = (type: string) => type?.toLowerCase() === 'bye'; - - return ( - - {matches.map((match, matchIndex) => { - if (!match) return null; - if (isBye(match.type)) return <>; // for spacing - - return ( - - - {match.order} - - - - ); - })} - - ); -}; \ No newline at end of file diff --git a/src/features/_bracket/components/bracket-view.tsx b/src/features/_bracket/components/bracket-view.tsx deleted file mode 100644 index 2f337bb..0000000 --- a/src/features/_bracket/components/bracket-view.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Flex } from '@mantine/core'; -import React, { useCallback } from 'react'; -import { BracketMaps } from '../utils/bracket-maps'; -import { BracketRound } from './bracket-round'; -import { Match } from '../types'; - -interface BracketViewProps { - bracket: Match[][]; - bracketMaps: BracketMaps; - onAnnounce?: (teamOne: any, teamTwo: any) => void; -} - -const BracketView: React.FC = ({ - bracket, - bracketMaps, - onAnnounce, -}) => { - - const getParentMatchOrder = useCallback((parentLid: number): number | string => { - const parentMatch = bracketMaps.matchByLid.get(parentLid); - if ( - parentMatch && - parentMatch.order !== null && - parentMatch.order !== undefined - ) { - return parentMatch.order; - } - return `Match ${parentLid}`; - }, [bracketMaps]); - - return ( - - {bracket.map((round, roundIndex) => ( - - ))} - - ); -}; - -export default BracketView; \ No newline at end of file diff --git a/src/features/_bracket/components/bracket.tsx b/src/features/_bracket/components/bracket.tsx deleted file mode 100644 index af7db61..0000000 --- a/src/features/_bracket/components/bracket.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ScrollArea, Text } from "@mantine/core"; -import BracketView from "./bracket-view"; -import useAppShellHeight from "@/hooks/use-appshell-height"; -import { BracketMaps } from "../utils/bracket-maps"; -import { Match } from "@/features/matches/types"; - -interface BracketProps { - winners: Match[][]; - losers?: Match[][]; - bracketMaps: BracketMaps | null; -} - -const Bracket: React.FC = ({ winners, losers, bracketMaps }) => { - const height = useAppShellHeight(); - - if (!bracketMaps) return

Bracket not available.

; - - return ( - -
- - Winners Bracket - - -
- {losers && ( -
- - Losers Bracket - - -
- )} -
- ); -}; - -export default Bracket; diff --git a/src/features/_bracket/components/match-card.tsx b/src/features/_bracket/components/match-card.tsx deleted file mode 100644 index 1bde010..0000000 --- a/src/features/_bracket/components/match-card.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { ActionIcon, Card, Text } from '@mantine/core'; -import { PlayIcon } from '@phosphor-icons/react'; -import React, { useCallback, useMemo } from 'react'; -import { MatchSlot } from './match-slot'; -import { Match } from '../types'; - -interface MatchCardProps { - match: Match; - getParentMatchOrder: (parentLid: number) => number | string; - onAnnounce?: (teamOne: any, teamTwo: any) => void; -} - -export const MatchCard: React.FC = ({ - match, - getParentMatchOrder, - onAnnounce -}) => { - - const showAnnounce = useMemo(() => - onAnnounce && match.home.team && match.away.team, - [onAnnounce, match.home.team, match.away.team]); - - const handleAnnounce = useCallback(() => - onAnnounce?.(match.home.team, match.away.team), [match.home.team, match.away.team]); - - return ( - - - - - - - - - - {match.reset && ( - - * If necessary - - )} - - {showAnnounce && ( - - - - )} - - ); -}; \ No newline at end of file diff --git a/src/features/_bracket/components/match-slot.tsx b/src/features/_bracket/components/match-slot.tsx deleted file mode 100644 index 7babadc..0000000 --- a/src/features/_bracket/components/match-slot.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Flex, Text } from "@mantine/core"; -import React from "react"; -import { SeedBadge } from "./seed-badge"; - -interface MatchSlotProps { - slot: any; - getParentMatchOrder: (parentLid: number) => number | string; -} - -export const MatchSlot: React.FC = ({ - slot, - getParentMatchOrder, -}) => { - const renderSlotContent = () => { - if (slot?.seed) { - return slot.team ? ( - {slot.team.name} - ) : ( - - Team {slot.seed} - - ); - } - - if (slot?.parent_lid !== null && slot?.parent_lid !== undefined) { - return ( - - {slot.loser ? "Loser" : "Winner"} of Match{" "} - {getParentMatchOrder(slot.parent_lid)} - - ); - } - - if (slot) { - return ( - - TBD - - ); - } - - return null; - }; - - return ( - - {slot?.seed && } -
{renderSlotContent()}
-
- ); -}; diff --git a/src/features/_bracket/components/preview.tsx b/src/features/_bracket/components/preview.tsx deleted file mode 100644 index 3286dd0..0000000 --- a/src/features/_bracket/components/preview.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { - Text, - Container, - Flex, - NumberInput, - Group, - Loader, -} from "@mantine/core"; -import { useEffect, useState } from "react"; -import { bracketQueries, useBracketPreview } from "../queries"; -import { useQuery } from "@tanstack/react-query"; -import { createBracketMaps, BracketMaps } from "../utils/bracket-maps"; -import { BracketData, Match } from "../types"; -import Bracket from "./bracket"; -import "./styles.module.css"; - -interface PreviewTeam { - id: string; - name: string; -} - -export const PreviewBracket: React.FC = () => { - const [teamCount, setTeamCount] = useState(20); - const { data, isLoading, error } = useBracketPreview(teamCount); - - const [teams, setTeams] = useState([]); - - useEffect(() => { - setTeams( - Array.from({ length: teamCount }, (_, i) => ({ - id: `team-${i + 1}`, - name: `Team ${i + 1}`, - })) - ); - }, [teamCount]); - - const [seededWinnersBracket, setSeededWinnersBracket] = useState( - [] - ); - const [seededLosersBracket, setSeededLosersBracket] = useState([]); - const [bracketMaps, setBracketMaps] = useState(null); - - useEffect(() => { - if (!data || teams.length === 0) return; - - const maps = createBracketMaps(data); - setBracketMaps(maps); - - const mapBracket = (bracket: Match[][]) => { - return bracket.map((round) => - round.map((match) => { - const mappedMatch = { ...match }; - - if (match.home?.seed && match.home.seed > 0) { - const teamIndex = match.home.seed - 1; - if (teams[teamIndex]) { - mappedMatch.home = { - ...match.home, - team: teams[teamIndex], - }; - } - } - - if (match.away?.seed && match.away.seed > 0) { - const teamIndex = match.away.seed - 1; - if (teams[teamIndex]) { - mappedMatch.away = { - ...match.away, - team: teams[teamIndex], - }; - } - } - - return mappedMatch; - }) - ); - }; - - const bracketData = data as BracketData; - setSeededWinnersBracket(mapBracket(bracketData.winners)); - setSeededLosersBracket(mapBracket(bracketData.losers)); - }, [teams, data]); - - if (error) return

Error loading bracket

; - - return ( - - - - - Teams: - - setTeamCount(Number(value) || 12)} - min={12} - max={20} - size="sm" - w={80} - allowDecimal={false} - clampBehavior="strict" - /> - - - - {isLoading ? ( - - - - ) : ( - - )} - - - ); -}; diff --git a/src/features/_bracket/components/seed-badge.tsx b/src/features/_bracket/components/seed-badge.tsx deleted file mode 100644 index 52eb316..0000000 --- a/src/features/_bracket/components/seed-badge.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Text } from "@mantine/core"; -import React from "react"; - -interface SeedBadgeProps { - seed: number; -} - -export const SeedBadge: React.FC = ({ seed }) => { - return ( - - {seed} - - ); -}; diff --git a/src/features/_bracket/components/seed-list.tsx b/src/features/_bracket/components/seed-list.tsx deleted file mode 100644 index 89744e3..0000000 --- a/src/features/_bracket/components/seed-list.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Flex, Text, Select, Card } from "@mantine/core"; - -interface Team { - id: string; - name: string; -} - -interface SeedListProps { - teams: Team[]; - onSeedChange: (currentIndex: number, newIndex: number) => void; -} - -export function SeedList({ teams, onSeedChange }: SeedListProps) { - const seedOptions = teams.map((_, index) => ({ - value: index.toString(), - label: `Seed ${index + 1}`, - })); - - return ( - - {teams.map((team, index) => ( - - - -