193 lines
6.2 KiB
TypeScript
193 lines
6.2 KiB
TypeScript
import { Box, FileInput, Group, Stack, TextInput, Textarea, Title } from "@mantine/core";
|
|
import { useForm, UseFormInput } from "@mantine/form";
|
|
import { LinkIcon } from "@phosphor-icons/react";
|
|
import { Tournament, TournamentFormInput } from "@/features/tournaments/types";
|
|
import { DateInputSheet } from "../../../components/date-input";
|
|
import { isNotEmpty } from "@mantine/form";
|
|
import toast from '@/lib/sonner';
|
|
import { logger } from "..";
|
|
import { useQueryClient, useMutation } from "@tanstack/react-query";
|
|
import { tournamentQueries } from "@/features/tournaments/queries";
|
|
import { updateTournament } from "@/features/tournaments/server";
|
|
import { useRouter } from "@tanstack/react-router";
|
|
import Button from "@/components/button";
|
|
|
|
interface EditTournamentProps {
|
|
tournament: Tournament;
|
|
}
|
|
|
|
const EditTournament = ({ tournament }: EditTournamentProps) => {
|
|
const router = useRouter();
|
|
const queryClient = useQueryClient();
|
|
|
|
const config: UseFormInput<TournamentFormInput> = {
|
|
initialValues: {
|
|
name: tournament.name || '',
|
|
location: tournament.location || '',
|
|
desc: tournament.desc || '',
|
|
start_time: tournament.start_time || '',
|
|
enroll_time: tournament.enroll_time || '',
|
|
end_time: tournament.end_time || '',
|
|
rules: tournament.rules || '',
|
|
logo: undefined, // Always start with no file selected
|
|
},
|
|
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 { mutate: editTournament, isPending } = useMutation({
|
|
mutationFn: (data: Partial<TournamentFormInput>) =>
|
|
updateTournament({ data: { id: tournament.id, updates: data } }),
|
|
onSuccess: (updatedTournament) => {
|
|
if (updatedTournament) {
|
|
queryClient.invalidateQueries({ queryKey: tournamentQueries.list().queryKey });
|
|
queryClient.setQueryData(
|
|
tournamentQueries.details(updatedTournament.id).queryKey,
|
|
updatedTournament
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
const handleSubmit = async (values: TournamentFormInput) => {
|
|
const { logo, ...tournamentData } = values;
|
|
|
|
editTournament(tournamentData, {
|
|
onSuccess: async (updatedTournament) => {
|
|
if (logo && updatedTournament) {
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('tournamentId', updatedTournament.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.setQueryData(
|
|
tournamentQueries.details(result.tournament!.id).queryKey,
|
|
result.tournament
|
|
);
|
|
|
|
toast.success('Tournament updated successfully!');
|
|
} catch (error: any) {
|
|
toast.error(`Tournament updated but logo upload failed: ${error.message}`);
|
|
logger.error('Tournament logo upload error', error);
|
|
}
|
|
} else {
|
|
toast.success('Tournament updated successfully!');
|
|
}
|
|
|
|
router.history.back();
|
|
},
|
|
onError: (error: any) => {
|
|
toast.error(`Failed to update tournament: ${error.message}`);
|
|
logger.error('Tournament update error', error);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Box w="100%" maw={600} mx="auto" p="lg">
|
|
<Stack gap="lg">
|
|
<Title order={2}>Edit Tournament</Title>
|
|
|
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
|
<Stack gap="md">
|
|
<TextInput
|
|
label="Name"
|
|
withAsterisk
|
|
key={form.key('name')}
|
|
{...form.getInputProps('name')}
|
|
/>
|
|
|
|
<TextInput
|
|
label="Location"
|
|
withAsterisk
|
|
key={form.key('location')}
|
|
{...form.getInputProps('location')}
|
|
/>
|
|
|
|
<Textarea
|
|
label="Description"
|
|
key={form.key('desc')}
|
|
{...form.getInputProps('desc')}
|
|
minRows={3}
|
|
/>
|
|
|
|
<Textarea
|
|
label="Rules"
|
|
key={form.key('rules')}
|
|
{...form.getInputProps('rules')}
|
|
minRows={4}
|
|
/>
|
|
|
|
<FileInput
|
|
key={form.key('logo')}
|
|
accept="image/png,image/jpeg,image/gif,image/jpg"
|
|
label="Change Logo"
|
|
leftSection={<LinkIcon size={16} />}
|
|
{...form.getInputProps('logo')}
|
|
/>
|
|
|
|
<DateInputSheet
|
|
label="Start Date"
|
|
withAsterisk
|
|
value={form.values.start_time}
|
|
onChange={(value) => form.setFieldValue('start_time', value)}
|
|
error={form.errors.start_time}
|
|
/>
|
|
|
|
<DateInputSheet
|
|
label="Enrollment Due"
|
|
withAsterisk
|
|
value={form.values.enroll_time}
|
|
onChange={(value) => form.setFieldValue('enroll_time', value)}
|
|
error={form.errors.enroll_time}
|
|
/>
|
|
|
|
<DateInputSheet
|
|
label="End Date (Optional)"
|
|
value={form.values.end_time || ''}
|
|
onChange={(value) => form.setFieldValue('end_time', value)}
|
|
error={form.errors.end_time}
|
|
/>
|
|
|
|
<Group justify="flex-end" mt="lg">
|
|
<Button
|
|
variant="light"
|
|
onClick={() => router.history.back()}
|
|
disabled={isPending}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
loading={isPending}
|
|
>
|
|
Update Tournament
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
</form>
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default EditTournament; |