wip league admin tools
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type { LeagueAdminRosterJoinRequestViewModel } from './LeagueAdminRosterJoinRequestViewModel';
|
||||
|
||||
describe('LeagueAdminRosterJoinRequestViewModel', () => {
|
||||
it('requires and exposes expected fields', () => {
|
||||
const vm: LeagueAdminRosterJoinRequestViewModel = {
|
||||
id: 'req-1',
|
||||
leagueId: 'league-1',
|
||||
driverId: 'driver-1',
|
||||
driverName: 'Driver One',
|
||||
requestedAtIso: '2025-01-02T03:04:05.000Z',
|
||||
};
|
||||
|
||||
expect(vm.id).toBe('req-1');
|
||||
expect(vm.leagueId).toBe('league-1');
|
||||
expect(vm.driverId).toBe('driver-1');
|
||||
expect(vm.driverName).toBe('Driver One');
|
||||
expect(vm.requestedAtIso).toBe('2025-01-02T03:04:05.000Z');
|
||||
|
||||
expect(typeof vm.id).toBe('string');
|
||||
expect(typeof vm.leagueId).toBe('string');
|
||||
expect(typeof vm.driverId).toBe('string');
|
||||
expect(typeof vm.driverName).toBe('string');
|
||||
expect(typeof vm.requestedAtIso).toBe('string');
|
||||
});
|
||||
|
||||
it('supports optional message', () => {
|
||||
const withoutMessage: LeagueAdminRosterJoinRequestViewModel = {
|
||||
id: 'req-1',
|
||||
leagueId: 'league-1',
|
||||
driverId: 'driver-1',
|
||||
driverName: 'Driver One',
|
||||
requestedAtIso: '2025-01-02T03:04:05.000Z',
|
||||
};
|
||||
|
||||
const withMessage: LeagueAdminRosterJoinRequestViewModel = {
|
||||
...withoutMessage,
|
||||
message: 'Please approve',
|
||||
};
|
||||
|
||||
expect(withoutMessage.message).toBeUndefined();
|
||||
expect(withMessage.message).toBe('Please approve');
|
||||
expect(typeof withMessage.message).toBe('string');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface LeagueAdminRosterJoinRequestViewModel {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
requestedAtIso: string;
|
||||
message?: string;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type { LeagueAdminRosterMemberViewModel } from './LeagueAdminRosterMemberViewModel';
|
||||
|
||||
describe('LeagueAdminRosterMemberViewModel', () => {
|
||||
it('requires and exposes expected fields', () => {
|
||||
const vm: LeagueAdminRosterMemberViewModel = {
|
||||
driverId: 'driver-1',
|
||||
driverName: 'Driver One',
|
||||
role: 'member',
|
||||
joinedAtIso: '2025-01-02T03:04:05.000Z',
|
||||
};
|
||||
|
||||
expect(vm.driverId).toBe('driver-1');
|
||||
expect(vm.driverName).toBe('Driver One');
|
||||
expect(vm.role).toBe('member');
|
||||
expect(vm.joinedAtIso).toBe('2025-01-02T03:04:05.000Z');
|
||||
|
||||
expect(typeof vm.driverId).toBe('string');
|
||||
expect(typeof vm.driverName).toBe('string');
|
||||
expect(typeof vm.joinedAtIso).toBe('string');
|
||||
});
|
||||
|
||||
it('keeps role values stable as MembershipRole', () => {
|
||||
const roles: LeagueAdminRosterMemberViewModel['role'][] = ['owner', 'admin', 'steward', 'member'];
|
||||
|
||||
expect(roles).toEqual(['owner', 'admin', 'steward', 'member']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { MembershipRole } from '@/lib/types/MembershipRole';
|
||||
|
||||
export interface LeagueAdminRosterMemberViewModel {
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
role: MembershipRole;
|
||||
joinedAtIso: string;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueAdminScheduleViewModel } from './LeagueAdminScheduleViewModel';
|
||||
import type { LeagueScheduleRaceViewModel } from './LeagueScheduleViewModel';
|
||||
|
||||
describe('LeagueAdminScheduleViewModel', () => {
|
||||
it('exposes seasonId/published/races from constructor input', () => {
|
||||
const races: LeagueScheduleRaceViewModel[] = [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Round 1',
|
||||
scheduledAt: new Date('2025-01-02T20:00:00Z'),
|
||||
isPast: false,
|
||||
isUpcoming: true,
|
||||
status: 'scheduled',
|
||||
},
|
||||
];
|
||||
|
||||
const vm = new LeagueAdminScheduleViewModel({
|
||||
seasonId: 'season-1',
|
||||
published: true,
|
||||
races,
|
||||
});
|
||||
|
||||
expect(vm.seasonId).toBe('season-1');
|
||||
expect(vm.published).toBe(true);
|
||||
expect(vm.races).toBe(races);
|
||||
expect(vm.races).toHaveLength(1);
|
||||
|
||||
expect(typeof vm.seasonId).toBe('string');
|
||||
expect(typeof vm.published).toBe('boolean');
|
||||
expect(vm.races[0]?.scheduledAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('keeps published as a boolean even when false', () => {
|
||||
const vm = new LeagueAdminScheduleViewModel({
|
||||
seasonId: 'season-1',
|
||||
published: false,
|
||||
races: [],
|
||||
});
|
||||
|
||||
expect(vm.published).toBe(false);
|
||||
expect(typeof vm.published).toBe('boolean');
|
||||
});
|
||||
});
|
||||
13
apps/website/lib/view-models/LeagueAdminScheduleViewModel.ts
Normal file
13
apps/website/lib/view-models/LeagueAdminScheduleViewModel.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { LeagueScheduleRaceViewModel } from './LeagueScheduleViewModel';
|
||||
|
||||
export class LeagueAdminScheduleViewModel {
|
||||
readonly seasonId: string;
|
||||
readonly published: boolean;
|
||||
readonly races: LeagueScheduleRaceViewModel[];
|
||||
|
||||
constructor(input: { seasonId: string; published: boolean; races: LeagueScheduleRaceViewModel[] }) {
|
||||
this.seasonId = input.seasonId;
|
||||
this.published = input.published;
|
||||
this.races = input.races;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LeagueWithCapacityDTO } from '../types/generated/LeagueWithCapacityDTO';
|
||||
import { LeagueWithCapacityAndScoringDTO } from '../types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import { LeagueStatsDTO } from '../types/generated/LeagueStatsDTO';
|
||||
import { LeagueMembershipsDTO } from '../types/generated/LeagueMembershipsDTO';
|
||||
import { LeagueScheduleDTO } from '../types/generated/LeagueScheduleDTO';
|
||||
@@ -93,7 +93,7 @@ export class LeagueDetailPageViewModel {
|
||||
stewardSummaries: DriverSummary[];
|
||||
|
||||
constructor(
|
||||
league: LeagueWithCapacityDTO,
|
||||
league: LeagueWithCapacityAndScoringDTO,
|
||||
owner: GetDriverOutputDTO | null,
|
||||
scoringConfig: LeagueScoringConfigDTO | null,
|
||||
drivers: GetDriverOutputDTO[],
|
||||
@@ -111,9 +111,9 @@ export class LeagueDetailPageViewModel {
|
||||
maxDrivers: league.settings?.maxDrivers ?? (league as any).maxDrivers,
|
||||
};
|
||||
this.socialLinks = {
|
||||
discordUrl: league.discordUrl ?? (league as any).socialLinks?.discordUrl,
|
||||
youtubeUrl: league.youtubeUrl ?? (league as any).socialLinks?.youtubeUrl,
|
||||
websiteUrl: league.websiteUrl ?? (league as any).socialLinks?.websiteUrl,
|
||||
discordUrl: league.socialLinks?.discordUrl ?? (league as any).socialLinks?.discordUrl,
|
||||
youtubeUrl: league.socialLinks?.youtubeUrl ?? (league as any).socialLinks?.youtubeUrl,
|
||||
websiteUrl: league.socialLinks?.websiteUrl ?? (league as any).socialLinks?.websiteUrl,
|
||||
};
|
||||
|
||||
this.owner = owner;
|
||||
|
||||
@@ -2,28 +2,36 @@ import { describe, it, expect } from 'vitest';
|
||||
import { LeagueScheduleViewModel } from './LeagueScheduleViewModel';
|
||||
|
||||
describe('LeagueScheduleViewModel', () => {
|
||||
it('maps races array from DTO', () => {
|
||||
const races = [{ id: 'race-1' }, { id: 'race-2' }];
|
||||
it('exposes raceCount/hasRaces based on provided races', () => {
|
||||
const vm = new LeagueScheduleViewModel([
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Round 1',
|
||||
scheduledAt: new Date('2025-01-02T20:00:00Z'),
|
||||
isPast: false,
|
||||
isUpcoming: true,
|
||||
status: 'scheduled',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Round 2',
|
||||
scheduledAt: new Date('2024-12-31T20:00:00Z'),
|
||||
isPast: true,
|
||||
isUpcoming: false,
|
||||
status: 'completed',
|
||||
},
|
||||
]);
|
||||
|
||||
const vm = new LeagueScheduleViewModel({ races });
|
||||
|
||||
expect(vm.races).toBe(races);
|
||||
expect(vm.raceCount).toBe(2);
|
||||
});
|
||||
|
||||
it('derives hasRaces correctly for non-empty schedule', () => {
|
||||
const races = [{ id: 'race-1' }];
|
||||
|
||||
const vm = new LeagueScheduleViewModel({ races });
|
||||
|
||||
expect(vm.raceCount).toBe(1);
|
||||
expect(vm.hasRaces).toBe(true);
|
||||
expect(vm.races).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('derives hasRaces correctly for empty schedule', () => {
|
||||
const vm = new LeagueScheduleViewModel({ races: [] });
|
||||
it('handles empty schedules', () => {
|
||||
const vm = new LeagueScheduleViewModel([]);
|
||||
|
||||
expect(vm.raceCount).toBe(0);
|
||||
expect(vm.hasRaces).toBe(false);
|
||||
expect(vm.races).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
/**
|
||||
* View Model for League Schedule
|
||||
*
|
||||
* Represents the league's race schedule in a UI-ready format.
|
||||
* Service layer maps DTOs into these shapes; UI consumes ViewModels only.
|
||||
*/
|
||||
export class LeagueScheduleViewModel {
|
||||
races: Array<unknown>;
|
||||
export interface LeagueScheduleRaceViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
scheduledAt: Date;
|
||||
isPast: boolean;
|
||||
isUpcoming: boolean;
|
||||
status: string;
|
||||
track?: string;
|
||||
car?: string;
|
||||
sessionType?: string;
|
||||
isRegistered?: boolean;
|
||||
}
|
||||
|
||||
constructor(dto: { races: Array<unknown> }) {
|
||||
this.races = dto.races;
|
||||
export class LeagueScheduleViewModel {
|
||||
readonly races: LeagueScheduleRaceViewModel[];
|
||||
|
||||
constructor(races: LeagueScheduleRaceViewModel[]) {
|
||||
this.races = races;
|
||||
}
|
||||
|
||||
/** UI-specific: Number of races in the schedule */
|
||||
get raceCount(): number {
|
||||
return this.races.length;
|
||||
}
|
||||
|
||||
/** UI-specific: Whether the schedule has races */
|
||||
get hasRaces(): boolean {
|
||||
return this.raceCount > 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueScoringChampionshipViewModel } from './LeagueScoringChampionshipViewModel';
|
||||
|
||||
describe('LeagueScoringChampionshipViewModel', () => {
|
||||
it('exposes required fields from input', () => {
|
||||
const input = {
|
||||
id: 'champ-1',
|
||||
name: 'Drivers',
|
||||
type: 'driver',
|
||||
sessionTypes: ['race'],
|
||||
pointsPreview: [{ sessionType: 'race', position: 1, points: 25 }],
|
||||
bonusSummary: ['Pole: +1'],
|
||||
dropPolicyDescription: 'Best 6 of 8',
|
||||
};
|
||||
|
||||
const vm = new LeagueScoringChampionshipViewModel(input);
|
||||
|
||||
expect(vm.id).toBe('champ-1');
|
||||
expect(vm.name).toBe('Drivers');
|
||||
expect(vm.type).toBe('driver');
|
||||
expect(vm.sessionTypes).toEqual(['race']);
|
||||
expect(vm.pointsPreview).toEqual([{ sessionType: 'race', position: 1, points: 25 }]);
|
||||
expect(vm.bonusSummary).toEqual(['Pole: +1']);
|
||||
expect(vm.dropPolicyDescription).toBe('Best 6 of 8');
|
||||
|
||||
expect(typeof vm.id).toBe('string');
|
||||
expect(typeof vm.name).toBe('string');
|
||||
expect(typeof vm.type).toBe('string');
|
||||
expect(Array.isArray(vm.sessionTypes)).toBe(true);
|
||||
expect(Array.isArray(vm.pointsPreview)).toBe(true);
|
||||
expect(Array.isArray(vm.bonusSummary)).toBe(true);
|
||||
});
|
||||
|
||||
it('defaults optional extended fields deterministically', () => {
|
||||
const input = {
|
||||
id: 'champ-1',
|
||||
name: 'Drivers',
|
||||
type: 'driver',
|
||||
sessionTypes: ['race'],
|
||||
pointsPreview: undefined,
|
||||
};
|
||||
|
||||
const vm = new LeagueScoringChampionshipViewModel(input);
|
||||
|
||||
expect(vm.pointsPreview).toEqual([]);
|
||||
expect(vm.bonusSummary).toEqual([]);
|
||||
expect(vm.dropPolicyDescription).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,12 @@
|
||||
import { LeagueScoringChampionshipDTO } from '@/lib/types/generated/LeagueScoringChampionshipDTO';
|
||||
export type LeagueScoringChampionshipViewModelInput = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
sessionTypes: string[];
|
||||
pointsPreview?: Array<{ sessionType: string; position: number; points: number }> | null;
|
||||
bonusSummary?: string[] | null;
|
||||
dropPolicyDescription?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* LeagueScoringChampionshipViewModel
|
||||
@@ -14,13 +22,13 @@ export class LeagueScoringChampionshipViewModel {
|
||||
readonly bonusSummary: string[];
|
||||
readonly dropPolicyDescription?: string;
|
||||
|
||||
constructor(dto: LeagueScoringChampionshipDTO) {
|
||||
this.id = dto.id;
|
||||
this.name = dto.name;
|
||||
this.type = dto.type;
|
||||
this.sessionTypes = dto.sessionTypes;
|
||||
this.pointsPreview = (dto.pointsPreview as any) || [];
|
||||
this.bonusSummary = (dto as any).bonusSummary || [];
|
||||
this.dropPolicyDescription = (dto as any).dropPolicyDescription;
|
||||
constructor(input: LeagueScoringChampionshipViewModelInput) {
|
||||
this.id = input.id;
|
||||
this.name = input.name;
|
||||
this.type = input.type;
|
||||
this.sessionTypes = input.sessionTypes;
|
||||
this.pointsPreview = (input.pointsPreview as any) || [];
|
||||
this.bonusSummary = (input as any).bonusSummary || [];
|
||||
this.dropPolicyDescription = (input as any).dropPolicyDescription;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueScoringPresetViewModel } from './LeagueScoringPresetViewModel';
|
||||
|
||||
describe('LeagueScoringPresetViewModel', () => {
|
||||
it('exposes required fields from input', () => {
|
||||
const input = {
|
||||
id: 'preset-1',
|
||||
name: 'Standard scoring',
|
||||
sessionSummary: 'Sprint + Main',
|
||||
bonusSummary: 'Pole: +1',
|
||||
defaultTimings: {
|
||||
practiceMinutes: 10,
|
||||
qualifyingMinutes: 15,
|
||||
sprintRaceMinutes: 20,
|
||||
mainRaceMinutes: 40,
|
||||
sessionCount: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const vm = new LeagueScoringPresetViewModel(input);
|
||||
|
||||
expect(vm.id).toBe('preset-1');
|
||||
expect(vm.name).toBe('Standard scoring');
|
||||
expect(vm.sessionSummary).toBe('Sprint + Main');
|
||||
expect(vm.bonusSummary).toBe('Pole: +1');
|
||||
expect(vm.defaultTimings).toEqual(input.defaultTimings);
|
||||
|
||||
expect(typeof vm.id).toBe('string');
|
||||
expect(typeof vm.name).toBe('string');
|
||||
expect(typeof vm.sessionSummary).toBe('string');
|
||||
expect(typeof vm.bonusSummary).toBe('string');
|
||||
|
||||
expect(typeof vm.defaultTimings.practiceMinutes).toBe('number');
|
||||
expect(typeof vm.defaultTimings.qualifyingMinutes).toBe('number');
|
||||
expect(typeof vm.defaultTimings.sprintRaceMinutes).toBe('number');
|
||||
expect(typeof vm.defaultTimings.mainRaceMinutes).toBe('number');
|
||||
expect(typeof vm.defaultTimings.sessionCount).toBe('number');
|
||||
});
|
||||
|
||||
it('allows bonusSummary to be omitted', () => {
|
||||
const input = {
|
||||
id: 'preset-1',
|
||||
name: 'Standard scoring',
|
||||
sessionSummary: 'Sprint + Main',
|
||||
defaultTimings: {
|
||||
practiceMinutes: 10,
|
||||
qualifyingMinutes: 15,
|
||||
sprintRaceMinutes: 20,
|
||||
mainRaceMinutes: 40,
|
||||
sessionCount: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const vm = new LeagueScoringPresetViewModel(input);
|
||||
|
||||
expect(vm.bonusSummary).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
import { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';
|
||||
|
||||
export type LeagueScoringPresetTimingDefaultsViewModel = {
|
||||
practiceMinutes: number;
|
||||
qualifyingMinutes: number;
|
||||
@@ -8,6 +6,14 @@ export type LeagueScoringPresetTimingDefaultsViewModel = {
|
||||
sessionCount: number;
|
||||
};
|
||||
|
||||
export type LeagueScoringPresetViewModelInput = {
|
||||
id: string;
|
||||
name: string;
|
||||
sessionSummary: string;
|
||||
bonusSummary?: string;
|
||||
defaultTimings: LeagueScoringPresetTimingDefaultsViewModel;
|
||||
};
|
||||
|
||||
/**
|
||||
* LeagueScoringPresetViewModel
|
||||
*
|
||||
@@ -20,11 +26,11 @@ export class LeagueScoringPresetViewModel {
|
||||
readonly bonusSummary?: string;
|
||||
readonly defaultTimings: LeagueScoringPresetTimingDefaultsViewModel;
|
||||
|
||||
constructor(dto: LeagueScoringPresetDTO) {
|
||||
this.id = dto.id;
|
||||
this.name = dto.name;
|
||||
this.sessionSummary = dto.sessionSummary;
|
||||
this.bonusSummary = dto.bonusSummary;
|
||||
this.defaultTimings = dto.defaultTimings;
|
||||
constructor(input: LeagueScoringPresetViewModelInput) {
|
||||
this.id = input.id;
|
||||
this.name = input.name;
|
||||
this.sessionSummary = input.sessionSummary;
|
||||
this.bonusSummary = input.bonusSummary;
|
||||
this.defaultTimings = input.defaultTimings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueSeasonSummaryViewModel } from './LeagueSeasonSummaryViewModel';
|
||||
|
||||
describe('LeagueSeasonSummaryViewModel', () => {
|
||||
it('exposes required fields from input', () => {
|
||||
const input = {
|
||||
seasonId: 'season-1',
|
||||
name: 'Season 1',
|
||||
status: 'active',
|
||||
isPrimary: true,
|
||||
isParallelActive: false,
|
||||
};
|
||||
|
||||
const vm = new LeagueSeasonSummaryViewModel(input);
|
||||
|
||||
expect(vm.seasonId).toBe('season-1');
|
||||
expect(vm.name).toBe('Season 1');
|
||||
expect(vm.status).toBe('active');
|
||||
expect(vm.isPrimary).toBe(true);
|
||||
expect(vm.isParallelActive).toBe(false);
|
||||
|
||||
expect(typeof vm.seasonId).toBe('string');
|
||||
expect(typeof vm.name).toBe('string');
|
||||
expect(typeof vm.status).toBe('string');
|
||||
expect(typeof vm.isPrimary).toBe('boolean');
|
||||
expect(typeof vm.isParallelActive).toBe('boolean');
|
||||
});
|
||||
|
||||
it('keeps booleans as booleans even when false', () => {
|
||||
const vm = new LeagueSeasonSummaryViewModel({
|
||||
seasonId: 'season-2',
|
||||
name: 'Season 2',
|
||||
status: 'archived',
|
||||
isPrimary: false,
|
||||
isParallelActive: false,
|
||||
});
|
||||
|
||||
expect(vm.isPrimary).toBe(false);
|
||||
expect(vm.isParallelActive).toBe(false);
|
||||
expect(typeof vm.isPrimary).toBe('boolean');
|
||||
expect(typeof vm.isParallelActive).toBe('boolean');
|
||||
});
|
||||
});
|
||||
23
apps/website/lib/view-models/LeagueSeasonSummaryViewModel.ts
Normal file
23
apps/website/lib/view-models/LeagueSeasonSummaryViewModel.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export type LeagueSeasonSummaryViewModelInput = {
|
||||
seasonId: string;
|
||||
name: string;
|
||||
status: string;
|
||||
isPrimary: boolean;
|
||||
isParallelActive: boolean;
|
||||
};
|
||||
|
||||
export class LeagueSeasonSummaryViewModel {
|
||||
readonly seasonId: string;
|
||||
readonly name: string;
|
||||
readonly status: string;
|
||||
readonly isPrimary: boolean;
|
||||
readonly isParallelActive: boolean;
|
||||
|
||||
constructor(input: LeagueSeasonSummaryViewModelInput) {
|
||||
this.seasonId = input.seasonId;
|
||||
this.name = input.name;
|
||||
this.status = input.status;
|
||||
this.isPrimary = input.isPrimary;
|
||||
this.isParallelActive = input.isParallelActive;
|
||||
}
|
||||
}
|
||||
26
apps/website/lib/view-models/ProtestDetailViewModel.ts
Normal file
26
apps/website/lib/view-models/ProtestDetailViewModel.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ProtestDriverViewModel } from './ProtestDriverViewModel';
|
||||
import { ProtestViewModel } from './ProtestViewModel';
|
||||
import { RaceViewModel } from './RaceViewModel';
|
||||
|
||||
export type PenaltyTypeOptionViewModel = {
|
||||
type: string;
|
||||
label: string;
|
||||
description: string;
|
||||
requiresValue: boolean;
|
||||
valueLabel: string;
|
||||
defaultValue: number;
|
||||
};
|
||||
|
||||
export type ProtestDetailViewModel = {
|
||||
protest: ProtestViewModel;
|
||||
race: RaceViewModel;
|
||||
protestingDriver: ProtestDriverViewModel;
|
||||
accusedDriver: ProtestDriverViewModel;
|
||||
penaltyTypes: PenaltyTypeOptionViewModel[];
|
||||
defaultReasons: {
|
||||
upheld: string;
|
||||
dismissed: string;
|
||||
};
|
||||
initialPenaltyType: string | null;
|
||||
initialPenaltyValue: number;
|
||||
};
|
||||
33
apps/website/lib/view-models/RaceDetailsViewModel.ts
Normal file
33
apps/website/lib/view-models/RaceDetailsViewModel.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { RaceDetailEntryViewModel } from './RaceDetailEntryViewModel';
|
||||
import { RaceDetailUserResultViewModel } from './RaceDetailUserResultViewModel';
|
||||
|
||||
export type RaceDetailsRaceViewModel = {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: string;
|
||||
sessionType: string;
|
||||
};
|
||||
|
||||
export type RaceDetailsLeagueViewModel = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
settings?: unknown;
|
||||
};
|
||||
|
||||
export type RaceDetailsRegistrationViewModel = {
|
||||
canRegister: boolean;
|
||||
isUserRegistered: boolean;
|
||||
};
|
||||
|
||||
export type RaceDetailsViewModel = {
|
||||
race: RaceDetailsRaceViewModel | null;
|
||||
league: RaceDetailsLeagueViewModel | null;
|
||||
entryList: RaceDetailEntryViewModel[];
|
||||
registration: RaceDetailsRegistrationViewModel;
|
||||
userResult: RaceDetailUserResultViewModel | null;
|
||||
canReopenRace: boolean;
|
||||
error?: string;
|
||||
};
|
||||
Reference in New Issue
Block a user