226 lines
6.6 KiB
TypeScript
226 lines
6.6 KiB
TypeScript
'use client';
|
|
|
|
import React, { useMemo, useEffect } from 'react';
|
|
import { Card } from '@/ui/Card';
|
|
import { Button } from '@/ui/Button';
|
|
import { Heading } from '@/ui/Heading';
|
|
import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
|
import {
|
|
Flag,
|
|
SlidersHorizontal,
|
|
Calendar,
|
|
} from 'lucide-react';
|
|
import { RaceFilterModal } from '@/components/races/RaceFilterModal';
|
|
import { RacePagination } from '@/components/races/RacePagination';
|
|
import { Box } from '@/ui/Box';
|
|
import { Stack } from '@/ui/Stack';
|
|
import { Text } from '@/ui/Text';
|
|
import { Container } from '@/ui/Container';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { Surface } from '@/ui/Surface';
|
|
import { Skeleton } from '@/ui/Skeleton';
|
|
import { RaceListItem } from '@/components/races/RaceListItem';
|
|
import type { RacesViewData } from '@/lib/view-data/RacesViewData';
|
|
|
|
export type StatusFilter = 'scheduled' | 'running' | 'completed' | 'cancelled' | 'all';
|
|
|
|
interface RacesAllTemplateProps {
|
|
viewData: RacesViewData;
|
|
isLoading: boolean;
|
|
// Pagination
|
|
currentPage: number;
|
|
totalPages: number;
|
|
itemsPerPage: number;
|
|
onPageChange: (page: number) => void;
|
|
// Filters
|
|
statusFilter: StatusFilter;
|
|
setStatusFilter: (filter: StatusFilter) => void;
|
|
leagueFilter: string;
|
|
setLeagueFilter: (filter: string) => void;
|
|
searchQuery: string;
|
|
setSearchQuery: (query: string) => void;
|
|
// UI State
|
|
showFilters: boolean;
|
|
setShowFilters: (show: boolean) => void;
|
|
showFilterModal: boolean;
|
|
setShowFilterModal: (show: boolean) => void;
|
|
// Actions
|
|
onRaceClick: (raceId: string) => void;
|
|
onLeagueClick: (leagueId: string) => void;
|
|
}
|
|
|
|
export function RacesAllTemplate({
|
|
viewData,
|
|
isLoading,
|
|
currentPage,
|
|
totalPages,
|
|
itemsPerPage,
|
|
onPageChange,
|
|
statusFilter,
|
|
setStatusFilter,
|
|
leagueFilter,
|
|
setLeagueFilter,
|
|
searchQuery,
|
|
setSearchQuery,
|
|
showFilters,
|
|
setShowFilters,
|
|
showFilterModal,
|
|
setShowFilterModal,
|
|
onRaceClick,
|
|
}: RacesAllTemplateProps) {
|
|
const { races } = viewData;
|
|
|
|
// Filter races
|
|
const filteredRaces = useMemo(() => {
|
|
return races.filter(race => {
|
|
if (statusFilter !== 'all' && race.status !== statusFilter) {
|
|
return false;
|
|
}
|
|
|
|
if (leagueFilter !== 'all' && race.leagueId !== leagueFilter) {
|
|
return false;
|
|
}
|
|
|
|
if (searchQuery) {
|
|
const query = searchQuery.toLowerCase();
|
|
const matchesTrack = race.track.toLowerCase().includes(query);
|
|
const matchesCar = race.car.toLowerCase().includes(query);
|
|
const matchesLeague = race.leagueName?.toLowerCase().includes(query);
|
|
if (!matchesTrack && !matchesCar && !matchesLeague) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}, [races, statusFilter, leagueFilter, searchQuery]);
|
|
|
|
// Paginate
|
|
const paginatedRaces = useMemo(() => {
|
|
const start = (currentPage - 1) * itemsPerPage;
|
|
return filteredRaces.slice(start, start + itemsPerPage);
|
|
}, [filteredRaces, currentPage, itemsPerPage]);
|
|
|
|
// Reset page when filters change
|
|
useEffect(() => {
|
|
onPageChange(1);
|
|
}, [statusFilter, leagueFilter, searchQuery]);
|
|
|
|
const breadcrumbItems = [
|
|
{ label: 'Races', href: '/races' },
|
|
{ label: 'All Races' },
|
|
];
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Container size="lg" py={8}>
|
|
<Stack gap={6}>
|
|
<Skeleton width="8rem" height="1.5rem" />
|
|
<Skeleton width="12rem" height="2.5rem" />
|
|
<Stack gap={4}>
|
|
{[1, 2, 3, 4, 5].map(i => (
|
|
<Skeleton key={i} width="100%" height="6rem" />
|
|
))}
|
|
</Stack>
|
|
</Stack>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Container size="lg" py={8}>
|
|
<Stack gap={6}>
|
|
{/* Breadcrumbs */}
|
|
<Breadcrumbs items={breadcrumbItems} />
|
|
|
|
{/* Header */}
|
|
<Stack direction="row" align="center" justify="between" wrap gap={4}>
|
|
<Box>
|
|
<Heading level={1} icon={<Icon icon={Flag} size={6} color="#3b82f6" />}>
|
|
All Races
|
|
</Heading>
|
|
<Text size="sm" color="text-gray-400" block mt={1}>
|
|
{filteredRaces.length} race{filteredRaces.length !== 1 ? 's' : ''} found
|
|
</Text>
|
|
</Box>
|
|
|
|
<Button
|
|
variant="secondary"
|
|
onClick={() => setShowFilters(!showFilters)}
|
|
icon={<Icon icon={SlidersHorizontal} size={4} />}
|
|
>
|
|
Filters
|
|
</Button>
|
|
</Stack>
|
|
|
|
{/* Search & Filters (Simplified for template) */}
|
|
{showFilters && (
|
|
<Card>
|
|
<Stack gap={4}>
|
|
<Text size="sm" color="text-gray-400">
|
|
Use the filter button to open advanced search and filtering options.
|
|
</Text>
|
|
<Box>
|
|
<Button variant="primary" onClick={() => setShowFilterModal(true)}>
|
|
Open Filters
|
|
</Button>
|
|
</Box>
|
|
</Stack>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Race List */}
|
|
{paginatedRaces.length === 0 ? (
|
|
<Card>
|
|
<Stack align="center" py={12} gap={4}>
|
|
<Surface variant="muted" rounded="full" padding={4}>
|
|
<Icon icon={Calendar} size={8} color="#525252" />
|
|
</Surface>
|
|
<Box style={{ textAlign: 'center' }}>
|
|
<Text weight="medium" color="text-white" block mb={1}>No races found</Text>
|
|
<Text size="sm" color="text-gray-500">
|
|
{races.length === 0
|
|
? 'No races have been scheduled yet'
|
|
: 'Try adjusting your search or filters'}
|
|
</Text>
|
|
</Box>
|
|
</Stack>
|
|
</Card>
|
|
) : (
|
|
<Stack gap={3}>
|
|
{paginatedRaces.map(race => (
|
|
<RaceListItem key={race.id} race={race as any} onClick={onRaceClick} />
|
|
))}
|
|
</Stack>
|
|
)}
|
|
|
|
{/* Pagination */}
|
|
<RacePagination
|
|
currentPage={currentPage}
|
|
totalPages={totalPages}
|
|
totalItems={filteredRaces.length}
|
|
itemsPerPage={itemsPerPage}
|
|
onPageChange={onPageChange}
|
|
/>
|
|
|
|
{/* Filter Modal */}
|
|
<RaceFilterModal
|
|
isOpen={showFilterModal}
|
|
onClose={() => setShowFilterModal(false)}
|
|
statusFilter={statusFilter}
|
|
setStatusFilter={setStatusFilter}
|
|
leagueFilter={leagueFilter}
|
|
setLeagueFilter={setLeagueFilter}
|
|
timeFilter="all"
|
|
setTimeFilter={() => {}}
|
|
searchQuery={searchQuery}
|
|
setSearchQuery={setSearchQuery}
|
|
leagues={viewData.leagues}
|
|
showSearch={true}
|
|
showTimeFilter={false}
|
|
/>
|
|
</Stack>
|
|
</Container>
|
|
);
|
|
}
|