website refactor
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
'use server';
|
||||
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { ScheduleAdminMutation } from '@/lib/mutations/leagues/ScheduleAdminMutation';
|
||||
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function publishScheduleAction(leagueId: string, seasonId: string): Promise<Result<void, string>> {
|
||||
@@ -31,8 +36,8 @@ export async function unpublishScheduleAction(leagueId: string, seasonId: string
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function createRaceAction(
|
||||
leagueId: string,
|
||||
seasonId: string,
|
||||
leagueId: string,
|
||||
seasonId: string,
|
||||
input: { track: string; car: string; scheduledAtIso: string }
|
||||
): Promise<Result<void, string>> {
|
||||
const mutation = new ScheduleAdminMutation();
|
||||
@@ -47,9 +52,9 @@ export async function createRaceAction(
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function updateRaceAction(
|
||||
leagueId: string,
|
||||
seasonId: string,
|
||||
raceId: string,
|
||||
leagueId: string,
|
||||
seasonId: string,
|
||||
raceId: string,
|
||||
input: Partial<{ track: string; car: string; scheduledAtIso: string }>
|
||||
): Promise<Result<void, string>> {
|
||||
const mutation = new ScheduleAdminMutation();
|
||||
@@ -73,3 +78,62 @@ export async function deleteRaceAction(leagueId: string, seasonId: string, raceI
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function registerForRaceAction(raceId: string, leagueId: string, driverId: string): Promise<Result<void, string>> {
|
||||
try {
|
||||
const baseUrl = getWebsiteApiBaseUrl();
|
||||
const apiClient = new RacesApiClient(
|
||||
baseUrl,
|
||||
new ConsoleErrorReporter(),
|
||||
new ConsoleLogger()
|
||||
);
|
||||
|
||||
await apiClient.register(raceId, { raceId, leagueId, driverId });
|
||||
|
||||
// Revalidate the schedule page to show updated registration status
|
||||
revalidatePath(routes.league.schedule(leagueId));
|
||||
|
||||
return Result.ok(undefined);
|
||||
} catch (error) {
|
||||
console.error('registerForRaceAction failed:', error);
|
||||
return Result.err('Failed to register for race');
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function withdrawFromRaceAction(raceId: string, driverId: string, leagueId: string): Promise<Result<void, string>> {
|
||||
try {
|
||||
const baseUrl = getWebsiteApiBaseUrl();
|
||||
const apiClient = new RacesApiClient(
|
||||
baseUrl,
|
||||
new ConsoleErrorReporter(),
|
||||
new ConsoleLogger()
|
||||
);
|
||||
|
||||
await apiClient.withdraw(raceId, { raceId, driverId });
|
||||
|
||||
// Revalidate the schedule page to show updated registration status
|
||||
revalidatePath(routes.league.schedule(leagueId));
|
||||
|
||||
return Result.ok(undefined);
|
||||
} catch (error) {
|
||||
console.error('withdrawFromRaceAction failed:', error);
|
||||
return Result.err('Failed to withdraw from race');
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function navigateToEditRaceAction(raceId: string, leagueId: string): Promise<void> {
|
||||
redirect(routes.league.scheduleAdmin(leagueId));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function navigateToRescheduleRaceAction(raceId: string, leagueId: string): Promise<void> {
|
||||
redirect(routes.league.scheduleAdmin(leagueId));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function navigateToRaceResultsAction(raceId: string, leagueId: string): Promise<void> {
|
||||
redirect(routes.race.results(raceId));
|
||||
}
|
||||
|
||||
@@ -16,102 +16,10 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useState } from 'react';
|
||||
import { LeaguesTemplate, Category, CategoryId } from '@/templates/LeaguesTemplate';
|
||||
import { LeaguesTemplate } from '@/templates/LeaguesTemplate';
|
||||
import { LEAGUE_CATEGORIES, CategoryId } from '@/lib/config/leagueCategories';
|
||||
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
||||
|
||||
const CATEGORIES: Category[] = [
|
||||
{
|
||||
id: 'all',
|
||||
label: 'All',
|
||||
icon: Globe,
|
||||
description: 'All available competition infrastructure.',
|
||||
filter: () => true,
|
||||
},
|
||||
{
|
||||
id: 'popular',
|
||||
label: 'Popular',
|
||||
icon: Flame,
|
||||
description: 'High utilization infrastructure.',
|
||||
filter: (league) => {
|
||||
const fillRate = (league.usedDriverSlots ?? 0) / (league.maxDrivers ?? 1);
|
||||
return fillRate > 0.7;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'new',
|
||||
label: 'New',
|
||||
icon: Sparkles,
|
||||
description: 'Recently deployed infrastructure.',
|
||||
filter: (league) => {
|
||||
const oneWeekAgo = new Date();
|
||||
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
|
||||
return new Date(league.createdAt) > oneWeekAgo;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'openSlots',
|
||||
label: 'Open',
|
||||
icon: Target,
|
||||
description: 'Infrastructure with available capacity.',
|
||||
filter: (league) => {
|
||||
if (league.maxTeams && league.maxTeams > 0) {
|
||||
const usedTeams = league.usedTeamSlots ?? 0;
|
||||
return usedTeams < league.maxTeams;
|
||||
}
|
||||
const used = league.usedDriverSlots ?? 0;
|
||||
const max = league.maxDrivers ?? 0;
|
||||
return max > 0 && used < max;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'driver',
|
||||
label: 'Driver',
|
||||
icon: Trophy,
|
||||
description: 'Individual competition format.',
|
||||
filter: (league) => league.scoring?.primaryChampionshipType === 'driver',
|
||||
},
|
||||
{
|
||||
id: 'team',
|
||||
label: 'Team',
|
||||
icon: Users,
|
||||
description: 'Team-based competition format.',
|
||||
filter: (league) => league.scoring?.primaryChampionshipType === 'team',
|
||||
},
|
||||
{
|
||||
id: 'nations',
|
||||
label: 'Nations',
|
||||
icon: Flag,
|
||||
description: 'National representation format.',
|
||||
filter: (league) => league.scoring?.primaryChampionshipType === 'nations',
|
||||
},
|
||||
{
|
||||
id: 'trophy',
|
||||
label: 'Trophy',
|
||||
icon: Award,
|
||||
description: 'Special event infrastructure.',
|
||||
filter: (league) => league.scoring?.primaryChampionshipType === 'trophy',
|
||||
},
|
||||
{
|
||||
id: 'endurance',
|
||||
label: 'Endurance',
|
||||
icon: Timer,
|
||||
description: 'Long-duration competition.',
|
||||
filter: (league) =>
|
||||
league.scoring?.scoringPresetId?.includes('endurance') ??
|
||||
league.timingSummary?.includes('h Race') ??
|
||||
false,
|
||||
},
|
||||
{
|
||||
id: 'sprint',
|
||||
label: 'Sprint',
|
||||
icon: Clock,
|
||||
description: 'Short-duration competition.',
|
||||
filter: (league) =>
|
||||
(league.scoring?.scoringPresetId?.includes('sprint') ?? false) &&
|
||||
!(league.scoring?.scoringPresetId?.includes('endurance') ?? false),
|
||||
},
|
||||
];
|
||||
|
||||
export function LeaguesPageClient({ viewData }: ClientWrapperProps<LeaguesViewData>) {
|
||||
const router = useRouter();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -122,7 +30,7 @@ export function LeaguesPageClient({ viewData }: ClientWrapperProps<LeaguesViewDa
|
||||
league.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
(league.description ?? '').toLowerCase().includes(searchQuery.toLowerCase());
|
||||
|
||||
const category = CATEGORIES.find(c => c.id === activeCategory);
|
||||
const category = LEAGUE_CATEGORIES.find(c => c.id === activeCategory);
|
||||
const matchesCategory = !category || category.filter(league);
|
||||
|
||||
return matchesSearch && matchesCategory;
|
||||
@@ -136,7 +44,7 @@ export function LeaguesPageClient({ viewData }: ClientWrapperProps<LeaguesViewDa
|
||||
activeCategory={activeCategory}
|
||||
onCategoryChange={setActiveCategory}
|
||||
filteredLeagues={filteredLeagues}
|
||||
categories={CATEGORIES}
|
||||
categories={LEAGUE_CATEGORIES}
|
||||
onCreateLeague={() => router.push(routes.league.create)}
|
||||
onLeagueClick={(id) => router.push(routes.league.detail(id))}
|
||||
onClearFilters={() => { setSearchQuery(''); setActiveCategory('all'); }}
|
||||
|
||||
@@ -28,22 +28,10 @@ export default async function LeagueSchedulePage({ params }: Props) {
|
||||
currentDriverId: undefined,
|
||||
isAdmin: false,
|
||||
}}
|
||||
onRegister={async () => {}}
|
||||
onWithdraw={async () => {}}
|
||||
onEdit={() => {}}
|
||||
onReschedule={() => {}}
|
||||
onResultsClick={() => {}}
|
||||
/>;
|
||||
}
|
||||
|
||||
const viewData = result.unwrap();
|
||||
|
||||
return <LeagueScheduleTemplate
|
||||
viewData={viewData}
|
||||
onRegister={async () => {}}
|
||||
onWithdraw={async () => {}}
|
||||
onEdit={() => {}}
|
||||
onReschedule={() => {}}
|
||||
onResultsClick={() => {}}
|
||||
/>;
|
||||
return <LeagueScheduleTemplate viewData={viewData} />;
|
||||
}
|
||||
Reference in New Issue
Block a user