website refactor
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { CompleteOnboardingOutputDTO } from '../types/generated/CompleteOnboardingOutputDTO';
|
||||
import { CompleteOnboardingOutputDTO } from '@/lib/types/generated/CompleteOnboardingOutputDTO';
|
||||
|
||||
/**
|
||||
* Complete onboarding view model
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CreateLeagueOutputDTO } from '../types/generated/CreateLeagueOutputDTO';
|
||||
import { CreateLeagueOutputDTO } from '@/lib/types/generated/CreateLeagueOutputDTO';
|
||||
|
||||
/**
|
||||
* View Model for Create League Result
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DriverLeaderboardItemDTO } from '../types/generated/DriverLeaderboardItemDTO';
|
||||
import type { DriverLeaderboardItemDTO } from '@/lib/types/generated/DriverLeaderboardItemDTO';
|
||||
|
||||
export class DriverLeaderboardItemViewModel {
|
||||
id: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DriverLeaderboardItemDTO } from '../types/generated/DriverLeaderboardItemDTO';
|
||||
import { DriverLeaderboardItemDTO } from '@/lib/types/generated/DriverLeaderboardItemDTO';
|
||||
import { DriverLeaderboardItemViewModel } from './DriverLeaderboardItemViewModel';
|
||||
|
||||
export class DriverLeaderboardViewModel {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DriverRegistrationStatusDTO } from '../types/generated/DriverRegistrationStatusDTO';
|
||||
import { DriverRegistrationStatusDTO } from '@/lib/types/generated/DriverRegistrationStatusDTO';
|
||||
|
||||
export class DriverRegistrationStatusViewModel {
|
||||
isRegistered!: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||
|
||||
/**
|
||||
* View Model for driver summary with rating and rank
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { LeagueWithCapacityAndScoringDTO } from '../types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import { LeagueStatsDTO } from '../types/generated/LeagueStatsDTO';
|
||||
import { LeagueMembershipsDTO } from '../types/generated/LeagueMembershipsDTO';
|
||||
import { LeagueScheduleDTO } from '../types/generated/LeagueScheduleDTO';
|
||||
import { LeagueStandingsDTO } from '../types/generated/LeagueStandingsDTO';
|
||||
import { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
import { RaceDTO } from '../types/generated/RaceDTO';
|
||||
import { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import { LeagueStatsDTO } from '@/lib/types/generated/LeagueStatsDTO';
|
||||
import { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
|
||||
import { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||
import { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
|
||||
import { RaceViewModel } from './RaceViewModel';
|
||||
import { DriverViewModel } from './DriverViewModel';
|
||||
@@ -34,6 +31,29 @@ export interface LeagueMembershipWithRole {
|
||||
joinedAt: string;
|
||||
}
|
||||
|
||||
// Helper interfaces for type narrowing
|
||||
interface LeagueSettings {
|
||||
maxDrivers?: number;
|
||||
}
|
||||
|
||||
interface SocialLinks {
|
||||
discordUrl?: string;
|
||||
youtubeUrl?: string;
|
||||
websiteUrl?: string;
|
||||
}
|
||||
|
||||
interface LeagueStatsExtended {
|
||||
averageSOF?: number;
|
||||
averageRating?: number;
|
||||
completedRaces?: number;
|
||||
totalRaces?: number;
|
||||
}
|
||||
|
||||
interface MembershipsContainer {
|
||||
members?: Array<{ driverId: string; role: string; status?: 'active' | 'inactive'; joinedAt: string }>;
|
||||
memberships?: Array<{ driverId: string; role: string; status?: 'active' | 'inactive'; joinedAt: string }>;
|
||||
}
|
||||
|
||||
export class LeagueDetailPageViewModel {
|
||||
// League basic info
|
||||
id: string;
|
||||
@@ -107,25 +127,35 @@ export class LeagueDetailPageViewModel {
|
||||
this.description = league.description ?? '';
|
||||
this.ownerId = league.ownerId;
|
||||
this.createdAt = league.createdAt;
|
||||
|
||||
// Handle settings with proper type narrowing
|
||||
const settings = league.settings as LeagueSettings | undefined;
|
||||
const maxDrivers = settings?.maxDrivers;
|
||||
this.settings = {
|
||||
maxDrivers: league.settings?.maxDrivers ?? (league as any).maxDrivers,
|
||||
maxDrivers: maxDrivers,
|
||||
};
|
||||
|
||||
// Handle social links with proper type narrowing
|
||||
const socialLinks = league.socialLinks as SocialLinks | undefined;
|
||||
const discordUrl = socialLinks?.discordUrl;
|
||||
const youtubeUrl = socialLinks?.youtubeUrl;
|
||||
const websiteUrl = socialLinks?.websiteUrl;
|
||||
|
||||
this.socialLinks = {
|
||||
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,
|
||||
discordUrl,
|
||||
youtubeUrl,
|
||||
websiteUrl,
|
||||
};
|
||||
|
||||
this.owner = owner;
|
||||
this.scoringConfig = scoringConfig;
|
||||
this.drivers = drivers;
|
||||
|
||||
const membershipDtos = ((memberships as any).members ?? (memberships as any).memberships ?? []) as Array<{
|
||||
driverId: string;
|
||||
role: string;
|
||||
status?: 'active' | 'inactive';
|
||||
joinedAt: string;
|
||||
}>;
|
||||
// Handle memberships with proper type narrowing
|
||||
const membershipsContainer = memberships as MembershipsContainer;
|
||||
const membershipDtos = membershipsContainer.members ??
|
||||
membershipsContainer.memberships ??
|
||||
[];
|
||||
|
||||
this.memberships = membershipDtos.map((m) => ({
|
||||
driverId: m.driverId,
|
||||
@@ -137,11 +167,15 @@ export class LeagueDetailPageViewModel {
|
||||
this.allRaces = allRaces;
|
||||
this.runningRaces = allRaces.filter(r => r.status === 'running');
|
||||
|
||||
const leagueStatsAny = leagueStats as any;
|
||||
// Calculate SOF from available data with proper type narrowing
|
||||
const statsExtended = leagueStats as LeagueStatsExtended;
|
||||
const averageSOF = statsExtended.averageSOF ??
|
||||
statsExtended.averageRating ?? undefined;
|
||||
const completedRaces = statsExtended.completedRaces ??
|
||||
statsExtended.totalRaces ?? undefined;
|
||||
|
||||
// Calculate SOF from available data
|
||||
this.averageSOF = leagueStatsAny.averageSOF ?? leagueStats.averageRating ?? null;
|
||||
this.completedRacesCount = leagueStatsAny.completedRaces ?? leagueStats.totalRaces ?? 0;
|
||||
this.averageSOF = typeof averageSOF === 'number' ? averageSOF : null;
|
||||
this.completedRacesCount = typeof completedRaces === 'number' ? completedRaces : 0;
|
||||
|
||||
this.sponsors = sponsors;
|
||||
|
||||
@@ -183,10 +217,14 @@ export class LeagueDetailPageViewModel {
|
||||
const driverDto = this.drivers.find(d => d.id === driverId);
|
||||
if (!driverDto) return null;
|
||||
|
||||
// Handle avatarUrl with proper type checking
|
||||
const driverAny = driverDto as { avatarUrl?: unknown };
|
||||
const avatarUrl = typeof driverAny.avatarUrl === 'string' ? driverAny.avatarUrl : null;
|
||||
|
||||
const driver = new DriverViewModel({
|
||||
id: driverDto.id,
|
||||
name: driverDto.name,
|
||||
avatarUrl: (driverDto as any).avatarUrl ?? null,
|
||||
avatarUrl: avatarUrl,
|
||||
iracingId: driverDto.iracingId,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LeagueJoinRequestDTO } from '../types/generated/LeagueJoinRequestDTO';
|
||||
import type { LeagueJoinRequestDTO } from '@/lib/types/generated/LeagueJoinRequestDTO';
|
||||
|
||||
/**
|
||||
* League join request view model
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LeagueMemberDTO } from '../types/generated/LeagueMemberDTO';
|
||||
import { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
|
||||
import { DriverViewModel } from './DriverViewModel';
|
||||
|
||||
export class LeagueMemberViewModel {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LeagueMemberViewModel } from './LeagueMemberViewModel';
|
||||
import type { LeagueMemberDTO } from '../types/generated/LeagueMemberDTO';
|
||||
import type { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
|
||||
|
||||
/**
|
||||
* View Model for League Memberships
|
||||
|
||||
@@ -27,8 +27,8 @@ export class LeagueScoringChampionshipViewModel {
|
||||
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;
|
||||
this.pointsPreview = input.pointsPreview ?? [];
|
||||
this.bonusSummary = input.bonusSummary ?? [];
|
||||
this.dropPolicyDescription = input.dropPolicyDescription;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
|
||||
import type { LeagueScoringChampionshipDTO } from '@/lib/types/generated/LeagueScoringChampionshipDTO';
|
||||
|
||||
/**
|
||||
* LeagueScoringConfigViewModel
|
||||
@@ -9,21 +10,12 @@ export class LeagueScoringConfigViewModel {
|
||||
readonly gameName: string;
|
||||
readonly scoringPresetName?: string;
|
||||
readonly dropPolicySummary?: string;
|
||||
readonly championships?: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'driver' | 'team' | 'nations' | 'trophy' | string;
|
||||
sessionTypes: string[];
|
||||
pointsPreview: Array<{ sessionType: string; position: number; points: number }>;
|
||||
bonusSummary: string[];
|
||||
dropPolicyDescription?: string;
|
||||
}>;
|
||||
readonly championships?: LeagueScoringChampionshipDTO[];
|
||||
|
||||
constructor(dto: LeagueScoringConfigDTO) {
|
||||
this.gameName = dto.gameName;
|
||||
// These would be mapped from extended properties if available
|
||||
this.scoringPresetName = (dto as any).scoringPresetName;
|
||||
this.dropPolicySummary = (dto as any).dropPolicySummary;
|
||||
this.championships = (dto as any).championships;
|
||||
this.scoringPresetName = dto.scoringPresetName;
|
||||
this.dropPolicySummary = dto.dropPolicySummary;
|
||||
this.championships = dto.championships;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LeagueStandingDTO } from '../types/generated/LeagueStandingDTO';
|
||||
import { LeagueStandingDTO } from '@/lib/types/generated/LeagueStandingDTO';
|
||||
import { StandingEntryViewModel } from './StandingEntryViewModel';
|
||||
import { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
import { LeagueMembership } from '../types/LeagueMembership';
|
||||
import { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||
import { LeagueMembership } from '@/lib/types/LeagueMembership';
|
||||
|
||||
export class LeagueStandingsViewModel {
|
||||
standings: StandingEntryViewModel[];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LeagueWalletViewModel } from './LeagueWalletViewModel';
|
||||
import { WalletTransactionViewModel } from './WalletTransactionViewModel';
|
||||
import { WalletTransactionViewModel, FullTransactionDto } from './WalletTransactionViewModel';
|
||||
|
||||
const createTransaction = (overrides: Partial<WalletTransactionViewModel> = {}): WalletTransactionViewModel =>
|
||||
const createTransaction = (overrides: Partial<FullTransactionDto> = {}): WalletTransactionViewModel =>
|
||||
new WalletTransactionViewModel({
|
||||
id: 'tx-1',
|
||||
type: 'sponsorship',
|
||||
@@ -13,7 +13,7 @@ const createTransaction = (overrides: Partial<WalletTransactionViewModel> = {}):
|
||||
date: new Date('2024-01-01T00:00:00Z'),
|
||||
status: 'completed',
|
||||
reference: 'ref-1',
|
||||
...(overrides as any),
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('LeagueWalletViewModel', () => {
|
||||
@@ -62,10 +62,10 @@ describe('LeagueWalletViewModel', () => {
|
||||
});
|
||||
|
||||
it('filters transactions by type and supports all', () => {
|
||||
const sponsorshipTx = createTransaction({ type: 'sponsorship' as any });
|
||||
const membershipTx = createTransaction({ type: 'membership' as any, id: 'tx-2' });
|
||||
const withdrawalTx = createTransaction({ type: 'withdrawal' as any, id: 'tx-3' });
|
||||
const prizeTx = createTransaction({ type: 'prize' as any, id: 'tx-4' });
|
||||
const sponsorshipTx = createTransaction({ type: 'sponsorship' });
|
||||
const membershipTx = createTransaction({ type: 'membership', id: 'tx-2' });
|
||||
const withdrawalTx = createTransaction({ type: 'withdrawal', id: 'tx-3' });
|
||||
const prizeTx = createTransaction({ type: 'prize', id: 'tx-4' });
|
||||
|
||||
const vm = new LeagueWalletViewModel({
|
||||
balance: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GetMediaOutputDTO } from '../types/generated/GetMediaOutputDTO';
|
||||
import type { GetMediaOutputDTO } from '@/lib/types/generated/GetMediaOutputDTO';
|
||||
|
||||
/**
|
||||
* Media View Model
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { MembershipFeeViewModel } from './MembershipFeeViewModel';
|
||||
import type { MembershipFeeDto } from '../types/generated';
|
||||
import type { MembershipFeeDTO } from '@/lib/types/generated';
|
||||
|
||||
const createMembershipFeeDto = (overrides: Partial<MembershipFeeDto> = {}): MembershipFeeDto => ({
|
||||
const createMembershipFeeDto = (overrides: Partial<MembershipFeeDTO> = {}): MembershipFeeDTO => ({
|
||||
id: 'fee-1',
|
||||
leagueId: 'league-1',
|
||||
seasonId: 'season-1',
|
||||
@@ -38,7 +38,7 @@ describe('MembershipFeeViewModel', () => {
|
||||
const seasonVm = new MembershipFeeViewModel(createMembershipFeeDto({ type: 'season' }));
|
||||
const monthlyVm = new MembershipFeeViewModel(createMembershipFeeDto({ type: 'monthly' }));
|
||||
const perRaceVm = new MembershipFeeViewModel(createMembershipFeeDto({ type: 'per_race' }));
|
||||
const otherVm = new MembershipFeeViewModel(createMembershipFeeDto({ type: 'custom' as any }));
|
||||
const otherVm = new MembershipFeeViewModel(createMembershipFeeDto({ type: 'custom' }));
|
||||
|
||||
expect(seasonVm.typeDisplay).toBe('Per Season');
|
||||
expect(monthlyVm.typeDisplay).toBe('Monthly');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { MembershipFeeDTO } from '../types/generated/MembershipFeeDTO';
|
||||
import type { MembershipFeeDTO } from '@/lib/types/generated';
|
||||
|
||||
export class MembershipFeeViewModel {
|
||||
id!: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PaymentDTO } from '../types/generated/PaymentDTO';
|
||||
import type { PaymentDTO } from '@/lib/types/generated/PaymentDTO';
|
||||
|
||||
export class PaymentViewModel {
|
||||
id!: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PrizeDTO } from '../types/generated/PrizeDTO';
|
||||
import type { PrizeDTO } from '@/lib/types/generated/PrizeDTO';
|
||||
|
||||
export class PrizeViewModel {
|
||||
id!: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DriverSummaryDTO } from '../types/generated/DriverSummaryDTO';
|
||||
import { DriverSummaryDTO } from '@/lib/types/generated/DriverSummaryDTO';
|
||||
|
||||
export class ProtestDriverViewModel {
|
||||
constructor(private readonly dto: DriverSummaryDTO) {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ProtestDTO } from '../types/generated/ProtestDTO';
|
||||
import { RaceProtestDTO } from '../types/generated/RaceProtestDTO';
|
||||
import { ProtestDTO } from '@/lib/types/generated/ProtestDTO';
|
||||
import { RaceProtestDTO } from '@/lib/types/generated/RaceProtestDTO';
|
||||
|
||||
/**
|
||||
* Protest view model
|
||||
@@ -22,15 +22,41 @@ export class ProtestViewModel {
|
||||
|
||||
constructor(dto: ProtestDTO | RaceProtestDTO) {
|
||||
this.id = dto.id;
|
||||
this.raceId = (dto as any).raceId || '';
|
||||
|
||||
// Type narrowing for raceId
|
||||
if ('raceId' in dto) {
|
||||
this.raceId = dto.raceId;
|
||||
} else {
|
||||
this.raceId = '';
|
||||
}
|
||||
|
||||
this.protestingDriverId = dto.protestingDriverId;
|
||||
this.accusedDriverId = dto.accusedDriverId;
|
||||
this.description = (dto as any).description || dto.description;
|
||||
this.submittedAt = (dto as any).submittedAt || (dto as any).filedAt || '';
|
||||
this.filedAt = (dto as any).filedAt || (dto as any).submittedAt;
|
||||
|
||||
// Type narrowing for description
|
||||
if ('description' in dto && typeof dto.description === 'string') {
|
||||
this.description = dto.description;
|
||||
} else {
|
||||
this.description = '';
|
||||
}
|
||||
|
||||
// Type narrowing for submittedAt and filedAt
|
||||
if ('submittedAt' in dto && typeof dto.submittedAt === 'string') {
|
||||
this.submittedAt = dto.submittedAt;
|
||||
} else if ('filedAt' in dto && typeof dto.filedAt === 'string') {
|
||||
this.submittedAt = dto.filedAt;
|
||||
} else {
|
||||
this.submittedAt = '';
|
||||
}
|
||||
|
||||
if ('filedAt' in dto && typeof dto.filedAt === 'string') {
|
||||
this.filedAt = dto.filedAt;
|
||||
} else if ('submittedAt' in dto && typeof dto.submittedAt === 'string') {
|
||||
this.filedAt = dto.submittedAt;
|
||||
}
|
||||
|
||||
// Handle different DTO structures
|
||||
if ('status' in dto) {
|
||||
if ('status' in dto && typeof dto.status === 'string') {
|
||||
this.status = dto.status;
|
||||
} else {
|
||||
this.status = 'pending';
|
||||
@@ -38,14 +64,16 @@ export class ProtestViewModel {
|
||||
|
||||
// Handle incident data
|
||||
if ('incident' in dto && dto.incident) {
|
||||
const incident = dto.incident as { lap?: number; description?: string };
|
||||
this.incident = {
|
||||
lap: (dto.incident as any).lap,
|
||||
description: (dto.incident as any).description
|
||||
lap: typeof incident.lap === 'number' ? incident.lap : undefined,
|
||||
description: typeof incident.description === 'string' ? incident.description : undefined
|
||||
};
|
||||
} else if ('lap' in dto || 'description' in dto) {
|
||||
} else if (('lap' in dto && typeof (dto as { lap?: number }).lap === 'number') ||
|
||||
('description' in dto && typeof (dto as { description?: string }).description === 'string')) {
|
||||
this.incident = {
|
||||
lap: (dto as any).lap,
|
||||
description: (dto as any).description
|
||||
lap: 'lap' in dto ? (dto as { lap?: number }).lap : undefined,
|
||||
description: 'description' in dto ? (dto as { description?: string }).description : undefined
|
||||
};
|
||||
} else {
|
||||
this.incident = null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RaceDetailEntryDTO } from '../types/generated/RaceDetailEntryDTO';
|
||||
import { RaceDetailEntryDTO } from '@/lib/types/generated/RaceDetailEntryDTO';
|
||||
|
||||
export class RaceDetailEntryViewModel {
|
||||
id: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
|
||||
import { RaceDetailUserResultDTO } from '@/lib/types/generated/RaceDetailUserResultDTO';
|
||||
|
||||
export class RaceDetailUserResultViewModel {
|
||||
position!: number;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { RaceDetailLeagueDTO } from '../types/generated/RaceDetailLeagueDTO';
|
||||
import type { RaceDetailRaceDTO } from '../types/generated/RaceDetailRaceDTO';
|
||||
import type { RaceDetailRegistrationDTO } from '../types/generated/RaceDetailRegistrationDTO';
|
||||
import type { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
|
||||
import type { RaceDetailEntryDTO } from '../types/RaceDetailEntryDTO';
|
||||
import type { RaceDetailLeagueDTO } from '@/lib/types/generated/RaceDetailLeagueDTO';
|
||||
import type { RaceDetailRaceDTO } from '@/lib/types/generated/RaceDetailRaceDTO';
|
||||
import type { RaceDetailRegistrationDTO } from '@/lib/types/generated/RaceDetailRegistrationDTO';
|
||||
import type { RaceDetailUserResultDTO } from '@/lib/types/generated/RaceDetailUserResultDTO';
|
||||
import type { RaceDetailEntryDTO } from '@/lib/types/RaceDetailEntryDTO';
|
||||
import { RaceDetailViewModel } from './RaceDetailViewModel';
|
||||
|
||||
describe('RaceDetailViewModel', () => {
|
||||
@@ -262,7 +262,7 @@ describe('RaceDetailViewModel', () => {
|
||||
});
|
||||
|
||||
const cancelledVm = new RaceDetailViewModel({
|
||||
race: createMockRace({ status: 'cancelled' as any }),
|
||||
race: createMockRace({ status: 'cancelled' }),
|
||||
league: createMockLeague(),
|
||||
entryList: [],
|
||||
registration: createMockRegistration(),
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
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/generated/RaceDetailEntryDTO';
|
||||
import { RaceDetailEntryViewModel } from './RaceDetailEntryViewModel';
|
||||
import { RaceDetailUserResultViewModel } from './RaceDetailUserResultViewModel';
|
||||
|
||||
export class RaceDetailViewModel {
|
||||
race: RaceDetailRaceDTO | null;
|
||||
league: RaceDetailLeagueDTO | null;
|
||||
entryList: RaceDetailEntryViewModel[];
|
||||
registration: RaceDetailRegistrationDTO;
|
||||
userResult: RaceDetailUserResultViewModel | null;
|
||||
error?: string;
|
||||
|
||||
constructor(dto: {
|
||||
race: RaceDetailRaceDTO | null;
|
||||
league: RaceDetailLeagueDTO | null;
|
||||
entryList: RaceDetailEntryDTO[];
|
||||
registration: RaceDetailRegistrationDTO;
|
||||
userResult: RaceDetailUserResultDTO | null;
|
||||
error?: string;
|
||||
}, currentDriverId: string) {
|
||||
this.race = dto.race;
|
||||
this.league = dto.league;
|
||||
this.entryList = dto.entryList.map(entry => new RaceDetailEntryViewModel(entry, currentDriverId));
|
||||
this.registration = dto.registration;
|
||||
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 as any).isUserRegistered ?? (this.registration as any).isRegistered ?? false;
|
||||
}
|
||||
|
||||
/** UI-specific: Whether user can register */
|
||||
get canRegister(): boolean {
|
||||
return this.registration.canRegister;
|
||||
}
|
||||
|
||||
/** UI-specific: Race status display */
|
||||
get raceStatusDisplay(): string {
|
||||
if (!this.race) return 'Unknown';
|
||||
switch (this.race.status) {
|
||||
case 'upcoming': return 'Upcoming';
|
||||
case 'live': return 'Live';
|
||||
case 'finished': return 'Finished';
|
||||
default: return this.race.status;
|
||||
}
|
||||
}
|
||||
|
||||
/** UI-specific: Formatted scheduled time */
|
||||
get formattedScheduledTime(): string {
|
||||
return this.race ? new Date(this.race.scheduledAt).toLocaleString() : '';
|
||||
}
|
||||
|
||||
/** UI-specific: Entry list count */
|
||||
get entryCount(): number {
|
||||
return this.entryList.length;
|
||||
}
|
||||
|
||||
/** UI-specific: Whether race has results */
|
||||
get hasResults(): boolean {
|
||||
return this.userResult !== null;
|
||||
}
|
||||
|
||||
/** UI-specific: Registration status message */
|
||||
get registrationStatusMessage(): string {
|
||||
if (this.isRegistered) return 'You are registered for this race';
|
||||
if (this.canRegister) return 'You can register for this race';
|
||||
return 'Registration not available';
|
||||
}
|
||||
|
||||
/** UI-specific: Whether race can be re-opened */
|
||||
get canReopenRace(): boolean {
|
||||
if (!this.race) return false;
|
||||
return this.race.status === 'completed' || this.race.status === 'cancelled';
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RaceResultDTO } from '../types/generated/RaceResultDTO';
|
||||
import { RaceResultDTO } from '@/lib/types/generated/RaceResultDTO';
|
||||
|
||||
export class RaceResultViewModel {
|
||||
driverId!: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LeagueMembershipsViewModel } from '@/lib/view-models/LeagueMembershipsViewModel';
|
||||
import type { RaceResultsDetailViewModel } from '@/lib/view-models/RaceResultsDetailViewModel';
|
||||
import type { RaceWithSOFViewModel } from '@/lib/view-models/RaceWithSOFViewModel';
|
||||
import type { LeagueMembershipsViewModel } from './LeagueMembershipsViewModel';
|
||||
import type { RaceResultsDetailViewModel } from './RaceResultsDetailViewModel';
|
||||
import type { RaceWithSOFViewModel } from './RaceWithSOFViewModel';
|
||||
|
||||
// TODO fucking violating our architecture, it should be a ViewModel
|
||||
|
||||
@@ -58,7 +58,7 @@ export class RaceResultsDataTransformer {
|
||||
}
|
||||
|
||||
// Transform results
|
||||
const results = resultsData.results.map((result: any) => ({
|
||||
const results = resultsData.results.map((result) => ({
|
||||
position: result.position,
|
||||
driverId: result.driverId,
|
||||
driverName: result.driverName,
|
||||
@@ -74,9 +74,9 @@ export class RaceResultsDataTransformer {
|
||||
}));
|
||||
|
||||
// Transform penalties
|
||||
const penalties = resultsData.penalties.map((penalty: any) => ({
|
||||
const penalties = resultsData.penalties.map((penalty) => ({
|
||||
driverId: penalty.driverId,
|
||||
driverName: resultsData.results.find((r: any) => r.driverId === penalty.driverId)?.driverName || 'Unknown',
|
||||
driverName: resultsData.results.find((r) => r.driverId === penalty.driverId)?.driverName || 'Unknown',
|
||||
type: penalty.type as 'time_penalty' | 'grid_penalty' | 'points_deduction' | 'disqualification' | 'warning' | 'license_points',
|
||||
value: penalty.value || 0,
|
||||
reason: 'Penalty applied', // Default since view model doesn't have reason
|
||||
@@ -84,7 +84,7 @@ export class RaceResultsDataTransformer {
|
||||
}));
|
||||
|
||||
// Transform memberships
|
||||
const memberships = membershipsData?.memberships.map((membership: any) => ({
|
||||
const memberships = membershipsData?.memberships.map((membership) => ({
|
||||
driverId: membership.driverId,
|
||||
role: membership.role || 'member',
|
||||
}));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RaceResultDTO } from '../types/generated/RaceResultDTO';
|
||||
import { RaceResultsDetailDTO } from '../types/generated/RaceResultsDetailDTO';
|
||||
import { RaceResultDTO } from '@/lib/types/generated/RaceResultDTO';
|
||||
import { RaceResultsDetailDTO } from '@/lib/types/generated/RaceResultsDetailDTO';
|
||||
import { RaceResultViewModel } from './RaceResultViewModel';
|
||||
|
||||
export class RaceResultsDetailViewModel {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RaceStatsDTO } from '../types/generated/RaceStatsDTO';
|
||||
import type { RaceStatsDTO } from '@/lib/types/generated/RaceStatsDTO';
|
||||
|
||||
/**
|
||||
* Race stats view model
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RaceDTO } from '../types/generated/RaceDTO';
|
||||
import { RacesPageDataRaceDTO } from '../types/generated/RacesPageDataRaceDTO';
|
||||
import { RaceDTO } from '@/lib/types/generated/RaceDTO';
|
||||
import { RacesPageDataRaceDTO } from '@/lib/types/generated/RacesPageDataRaceDTO';
|
||||
|
||||
export class RaceViewModel {
|
||||
constructor(
|
||||
@@ -31,15 +31,15 @@ export class RaceViewModel {
|
||||
}
|
||||
|
||||
get track(): string {
|
||||
return (this.dto as any).track || '';
|
||||
return 'track' in this.dto ? this.dto.track || '' : '';
|
||||
}
|
||||
|
||||
get car(): string {
|
||||
return (this.dto as any).car || '';
|
||||
return 'car' in this.dto ? this.dto.car || '' : '';
|
||||
}
|
||||
|
||||
get status(): string | undefined {
|
||||
return this._status || (this.dto as any).status;
|
||||
return this._status || ('status' in this.dto ? this.dto.status : undefined);
|
||||
}
|
||||
|
||||
get registeredCount(): number | undefined {
|
||||
@@ -47,7 +47,7 @@ export class RaceViewModel {
|
||||
}
|
||||
|
||||
get strengthOfField(): number | undefined {
|
||||
return this._strengthOfField || (this.dto as any).strengthOfField;
|
||||
return this._strengthOfField || ('strengthOfField' in this.dto ? this.dto.strengthOfField : undefined);
|
||||
}
|
||||
|
||||
/** UI-specific: Formatted date */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RaceWithSOFDTO } from '../types/generated/RaceWithSOFDTO';
|
||||
import { RaceWithSOFDTO } from '@/lib/types/generated/RaceWithSOFDTO';
|
||||
|
||||
export class RaceWithSOFViewModel {
|
||||
id: string;
|
||||
@@ -8,6 +8,6 @@ export class RaceWithSOFViewModel {
|
||||
constructor(dto: RaceWithSOFDTO) {
|
||||
this.id = dto.id;
|
||||
this.track = dto.track;
|
||||
this.strengthOfField = (dto as any).strengthOfField ?? null;
|
||||
this.strengthOfField = 'strengthOfField' in dto ? dto.strengthOfField ?? null : null;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { RaceListItemViewModel } from './RaceListItemViewModel';
|
||||
import type { RacesPageDataRaceDTO } from '../types/generated/RacesPageDataRaceDTO';
|
||||
import type { RaceListItemDTO } from './RaceListItemViewModel';
|
||||
import type { RacesPageDataRaceDTO } from '@/lib/types/generated/RacesPageDataRaceDTO';
|
||||
|
||||
// DTO matching the backend RacesPageDataDTO
|
||||
interface RacesPageDTO {
|
||||
@@ -16,23 +15,33 @@ export class RacesPageViewModel {
|
||||
|
||||
constructor(dto: RacesPageDTO) {
|
||||
this.races = dto.races.map((r) => {
|
||||
const status = (r as any).status as string | undefined;
|
||||
const status = 'status' in r ? r.status : 'unknown';
|
||||
|
||||
const isUpcoming =
|
||||
(r as any).isUpcoming ??
|
||||
'isUpcoming' in r ? r.isUpcoming :
|
||||
(status === 'upcoming' || status === 'scheduled');
|
||||
|
||||
const isLive =
|
||||
(r as any).isLive ??
|
||||
'isLive' in r ? r.isLive :
|
||||
(status === 'live' || status === 'running');
|
||||
|
||||
const isPast =
|
||||
(r as any).isPast ??
|
||||
'isPast' in r ? r.isPast :
|
||||
(status === 'completed' || status === 'finished' || status === 'cancelled');
|
||||
|
||||
const normalized: RaceListItemDTO = {
|
||||
...(r as any),
|
||||
strengthOfField: (r as any).strengthOfField ?? null,
|
||||
// Build the RaceListItemDTO from the input with proper type checking
|
||||
const scheduledAt = 'scheduledAt' in r ? r.scheduledAt :
|
||||
('date' in r ? (r as { date?: string }).date : '');
|
||||
|
||||
const normalized = {
|
||||
id: r.id,
|
||||
track: 'track' in r ? r.track : '',
|
||||
car: 'car' in r ? r.car : '',
|
||||
scheduledAt: scheduledAt || '',
|
||||
status: status,
|
||||
leagueId: 'leagueId' in r ? r.leagueId : '',
|
||||
leagueName: 'leagueName' in r ? r.leagueName : '',
|
||||
strengthOfField: 'strengthOfField' in r ? (r as { strengthOfField?: number }).strengthOfField ?? null : null,
|
||||
isUpcoming: Boolean(isUpcoming),
|
||||
isLive: Boolean(isLive),
|
||||
isPast: Boolean(isPast),
|
||||
@@ -76,4 +85,4 @@ export class RacesPageViewModel {
|
||||
get completedRaces(): RaceListItemViewModel[] {
|
||||
return this.races.filter(r => r.status === 'completed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RecordEngagementOutputDTO } from '../types/generated/RecordEngagementOutputDTO';
|
||||
import type { RecordEngagementOutputDTO } from '@/lib/types/generated/RecordEngagementOutputDTO';
|
||||
|
||||
/**
|
||||
* Record engagement output view model
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RecordPageViewOutputDTO } from '../types/generated/RecordPageViewOutputDTO';
|
||||
import type { RecordPageViewOutputDTO } from '@/lib/types/generated/RecordPageViewOutputDTO';
|
||||
|
||||
/**
|
||||
* Record page view output view model
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RemoveLeagueMemberOutputDTO } from '../types/generated/RemoveLeagueMemberOutputDTO';
|
||||
import { RemoveLeagueMemberOutputDTO } from '@/lib/types/generated/RemoveLeagueMemberOutputDTO';
|
||||
|
||||
/**
|
||||
* View Model for Remove Member Result
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequestAvatarGenerationOutputDTO } from '../types/generated/RequestAvatarGenerationOutputDTO';
|
||||
import { RequestAvatarGenerationOutputDTO } from '@/lib/types/generated/RequestAvatarGenerationOutputDTO';
|
||||
|
||||
/**
|
||||
* Request Avatar Generation View Model
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AuthenticatedUserDTO } from '../types/generated/AuthenticatedUserDTO';
|
||||
import { AuthenticatedUserDTO } from '@/lib/types/generated/AuthenticatedUserDTO';
|
||||
|
||||
export class SessionViewModel {
|
||||
userId: string;
|
||||
@@ -6,31 +6,28 @@ export class SessionViewModel {
|
||||
displayName: string;
|
||||
avatarUrl?: string | null;
|
||||
role?: string;
|
||||
driverId?: string;
|
||||
isAuthenticated: boolean = true;
|
||||
|
||||
constructor(dto: AuthenticatedUserDTO) {
|
||||
this.userId = dto.userId;
|
||||
this.email = dto.email;
|
||||
this.displayName = dto.displayName;
|
||||
|
||||
const anyDto = dto as unknown as { primaryDriverId?: unknown; driverId?: unknown; avatarUrl?: unknown; role?: unknown };
|
||||
if (typeof anyDto.primaryDriverId === 'string' && anyDto.primaryDriverId) {
|
||||
this.driverId = anyDto.primaryDriverId;
|
||||
} else if (typeof anyDto.driverId === 'string' && anyDto.driverId) {
|
||||
this.driverId = anyDto.driverId;
|
||||
// Use the optional fields from the DTO
|
||||
if (dto.primaryDriverId) {
|
||||
this.driverId = dto.primaryDriverId;
|
||||
}
|
||||
if (anyDto.avatarUrl !== undefined) {
|
||||
this.avatarUrl = anyDto.avatarUrl as string | null;
|
||||
|
||||
if (dto.avatarUrl !== undefined) {
|
||||
this.avatarUrl = dto.avatarUrl;
|
||||
}
|
||||
if (typeof anyDto.role === 'string' && anyDto.role) {
|
||||
this.role = anyDto.role;
|
||||
|
||||
if (dto.role) {
|
||||
this.role = dto.role;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: The generated DTO doesn't have these fields
|
||||
// These will need to be added when the OpenAPI spec is updated
|
||||
driverId?: string;
|
||||
isAuthenticated: boolean = true;
|
||||
|
||||
/**
|
||||
* Compatibility accessor.
|
||||
* Some legacy components expect `session.user.*`.
|
||||
@@ -73,4 +70,4 @@ export class SessionViewModel {
|
||||
get authStatusDisplay(): string {
|
||||
return this.isAuthenticated ? 'Logged In' : 'Logged Out';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import type { SponsorDashboardDTO } from '../types/generated/SponsorDashboardDTO';
|
||||
import { SponsorshipViewModel } from './SponsorshipViewModel';
|
||||
import { ActivityItemViewModel } from './ActivityItemViewModel';
|
||||
import { RenewalAlertViewModel } from './RenewalAlertViewModel';
|
||||
|
||||
/**
|
||||
* Sponsor Dashboard View Model
|
||||
*
|
||||
* View model for sponsor dashboard data with UI-specific transformations.
|
||||
*/
|
||||
export class SponsorDashboardViewModel {
|
||||
sponsorId: string;
|
||||
sponsorName: string;
|
||||
metrics: any;
|
||||
sponsorships: {
|
||||
leagues: SponsorshipViewModel[];
|
||||
teams: SponsorshipViewModel[];
|
||||
drivers: SponsorshipViewModel[];
|
||||
races: SponsorshipViewModel[];
|
||||
platform: SponsorshipViewModel[];
|
||||
};
|
||||
recentActivity: ActivityItemViewModel[];
|
||||
upcomingRenewals: RenewalAlertViewModel[];
|
||||
|
||||
constructor(dto: SponsorDashboardDTO) {
|
||||
this.sponsorId = dto.sponsorId;
|
||||
this.sponsorName = dto.sponsorName;
|
||||
this.metrics = dto.metrics;
|
||||
|
||||
// Cast sponsorships to proper type
|
||||
const sponsorships = dto.sponsorships as any;
|
||||
this.sponsorships = {
|
||||
leagues: (sponsorships?.leagues || []).map((s: any) => new SponsorshipViewModel(s)),
|
||||
teams: (sponsorships?.teams || []).map((s: any) => new SponsorshipViewModel(s)),
|
||||
drivers: (sponsorships?.drivers || []).map((s: any) => new SponsorshipViewModel(s)),
|
||||
races: (sponsorships?.races || []).map((s: any) => new SponsorshipViewModel(s)),
|
||||
platform: (sponsorships?.platform || []).map((s: any) => new SponsorshipViewModel(s)),
|
||||
};
|
||||
this.recentActivity = (dto.recentActivity || []).map((a: any) => new ActivityItemViewModel(a));
|
||||
this.upcomingRenewals = (dto.upcomingRenewals || []).map((r: any) => new RenewalAlertViewModel(r));
|
||||
}
|
||||
|
||||
get totalSponsorships(): number {
|
||||
return this.sponsorships.leagues.length +
|
||||
this.sponsorships.teams.length +
|
||||
this.sponsorships.drivers.length +
|
||||
this.sponsorships.races.length +
|
||||
this.sponsorships.platform.length;
|
||||
}
|
||||
|
||||
get activeSponsorships(): number {
|
||||
const all = [
|
||||
...this.sponsorships.leagues,
|
||||
...this.sponsorships.teams,
|
||||
...this.sponsorships.drivers,
|
||||
...this.sponsorships.races,
|
||||
...this.sponsorships.platform,
|
||||
];
|
||||
return all.filter(s => s.status === 'active').length;
|
||||
}
|
||||
|
||||
get totalInvestment(): number {
|
||||
const all = [
|
||||
...this.sponsorships.leagues,
|
||||
...this.sponsorships.teams,
|
||||
...this.sponsorships.drivers,
|
||||
...this.sponsorships.races,
|
||||
...this.sponsorships.platform,
|
||||
];
|
||||
return all.filter(s => s.status === 'active').reduce((sum, s) => sum + s.price, 0);
|
||||
}
|
||||
|
||||
get totalImpressions(): number {
|
||||
const all = [
|
||||
...this.sponsorships.leagues,
|
||||
...this.sponsorships.teams,
|
||||
...this.sponsorships.drivers,
|
||||
...this.sponsorships.races,
|
||||
...this.sponsorships.platform,
|
||||
];
|
||||
return all.reduce((sum, s) => sum + s.impressions, 0);
|
||||
}
|
||||
|
||||
/** UI-specific: Formatted total investment */
|
||||
get formattedTotalInvestment(): string {
|
||||
return `$${this.totalInvestment.toLocaleString()}`;
|
||||
}
|
||||
|
||||
/** UI-specific: Active percentage */
|
||||
get activePercentage(): number {
|
||||
if (this.totalSponsorships === 0) return 0;
|
||||
return Math.round((this.activeSponsorships / this.totalSponsorships) * 100);
|
||||
}
|
||||
|
||||
/** UI-specific: Has sponsorships */
|
||||
get hasSponsorships(): boolean {
|
||||
return this.totalSponsorships > 0;
|
||||
}
|
||||
|
||||
/** UI-specific: Status text */
|
||||
get statusText(): string {
|
||||
if (this.activeSponsorships === 0) return 'No active sponsorships';
|
||||
if (this.activeSponsorships === this.totalSponsorships) return 'All sponsorships active';
|
||||
return `${this.activeSponsorships} of ${this.totalSponsorships} active`;
|
||||
}
|
||||
|
||||
/** UI-specific: Cost per 1K views */
|
||||
get costPerThousandViews(): string {
|
||||
if (this.totalImpressions === 0) return '$0.00';
|
||||
return `$${(this.totalInvestment / this.totalImpressions * 1000).toFixed(2)}`;
|
||||
}
|
||||
|
||||
/** UI-specific: Category data for charts */
|
||||
get categoryData() {
|
||||
return {
|
||||
leagues: {
|
||||
count: this.sponsorships.leagues.length,
|
||||
impressions: this.sponsorships.leagues.reduce((sum, l) => sum + l.impressions, 0),
|
||||
},
|
||||
teams: {
|
||||
count: this.sponsorships.teams.length,
|
||||
impressions: this.sponsorships.teams.reduce((sum, t) => sum + t.impressions, 0),
|
||||
},
|
||||
drivers: {
|
||||
count: this.sponsorships.drivers.length,
|
||||
impressions: this.sponsorships.drivers.reduce((sum, d) => sum + d.impressions, 0),
|
||||
},
|
||||
races: {
|
||||
count: this.sponsorships.races.length,
|
||||
impressions: this.sponsorships.races.reduce((sum, r) => sum + r.impressions, 0),
|
||||
},
|
||||
platform: {
|
||||
count: this.sponsorships.platform.length,
|
||||
impressions: this.sponsorships.platform.reduce((sum, p) => sum + p.impressions, 0),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SponsorSponsorshipsDTO } from '../types/generated/SponsorSponsorshipsDTO';
|
||||
import type { SponsorSponsorshipsDTO } from '@/lib/types/generated/SponsorSponsorshipsDTO';
|
||||
import { SponsorshipDetailViewModel } from './SponsorshipDetailViewModel';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SponsorshipDetailDTO } from '../types/generated/SponsorshipDetailDTO';
|
||||
import { SponsorshipDetailDTO } from '@/lib/types/generated/SponsorshipDetailDTO';
|
||||
|
||||
export class SponsorshipDetailViewModel {
|
||||
id: string;
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
/**
|
||||
* Interface for sponsorship data input
|
||||
*/
|
||||
export interface SponsorshipDataInput {
|
||||
id: string;
|
||||
type: 'leagues' | 'teams' | 'drivers' | 'races' | 'platform';
|
||||
entityId: string;
|
||||
entityName: string;
|
||||
tier?: 'main' | 'secondary';
|
||||
status: 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired';
|
||||
applicationDate?: string | Date;
|
||||
approvalDate?: string | Date;
|
||||
rejectionReason?: string;
|
||||
startDate: string | Date;
|
||||
endDate: string | Date;
|
||||
price: number;
|
||||
impressions: number;
|
||||
impressionsChange?: number;
|
||||
engagement?: number;
|
||||
details?: string;
|
||||
entityOwner?: string;
|
||||
applicationMessage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sponsorship View Model
|
||||
*
|
||||
@@ -23,7 +47,7 @@ export class SponsorshipViewModel {
|
||||
entityOwner?: string;
|
||||
applicationMessage?: string;
|
||||
|
||||
constructor(data: any) {
|
||||
constructor(data: SponsorshipDataInput) {
|
||||
this.id = data.id;
|
||||
this.type = data.type;
|
||||
this.entityId = data.entityId;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LeagueStandingDTO } from '../types/generated/LeagueStandingDTO';
|
||||
import { LeagueStandingDTO } from '@/lib/types/generated/LeagueStandingDTO';
|
||||
|
||||
export class StandingEntryViewModel {
|
||||
driverId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { WalletTransactionViewModel } from './WalletTransactionViewModel';
|
||||
import { WalletTransactionViewModel, FullTransactionDto } from './WalletTransactionViewModel';
|
||||
|
||||
const createTx = (overrides: Partial<any> = {}): any => ({
|
||||
const createTx = (overrides: Partial<FullTransactionDto> = {}): FullTransactionDto => ({
|
||||
id: 'tx-1',
|
||||
type: 'sponsorship',
|
||||
description: 'Test',
|
||||
@@ -44,7 +44,7 @@ describe('WalletTransactionViewModel', () => {
|
||||
});
|
||||
|
||||
it('derives typeDisplay and formattedDate', () => {
|
||||
const vm = new WalletTransactionViewModel(createTx({ type: 'membership' as any }));
|
||||
const vm = new WalletTransactionViewModel(createTx({ type: 'membership' }));
|
||||
|
||||
expect(vm.typeDisplay).toBe('Membership');
|
||||
expect(typeof vm.formattedDate).toBe('string');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WalletDTO } from '../types/generated/WalletDTO';
|
||||
import { WalletDTO } from '@/lib/types/generated/WalletDTO';
|
||||
import { FullTransactionDto, WalletTransactionViewModel } from './WalletTransactionViewModel';
|
||||
|
||||
export class WalletViewModel {
|
||||
|
||||
@@ -1,91 +1,100 @@
|
||||
export * from './ActivityItemViewModel';
|
||||
export * from './AnalyticsDashboardViewModel';
|
||||
export * from './AnalyticsMetricsViewModel';
|
||||
export * from './AvailableLeaguesViewModel';
|
||||
export * from './AvatarGenerationViewModel';
|
||||
export * from './AvatarViewModel';
|
||||
export * from './BillingViewModel';
|
||||
export * from './CompleteOnboardingViewModel';
|
||||
export * from './CreateLeagueViewModel';
|
||||
export * from './CreateTeamViewModel';
|
||||
export * from './view-models/ActivityItemViewModel';
|
||||
export * from './view-models/AnalyticsDashboardViewModel';
|
||||
export * from './view-models/AnalyticsMetricsViewModel';
|
||||
export * from './view-models/AvailableLeaguesViewModel';
|
||||
export * from './view-models/AvatarGenerationViewModel';
|
||||
export * from './view-models/AvatarViewModel';
|
||||
export * from './view-models/BillingViewModel';
|
||||
export * from './view-models/CompleteOnboardingViewModel';
|
||||
export * from './view-models/CreateLeagueViewModel';
|
||||
export * from './view-models/CreateTeamViewModel';
|
||||
export {
|
||||
DashboardOverviewViewModel,
|
||||
} from './DashboardOverviewViewModel';
|
||||
export * from './DeleteMediaViewModel';
|
||||
export * from './DriverLeaderboardItemViewModel';
|
||||
export * from './DriverLeaderboardViewModel';
|
||||
export * from './DriverProfileViewModel';
|
||||
export * from './DriverRegistrationStatusViewModel';
|
||||
export * from './DriverSummaryViewModel';
|
||||
export * from './DriverTeamViewModel';
|
||||
export * from './DriverViewModel';
|
||||
export * from './EmailSignupViewModel';
|
||||
export * from './HomeDiscoveryViewModel';
|
||||
export * from './ImportRaceResultsSummaryViewModel';
|
||||
export * from './LeagueAdminViewModel';
|
||||
export * from './LeagueCardViewModel';
|
||||
export * from './LeagueDetailPageViewModel';
|
||||
export { LeagueDetailViewModel, LeagueViewModel } from './LeagueDetailViewModel';
|
||||
export * from './LeagueJoinRequestViewModel';
|
||||
export * from './LeagueMembershipsViewModel';
|
||||
export * from './LeagueMemberViewModel';
|
||||
export * from './LeaguePageDetailViewModel';
|
||||
export * from './LeagueScheduleViewModel';
|
||||
export * from './LeagueScoringChampionshipViewModel';
|
||||
export * from './LeagueScoringConfigViewModel';
|
||||
export * from './LeagueScoringPresetsViewModel';
|
||||
export * from './LeagueScoringPresetViewModel';
|
||||
export * from './LeagueSettingsViewModel';
|
||||
export * from './LeagueStandingsViewModel';
|
||||
export * from './LeagueStatsViewModel';
|
||||
export * from './LeagueStewardingViewModel';
|
||||
export * from './LeagueSummaryViewModel';
|
||||
export * from './LeagueWalletViewModel';
|
||||
export * from './MediaViewModel';
|
||||
export * from './MembershipFeeViewModel';
|
||||
export * from './PaymentViewModel';
|
||||
export * from './PrizeViewModel';
|
||||
export * from './ProfileOverviewViewModel';
|
||||
export * from './ProtestDriverViewModel';
|
||||
export * from './ProtestViewModel';
|
||||
export * from './RaceDetailEntryViewModel';
|
||||
export * from './RaceDetailUserResultViewModel';
|
||||
export * from './RaceDetailViewModel';
|
||||
export * from './RaceListItemViewModel';
|
||||
export * from './RaceResultsDataTransformer';
|
||||
export * from './RaceResultsDetailViewModel';
|
||||
export * from './RaceResultViewModel';
|
||||
export * from './RacesPageViewModel';
|
||||
export * from './RaceStatsViewModel';
|
||||
export * from './RaceStewardingViewModel';
|
||||
export * from './RaceViewModel';
|
||||
export * from './RaceWithSOFViewModel';
|
||||
export * from './RecordEngagementInputViewModel';
|
||||
export * from './RecordEngagementOutputViewModel';
|
||||
export * from './RecordPageViewInputViewModel';
|
||||
export * from './RecordPageViewOutputViewModel';
|
||||
export * from './RemoveMemberViewModel';
|
||||
export * from './RenewalAlertViewModel';
|
||||
export * from './RequestAvatarGenerationViewModel';
|
||||
export * from './SessionViewModel';
|
||||
export * from './SponsorDashboardViewModel';
|
||||
export * from './SponsorSettingsViewModel';
|
||||
export * from './SponsorshipDetailViewModel';
|
||||
export * from './SponsorshipPricingViewModel';
|
||||
export * from './SponsorshipRequestViewModel';
|
||||
export * from './SponsorshipViewModel';
|
||||
export * from './SponsorSponsorshipsViewModel';
|
||||
export * from './SponsorViewModel';
|
||||
export * from './StandingEntryViewModel';
|
||||
export * from './TeamCardViewModel';
|
||||
export * from './TeamDetailsViewModel';
|
||||
export * from './TeamJoinRequestViewModel';
|
||||
export * from './TeamMemberViewModel';
|
||||
export * from './TeamSummaryViewModel';
|
||||
export * from './UpcomingRaceCardViewModel';
|
||||
export * from './UpdateAvatarViewModel';
|
||||
export * from './UpdateTeamViewModel';
|
||||
export * from './UploadMediaViewModel';
|
||||
export * from './UserProfileViewModel';
|
||||
export * from './WalletTransactionViewModel';
|
||||
export * from './WalletViewModel';
|
||||
} from './view-models/DashboardOverviewViewModel';
|
||||
export * from './view-models/DashboardOverviewViewModelData';
|
||||
export * from './view-models/DeleteMediaViewModel';
|
||||
export * from './view-models/DriverLeaderboardItemViewModel';
|
||||
export * from './view-models/DriverLeaderboardViewModel';
|
||||
export * from './view-models/DriverProfileViewModel';
|
||||
export * from './view-models/DriverRegistrationStatusViewModel';
|
||||
export * from './view-models/DriverSummaryViewModel';
|
||||
export * from './view-models/DriverTeamViewModel';
|
||||
export * from './view-models/DriverViewModel';
|
||||
export * from './view-models/EmailSignupViewModel';
|
||||
export * from './view-models/HomeDiscoveryViewModel';
|
||||
export * from './view-models/ImportRaceResultsSummaryViewModel';
|
||||
export * from './view-models/LeagueAdminViewModel';
|
||||
export * from './view-models/LeagueCardViewModel';
|
||||
export * from './view-models/LeagueDetailPageViewModel';
|
||||
export { LeagueDetailViewModel, LeagueViewModel } from './view-models/LeagueDetailViewModel';
|
||||
export * from './view-models/LeagueJoinRequestViewModel';
|
||||
export * from './view-models/LeagueMembershipsViewModel';
|
||||
export * from './view-models/LeagueMemberViewModel';
|
||||
export * from './view-models/LeaguePageDetailViewModel';
|
||||
export * from './view-models/LeagueScheduleViewModel';
|
||||
export * from './view-models/LeagueScoringChampionshipViewModel';
|
||||
export * from './view-models/LeagueScoringConfigViewModel';
|
||||
export * from './view-models/LeagueScoringPresetsViewModel';
|
||||
export * from './view-models/LeagueScoringPresetViewModel';
|
||||
export * from './view-models/LeagueScoringSectionViewModel';
|
||||
export * from './view-models/LeagueSettingsViewModel';
|
||||
export * from './view-models/LeagueStandingsViewModel';
|
||||
export * from './view-models/LeagueStatsViewModel';
|
||||
export * from './view-models/LeagueStewardingViewModel';
|
||||
export * from './view-models/LeagueSummaryViewModel';
|
||||
export * from './view-models/LeagueWalletViewModel';
|
||||
export * from './view-models/MediaViewModel';
|
||||
export * from './view-models/MembershipFeeViewModel';
|
||||
export * from './view-models/PaymentViewModel';
|
||||
export * from './view-models/PrizeViewModel';
|
||||
export * from './view-models/ProfileOverviewViewModel';
|
||||
export * from './view-models/ProtestDriverViewModel';
|
||||
export * from './view-models/ProtestViewModel';
|
||||
export * from './view-models/RaceDetailEntryViewModel';
|
||||
export * from './view-models/RaceDetailUserResultViewModel';
|
||||
export * from './view-models/RaceDetailViewModel';
|
||||
export * from './view-models/RaceListItemViewModel';
|
||||
export * from './view-models/RaceResultsDataTransformer';
|
||||
export * from './view-models/RaceResultsDetailViewModel';
|
||||
export * from './view-models/RaceResultViewModel';
|
||||
export * from './view-models/RacesPageViewModel';
|
||||
export * from './view-models/RaceStatsViewModel';
|
||||
export * from './view-models/RaceStewardingViewModel';
|
||||
export * from './view-models/RaceViewModel';
|
||||
export * from './view-models/RaceWithSOFViewModel';
|
||||
export * from './view-models/RecordEngagementInputViewModel';
|
||||
export * from './view-models/RecordEngagementOutputViewModel';
|
||||
export * from './view-models/RecordPageViewInputViewModel';
|
||||
export * from './view-models/RecordPageViewOutputViewModel';
|
||||
export * from './view-models/RemoveMemberViewModel';
|
||||
export * from './view-models/RenewalAlertViewModel';
|
||||
export * from './view-models/RequestAvatarGenerationViewModel';
|
||||
export * from './view-models/ScoringConfigurationViewModel';
|
||||
export * from './view-models/SessionViewModel';
|
||||
export * from './view-models/SponsorDashboardViewModel';
|
||||
export * from './view-models/SponsorSettingsViewModel';
|
||||
export * from './view-models/SponsorshipDetailViewModel';
|
||||
export * from './view-models/SponsorshipPricingViewModel';
|
||||
export * from './view-models/SponsorshipRequestViewModel';
|
||||
export * from './view-models/SponsorshipViewModel';
|
||||
export * from './view-models/SponsorSponsorshipsViewModel';
|
||||
export * from './view-models/SponsorViewModel';
|
||||
export * from './view-models/StandingEntryViewModel';
|
||||
export * from './view-models/TeamCardViewModel';
|
||||
export * from './view-models/TeamDetailsViewModel';
|
||||
export * from './view-models/TeamJoinRequestViewModel';
|
||||
export * from './view-models/TeamMemberViewModel';
|
||||
export * from './view-models/TeamSummaryViewModel';
|
||||
export * from './view-models/UpcomingRaceCardViewModel';
|
||||
export * from './view-models/UpdateAvatarViewModel';
|
||||
export * from './view-models/UpdateTeamViewModel';
|
||||
export * from './view-models/UploadMediaViewModel';
|
||||
export * from './view-models/UserProfileViewModel';
|
||||
export * from './view-models/WalletTransactionViewModel';
|
||||
export * from './view-models/WalletViewModel';
|
||||
|
||||
export * from './presenters/DashboardPresenter';
|
||||
export * from './presenters/ProfileLeaguesPresenter';
|
||||
export * from './presenters/TeamDetailPresenter';
|
||||
export * from './presenters/TeamsPresenter';
|
||||
export * from './presenters/AdminViewModelPresenter';
|
||||
Reference in New Issue
Block a user