website cleanup

This commit is contained in:
2025-12-24 21:44:58 +01:00
parent 9b683a59d3
commit d78854a4c6
277 changed files with 6141 additions and 2693 deletions

View File

@@ -47,6 +47,26 @@ export class LeagueMembershipService {
}
}
/**
* Join a league.
*
* NOTE: The API currently exposes membership mutations via league member management endpoints.
* For now we keep the website decoupled by consuming only the API through this service.
*/
async joinLeague(leagueId: string, driverId: string): Promise<void> {
// Temporary: no join endpoint exposed yet in API.
// Keep behavior predictable for UI.
throw new Error('Joining leagues is not available in this build.');
}
/**
* Leave a league.
*/
async leaveLeague(leagueId: string, driverId: string): Promise<void> {
// Temporary: no leave endpoint exposed yet in API.
throw new Error('Leaving leagues is not available in this build.');
}
/**
* Set memberships in cache (for use after API calls).
*/
@@ -118,4 +138,4 @@ export class LeagueMembershipService {
clearLeagueMemberships(leagueId: string): void {
LeagueMembershipService.clearLeagueMemberships(leagueId);
}
}
}

View File

@@ -19,6 +19,8 @@ import { SubmitBlocker, ThrottleBlocker } from "@/lib/blockers";
import { RaceDTO } from "@/lib/types/generated/RaceDTO";
import { LeagueStatsDTO } from "@/lib/types/generated/LeagueStatsDTO";
import { LeagueScoringConfigDTO } from "@/lib/types/LeagueScoringConfigDTO";
import type { LeagueMembership } from "@/lib/types/LeagueMembership";
import type { LeagueSeasonSummaryDTO } from '@/lib/types/generated/LeagueSeasonSummaryDTO';
/**
@@ -48,9 +50,9 @@ export class LeagueService {
name: league.name,
description: league.description ?? '',
ownerId: league.ownerId,
createdAt: '', // Not provided by API
maxDrivers: league.maxMembers,
usedDriverSlots: league.memberCount,
createdAt: league.createdAt,
maxDrivers: league.settings.maxDrivers ?? 0,
usedDriverSlots: league.usedSlots,
structureSummary: 'TBD',
timingSummary: 'TBD'
}));
@@ -66,16 +68,20 @@ export class LeagueService {
// League memberships (roles, statuses)
const membershipsDto = await this.apiClient.getMemberships(leagueId);
const memberships: LeagueMembership[] = membershipsDto.members.map((m) => ({
driverId: m.driverId,
leagueId,
role: (m.role as LeagueMembership['role']) ?? 'member',
joinedAt: m.joinedAt,
status: 'active',
}));
// Resolve unique drivers that appear in standings
const driverIds: string[] = Array.from(new Set(dto.standings.map((entry: any) => entry.driverId)));
const driverDtos = await Promise.all(driverIds.map((id: string) => this.driversApiClient.getDriver(id)));
const drivers = driverDtos.filter((d): d is NonNullable<typeof d> => d !== null);
const dtoWithExtras = {
standings: dto.standings,
drivers,
memberships: membershipsDto.members,
};
const dtoWithExtras = { standings: dto.standings, drivers, memberships };
return new LeagueStandingsViewModel(dtoWithExtras, currentUserId);
}
@@ -96,6 +102,13 @@ export class LeagueService {
return new LeagueScheduleViewModel(dto);
}
/**
* Get seasons for a league
*/
async getLeagueSeasons(leagueId: string): Promise<LeagueSeasonSummaryDTO[]> {
return this.apiClient.getSeasons(leagueId);
}
/**
* Get league memberships
*/
@@ -162,18 +175,19 @@ export class LeagueService {
// Get membership
const membershipsDto = await this.apiClient.getMemberships(leagueId);
const membership = membershipsDto.members.find((m: any) => m.driverId === currentDriverId);
const isAdmin = membership ? ['admin', 'owner'].includes(membership.role) : false;
const isAdmin = membership ? ['admin', 'owner'].includes((membership as any).role) : false;
// Get main sponsor
let mainSponsor = null;
try {
const seasonsDto = await this.apiClient.getSeasons(leagueId);
const activeSeason = seasonsDto.seasons.find((s: any) => s.status === 'active') ?? seasonsDto.seasons[0];
const seasons = await this.apiClient.getSeasons(leagueId);
const activeSeason = seasons.find((s) => s.status === 'active') ?? seasons[0];
if (activeSeason) {
const sponsorshipsDto = await this.apiClient.getSeasonSponsorships(activeSeason.id);
const sponsorshipsDto = await this.apiClient.getSeasonSponsorships(activeSeason.seasonId);
const mainSponsorship = sponsorshipsDto.sponsorships.find((s: any) => s.tier === 'main' && s.status === 'active');
if (mainSponsorship) {
const sponsor = await this.sponsorsApiClient.getSponsor(mainSponsorship.sponsorId);
const sponsorResult = await this.sponsorsApiClient.getSponsor((mainSponsorship as any).sponsorId ?? (mainSponsorship as any).sponsor?.id);
const sponsor = (sponsorResult as any)?.sponsor ?? null;
if (sponsor) {
mainSponsor = {
name: sponsor.name,
@@ -241,7 +255,11 @@ export class LeagueService {
const allRaces = leagueRaces.races.map(r => new RaceViewModel(r as RaceDTO));
// League stats endpoint currently returns global league statistics rather than per-league values
const leagueStats = await this.apiClient.getTotal();
const leagueStats: LeagueStatsDTO = {
totalMembers: league.usedSlots,
totalRaces: allRaces.length,
averageRating: 0,
};
// Get sponsors
const sponsors = await this.getLeagueSponsors(leagueId);
@@ -268,16 +286,17 @@ export class LeagueService {
private async getLeagueSponsors(leagueId: string): Promise<SponsorInfo[]> {
try {
const seasons = await this.apiClient.getSeasons(leagueId);
const activeSeason = seasons.seasons.find((s: any) => s.status === 'active') ?? seasons.seasons[0];
const activeSeason = seasons.find((s) => s.status === 'active') ?? seasons[0];
if (!activeSeason) return [];
const sponsorships = await this.apiClient.getSeasonSponsorships(activeSeason.id);
const sponsorships = await this.apiClient.getSeasonSponsorships(activeSeason.seasonId);
const activeSponsorships = sponsorships.sponsorships.filter((s: any) => s.status === 'active');
const sponsorInfos: SponsorInfo[] = [];
for (const sponsorship of activeSponsorships) {
const sponsor = await this.sponsorsApiClient.getSponsor(sponsorship.sponsorId);
const sponsorResult = await this.sponsorsApiClient.getSponsor((sponsorship as any).sponsorId ?? (sponsorship as any).sponsor?.id);
const sponsor = (sponsorResult as any)?.sponsor ?? null;
if (sponsor) {
// Tagline is not supplied by the sponsor API in this build; callers may derive one from marketing content if needed
sponsorInfos.push({
@@ -285,7 +304,7 @@ export class LeagueService {
name: sponsor.name,
logoUrl: sponsor.logoUrl ?? '',
websiteUrl: sponsor.websiteUrl ?? '',
tier: sponsorship.tier,
tier: ((sponsorship as any).tier as 'main' | 'secondary') ?? 'secondary',
tagline: '',
});
}
@@ -312,4 +331,4 @@ export class LeagueService {
const result = await this.apiClient.getScoringPresets();
return result.presets;
}
}
}

View File

@@ -1,8 +1,7 @@
import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient";
import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
import type { LeagueConfigFormModel } from "@core/racing/application";
import type { LeagueScoringPresetDTO } from "@core/racing/application/ports/LeagueScoringPresetProvider";
import type { GetDriverOutputDTO } from "@/lib/types/generated/GetDriverOutputDTO";
import type { LeagueConfigFormModel } from "@/lib/types/LeagueConfigFormModel";
import type { LeagueScoringPresetDTO } from "@/lib/types/generated/LeagueScoringPresetDTO";
import { LeagueSettingsViewModel } from "@/lib/view-models/LeagueSettingsViewModel";
import { DriverSummaryViewModel } from "@/lib/view-models/DriverSummaryViewModel";
@@ -36,7 +35,7 @@ export class LeagueSettingsService {
// Get config
const configDto = await this.leaguesApiClient.getLeagueConfig(leagueId);
const config: LeagueConfigFormModel = configDto.config;
const config: LeagueConfigFormModel = (configDto.form ?? undefined) as unknown as LeagueConfigFormModel;
// Get presets
const presetsDto = await this.leaguesApiClient.getScoringPresets();
@@ -102,4 +101,4 @@ export class LeagueSettingsService {
throw error;
}
}
}
}