website cleanup

This commit is contained in:
2025-12-24 14:01:52 +01:00
parent a7aee42409
commit 9b683a59d3
65 changed files with 880 additions and 745 deletions

View File

@@ -7,6 +7,7 @@ import { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
import { RaceDTO } from '../types/generated/RaceDTO';
import { LeagueScoringConfigDTO } from '../types/LeagueScoringConfigDTO';
import { RaceViewModel } from './RaceViewModel';
import { DriverViewModel } from './DriverViewModel';
// Sponsor info type
export interface SponsorInfo {
@@ -20,7 +21,7 @@ export interface SponsorInfo {
// Driver summary for management section
export interface DriverSummary {
driver: GetDriverOutputDTO;
driver: DriverViewModel;
rating: number | null;
rank: number | null;
}
@@ -117,7 +118,7 @@ export class LeagueDetailPageViewModel {
this.memberships = memberships.memberships.map(m => ({
driverId: m.driverId,
role: m.role,
status: m.status,
status: 'active',
joinedAt: m.joinedAt,
}));
@@ -164,8 +165,14 @@ export class LeagueDetailPageViewModel {
}
private buildDriverSummary(driverId: string): DriverSummary | null {
const driver = this.drivers.find(d => d.id === driverId);
if (!driver) return null;
const driverDto = this.drivers.find(d => d.id === driverId);
if (!driverDto) return null;
const driver = new DriverViewModel({
id: driverDto.id,
name: driverDto.name,
iracingId: driverDto.iracingId,
});
// 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.

View File

@@ -1,5 +1,5 @@
import type { LeagueConfigFormModel } from '@core/racing/application';
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
import type { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';
import { LeagueScoringPresetsViewModel } from './LeagueScoringPresetsViewModel';
import { DriverSummaryViewModel } from './DriverSummaryViewModel';
@@ -23,6 +23,7 @@ export class LeagueSettingsViewModel {
id: string;
name: string;
ownerId: string;
createdAt: string;
};
config: LeagueConfigFormModel;
presets: LeagueScoringPresetDTO[];

View File

@@ -1,4 +1,4 @@
import type { PaymentDTO } from '../types/generated/PaymentDto';
import type { PaymentDTO } from '../types/generated/PaymentDTO';
export class PaymentViewModel {
id: string;
@@ -14,7 +14,7 @@ export class PaymentViewModel {
createdAt: Date;
completedAt?: Date;
constructor(dto: PaymentDto) {
constructor(dto: PaymentDTO) {
Object.assign(this, dto);
}

View File

@@ -0,0 +1,19 @@
import { RaceDetailEntryDTO } from '../types/generated/RaceDetailEntryDTO';
export class RaceDetailEntryViewModel {
id: string;
name: string;
country: string;
avatarUrl: string;
isCurrentUser: boolean;
rating: number | null;
constructor(dto: RaceDetailEntryDTO, currentDriverId: string, rating?: number) {
this.id = dto.id;
this.name = dto.name;
this.country = dto.country;
this.avatarUrl = dto.avatarUrl;
this.isCurrentUser = dto.id === currentDriverId;
this.rating = rating ?? null;
}
}

View File

@@ -0,0 +1,23 @@
import { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
export class RaceDetailUserResultViewModel {
position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
ratingChange: number;
isPodium: boolean;
isClean: boolean;
constructor(dto: RaceDetailUserResultDTO) {
this.position = dto.position;
this.startPosition = dto.startPosition;
this.incidents = dto.incidents;
this.fastestLap = dto.fastestLap;
this.positionChange = dto.positionChange;
this.ratingChange = dto.ratingChange;
this.isPodium = dto.isPodium;
this.isClean = dto.isClean;
}
}

View File

@@ -41,11 +41,11 @@ describe('RaceDetailViewModel', () => {
entryList: entries,
registration,
userResult,
});
}, 'current-driver');
expect(viewModel.race).toBe(race);
expect(viewModel.league).toBe(league);
expect(viewModel.entryList).toBe(entries);
expect(viewModel.entryList).toHaveLength(0);
expect(viewModel.registration).toBe(registration);
expect(viewModel.userResult).toBe(userResult);
});

View File

@@ -2,14 +2,16 @@ import { RaceDetailLeagueDTO } from '../types/generated/RaceDetailLeagueDTO';
import { RaceDetailRaceDTO } from '../types/generated/RaceDetailRaceDTO';
import { RaceDetailRegistrationDTO } from '../types/generated/RaceDetailRegistrationDTO';
import { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
import { RaceDetailEntryDTO } from '../types/RaceDetailEntryDTO';
import { RaceDetailEntryDTO } from '../types/generated/RaceDetailEntryDTO';
import { RaceDetailEntryViewModel } from './RaceDetailEntryViewModel';
import { RaceDetailUserResultViewModel } from './RaceDetailUserResultViewModel';
export class RaceDetailViewModel {
race: RaceDetailRaceDTO | null;
league: RaceDetailLeagueDTO | null;
entryList: RaceDetailEntryDTO[];
entryList: RaceDetailEntryViewModel[];
registration: RaceDetailRegistrationDTO;
userResult: RaceDetailUserResultDTO | null;
userResult: RaceDetailUserResultViewModel | null;
error?: string;
constructor(dto: {
@@ -19,18 +21,18 @@ export class RaceDetailViewModel {
registration: RaceDetailRegistrationDTO;
userResult: RaceDetailUserResultDTO | null;
error?: string;
}) {
}, currentDriverId: string) {
this.race = dto.race;
this.league = dto.league;
this.entryList = dto.entryList;
this.entryList = dto.entryList.map(entry => new RaceDetailEntryViewModel(entry, currentDriverId));
this.registration = dto.registration;
this.userResult = dto.userResult;
this.userResult = dto.userResult ? new RaceDetailUserResultViewModel(dto.userResult) : null;
this.error = dto.error;
}
/** UI-specific: Whether user is registered */
get isRegistered(): boolean {
return this.registration.isRegistered;
return this.registration.isUserRegistered;
}
/** UI-specific: Whether user can register */

View File

@@ -61,6 +61,11 @@ export class RaceResultViewModel {
return `${minutes}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(3, '0')}`;
}
/** Required by ResultsTable */
getPositionChange(): number {
return this.positionChange;
}
// Note: The generated DTO doesn't have id or raceId
// These will need to be added when the OpenAPI spec is updated
id: string = '';

View File

@@ -3,12 +3,11 @@ import { RaceWithSOFDTO } from '../types/generated/RaceWithSOFDTO';
export class RaceWithSOFViewModel {
id: string;
track: string;
strengthOfField: number | null;
constructor(dto: RaceWithSOFDTO) {
this.id = dto.id;
this.track = dto.track;
this.strengthOfField = (dto as any).strengthOfField ?? null;
}
// 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.
}

View File

@@ -21,6 +21,10 @@ export class SponsorshipDetailViewModel {
status: string = 'active';
amount: number = 0;
currency: string = 'USD';
type: string = 'league';
entityName: string = '';
price: number = 0;
impressions: number = 0;
/** UI-specific: Formatted amount */
get formattedAmount(): string {

View File

@@ -3,7 +3,7 @@ import type { TeamMemberDTO } from '@/lib/types/generated/GetTeamMembersOutputDT
export class TeamMemberViewModel {
driverId: string;
driverName: string;
role: 'owner' | 'manager' | 'member';
role: 'owner' | 'admin' | 'member';
joinedAt: string;
isActive: boolean;
avatarUrl: string;
@@ -26,7 +26,7 @@ export class TeamMemberViewModel {
get roleBadgeVariant(): string {
switch (this.role) {
case 'owner': return 'primary';
case 'manager': return 'secondary';
case 'admin': return 'secondary';
case 'member': return 'default';
default: return 'default';
}

View File

@@ -1,4 +1,4 @@
import { WalletDto } from '../types/generated/WalletDto';
import { WalletDTO } from '../types/generated/WalletDTO';
import { FullTransactionDto, WalletTransactionViewModel } from './WalletTransactionViewModel';
export class WalletViewModel {
@@ -11,7 +11,7 @@ export class WalletViewModel {
createdAt: string;
currency: string;
constructor(dto: WalletDto & { transactions?: FullTransactionDto[] }) {
constructor(dto: WalletDTO & { transactions?: any[] }) {
this.id = dto.id;
this.leagueId = dto.leagueId;
this.balance = dto.balance;