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

@@ -1,9 +1,9 @@
import { BaseApiClient } from '../base/BaseApiClient';
import { AuthSessionDTO } from '../../types/generated/AuthSessionDTO';
import { LoginParams } from '../../types/generated/LoginParams';
import { SignupParams } from '../../types/generated/SignupParams';
import { LoginWithIracingCallbackParams } from '../../types/generated/LoginWithIracingCallbackParams';
import { IracingAuthRedirectResult } from '../../types/generated/IracingAuthRedirectResult';
import { LoginParamsDTO } from '../../types/generated/LoginParamsDTO';
import { SignupParamsDTO } from '../../types/generated/SignupParamsDTO';
import { LoginWithIracingCallbackParamsDTO } from '../../types/generated/LoginWithIracingCallbackParamsDTO';
import { IracingAuthRedirectResultDTO } from '../../types/generated/IracingAuthRedirectResultDTO';
/**
* Auth API Client
@@ -12,12 +12,12 @@ import { IracingAuthRedirectResult } from '../../types/generated/IracingAuthRedi
*/
export class AuthApiClient extends BaseApiClient {
/** Sign up with email */
signup(params: SignupParams): Promise<AuthSessionDTO> {
signup(params: SignupParamsDTO): Promise<AuthSessionDTO> {
return this.post<AuthSessionDTO>('/auth/signup', params);
}
/** Login with email */
login(params: LoginParams): Promise<AuthSessionDTO> {
login(params: LoginParamsDTO): Promise<AuthSessionDTO> {
return this.post<AuthSessionDTO>('/auth/login', params);
}
@@ -32,13 +32,22 @@ export class AuthApiClient extends BaseApiClient {
}
/** Start iRacing auth redirect */
startIracingAuthRedirect(returnTo?: string): Promise<IracingAuthRedirectResult> {
startIracingAuthRedirect(returnTo?: string): Promise<IracingAuthRedirectResultDTO> {
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : '';
return this.get<IracingAuthRedirectResult>(`/auth/iracing/start${query}`);
return this.get<IracingAuthRedirectResultDTO>(`/auth/iracing/start${query}`);
}
/**
* Convenience: build iRacing auth start URL.
* Used by AuthService for view-layer navigation.
*/
getIracingAuthUrl(returnTo?: string): string {
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : '';
return `${this.baseUrl}/auth/iracing/start${query}`;
}
/** Login with iRacing callback */
loginWithIracingCallback(params: LoginWithIracingCallbackParams): Promise<AuthSessionDTO> {
loginWithIracingCallback(params: LoginWithIracingCallbackParamsDTO): Promise<AuthSessionDTO> {
const query = new URLSearchParams();
query.append('code', params.code);
query.append('state', params.state);
@@ -47,4 +56,4 @@ export class AuthApiClient extends BaseApiClient {
}
return this.get<AuthSessionDTO>(`/auth/iracing/callback?${query.toString()}`);
}
}
}

View File

@@ -9,7 +9,7 @@ import { Logger } from '../../interfaces/Logger';
import { ErrorReporter } from '../../interfaces/ErrorReporter';
export class BaseApiClient {
private baseUrl: string;
protected baseUrl: string;
private errorReporter: ErrorReporter;
private logger: Logger;
@@ -19,12 +19,16 @@ export class BaseApiClient {
this.logger = logger;
}
protected async request<T>(method: string, path: string, data?: object): Promise<T> {
protected async request<T>(method: string, path: string, data?: object | FormData): Promise<T> {
this.logger.info(`${method} ${path}`);
const headers: HeadersInit = {
'Content-Type': 'application/json',
};
const isFormData = typeof FormData !== 'undefined' && data instanceof FormData;
const headers: HeadersInit = isFormData
? {}
: {
'Content-Type': 'application/json',
};
const config: RequestInit = {
method,
@@ -33,7 +37,7 @@ export class BaseApiClient {
};
if (data) {
config.body = JSON.stringify(data);
config.body = isFormData ? data : JSON.stringify(data);
}
const response = await fetch(`${this.baseUrl}${path}`, config);
@@ -45,7 +49,10 @@ export class BaseApiClient {
} catch {
// Keep default error message
}
const error = new Error(errorData.message || `API request failed with status ${response.status}`);
const error = new Error(
errorData.message || `API request failed with status ${response.status}`,
) as Error & { status?: number };
error.status = response.status;
this.errorReporter.report(error);
throw error;
}
@@ -76,4 +83,4 @@ export class BaseApiClient {
protected patch<T>(path: string, data: object): Promise<T> {
return this.request<T>('PATCH', path, data);
}
}
}

View File

@@ -1,28 +1,5 @@
import { BaseApiClient } from '../base/BaseApiClient';
import {
DashboardDriverSummaryDTO,
DashboardRaceSummaryDTO,
DashboardLeagueStandingSummaryDTO,
DashboardFeedItemSummaryDTO,
DashboardFriendSummaryDTO,
DashboardRecentResultDTO,
} from '../../types/generated';
// Define DashboardOverviewDTO using generated types
export type DashboardOverviewDto = {
currentDriver: DashboardDriverSummaryDTO | null;
myUpcomingRaces: DashboardRaceSummaryDTO[];
otherUpcomingRaces: DashboardRaceSummaryDTO[];
upcomingRaces: DashboardRaceSummaryDTO[];
activeLeaguesCount: number;
nextRace: DashboardRaceSummaryDTO | null;
recentResults: DashboardRecentResultDTO[];
leagueStandingsSummaries: DashboardLeagueStandingSummaryDTO[];
feedSummary: {
feedItems: DashboardFeedItemSummaryDTO[];
};
friends: DashboardFriendSummaryDTO[];
};
import type { DashboardOverviewDTO } from '../../types/generated/DashboardOverviewDTO';
/**
* Dashboard API Client
@@ -31,7 +8,7 @@ export type DashboardOverviewDto = {
*/
export class DashboardApiClient extends BaseApiClient {
/** Get dashboard overview data */
getDashboardOverview(): Promise<DashboardOverviewDto> {
return this.get<DashboardOverviewDto>('/dashboard/overview');
getDashboardOverview(): Promise<DashboardOverviewDTO> {
return this.get<DashboardOverviewDTO>('/dashboard/overview');
}
}
}

View File

@@ -1,6 +1,10 @@
import { BaseApiClient } from '../base/BaseApiClient';
// Import generated types
import type { CompleteOnboardingInputDTO, CompleteOnboardingOutputDTO, DriverRegistrationStatusDTO, DriverLeaderboardItemDTO, DriverProfileDTO, GetDriverOutputDTO } from '../../types/generated';
import type { CompleteOnboardingInputDTO } from '../../types/generated/CompleteOnboardingInputDTO';
import type { CompleteOnboardingOutputDTO } from '../../types/generated/CompleteOnboardingOutputDTO';
import type { DriverRegistrationStatusDTO } from '../../types/generated/DriverRegistrationStatusDTO';
import type { DriverLeaderboardItemDTO } from '../../types/generated/DriverLeaderboardItemDTO';
import type { GetDriverOutputDTO } from '../../types/generated/GetDriverOutputDTO';
import type { GetDriverProfileOutputDTO } from '../../types/generated/GetDriverProfileOutputDTO';
type DriversLeaderboardDto = {
drivers: DriverLeaderboardItemDTO[];
@@ -38,12 +42,12 @@ export class DriversApiClient extends BaseApiClient {
}
/** Get driver profile with full details */
getDriverProfile(driverId: string): Promise<DriverProfileDTO> {
return this.get<DriverProfileDTO>(`/drivers/${driverId}/profile`);
getDriverProfile(driverId: string): Promise<GetDriverProfileOutputDTO> {
return this.get<GetDriverProfileOutputDTO>(`/drivers/${driverId}/profile`);
}
/** Update current driver profile */
updateProfile(updates: { bio?: string; country?: string }): Promise<GetDriverOutputDTO> {
return this.put<GetDriverOutputDTO>('/drivers/profile', updates);
}
}
}

View File

@@ -1,15 +1,16 @@
import { BaseApiClient } from '../base/BaseApiClient';
import type {
AllLeaguesWithCapacityDto,
LeagueStatsDto,
LeagueStandingsDto,
LeagueScheduleDto,
LeagueMembershipsDto,
CreateLeagueInputDto,
CreateLeagueOutputDto,
SponsorshipDetailDTO,
RaceDTO,
} from '../../dtos';
import type { AllLeaguesWithCapacityDTO } from '../../types/generated/AllLeaguesWithCapacityDTO';
import type { TotalLeaguesDTO } from '../../types/generated/TotalLeaguesDTO';
import type { LeagueStandingsDTO } from '../../types/generated/LeagueStandingsDTO';
import type { LeagueScheduleDTO } from '../../types/generated/LeagueScheduleDTO';
import type { LeagueMembershipsDTO } from '../../types/generated/LeagueMembershipsDTO';
import type { CreateLeagueInputDTO } from '../../types/generated/CreateLeagueInputDTO';
import type { CreateLeagueOutputDTO } from '../../types/generated/CreateLeagueOutputDTO';
import type { SponsorshipDetailDTO } from '../../types/generated/SponsorshipDetailDTO';
import type { RaceDTO } from '../../types/generated/RaceDTO';
import type { GetLeagueAdminConfigOutputDTO } from '../../types/generated/GetLeagueAdminConfigOutputDTO';
import type { LeagueScoringPresetDTO } from '../../types/generated/LeagueScoringPresetDTO';
import type { LeagueSeasonSummaryDTO } from '../../types/generated/LeagueSeasonSummaryDTO';
/**
* Leagues API Client
@@ -18,33 +19,33 @@ import type {
*/
export class LeaguesApiClient extends BaseApiClient {
/** Get all leagues with capacity information */
getAllWithCapacity(): Promise<AllLeaguesWithCapacityDto> {
return this.get<AllLeaguesWithCapacityDto>('/leagues/all-with-capacity');
getAllWithCapacity(): Promise<AllLeaguesWithCapacityDTO> {
return this.get<AllLeaguesWithCapacityDTO>('/leagues/all-with-capacity');
}
/** Get total number of leagues */
getTotal(): Promise<LeagueStatsDto> {
return this.get<LeagueStatsDto>('/leagues/total-leagues');
getTotal(): Promise<TotalLeaguesDTO> {
return this.get<TotalLeaguesDTO>('/leagues/total-leagues');
}
/** Get league standings */
getStandings(leagueId: string): Promise<LeagueStandingsDto> {
return this.get<LeagueStandingsDto>(`/leagues/${leagueId}/standings`);
getStandings(leagueId: string): Promise<LeagueStandingsDTO> {
return this.get<LeagueStandingsDTO>(`/leagues/${leagueId}/standings`);
}
/** Get league schedule */
getSchedule(leagueId: string): Promise<LeagueScheduleDto> {
return this.get<LeagueScheduleDto>(`/leagues/${leagueId}/schedule`);
getSchedule(leagueId: string): Promise<LeagueScheduleDTO> {
return this.get<LeagueScheduleDTO>(`/leagues/${leagueId}/schedule`);
}
/** Get league memberships */
getMemberships(leagueId: string): Promise<LeagueMembershipsDto> {
return this.get<LeagueMembershipsDto>(`/leagues/${leagueId}/memberships`);
getMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
return this.get<LeagueMembershipsDTO>(`/leagues/${leagueId}/memberships`);
}
/** Create a new league */
create(input: CreateLeagueInputDto): Promise<CreateLeagueOutputDto> {
return this.post<CreateLeagueOutputDto>('/leagues', input);
create(input: CreateLeagueInputDTO): Promise<CreateLeagueOutputDTO> {
return this.post<CreateLeagueOutputDTO>('/leagues', input);
}
/** Remove a member from league */
@@ -58,8 +59,8 @@ export class LeaguesApiClient extends BaseApiClient {
}
/** Get league seasons */
getSeasons(leagueId: string): Promise<{ seasons: Array<{ id: string; status: string }> }> {
return this.get<{ seasons: Array<{ id: string; status: string }> }>(`/leagues/${leagueId}/seasons`);
getSeasons(leagueId: string): Promise<LeagueSeasonSummaryDTO[]> {
return this.get<LeagueSeasonSummaryDTO[]>(`/leagues/${leagueId}/seasons`);
}
/** Get season sponsorships */
@@ -68,13 +69,13 @@ export class LeaguesApiClient extends BaseApiClient {
}
/** Get league config */
getLeagueConfig(leagueId: string): Promise<{ config: any }> {
return this.get<{ config: any }>(`/leagues/${leagueId}/config`);
getLeagueConfig(leagueId: string): Promise<GetLeagueAdminConfigOutputDTO> {
return this.get<GetLeagueAdminConfigOutputDTO>(`/leagues/${leagueId}/config`);
}
/** Get league scoring presets */
getScoringPresets(): Promise<{ presets: any[] }> {
return this.get<{ presets: any[] }>(`/leagues/scoring-presets`);
getScoringPresets(): Promise<{ presets: LeagueScoringPresetDTO[] }> {
return this.get<{ presets: LeagueScoringPresetDTO[] }>(`/leagues/scoring-presets`);
}
/** Transfer league ownership */
@@ -89,4 +90,4 @@ export class LeaguesApiClient extends BaseApiClient {
getRaces(leagueId: string): Promise<{ races: RaceDTO[] }> {
return this.get<{ races: RaceDTO[] }>(`/leagues/${leagueId}/races`);
}
}
}

View File

@@ -1,13 +1,11 @@
import type {
DeleteMediaOutputDTO,
GetMediaOutputDTO,
RequestAvatarGenerationInputDTO,
RequestAvatarGenerationOutputDTO,
UpdateAvatarInputDTO,
UpdateAvatarOutputDTO,
UploadMediaOutputDTO,
} from '../generated';
import type { GetAvatarOutputDTO } from '../generated';
import type { DeleteMediaOutputDTO } from '../../types/generated/DeleteMediaOutputDTO';
import type { GetAvatarOutputDTO } from '../../types/generated/GetAvatarOutputDTO';
import type { GetMediaOutputDTO } from '../../types/generated/GetMediaOutputDTO';
import type { RequestAvatarGenerationInputDTO } from '../../types/generated/RequestAvatarGenerationInputDTO';
import type { RequestAvatarGenerationOutputDTO } from '../../types/generated/RequestAvatarGenerationOutputDTO';
import type { UpdateAvatarInputDTO } from '../../types/generated/UpdateAvatarInputDTO';
import type { UpdateAvatarOutputDTO } from '../../types/generated/UpdateAvatarOutputDTO';
import type { UploadMediaOutputDTO } from '../../types/generated/UploadMediaOutputDTO';
import { BaseApiClient } from '../base/BaseApiClient';
/**
@@ -51,4 +49,4 @@ export class MediaApiClient extends BaseApiClient {
updateAvatar(input: UpdateAvatarInputDTO): Promise<UpdateAvatarOutputDTO> {
return this.put<UpdateAvatarOutputDTO>(`/media/avatar/${input.driverId}`, { avatarUrl: input.avatarUrl });
}
}
}

View File

@@ -1,8 +1,14 @@
import { BaseApiClient } from '../base/BaseApiClient';
import type { PaymentDto, MembershipFeeDto, MemberPaymentDto, PrizeDto, WalletDto, TransactionDto, UpdatePaymentStatusInputDTO } from '../types/generated';
import type { MembershipFeeDTO } from '../../types/generated/MembershipFeeDTO';
import type { MemberPaymentDTO } from '../../types/generated/MemberPaymentDTO';
import type { PaymentDTO } from '../../types/generated/PaymentDTO';
import type { PrizeDTO } from '../../types/generated/PrizeDTO';
import type { TransactionDTO } from '../../types/generated/TransactionDTO';
import type { UpdatePaymentStatusInputDTO } from '../../types/generated/UpdatePaymentStatusInputDTO';
import type { WalletDTO } from '../../types/generated/WalletDTO';
// Define missing types that are not fully generated
type GetPaymentsOutputDto = { payments: PaymentDto[] };
type GetPaymentsOutputDto = { payments: PaymentDTO[] };
type CreatePaymentInputDto = {
type: 'sponsorship' | 'membership_fee';
amount: number;
@@ -11,15 +17,15 @@ type CreatePaymentInputDto = {
leagueId: string;
seasonId?: string;
};
type CreatePaymentOutputDto = { payment: PaymentDto };
type CreatePaymentOutputDto = { payment: PaymentDTO };
type GetMembershipFeesOutputDto = {
fee: MembershipFeeDto | null;
payments: MemberPaymentDto[]
fee: MembershipFeeDTO | null;
payments: MemberPaymentDTO[]
};
type GetPrizesOutputDto = { prizes: PrizeDto[] };
type GetPrizesOutputDto = { prizes: PrizeDTO[] };
type GetWalletOutputDto = {
wallet: WalletDto;
transactions: TransactionDto[]
wallet: WalletDTO;
transactions: TransactionDTO[]
};
type ProcessWalletTransactionInputDto = {
leagueId: string;
@@ -30,8 +36,8 @@ type ProcessWalletTransactionInputDto = {
referenceType?: 'sponsorship' | 'membership_fee' | 'prize';
};
type ProcessWalletTransactionOutputDto = {
wallet: WalletDto;
transaction: TransactionDto
wallet: WalletDTO;
transaction: TransactionDTO
};
type UpdateMemberPaymentInputDto = {
feeId: string;
@@ -39,16 +45,16 @@ type UpdateMemberPaymentInputDto = {
status?: 'pending' | 'paid' | 'overdue';
paidAt?: Date | string;
};
type UpdateMemberPaymentOutputDto = { payment: MemberPaymentDto };
type GetWalletTransactionsOutputDto = { transactions: TransactionDto[] };
type UpdatePaymentStatusOutputDto = { payment: PaymentDto };
type UpdateMemberPaymentOutputDto = { payment: MemberPaymentDTO };
type GetWalletTransactionsOutputDto = { transactions: TransactionDTO[] };
type UpdatePaymentStatusOutputDto = { payment: PaymentDTO };
type UpsertMembershipFeeInputDto = {
leagueId: string;
seasonId?: string;
type: 'season' | 'monthly' | 'per_race';
amount: number;
};
type UpsertMembershipFeeOutputDto = { fee: MembershipFeeDto };
type UpsertMembershipFeeOutputDto = { fee: MembershipFeeDTO };
type CreatePrizeInputDto = {
leagueId: string;
seasonId: string;
@@ -58,12 +64,12 @@ type CreatePrizeInputDto = {
type: 'cash' | 'merchandise' | 'other';
description?: string;
};
type CreatePrizeOutputDto = { prize: PrizeDto };
type CreatePrizeOutputDto = { prize: PrizeDTO };
type AwardPrizeInputDto = {
prizeId: string;
driverId: string;
};
type AwardPrizeOutputDto = { prize: PrizeDto };
type AwardPrizeOutputDto = { prize: PrizeDTO };
type DeletePrizeInputDto = { prizeId: string };
type DeletePrizeOutputDto = { success: boolean };
@@ -149,4 +155,4 @@ export class PaymentsApiClient extends BaseApiClient {
processWalletTransaction(input: ProcessWalletTransactionInputDto): Promise<ProcessWalletTransactionOutputDto> {
return this.post<ProcessWalletTransactionOutputDto>('/payments/wallets/transactions', input);
}
}
}

View File

@@ -1,11 +1,9 @@
import { BaseApiClient } from '../base/BaseApiClient';
import type {
LeagueAdminProtestsDTO,
ApplyPenaltyCommandDTO,
RequestProtestDefenseCommandDTO,
ReviewProtestCommandDTO,
} from '../../types/generated';
import type { RaceProtestsDTO } from '../../types';
import type { ApplyPenaltyCommandDTO } from '../../types/generated/ApplyPenaltyCommandDTO';
import type { LeagueAdminProtestsDTO } from '../../types/generated/LeagueAdminProtestsDTO';
import type { RaceProtestsDTO } from '../../types/generated/RaceProtestsDTO';
import type { RequestProtestDefenseCommandDTO } from '../../types/generated/RequestProtestDefenseCommandDTO';
import type { ReviewProtestCommandDTO } from '../../types/generated/ReviewProtestCommandDTO';
/**
* Protests API Client
@@ -42,4 +40,4 @@ export class ProtestsApiClient extends BaseApiClient {
getRaceProtests(raceId: string): Promise<RaceProtestsDTO> {
return this.get<RaceProtestsDTO>(`/races/${raceId}/protests`);
}
}
}

View File

@@ -11,6 +11,7 @@ import type { RaceDetailLeagueDTO } from '../../types/generated/RaceDetailLeague
import type { RaceDetailEntryDTO } from '../../types/generated/RaceDetailEntryDTO';
import type { RaceDetailRegistrationDTO } from '../../types/generated/RaceDetailRegistrationDTO';
import type { RaceDetailUserResultDTO } from '../../types/generated/RaceDetailUserResultDTO';
import type { FileProtestCommandDTO } from '../../types/generated/FileProtestCommandDTO';
// Define missing types
type RacesPageDataDTO = { races: RacesPageDataRaceDTO[] };
@@ -42,8 +43,9 @@ export class RacesApiClient extends BaseApiClient {
}
/** Get races page data */
getPageData(): Promise<RacesPageDataDTO> {
return this.get<RacesPageDataDTO>('/races/page-data');
getPageData(leagueId?: string): Promise<RacesPageDataDTO> {
const query = leagueId ? `?leagueId=${encodeURIComponent(leagueId)}` : '';
return this.get<RacesPageDataDTO>(`/races/page-data${query}`);
}
/** Get race detail */
@@ -90,4 +92,9 @@ export class RacesApiClient extends BaseApiClient {
reopen(raceId: string): Promise<void> {
return this.post<void>(`/races/${raceId}/reopen`, {});
}
}
/** File a protest */
fileProtest(input: FileProtestCommandDTO): Promise<void> {
return this.post<void>('/races/protests/file', input);
}
}

View File

@@ -11,10 +11,11 @@ import {
} from 'react';
import { useRouter } from 'next/navigation';
import type { AuthSession } from './AuthService';
import type { SessionViewModel } from '@/lib/view-models/SessionViewModel';
import { useServices } from '@/lib/services/ServiceProvider';
type AuthContextValue = {
session: AuthSession | null;
session: SessionViewModel | null;
loading: boolean;
login: (returnTo?: string) => void;
logout: () => Promise<void>;
@@ -24,33 +25,24 @@ type AuthContextValue = {
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
interface AuthProviderProps {
initialSession?: AuthSession | null;
initialSession?: SessionViewModel | null;
children: ReactNode;
}
export function AuthProvider({ initialSession = null, children }: AuthProviderProps) {
const router = useRouter();
const [session, setSession] = useState<AuthSession | null>(initialSession);
const { sessionService, authService } = useServices();
const [session, setSession] = useState<SessionViewModel | null>(initialSession);
const [loading, setLoading] = useState(false);
const fetchSession = useCallback(async () => {
try {
const res = await fetch('/api/auth/session', {
method: 'GET',
credentials: 'include',
});
if (!res.ok) {
setSession(null);
return;
}
const data = (await res.json()) as { session: AuthSession | null };
setSession(data.session ?? null);
const current = await sessionService.getSession();
setSession(current);
} catch {
setSession(null);
}
}, []);
}, [sessionService]);
const refreshSession = useCallback(async () => {
await fetchSession();
@@ -79,17 +71,14 @@ export function AuthProvider({ initialSession = null, children }: AuthProviderPr
const logout = useCallback(async () => {
setLoading(true);
try {
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include',
});
await authService.logout();
setSession(null);
router.push('/');
router.refresh();
} finally {
setLoading(false);
}
}, [router]);
}, [authService, router]);
const value = useMemo(
() => ({
@@ -111,4 +100,4 @@ export function useAuth(): AuthContextValue {
throw new Error('useAuth must be used within an AuthProvider');
}
return ctx;
}
}

View File

@@ -1,4 +1,4 @@
import { CreateLeagueInputDTO } from '@/lib/types/CreateLeagueInputDTO';
import type { CreateLeagueInputDTO } from '@/lib/types/generated/CreateLeagueInputDTO';
import { LeagueWizardValidationMessages } from '@/lib/display-objects/LeagueWizardValidationMessages';
import { ScoringPresetApplier } from '@/lib/utilities/ScoringPresetApplier';
@@ -267,21 +267,11 @@ export class LeagueWizardCommandModel {
}
toCreateLeagueCommand(ownerId: string): CreateLeagueInputDTO {
let maxMembers: number;
if (this.structure.mode === 'solo') {
maxMembers = this.structure.maxDrivers ?? 0;
} else {
const teams = this.structure.maxTeams ?? 0;
const perTeam = this.structure.driversPerTeam ?? 0;
maxMembers = teams * perTeam;
}
return {
name: this.basics.name.trim(),
description: this.basics.description?.trim() ?? '',
isPublic: this.basics.visibility === 'public',
maxMembers,
// API currently only supports public/private. Treat unlisted as private for now.
visibility: this.basics.visibility === 'public' ? 'public' : 'private',
ownerId,
};
}
@@ -311,4 +301,4 @@ export class LeagueWizardCommandModel {
stewarding: instance.stewarding,
};
}
}
}

View File

@@ -14,6 +14,7 @@ import { PenaltiesApiClient } from '../api/penalties/PenaltiesApiClient';
import { PenaltyService } from './penalties/PenaltyService';
import { ConsoleErrorReporter } from '../infrastructure/logging/ConsoleErrorReporter';
import { ConsoleLogger } from '../infrastructure/logging/ConsoleLogger';
import { LandingService } from './landing/LandingService';
// Services
import { RaceService } from './races/RaceService';
@@ -88,6 +89,23 @@ export class ServiceFactory {
};
}
/**
* Legacy compatibility: older pages/components were written against a static ServiceFactory.
* Prefer `useServices()` + react-query hooks.
*/
private static defaultInstance: ServiceFactory | null = null;
private static getDefaultInstance(): ServiceFactory {
if (!this.defaultInstance) {
this.defaultInstance = new ServiceFactory(process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001');
}
return this.defaultInstance;
}
static getSponsorService(): SponsorService {
return this.getDefaultInstance().createSponsorService();
}
/**
* Create RaceService instance
*/
@@ -151,8 +169,8 @@ export class ServiceFactory {
/**
* Create LeagueMembershipService instance
*/
createLeagueMembershipService(): LeagueMembershipService {
return new LeagueMembershipService(this.apiClients.leagues);
createLeagueMembershipService(): LeagueMembershipService {
return new LeagueMembershipService();
}
/**
@@ -279,4 +297,11 @@ export class ServiceFactory {
createPenaltyService(): PenaltyService {
return new PenaltyService(this.apiClients.penalties);
}
}
/**
* Create LandingService instance (used by server components)
*/
createLandingService(): LandingService {
return new LandingService(this.apiClients.races, this.apiClients.leagues, this.apiClients.teams);
}
}

View File

@@ -1,35 +1,35 @@
'use client';
import React, { createContext, useContext, useMemo, ReactNode } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createContext, ReactNode, useContext, useMemo } from 'react';
import { ServiceFactory } from './ServiceFactory';
// Import all service types
import { RaceService } from './races/RaceService';
import { RaceResultsService } from './races/RaceResultsService';
import { RaceStewardingService } from './races/RaceStewardingService';
import { DriverService } from './drivers/DriverService';
import { AnalyticsService } from './analytics/AnalyticsService';
import { AuthService } from './auth/AuthService';
import { SessionService } from './auth/SessionService';
import { DashboardService } from './dashboard/DashboardService';
import { DriverRegistrationService } from './drivers/DriverRegistrationService';
import { TeamService } from './teams/TeamService';
import { TeamJoinService } from './teams/TeamJoinService';
import { LeagueService } from './leagues/LeagueService';
import { DriverService } from './drivers/DriverService';
import { LeagueMembershipService } from './leagues/LeagueMembershipService';
import { LeagueService } from './leagues/LeagueService';
import { LeagueSettingsService } from './leagues/LeagueSettingsService';
import { LeagueStewardingService } from './leagues/LeagueStewardingService';
import { LeagueWalletService } from './leagues/LeagueWalletService';
import { AvatarService } from './media/AvatarService';
import { MediaService } from './media/MediaService';
import { MembershipFeeService } from './payments/MembershipFeeService';
import { PaymentService } from './payments/PaymentService';
import { WalletService } from './payments/WalletService';
import { PenaltyService } from './penalties/PenaltyService';
import { ProtestService } from './protests/ProtestService';
import { RaceResultsService } from './races/RaceResultsService';
import { RaceService } from './races/RaceService';
import { RaceStewardingService } from './races/RaceStewardingService';
import { SponsorService } from './sponsors/SponsorService';
import { SponsorshipService } from './sponsors/SponsorshipService';
import { PaymentService } from './payments/PaymentService';
import { AnalyticsService } from './analytics/AnalyticsService';
import { DashboardService } from './dashboard/DashboardService';
import { MediaService } from './media/MediaService';
import { AvatarService } from './media/AvatarService';
import { WalletService } from './payments/WalletService';
import { MembershipFeeService } from './payments/MembershipFeeService';
import { AuthService } from './auth/AuthService';
import { SessionService } from './auth/SessionService';
import { ProtestService } from './protests/ProtestService';
import { PenaltyService } from './penalties/PenaltyService';
import { TeamJoinService } from './teams/TeamJoinService';
import { TeamService } from './teams/TeamService';
export interface Services {
raceService: RaceService;
@@ -115,7 +115,7 @@ export function ServiceProvider({ children }: ServiceProviderProps) {
</QueryClientProvider>
);
}
// Before using this check for enhanced hooks that use react-query
export function useServices(): Services {
const services = useContext(ServicesContext);
if (!services) {

View File

@@ -1,8 +1,8 @@
import { AuthApiClient } from '../../api/auth/AuthApiClient';
import { SessionViewModel } from '../../view-models/SessionViewModel';
import type { LoginParams } from '../../types/generated/LoginParams';
import type { SignupParams } from '../../types/generated/SignupParams';
import type { LoginWithIracingCallbackParams } from '../../types/generated/LoginWithIracingCallbackParams';
import type { LoginParamsDTO } from '../../types/generated/LoginParamsDTO';
import type { SignupParamsDTO } from '../../types/generated/SignupParamsDTO';
import type { LoginWithIracingCallbackParamsDTO } from '../../types/generated/LoginWithIracingCallbackParamsDTO';
/**
* Auth Service
@@ -18,7 +18,7 @@ export class AuthService {
/**
* Sign up a new user
*/
async signup(params: SignupParams): Promise<SessionViewModel> {
async signup(params: SignupParamsDTO): Promise<SessionViewModel> {
try {
const dto = await this.apiClient.signup(params);
return new SessionViewModel(dto.user);
@@ -30,7 +30,7 @@ export class AuthService {
/**
* Log in an existing user
*/
async login(params: LoginParams): Promise<SessionViewModel> {
async login(params: LoginParamsDTO): Promise<SessionViewModel> {
try {
const dto = await this.apiClient.login(params);
return new SessionViewModel(dto.user);
@@ -60,7 +60,7 @@ export class AuthService {
/**
* Login with iRacing callback
*/
async loginWithIracingCallback(params: LoginWithIracingCallbackParams): Promise<SessionViewModel> {
async loginWithIracingCallback(params: LoginWithIracingCallbackParamsDTO): Promise<SessionViewModel> {
try {
const dto = await this.apiClient.loginWithIracingCallback(params);
return new SessionViewModel(dto.user);

View File

@@ -1,7 +1,6 @@
import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
import { CompleteOnboardingInputDTO } from "@/lib/types/generated/CompleteOnboardingInputDTO";
import { DriverProfileDTO } from "@/lib/types/generated/DriverProfileDTO";
import type { DriverDTO } from "@/lib/types/generated/DriverDTO";
import { DriversApiClient } from '@/lib/api/drivers/DriversApiClient';
import type { CompleteOnboardingInputDTO } from '@/lib/types/generated/CompleteOnboardingInputDTO';
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
import { CompleteOnboardingViewModel } from "@/lib/view-models/CompleteOnboardingViewModel";
import { DriverLeaderboardViewModel } from "@/lib/view-models/DriverLeaderboardViewModel";
import { DriverViewModel } from "@/lib/view-models/DriverViewModel";
@@ -50,7 +49,92 @@ export class DriverService {
*/
async getDriverProfile(driverId: string): Promise<DriverProfileViewModel> {
const dto = await this.apiClient.getDriverProfile(driverId);
return new DriverProfileViewModel(dto);
return new DriverProfileViewModel({
currentDriver: dto.currentDriver
? {
id: dto.currentDriver.id,
name: dto.currentDriver.name,
country: dto.currentDriver.country,
avatarUrl: dto.currentDriver.avatarUrl,
iracingId: dto.currentDriver.iracingId ?? null,
joinedAt: dto.currentDriver.joinedAt,
rating: dto.currentDriver.rating ?? null,
globalRank: dto.currentDriver.globalRank ?? null,
consistency: dto.currentDriver.consistency ?? null,
bio: dto.currentDriver.bio ?? null,
totalDrivers: dto.currentDriver.totalDrivers ?? null,
}
: null,
stats: dto.stats
? {
totalRaces: dto.stats.totalRaces,
wins: dto.stats.wins,
podiums: dto.stats.podiums,
dnfs: dto.stats.dnfs,
avgFinish: dto.stats.avgFinish ?? null,
bestFinish: dto.stats.bestFinish ?? null,
worstFinish: dto.stats.worstFinish ?? null,
finishRate: dto.stats.finishRate ?? null,
winRate: dto.stats.winRate ?? null,
podiumRate: dto.stats.podiumRate ?? null,
percentile: dto.stats.percentile ?? null,
rating: dto.stats.rating ?? null,
consistency: dto.stats.consistency ?? null,
overallRank: dto.stats.overallRank ?? null,
}
: null,
finishDistribution: dto.finishDistribution
? {
totalRaces: dto.finishDistribution.totalRaces,
wins: dto.finishDistribution.wins,
podiums: dto.finishDistribution.podiums,
topTen: dto.finishDistribution.topTen,
dnfs: dto.finishDistribution.dnfs,
other: dto.finishDistribution.other,
}
: null,
teamMemberships: dto.teamMemberships.map((m) => ({
teamId: m.teamId,
teamName: m.teamName,
teamTag: m.teamTag ?? null,
role: m.role,
joinedAt: m.joinedAt,
isCurrent: m.isCurrent,
})),
socialSummary: {
friendsCount: dto.socialSummary.friendsCount,
friends: dto.socialSummary.friends.map((f) => ({
id: f.id,
name: f.name,
country: f.country,
avatarUrl: f.avatarUrl,
})),
},
extendedProfile: dto.extendedProfile
? {
socialHandles: dto.extendedProfile.socialHandles.map((h) => ({
platform: h.platform as any,
handle: h.handle,
url: h.url,
})),
achievements: dto.extendedProfile.achievements.map((a) => ({
id: a.id,
title: a.title,
description: a.description,
icon: a.icon as any,
rarity: a.rarity as any,
earnedAt: a.earnedAt,
})),
racingStyle: dto.extendedProfile.racingStyle,
favoriteTrack: dto.extendedProfile.favoriteTrack,
favoriteCar: dto.extendedProfile.favoriteCar,
timezone: dto.extendedProfile.timezone,
availableHours: dto.extendedProfile.availableHours,
lookingForTeam: dto.extendedProfile.lookingForTeam,
openToRequests: dto.extendedProfile.openToRequests,
}
: null,
});
}
/**
@@ -65,15 +149,15 @@ export class DriverService {
/**
* Find driver by ID
*/
async findById(id: string): Promise<DriverDTO | null> {
async findById(id: string): Promise<GetDriverOutputDTO | null> {
return this.apiClient.getDriver(id);
}
/**
* Find multiple drivers by IDs
*/
async findByIds(ids: string[]): Promise<DriverDTO[]> {
async findByIds(ids: string[]): Promise<GetDriverOutputDTO[]> {
const drivers = await Promise.all(ids.map(id => this.apiClient.getDriver(id)));
return drivers.filter((d): d is DriverDTO => d !== null);
return drivers.filter((d): d is GetDriverOutputDTO => d !== null);
}
}
}

View File

@@ -1,31 +1,17 @@
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
import { LeaguesApiClient } from '@/lib/api/leagues/LeaguesApiClient';
import { TeamsApiClient } from '@/lib/api/teams/TeamsApiClient';
import type { AllLeaguesWithCapacityDTO } from '@/lib/types/generated/AllLeaguesWithCapacityDTO';
import type { GetAllTeamsOutputDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
import type { RacesPageDataDTO } from '@/lib/types/generated/RacesPageDataDTO';
import type { LeagueSummaryDTO } from '@/lib/types/generated/LeagueSummaryDTO';
import type { GetAllTeamsOutputDTO, TeamListItemDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
import { RacesPageViewModel } from '@/lib/view-models/RacesPageViewModel';
import { HomeDiscoveryViewModel } from '@/lib/view-models/HomeDiscoveryViewModel';
import { LeagueCardViewModel } from '@/lib/view-models/LeagueCardViewModel';
import { TeamCardViewModel } from '@/lib/view-models/TeamCardViewModel';
import { UpcomingRaceCardViewModel } from '@/lib/view-models/UpcomingRaceCardViewModel';
// DTO matching backend RacesPageDataDTO for discovery usage
interface RacesPageDataDTO {
races: {
id: string;
track: string;
car: string;
scheduledAt: string;
status: string;
leagueId: string;
leagueName: string;
strengthOfField: number | null;
isUpcoming: boolean;
isLive: boolean;
isPast: boolean;
}[];
}
export class LandingService {
constructor(
private readonly racesApi: RacesApiClient,
@@ -36,21 +22,21 @@ export class LandingService {
async getHomeDiscovery(): Promise<HomeDiscoveryViewModel> {
const [racesDto, leaguesDto, teamsDto] = await Promise.all([
this.racesApi.getPageData() as Promise<RacesPageDataDTO>,
this.leaguesApi.getAllWithCapacity() as Promise<{ leagues: LeagueSummaryDTO[] }>,
this.teamsApi.getAll(),
this.leaguesApi.getAllWithCapacity() as Promise<AllLeaguesWithCapacityDTO>,
this.teamsApi.getAll() as Promise<GetAllTeamsOutputDTO>,
]);
const racesVm = new RacesPageViewModel(racesDto);
const topLeagues = leaguesDto.leagues.slice(0, 4).map(
league => new LeagueCardViewModel({
(league: LeagueSummaryDTO) => new LeagueCardViewModel({
id: league.id,
name: league.name,
description: 'Competitive iRacing league',
}),
);
const teams = (teamsDto as GetAllTeamsOutputDTO).teams.slice(0, 4).map(
const teams = teamsDto.teams.slice(0, 4).map(
(team: TeamListItemDTO) =>
new TeamCardViewModel({
id: team.id,

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;
}
}
}
}

View File

@@ -2,6 +2,7 @@ import { RacesApiClient } from '../../api/races/RacesApiClient';
import { RaceDetailViewModel } from '../../view-models/RaceDetailViewModel';
import { RacesPageViewModel } from '../../view-models/RacesPageViewModel';
import { RaceStatsViewModel } from '../../view-models/RaceStatsViewModel';
import type { FileProtestCommandDTO } from '../../types/generated/FileProtestCommandDTO';
import type { RaceStatsDTO } from '../../types/generated/RaceStatsDTO';
/**
* Race Service
@@ -33,6 +34,14 @@ export class RaceService {
return new RacesPageViewModel(dto);
}
/**
* Get races page data filtered by league
*/
async getLeagueRacesPageData(leagueId: string): Promise<RacesPageViewModel> {
const dto = await this.apiClient.getPageData(leagueId);
return new RacesPageViewModel(dto);
}
/**
* Get all races page data with view model transformation
* Currently same as getRacesPageData, but can be extended for different filtering
@@ -54,14 +63,14 @@ export class RaceService {
* Register for a race
*/
async registerForRace(raceId: string, leagueId: string, driverId: string): Promise<void> {
await this.apiClient.register(raceId, { leagueId, driverId });
await this.apiClient.register(raceId, { raceId, leagueId, driverId });
}
/**
* Withdraw from a race
*/
async withdrawFromRace(raceId: string, driverId: string): Promise<void> {
await this.apiClient.withdraw(raceId, { driverId });
await this.apiClient.withdraw(raceId, { raceId, driverId });
}
/**
@@ -85,6 +94,13 @@ export class RaceService {
await this.apiClient.reopen(raceId);
}
/**
* File a protest
*/
async fileProtest(input: FileProtestCommandDTO): Promise<void> {
await this.apiClient.fileProtest(input);
}
/**
* Find races by league ID
*
@@ -92,7 +108,8 @@ export class RaceService {
* so this method deliberately signals that the operation is unavailable instead of making
* assumptions about URL structure.
*/
async findByLeagueId(_leagueId: string): Promise<never> {
throw new Error('Finding races by league ID is not supported in this build');
async findByLeagueId(leagueId: string): Promise<RacesPageViewModel['races']> {
const page = await this.getLeagueRacesPageData(leagueId);
return page.races;
}
}
}

View File

@@ -1,9 +1,9 @@
import type { SponsorsApiClient } from '../../api/sponsors/SponsorsApiClient';
import type { GetEntitySponsorshipPricingResultDto } from '../../api/sponsors/SponsorsApiClient';
import { SponsorshipPricingViewModel } from '../../view-models/SponsorshipPricingViewModel';
import { SponsorSponsorshipsViewModel } from '../../view-models/SponsorSponsorshipsViewModel';
import { SponsorshipRequestViewModel } from '../../view-models/SponsorshipRequestViewModel';
import type { SponsorSponsorshipsDTO } from '../../types/generated';
import type { GetPendingSponsorshipRequestsOutputDTO } from '../../types/generated/GetPendingSponsorshipRequestsOutputDTO';
import type { SponsorshipRequestDTO } from '../../types/generated/SponsorshipRequestDTO';
/**
* Sponsorship Service
@@ -20,8 +20,16 @@ export class SponsorshipService {
* Get sponsorship pricing with view model transformation
*/
async getSponsorshipPricing(): Promise<SponsorshipPricingViewModel> {
// Pricing shape isn't finalized in the API yet.
// Keep a predictable, UI-friendly structure until a dedicated DTO is introduced.
const dto = await this.apiClient.getPricing();
return new SponsorshipPricingViewModel(dto);
const main = dto.pricing.find((p) => p.entityType === 'main')?.price ?? 0;
const secondary = dto.pricing.find((p) => p.entityType === 'secondary')?.price ?? 0;
return new SponsorshipPricingViewModel({
mainSlotPrice: main,
secondarySlotPrice: secondary,
currency: 'USD',
});
}
/**
@@ -39,21 +47,22 @@ export class SponsorshipService {
* Get pending sponsorship requests for an entity
*/
async getPendingSponsorshipRequests(params: { entityType: string; entityId: string }): Promise<SponsorshipRequestViewModel[]> {
const dto = await this.apiClient.getPendingSponsorshipRequests(params);
return dto.requests.map(dto => new SponsorshipRequestViewModel(dto));
const dto = (await this.apiClient.getPendingSponsorshipRequests(params)) as unknown as GetPendingSponsorshipRequestsOutputDTO;
const requests = (dto as any).requests as SponsorshipRequestDTO[];
return (requests ?? []).map((r: SponsorshipRequestDTO) => new SponsorshipRequestViewModel(r));
}
/**
* Accept a sponsorship request
*/
async acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<void> {
await this.apiClient.acceptSponsorshipRequest(requestId, respondedBy);
await this.apiClient.acceptSponsorshipRequest(requestId, { respondedBy });
}
/**
* Reject a sponsorship request
*/
async rejectSponsorshipRequest(requestId: string, respondedBy: string, reason?: string): Promise<void> {
await this.apiClient.rejectSponsorshipRequest(requestId, respondedBy, reason);
await this.apiClient.rejectSponsorshipRequest(requestId, { respondedBy, ...(reason ? { reason } : {}) });
}
}
}

View File

@@ -4,11 +4,12 @@ import { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
import { CreateTeamViewModel } from '@/lib/view-models/CreateTeamViewModel';
import { UpdateTeamViewModel } from '@/lib/view-models/UpdateTeamViewModel';
import { DriverTeamViewModel } from '@/lib/view-models/DriverTeamViewModel';
import { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
import type { GetAllTeamsOutputDTO, TeamListItemDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
import type { GetAllTeamsOutputDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
import type { GetTeamMembersOutputDTO, TeamMemberDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
import type { GetTeamMembersOutputDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
import type { TeamMemberDTO } from '@/lib/types/generated/TeamMemberDTO';
import type { CreateTeamInputDTO } from '@/lib/types/generated/CreateTeamInputDTO';
import type { CreateTeamOutputDTO } from '@/lib/types/generated/CreateTeamOutputDTO';
import type { UpdateTeamInputDTO } from '@/lib/types/generated/UpdateTeamInputDTO';
@@ -71,19 +72,19 @@ export class TeamService {
}
/**
* Get driver's team with view model transformation
*/
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
const dto: GetDriverTeamOutputDTO | null = await this.apiClient.getDriverTeam(driverId);
return dto ? new DriverTeamViewModel(dto) : null;
}
* Get driver's team with view model transformation
*/
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
const dto: GetDriverTeamOutputDTO | null = await this.apiClient.getDriverTeam(driverId);
return dto ? new DriverTeamViewModel(dto) : null;
}
/**
* Get team membership for a driver
*/
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
return this.apiClient.getMembership(teamId, driverId);
}
/**
* Get team membership for a driver
*/
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
return this.apiClient.getMembership(teamId, driverId);
}
/**
* Remove a driver from the team
@@ -91,11 +92,11 @@ export class TeamService {
* The backend does not yet expose a dedicated endpoint for removing team memberships,
* so this method fails explicitly to avoid silently ignoring removal requests.
*/
async removeMembership(teamId: string, driverId: string): Promise<void> {
void teamId;
void driverId;
throw new Error('Team membership removal is not supported in this build');
}
async removeMembership(teamId: string, driverId: string): Promise<void> {
void teamId;
void driverId;
throw new Error('Team membership removal is not supported in this build');
}
/**
* Update team membership role
@@ -103,10 +104,10 @@ export class TeamService {
* Role updates for team memberships are not supported by the current API surface;
* callers must treat this as an unavailable operation.
*/
async updateMembership(teamId: string, driverId: string, role: string): Promise<void> {
void teamId;
void driverId;
void role;
throw new Error('Team membership role updates are not supported in this build');
}
}
async updateMembership(teamId: string, driverId: string, role: string): Promise<void> {
void teamId;
void driverId;
void role;
throw new Error('Team membership role updates are not supported in this build');
}
}

View File

@@ -1,32 +1,34 @@
export interface LeagueConfigFormModel {
leagueId?: string;
basics?: {
basics: {
name: string;
description?: string;
visibility: 'public' | 'private' | 'unlisted';
gameId: string;
};
structure?: {
structure: {
mode: 'solo' | 'fixedTeams';
maxDrivers?: number;
maxTeams?: number;
driversPerTeam?: number;
/** Prototype flag kept for backward compatibility with older wizard UI */
multiClassEnabled?: boolean;
};
championships?: {
championships: {
enableDriverChampionship: boolean;
enableTeamChampionship: boolean;
enableNationsChampionship: boolean;
enableTrophyChampionship: boolean;
};
scoring?: {
scoring: {
patternId?: string;
customScoringEnabled?: boolean;
};
dropPolicy?: {
dropPolicy: {
strategy: 'none' | 'bestNResults' | 'dropWorstN';
n?: number;
};
timings?: {
timings: {
practiceMinutes?: number;
qualifyingMinutes?: number;
sprintRaceMinutes?: number;
@@ -39,9 +41,24 @@ export interface LeagueConfigFormModel {
recurrenceStrategy?: string;
timezoneId?: string;
seasonStartDate?: string;
/** Prototype fields used by scheduling UI */
seasonEndDate?: string;
raceStartTime?: string;
};
stewarding?: {
decisionMode: 'owner_only' | 'admin_vote' | 'steward_panel';
stewarding: {
/**
* Matches API `LeagueConfigFormModelStewardingDTO.decisionMode`.
* Website should not depend on core enums.
*/
decisionMode:
| 'single_steward'
| 'committee_vote'
// legacy wizard values
| 'owner_only'
| 'admin_vote'
| 'steward_panel'
| 'admin_only'
| 'steward_vote';
requiredVotes?: number;
requireDefense: boolean;
defenseTimeLimit: number;
@@ -51,4 +68,4 @@ export interface LeagueConfigFormModel {
notifyAccusedOnProtest: boolean;
notifyOnVoteRequired: boolean;
};
}
}

View File

@@ -6,4 +6,8 @@
export interface ActivityItemDTO {
id: string;
type: string;
message: string;
time: string;
impressions?: number;
}

View File

@@ -0,0 +1,12 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueSummaryDTO } from './LeagueSummaryDTO';
export interface AllLeaguesWithCapacityAndScoringDTO {
leagues: LeagueSummaryDTO[];
totalCount: number;
}

View File

@@ -0,0 +1,12 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueWithCapacityDTO } from './LeagueWithCapacityDTO';
export interface AllLeaguesWithCapacityDTO {
leagues: LeagueWithCapacityDTO[];
totalCount: number;
}

View File

@@ -0,0 +1,13 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { AllRacesStatusFilterDTO } from './AllRacesStatusFilterDTO';
import type { AllRacesLeagueFilterDTO } from './AllRacesLeagueFilterDTO';
export interface AllRacesFilterOptionsDTO {
statuses: AllRacesStatusFilterDTO[];
leagues: AllRacesLeagueFilterDTO[];
}

View File

@@ -12,4 +12,5 @@ export interface AllRacesListItemDTO {
status: string;
leagueId: string;
leagueName: string;
strengthOfField?: number;
}

View File

@@ -0,0 +1,13 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { AllRacesListItemDTO } from './AllRacesListItemDTO';
import type { AllRacesFilterOptionsDTO } from './AllRacesFilterOptionsDTO';
export interface AllRacesPageDTO {
races: AllRacesListItemDTO[];
filters: AllRacesFilterOptionsDTO;
}

View File

@@ -9,4 +9,9 @@ export interface ApplyPenaltyCommandDTO {
driverId: string;
stewardId: string;
enum: string;
type: string;
value?: number;
reason: string;
protestId?: string;
notes?: string;
}

View File

@@ -6,4 +6,5 @@
export interface ApproveJoinRequestOutputDTO {
success: boolean;
message?: string;
}

View File

@@ -8,6 +8,16 @@ export interface AvailableLeagueDTO {
id: string;
name: string;
game: string;
drivers: string;
avgViewsPerRace: string;
drivers: number;
avgViewsPerRace: number;
mainSponsorSlot: Record<string, unknown>;
available: number;
price: number;
secondarySlots: Record<string, unknown>;
total: number;
rating: number;
tier: string;
nextRace?: string;
seasonStatus: string;
description: string;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { PrizeDTO } from './PrizeDTO';
export interface AwardPrizeResultDTO {
prize: PrizeDTO;
}

View File

@@ -5,10 +5,10 @@
*/
export interface BillingStatsDTO {
totalSpent: string;
pendingAmount: string;
totalSpent: number;
pendingAmount: number;
nextPaymentDate: string;
nextPaymentAmount: string;
activeSponsorships: string;
averageMonthlySpend: string;
nextPaymentAmount: number;
activeSponsorships: number;
averageMonthlySpend: number;
}

View File

@@ -9,4 +9,6 @@ export interface CompleteOnboardingInputDTO {
lastName: string;
displayName: string;
country: string;
timezone?: string;
bio?: string;
}

View File

@@ -6,4 +6,6 @@
export interface CompleteOnboardingOutputDTO {
success: boolean;
driverId?: string;
errorMessage?: string;
}

View File

@@ -7,4 +7,6 @@
export interface CreateLeagueInputDTO {
name: string;
description: string;
visibility: string;
ownerId: string;
}

View File

@@ -0,0 +1,14 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface CreatePaymentInputDTO {
type: string;
amount: number;
payerId: string;
payerType: string;
leagueId: string;
seasonId?: string;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { PaymentDTO } from './PaymentDTO';
export interface CreatePaymentOutputDTO {
payment: PaymentDTO;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { PrizeDTO } from './PrizeDTO';
export interface CreatePrizeResultDTO {
prize: PrizeDTO;
}

View File

@@ -7,4 +7,6 @@
export interface CreateSponsorInputDTO {
name: string;
contactEmail: string;
websiteUrl?: string;
logoUrl?: string;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { SponsorDTO } from './SponsorDTO';
export interface CreateSponsorOutputDTO {
sponsor: SponsorDTO;
}

View File

@@ -7,4 +7,5 @@
export interface CreateTeamInputDTO {
name: string;
tag: string;
description?: string;
}

View File

@@ -9,4 +9,10 @@ export interface DashboardDriverSummaryDTO {
name: string;
country: string;
avatarUrl: string;
rating?: number;
globalRank?: number;
totalRaces: number;
wins: number;
podiums: number;
consistency?: number;
}

View File

@@ -7,4 +7,10 @@
export interface DashboardFeedItemSummaryDTO {
id: string;
enum: string;
type: string;
headline: string;
body?: string;
timestamp: string;
ctaLabel?: string;
ctaHref?: string;
}

View File

@@ -4,6 +4,9 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DashboardFeedItemSummaryDTO } from './DashboardFeedItemSummaryDTO';
export interface DashboardFeedSummaryDTO {
notificationCount: number;
items: DashboardFeedItemSummaryDTO[];
}

View File

@@ -0,0 +1,25 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DashboardDriverSummaryDTO } from './DashboardDriverSummaryDTO';
import type { DashboardRaceSummaryDTO } from './DashboardRaceSummaryDTO';
import type { DashboardRecentResultDTO } from './DashboardRecentResultDTO';
import type { DashboardLeagueStandingSummaryDTO } from './DashboardLeagueStandingSummaryDTO';
import type { DashboardFeedSummaryDTO } from './DashboardFeedSummaryDTO';
import type { DashboardFriendSummaryDTO } from './DashboardFriendSummaryDTO';
export interface DashboardOverviewDTO {
currentDriver?: DashboardDriverSummaryDTO;
myUpcomingRaces: DashboardRaceSummaryDTO[];
otherUpcomingRaces: DashboardRaceSummaryDTO[];
upcomingRaces: DashboardRaceSummaryDTO[];
activeLeaguesCount: number;
nextRace?: DashboardRaceSummaryDTO;
recentResults: DashboardRecentResultDTO[];
leagueStandingsSummaries: DashboardLeagueStandingSummaryDTO[];
feedSummary: DashboardFeedSummaryDTO;
friends: DashboardFriendSummaryDTO[];
}

View File

@@ -11,4 +11,6 @@ export interface DashboardRaceSummaryDTO {
track: string;
car: string;
scheduledAt: string;
status: string;
isMyLeague: boolean;
}

View File

@@ -5,5 +5,6 @@
*/
export interface DeleteMediaOutputDTO {
success: string;
success: boolean;
error?: string;
}

View File

@@ -6,10 +6,9 @@
export interface DriverDTO {
id: string;
iracingId: string;
name: string;
country: string;
position: string;
races: string;
impressions: string;
team: string;
bio?: string;
joinedAt: string;
}

View File

@@ -15,4 +15,5 @@ export interface DriverLeaderboardItemDTO {
podiums: number;
isActive: boolean;
rank: number;
avatarUrl?: string;
}

View File

@@ -8,4 +8,7 @@ export interface DriverProfileAchievementDTO {
id: string;
title: string;
description: string;
icon: string;
rarity: string;
earnedAt: string;
}

View File

@@ -9,4 +9,11 @@ export interface DriverProfileDriverSummaryDTO {
name: string;
country: string;
avatarUrl: string;
iracingId?: string;
joinedAt: string;
rating?: number;
globalRank?: number;
consistency?: number;
bio?: string;
totalDrivers?: number;
}

View File

@@ -0,0 +1,20 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DriverProfileSocialHandleDTO } from './DriverProfileSocialHandleDTO';
import type { DriverProfileAchievementDTO } from './DriverProfileAchievementDTO';
export interface DriverProfileExtendedProfileDTO {
socialHandles: DriverProfileSocialHandleDTO[];
achievements: DriverProfileAchievementDTO[];
racingStyle: string;
favoriteTrack: string;
favoriteCar: string;
timezone: string;
availableHours: string;
lookingForTeam: boolean;
openToRequests: boolean;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface DriverProfileSocialHandleDTO {
platform: string;
handle: string;
url: string;
}

View File

@@ -4,6 +4,9 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DriverProfileSocialFriendSummaryDTO } from './DriverProfileSocialFriendSummaryDTO';
export interface DriverProfileSocialSummaryDTO {
friendsCount: number;
friends: DriverProfileSocialFriendSummaryDTO[];
}

View File

@@ -9,4 +9,14 @@ export interface DriverProfileStatsDTO {
wins: number;
podiums: number;
dnfs: number;
avgFinish?: number;
bestFinish?: number;
worstFinish?: number;
finishRate?: number;
winRate?: number;
podiumRate?: number;
percentile?: number;
rating?: number;
consistency?: number;
overallRank?: number;
}

View File

@@ -7,4 +7,8 @@
export interface DriverProfileTeamMembershipDTO {
teamId: string;
teamName: string;
teamTag?: string;
role: string;
joinedAt: string;
isCurrent: boolean;
}

View File

@@ -0,0 +1,14 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DriverLeaderboardItemDTO } from './DriverLeaderboardItemDTO';
export interface DriversLeaderboardDTO {
drivers: DriverLeaderboardItemDTO[];
totalRaces: number;
totalWins: number;
activeCount: number;
}

View File

@@ -4,12 +4,13 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { ProtestIncidentDTO } from './ProtestIncidentDTO';
export interface FileProtestCommandDTO {
raceId: string;
protestingDriverId: string;
accusedDriverId: string;
incident: Record<string, unknown>;
lap: number;
description: string;
timeInRace?: number;
incident: ProtestIncidentDTO;
comment?: string;
proofVideoUrl?: string;
}

View File

@@ -0,0 +1,12 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { TeamListItemDTO } from './TeamListItemDTO';
export interface GetAllTeamsOutputDTO {
teams: TeamListItemDTO[];
totalCount: number;
}

View File

@@ -0,0 +1,21 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DriverProfileDriverSummaryDTO } from './DriverProfileDriverSummaryDTO';
import type { DriverProfileStatsDTO } from './DriverProfileStatsDTO';
import type { DriverProfileFinishDistributionDTO } from './DriverProfileFinishDistributionDTO';
import type { DriverProfileTeamMembershipDTO } from './DriverProfileTeamMembershipDTO';
import type { DriverProfileSocialSummaryDTO } from './DriverProfileSocialSummaryDTO';
import type { DriverProfileExtendedProfileDTO } from './DriverProfileExtendedProfileDTO';
export interface GetDriverProfileOutputDTO {
currentDriver?: DriverProfileDriverSummaryDTO;
stats?: DriverProfileStatsDTO;
finishDistribution?: DriverProfileFinishDistributionDTO;
teamMemberships: DriverProfileTeamMembershipDTO[];
socialSummary: DriverProfileSocialSummaryDTO;
extendedProfile?: DriverProfileExtendedProfileDTO;
}

View File

@@ -0,0 +1,15 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { TeamDTO } from './TeamDTO';
import type { TeamMembershipDTO } from './TeamMembershipDTO';
export interface GetDriverTeamOutputDTO {
team: TeamDTO;
membership: TeamMembershipDTO;
isOwner: boolean;
canManage: boolean;
}

View File

@@ -4,7 +4,10 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { SponsorshipPricingItemDTO } from './SponsorshipPricingItemDTO';
export interface GetEntitySponsorshipPricingResultDTO {
entityType: string;
entityId: string;
pricing: SponsorshipPricingItemDTO[];
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueConfigFormModelDTO } from './LeagueConfigFormModelDTO';
export interface GetLeagueAdminConfigOutputDTO {
form?: LeagueConfigFormModelDTO;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { RaceDTO } from './RaceDTO';
export interface GetLeagueRacesOutputDTO {
races: RaceDTO[];
}

View File

@@ -4,6 +4,8 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { WalletTransactionDTO } from './WalletTransactionDTO';
export interface GetLeagueWalletOutputDTO {
balance: number;
currency: string;
@@ -12,4 +14,6 @@ export interface GetLeagueWalletOutputDTO {
totalWithdrawals: number;
pendingPayouts: number;
canWithdraw: boolean;
withdrawalBlockReason?: string;
transactions: WalletTransactionDTO[];
}

View File

@@ -9,6 +9,7 @@ export interface GetMediaOutputDTO {
url: string;
type: string;
category?: string;
/** Format: date-time */
uploadedAt: string;
size?: number;
}

View File

@@ -0,0 +1,13 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { MembershipFeeDTO } from './MembershipFeeDTO';
import type { MemberPaymentDTO } from './MemberPaymentDTO';
export interface GetMembershipFeesResultDTO {
fee?: MembershipFeeDTO;
payments: MemberPaymentDTO[];
}

View File

@@ -4,7 +4,11 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { SponsorshipRequestDTO } from './SponsorshipRequestDTO';
export interface GetPendingSponsorshipRequestsOutputDTO {
entityType: string;
entityId: string;
requests: SponsorshipRequestDTO[];
totalCount: number;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { PrizeDTO } from './PrizeDTO';
export interface GetPrizesResultDTO {
prizes: PrizeDTO[];
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { SponsorshipDetailDTO } from './SponsorshipDetailDTO';
export interface GetSeasonSponsorshipsOutputDTO {
sponsorships: SponsorshipDetailDTO[];
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { SponsorDTO } from './SponsorDTO';
export interface GetSponsorOutputDTO {
sponsor: SponsorDTO;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { SponsorDTO } from './SponsorDTO';
export interface GetSponsorsOutputDTO {
sponsors: SponsorDTO[];
}

View File

@@ -0,0 +1,14 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { TeamDTO } from './TeamDTO';
import type { TeamMembershipDTO } from './TeamMembershipDTO';
export interface GetTeamDetailsOutputDTO {
team: TeamDTO;
membership?: TeamMembershipDTO;
canManage: boolean;
}

View File

@@ -0,0 +1,13 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { TeamJoinRequestDTO } from './TeamJoinRequestDTO';
export interface GetTeamJoinRequestsOutputDTO {
requests: TeamJoinRequestDTO[];
pendingCount: number;
totalCount: number;
}

View File

@@ -0,0 +1,15 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { TeamMemberDTO } from './TeamMemberDTO';
export interface GetTeamMembersOutputDTO {
members: TeamMemberDTO[];
totalCount: number;
ownerCount: number;
managerCount: number;
memberCount: number;
}

View File

@@ -0,0 +1,14 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { TeamLeaderboardItemDTO } from './TeamLeaderboardItemDTO';
export interface GetTeamsLeaderboardOutputDTO {
teams: TeamLeaderboardItemDTO[];
recruitingCount: number;
groupsBySkillLevel: string;
topTeams: TeamLeaderboardItemDTO[];
}

View File

@@ -0,0 +1,13 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { WalletDTO } from './WalletDTO';
import type { TransactionDTO } from './TransactionDTO';
export interface GetWalletResultDTO {
wallet: WalletDTO;
transactions: TransactionDTO[];
}

View File

@@ -9,4 +9,5 @@ export interface ImportRaceResultsSummaryDTO {
raceId: string;
driversProcessed: number;
resultsRecorded: number;
errors?: string[];
}

View File

@@ -9,7 +9,11 @@ export interface InvoiceDTO {
invoiceNumber: string;
date: string;
dueDate: string;
amount: string;
vatAmount: string;
totalAmount: string;
amount: number;
vatAmount: number;
totalAmount: number;
status: string;
description: string;
sponsorshipType: string;
pdfUrl: string;
}

View File

@@ -0,0 +1,10 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface IracingAuthRedirectResultDTO {
redirectUrl: string;
state: string;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueConfigFormModelDTO } from './LeagueConfigFormModelDTO';
export interface LeagueAdminConfigDTO {
form?: LeagueConfigFormModelDTO;
}

View File

@@ -0,0 +1,19 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueJoinRequestDTO } from './LeagueJoinRequestDTO';
import type { LeagueOwnerSummaryDTO } from './LeagueOwnerSummaryDTO';
import type { LeagueAdminConfigDTO } from './LeagueAdminConfigDTO';
import type { LeagueAdminProtestsDTO } from './LeagueAdminProtestsDTO';
import type { LeagueSeasonSummaryDTO } from './LeagueSeasonSummaryDTO';
export interface LeagueAdminDTO {
joinRequests: LeagueJoinRequestDTO[];
ownerSummary?: LeagueOwnerSummaryDTO;
config: LeagueAdminConfigDTO;
protests: LeagueAdminProtestsDTO;
seasons: LeagueSeasonSummaryDTO[];
}

View File

@@ -0,0 +1,15 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { ProtestDTO } from './ProtestDTO';
import type { RaceDTO } from './RaceDTO';
import type { DriverDTO } from './DriverDTO';
export interface LeagueAdminProtestsDTO {
protests: ProtestDTO[];
racesById: RaceDTO;
driversById: DriverDTO;
}

View File

@@ -7,4 +7,5 @@
export interface LeagueConfigFormModelBasicsDTO {
name: string;
description: string;
visibility: string;
}

View File

@@ -4,6 +4,20 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueConfigFormModelBasicsDTO } from './LeagueConfigFormModelBasicsDTO';
import type { LeagueConfigFormModelStructureDTO } from './LeagueConfigFormModelStructureDTO';
import type { LeagueConfigFormModelScoringDTO } from './LeagueConfigFormModelScoringDTO';
import type { LeagueConfigFormModelDropPolicyDTO } from './LeagueConfigFormModelDropPolicyDTO';
import type { LeagueConfigFormModelTimingsDTO } from './LeagueConfigFormModelTimingsDTO';
import type { LeagueConfigFormModelStewardingDTO } from './LeagueConfigFormModelStewardingDTO';
export interface LeagueConfigFormModelDTO {
leagueId: string;
basics: LeagueConfigFormModelBasicsDTO;
structure: LeagueConfigFormModelStructureDTO;
championships: Record<string, unknown>[];
scoring: LeagueConfigFormModelScoringDTO;
dropPolicy: LeagueConfigFormModelDropPolicyDTO;
timings: LeagueConfigFormModelTimingsDTO;
stewarding: LeagueConfigFormModelStewardingDTO;
}

View File

@@ -0,0 +1,10 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueConfigFormModelDropPolicyDTO {
strategy: string;
n?: number;
}

View File

@@ -0,0 +1,17 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueConfigFormModelStewardingDTO {
decisionMode: string;
requiredVotes?: number;
requireDefense: boolean;
defenseTimeLimit: number;
voteTimeLimit: number;
protestDeadlineHours: number;
stewardingClosesHours: number;
notifyAccusedOnProtest: boolean;
notifyOnVoteRequired: boolean;
}

View File

@@ -8,4 +8,27 @@ export interface LeagueDetailDTO {
id: string;
name: string;
game: string;
tier: string;
season: string;
description: string;
drivers: number;
races: number;
completedRaces: number;
totalImpressions: number;
avgViewsPerRace: number;
engagement: number;
rating: number;
seasonStatus: string;
seasonDates: Record<string, unknown>;
start: string;
end: string;
nextRace?: Record<string, unknown>;
date: string;
sponsorSlots: Record<string, unknown>;
main: Record<string, unknown>;
available: number;
price: number;
benefits: string[];
secondary: Record<string, unknown>;
total: number;
}

View File

@@ -10,4 +10,8 @@ export interface LeagueJoinRequestDTO {
driverId: string;
/** Format: date-time */
requestedAt: string;
message?: string;
required: string;
type: string;
driver?: string;
}

View File

@@ -4,6 +4,12 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DriverDTO } from './DriverDTO';
export interface LeagueMemberDTO {
driverId: string;
driver: DriverDTO;
role: string;
/** Format: date-time */
joinedAt: string;
}

View File

@@ -8,4 +8,7 @@ export interface LeagueMembershipDTO {
id: string;
leagueId: string;
driverId: string;
role: string;
status: string;
joinedAt: string;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueMemberDTO } from './LeagueMemberDTO';
export interface LeagueMembershipsDTO {
members: LeagueMemberDTO[];
}

View File

@@ -0,0 +1,13 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { DriverDTO } from './DriverDTO';
export interface LeagueOwnerSummaryDTO {
driver: DriverDTO;
rating?: number;
rank?: number;
}

View File

@@ -0,0 +1,9 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueRoleDTO {
value: string;
}

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { RaceDTO } from './RaceDTO';
export interface LeagueScheduleDTO {
races: RaceDTO[];
}

View File

@@ -9,5 +9,7 @@ export interface LeagueScoringChampionshipDTO {
name: string;
type: string;
sessionTypes: string[];
pointsPreview: string;
pointsPreview: Record<string, unknown>[];
bonusSummary: string[];
dropPolicyDescription: string;
}

View File

@@ -4,9 +4,15 @@
* Do not edit manually - regenerate using: npm run api:sync-types
*/
import type { LeagueScoringChampionshipDTO } from './LeagueScoringChampionshipDTO';
export interface LeagueScoringConfigDTO {
leagueId: string;
seasonId: string;
gameId: string;
gameName: string;
scoringPresetId?: string;
scoringPresetName?: string;
dropPolicySummary: string;
championships: LeagueScoringChampionshipDTO[];
}

View File

@@ -8,4 +8,8 @@ export interface LeagueScoringPresetDTO {
id: string;
name: string;
description: string;
primaryChampionshipType: string;
sessionSummary: string;
bonusSummary: string;
dropPolicySummary: string;
}

Some files were not shown because too many files have changed in this diff Show More