some changes
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { Text, ScrollArea } from "@mantine/core";
|
||||
import { MatchCard } from "./match-card";
|
||||
import { BracketData } from "../types";
|
||||
import { Bracket } from "./bracket";
|
||||
import useAppShellHeight from "@/hooks/use-appshell-height";
|
||||
import { Match } from "@/features/matches/types";
|
||||
|
||||
interface BracketViewProps {
|
||||
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 orders = useMemo(() => {
|
||||
const map: Record<number, number> = {};
|
||||
@@ -32,14 +32,14 @@ const BracketView: React.FC<BracketViewProps> = ({ bracket, onAnnounce }) => {
|
||||
<Text fw={600} size="md" m={16}>
|
||||
Winners Bracket
|
||||
</Text>
|
||||
<Bracket rounds={bracket.winners} orders={orders} onAnnounce={onAnnounce} />
|
||||
<Bracket rounds={bracket.winners} orders={orders} onStartMatch={onStartMatch} />
|
||||
</div>
|
||||
{bracket.losers && (
|
||||
<div>
|
||||
<Text fw={600} size="md" m={16}>
|
||||
Losers Bracket
|
||||
</Text>
|
||||
<Bracket rounds={bracket.losers} orders={orders} onAnnounce={onAnnounce} />
|
||||
<Bracket rounds={bracket.losers} orders={orders} onStartMatch={onStartMatch} />
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
|
||||
@@ -5,13 +5,13 @@ import { MatchCard } from "./match-card";
|
||||
interface BracketProps {
|
||||
rounds: Match[][];
|
||||
orders: Record<number, number>;
|
||||
onAnnounce?: (teamOne: any, teamTwo: any) => void;
|
||||
onStartMatch?: (match: Match) => void;
|
||||
}
|
||||
|
||||
export const Bracket: React.FC<BracketProps> = ({
|
||||
rounds,
|
||||
orders,
|
||||
onAnnounce,
|
||||
onStartMatch,
|
||||
}) => {
|
||||
return (
|
||||
<Flex direction="row" gap={24} justify="left" p="xl">
|
||||
@@ -32,7 +32,7 @@ export const Bracket: React.FC<BracketProps> = ({
|
||||
<MatchCard
|
||||
match={match}
|
||||
orders={orders}
|
||||
onAnnounce={onAnnounce}
|
||||
onStartMatch={onStartMatch}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,13 +7,13 @@ import { Match } from "@/features/matches/types";
|
||||
interface MatchCardProps {
|
||||
match: Match;
|
||||
orders: Record<number, number>;
|
||||
onAnnounce?: (teamOne: any, teamTwo: any) => void;
|
||||
onStartMatch?: (match: Match) => void;
|
||||
}
|
||||
|
||||
export const MatchCard: React.FC<MatchCardProps> = ({
|
||||
match,
|
||||
orders,
|
||||
onAnnounce,
|
||||
onStartMatch,
|
||||
}) => {
|
||||
const homeSlot = useMemo(
|
||||
() => ({
|
||||
@@ -35,13 +35,13 @@ export const MatchCard: React.FC<MatchCardProps> = ({
|
||||
);
|
||||
|
||||
const showToolbar = useMemo(
|
||||
() => match.home && match.away && onAnnounce,
|
||||
() => match.home && match.away && onStartMatch,
|
||||
[match.home, match.away]
|
||||
);
|
||||
|
||||
const handleAnnounce = useCallback(
|
||||
() => onAnnounce?.(match.home, match.away),
|
||||
[onAnnounce, match.home, match.away]
|
||||
() => onStartMatch?.(match),
|
||||
[onStartMatch, match]
|
||||
);
|
||||
|
||||
const handleEdit = useCallback(() => {
|
||||
|
||||
@@ -11,11 +11,11 @@ const BackButton = ({ offsetY }: BackButtonProps) => {
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{ cursor: 'pointer', zIndex: 1000, transform: `translateY(-${offsetY}px)` }}
|
||||
style={{ cursor: 'pointer', zIndex: 1000 }}
|
||||
onClick={() => router.history.back()}
|
||||
pos='absolute'
|
||||
left={{ base: 0, sm: 100, md: 200, lg: 300 }}
|
||||
m={20}
|
||||
left={16}
|
||||
top={0}
|
||||
>
|
||||
<ArrowLeftIcon weight='bold' size={20} />
|
||||
</Box>
|
||||
|
||||
@@ -13,15 +13,11 @@ const Header = ({ withBackButton, settingsLink, collapsed, title, scrollPosition
|
||||
}, [collapsed, scrollPosition.y]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{settingsLink && <SettingsButton to={settingsLink} offsetY={offsetY} />}
|
||||
{withBackButton && <BackButton offsetY={offsetY} />}
|
||||
<AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}>
|
||||
<Flex justify='center' align='center' h='100%' px='md'>
|
||||
<Title order={2}>{title}</Title>
|
||||
</Flex>
|
||||
</AppShell.Header>
|
||||
</>
|
||||
<AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}>
|
||||
<Flex justify='center' align='center' h='100%' px='md'>
|
||||
<Title order={2}>{title}</Title>
|
||||
</Flex>
|
||||
</AppShell.Header>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
mah='100%'
|
||||
pb={{ base: 70, md: 0 }}
|
||||
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}>
|
||||
<Page noPadding={!withPadding} fullWidth={fullWidth}>
|
||||
|
||||
@@ -12,11 +12,11 @@ const SettingsButton = ({ offsetY, to }: SettingButtonProps) => {
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{ cursor: 'pointer', zIndex: 1000, transform: `translateY(-${offsetY}px)` }}
|
||||
style={{ cursor: 'pointer', zIndex: 1000 }}
|
||||
onClick={() => navigate({ to })}
|
||||
pos='absolute'
|
||||
right={{ base: 0, sm: 100, md: 200, lg: 300 }}
|
||||
m={20}
|
||||
right={16}
|
||||
top={0}
|
||||
>
|
||||
<GearIcon weight='bold' size={20} />
|
||||
</Box>
|
||||
|
||||
@@ -31,8 +31,8 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
radius='md'
|
||||
>
|
||||
<Stack align='center' gap='xs' mb='md'>
|
||||
<TrophyIcon size={150} />
|
||||
<Title order={2} ta='center'>Welcome to FLXN</Title>
|
||||
<TrophyIcon size={75} />
|
||||
<Title order={4} ta='center'>Welcome to FLXN</Title>
|
||||
</Stack>
|
||||
{children}
|
||||
</Paper>
|
||||
|
||||
@@ -19,18 +19,15 @@ export const generateTournamentBracket = createServerFn()
|
||||
toServerResult(async () => {
|
||||
logger.info('Generating tournament bracket', { tournamentId, teamCount: orderedTeamIds.length });
|
||||
|
||||
// Get tournament with teams
|
||||
const tournament = await pbAdmin.getTournament(tournamentId);
|
||||
if (!tournament) {
|
||||
throw new Error('Tournament not found');
|
||||
}
|
||||
|
||||
// Check if tournament already has matches
|
||||
if (tournament.matches && tournament.matches.length > 0) {
|
||||
throw new Error('Tournament already has matches generated');
|
||||
}
|
||||
|
||||
// Get bracket template based on team count
|
||||
const teamCount = orderedTeamIds.length;
|
||||
if (!Object.keys(brackets).includes(teamCount.toString())) {
|
||||
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;
|
||||
|
||||
// Create seed to team mapping (index + 1 = seed)
|
||||
const seedToTeamId = new Map<number, string>();
|
||||
orderedTeamIds.forEach((teamId, index) => {
|
||||
seedToTeamId.set(index + 1, teamId);
|
||||
});
|
||||
|
||||
// Convert bracket template to match records
|
||||
const matchInputs: MatchInput[] = [];
|
||||
|
||||
// Process winners bracket
|
||||
bracketTemplate.winners.forEach((round: any[]) => {
|
||||
round.forEach((match: any) => {
|
||||
const matchInput: MatchInput = {
|
||||
@@ -67,7 +61,6 @@ export const generateTournamentBracket = createServerFn()
|
||||
tournament: tournamentId,
|
||||
};
|
||||
|
||||
// Assign teams based on seeds
|
||||
if (match.home_seed) {
|
||||
const teamId = seedToTeamId.get(match.home_seed);
|
||||
if (teamId) {
|
||||
@@ -88,7 +81,6 @@ export const generateTournamentBracket = createServerFn()
|
||||
});
|
||||
});
|
||||
|
||||
// Process losers bracket
|
||||
bracketTemplate.losers.forEach((round: any[]) => {
|
||||
round.forEach((match: any) => {
|
||||
const matchInput: MatchInput = {
|
||||
@@ -108,17 +100,12 @@ export const generateTournamentBracket = createServerFn()
|
||||
tournament: tournamentId,
|
||||
};
|
||||
|
||||
// Losers bracket matches don't start with teams
|
||||
// Teams come from winners bracket losses
|
||||
|
||||
matchInputs.push(matchInput);
|
||||
});
|
||||
});
|
||||
|
||||
// Create all matches
|
||||
const createdMatches = await pbAdmin.createMatches(matchInputs);
|
||||
|
||||
// Update tournament to include all match IDs in the matches relation
|
||||
const matchIds = createdMatches.map(match => match.id);
|
||||
await pbAdmin.updateTournamentMatches(tournamentId, matchIds);
|
||||
|
||||
@@ -133,4 +120,22 @@ export const generateTournamentBracket = createServerFn()
|
||||
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",
|
||||
content: <>
|
||||
<TeamList teams={tournament.teams || []} />
|
||||
<TeamList teams={tournament.teams || []} />
|
||||
<TeamList teams={tournament.teams || []} />
|
||||
<TeamList teams={tournament.teams || []} />
|
||||
</>
|
||||
}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user