fix issues

This commit is contained in:
2025-12-26 11:49:20 +01:00
parent d08ec10b40
commit 68ae9da22a
44 changed files with 505 additions and 179 deletions

View File

@@ -57,11 +57,11 @@ export class DashboardRaceSummaryViewModel {
}
get leagueId(): string {
return this.dto.leagueId;
return (this.dto as any).leagueId ?? '';
}
get leagueName(): string {
return this.dto.leagueName;
return (this.dto as any).leagueName ?? '';
}
get track(): string {
@@ -166,38 +166,52 @@ export class DashboardOverviewViewModel {
get currentDriver(): DashboardDriverSummaryViewModel {
// DTO uses optional property; enforce a consistent object for the UI
return new DashboardDriverSummaryViewModel(this.dto.currentDriver ?? {
id: '',
name: '',
country: '',
avatarUrl: '',
totalRaces: 0,
wins: 0,
podiums: 0,
});
return new DashboardDriverSummaryViewModel(
(this.dto as any).currentDriver ?? {
id: '',
name: '',
country: '',
avatarUrl: '',
totalRaces: 0,
wins: 0,
podiums: 0,
},
);
}
get nextRace(): DashboardRaceSummaryViewModel | null {
return this.dto.nextRace ? new DashboardRaceSummaryViewModel(this.dto.nextRace) : null;
const nextRace = (this.dto as any).nextRace;
return nextRace ? new DashboardRaceSummaryViewModel(nextRace) : null;
}
get upcomingRaces(): DashboardRaceSummaryViewModel[] {
return this.dto.upcomingRaces.map((r) => new DashboardRaceSummaryViewModel(r));
const upcomingRaces = (this.dto as any).upcomingRaces ?? [];
return upcomingRaces.map((r: any) => new DashboardRaceSummaryViewModel(r));
}
get leagueStandings(): DashboardLeagueStandingSummaryViewModel[] {
return this.dto.leagueStandingsSummaries.map((s) => new DashboardLeagueStandingSummaryViewModel(s));
const leagueStandings = (this.dto as any).leagueStandingsSummaries ?? (this.dto as any).leagueStandings ?? [];
return leagueStandings.map((s: any) => new DashboardLeagueStandingSummaryViewModel(s));
}
get feedItems(): DashboardFeedItemSummaryViewModel[] {
return this.dto.feedSummary.items.map((i) => new DashboardFeedItemSummaryViewModel(i));
const feedItems = (this.dto as any).feedSummary?.items ?? (this.dto as any).feedItems ?? [];
return feedItems.map((i: any) => new DashboardFeedItemSummaryViewModel(i));
}
get friends(): DashboardFriendSummaryViewModel[] {
return this.dto.friends.map((f) => new DashboardFriendSummaryViewModel(f));
const friends = (this.dto as any).friends ?? [];
return friends.map((f: any) => new DashboardFriendSummaryViewModel(f));
}
get activeLeaguesCount(): number {
return this.dto.activeLeaguesCount;
return (this.dto as any).activeLeaguesCount ?? 0;
}
}
export {
DashboardDriverSummaryViewModel as DriverViewModel,
DashboardRaceSummaryViewModel as RaceViewModel,
DashboardLeagueStandingSummaryViewModel as LeagueStandingViewModel,
DashboardFriendSummaryViewModel as FriendViewModel,
};

View File

@@ -108,30 +108,40 @@ export class LeagueDetailPageViewModel {
this.ownerId = league.ownerId;
this.createdAt = league.createdAt;
this.settings = {
maxDrivers: league.settings?.maxDrivers,
maxDrivers: league.settings?.maxDrivers ?? (league as any).maxDrivers,
};
this.socialLinks = {
discordUrl: league.discordUrl,
youtubeUrl: league.youtubeUrl,
websiteUrl: league.websiteUrl,
discordUrl: league.discordUrl ?? (league as any).socialLinks?.discordUrl,
youtubeUrl: league.youtubeUrl ?? (league as any).socialLinks?.youtubeUrl,
websiteUrl: league.websiteUrl ?? (league as any).socialLinks?.websiteUrl,
};
this.owner = owner;
this.scoringConfig = scoringConfig;
this.drivers = drivers;
this.memberships = memberships.members.map(m => ({
const membershipDtos = ((memberships as any).members ?? (memberships as any).memberships ?? []) as Array<{
driverId: string;
role: string;
status?: 'active' | 'inactive';
joinedAt: string;
}>;
this.memberships = membershipDtos.map((m) => ({
driverId: m.driverId,
role: m.role as 'owner' | 'admin' | 'steward' | 'member',
status: 'active',
status: m.status ?? 'active',
joinedAt: m.joinedAt,
}));
this.allRaces = allRaces;
this.runningRaces = allRaces.filter(r => r.status === 'running');
const leagueStatsAny = leagueStats as any;
// Calculate SOF from available data
this.averageSOF = leagueStats.averageRating ?? null;
this.completedRacesCount = leagueStats.totalRaces ?? 0;
this.averageSOF = leagueStatsAny.averageSOF ?? leagueStats.averageRating ?? null;
this.completedRacesCount = leagueStatsAny.completedRaces ?? leagueStats.totalRaces ?? 0;
this.sponsors = sponsors;

View File

@@ -4,7 +4,7 @@ import { DriverViewModel } from './DriverViewModel';
export class LeagueMemberViewModel {
driverId: string;
private currentUserId: string;
currentUserId: string;
constructor(dto: LeagueMemberDTO, currentUserId: string) {
this.driverId = dto.driverId;

View File

@@ -9,8 +9,9 @@ import type { LeagueMemberDTO } from '../types/generated/LeagueMemberDTO';
export class LeagueMembershipsViewModel {
memberships: LeagueMemberViewModel[];
constructor(dto: { members: LeagueMemberDTO[] }, currentUserId: string) {
this.memberships = dto.members.map(membership => new LeagueMemberViewModel(membership, currentUserId));
constructor(dto: { members?: LeagueMemberDTO[]; memberships?: LeagueMemberDTO[] }, currentUserId: string) {
const memberships = dto.members ?? dto.memberships ?? [];
this.memberships = memberships.map((membership) => new LeagueMemberViewModel(membership, currentUserId));
}
/** UI-specific: Number of members */

View File

@@ -26,7 +26,9 @@ export class MediaViewModel {
get formattedSize(): string {
if (!this.size) return 'Unknown';
const kb = this.size / 1024;
if (kb < 1024) return `${kb.toFixed(2)} KB`;
const mb = kb / 1024;
return `${mb.toFixed(2)} MB`;
}

View File

@@ -32,7 +32,7 @@ export class RaceDetailViewModel {
/** UI-specific: Whether user is registered */
get isRegistered(): boolean {
return this.registration.isUserRegistered;
return (this.registration as any).isUserRegistered ?? (this.registration as any).isRegistered ?? false;
}
/** UI-specific: Whether user can register */

View File

@@ -40,6 +40,10 @@ export class RaceListItemViewModel {
this.isPast = dto.isPast;
}
get title(): string {
return `${this.track} - ${this.car}`;
}
/** UI-specific: Formatted scheduled time */
get formattedScheduledTime(): string {
return new Date(this.scheduledAt).toLocaleString();

View File

@@ -16,12 +16,26 @@ export class RacesPageViewModel {
constructor(dto: RacesPageDTO) {
this.races = dto.races.map((r) => {
const status = (r as any).status as string | undefined;
const isUpcoming =
(r as any).isUpcoming ??
(status === 'upcoming' || status === 'scheduled');
const isLive =
(r as any).isLive ??
(status === 'live' || status === 'running');
const isPast =
(r as any).isPast ??
(status === 'completed' || status === 'finished' || status === 'cancelled');
const normalized: RaceListItemDTO = {
...r,
...(r as any),
strengthOfField: (r as any).strengthOfField ?? null,
isUpcoming: r.isUpcoming,
isLive: r.isLive,
isPast: r.isPast,
isUpcoming: Boolean(isUpcoming),
isLive: Boolean(isLive),
isPast: Boolean(isPast),
};
return new RaceListItemViewModel(normalized);

View File

@@ -11,11 +11,33 @@ export class RequestAvatarGenerationViewModel {
avatarUrls?: string[];
errorMessage?: string;
constructor(dto: RequestAvatarGenerationOutputDTO) {
constructor(
dto:
| RequestAvatarGenerationOutputDTO
| {
success: boolean;
requestId?: string;
avatarUrls?: string[];
errorMessage?: string;
avatarUrl?: string;
error?: string;
},
) {
this.success = dto.success;
if (dto.requestId !== undefined) this.requestId = dto.requestId;
if (dto.avatarUrls !== undefined) this.avatarUrls = dto.avatarUrls;
if (dto.errorMessage !== undefined) this.errorMessage = dto.errorMessage;
if ('requestId' in dto && dto.requestId !== undefined) this.requestId = dto.requestId;
if ('avatarUrls' in dto && dto.avatarUrls !== undefined) {
this.avatarUrls = dto.avatarUrls;
} else if ('avatarUrl' in dto && dto.avatarUrl !== undefined) {
this.avatarUrls = [dto.avatarUrl];
}
if ('errorMessage' in dto && dto.errorMessage !== undefined) {
this.errorMessage = dto.errorMessage;
} else if ('error' in dto && dto.error !== undefined) {
this.errorMessage = dto.error;
}
}
/** UI-specific: Whether generation was successful */

View File

@@ -9,8 +9,8 @@ interface SponsorDTO {
export class SponsorViewModel {
id: string;
name: string;
logoUrl?: string;
websiteUrl?: string;
declare logoUrl?: string;
declare websiteUrl?: string;
constructor(dto: SponsorDTO) {
this.id = dto.id;

View File

@@ -23,10 +23,16 @@ export class TeamDetailsViewModel {
this.ownerId = dto.team.ownerId;
this.leagues = dto.team.leagues;
this.createdAt = dto.team.createdAt;
// These properties don't exist in the current TeamDTO but may be added later
this.specialization = undefined;
this.region = undefined;
this.languages = undefined;
const teamExtras = dto.team as typeof dto.team & {
specialization?: string;
region?: string;
languages?: string[];
};
this.specialization = teamExtras.specialization ?? undefined;
this.region = teamExtras.region ?? undefined;
this.languages = teamExtras.languages ?? undefined;
this.membership = dto.membership ? {
role: dto.membership.role,
joinedAt: dto.membership.joinedAt,

View File

@@ -0,0 +1,95 @@
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 {
DashboardOverviewViewModel,
DashboardDriverSummaryViewModel,
DashboardRaceSummaryViewModel,
DashboardLeagueStandingSummaryViewModel,
DashboardFeedItemSummaryViewModel,
DashboardFriendSummaryViewModel,
} 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 './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';