website cleanup
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
type LeagueRole = MembershipRole;
|
||||
type LeagueRole = 'owner' | 'admin' | 'steward' | 'member';
|
||||
|
||||
export interface LeagueRoleDisplayData {
|
||||
text: string;
|
||||
|
||||
@@ -18,6 +18,7 @@ import { RaceViewModel } from "@/lib/view-models/RaceViewModel";
|
||||
import { SubmitBlocker, ThrottleBlocker } from "@/lib/blockers";
|
||||
import { RaceDTO } from "@/lib/types/generated/RaceDTO";
|
||||
import { LeagueStatsDTO } from "@/lib/types/generated/LeagueStatsDTO";
|
||||
import { LeagueScoringConfigDTO } from "@/lib/types/LeagueScoringConfigDTO";
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { MembershipFeeDto } from '@/lib/types/generated/MembershipFeeDto';
|
||||
import type { MemberPaymentDto } from '@/lib/types/generated/MemberPaymentDto';
|
||||
import { MembershipFeeDTO } from '@/lib/types/generated/MembershipFeeDTO';
|
||||
import type { MemberPaymentDTO } from '@/lib/types/generated/MemberPaymentDTO';
|
||||
import { MembershipFeeViewModel } from '@/lib/view-models/MembershipFeeViewModel';
|
||||
import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
|
||||
// Response shape as returned by the membership-fees payments endpoint; mirrors the API contract until a generated type is introduced
|
||||
export interface GetMembershipFeesOutputDto {
|
||||
fee: MembershipFeeDto | null;
|
||||
payments: MemberPaymentDto[];
|
||||
fee: MembershipFeeDTO | null;
|
||||
payments: MemberPaymentDTO[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ export class MembershipFeeService {
|
||||
/**
|
||||
* Get membership fees by league ID with view model transformation
|
||||
*/
|
||||
async getMembershipFees(leagueId: string): Promise<{ fee: MembershipFeeViewModel | null; payments: MemberPaymentDto[] }> {
|
||||
async getMembershipFees(leagueId: string): Promise<{ fee: MembershipFeeViewModel | null; payments: MemberPaymentDTO[] }> {
|
||||
const dto: GetMembershipFeesOutputDto = await this.apiClient.getMembershipFees({ leagueId });
|
||||
return {
|
||||
fee: dto.fee ? new MembershipFeeViewModel(dto.fee) : null,
|
||||
|
||||
@@ -3,8 +3,8 @@ import { PaymentViewModel } from '@/lib/view-models/PaymentViewModel';
|
||||
import { PrizeViewModel } from '@/lib/view-models/PrizeViewModel';
|
||||
import { WalletViewModel } from '@/lib/view-models/WalletViewModel';
|
||||
import type { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
import type { PaymentDTO } from '../../types/generated/PaymentDto';
|
||||
import type { PrizeDto } from '../../types/generated/PrizeDto';
|
||||
import type { PaymentDTO } from '../../types/generated/PaymentDTO';
|
||||
import type { PrizeDTO } from '../../types/generated/PrizeDTO';
|
||||
|
||||
// Local payment creation request matching the Payments API contract until a shared generated type is introduced
|
||||
type CreatePaymentRequest = {
|
||||
@@ -32,7 +32,7 @@ export class PaymentService {
|
||||
* Get all payments with optional filters
|
||||
*/
|
||||
async getPayments(leagueId?: string, payerId?: string): Promise<PaymentViewModel[]> {
|
||||
const query = leagueId || payerId ? { leagueId, payerId } : undefined;
|
||||
const query = (leagueId || payerId) ? { ...(leagueId && { leagueId }), ...(payerId && { payerId }) } : undefined;
|
||||
const dto = await this.apiClient.getPayments(query);
|
||||
return dto.payments.map((payment: PaymentDTO) => new PaymentViewModel(payment));
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export class PaymentService {
|
||||
* Get membership fees for a league
|
||||
*/
|
||||
async getMembershipFees(leagueId: string, driverId?: string): Promise<MembershipFeeViewModel | null> {
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId, driverId });
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId, ...(driverId && { driverId }) });
|
||||
return dto.fee ? new MembershipFeeViewModel(dto.fee) : null;
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ export class PaymentService {
|
||||
* Get prizes with optional filters
|
||||
*/
|
||||
async getPrizes(leagueId?: string, seasonId?: string): Promise<PrizeViewModel[]> {
|
||||
const query = leagueId || seasonId ? { leagueId, seasonId } : undefined;
|
||||
const query = (leagueId || seasonId) ? { ...(leagueId && { leagueId }), ...(seasonId && { seasonId }) } : undefined;
|
||||
const dto = await this.apiClient.getPrizes(query);
|
||||
return dto.prizes.map((prize: PrizeDto) => new PrizeViewModel(prize));
|
||||
return dto.prizes.map((prize: PrizeDTO) => new PrizeViewModel(prize));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ export class RaceService {
|
||||
driverId: string
|
||||
): Promise<RaceDetailViewModel> {
|
||||
const dto = await this.apiClient.getDetail(raceId, driverId);
|
||||
return new RaceDetailViewModel(dto);
|
||||
return new RaceDetailViewModel(dto, driverId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,7 @@ export const siteConfig = {
|
||||
// Note: All prices displayed are exclusive of VAT
|
||||
euReverseChargeApplies: true,
|
||||
nonEuVatExempt: true,
|
||||
standardRate: 20,
|
||||
notice: 'All prices shown are exclusive of VAT. Applicable taxes will be calculated at checkout.',
|
||||
euBusinessNotice: 'EU businesses with a valid VAT ID may apply reverse charge.',
|
||||
nonEuNotice: 'Non-EU businesses are not charged VAT.',
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
export interface LeagueScoringChampionshipDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
sessionTypes: string[];
|
||||
pointsPreview: Array<{ sessionType: string; position: number; points: number }>;
|
||||
bonusSummary: string[];
|
||||
dropPolicyDescription: string;
|
||||
}
|
||||
|
||||
export interface LeagueScoringConfigDTO {
|
||||
patternId: string;
|
||||
customScoringEnabled: boolean;
|
||||
points: Record<string, number>;
|
||||
leagueId: string;
|
||||
seasonId: string;
|
||||
gameId: string;
|
||||
gameName: string;
|
||||
scoringPresetId?: string;
|
||||
scoringPresetName?: string;
|
||||
dropPolicySummary: string;
|
||||
championships: LeagueScoringChampionshipDTO[];
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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 LeagueScoringChampionshipDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
sessionTypes: string[];
|
||||
pointsPreview: string;
|
||||
}
|
||||
12
apps/website/lib/types/generated/LeagueScoringConfigDTO.ts
Normal file
12
apps/website/lib/types/generated/LeagueScoringConfigDTO.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 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 LeagueScoringConfigDTO {
|
||||
leagueId: string;
|
||||
seasonId: string;
|
||||
gameId: string;
|
||||
gameName: string;
|
||||
}
|
||||
@@ -4,14 +4,6 @@
|
||||
* 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;
|
||||
}
|
||||
|
||||
9
apps/website/lib/types/generated/MembershipRoleDTO.ts
Normal file
9
apps/website/lib/types/generated/MembershipRoleDTO.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 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 MembershipRoleDTO {
|
||||
value: 'owner' | 'admin' | 'steward' | 'member';
|
||||
}
|
||||
9
apps/website/lib/types/generated/PaymentDTO.ts
Normal file
9
apps/website/lib/types/generated/PaymentDTO.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
import { RaceDTO } from '../types/generated/RaceDTO';
|
||||
import { LeagueScoringConfigDTO } from '../types/LeagueScoringConfigDTO';
|
||||
import { RaceViewModel } from './RaceViewModel';
|
||||
import { DriverViewModel } from './DriverViewModel';
|
||||
|
||||
// Sponsor info type
|
||||
export interface SponsorInfo {
|
||||
@@ -20,7 +21,7 @@ export interface SponsorInfo {
|
||||
|
||||
// Driver summary for management section
|
||||
export interface DriverSummary {
|
||||
driver: GetDriverOutputDTO;
|
||||
driver: DriverViewModel;
|
||||
rating: number | null;
|
||||
rank: number | null;
|
||||
}
|
||||
@@ -117,7 +118,7 @@ export class LeagueDetailPageViewModel {
|
||||
this.memberships = memberships.memberships.map(m => ({
|
||||
driverId: m.driverId,
|
||||
role: m.role,
|
||||
status: m.status,
|
||||
status: 'active',
|
||||
joinedAt: m.joinedAt,
|
||||
}));
|
||||
|
||||
@@ -164,8 +165,14 @@ export class LeagueDetailPageViewModel {
|
||||
}
|
||||
|
||||
private buildDriverSummary(driverId: string): DriverSummary | null {
|
||||
const driver = this.drivers.find(d => d.id === driverId);
|
||||
if (!driver) return null;
|
||||
const driverDto = this.drivers.find(d => d.id === driverId);
|
||||
if (!driverDto) return null;
|
||||
|
||||
const driver = new DriverViewModel({
|
||||
id: driverDto.id,
|
||||
name: driverDto.name,
|
||||
iracingId: driverDto.iracingId,
|
||||
});
|
||||
|
||||
// Detailed rating and rank data are not wired from the analytics services yet;
|
||||
// expose the driver identity only so the UI can still render role assignments.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { LeagueConfigFormModel } from '@core/racing/application';
|
||||
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
|
||||
import type { LeagueScoringPresetDTO } from '@/lib/types/generated/LeagueScoringPresetDTO';
|
||||
import { LeagueScoringPresetsViewModel } from './LeagueScoringPresetsViewModel';
|
||||
import { DriverSummaryViewModel } from './DriverSummaryViewModel';
|
||||
|
||||
@@ -23,6 +23,7 @@ export class LeagueSettingsViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
ownerId: string;
|
||||
createdAt: string;
|
||||
};
|
||||
config: LeagueConfigFormModel;
|
||||
presets: LeagueScoringPresetDTO[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PaymentDTO } from '../types/generated/PaymentDto';
|
||||
import type { PaymentDTO } from '../types/generated/PaymentDTO';
|
||||
|
||||
export class PaymentViewModel {
|
||||
id: string;
|
||||
@@ -14,7 +14,7 @@ export class PaymentViewModel {
|
||||
createdAt: Date;
|
||||
completedAt?: Date;
|
||||
|
||||
constructor(dto: PaymentDto) {
|
||||
constructor(dto: PaymentDTO) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
|
||||
|
||||
19
apps/website/lib/view-models/RaceDetailEntryViewModel.ts
Normal file
19
apps/website/lib/view-models/RaceDetailEntryViewModel.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { RaceDetailEntryDTO } from '../types/generated/RaceDetailEntryDTO';
|
||||
|
||||
export class RaceDetailEntryViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
isCurrentUser: boolean;
|
||||
rating: number | null;
|
||||
|
||||
constructor(dto: RaceDetailEntryDTO, currentDriverId: string, rating?: number) {
|
||||
this.id = dto.id;
|
||||
this.name = dto.name;
|
||||
this.country = dto.country;
|
||||
this.avatarUrl = dto.avatarUrl;
|
||||
this.isCurrentUser = dto.id === currentDriverId;
|
||||
this.rating = rating ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
|
||||
|
||||
export class RaceDetailUserResultViewModel {
|
||||
position: number;
|
||||
startPosition: number;
|
||||
incidents: number;
|
||||
fastestLap: number;
|
||||
positionChange: number;
|
||||
ratingChange: number;
|
||||
isPodium: boolean;
|
||||
isClean: boolean;
|
||||
|
||||
constructor(dto: RaceDetailUserResultDTO) {
|
||||
this.position = dto.position;
|
||||
this.startPosition = dto.startPosition;
|
||||
this.incidents = dto.incidents;
|
||||
this.fastestLap = dto.fastestLap;
|
||||
this.positionChange = dto.positionChange;
|
||||
this.ratingChange = dto.ratingChange;
|
||||
this.isPodium = dto.isPodium;
|
||||
this.isClean = dto.isClean;
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,11 @@ describe('RaceDetailViewModel', () => {
|
||||
entryList: entries,
|
||||
registration,
|
||||
userResult,
|
||||
});
|
||||
}, 'current-driver');
|
||||
|
||||
expect(viewModel.race).toBe(race);
|
||||
expect(viewModel.league).toBe(league);
|
||||
expect(viewModel.entryList).toBe(entries);
|
||||
expect(viewModel.entryList).toHaveLength(0);
|
||||
expect(viewModel.registration).toBe(registration);
|
||||
expect(viewModel.userResult).toBe(userResult);
|
||||
});
|
||||
|
||||
@@ -2,14 +2,16 @@ import { RaceDetailLeagueDTO } from '../types/generated/RaceDetailLeagueDTO';
|
||||
import { RaceDetailRaceDTO } from '../types/generated/RaceDetailRaceDTO';
|
||||
import { RaceDetailRegistrationDTO } from '../types/generated/RaceDetailRegistrationDTO';
|
||||
import { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
|
||||
import { RaceDetailEntryDTO } from '../types/RaceDetailEntryDTO';
|
||||
import { RaceDetailEntryDTO } from '../types/generated/RaceDetailEntryDTO';
|
||||
import { RaceDetailEntryViewModel } from './RaceDetailEntryViewModel';
|
||||
import { RaceDetailUserResultViewModel } from './RaceDetailUserResultViewModel';
|
||||
|
||||
export class RaceDetailViewModel {
|
||||
race: RaceDetailRaceDTO | null;
|
||||
league: RaceDetailLeagueDTO | null;
|
||||
entryList: RaceDetailEntryDTO[];
|
||||
entryList: RaceDetailEntryViewModel[];
|
||||
registration: RaceDetailRegistrationDTO;
|
||||
userResult: RaceDetailUserResultDTO | null;
|
||||
userResult: RaceDetailUserResultViewModel | null;
|
||||
error?: string;
|
||||
|
||||
constructor(dto: {
|
||||
@@ -19,18 +21,18 @@ export class RaceDetailViewModel {
|
||||
registration: RaceDetailRegistrationDTO;
|
||||
userResult: RaceDetailUserResultDTO | null;
|
||||
error?: string;
|
||||
}) {
|
||||
}, currentDriverId: string) {
|
||||
this.race = dto.race;
|
||||
this.league = dto.league;
|
||||
this.entryList = dto.entryList;
|
||||
this.entryList = dto.entryList.map(entry => new RaceDetailEntryViewModel(entry, currentDriverId));
|
||||
this.registration = dto.registration;
|
||||
this.userResult = dto.userResult;
|
||||
this.userResult = dto.userResult ? new RaceDetailUserResultViewModel(dto.userResult) : null;
|
||||
this.error = dto.error;
|
||||
}
|
||||
|
||||
/** UI-specific: Whether user is registered */
|
||||
get isRegistered(): boolean {
|
||||
return this.registration.isRegistered;
|
||||
return this.registration.isUserRegistered;
|
||||
}
|
||||
|
||||
/** UI-specific: Whether user can register */
|
||||
|
||||
@@ -61,6 +61,11 @@ export class RaceResultViewModel {
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(3, '0')}`;
|
||||
}
|
||||
|
||||
/** Required by ResultsTable */
|
||||
getPositionChange(): number {
|
||||
return this.positionChange;
|
||||
}
|
||||
|
||||
// Note: The generated DTO doesn't have id or raceId
|
||||
// These will need to be added when the OpenAPI spec is updated
|
||||
id: string = '';
|
||||
|
||||
@@ -3,12 +3,11 @@ import { RaceWithSOFDTO } from '../types/generated/RaceWithSOFDTO';
|
||||
export class RaceWithSOFViewModel {
|
||||
id: string;
|
||||
track: string;
|
||||
strengthOfField: number | null;
|
||||
|
||||
constructor(dto: RaceWithSOFDTO) {
|
||||
this.id = dto.id;
|
||||
this.track = dto.track;
|
||||
this.strengthOfField = (dto as any).strengthOfField ?? null;
|
||||
}
|
||||
|
||||
// The view model currently exposes only basic race identity and track information.
|
||||
// Additional strength-of-field or result details can be added here once the DTO carries them.
|
||||
}
|
||||
@@ -21,6 +21,10 @@ export class SponsorshipDetailViewModel {
|
||||
status: string = 'active';
|
||||
amount: number = 0;
|
||||
currency: string = 'USD';
|
||||
type: string = 'league';
|
||||
entityName: string = '';
|
||||
price: number = 0;
|
||||
impressions: number = 0;
|
||||
|
||||
/** UI-specific: Formatted amount */
|
||||
get formattedAmount(): string {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { TeamMemberDTO } from '@/lib/types/generated/GetTeamMembersOutputDT
|
||||
export class TeamMemberViewModel {
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
role: 'owner' | 'admin' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
avatarUrl: string;
|
||||
@@ -26,7 +26,7 @@ export class TeamMemberViewModel {
|
||||
get roleBadgeVariant(): string {
|
||||
switch (this.role) {
|
||||
case 'owner': return 'primary';
|
||||
case 'manager': return 'secondary';
|
||||
case 'admin': return 'secondary';
|
||||
case 'member': return 'default';
|
||||
default: return 'default';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WalletDto } from '../types/generated/WalletDto';
|
||||
import { WalletDTO } from '../types/generated/WalletDTO';
|
||||
import { FullTransactionDto, WalletTransactionViewModel } from './WalletTransactionViewModel';
|
||||
|
||||
export class WalletViewModel {
|
||||
@@ -11,7 +11,7 @@ export class WalletViewModel {
|
||||
createdAt: string;
|
||||
currency: string;
|
||||
|
||||
constructor(dto: WalletDto & { transactions?: FullTransactionDto[] }) {
|
||||
constructor(dto: WalletDTO & { transactions?: any[] }) {
|
||||
this.id = dto.id;
|
||||
this.leagueId = dto.leagueId;
|
||||
this.balance = dto.balance;
|
||||
|
||||
Reference in New Issue
Block a user