init
This commit is contained in:
44
src/features/tournaments/components/tournament-card.tsx
Normal file
44
src/features/tournaments/components/tournament-card.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Badge, Card, Text, Image, Stack, Flex } from "@mantine/core"
|
||||
import { Tournament } from "@/features/tournaments/types"
|
||||
import { useMemo } from "react"
|
||||
import { CaretRightIcon } from "@phosphor-icons/react"
|
||||
import { useNavigate } from "@tanstack/react-router"
|
||||
|
||||
interface TournamentCardProps {
|
||||
tournament: Tournament
|
||||
}
|
||||
|
||||
export const TournamentCard = ({ tournament }: TournamentCardProps) => {
|
||||
const navigate = useNavigate({ from: '/tournaments/$tournamentId' })
|
||||
const date = useMemo(() => new Date(tournament.start_time), [tournament?.start_time])
|
||||
const year = useMemo(() => date.getFullYear(), [date])
|
||||
const month = useMemo(() => date.getMonth(), [date])
|
||||
const monthName = useMemo(() => new Date(date.getFullYear(), month, 1).toLocaleString('default', { month: 'long' }), [date])
|
||||
const day = useMemo(() => date.getDate(), [date])
|
||||
|
||||
return (
|
||||
<Card shadow="sm" padding="lg" radius="md" withBorder style={{ cursor: 'pointer' }} onClick={() => navigate({ to: `/tournaments/${tournament.id}` })}>
|
||||
<Stack>
|
||||
<Flex align='center' gap='md'>
|
||||
<Image
|
||||
src={tournament.logo_url}
|
||||
maw={100}
|
||||
mah={100}
|
||||
fit='contain'
|
||||
alt={tournament.name}
|
||||
/>
|
||||
<Stack ta='center' mx='auto' gap='0'>
|
||||
<Text size='lg' fw={800}>{tournament.name} <CaretRightIcon size={12} weight='bold' /></Text>
|
||||
<Text c='dimmed' size='xs' fw={600}>{monthName} {day}, {year}</Text>
|
||||
<Stack gap={4} mt={4}>
|
||||
{ /* TODO: Add medalists when data is available */}
|
||||
<Badge variant='dot' color='gold'>Longer Team Name Goes Here</Badge>
|
||||
<Badge variant='dot' color='silver'>Some Team</Badge>
|
||||
<Badge variant='dot' color='orange'>Medium Team Name</Badge>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
3
src/features/tournaments/index.ts
Normal file
3
src/features/tournaments/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Logger } from "@/lib/logger";
|
||||
|
||||
export const logger = new Logger('Tournaments');
|
||||
18
src/features/tournaments/queries.ts
Normal file
18
src/features/tournaments/queries.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { queryOptions, useQuery } from "@tanstack/react-query";
|
||||
import { getTournament, listTournaments } from "./server";
|
||||
|
||||
const tournamentKeys = {
|
||||
list: ['tournaments'] as const,
|
||||
details: (id: string) => [...tournamentKeys.list, id] as const,
|
||||
};
|
||||
|
||||
export const tournamentQueries = {
|
||||
list: () => queryOptions({
|
||||
queryKey: tournamentKeys.list,
|
||||
queryFn: listTournaments,
|
||||
}),
|
||||
details: (id: string) => queryOptions({
|
||||
queryKey: tournamentKeys.details(id),
|
||||
queryFn: () => getTournament({ data: id }),
|
||||
}),
|
||||
};
|
||||
43
src/features/tournaments/server.ts
Normal file
43
src/features/tournaments/server.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { superTokensAdminFunctionMiddleware, superTokensFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { tournamentInputSchema } from "@/features/tournaments/types";
|
||||
import { logger } from ".";
|
||||
import { z } from "zod";
|
||||
|
||||
export const listTournaments = createServerFn()
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.handler(async () => {
|
||||
try {
|
||||
const result = await pbAdmin.listTournaments();
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('Error fetching tournaments', error);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
export const createTournament = createServerFn()
|
||||
.validator(tournamentInputSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.handler(async ({ data }) => {
|
||||
try {
|
||||
logger.info('Creating tournament', data);
|
||||
|
||||
const tournament = await pbAdmin.createTournament(data);
|
||||
|
||||
return tournament;
|
||||
} catch (error) {
|
||||
logger.error('Error creating tournament', error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export const getTournament = createServerFn()
|
||||
.validator(z.string())
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.handler(async ({ data: tournamentId }) => {
|
||||
logger.info('Getting tournament', tournamentId);
|
||||
const tournament = await pbAdmin.getTournament(tournamentId);
|
||||
return tournament;
|
||||
});
|
||||
45
src/features/tournaments/types.ts
Normal file
45
src/features/tournaments/types.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Team } from "@/features/teams/types";
|
||||
import { z } from "zod";
|
||||
|
||||
export interface Tournament {
|
||||
id: string;
|
||||
name: string;
|
||||
location?: string;
|
||||
desc?: string;
|
||||
rules?: string;
|
||||
logo_url?: string;
|
||||
enroll_time?: string;
|
||||
start_time: string;
|
||||
end_time?: string;
|
||||
created: string;
|
||||
updated: string;
|
||||
teams?: Team[];
|
||||
}
|
||||
|
||||
// Schema for the form (client-side)
|
||||
export const tournamentFormSchema = z.object({
|
||||
name: z.string(),
|
||||
location: z.string().optional(),
|
||||
desc: z.string().optional(),
|
||||
rules: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
enroll_time: z.string(),
|
||||
start_time: z.string(),
|
||||
end_time: z.string().optional(),
|
||||
});
|
||||
|
||||
// Schema for the server input (with base64 logo)
|
||||
export const tournamentInputSchema = z.object({
|
||||
name: z.string(),
|
||||
location: z.string().optional(),
|
||||
desc: z.string().optional(),
|
||||
rules: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
enroll_time: z.string(),
|
||||
start_time: z.string(),
|
||||
end_time: z.string().optional(),
|
||||
});
|
||||
|
||||
export type TournamentFormInput = z.infer<typeof tournamentFormSchema>;
|
||||
export type TournamentInput = z.infer<typeof tournamentInputSchema>;
|
||||
export type TournamentUpdateInput = Partial<TournamentInput>;
|
||||
Reference in New Issue
Block a user