224 lines
6.8 KiB
TypeScript
224 lines
6.8 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useParams } from 'next/navigation';
|
|
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
|
import { LeagueAdminScheduleTemplate } from '@/templates/LeagueAdminScheduleTemplate';
|
|
import {
|
|
useLeagueAdminStatus,
|
|
useLeagueSeasons,
|
|
useLeagueAdminSchedule
|
|
} from "@/hooks/league/useLeagueScheduleAdminPageData";
|
|
import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId";
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { useInject } from '@/lib/di/hooks/useInject';
|
|
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
|
import { Box } from '@/ui/Box';
|
|
import { Stack } from '@/ui/Stack';
|
|
import { Text } from '@/ui/Text';
|
|
import { Card } from '@/ui/Card';
|
|
import { Heading } from '@/ui/Heading';
|
|
|
|
export function LeagueAdminSchedulePageClient() {
|
|
const params = useParams();
|
|
const leagueId = params.id as string;
|
|
const currentDriverId = useEffectiveDriverId() || '';
|
|
const queryClient = useQueryClient();
|
|
|
|
const leagueService = useInject(LEAGUE_SERVICE_TOKEN);
|
|
|
|
// Form state
|
|
const [seasonId, setSeasonId] = useState<string>('');
|
|
const [track, setTrack] = useState('');
|
|
const [car, setCar] = useState('');
|
|
const [scheduledAtIso, setScheduledAtIso] = useState('');
|
|
const [editingRaceId, setEditingRaceId] = useState<string | null>(null);
|
|
|
|
// Check admin status using domain hook
|
|
const { data: isAdmin, isLoading: isAdminLoading } = useLeagueAdminStatus(leagueId, currentDriverId);
|
|
|
|
// Load seasons using domain hook
|
|
const { data: seasonsData, isLoading: seasonsLoading } = useLeagueSeasons(leagueId, !!isAdmin);
|
|
|
|
// Auto-select season
|
|
const selectedSeasonId = seasonId || (seasonsData && seasonsData.length > 0
|
|
? (seasonsData.find((s) => s.status === 'active') ?? seasonsData[0])?.seasonId
|
|
: '');
|
|
|
|
// Load schedule using domain hook
|
|
const { data: schedule, isLoading: scheduleLoading } = useLeagueAdminSchedule(leagueId, selectedSeasonId, !!isAdmin);
|
|
|
|
// Mutations
|
|
const publishMutation = useMutation({
|
|
mutationFn: async () => {
|
|
if (!schedule || !selectedSeasonId) return null;
|
|
return schedule.published
|
|
? await leagueService.unpublishAdminSchedule(leagueId, selectedSeasonId)
|
|
: await leagueService.publishAdminSchedule(leagueId, selectedSeasonId);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['adminSchedule', leagueId, selectedSeasonId] });
|
|
},
|
|
});
|
|
|
|
const saveMutation = useMutation({
|
|
mutationFn: async () => {
|
|
if (!selectedSeasonId || !scheduledAtIso) return null;
|
|
|
|
if (!editingRaceId) {
|
|
return await leagueService.createAdminScheduleRace(leagueId, selectedSeasonId, {
|
|
track,
|
|
car,
|
|
scheduledAtIso,
|
|
});
|
|
} else {
|
|
return await leagueService.updateAdminScheduleRace(leagueId, selectedSeasonId, editingRaceId, {
|
|
...(track ? { track } : {}),
|
|
...(car ? { car } : {}),
|
|
...(scheduledAtIso ? { scheduledAtIso } : {}),
|
|
});
|
|
}
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['adminSchedule', leagueId, selectedSeasonId] });
|
|
// Reset form
|
|
setTrack('');
|
|
setCar('');
|
|
setScheduledAtIso('');
|
|
setEditingRaceId(null);
|
|
},
|
|
});
|
|
|
|
const deleteMutation = useMutation({
|
|
mutationFn: async (raceId: string) => {
|
|
return await leagueService.deleteAdminScheduleRace(leagueId, selectedSeasonId, raceId);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['adminSchedule', leagueId, selectedSeasonId] });
|
|
},
|
|
});
|
|
|
|
// Derived states
|
|
const isLoading = isAdminLoading || seasonsLoading || scheduleLoading;
|
|
const isPublishing = publishMutation.isPending;
|
|
const isSaving = saveMutation.isPending;
|
|
const isDeleting = deleteMutation.variables || null;
|
|
|
|
// Handlers
|
|
const handleSeasonChange = (newSeasonId: string) => {
|
|
setSeasonId(newSeasonId);
|
|
setEditingRaceId(null);
|
|
setTrack('');
|
|
setCar('');
|
|
setScheduledAtIso('');
|
|
};
|
|
|
|
const handlePublishToggle = () => {
|
|
publishMutation.mutate();
|
|
};
|
|
|
|
const handleAddOrSave = () => {
|
|
if (!scheduledAtIso) return;
|
|
saveMutation.mutate();
|
|
};
|
|
|
|
const handleEdit = (raceId: string) => {
|
|
if (!schedule) return;
|
|
const race = schedule.races.find((r) => r.id === raceId);
|
|
if (!race) return;
|
|
|
|
setEditingRaceId(raceId);
|
|
setTrack('');
|
|
setCar('');
|
|
setScheduledAtIso(race.scheduledAt.toISOString());
|
|
};
|
|
|
|
const handleDelete = (raceId: string) => {
|
|
const confirmed = window.confirm('Delete this race?');
|
|
if (!confirmed) return;
|
|
deleteMutation.mutate(raceId);
|
|
};
|
|
|
|
const handleCancelEdit = () => {
|
|
setEditingRaceId(null);
|
|
setTrack('');
|
|
setCar('');
|
|
setScheduledAtIso('');
|
|
};
|
|
|
|
// Prepare template data
|
|
const templateData = schedule && seasonsData && selectedSeasonId
|
|
? {
|
|
published: schedule.published,
|
|
races: schedule.races.map(r => ({
|
|
id: r.id,
|
|
name: r.name,
|
|
track: r.track || '',
|
|
car: r.car || '',
|
|
scheduledAt: r.scheduledAt.toISOString(),
|
|
})),
|
|
seasons: seasonsData.map(s => ({
|
|
seasonId: s.seasonId,
|
|
name: s.name,
|
|
})),
|
|
seasonId: selectedSeasonId,
|
|
}
|
|
: undefined;
|
|
|
|
// Render admin access required if not admin
|
|
if (!isLoading && !isAdmin) {
|
|
return (
|
|
<Stack gap={6}>
|
|
<Card>
|
|
<Box p={6} textAlign="center">
|
|
<Heading level={3}>Admin Access Required</Heading>
|
|
<Box mt={2}>
|
|
<Text size="sm" color="text-gray-400">Only league admins can manage the schedule.</Text>
|
|
</Box>
|
|
</Box>
|
|
</Card>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
// Template component that wraps the actual template with all props
|
|
const TemplateWrapper = ({ data }: { data: typeof templateData }) => {
|
|
if (!data) return null;
|
|
|
|
return (
|
|
<LeagueAdminScheduleTemplate
|
|
viewData={data}
|
|
onSeasonChange={handleSeasonChange}
|
|
onPublishToggle={handlePublishToggle}
|
|
onAddOrSave={handleAddOrSave}
|
|
onEdit={handleEdit}
|
|
onDelete={handleDelete}
|
|
onCancelEdit={handleCancelEdit}
|
|
track={track}
|
|
car={car}
|
|
scheduledAtIso={scheduledAtIso}
|
|
editingRaceId={editingRaceId}
|
|
isPublishing={isPublishing}
|
|
isSaving={isSaving}
|
|
isDeleting={isDeleting}
|
|
setTrack={setTrack}
|
|
setCar={setCar}
|
|
setScheduledAtIso={setScheduledAtIso}
|
|
/>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<PageWrapper
|
|
data={templateData}
|
|
isLoading={isLoading}
|
|
error={null}
|
|
Template={TemplateWrapper}
|
|
loading={{ variant: 'full-screen', message: 'Loading schedule admin...' }}
|
|
empty={{
|
|
title: 'No schedule data available',
|
|
description: 'Unable to load schedule administration data',
|
|
}}
|
|
/>
|
|
);
|
|
} |