wip
This commit is contained in:
@@ -7,9 +7,11 @@ import Card from '@/components/ui/Card';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
||||
import { Race, RaceStatus } from '@gridpilot/racing/domain/entities/Race';
|
||||
import { League } from '@gridpilot/racing/domain/entities/League';
|
||||
import { getRaceRepository, getLeagueRepository } from '@/lib/di-container';
|
||||
import { getGetAllRacesPageDataUseCase } from '@/lib/di-container';
|
||||
import type {
|
||||
AllRacesPageViewModel,
|
||||
AllRacesListItemViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/IAllRacesPagePresenter';
|
||||
import {
|
||||
Calendar,
|
||||
Clock,
|
||||
@@ -30,38 +32,30 @@ import {
|
||||
|
||||
const ITEMS_PER_PAGE = 10;
|
||||
|
||||
type StatusFilter = 'scheduled' | 'running' | 'completed' | 'cancelled' | 'all';
|
||||
|
||||
export default function AllRacesPage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const [races, setRaces] = useState<Race[]>([]);
|
||||
const [leagues, setLeagues] = useState<Map<string, League>>(new Map());
|
||||
|
||||
const [pageData, setPageData] = useState<AllRacesPageViewModel | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
||||
// Pagination
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
|
||||
// Filters
|
||||
const [statusFilter, setStatusFilter] = useState<RaceStatus | 'all'>('all');
|
||||
const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');
|
||||
const [leagueFilter, setLeagueFilter] = useState<string>('all');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
|
||||
const loadRaces = async () => {
|
||||
try {
|
||||
const raceRepo = getRaceRepository();
|
||||
const leagueRepo = getLeagueRepository();
|
||||
|
||||
const [allRaces, allLeagues] = await Promise.all([
|
||||
raceRepo.findAll(),
|
||||
leagueRepo.findAll()
|
||||
]);
|
||||
|
||||
setRaces(allRaces.sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime()));
|
||||
|
||||
const leagueMap = new Map<string, League>();
|
||||
allLeagues.forEach(league => leagueMap.set(league.id, league));
|
||||
setLeagues(leagueMap);
|
||||
const useCase = getGetAllRacesPageDataUseCase();
|
||||
await useCase.execute();
|
||||
const viewModel = useCase.presenter.getViewModel();
|
||||
setPageData(viewModel);
|
||||
} catch (err) {
|
||||
console.error('Failed to load races:', err);
|
||||
} finally {
|
||||
@@ -70,29 +64,26 @@ export default function AllRacesPage() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadRaces();
|
||||
void loadRaces();
|
||||
}, []);
|
||||
|
||||
// Filter races
|
||||
const races: AllRacesListItemViewModel[] = pageData?.races ?? [];
|
||||
|
||||
const filteredRaces = useMemo(() => {
|
||||
return races.filter(race => {
|
||||
// Status filter
|
||||
if (statusFilter !== 'all' && race.status !== statusFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// League filter
|
||||
if (leagueFilter !== 'all' && race.leagueId !== leagueFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if (searchQuery) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
const league = leagues.get(race.leagueId);
|
||||
const matchesTrack = race.track.toLowerCase().includes(query);
|
||||
const matchesCar = race.car.toLowerCase().includes(query);
|
||||
const matchesLeague = league?.name.toLowerCase().includes(query);
|
||||
const matchesLeague = race.leagueName.toLowerCase().includes(query);
|
||||
if (!matchesTrack && !matchesCar && !matchesLeague) {
|
||||
return false;
|
||||
}
|
||||
@@ -100,7 +91,7 @@ export default function AllRacesPage() {
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [races, statusFilter, leagueFilter, searchQuery, leagues]);
|
||||
}, [races, statusFilter, leagueFilter, searchQuery]);
|
||||
|
||||
// Paginate
|
||||
const totalPages = Math.ceil(filteredRaces.length / ITEMS_PER_PAGE);
|
||||
@@ -232,7 +223,7 @@ export default function AllRacesPage() {
|
||||
{/* Status Filter */}
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value as RaceStatus | 'all')}
|
||||
onChange={(e) => setStatusFilter(e.target.value as StatusFilter)}
|
||||
className="px-4 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary-blue"
|
||||
>
|
||||
<option value="all">All Statuses</option>
|
||||
@@ -249,7 +240,7 @@ export default function AllRacesPage() {
|
||||
className="px-4 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary-blue"
|
||||
>
|
||||
<option value="all">All Leagues</option>
|
||||
{Array.from(leagues.values()).map(league => (
|
||||
{pageData?.filters.leagues.map((league) => (
|
||||
<option key={league.id} value={league.id}>
|
||||
{league.name}
|
||||
</option>
|
||||
@@ -295,7 +286,6 @@ export default function AllRacesPage() {
|
||||
{paginatedRaces.map(race => {
|
||||
const config = statusConfig[race.status];
|
||||
const StatusIcon = config.icon;
|
||||
const league = leagues.get(race.leagueId);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -347,16 +337,14 @@ export default function AllRacesPage() {
|
||||
{formatDate(race.scheduledAt)}
|
||||
</span>
|
||||
</div>
|
||||
{league && (
|
||||
<Link
|
||||
href={`/leagues/${league.id}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="inline-flex items-center gap-1.5 mt-2 text-sm text-primary-blue hover:underline"
|
||||
>
|
||||
<Trophy className="w-3.5 h-3.5" />
|
||||
{league.name}
|
||||
</Link>
|
||||
)}
|
||||
<Link
|
||||
href={`/leagues/${race.leagueId}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="inline-flex items-center gap-1.5 mt-2 text-sm text-primary-blue hover:underline"
|
||||
>
|
||||
<Trophy className="w-3.5 h-3.5" />
|
||||
{race.leagueName}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Status Badge */}
|
||||
|
||||
Reference in New Issue
Block a user