Files
flxn-app/src/features/admin/components/create-tournament.tsx
2025-08-24 13:50:48 -05:00

154 lines
4.8 KiB
TypeScript

import { FileInput, Stack, TextInput, Textarea } from "@mantine/core";
import { useForm, UseFormInput } from "@mantine/form";
import { LinkIcon } from "@phosphor-icons/react";
import SlidePanel, { SlidePanelField } from "@/components/sheet/slide-panel";
import { TournamentFormInput } from "@/features/tournaments/types";
import { DateTimePicker } from "./date-time-picker";
import { isNotEmpty } from "@mantine/form";
import useCreateTournament from "../hooks/use-create-tournament";
import toast from '@/lib/sonner';
import { logger } from "..";
import { useQueryClient } from "@tanstack/react-query";
import { tournamentQueries } from "@/features/tournaments/queries";
const CreateTournament = ({ close }: { close: () => void }) => {
const config: UseFormInput<TournamentFormInput> = {
initialValues: { // TODO : Remove fake initial values
name: 'Test Tournament',
location: 'Test Location',
desc: 'Test Description',
start_time: '2025-01-01T00:00:00Z',
enroll_time: '2025-01-01T00:00:00Z',
},
onSubmitPreventDefault: 'always',
validate: {
name: isNotEmpty('Name is required'),
location: isNotEmpty('Location is required'),
start_time: isNotEmpty('Start time is required'),
enroll_time: isNotEmpty('Enrollment time is required'),
}
}
const form = useForm(config);
const queryClient = useQueryClient();
const { mutate: createTournament, isPending } = useCreateTournament();
const handleSubmit = async (values: TournamentFormInput) => {
const { logo, ...tournamentData } = values;
createTournament(tournamentData, {
onSuccess: async (tournament) => {
if (logo && tournament) {
try {
const formData = new FormData();
formData.append('tournamentId', tournament.id);
formData.append('logo', logo);
const response = await fetch('/api/tournaments/upload-logo', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to upload logo');
}
const result = await response.json();
queryClient.invalidateQueries({ queryKey: tournamentQueries.list().queryKey });
queryClient.setQueryData(
tournamentQueries.details(result.tournament!.id).queryKey,
result.tournament
);
toast.success('Tournament created successfully!');
} catch (error: any) {
toast.error(`Tournament created but logo upload failed: ${error.message}`);
logger.error('Tournament logo upload error', error);
}
}
close();
}
});
}
return (
<SlidePanel
onSubmit={form.onSubmit(handleSubmit)}
onCancel={close}
submitText="Create Tournament"
cancelText="Cancel"
loading={isPending}
>
<Stack>
<TextInput
label="Name"
withAsterisk
key={form.key('name')}
{...form.getInputProps('name')}
/>
<TextInput
label="Location"
withAsterisk
key={form.key('location')}
{...form.getInputProps('location')}
/>
<TextInput
label="Short Description"
key={form.key('desc')}
{...form.getInputProps('desc')}
/>
<FileInput
key={form.key('logo')}
accept="image/png,image/jpeg,image/gif,image/jpg"
label="Logo"
leftSection={<LinkIcon size={16} />}
{...form.getInputProps('logo')}
/>
<SlidePanelField
key={form.key('start_time')}
{...form.getInputProps('start_time')}
Component={DateTimePicker}
title="Select Start Date"
label="Start Date"
withAsterisk
formatValue={(date) => new Date(date).toLocaleDateString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
hour12: true
})}
/>
<SlidePanelField
key={form.key('enroll_time')}
{...form.getInputProps('enroll_time')}
Component={DateTimePicker}
title="Select Enrollment Due Date"
label="Enrollment Due"
withAsterisk
formatValue={(date) => new Date(date).toLocaleDateString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
hour12: true
})}
/>
</Stack>
</SlidePanel>
);
};
export default CreateTournament;