This commit is contained in:
2025-12-11 21:06:25 +01:00
parent c49ea2598d
commit ec3ddc3a5c
227 changed files with 3496 additions and 2083 deletions

View File

@@ -19,8 +19,8 @@ import type { League } from '@gridpilot/racing/domain/entities/League';
// Main sponsor info for "by XYZ" display
interface MainSponsorInfo {
name: string;
logoUrl?: string;
websiteUrl?: string;
logoUrl: string;
websiteUrl: string;
}
export default function LeagueLayout({
@@ -80,8 +80,8 @@ export default function LeagueLayout({
if (sponsor) {
setMainSponsor({
name: sponsor.name,
logoUrl: sponsor.logoUrl,
websiteUrl: sponsor.websiteUrl,
logoUrl: sponsor.logoUrl ?? '',
websiteUrl: sponsor.websiteUrl ?? '',
});
}
}

View File

@@ -127,7 +127,7 @@ export default function LeagueDetailPage() {
const getLeagueScoringConfigUseCase = getGetLeagueScoringConfigUseCase();
await getLeagueScoringConfigUseCase.execute({ leagueId });
const scoringViewModel = getLeagueScoringConfigUseCase.presenter.getViewModel();
setScoringConfig(scoringViewModel);
setScoringConfig(scoringViewModel as unknown as LeagueScoringConfigDTO);
// Load all drivers for standings and map to DTOs for UI components
const allDrivers = await driverRepo.findAll();
@@ -157,23 +157,23 @@ export default function LeagueDetailPage() {
if (activeSeason) {
const sponsorships = await sponsorshipRepo.findBySeasonId(activeSeason.id);
const activeSponsorships = sponsorships.filter(s => s.status === 'active');
const activeSponsorships = sponsorships.filter((s) => s.status === 'active');
const sponsorInfos: SponsorInfo[] = [];
for (const sponsorship of activeSponsorships) {
const sponsor = await sponsorRepo.findById(sponsorship.sponsorId);
if (sponsor) {
// Get tagline from demo data if available
const demoSponsors = (await import('@gridpilot/testing-support')).sponsors;
const demoSponsor = demoSponsors.find((s: any) => s.id === sponsor.id);
const testingSupportModule = await import('@gridpilot/testing-support');
const demoSponsors = testingSupportModule.sponsors as Array<{ id: string; tagline?: string }>;
const demoSponsor = demoSponsors.find((demo) => demo.id === sponsor.id);
sponsorInfos.push({
id: sponsor.id,
name: sponsor.name,
logoUrl: sponsor.logoUrl,
websiteUrl: sponsor.websiteUrl,
logoUrl: sponsor.logoUrl ?? '',
websiteUrl: sponsor.websiteUrl ?? '',
tier: sponsorship.tier,
tagline: demoSponsor?.tagline,
tagline: demoSponsor?.tagline ?? '',
});
}
}

View File

@@ -37,7 +37,7 @@ export default function LeagueRulebookPage() {
await scoringUseCase.execute({ leagueId });
const scoringViewModel = scoringUseCase.presenter.getViewModel();
setScoringConfig(scoringViewModel);
setScoringConfig(scoringViewModel as unknown as LeagueScoringConfigDTO);
} catch (err) {
console.error('Failed to load scoring config:', err);
} finally {

View File

@@ -14,6 +14,8 @@ import {
getListLeagueScoringPresetsUseCase,
getTransferLeagueOwnershipUseCase
} from '@/lib/di-container';
import { LeagueFullConfigPresenter } from '@/lib/presenters/LeagueFullConfigPresenter';
import { LeagueScoringPresetsPresenter } from '@/lib/presenters/LeagueScoringPresetsPresenter';
import { useEffectiveDriverId } from '@/lib/currentDriver';
import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles';
import { ScoringPatternSection, ChampionshipsSection } from '@/components/leagues/LeagueScoringSection';
@@ -70,13 +72,17 @@ export default function LeagueSettingsPage() {
setLeague(leagueData);
await useCase.execute({ leagueId });
const configViewModel = useCase.presenter.getViewModel();
setConfigForm(configViewModel);
const configPresenter = new LeagueFullConfigPresenter();
await useCase.execute({ leagueId }, configPresenter);
const configViewModel = configPresenter.getViewModel();
if (configViewModel) {
setConfigForm(configViewModel as LeagueConfigFormModel);
}
await presetsUseCase.execute();
const presetsViewModel = presetsUseCase.presenter.getViewModel();
setPresets(presetsViewModel);
const presetsPresenter = new LeagueScoringPresetsPresenter();
await presetsUseCase.execute(undefined as void, presetsPresenter);
const presetsViewModel = presetsPresenter.getViewModel();
setPresets(presetsViewModel.presets);
const entity = await driverRepo.findById(leagueData.ownerId);
if (entity) {

View File

@@ -37,8 +37,15 @@ export default function LeagueStandingsPage() {
const membershipRepo = getLeagueMembershipRepository();
await getLeagueDriverSeasonStatsUseCase.execute({ leagueId });
const standingsViewModel = getLeagueDriverSeasonStatsUseCase.presenter.getViewModel();
setStandings(standingsViewModel);
type GetLeagueDriverSeasonStatsUseCaseType = {
presenter: {
getViewModel(): { stats: LeagueDriverSeasonStatsDTO[] };
};
};
const typedUseCase =
getLeagueDriverSeasonStatsUseCase as GetLeagueDriverSeasonStatsUseCaseType;
const standingsViewModel = typedUseCase.presenter.getViewModel();
setStandings(standingsViewModel.stats);
const allDrivers = await driverRepo.findAll();
const driverDtos: DriverDTO[] = allDrivers
@@ -48,8 +55,19 @@ export default function LeagueStandingsPage() {
// Load league memberships from repository (consistent with other data)
const allMemberships = await membershipRepo.getLeagueMembers(leagueId);
// Convert to the format expected by StandingsTable
const membershipData: LeagueMembership[] = allMemberships.map(m => ({
type RawMembership = {
id: string | number;
leagueId: string;
driverId: string;
role: MembershipRole;
status: LeagueMembership['status'];
joinedAt: string | Date;
};
// Convert to the format expected by StandingsTable (website-level LeagueMembership)
const membershipData: LeagueMembership[] = (allMemberships as RawMembership[]).map((m) => ({
id: String(m.id),
leagueId: m.leagueId,
driverId: m.driverId,
role: m.role,

View File

@@ -246,12 +246,15 @@ export default function ProtestReviewPage() {
});
const selectedPenalty = PENALTY_TYPES.find(p => p.type === penaltyType);
const penaltyValueToUse =
selectedPenalty && selectedPenalty.requiresValue ? penaltyValue : 0;
await penaltyUseCase.execute({
raceId: protest.raceId,
driverId: protest.accusedDriverId,
stewardId: currentDriverId,
type: penaltyType,
value: selectedPenalty?.requiresValue ? penaltyValue : undefined,
value: penaltyValueToUse,
reason: protest.incident.description,
protestId: protest.id,
notes: stewardNotes,

View File

@@ -35,8 +35,8 @@ export default function CreateLeaguePage() {
const handleStepChange = (stepName: StepName) => {
const params = new URLSearchParams(
searchParams && typeof (searchParams as any).toString === 'function'
? (searchParams as any).toString()
searchParams && typeof searchParams.toString === 'function'
? searchParams.toString()
: '',
);
params.set('step', stepName);

View File

@@ -391,8 +391,11 @@ export default function LeaguesPage() {
try {
const useCase = getGetAllLeaguesWithCapacityAndScoringUseCase();
await useCase.execute();
const viewModel = useCase.presenter.getViewModel();
setRealLeagues(viewModel);
const presenter = useCase.presenter as unknown as {
getViewModel(): { leagues: LeagueSummaryDTO[] };
};
const viewModel = presenter.getViewModel();
setRealLeagues(viewModel.leagues);
} catch (error) {
console.error('Failed to load leagues:', error);
} finally {