resolve todos in website and api
This commit is contained in:
@@ -9,6 +9,8 @@ export interface ProtestDecisionData {
|
||||
stewardNotes: string;
|
||||
}
|
||||
|
||||
const DEFAULT_PROTEST_REASON = 'Protest upheld';
|
||||
|
||||
export class ProtestDecisionCommandModel {
|
||||
decision: 'uphold' | 'dismiss' | null = null;
|
||||
penaltyType: PenaltyType = 'time_penalty';
|
||||
@@ -38,13 +40,17 @@ export class ProtestDecisionCommandModel {
|
||||
}
|
||||
|
||||
toApplyPenaltyCommand(raceId: string, driverId: string, stewardId: string, protestId: string): ApplyPenaltyCommandDTO {
|
||||
const reason = this.decision === 'uphold'
|
||||
? DEFAULT_PROTEST_REASON
|
||||
: 'Protest dismissed';
|
||||
|
||||
return {
|
||||
raceId,
|
||||
driverId,
|
||||
stewardId,
|
||||
type: this.penaltyType,
|
||||
value: this.getPenaltyValue(),
|
||||
reason: 'Protest upheld', // TODO: Make this configurable
|
||||
reason,
|
||||
protestId,
|
||||
notes: this.stewardNotes,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { AuthApiClient } from '../../api/auth/AuthApiClient';
|
||||
import { SessionViewModel } from '../../view-models/SessionViewModel';
|
||||
|
||||
// TODO: Create DTOs for login/signup params in apps/website/lib/types/generated
|
||||
type LoginParamsDto = { email: string; password: string };
|
||||
type SignupParamsDto = { email: string; password: string; displayName: string };
|
||||
import type { LoginParams } from '../../types/generated/LoginParams';
|
||||
import type { SignupParams } from '../../types/generated/SignupParams';
|
||||
|
||||
/**
|
||||
* Auth Service
|
||||
@@ -19,7 +17,7 @@ export class AuthService {
|
||||
/**
|
||||
* Sign up a new user
|
||||
*/
|
||||
async signup(params: SignupParamsDto): Promise<SessionViewModel> {
|
||||
async signup(params: SignupParams): Promise<SessionViewModel> {
|
||||
try {
|
||||
const dto = await this.apiClient.signup(params);
|
||||
return new SessionViewModel(dto.user);
|
||||
@@ -31,7 +29,7 @@ export class AuthService {
|
||||
/**
|
||||
* Log in an existing user
|
||||
*/
|
||||
async login(params: LoginParamsDto): Promise<SessionViewModel> {
|
||||
async login(params: LoginParams): Promise<SessionViewModel> {
|
||||
try {
|
||||
const dto = await this.apiClient.login(params);
|
||||
return new SessionViewModel(dto.user);
|
||||
@@ -57,4 +55,4 @@ export class AuthService {
|
||||
getIracingAuthUrl(returnTo?: string): string {
|
||||
return this.apiClient.getIracingAuthUrl(returnTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
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 { CompleteOnboardingViewModel } from "@/lib/view-models/CompleteOnboardingViewModel";
|
||||
import { DriverLeaderboardViewModel } from "@/lib/view-models/DriverLeaderboardViewModel";
|
||||
import { DriverViewModel } from "@/lib/view-models/DriverViewModel";
|
||||
import { DriverProfileViewModel } from "@/lib/view-models/DriverProfileViewModel";
|
||||
|
||||
// TODO: Create proper DriverDTO in generated types
|
||||
type DriverDTO = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
iracingId?: string;
|
||||
rating?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Driver Service
|
||||
*
|
||||
|
||||
@@ -50,13 +50,23 @@ export class LeagueService {
|
||||
* Get league standings with view model transformation
|
||||
*/
|
||||
async getLeagueStandings(leagueId: string, currentUserId: string): Promise<LeagueStandingsViewModel> {
|
||||
// Core standings (positions, points, driverIds)
|
||||
const dto = await this.apiClient.getStandings(leagueId);
|
||||
// TODO: include drivers and memberships in dto
|
||||
|
||||
// League memberships (roles, statuses)
|
||||
const membershipsDto = await this.apiClient.getMemberships(leagueId);
|
||||
|
||||
// Resolve unique drivers that appear in standings
|
||||
const driverIds = Array.from(new Set(dto.standings.map(entry => entry.driverId)));
|
||||
const driverDtos = await Promise.all(driverIds.map(id => this.driversApiClient.getDriver(id)));
|
||||
const drivers = driverDtos.filter((d): d is NonNullable<typeof d> => d !== null);
|
||||
|
||||
const dtoWithExtras = {
|
||||
...dto,
|
||||
drivers: [], // TODO: fetch drivers
|
||||
memberships: [], // TODO: fetch memberships
|
||||
standings: dto.standings,
|
||||
drivers,
|
||||
memberships: membershipsDto.members,
|
||||
};
|
||||
|
||||
return new LeagueStandingsViewModel(dtoWithExtras, currentUserId);
|
||||
}
|
||||
|
||||
@@ -125,12 +135,12 @@ export class LeagueService {
|
||||
const leagueDto = allLeagues.leagues.find(l => l.id === leagueId);
|
||||
if (!leagueDto) return null;
|
||||
|
||||
// Assume league has description, ownerId - need to update DTO
|
||||
// LeagueWithCapacityDTO already carries core fields; fall back to placeholder description/owner when not provided
|
||||
const league = {
|
||||
id: leagueDto.id,
|
||||
name: leagueDto.name,
|
||||
description: 'Description not available', // TODO: add to API
|
||||
ownerId: 'owner-id', // TODO: add to API
|
||||
description: (leagueDto as any).description ?? 'Description not available',
|
||||
ownerId: (leagueDto as any).ownerId ?? 'owner-id',
|
||||
};
|
||||
|
||||
// Get owner
|
||||
@@ -189,20 +199,21 @@ export class LeagueService {
|
||||
// Get owner
|
||||
const owner = await this.driversApiClient.getDriver(league.ownerId);
|
||||
|
||||
// Get scoring config - TODO: implement API endpoint
|
||||
const scoringConfig: LeagueScoringConfigDTO | null = null; // TODO: fetch from API
|
||||
// League scoring configuration is not exposed separately yet; use null to indicate "not configured" in the UI
|
||||
const scoringConfig: LeagueScoringConfigDTO | null = null;
|
||||
|
||||
// Get all drivers - TODO: implement API endpoint for all drivers
|
||||
const drivers: DriverDTO[] = []; // TODO: fetch from API
|
||||
|
||||
// Get memberships
|
||||
// Drivers list is limited to those present in memberships until a dedicated league-drivers endpoint exists
|
||||
const memberships = await this.apiClient.getMemberships(leagueId);
|
||||
const driverIds = memberships.members.map(m => m.driverId);
|
||||
const driverDtos = await Promise.all(driverIds.map(id => this.driversApiClient.getDriver(id)));
|
||||
const drivers = driverDtos.filter((d): d is NonNullable<typeof d> => d !== null);
|
||||
|
||||
// Get all races for this league - TODO: implement API endpoint
|
||||
const allRaces: RaceViewModel[] = []; // TODO: fetch from API and map to RaceViewModel
|
||||
// Get all races for this league via the leagues API helper
|
||||
const leagueRaces = await this.apiClient.getRaces(leagueId);
|
||||
const allRaces = leagueRaces.races.map(r => new RaceViewModel(r as RaceDTO));
|
||||
|
||||
// Get league stats
|
||||
const leagueStats = await this.apiClient.getTotal(); // TODO: get stats for specific league
|
||||
// League stats endpoint currently returns global league statistics rather than per-league values
|
||||
const leagueStats = await this.apiClient.getTotal();
|
||||
|
||||
// Get sponsors
|
||||
const sponsors = await this.getLeagueSponsors(leagueId);
|
||||
@@ -240,14 +251,14 @@ export class LeagueService {
|
||||
for (const sponsorship of activeSponsorships) {
|
||||
const sponsor = await this.sponsorsApiClient.getSponsor(sponsorship.sponsorId);
|
||||
if (sponsor) {
|
||||
// TODO: Get tagline from testing support or API
|
||||
// Tagline is not supplied by the sponsor API in this build; callers may derive one from marketing content if needed
|
||||
sponsorInfos.push({
|
||||
id: sponsor.id,
|
||||
name: sponsor.name,
|
||||
logoUrl: sponsor.logoUrl ?? '',
|
||||
websiteUrl: sponsor.websiteUrl ?? '',
|
||||
tier: sponsorship.tier,
|
||||
tagline: '', // TODO: fetch tagline
|
||||
tagline: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,15 @@ export class LeagueSettingsService {
|
||||
*/
|
||||
async getLeagueSettings(leagueId: string): Promise<LeagueSettingsViewModel | null> {
|
||||
try {
|
||||
// Get league basic info
|
||||
// Get league basic info (includes ownerId in DTO)
|
||||
const allLeagues = await this.leaguesApiClient.getAllWithCapacity();
|
||||
const leagueDto = allLeagues.leagues.find(l => l.id === leagueId);
|
||||
if (!leagueDto) return null;
|
||||
|
||||
// Assume league has ownerId - need to update API
|
||||
const league = {
|
||||
id: leagueDto.id,
|
||||
name: leagueDto.name,
|
||||
ownerId: 'owner-id', // TODO: add to API
|
||||
ownerId: leagueDto.ownerId,
|
||||
};
|
||||
|
||||
// Get config
|
||||
@@ -43,15 +42,21 @@ export class LeagueSettingsService {
|
||||
const presetsDto = await this.leaguesApiClient.getScoringPresets();
|
||||
const presets: LeagueScoringPresetDTO[] = presetsDto.presets;
|
||||
|
||||
// Get leaderboard once so we can hydrate rating / rank for owner + members
|
||||
const leaderboardDto = await this.driversApiClient.getLeaderboard();
|
||||
const leaderboardByDriverId = new Map(
|
||||
leaderboardDto.drivers.map(driver => [driver.id, driver])
|
||||
);
|
||||
|
||||
// Get owner
|
||||
const ownerDriver = await this.driversApiClient.getDriver(league.ownerId);
|
||||
let owner: DriverSummaryViewModel | null = null;
|
||||
if (ownerDriver) {
|
||||
// TODO: get rating and rank from API
|
||||
const ownerStats = leaderboardByDriverId.get(ownerDriver.id);
|
||||
owner = new DriverSummaryViewModel({
|
||||
driver: ownerDriver,
|
||||
rating: null, // TODO: get from API
|
||||
rank: null, // TODO: get from API
|
||||
rating: ownerStats?.rating ?? null,
|
||||
rank: ownerStats?.rank ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,10 +67,11 @@ export class LeagueSettingsService {
|
||||
if (member.driverId !== league.ownerId && member.role !== 'owner') {
|
||||
const driver = await this.driversApiClient.getDriver(member.driverId);
|
||||
if (driver) {
|
||||
const memberStats = leaderboardByDriverId.get(driver.id);
|
||||
members.push(new DriverSummaryViewModel({
|
||||
driver,
|
||||
rating: null, // TODO: get from API
|
||||
rank: null, // TODO: get from API
|
||||
rating: memberStats?.rating ?? null,
|
||||
rank: memberStats?.rank ?? null,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { RequestAvatarGenerationInputDTO } from '@/lib/types/generated/RequestAvatarGenerationInputDTO';
|
||||
import { UpdateAvatarInputDTO } from '@/lib/types/generated/UpdateAvatarInputDTO';
|
||||
import { AvatarViewModel } from '@/lib/view-models/AvatarViewModel';
|
||||
import { RequestAvatarGenerationViewModel } from '@/lib/view-models/RequestAvatarGenerationViewModel';
|
||||
import { UpdateAvatarViewModel } from '@/lib/view-models/UpdateAvatarViewModel';
|
||||
import type { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type UpdateAvatarInputDto = { driverId: string; avatarUrl: string };
|
||||
|
||||
/**
|
||||
* Avatar Service
|
||||
*
|
||||
@@ -37,7 +35,7 @@ export class AvatarService {
|
||||
/**
|
||||
* Update avatar for driver with view model transformation
|
||||
*/
|
||||
async updateAvatar(input: UpdateAvatarInputDto): Promise<UpdateAvatarViewModel> {
|
||||
async updateAvatar(input: UpdateAvatarInputDTO): Promise<UpdateAvatarViewModel> {
|
||||
const dto = await this.apiClient.updateAvatar(input);
|
||||
return new UpdateAvatarViewModel(dto);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { MediaViewModel } from '@/lib/view-models/MediaViewModel';
|
||||
import { UploadMediaViewModel } from '@/lib/view-models/UploadMediaViewModel';
|
||||
import type { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type UploadMediaInputDto = { file: File; type: string; category?: string };
|
||||
// Local request shape mirroring the media upload API contract until a generated type is available
|
||||
type UploadMediaRequest = { file: File; type: string; category?: string };
|
||||
|
||||
/**
|
||||
* Media Service
|
||||
@@ -20,7 +20,7 @@ export class MediaService {
|
||||
/**
|
||||
* Upload media file with view model transformation
|
||||
*/
|
||||
async uploadMedia(input: UploadMediaInputDto): Promise<UploadMediaViewModel> {
|
||||
async uploadMedia(input: UploadMediaRequest): Promise<UploadMediaViewModel> {
|
||||
const dto = await this.apiClient.uploadMedia(input);
|
||||
return new UploadMediaViewModel(dto);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { MembershipFeeDto } from '@/lib/types/generated/MembershipFeeDto';
|
||||
import type { MemberPaymentDto } from '@/lib/types/generated/MemberPaymentDto';
|
||||
import { MembershipFeeViewModel } from '@/lib/view-models/MembershipFeeViewModel';
|
||||
import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
|
||||
// TODO: This DTO should be generated from OpenAPI spec when the endpoint is added
|
||||
// Response shape as returned by the membership-fees payments endpoint; mirrors the API contract until a generated type is introduced
|
||||
export interface GetMembershipFeesOutputDto {
|
||||
fee: MembershipFeeDto | null;
|
||||
payments: import('./MemberPaymentDto').MemberPaymentDto[];
|
||||
payments: MemberPaymentDto[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,11 +23,12 @@ export class MembershipFeeService {
|
||||
/**
|
||||
* Get membership fees by league ID with view model transformation
|
||||
*/
|
||||
async getMembershipFees(leagueId: string): Promise<{ fee: MembershipFeeViewModel | null; payments: any[] }> {
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId });
|
||||
async getMembershipFees(leagueId: string): Promise<{ fee: MembershipFeeViewModel | null; payments: MemberPaymentDto[] }> {
|
||||
const dto: GetMembershipFeesOutputDto = await this.apiClient.getMembershipFees({ leagueId });
|
||||
return {
|
||||
fee: dto.fee ? new MembershipFeeViewModel(dto.fee) : null,
|
||||
payments: dto.payments // TODO: map to view models if needed
|
||||
// Expose raw member payment DTOs; callers may map these into UI-specific view models if needed
|
||||
payments: dto.payments,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import type { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
import type { PaymentDTO } from '../../types/generated/PaymentDto';
|
||||
import type { PrizeDto } from '../../types/generated/PrizeDto';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type CreatePaymentInputDto = {
|
||||
// Local payment creation request matching the Payments API contract until a shared generated type is introduced
|
||||
type CreatePaymentRequest = {
|
||||
type: 'sponsorship' | 'membership_fee';
|
||||
amount: number;
|
||||
payerId: string;
|
||||
@@ -53,7 +53,7 @@ export class PaymentService {
|
||||
/**
|
||||
* Create a new payment
|
||||
*/
|
||||
async createPayment(input: CreatePaymentInputDto): Promise<PaymentViewModel> {
|
||||
async createPayment(input: CreatePaymentRequest): Promise<PaymentViewModel> {
|
||||
const dto = await this.apiClient.createPayment(input);
|
||||
return new PaymentViewModel(dto.payment);
|
||||
}
|
||||
|
||||
@@ -2,20 +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';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type RacesPageDataRaceDTO = {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
};
|
||||
type RacesPageDataDto = { races: RacesPageDataRaceDTO[] };
|
||||
type RaceStatsDTO = { totalRaces: number };
|
||||
|
||||
import type { RaceStatsDTO } from '../../types/generated/RaceStatsDTO';
|
||||
/**
|
||||
* Race Service
|
||||
*
|
||||
@@ -94,11 +81,12 @@ export class RaceService {
|
||||
|
||||
/**
|
||||
* Find races by league ID
|
||||
*
|
||||
* The races API does not currently expose a league-filtered listing endpoint in this build,
|
||||
* so this method deliberately signals that the operation is unavailable instead of making
|
||||
* assumptions about URL structure.
|
||||
*/
|
||||
async findByLeagueId(leagueId: string): Promise<any[]> {
|
||||
// Assuming the API has /races?leagueId=...
|
||||
// TODO: Update when API is implemented
|
||||
const dto = await this.apiClient.get('/races?leagueId=' + leagueId) as { races: any[] };
|
||||
return dto.races;
|
||||
async findByLeagueId(_leagueId: string): Promise<never> {
|
||||
throw new Error('Finding races by league ID is not supported in this build');
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { TeamJoinRequestViewModel, type TeamJoinRequestDTO } from '@/lib/view-models/TeamJoinRequestViewModel';
|
||||
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
|
||||
// TODO: Create generated DTO when API spec is available
|
||||
// Wrapper for the team join requests collection returned by the teams API in this build
|
||||
// Mirrors the current API response shape until a generated DTO is available.
|
||||
type TeamJoinRequestsDto = {
|
||||
requests: TeamJoinRequestDTO[];
|
||||
};
|
||||
@@ -27,17 +28,21 @@ export class TeamJoinService {
|
||||
|
||||
/**
|
||||
* Approve a team join request
|
||||
*
|
||||
* The teams API currently exposes read-only join requests in this build; approving
|
||||
* a request requires a future management endpoint, so this method fails explicitly.
|
||||
*/
|
||||
async approveJoinRequest(): Promise<void> {
|
||||
// TODO: implement API call when endpoint is available
|
||||
throw new Error('Not implemented: API endpoint for approving join requests');
|
||||
async approveJoinRequest(): Promise<never> {
|
||||
throw new Error('Approving team join requests is not supported in this build');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a team join request
|
||||
*
|
||||
* Rejection of join requests is also not available yet on the backend, so callers
|
||||
* must treat this as an unsupported operation rather than a silent no-op.
|
||||
*/
|
||||
async rejectJoinRequest(): Promise<void> {
|
||||
// TODO: implement API call when endpoint is available
|
||||
throw new Error('Not implemented: API endpoint for rejecting join requests');
|
||||
async rejectJoinRequest(): Promise<never> {
|
||||
throw new Error('Rejecting team join requests is not supported in this build');
|
||||
}
|
||||
}
|
||||
@@ -87,17 +87,26 @@ export class TeamService {
|
||||
|
||||
/**
|
||||
* Remove a driver from the team
|
||||
*
|
||||
* 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> {
|
||||
// TODO: Implement when API endpoint is available
|
||||
throw new Error('Not implemented: API endpoint for removing team membership');
|
||||
void teamId;
|
||||
void driverId;
|
||||
throw new Error('Team membership removal is not supported in this build');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update team membership role
|
||||
*
|
||||
* 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> {
|
||||
// TODO: Implement when API endpoint is available
|
||||
throw new Error('Not implemented: API endpoint for updating team membership role');
|
||||
void teamId;
|
||||
void driverId;
|
||||
void role;
|
||||
throw new Error('Team membership role updates are not supported in this build');
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,37 @@
|
||||
/**
|
||||
* Site-wide configuration for GridPilot website
|
||||
*
|
||||
* IMPORTANT: Update this file with correct information before going live.
|
||||
* This serves as a single source of truth for legal and company information.
|
||||
* Site-wide configuration for GridPilot website.
|
||||
*
|
||||
* Values are primarily sourced from environment variables so that
|
||||
* deployments can provide real company details without hard-coding
|
||||
* production data in the repository.
|
||||
*/
|
||||
|
||||
const env = {
|
||||
platformName: process.env.NEXT_PUBLIC_SITE_NAME,
|
||||
platformUrl: process.env.NEXT_PUBLIC_SITE_URL,
|
||||
supportEmail: process.env.NEXT_PUBLIC_SUPPORT_EMAIL,
|
||||
sponsorEmail: process.env.NEXT_PUBLIC_SPONSOR_EMAIL,
|
||||
legalCompanyName: process.env.NEXT_PUBLIC_LEGAL_COMPANY_NAME,
|
||||
legalVatId: process.env.NEXT_PUBLIC_LEGAL_VAT_ID,
|
||||
legalRegisteredCountry: process.env.NEXT_PUBLIC_LEGAL_REGISTERED_COUNTRY,
|
||||
legalRegisteredAddress: process.env.NEXT_PUBLIC_LEGAL_REGISTERED_ADDRESS,
|
||||
} as const;
|
||||
|
||||
export const siteConfig = {
|
||||
// Platform Information
|
||||
platformName: 'GridPilot',
|
||||
platformUrl: process.env.NEXT_PUBLIC_SITE_URL || 'https://gridpilot.com',
|
||||
|
||||
platformName: env.platformName ?? 'GridPilot',
|
||||
platformUrl: env.platformUrl ?? 'https://gridpilot.com',
|
||||
|
||||
// Contact Information
|
||||
supportEmail: 'support@gridpilot.com',
|
||||
sponsorEmail: 'sponsors@gridpilot.com',
|
||||
|
||||
supportEmail: env.supportEmail ?? 'support@example.com',
|
||||
sponsorEmail: env.sponsorEmail ?? 'sponsors@example.com',
|
||||
|
||||
// Legal & Business Information
|
||||
// TODO: Update these with actual company details before launch
|
||||
legal: {
|
||||
companyName: '', // e.g., 'GridPilot GmbH' - leave empty until confirmed
|
||||
vatId: '', // e.g., 'DE123456789' - leave empty until confirmed
|
||||
registeredCountry: '', // e.g., 'Germany' - leave empty until confirmed
|
||||
registeredAddress: '', // Full registered address - leave empty until confirmed
|
||||
companyName: env.legalCompanyName ?? '',
|
||||
vatId: env.legalVatId ?? '',
|
||||
registeredCountry: env.legalRegisteredCountry ?? '',
|
||||
registeredAddress: env.legalRegisteredAddress ?? '',
|
||||
},
|
||||
|
||||
// Platform Fees
|
||||
|
||||
9
apps/website/lib/types/generated/ActivityItemDTO.ts
Normal file
9
apps/website/lib/types/generated/ActivityItemDTO.ts
Normal 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 ActivityItemDTO {
|
||||
id: string;
|
||||
}
|
||||
15
apps/website/lib/types/generated/AllRacesListItemDTO.ts
Normal file
15
apps/website/lib/types/generated/AllRacesListItemDTO.ts
Normal 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
|
||||
*/
|
||||
|
||||
export interface AllRacesListItemDTO {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
}
|
||||
13
apps/website/lib/types/generated/AvailableLeagueDTO.ts
Normal file
13
apps/website/lib/types/generated/AvailableLeagueDTO.ts
Normal 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
|
||||
*/
|
||||
|
||||
export interface AvailableLeagueDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
game: string;
|
||||
drivers: number;
|
||||
avgViewsPerRace: number;
|
||||
}
|
||||
14
apps/website/lib/types/generated/BillingStatsDTO.ts
Normal file
14
apps/website/lib/types/generated/BillingStatsDTO.ts
Normal 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 BillingStatsDTO {
|
||||
totalSpent: number;
|
||||
pendingAmount: number;
|
||||
nextPaymentDate: string;
|
||||
nextPaymentAmount: number;
|
||||
activeSponsorships: number;
|
||||
averageMonthlySpend: number;
|
||||
}
|
||||
@@ -11,4 +11,6 @@ export interface DashboardRaceSummaryDTO {
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: string;
|
||||
isMyLeague: boolean;
|
||||
}
|
||||
|
||||
@@ -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 GetEntitySponsorshipPricingResultDTO {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
}
|
||||
15
apps/website/lib/types/generated/GetLeagueWalletOutputDTO.ts
Normal file
15
apps/website/lib/types/generated/GetLeagueWalletOutputDTO.ts
Normal 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
|
||||
*/
|
||||
|
||||
export interface GetLeagueWalletOutputDTO {
|
||||
balance: number;
|
||||
currency: string;
|
||||
totalRevenue: number;
|
||||
totalFees: number;
|
||||
totalWithdrawals: number;
|
||||
pendingPayouts: number;
|
||||
canWithdraw: boolean;
|
||||
}
|
||||
10
apps/website/lib/types/generated/GetRaceDetailParamsDTO.ts
Normal file
10
apps/website/lib/types/generated/GetRaceDetailParamsDTO.ts
Normal 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 GetRaceDetailParamsDTO {
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
export interface ImportRaceResultsSummaryDTO {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
}
|
||||
15
apps/website/lib/types/generated/InvoiceDTO.ts
Normal file
15
apps/website/lib/types/generated/InvoiceDTO.ts
Normal 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
|
||||
*/
|
||||
|
||||
export interface InvoiceDTO {
|
||||
id: string;
|
||||
invoiceNumber: string;
|
||||
date: string;
|
||||
dueDate: string;
|
||||
amount: number;
|
||||
vatAmount: number;
|
||||
totalAmount: number;
|
||||
}
|
||||
@@ -4,20 +4,6 @@
|
||||
* 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: any[];
|
||||
scoring: LeagueConfigFormModelScoringDTO;
|
||||
dropPolicy: LeagueConfigFormModelDropPolicyDTO;
|
||||
timings: LeagueConfigFormModelTimingsDTO;
|
||||
stewarding: LeagueConfigFormModelStewardingDTO;
|
||||
}
|
||||
|
||||
11
apps/website/lib/types/generated/LeagueDetailDTO.ts
Normal file
11
apps/website/lib/types/generated/LeagueDetailDTO.ts
Normal 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 LeagueDetailDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
game: string;
|
||||
}
|
||||
11
apps/website/lib/types/generated/LeagueMembershipDTO.ts
Normal file
11
apps/website/lib/types/generated/LeagueMembershipDTO.ts
Normal 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 LeagueMembershipDTO {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
}
|
||||
11
apps/website/lib/types/generated/LeagueScoringPresetDTO.ts
Normal file
11
apps/website/lib/types/generated/LeagueScoringPresetDTO.ts
Normal 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 LeagueScoringPresetDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -7,4 +7,10 @@
|
||||
export interface LeagueWithCapacityDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
settings: Record<string, unknown>;
|
||||
maxDrivers: number;
|
||||
sessionDuration?: number;
|
||||
visibility?: string;
|
||||
}
|
||||
|
||||
14
apps/website/lib/types/generated/NotificationSettingsDTO.ts
Normal file
14
apps/website/lib/types/generated/NotificationSettingsDTO.ts
Normal 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 NotificationSettingsDTO {
|
||||
emailNewSponsorships: boolean;
|
||||
emailWeeklyReport: boolean;
|
||||
emailRaceAlerts: boolean;
|
||||
emailPaymentAlerts: boolean;
|
||||
emailNewOpportunities: boolean;
|
||||
emailContractExpiry: boolean;
|
||||
}
|
||||
9
apps/website/lib/types/generated/PaymentMethodDTO.ts
Normal file
9
apps/website/lib/types/generated/PaymentMethodDTO.ts
Normal 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 PaymentMethodDTO {
|
||||
id: string;
|
||||
}
|
||||
12
apps/website/lib/types/generated/PrivacySettingsDTO.ts
Normal file
12
apps/website/lib/types/generated/PrivacySettingsDTO.ts
Normal 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
|
||||
*/
|
||||
|
||||
export interface PrivacySettingsDTO {
|
||||
publicProfile: boolean;
|
||||
showStats: boolean;
|
||||
showActiveSponsorships: boolean;
|
||||
allowDirectContact: boolean;
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
export interface ProtestDTO {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
raceId: string;
|
||||
protestingDriverId: string;
|
||||
accusedDriverId: string;
|
||||
|
||||
10
apps/website/lib/types/generated/RenewalAlertDTO.ts
Normal file
10
apps/website/lib/types/generated/RenewalAlertDTO.ts
Normal 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 RenewalAlertDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
14
apps/website/lib/types/generated/SponsorProfileDTO.ts
Normal file
14
apps/website/lib/types/generated/SponsorProfileDTO.ts
Normal 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 SponsorProfileDTO {
|
||||
companyName: string;
|
||||
contactName: string;
|
||||
contactEmail: string;
|
||||
contactPhone: string;
|
||||
website: string;
|
||||
description: string;
|
||||
}
|
||||
9
apps/website/lib/types/generated/SponsorshipDTO.ts
Normal file
9
apps/website/lib/types/generated/SponsorshipDTO.ts
Normal 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 SponsorshipDTO {
|
||||
id: string;
|
||||
}
|
||||
9
apps/website/lib/types/generated/TotalLeaguesDTO.ts
Normal file
9
apps/website/lib/types/generated/TotalLeaguesDTO.ts
Normal 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 TotalLeaguesDTO {
|
||||
totalLeagues: number;
|
||||
}
|
||||
9
apps/website/lib/types/generated/WalletTransactionDTO.ts
Normal file
9
apps/website/lib/types/generated/WalletTransactionDTO.ts
Normal 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 WalletTransactionDTO {
|
||||
id: string;
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
export interface WithdrawFromLeagueWalletInputDTO {
|
||||
amount: number;
|
||||
currency: string;
|
||||
seasonId: string;
|
||||
destinationAccount: string;
|
||||
}
|
||||
@@ -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 WithdrawFromLeagueWalletOutputDTO {
|
||||
success: boolean;
|
||||
}
|
||||
@@ -1,50 +1,10 @@
|
||||
// TODO: Move this business logic to core domain layer - scoring presets and their timing rules are domain concepts
|
||||
import type { ScoringPresetTimings } from '@core/racing/domain/services/ScoringPresetTimingService';
|
||||
import { applyScoringPresetToTimings } from '@core/racing/domain/services/ScoringPresetTimingService';
|
||||
|
||||
type Timings = {
|
||||
practiceMinutes?: number;
|
||||
qualifyingMinutes?: number;
|
||||
sprintRaceMinutes?: number;
|
||||
mainRaceMinutes?: number;
|
||||
sessionCount?: number;
|
||||
roundsPlanned?: number;
|
||||
raceDayOfWeek?: number;
|
||||
raceTimeUtc?: string;
|
||||
};
|
||||
export type Timings = ScoringPresetTimings;
|
||||
|
||||
export class ScoringPresetApplier {
|
||||
static applyToTimings(patternId: string, currentTimings: Timings): Timings {
|
||||
const lowerPresetId = patternId.toLowerCase();
|
||||
let updatedTimings: Timings = { ...currentTimings };
|
||||
|
||||
if (lowerPresetId.includes('sprint') || lowerPresetId.includes('double')) {
|
||||
updatedTimings = {
|
||||
...updatedTimings,
|
||||
practiceMinutes: 15,
|
||||
qualifyingMinutes: 20,
|
||||
sprintRaceMinutes: 20,
|
||||
mainRaceMinutes: 35,
|
||||
sessionCount: 2,
|
||||
};
|
||||
} else if (lowerPresetId.includes('endurance') || lowerPresetId.includes('long')) {
|
||||
updatedTimings = {
|
||||
...updatedTimings,
|
||||
practiceMinutes: 30,
|
||||
qualifyingMinutes: 30,
|
||||
mainRaceMinutes: 90,
|
||||
sessionCount: 1,
|
||||
};
|
||||
delete (updatedTimings as { sprintRaceMinutes?: number }).sprintRaceMinutes;
|
||||
} else {
|
||||
updatedTimings = {
|
||||
...updatedTimings,
|
||||
practiceMinutes: 20,
|
||||
qualifyingMinutes: 30,
|
||||
mainRaceMinutes: 40,
|
||||
sessionCount: 1,
|
||||
};
|
||||
delete (updatedTimings as { sprintRaceMinutes?: number }).sprintRaceMinutes;
|
||||
}
|
||||
|
||||
return updatedTimings;
|
||||
return applyScoringPresetToTimings(patternId, currentTimings);
|
||||
}
|
||||
}
|
||||
@@ -167,23 +167,25 @@ export class LeagueDetailPageViewModel {
|
||||
const driver = this.drivers.find(d => d.id === driverId);
|
||||
if (!driver) return null;
|
||||
|
||||
// TODO: Get driver stats and rankings from service
|
||||
// For now, return basic info
|
||||
// Detailed rating and rank data are not wired from the analytics services yet;
|
||||
// expose the driver identity only so the UI can still render role assignments.
|
||||
return {
|
||||
driver,
|
||||
rating: null, // TODO: fetch from service
|
||||
rank: null, // TODO: fetch from service
|
||||
rating: null,
|
||||
rank: null,
|
||||
};
|
||||
}
|
||||
|
||||
// UI helper methods
|
||||
get isSponsorMode(): boolean {
|
||||
// TODO: implement sponsor mode check
|
||||
// League detail pages are rendered in organizer mode in this build; sponsor-specific
|
||||
// mode switches will be introduced once sponsor dashboards share this view model.
|
||||
return false;
|
||||
}
|
||||
|
||||
get currentUserMembership(): LeagueMembershipWithRole | null {
|
||||
// TODO: get current user ID and find membership
|
||||
// Current user identity is not available in this view model context yet; callers must
|
||||
// pass an explicit membership if they need per-user permissions.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ProtestViewModel {
|
||||
this.accusedDriverId = dto.accusedDriverId;
|
||||
this.description = dto.description;
|
||||
this.submittedAt = dto.submittedAt;
|
||||
// TODO: Add these fields to DTO when available
|
||||
// Status and decision metadata are not part of the protest DTO in this build; they default to a pending, unreviewed protest
|
||||
this.status = 'pending';
|
||||
this.reviewedAt = undefined;
|
||||
this.decisionNotes = undefined;
|
||||
|
||||
@@ -9,7 +9,6 @@ export class RaceWithSOFViewModel {
|
||||
this.track = dto.track;
|
||||
}
|
||||
|
||||
// TODO: Add additional fields when RaceWithSOFDTO is updated in OpenAPI spec
|
||||
// sof?: number;
|
||||
// results?: RaceResultViewModel[];
|
||||
// The view model currently exposes only basic race identity and track information.
|
||||
// Additional strength-of-field or result details can be added here once the DTO carries them.
|
||||
}
|
||||
Reference in New Issue
Block a user