website cleanup
This commit is contained in:
@@ -10,6 +10,8 @@ import { PaymentsApiClient } from './payments/PaymentsApiClient';
|
||||
import { DashboardApiClient } from './dashboard/DashboardApiClient';
|
||||
import { PenaltiesApiClient } from './penalties/PenaltiesApiClient';
|
||||
import { ProtestsApiClient } from './protests/ProtestsApiClient';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
|
||||
|
||||
/**
|
||||
* Main API Client
|
||||
@@ -31,18 +33,21 @@ export class ApiClient {
|
||||
public readonly protests: ProtestsApiClient;
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.leagues = new LeaguesApiClient(baseUrl);
|
||||
this.races = new RacesApiClient(baseUrl);
|
||||
this.drivers = new DriversApiClient(baseUrl);
|
||||
this.teams = new TeamsApiClient(baseUrl);
|
||||
this.sponsors = new SponsorsApiClient(baseUrl);
|
||||
this.media = new MediaApiClient(baseUrl);
|
||||
this.analytics = new AnalyticsApiClient(baseUrl);
|
||||
this.auth = new AuthApiClient(baseUrl);
|
||||
this.payments = new PaymentsApiClient(baseUrl);
|
||||
this.dashboard = new DashboardApiClient(baseUrl);
|
||||
this.penalties = new PenaltiesApiClient(baseUrl);
|
||||
this.protests = new ProtestsApiClient(baseUrl);
|
||||
const logger = new ConsoleLogger();
|
||||
const errorReporter = new ConsoleErrorReporter();
|
||||
|
||||
this.leagues = new LeaguesApiClient(baseUrl, errorReporter, logger);
|
||||
this.races = new RacesApiClient(baseUrl, errorReporter, logger);
|
||||
this.drivers = new DriversApiClient(baseUrl, errorReporter, logger);
|
||||
this.teams = new TeamsApiClient(baseUrl, errorReporter, logger);
|
||||
this.sponsors = new SponsorsApiClient(baseUrl, errorReporter, logger);
|
||||
this.media = new MediaApiClient(baseUrl, errorReporter, logger);
|
||||
this.analytics = new AnalyticsApiClient(baseUrl, errorReporter, logger);
|
||||
this.auth = new AuthApiClient(baseUrl, errorReporter, logger);
|
||||
this.payments = new PaymentsApiClient(baseUrl, errorReporter, logger);
|
||||
this.dashboard = new DashboardApiClient(baseUrl, errorReporter, logger);
|
||||
this.penalties = new PenaltiesApiClient(baseUrl, errorReporter, logger);
|
||||
this.protests = new ProtestsApiClient(baseUrl, errorReporter, logger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
apps/website/lib/leagueMembership.ts
Normal file
24
apps/website/lib/leagueMembership.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { LeagueMembershipService } from './services/leagues/LeagueMembershipService';
|
||||
|
||||
/**
|
||||
* Get membership for a driver in a league
|
||||
*/
|
||||
export function getMembership(leagueId: string, driverId: string) {
|
||||
return LeagueMembershipService.getMembership(leagueId, driverId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all members of a league
|
||||
*/
|
||||
export function getLeagueMembers(leagueId: string) {
|
||||
return LeagueMembershipService.getLeagueMembers(leagueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary league ID for a driver (first league they joined)
|
||||
*/
|
||||
export function getPrimaryLeagueIdForDriver(driverId: string): string | null {
|
||||
const memberships = LeagueMembershipService.getAllMembershipsForDriver(driverId);
|
||||
if (memberships.length === 0) return null;
|
||||
return memberships[0].leagueId;
|
||||
}
|
||||
8
apps/website/lib/leagueRoles.ts
Normal file
8
apps/website/lib/leagueRoles.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Re-export from utilities for backward compatibility
|
||||
export { LeagueRoleUtility } from './utilities/LeagueRoleUtility';
|
||||
export { LeagueMembershipUtility } from './utilities/LeagueMembershipUtility';
|
||||
|
||||
// Direct function export for convenience
|
||||
export const isLeagueAdminOrHigherRole = (role: string): boolean => {
|
||||
return role === 'owner' || role === 'admin' || role === 'steward';
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { AuthApiClient } from '../../api/auth/AuthApiClient';
|
||||
import { SessionViewModel } from '../../view-models/SessionViewModel';
|
||||
import type { LoginParams } from '../../types/generated/LoginParams';
|
||||
import type { SignupParams } from '../../types/generated/SignupParams';
|
||||
import type { LoginWithIracingCallbackParams } from '../../types/generated/LoginWithIracingCallbackParams';
|
||||
|
||||
/**
|
||||
* Auth Service
|
||||
@@ -55,4 +56,16 @@ export class AuthService {
|
||||
getIracingAuthUrl(returnTo?: string): string {
|
||||
return this.apiClient.getIracingAuthUrl(returnTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login with iRacing callback
|
||||
*/
|
||||
async loginWithIracingCallback(params: LoginWithIracingCallbackParams): Promise<SessionViewModel> {
|
||||
try {
|
||||
const dto = await this.apiClient.loginWithIracingCallback(params);
|
||||
return new SessionViewModel(dto.user);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { DriversApiClient } from '../../api/drivers/DriversApiClient';
|
||||
import { DriverRegistrationStatusViewModel } from '../../view-models';
|
||||
import { DriverRegistrationStatusViewModel } from '../../view-models/DriverRegistrationStatusViewModel';
|
||||
|
||||
/**
|
||||
* Driver Registration Service
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import type { LeagueMembership } from '@core/racing/domain/entities/LeagueMembership';
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
import type { MembershipStatus } from '@core/racing/domain/entities/MembershipStatus';
|
||||
import type { LeagueMembership } from '@/lib/types/LeagueMembership';
|
||||
|
||||
export class LeagueMembershipService {
|
||||
// In-memory cache for memberships (populated via API calls)
|
||||
@@ -33,12 +31,12 @@ export class LeagueMembershipService {
|
||||
static async fetchLeagueMemberships(leagueId: string): Promise<LeagueMembership[]> {
|
||||
try {
|
||||
const result = await apiClient.leagues.getMemberships(leagueId);
|
||||
const memberships: LeagueMembership[] = result.members.map(member => ({
|
||||
const memberships: LeagueMembership[] = result.members.map((member: any) => ({
|
||||
id: `${member.driverId}-${leagueId}`, // Generate ID since API doesn't provide it
|
||||
leagueId,
|
||||
driverId: member.driverId,
|
||||
role: member.role as MembershipRole,
|
||||
status: 'active' as MembershipStatus, // Assume active since API returns current members
|
||||
role: member.role,
|
||||
status: 'active', // Assume active since API returns current members
|
||||
joinedAt: member.joinedAt,
|
||||
}));
|
||||
this.setLeagueMemberships(leagueId, memberships);
|
||||
@@ -70,6 +68,20 @@ export class LeagueMembershipService {
|
||||
return this.leagueMemberships.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all memberships for a specific driver across all leagues.
|
||||
*/
|
||||
static getAllMembershipsForDriver(driverId: string): LeagueMembership[] {
|
||||
const allMemberships: LeagueMembership[] = [];
|
||||
for (const [leagueId, memberships] of this.leagueMemberships.entries()) {
|
||||
const driverMembership = memberships.find(m => m.driverId === driverId);
|
||||
if (driverMembership) {
|
||||
allMemberships.push(driverMembership);
|
||||
}
|
||||
}
|
||||
return allMemberships;
|
||||
}
|
||||
|
||||
// Instance methods that delegate to static methods for consistency with service pattern
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
|
||||
import { SponsorsApiClient } from "@/lib/api/sponsors/SponsorsApiClient";
|
||||
import { RacesApiClient } from "@/lib/api/races/RacesApiClient";
|
||||
import { CreateLeagueInputDTO } from "@/lib/types/generated/CreateLeagueInputDTO";
|
||||
import { CreateLeagueOutputDTO } from "@/lib/types/generated/CreateLeagueOutputDTO";
|
||||
import { LeagueWithCapacityDTO } from "@/lib/types/generated/LeagueWithCapacityDTO";
|
||||
import { CreateLeagueViewModel } from "@/lib/view-models/CreateLeagueViewModel";
|
||||
import { LeagueMembershipsViewModel } from "@/lib/view-models/LeagueMembershipsViewModel";
|
||||
@@ -11,14 +12,12 @@ import { LeagueStandingsViewModel } from "@/lib/view-models/LeagueStandingsViewM
|
||||
import { LeagueStatsViewModel } from "@/lib/view-models/LeagueStatsViewModel";
|
||||
import { LeagueSummaryViewModel } from "@/lib/view-models/LeagueSummaryViewModel";
|
||||
import { RemoveMemberViewModel } from "@/lib/view-models/RemoveMemberViewModel";
|
||||
import { LeagueDetailViewModel } from "@/lib/view-models/LeagueDetailViewModel";
|
||||
import { LeaguePageDetailViewModel } from "@/lib/view-models/LeaguePageDetailViewModel";
|
||||
import { LeagueDetailPageViewModel, SponsorInfo } from "@/lib/view-models/LeagueDetailPageViewModel";
|
||||
import { RaceViewModel } from "@/lib/view-models/RaceViewModel";
|
||||
import { SubmitBlocker, ThrottleBlocker } from "@/lib/blockers";
|
||||
import { RaceDTO } from "@/lib/types/generated/RaceDTO";
|
||||
import { LeagueScoringConfigDTO } from "@/lib/types/LeagueScoringConfigDTO";
|
||||
import { LeagueStatsDTO } from "@/lib/types/generated/LeagueStatsDTO";
|
||||
import { LeagueMembershipsDTO } from "@/lib/types/generated/LeagueMembershipsDTO";
|
||||
|
||||
|
||||
/**
|
||||
@@ -43,7 +42,17 @@ export class LeagueService {
|
||||
*/
|
||||
async getAllLeagues(): Promise<LeagueSummaryViewModel[]> {
|
||||
const dto = await this.apiClient.getAllWithCapacity();
|
||||
return dto.leagues.map((league: LeagueWithCapacityDTO) => new LeagueSummaryViewModel(league));
|
||||
return dto.leagues.map((league: LeagueWithCapacityDTO) => ({
|
||||
id: league.id,
|
||||
name: league.name,
|
||||
description: league.description ?? '',
|
||||
ownerId: league.ownerId,
|
||||
createdAt: '', // Not provided by API
|
||||
maxDrivers: league.maxMembers,
|
||||
usedDriverSlots: league.memberCount,
|
||||
structureSummary: 'TBD',
|
||||
timingSummary: 'TBD'
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,8 +66,8 @@ export class LeagueService {
|
||||
const membershipsDto = await this.apiClient.getMemberships(leagueId);
|
||||
|
||||
// Resolve unique drivers that appear in standings
|
||||
const driverIds = Array.from(new Set(dto.standings.map(entry => entry.driverId)));
|
||||
const driverDtos = await Promise.all(driverIds.map(id => this.driversApiClient.getDriver(id)));
|
||||
const driverIds: string[] = Array.from(new Set(dto.standings.map((entry: any) => entry.driverId)));
|
||||
const driverDtos = await Promise.all(driverIds.map((id: string) => this.driversApiClient.getDriver(id)));
|
||||
const drivers = driverDtos.filter((d): d is NonNullable<typeof d> => d !== null);
|
||||
|
||||
const dtoWithExtras = {
|
||||
@@ -95,15 +104,17 @@ export class LeagueService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new league
|
||||
*/
|
||||
async createLeague(input: CreateLeagueInputDTO): Promise<void> {
|
||||
if (!this.submitBlocker.canExecute() || !this.throttle.canExecute()) return;
|
||||
* Create a new league
|
||||
*/
|
||||
async createLeague(input: CreateLeagueInputDTO): Promise<CreateLeagueOutputDTO> {
|
||||
if (!this.submitBlocker.canExecute() || !this.throttle.canExecute()) {
|
||||
throw new Error('Cannot execute at this time');
|
||||
}
|
||||
|
||||
this.submitBlocker.block();
|
||||
this.throttle.block();
|
||||
try {
|
||||
await this.apiClient.create(input);
|
||||
return await this.apiClient.create(input);
|
||||
} finally {
|
||||
this.submitBlocker.release();
|
||||
}
|
||||
@@ -127,12 +138,12 @@ export class LeagueService {
|
||||
/**
|
||||
* Get league detail with owner, membership, and sponsor info
|
||||
*/
|
||||
async getLeagueDetail(leagueId: string, currentDriverId: string): Promise<LeagueDetailViewModel | null> {
|
||||
async getLeagueDetail(leagueId: string, currentDriverId: string): Promise<LeaguePageDetailViewModel | null> {
|
||||
// For now, assume league data comes from getAllWithCapacity or a new endpoint
|
||||
// Since API may not have detailed league, we'll mock or assume
|
||||
// In real implementation, add getLeagueDetail to API
|
||||
const allLeagues = await this.apiClient.getAllWithCapacity();
|
||||
const leagueDto = allLeagues.leagues.find(l => l.id === leagueId);
|
||||
const leagueDto = allLeagues.leagues.find((l: any) => l.id === leagueId);
|
||||
if (!leagueDto) return null;
|
||||
|
||||
// LeagueWithCapacityDTO already carries core fields; fall back to placeholder description/owner when not provided
|
||||
@@ -149,7 +160,7 @@ export class LeagueService {
|
||||
|
||||
// Get membership
|
||||
const membershipsDto = await this.apiClient.getMemberships(leagueId);
|
||||
const membership = membershipsDto.members.find(m => m.driverId === currentDriverId);
|
||||
const membership = membershipsDto.members.find((m: any) => m.driverId === currentDriverId);
|
||||
const isAdmin = membership ? ['admin', 'owner'].includes(membership.role) : false;
|
||||
|
||||
// Get main sponsor
|
||||
@@ -175,15 +186,31 @@ export class LeagueService {
|
||||
console.warn('Failed to load main sponsor:', error);
|
||||
}
|
||||
|
||||
return new LeagueDetailViewModel(
|
||||
league.id,
|
||||
league.name,
|
||||
league.description,
|
||||
league.ownerId,
|
||||
ownerName,
|
||||
mainSponsor,
|
||||
isAdmin
|
||||
);
|
||||
return new LeaguePageDetailViewModel({
|
||||
league: {
|
||||
id: league.id,
|
||||
name: league.name,
|
||||
game: 'iRacing',
|
||||
tier: 'standard',
|
||||
season: 'Season 1',
|
||||
description: league.description,
|
||||
drivers: 0,
|
||||
races: 0,
|
||||
completedRaces: 0,
|
||||
totalImpressions: 0,
|
||||
avgViewsPerRace: 0,
|
||||
engagement: 0,
|
||||
rating: 0,
|
||||
seasonStatus: 'active',
|
||||
seasonDates: { start: new Date().toISOString(), end: new Date().toISOString() },
|
||||
sponsorSlots: {
|
||||
main: { available: true, price: 800, benefits: [] },
|
||||
secondary: { available: 2, total: 2, price: 250, benefits: [] }
|
||||
}
|
||||
},
|
||||
drivers: [],
|
||||
races: []
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +220,7 @@ export class LeagueService {
|
||||
try {
|
||||
// Get league basic info
|
||||
const allLeagues = await this.apiClient.getAllWithCapacity();
|
||||
const league = allLeagues.leagues.find(l => l.id === leagueId);
|
||||
const league = allLeagues.leagues.find((l: any) => l.id === leagueId);
|
||||
if (!league) return null;
|
||||
|
||||
// Get owner
|
||||
@@ -206,7 +233,7 @@ export class LeagueService {
|
||||
const memberships = await this.apiClient.getMemberships(leagueId);
|
||||
const driverIds = memberships.members.map(m => m.driverId);
|
||||
const driverDtos = await Promise.all(driverIds.map(id => this.driversApiClient.getDriver(id)));
|
||||
const drivers = driverDtos.filter((d): d is NonNullable<typeof d> => d !== null);
|
||||
const drivers = driverDtos.filter((d: any): d is NonNullable<typeof d> => d !== null);
|
||||
|
||||
// Get all races for this league via the leagues API helper
|
||||
const leagueRaces = await this.apiClient.getRaces(leagueId);
|
||||
@@ -276,4 +303,12 @@ export class LeagueService {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get league scoring presets
|
||||
*/
|
||||
async getScoringPresets(): Promise<any[]> {
|
||||
const result = await this.apiClient.getScoringPresets();
|
||||
return result.presets;
|
||||
}
|
||||
}
|
||||
@@ -54,4 +54,18 @@ export class MediaService {
|
||||
getDriverAvatar(driverId: string): string {
|
||||
return `/api/media/avatar/${driverId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get league cover URL
|
||||
*/
|
||||
getLeagueCover(leagueId: string): string {
|
||||
return `/api/media/leagues/${leagueId}/cover`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get league logo URL
|
||||
*/
|
||||
getLeagueLogo(leagueId: string): string {
|
||||
return `/api/media/leagues/${leagueId}/logo`;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { SponsorsApiClient } from '../../api/sponsors/SponsorsApiClient';
|
||||
import type { GetEntitySponsorshipPricingResultDto } from '../../api/sponsors/SponsorsApiClient';
|
||||
import {
|
||||
SponsorshipPricingViewModel,
|
||||
SponsorSponsorshipsViewModel,
|
||||
SponsorshipRequestViewModel
|
||||
} from '../../view-models';
|
||||
import { SponsorshipPricingViewModel } from '../../view-models/SponsorshipPricingViewModel';
|
||||
import { SponsorSponsorshipsViewModel } from '../../view-models/SponsorSponsorshipsViewModel';
|
||||
import { SponsorshipRequestViewModel } from '../../view-models/SponsorshipRequestViewModel';
|
||||
import type { SponsorSponsorshipsDTO } from '../../types/generated';
|
||||
|
||||
/**
|
||||
|
||||
10
apps/website/lib/types/League.ts
Normal file
10
apps/website/lib/types/League.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export interface League {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
isPublic: boolean;
|
||||
maxMembers: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
54
apps/website/lib/types/LeagueConfigFormModel.ts
Normal file
54
apps/website/lib/types/LeagueConfigFormModel.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
export interface LeagueConfigFormModel {
|
||||
leagueId?: string;
|
||||
basics?: {
|
||||
name: string;
|
||||
description?: string;
|
||||
visibility: 'public' | 'private' | 'unlisted';
|
||||
gameId: string;
|
||||
};
|
||||
structure?: {
|
||||
mode: 'solo' | 'fixedTeams';
|
||||
maxDrivers?: number;
|
||||
maxTeams?: number;
|
||||
driversPerTeam?: number;
|
||||
};
|
||||
championships?: {
|
||||
enableDriverChampionship: boolean;
|
||||
enableTeamChampionship: boolean;
|
||||
enableNationsChampionship: boolean;
|
||||
enableTrophyChampionship: boolean;
|
||||
};
|
||||
scoring?: {
|
||||
patternId?: string;
|
||||
customScoringEnabled?: boolean;
|
||||
};
|
||||
dropPolicy?: {
|
||||
strategy: 'none' | 'bestNResults' | 'dropWorstN';
|
||||
n?: number;
|
||||
};
|
||||
timings?: {
|
||||
practiceMinutes?: number;
|
||||
qualifyingMinutes?: number;
|
||||
sprintRaceMinutes?: number;
|
||||
mainRaceMinutes?: number;
|
||||
sessionCount?: number;
|
||||
roundsPlanned?: number;
|
||||
raceDayOfWeek?: number;
|
||||
raceTimeUtc?: string;
|
||||
weekdays?: string[];
|
||||
recurrenceStrategy?: string;
|
||||
timezoneId?: string;
|
||||
seasonStartDate?: string;
|
||||
};
|
||||
stewarding?: {
|
||||
decisionMode: 'owner_only' | 'admin_vote' | 'steward_panel';
|
||||
requiredVotes?: number;
|
||||
requireDefense: boolean;
|
||||
defenseTimeLimit: number;
|
||||
voteTimeLimit: number;
|
||||
protestDeadlineHours: number;
|
||||
stewardingClosesHours: number;
|
||||
notifyAccusedOnProtest: boolean;
|
||||
notifyOnVoteRequired: boolean;
|
||||
};
|
||||
}
|
||||
7
apps/website/lib/types/LeagueMembership.ts
Normal file
7
apps/website/lib/types/LeagueMembership.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface LeagueMembership {
|
||||
driverId: string;
|
||||
leagueId: string;
|
||||
role: 'owner' | 'admin' | 'steward' | 'member';
|
||||
joinedAt: string;
|
||||
status: 'active' | 'pending' | 'banned';
|
||||
}
|
||||
5
apps/website/lib/types/LeagueScoringConfigDTO.ts
Normal file
5
apps/website/lib/types/LeagueScoringConfigDTO.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface LeagueScoringConfigDTO {
|
||||
patternId: string;
|
||||
customScoringEnabled: boolean;
|
||||
points: Record<string, number>;
|
||||
}
|
||||
1
apps/website/lib/types/MembershipRole.ts
Normal file
1
apps/website/lib/types/MembershipRole.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type MembershipRole = 'owner' | 'admin' | 'steward' | 'member';
|
||||
1
apps/website/lib/types/Weekday.ts
Normal file
1
apps/website/lib/types/Weekday.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type Weekday = 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' | 'Sat' | 'Sun';
|
||||
21
apps/website/lib/types/WizardErrors.ts
Normal file
21
apps/website/lib/types/WizardErrors.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export interface WizardErrors {
|
||||
basics?: {
|
||||
name?: string;
|
||||
description?: string;
|
||||
visibility?: string;
|
||||
};
|
||||
structure?: {
|
||||
maxDrivers?: string;
|
||||
maxTeams?: string;
|
||||
driversPerTeam?: string;
|
||||
};
|
||||
timings?: {
|
||||
qualifyingMinutes?: string;
|
||||
mainRaceMinutes?: string;
|
||||
roundsPlanned?: string;
|
||||
};
|
||||
scoring?: {
|
||||
patternId?: string;
|
||||
};
|
||||
submit?: string;
|
||||
}
|
||||
@@ -4,6 +4,14 @@
|
||||
* Do not edit manually - regenerate using: npm run api:sync-types
|
||||
*/
|
||||
|
||||
import type { DriverDTO } from './DriverDTO';
|
||||
|
||||
export interface LeagueStandingDTO {
|
||||
driverId: string;
|
||||
driver: DriverDTO;
|
||||
points: number;
|
||||
position: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
races: number;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* Auto-generated DTO from OpenAPI spec
|
||||
* This file is generated by scripts/generate-api-types.ts
|
||||
* Do not edit manually - regenerate using: npm run api:sync-types
|
||||
*/
|
||||
|
||||
export interface PaymentDTO {
|
||||
id: string;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
type LeagueRole = MembershipRole;
|
||||
import type { MembershipRoleDTO } from '@/lib/types/generated/MembershipRoleDTO';
|
||||
type LeagueRole = MembershipRoleDTO['value'];
|
||||
|
||||
export class LeagueRoleUtility {
|
||||
static isLeagueOwnerRole(role: LeagueRole): boolean {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { AvailableLeaguesViewModel, AvailableLeagueViewModel } from './AvailableLeaguesViewModel';
|
||||
|
||||
describe('AvailableLeaguesViewModel', () => {
|
||||
@@ -22,9 +22,9 @@ describe('AvailableLeaguesViewModel', () => {
|
||||
|
||||
expect(vm.leagues).toHaveLength(1);
|
||||
expect(vm.leagues[0]).toBeInstanceOf(AvailableLeagueViewModel);
|
||||
expect(vm.leagues[0].id).toBe(baseLeague.id);
|
||||
expect(vm.leagues[0].name).toBe(baseLeague.name);
|
||||
expect(vm.leagues[0].avgViewsPerRace).toBe(baseLeague.avgViewsPerRace);
|
||||
expect(vm.leagues[0]?.id).toBe(baseLeague.id);
|
||||
expect(vm.leagues[0]?.name).toBe(baseLeague.name);
|
||||
expect(vm.leagues[0]?.avgViewsPerRace).toBe(baseLeague.avgViewsPerRace);
|
||||
});
|
||||
|
||||
it('exposes formatted average views and CPM for main sponsor slot', () => {
|
||||
|
||||
@@ -90,7 +90,7 @@ export interface DriverProfileExtendedProfileViewModel {
|
||||
openToRequests: boolean;
|
||||
}
|
||||
|
||||
export interface DriverProfileViewModel {
|
||||
export interface DriverProfileViewModelData {
|
||||
currentDriver: DriverProfileDriverSummaryViewModel | null;
|
||||
stats: DriverProfileStatsViewModel | null;
|
||||
finishDistribution: DriverProfileFinishDistributionViewModel | null;
|
||||
@@ -106,7 +106,7 @@ export interface DriverProfileViewModel {
|
||||
* Transforms API DTOs into UI-ready data structures.
|
||||
*/
|
||||
export class DriverProfileViewModel {
|
||||
constructor(private readonly dto: DriverProfileViewModel) {}
|
||||
constructor(private readonly dto: DriverProfileViewModelData) {}
|
||||
|
||||
get currentDriver(): DriverProfileDriverSummaryViewModel | null {
|
||||
return this.dto.currentDriver;
|
||||
@@ -135,7 +135,7 @@ export class DriverProfileViewModel {
|
||||
/**
|
||||
* Get the raw DTO for serialization or further processing
|
||||
*/
|
||||
toDTO(): DriverProfileViewModel {
|
||||
toDTO(): DriverProfileViewModelData {
|
||||
return this.dto;
|
||||
}
|
||||
}
|
||||
@@ -43,11 +43,11 @@ export class LeagueDetailPageViewModel {
|
||||
settings: {
|
||||
maxDrivers?: number;
|
||||
};
|
||||
socialLinks?: {
|
||||
socialLinks: {
|
||||
discordUrl?: string;
|
||||
youtubeUrl?: string;
|
||||
websiteUrl?: string;
|
||||
};
|
||||
} | undefined;
|
||||
|
||||
// Owner info
|
||||
owner: GetDriverOutputDTO | null;
|
||||
@@ -103,13 +103,13 @@ export class LeagueDetailPageViewModel {
|
||||
) {
|
||||
this.id = league.id;
|
||||
this.name = league.name;
|
||||
this.description = league.description;
|
||||
this.description = league.description ?? '';
|
||||
this.ownerId = league.ownerId;
|
||||
this.createdAt = league.createdAt;
|
||||
this.createdAt = ''; // Not provided by API
|
||||
this.settings = {
|
||||
maxDrivers: league.maxDrivers,
|
||||
maxDrivers: league.maxMembers,
|
||||
};
|
||||
this.socialLinks = league.socialLinks;
|
||||
this.socialLinks = undefined;
|
||||
|
||||
this.owner = owner;
|
||||
this.scoringConfig = scoringConfig;
|
||||
|
||||
24
apps/website/lib/view-models/LeaguePageDetailViewModel.ts
Normal file
24
apps/website/lib/view-models/LeaguePageDetailViewModel.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* League Page Detail View Model
|
||||
*
|
||||
* View model for league page details.
|
||||
*/
|
||||
export class LeaguePageDetailViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
ownerName: string;
|
||||
isAdmin: boolean;
|
||||
mainSponsor: { name: string; logoUrl: string; websiteUrl: string } | null;
|
||||
|
||||
constructor(data: any) {
|
||||
this.id = data.id;
|
||||
this.name = data.name;
|
||||
this.description = data.description;
|
||||
this.ownerId = data.ownerId;
|
||||
this.ownerName = data.ownerName;
|
||||
this.isAdmin = data.isAdmin;
|
||||
this.mainSponsor = data.mainSponsor;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import type { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';
|
||||
|
||||
/**
|
||||
* View Model for league scoring presets
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { MembershipFeeDto } from '../types/generated';
|
||||
import type { MembershipFeeDTO } from '../types/generated/MembershipFeeDTO';
|
||||
|
||||
export class MembershipFeeViewModel {
|
||||
id: string;
|
||||
@@ -10,7 +10,7 @@ export class MembershipFeeViewModel {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
|
||||
constructor(dto: MembershipFeeDto) {
|
||||
constructor(dto: MembershipFeeDTO) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PaymentDto } from '../types/generated';
|
||||
import type { PaymentDTO } from '../types/generated/PaymentDto';
|
||||
|
||||
export class PaymentViewModel {
|
||||
id: string;
|
||||
|
||||
@@ -13,7 +13,7 @@ export class StandingEntryViewModel {
|
||||
private currentUserId: string;
|
||||
private previousPosition?: number;
|
||||
|
||||
constructor(dto: LeagueStandingDTO & { position: number; points: number; wins?: number; podiums?: number; races?: number }, leaderPoints: number, nextPoints: number, currentUserId: string, previousPosition?: number) {
|
||||
constructor(dto: LeagueStandingDTO, leaderPoints: number, nextPoints: number, currentUserId: string, previousPosition?: number) {
|
||||
this.driverId = dto.driverId;
|
||||
this.position = dto.position;
|
||||
this.points = dto.points;
|
||||
|
||||
Reference in New Issue
Block a user