tournament logo upload via api
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Stack, TextInput, Textarea } from "@mantine/core";
|
||||
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";
|
||||
@@ -6,6 +6,10 @@ 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 }) => {
|
||||
|
||||
@@ -14,7 +18,6 @@ const CreateTournament = ({ close }: { close: () => void }) => {
|
||||
name: 'Test Tournament',
|
||||
location: 'Test Location',
|
||||
desc: 'Test Description',
|
||||
logo_url: 'https://en.wikipedia.org/wiki/Trophy#/media/File:1934_Melbourne_Cup,_National_Museum_of_Australia.jpg',
|
||||
start_time: '2025-01-01T00:00:00Z',
|
||||
enroll_time: '2025-01-01T00:00:00Z',
|
||||
},
|
||||
@@ -28,12 +31,45 @@ const CreateTournament = ({ close }: { close: () => void }) => {
|
||||
}
|
||||
|
||||
const form = useForm(config);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutate: createTournament, isPending } = useCreateTournament();
|
||||
|
||||
const handleSubmit = async (values: TournamentFormInput) => {
|
||||
createTournament(values, {
|
||||
onSuccess: () => {
|
||||
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();
|
||||
}
|
||||
});
|
||||
@@ -67,12 +103,12 @@ const CreateTournament = ({ close }: { close: () => void }) => {
|
||||
{...form.getInputProps('desc')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
key={form.key('logo_url')}
|
||||
accept="image/*"
|
||||
<FileInput
|
||||
key={form.key('logo')}
|
||||
accept="image/png,image/jpeg,image/gif,image/jpg"
|
||||
label="Logo"
|
||||
leftSection={<LinkIcon size={16} />}
|
||||
{...form.getInputProps('logo_url')}
|
||||
{...form.getInputProps('logo')}
|
||||
/>
|
||||
|
||||
<SlidePanelField
|
||||
|
||||
@@ -18,7 +18,6 @@ const useCreateTournament = () => {
|
||||
toast.error('There was an issue creating your tournament. Please try again later.');
|
||||
logger.error('Error creating tournament', data);
|
||||
} else {
|
||||
toast.success('Tournament created successfully!');
|
||||
logger.info('Tournament created successfully', data);
|
||||
navigate({ to: '/tournaments' });
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const Profile = ({ player }: ProfileProps) => {
|
||||
const tabs = [
|
||||
{
|
||||
label: "Overview",
|
||||
content: <Text p="md">Panel 1 content</Text>
|
||||
content: <Text p="md">Stats/Badges will go here</Text>
|
||||
},
|
||||
{
|
||||
label: "Teams",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
||||
export interface Team {
|
||||
id: string;
|
||||
name: string;
|
||||
logo_url: string;
|
||||
logo: string;
|
||||
primary_color: string;
|
||||
accent_color: string;
|
||||
song_id: string;
|
||||
@@ -22,7 +22,7 @@ export interface Team {
|
||||
|
||||
export const teamInputSchema = z.object({
|
||||
name: z.string().min(1, "Team name is required").max(100, "Name too long"),
|
||||
logo_url: z.url("Invalid logo URL").optional(),
|
||||
logo: z.file("Invalid logo").optional(),
|
||||
primary_color: z.string().regex(/^#[0-9A-F]{6}$/i, "Must be valid hex color (#FF0000)").optional(),
|
||||
accent_color: z.string().regex(/^#[0-9A-F]{6}$/i, "Must be valid hex color (#FF0000)").optional(),
|
||||
song_id: z.string().max(255).optional(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 { CaretRightIcon, TrophyIcon } from "@phosphor-icons/react"
|
||||
import { useNavigate } from "@tanstack/react-router"
|
||||
|
||||
interface TournamentCardProps {
|
||||
@@ -26,11 +26,12 @@ export const TournamentCard = ({ tournament }: TournamentCardProps) => {
|
||||
<Stack>
|
||||
<Flex align='center' gap='md'>
|
||||
<Image
|
||||
src={tournament.logo_url}
|
||||
src={tournament.logo ? `/api/files/tournaments/${tournament.id}/${tournament.logo}` : undefined}
|
||||
maw={100}
|
||||
mah={100}
|
||||
fit='contain'
|
||||
alt={tournament.name}
|
||||
fallbackSrc={"TODO"}
|
||||
/>
|
||||
<Stack ta='center' mx='auto' gap='0'>
|
||||
<Text size='lg' fw={800}>{tournament.name} <CaretRightIcon size={12} weight='bold' /></Text>
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface Tournament {
|
||||
location?: string;
|
||||
desc?: string;
|
||||
rules?: string;
|
||||
logo_url?: string;
|
||||
logo?: string;
|
||||
enroll_time?: string;
|
||||
start_time: string;
|
||||
end_time?: string;
|
||||
@@ -22,7 +22,7 @@ export const tournamentFormSchema = z.object({
|
||||
location: z.string().optional(),
|
||||
desc: z.string().optional(),
|
||||
rules: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
logo: z.file().optional(),
|
||||
enroll_time: z.string(),
|
||||
start_time: z.string(),
|
||||
end_time: z.string().optional(),
|
||||
@@ -34,7 +34,7 @@ export const tournamentInputSchema = z.object({
|
||||
location: z.string().optional(),
|
||||
desc: z.string().optional(),
|
||||
rules: z.string().optional(),
|
||||
logo_url: z.string().optional(),
|
||||
logo: z.file().optional(),
|
||||
enroll_time: z.string(),
|
||||
start_time: z.string(),
|
||||
end_time: z.string().optional(),
|
||||
|
||||
Reference in New Issue
Block a user