some changes
This commit is contained in:
16
.nitro/types/nitro-config.d.ts
vendored
Normal file
16
.nitro/types/nitro-config.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Generated by nitro
|
||||||
|
|
||||||
|
// App Config
|
||||||
|
import type { Defu } from 'defu'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type UserAppConfig = Defu<{}, []>
|
||||||
|
|
||||||
|
declare module "nitropack/types" {
|
||||||
|
interface AppConfig extends UserAppConfig {}
|
||||||
|
interface NitroRuntimeConfig {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {}
|
||||||
1
.nitro/types/nitro-imports.d.ts
vendored
Normal file
1
.nitro/types/nitro-imports.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export {}
|
||||||
8
.nitro/types/nitro-routes.d.ts
vendored
Normal file
8
.nitro/types/nitro-routes.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Generated by nitro
|
||||||
|
import type { Serialize, Simplify } from "nitropack/types";
|
||||||
|
declare module "nitropack/types" {
|
||||||
|
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
||||||
|
interface InternalApi {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {}
|
||||||
3
.nitro/types/nitro.d.ts
vendored
Normal file
3
.nitro/types/nitro.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference path="./nitro-routes.d.ts" />
|
||||||
|
/// <reference path="./nitro-config.d.ts" />
|
||||||
|
/// <reference path="./nitro-imports.d.ts" />
|
||||||
@@ -39,30 +39,24 @@ function RouteComponent() {
|
|||||||
if (!tournament.matches || tournament.matches.length === 0) {
|
if (!tournament.matches || tournament.matches.length === 0) {
|
||||||
return { winners: [], losers: [] }
|
return { winners: [], losers: [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Tournament Matches:', tournament.matches)
|
|
||||||
|
|
||||||
const winnersMap = new Map<number, Match[]>()
|
const winnersMap = new Map<number, Match[]>()
|
||||||
const losersMap = new Map<number, Match[]>()
|
const losersMap = new Map<number, Match[]>()
|
||||||
|
|
||||||
tournament.matches.sort((a, b) => a.lid - b.lid).forEach((match) => {
|
tournament.matches.sort((a, b) => a.lid - b.lid).forEach((match) => {
|
||||||
console.log('Processing Match:', match)
|
|
||||||
if (!match.is_losers_bracket) {
|
if (!match.is_losers_bracket) {
|
||||||
if (!winnersMap.has(match.round)) {
|
if (!winnersMap.has(match.round)) {
|
||||||
winnersMap.set(match.round, [])
|
winnersMap.set(match.round, [])
|
||||||
}
|
}
|
||||||
winnersMap.get(match.round)!.push(match)
|
winnersMap.get(match.round)!.push(match)
|
||||||
console.log('Added to Winners Bracket:', match)
|
|
||||||
} else {
|
} else {
|
||||||
if (!losersMap.has(match.round)) {
|
if (!losersMap.has(match.round)) {
|
||||||
losersMap.set(match.round, [])
|
losersMap.set(match.round, [])
|
||||||
}
|
}
|
||||||
losersMap.get(match.round)!.push(match)
|
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())
|
const winners = Array.from(winnersMap.entries())
|
||||||
.sort(([a], [b]) => a - b)
|
.sort(([a], [b]) => a - b)
|
||||||
.map(([, matches]) => matches)
|
.map(([, matches]) => matches)
|
||||||
@@ -70,15 +64,10 @@ function RouteComponent() {
|
|||||||
const losers = Array.from(losersMap.entries())
|
const losers = Array.from(losersMap.entries())
|
||||||
.sort(([a], [b]) => a - b)
|
.sort(([a], [b]) => a - b)
|
||||||
.map(([, matches]) => matches)
|
.map(([, matches]) => matches)
|
||||||
|
|
||||||
console.log('Winners Bracket (fixed):', winners)
|
|
||||||
console.log('Losers Bracket (fixed):', losers)
|
|
||||||
return { winners, losers }
|
return { winners, losers }
|
||||||
}, [tournament.matches])
|
}, [tournament.matches])
|
||||||
|
|
||||||
|
|
||||||
console.log('Bracket Data:', bracket)
|
|
||||||
|
|
||||||
const handleSuccess = () => {
|
const handleSuccess = () => {
|
||||||
router.navigate({
|
router.navigate({
|
||||||
to: '/admin/tournaments/$id',
|
to: '/admin/tournaments/$id',
|
||||||
@@ -86,13 +75,15 @@ function RouteComponent() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Tournament:', tournament)
|
const handleStartMatch = (match: Match) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container size="md">
|
<Container size="md">
|
||||||
{
|
{
|
||||||
tournament.matches?.length ?
|
tournament.matches?.length ?
|
||||||
<BracketView bracket={bracket} onAnnounce={console.log} />
|
<BracketView bracket={bracket} onStartMatch={console.log} />
|
||||||
: (
|
: (
|
||||||
<SeedTournament
|
<SeedTournament
|
||||||
tournamentId={tournament.id}
|
tournamentId={tournament.id}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Container, ContainerProps } from "@mantine/core";
|
import { Container, ContainerProps, Box } from "@mantine/core";
|
||||||
import useRouterConfig from "@/features/core/hooks/use-router-config";
|
import useRouterConfig from "@/features/core/hooks/use-router-config";
|
||||||
|
import BackButton from "@/features/core/components/back-button";
|
||||||
|
import SettingsButton from "@/features/core/components/settings-button";
|
||||||
|
|
||||||
interface PageProps extends ContainerProps, React.PropsWithChildren {
|
interface PageProps extends ContainerProps, React.PropsWithChildren {
|
||||||
noPadding?: boolean;
|
noPadding?: boolean;
|
||||||
@@ -16,8 +18,15 @@ const Page = ({ children, noPadding, fullWidth, ...props }: PageProps) => {
|
|||||||
m={0}
|
m={0}
|
||||||
maw={fullWidth ? '100%' : 600}
|
maw={fullWidth ? '100%' : 600}
|
||||||
mx="auto"
|
mx="auto"
|
||||||
|
pos="relative"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
{header.collapsed && header.withBackButton && (
|
||||||
|
<BackButton offsetY={0} />
|
||||||
|
)}
|
||||||
|
{header.collapsed && header.settingsLink && (
|
||||||
|
<SettingsButton to={header.settingsLink} offsetY={0} />
|
||||||
|
)}
|
||||||
{children}
|
{children}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ function SwipeableTabs({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box style={{ touchAction: "pan-y" }}>
|
||||||
<Box
|
<Box
|
||||||
ref={setRootRef}
|
ref={setRootRef}
|
||||||
pos="sticky"
|
pos="sticky"
|
||||||
@@ -137,7 +137,7 @@ function SwipeableTabs({
|
|||||||
onClick={() => changeTab(index)}
|
onClick={() => changeTab(index)}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: "var(--mantine-spacing-sm) var(--mantine-spacing-md)",
|
padding: "var(--mantine-spacing-sm) var(--mantine-spacing-xs)",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color:
|
color:
|
||||||
activeTab === index
|
activeTab === index
|
||||||
@@ -155,7 +155,7 @@ function SwipeableTabs({
|
|||||||
component="span"
|
component="span"
|
||||||
style={{
|
style={{
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
paddingInline: "1rem",
|
paddingInline: "0.5rem",
|
||||||
paddingBottom: "0.25rem",
|
paddingBottom: "0.25rem",
|
||||||
}}
|
}}
|
||||||
ref={setControlRef(index)}
|
ref={setControlRef(index)}
|
||||||
@@ -176,6 +176,7 @@ function SwipeableTabs({
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
height: carouselHeight === "auto" ? "auto" : `${carouselHeight}px`,
|
height: carouselHeight === "auto" ? "auto" : `${carouselHeight}px`,
|
||||||
transition: "height 300ms ease",
|
transition: "height 300ms ease",
|
||||||
|
touchAction: "pan-y",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { Text, ScrollArea } from "@mantine/core";
|
import { Text, ScrollArea } from "@mantine/core";
|
||||||
import { MatchCard } from "./match-card";
|
|
||||||
import { BracketData } from "../types";
|
import { BracketData } from "../types";
|
||||||
import { Bracket } from "./bracket";
|
import { Bracket } from "./bracket";
|
||||||
import useAppShellHeight from "@/hooks/use-appshell-height";
|
import useAppShellHeight from "@/hooks/use-appshell-height";
|
||||||
|
import { Match } from "@/features/matches/types";
|
||||||
|
|
||||||
interface BracketViewProps {
|
interface BracketViewProps {
|
||||||
bracket: BracketData;
|
bracket: BracketData;
|
||||||
onAnnounce?: (teamOne: any, teamTwo: any) => void;
|
onStartMatch?: (match: Match) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BracketView: React.FC<BracketViewProps> = ({ bracket, onAnnounce }) => {
|
const BracketView: React.FC<BracketViewProps> = ({ bracket, onStartMatch }) => {
|
||||||
const height = useAppShellHeight();
|
const height = useAppShellHeight();
|
||||||
const orders = useMemo(() => {
|
const orders = useMemo(() => {
|
||||||
const map: Record<number, number> = {};
|
const map: Record<number, number> = {};
|
||||||
@@ -32,14 +32,14 @@ const BracketView: React.FC<BracketViewProps> = ({ bracket, onAnnounce }) => {
|
|||||||
<Text fw={600} size="md" m={16}>
|
<Text fw={600} size="md" m={16}>
|
||||||
Winners Bracket
|
Winners Bracket
|
||||||
</Text>
|
</Text>
|
||||||
<Bracket rounds={bracket.winners} orders={orders} onAnnounce={onAnnounce} />
|
<Bracket rounds={bracket.winners} orders={orders} onStartMatch={onStartMatch} />
|
||||||
</div>
|
</div>
|
||||||
{bracket.losers && (
|
{bracket.losers && (
|
||||||
<div>
|
<div>
|
||||||
<Text fw={600} size="md" m={16}>
|
<Text fw={600} size="md" m={16}>
|
||||||
Losers Bracket
|
Losers Bracket
|
||||||
</Text>
|
</Text>
|
||||||
<Bracket rounds={bracket.losers} orders={orders} onAnnounce={onAnnounce} />
|
<Bracket rounds={bracket.losers} orders={orders} onStartMatch={onStartMatch} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { MatchCard } from "./match-card";
|
|||||||
interface BracketProps {
|
interface BracketProps {
|
||||||
rounds: Match[][];
|
rounds: Match[][];
|
||||||
orders: Record<number, number>;
|
orders: Record<number, number>;
|
||||||
onAnnounce?: (teamOne: any, teamTwo: any) => void;
|
onStartMatch?: (match: Match) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Bracket: React.FC<BracketProps> = ({
|
export const Bracket: React.FC<BracketProps> = ({
|
||||||
rounds,
|
rounds,
|
||||||
orders,
|
orders,
|
||||||
onAnnounce,
|
onStartMatch,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Flex direction="row" gap={24} justify="left" p="xl">
|
<Flex direction="row" gap={24} justify="left" p="xl">
|
||||||
@@ -32,7 +32,7 @@ export const Bracket: React.FC<BracketProps> = ({
|
|||||||
<MatchCard
|
<MatchCard
|
||||||
match={match}
|
match={match}
|
||||||
orders={orders}
|
orders={orders}
|
||||||
onAnnounce={onAnnounce}
|
onStartMatch={onStartMatch}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import { Match } from "@/features/matches/types";
|
|||||||
interface MatchCardProps {
|
interface MatchCardProps {
|
||||||
match: Match;
|
match: Match;
|
||||||
orders: Record<number, number>;
|
orders: Record<number, number>;
|
||||||
onAnnounce?: (teamOne: any, teamTwo: any) => void;
|
onStartMatch?: (match: Match) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MatchCard: React.FC<MatchCardProps> = ({
|
export const MatchCard: React.FC<MatchCardProps> = ({
|
||||||
match,
|
match,
|
||||||
orders,
|
orders,
|
||||||
onAnnounce,
|
onStartMatch,
|
||||||
}) => {
|
}) => {
|
||||||
const homeSlot = useMemo(
|
const homeSlot = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -35,13 +35,13 @@ export const MatchCard: React.FC<MatchCardProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const showToolbar = useMemo(
|
const showToolbar = useMemo(
|
||||||
() => match.home && match.away && onAnnounce,
|
() => match.home && match.away && onStartMatch,
|
||||||
[match.home, match.away]
|
[match.home, match.away]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAnnounce = useCallback(
|
const handleAnnounce = useCallback(
|
||||||
() => onAnnounce?.(match.home, match.away),
|
() => onStartMatch?.(match),
|
||||||
[onAnnounce, match.home, match.away]
|
[onStartMatch, match]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleEdit = useCallback(() => {
|
const handleEdit = useCallback(() => {
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ const BackButton = ({ offsetY }: BackButtonProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
style={{ cursor: 'pointer', zIndex: 1000, transform: `translateY(-${offsetY}px)` }}
|
style={{ cursor: 'pointer', zIndex: 1000 }}
|
||||||
onClick={() => router.history.back()}
|
onClick={() => router.history.back()}
|
||||||
pos='absolute'
|
pos='absolute'
|
||||||
left={{ base: 0, sm: 100, md: 200, lg: 300 }}
|
left={16}
|
||||||
m={20}
|
top={0}
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon weight='bold' size={20} />
|
<ArrowLeftIcon weight='bold' size={20} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -13,15 +13,11 @@ const Header = ({ withBackButton, settingsLink, collapsed, title, scrollPosition
|
|||||||
}, [collapsed, scrollPosition.y]);
|
}, [collapsed, scrollPosition.y]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}>
|
||||||
{settingsLink && <SettingsButton to={settingsLink} offsetY={offsetY} />}
|
<Flex justify='center' align='center' h='100%' px='md'>
|
||||||
{withBackButton && <BackButton offsetY={offsetY} />}
|
<Title order={2}>{title}</Title>
|
||||||
<AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}>
|
</Flex>
|
||||||
<Flex justify='center' align='center' h='100%' px='md'>
|
</AppShell.Header>
|
||||||
<Title order={2}>{title}</Title>
|
|
||||||
</Flex>
|
|
||||||
</AppShell.Header>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
|||||||
mah='100%'
|
mah='100%'
|
||||||
pb={{ base: 70, md: 0 }}
|
pb={{ base: 70, md: 0 }}
|
||||||
px={{ base: 0.01, sm: 100, md: 200, lg: 300 }}
|
px={{ base: 0.01, sm: 100, md: 200, lg: 300 }}
|
||||||
style={{ transition: 'none' }}
|
maw='100dvw'
|
||||||
|
style={{ transition: 'none', overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
<Pullable scrollPosition={scrollPosition} onScrollPositionChange={setScrollPosition}>
|
<Pullable scrollPosition={scrollPosition} onScrollPositionChange={setScrollPosition}>
|
||||||
<Page noPadding={!withPadding} fullWidth={fullWidth}>
|
<Page noPadding={!withPadding} fullWidth={fullWidth}>
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ const SettingsButton = ({ offsetY, to }: SettingButtonProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
style={{ cursor: 'pointer', zIndex: 1000, transform: `translateY(-${offsetY}px)` }}
|
style={{ cursor: 'pointer', zIndex: 1000 }}
|
||||||
onClick={() => navigate({ to })}
|
onClick={() => navigate({ to })}
|
||||||
pos='absolute'
|
pos='absolute'
|
||||||
right={{ base: 0, sm: 100, md: 200, lg: 300 }}
|
right={16}
|
||||||
m={20}
|
top={0}
|
||||||
>
|
>
|
||||||
<GearIcon weight='bold' size={20} />
|
<GearIcon weight='bold' size={20} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
|||||||
radius='md'
|
radius='md'
|
||||||
>
|
>
|
||||||
<Stack align='center' gap='xs' mb='md'>
|
<Stack align='center' gap='xs' mb='md'>
|
||||||
<TrophyIcon size={150} />
|
<TrophyIcon size={75} />
|
||||||
<Title order={2} ta='center'>Welcome to FLXN</Title>
|
<Title order={4} ta='center'>Welcome to FLXN</Title>
|
||||||
</Stack>
|
</Stack>
|
||||||
{children}
|
{children}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -19,18 +19,15 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
toServerResult(async () => {
|
toServerResult(async () => {
|
||||||
logger.info('Generating tournament bracket', { tournamentId, teamCount: orderedTeamIds.length });
|
logger.info('Generating tournament bracket', { tournamentId, teamCount: orderedTeamIds.length });
|
||||||
|
|
||||||
// Get tournament with teams
|
|
||||||
const tournament = await pbAdmin.getTournament(tournamentId);
|
const tournament = await pbAdmin.getTournament(tournamentId);
|
||||||
if (!tournament) {
|
if (!tournament) {
|
||||||
throw new Error('Tournament not found');
|
throw new Error('Tournament not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if tournament already has matches
|
|
||||||
if (tournament.matches && tournament.matches.length > 0) {
|
if (tournament.matches && tournament.matches.length > 0) {
|
||||||
throw new Error('Tournament already has matches generated');
|
throw new Error('Tournament already has matches generated');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get bracket template based on team count
|
|
||||||
const teamCount = orderedTeamIds.length;
|
const teamCount = orderedTeamIds.length;
|
||||||
if (!Object.keys(brackets).includes(teamCount.toString())) {
|
if (!Object.keys(brackets).includes(teamCount.toString())) {
|
||||||
throw new Error(`Bracket not available for ${teamCount} teams`);
|
throw new Error(`Bracket not available for ${teamCount} teams`);
|
||||||
@@ -38,16 +35,13 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
|
|
||||||
const bracketTemplate = brackets[teamCount as keyof typeof brackets] as any;
|
const bracketTemplate = brackets[teamCount as keyof typeof brackets] as any;
|
||||||
|
|
||||||
// Create seed to team mapping (index + 1 = seed)
|
|
||||||
const seedToTeamId = new Map<number, string>();
|
const seedToTeamId = new Map<number, string>();
|
||||||
orderedTeamIds.forEach((teamId, index) => {
|
orderedTeamIds.forEach((teamId, index) => {
|
||||||
seedToTeamId.set(index + 1, teamId);
|
seedToTeamId.set(index + 1, teamId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert bracket template to match records
|
|
||||||
const matchInputs: MatchInput[] = [];
|
const matchInputs: MatchInput[] = [];
|
||||||
|
|
||||||
// Process winners bracket
|
|
||||||
bracketTemplate.winners.forEach((round: any[]) => {
|
bracketTemplate.winners.forEach((round: any[]) => {
|
||||||
round.forEach((match: any) => {
|
round.forEach((match: any) => {
|
||||||
const matchInput: MatchInput = {
|
const matchInput: MatchInput = {
|
||||||
@@ -67,7 +61,6 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
tournament: tournamentId,
|
tournament: tournamentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assign teams based on seeds
|
|
||||||
if (match.home_seed) {
|
if (match.home_seed) {
|
||||||
const teamId = seedToTeamId.get(match.home_seed);
|
const teamId = seedToTeamId.get(match.home_seed);
|
||||||
if (teamId) {
|
if (teamId) {
|
||||||
@@ -88,7 +81,6 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process losers bracket
|
|
||||||
bracketTemplate.losers.forEach((round: any[]) => {
|
bracketTemplate.losers.forEach((round: any[]) => {
|
||||||
round.forEach((match: any) => {
|
round.forEach((match: any) => {
|
||||||
const matchInput: MatchInput = {
|
const matchInput: MatchInput = {
|
||||||
@@ -108,17 +100,12 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
tournament: tournamentId,
|
tournament: tournamentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Losers bracket matches don't start with teams
|
|
||||||
// Teams come from winners bracket losses
|
|
||||||
|
|
||||||
matchInputs.push(matchInput);
|
matchInputs.push(matchInput);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create all matches
|
|
||||||
const createdMatches = await pbAdmin.createMatches(matchInputs);
|
const createdMatches = await pbAdmin.createMatches(matchInputs);
|
||||||
|
|
||||||
// Update tournament to include all match IDs in the matches relation
|
|
||||||
const matchIds = createdMatches.map(match => match.id);
|
const matchIds = createdMatches.map(match => match.id);
|
||||||
await pbAdmin.updateTournamentMatches(tournamentId, matchIds);
|
await pbAdmin.updateTournamentMatches(tournamentId, matchIds);
|
||||||
|
|
||||||
@@ -133,4 +120,22 @@ export const generateTournamentBracket = createServerFn()
|
|||||||
matches: createdMatches,
|
matches: createdMatches,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const startMatch = createServerFn()
|
||||||
|
.validator(z.string())
|
||||||
|
.middleware([superTokensAdminFunctionMiddleware])
|
||||||
|
.handler(async ({ data }) =>
|
||||||
|
toServerResult(async () => {
|
||||||
|
logger.info('Starting match', data);
|
||||||
|
|
||||||
|
let match = await pbAdmin.getMatch(data);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error('Match not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
match = await pbAdmin.updateMatch(data, {
|
||||||
|
start_time: new Date().toISOString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
));
|
||||||
@@ -25,6 +25,9 @@ const Profile = ({ id }: ProfileProps) => {
|
|||||||
label: "Teams",
|
label: "Teams",
|
||||||
content: <>
|
content: <>
|
||||||
<TeamList teams={tournament.teams || []} />
|
<TeamList teams={tournament.teams || []} />
|
||||||
|
<TeamList teams={tournament.teams || []} />
|
||||||
|
<TeamList teams={tournament.teams || []} />
|
||||||
|
<TeamList teams={tournament.teams || []} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
import type { Match, MatchInput } from "@/features/matches/types";
|
import type { Match, MatchInput } from "@/features/matches/types";
|
||||||
import type PocketBase from "pocketbase";
|
import type PocketBase from "pocketbase";
|
||||||
|
import { transformMatch } from "../util/transform-types";
|
||||||
|
|
||||||
export function createMatchesService(pb: PocketBase) {
|
export function createMatchesService(pb: PocketBase) {
|
||||||
return {
|
return {
|
||||||
|
async getMatch(id: string): Promise<Match | null> {
|
||||||
|
logger.info("PocketBase | Getting match", id);
|
||||||
|
const result = await pb.collection("matches").getOne(id, {
|
||||||
|
expand: "tournament, home, away",
|
||||||
|
});
|
||||||
|
return transformMatch(result);
|
||||||
|
},
|
||||||
|
|
||||||
async createMatch(data: MatchInput): Promise<Match> {
|
async createMatch(data: MatchInput): Promise<Match> {
|
||||||
logger.info("PocketBase | Creating match", data);
|
logger.info("PocketBase | Creating match", data);
|
||||||
const result = await pb.collection("matches").create<Match>(data);
|
const result = await pb.collection("matches").create<Match>(data);
|
||||||
@@ -11,9 +20,11 @@ export function createMatchesService(pb: PocketBase) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async createMatches(matches: MatchInput[]): Promise<Match[]> {
|
async createMatches(matches: MatchInput[]): Promise<Match[]> {
|
||||||
logger.info("PocketBase | Creating multiple matches", { count: matches.length });
|
logger.info("PocketBase | Creating multiple matches", {
|
||||||
|
count: matches.length,
|
||||||
|
});
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
matches.map(match => pb.collection("matches").create<Match>(match))
|
matches.map((match) => pb.collection("matches").create<Match>(match))
|
||||||
);
|
);
|
||||||
return results;
|
return results;
|
||||||
},
|
},
|
||||||
@@ -30,10 +41,10 @@ export function createMatchesService(pb: PocketBase) {
|
|||||||
filter: `tournament = "${tournamentId}"`,
|
filter: `tournament = "${tournamentId}"`,
|
||||||
fields: "id",
|
fields: "id",
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
matches.map(match => pb.collection("matches").delete(match.id))
|
matches.map((match) => pb.collection("matches").delete(match.id))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user