website cleanup
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
19
apps/website/lib/view-models/RaceDetailEntryViewModel.ts
Normal file
19
apps/website/lib/view-models/RaceDetailEntryViewModel.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 = '';
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user