resolve manual DTOs
This commit is contained in:
@@ -173,8 +173,7 @@ interface TeamLeaderboardPreviewProps {
|
||||
function TeamLeaderboardPreview({ teams, onTeamClick }: TeamLeaderboardPreviewProps) {
|
||||
const router = useRouter();
|
||||
const top5 = [...teams]
|
||||
.filter((t) => t.rating !== null)
|
||||
.sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0))
|
||||
.sort((a, b) => b.memberCount - a.memberCount)
|
||||
.slice(0, 5);
|
||||
|
||||
const getMedalColor = (position: number) => {
|
||||
@@ -257,8 +256,8 @@ function TeamLeaderboardPreview({ teams, onTeamClick }: TeamLeaderboardPreviewPr
|
||||
{/* Stats */}
|
||||
<div className="flex items-center gap-4 text-sm">
|
||||
<div className="text-center">
|
||||
<p className="text-purple-400 font-mono font-semibold">{team.rating?.toLocaleString()}</p>
|
||||
<p className="text-[10px] text-gray-500">Rating</p>
|
||||
<p className="text-purple-400 font-mono font-semibold">{team.memberCount}</p>
|
||||
<p className="text-[10px] text-gray-500">Members</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-performance-green font-mono font-semibold">{team.totalWins}</p>
|
||||
|
||||
@@ -7,7 +7,7 @@ import Card from '@/components/ui/Card';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
|
||||
import type { LeagueConfigFormModel } from '@core/racing/application';
|
||||
import { LeagueSettingsViewModel } from '@/lib/view-models/LeagueSettingsViewModel';
|
||||
import { AlertTriangle, Settings, UserCog } from 'lucide-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
|
||||
@@ -4,7 +4,7 @@ import Button from '@/components/ui/Button';
|
||||
import Card from '@/components/ui/Card';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import type { LeagueMembership } from '@/lib/types/LeagueMembership';
|
||||
import type { LeagueMembership } from '@core/racing/domain/entities/LeagueMembership';
|
||||
import type { LeagueSummaryViewModel } from '@/lib/view-models/LeagueSummaryViewModel';
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import { RecordPageViewOutputDTO } from '../../types/generated/RecordPageViewOutputDTO';
|
||||
import { RecordEngagementOutputDTO } from '../../types/generated/RecordEngagementOutputDTO';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type RecordPageViewInputDto = { path: string; userId?: string };
|
||||
type RecordEngagementInputDto = { eventType: string; userId?: string; metadata?: Record<string, unknown> };
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type AnalyticsDashboardDto = {
|
||||
totalUsers: number;
|
||||
activeUsers: number;
|
||||
totalRaces: number;
|
||||
totalLeagues: number;
|
||||
};
|
||||
type AnalyticsMetricsDto = {
|
||||
pageViews: number;
|
||||
uniqueVisitors: number;
|
||||
averageSessionDuration: number;
|
||||
bounceRate: number;
|
||||
};
|
||||
import { GetDashboardDataOutputDTO } from '../../types/generated/GetDashboardDataOutputDTO';
|
||||
import { GetAnalyticsMetricsOutputDTO } from '../../types/generated/GetAnalyticsMetricsOutputDTO';
|
||||
import { RecordPageViewInputDTO } from '../../types/generated/RecordPageViewInputDTO';
|
||||
import { RecordEngagementInputDTO } from '../../types/generated/RecordEngagementInputDTO';
|
||||
|
||||
/**
|
||||
* Analytics API Client
|
||||
@@ -27,22 +13,22 @@ type AnalyticsMetricsDto = {
|
||||
*/
|
||||
export class AnalyticsApiClient extends BaseApiClient {
|
||||
/** Record a page view */
|
||||
recordPageView(input: RecordPageViewInputDto): Promise<RecordPageViewOutputDTO> {
|
||||
recordPageView(input: RecordPageViewInputDTO): Promise<RecordPageViewOutputDTO> {
|
||||
return this.post<RecordPageViewOutputDTO>('/analytics/page-view', input);
|
||||
}
|
||||
|
||||
/** Record an engagement event */
|
||||
recordEngagement(input: RecordEngagementInputDto): Promise<RecordEngagementOutputDTO> {
|
||||
recordEngagement(input: RecordEngagementInputDTO): Promise<RecordEngagementOutputDTO> {
|
||||
return this.post<RecordEngagementOutputDTO>('/analytics/engagement', input);
|
||||
}
|
||||
|
||||
/** Get analytics dashboard data */
|
||||
getDashboardData(): Promise<AnalyticsDashboardDto> {
|
||||
return this.get<AnalyticsDashboardDto>('/analytics/dashboard');
|
||||
getDashboardData(): Promise<GetDashboardDataOutputDTO> {
|
||||
return this.get<GetDashboardDataOutputDTO>('/analytics/dashboard');
|
||||
}
|
||||
|
||||
/** Get analytics metrics */
|
||||
getAnalyticsMetrics(): Promise<AnalyticsMetricsDto> {
|
||||
return this.get<AnalyticsMetricsDto>('/analytics/metrics');
|
||||
getAnalyticsMetrics(): Promise<GetAnalyticsMetricsOutputDTO> {
|
||||
return this.get<GetAnalyticsMetricsOutputDTO>('/analytics/metrics');
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import { AuthSessionDTO } from '../../types/generated/AuthSessionDTO';
|
||||
|
||||
// TODO: Create DTOs for login/signup params in apps/website/lib/types/generated
|
||||
type LoginParamsDto = { email: string; password: string };
|
||||
type SignupParamsDto = { email: string; password: string; displayName: string };
|
||||
import { LoginParams } from '../../types/generated/LoginParams';
|
||||
import { SignupParams } from '../../types/generated/SignupParams';
|
||||
import { LoginWithIracingCallbackParams } from '../../types/generated/LoginWithIracingCallbackParams';
|
||||
import { IracingAuthRedirectResult } from '../../types/generated/IracingAuthRedirectResult';
|
||||
|
||||
/**
|
||||
* Auth API Client
|
||||
@@ -12,12 +12,12 @@ type SignupParamsDto = { email: string; password: string; displayName: string };
|
||||
*/
|
||||
export class AuthApiClient extends BaseApiClient {
|
||||
/** Sign up with email */
|
||||
signup(params: SignupParamsDto): Promise<AuthSessionDTO> {
|
||||
signup(params: SignupParams): Promise<AuthSessionDTO> {
|
||||
return this.post<AuthSessionDTO>('/auth/signup', params);
|
||||
}
|
||||
|
||||
/** Login with email */
|
||||
login(params: LoginParamsDto): Promise<AuthSessionDTO> {
|
||||
login(params: LoginParams): Promise<AuthSessionDTO> {
|
||||
return this.post<AuthSessionDTO>('/auth/login', params);
|
||||
}
|
||||
|
||||
@@ -32,9 +32,19 @@ export class AuthApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Start iRacing auth redirect */
|
||||
getIracingAuthUrl(returnTo?: string): string {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
|
||||
const params = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : '';
|
||||
return `${baseUrl}/auth/iracing/start${params}`;
|
||||
startIracingAuthRedirect(returnTo?: string): Promise<IracingAuthRedirectResult> {
|
||||
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : '';
|
||||
return this.get<IracingAuthRedirectResult>(`/auth/iracing/start${query}`);
|
||||
}
|
||||
|
||||
/** Login with iRacing callback */
|
||||
loginWithIracingCallback(params: LoginWithIracingCallbackParams): Promise<AuthSessionDTO> {
|
||||
const query = new URLSearchParams();
|
||||
query.append('code', params.code);
|
||||
query.append('state', params.state);
|
||||
if (params.returnTo) {
|
||||
query.append('returnTo', params.returnTo);
|
||||
}
|
||||
return this.get<AuthSessionDTO>(`/auth/iracing/callback?${query.toString()}`);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,27 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import {
|
||||
DashboardDriverSummaryDTO,
|
||||
DashboardRaceSummaryDTO,
|
||||
DashboardLeagueStandingSummaryDTO,
|
||||
DashboardFeedItemSummaryDTO,
|
||||
DashboardFriendSummaryDTO,
|
||||
DashboardRecentResultDTO,
|
||||
} from '../../types/generated';
|
||||
|
||||
// DTOs
|
||||
export type DriverDto = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
country: string;
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
rating: number;
|
||||
globalRank: number;
|
||||
consistency: number;
|
||||
};
|
||||
|
||||
export type RaceDto = {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string; // ISO date string
|
||||
isMyLeague: boolean;
|
||||
leagueName?: string;
|
||||
};
|
||||
|
||||
export type LeagueStandingDto = {
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
position: number;
|
||||
points: number;
|
||||
totalDrivers: number;
|
||||
};
|
||||
|
||||
export type FeedItemDto = {
|
||||
id: string;
|
||||
type: string;
|
||||
headline: string;
|
||||
body: string | null;
|
||||
timestamp: string; // ISO date string
|
||||
ctaHref?: string;
|
||||
ctaLabel?: string;
|
||||
};
|
||||
|
||||
export type FriendDto = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
country: string;
|
||||
};
|
||||
|
||||
// Define DashboardOverviewDTO using generated types
|
||||
export type DashboardOverviewDto = {
|
||||
currentDriver: DriverDto;
|
||||
nextRace: RaceDto | null;
|
||||
upcomingRaces: RaceDto[];
|
||||
leagueStandings: LeagueStandingDto[];
|
||||
feedItems: FeedItemDto[];
|
||||
friends: FriendDto[];
|
||||
currentDriver: DashboardDriverSummaryDTO | null;
|
||||
myUpcomingRaces: DashboardRaceSummaryDTO[];
|
||||
otherUpcomingRaces: DashboardRaceSummaryDTO[];
|
||||
upcomingRaces: DashboardRaceSummaryDTO[];
|
||||
activeLeaguesCount: number;
|
||||
nextRace: DashboardRaceSummaryDTO | null;
|
||||
recentResults: DashboardRecentResultDTO[];
|
||||
leagueStandingsSummaries: DashboardLeagueStandingSummaryDTO[];
|
||||
feedSummary: {
|
||||
feedItems: DashboardFeedItemSummaryDTO[];
|
||||
};
|
||||
friends: DashboardFriendSummaryDTO[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
// Import generated types
|
||||
import type { CompleteOnboardingInputDTO, CompleteOnboardingOutputDTO, DriverRegistrationStatusDTO, DriverLeaderboardItemDTO, DriverProfileDTO } from '../../types/generated';
|
||||
|
||||
// TODO: Create proper DriverDTO in generated types
|
||||
type DriverDTO = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
iracingId?: string;
|
||||
rating?: number;
|
||||
};
|
||||
import type { CompleteOnboardingInputDTO, CompleteOnboardingOutputDTO, DriverRegistrationStatusDTO, DriverLeaderboardItemDTO, DriverProfileDTO, GetDriverOutputDTO } from '../../types/generated';
|
||||
|
||||
type DriversLeaderboardDto = {
|
||||
drivers: DriverLeaderboardItemDTO[];
|
||||
@@ -32,8 +23,8 @@ export class DriversApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Get current driver (based on session) */
|
||||
getCurrent(): Promise<DriverDTO | null> {
|
||||
return this.get<DriverDTO | null>('/drivers/current');
|
||||
getCurrent(): Promise<GetDriverOutputDTO | null> {
|
||||
return this.get<GetDriverOutputDTO | null>('/drivers/current');
|
||||
}
|
||||
|
||||
/** Get driver registration status for a specific race */
|
||||
@@ -42,8 +33,8 @@ export class DriversApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Get driver by ID */
|
||||
getDriver(driverId: string): Promise<DriverDTO | null> {
|
||||
return this.get<DriverDTO | null>(`/drivers/${driverId}`);
|
||||
getDriver(driverId: string): Promise<GetDriverOutputDTO | null> {
|
||||
return this.get<GetDriverOutputDTO | null>(`/drivers/${driverId}`);
|
||||
}
|
||||
|
||||
/** Get driver profile with full details */
|
||||
@@ -52,7 +43,7 @@ export class DriversApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Update current driver profile */
|
||||
updateProfile(updates: { bio?: string; country?: string }): Promise<DriverDTO> {
|
||||
return this.put<DriverDTO>('/drivers/profile', updates);
|
||||
updateProfile(updates: { bio?: string; country?: string }): Promise<GetDriverOutputDTO> {
|
||||
return this.put<GetDriverOutputDTO>('/drivers/profile', updates);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { AnalyticsApiClient } from './analytics/AnalyticsApiClient';
|
||||
import { AuthApiClient } from './auth/AuthApiClient';
|
||||
import { PaymentsApiClient } from './payments/PaymentsApiClient';
|
||||
import { DashboardApiClient } from './dashboard/DashboardApiClient';
|
||||
import { PenaltiesApiClient } from './penalties/PenaltiesApiClient';
|
||||
import { ProtestsApiClient } from './protests/ProtestsApiClient';
|
||||
|
||||
/**
|
||||
* Main API Client
|
||||
@@ -25,6 +27,8 @@ export class ApiClient {
|
||||
public readonly auth: AuthApiClient;
|
||||
public readonly payments: PaymentsApiClient;
|
||||
public readonly dashboard: DashboardApiClient;
|
||||
public readonly penalties: PenaltiesApiClient;
|
||||
public readonly protests: ProtestsApiClient;
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.leagues = new LeaguesApiClient(baseUrl);
|
||||
@@ -37,6 +41,8 @@ export class ApiClient {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import type {
|
||||
LeagueMembershipsDto,
|
||||
CreateLeagueInputDto,
|
||||
CreateLeagueOutputDto,
|
||||
SponsorshipDetailDTO,
|
||||
RaceDTO,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
@@ -61,8 +63,8 @@ export class LeaguesApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Get season sponsorships */
|
||||
getSeasonSponsorships(seasonId: string): Promise<{ sponsorships: Array<{ sponsorId: string; tier: string; status: string }> }> {
|
||||
return this.get<{ sponsorships: Array<{ sponsorId: string; tier: string; status: string }> }>(`/seasons/${seasonId}/sponsorships`);
|
||||
getSeasonSponsorships(seasonId: string): Promise<{ sponsorships: SponsorshipDetailDTO[] }> {
|
||||
return this.get<{ sponsorships: SponsorshipDetailDTO[] }>(`/leagues/seasons/${seasonId}/sponsorships`);
|
||||
}
|
||||
|
||||
/** Get league config */
|
||||
@@ -84,7 +86,7 @@ export class LeaguesApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Get races for a league */
|
||||
getRaces(leagueId: string): Promise<{ races: any[] }> {
|
||||
return this.get<{ races: any[] }>(`/leagues/${leagueId}/races`);
|
||||
getRaces(leagueId: string): Promise<{ races: RaceDTO[] }> {
|
||||
return this.get<{ races: RaceDTO[] }>(`/leagues/${leagueId}/races`);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
import type {
|
||||
DeleteMediaOutputDto,
|
||||
GetMediaOutputDto,
|
||||
RequestAvatarGenerationInputDto,
|
||||
RequestAvatarGenerationOutputDto,
|
||||
UpdateAvatarInputDto,
|
||||
UpdateAvatarOutputDto,
|
||||
UploadMediaInputDto,
|
||||
UploadMediaOutputDto,
|
||||
} from '../../dtos';
|
||||
import type { GetAvatarOutputDto } from '../../types/GetAvatarOutputDto';
|
||||
DeleteMediaOutputDTO,
|
||||
GetMediaOutputDTO,
|
||||
RequestAvatarGenerationInputDTO,
|
||||
RequestAvatarGenerationOutputDTO,
|
||||
UpdateAvatarInputDTO,
|
||||
UpdateAvatarOutputDTO,
|
||||
UploadMediaOutputDTO,
|
||||
} from '../generated';
|
||||
import type { GetAvatarOutputDTO } from '../generated';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
|
||||
/**
|
||||
@@ -18,38 +17,38 @@ import { BaseApiClient } from '../base/BaseApiClient';
|
||||
*/
|
||||
export class MediaApiClient extends BaseApiClient {
|
||||
/** Upload media file */
|
||||
uploadMedia(input: UploadMediaInputDto): Promise<UploadMediaOutputDto> {
|
||||
uploadMedia(input: { file: File; type: string; category?: string }): Promise<UploadMediaOutputDTO> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', input.file);
|
||||
formData.append('type', input.type);
|
||||
if (input.category) {
|
||||
formData.append('category', input.category);
|
||||
}
|
||||
return this.post<UploadMediaOutputDto>('/media/upload', formData);
|
||||
return this.post<UploadMediaOutputDTO>('/media/upload', formData);
|
||||
}
|
||||
|
||||
/** Get media by ID */
|
||||
getMedia(mediaId: string): Promise<GetMediaOutputDto> {
|
||||
return this.get<GetMediaOutputDto>(`/media/${mediaId}`);
|
||||
getMedia(mediaId: string): Promise<GetMediaOutputDTO> {
|
||||
return this.get<GetMediaOutputDTO>(`/media/${mediaId}`);
|
||||
}
|
||||
|
||||
/** Delete media by ID */
|
||||
deleteMedia(mediaId: string): Promise<DeleteMediaOutputDto> {
|
||||
return this.delete<DeleteMediaOutputDto>(`/media/${mediaId}`);
|
||||
deleteMedia(mediaId: string): Promise<DeleteMediaOutputDTO> {
|
||||
return this.delete<DeleteMediaOutputDTO>(`/media/${mediaId}`);
|
||||
}
|
||||
|
||||
/** Request avatar generation */
|
||||
requestAvatarGeneration(input: RequestAvatarGenerationInputDto): Promise<RequestAvatarGenerationOutputDto> {
|
||||
return this.post<RequestAvatarGenerationOutputDto>('/media/avatar/generate', input);
|
||||
requestAvatarGeneration(input: RequestAvatarGenerationInputDTO): Promise<RequestAvatarGenerationOutputDTO> {
|
||||
return this.post<RequestAvatarGenerationOutputDTO>('/media/avatar/generate', input);
|
||||
}
|
||||
|
||||
/** Get avatar for driver */
|
||||
getAvatar(driverId: string): Promise<GetAvatarOutputDto> {
|
||||
return this.get<GetAvatarOutputDto>(`/media/avatar/${driverId}`);
|
||||
getAvatar(driverId: string): Promise<GetAvatarOutputDTO> {
|
||||
return this.get<GetAvatarOutputDTO>(`/media/avatar/${driverId}`);
|
||||
}
|
||||
|
||||
/** Update avatar for driver */
|
||||
updateAvatar(input: UpdateAvatarInputDto): Promise<UpdateAvatarOutputDto> {
|
||||
return this.put<UpdateAvatarOutputDto>(`/media/avatar/${input.driverId}`, { avatarUrl: input.avatarUrl });
|
||||
updateAvatar(input: UpdateAvatarInputDTO): Promise<UpdateAvatarOutputDTO> {
|
||||
return this.put<UpdateAvatarOutputDTO>(`/media/avatar/${input.driverId}`, { avatarUrl: input.avatarUrl });
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { PaymentDto, MembershipFeeDto, MemberPaymentDto, PrizeDto, WalletDto, TransactionDto, UpdatePaymentStatusInputDTO } from '../types/generated';
|
||||
|
||||
// TODO: Import these types from apps/website/lib/types/generated when available
|
||||
type GetPaymentsOutputDto = { payments: import('../types/generated').PaymentDto[] };
|
||||
// Define missing types that are not fully generated
|
||||
type GetPaymentsOutputDto = { payments: PaymentDto[] };
|
||||
type CreatePaymentInputDto = {
|
||||
type: 'sponsorship' | 'membership_fee';
|
||||
amount: number;
|
||||
@@ -10,15 +11,15 @@ type CreatePaymentInputDto = {
|
||||
leagueId: string;
|
||||
seasonId?: string;
|
||||
};
|
||||
type CreatePaymentOutputDto = { payment: import('../types/generated').PaymentDto };
|
||||
type CreatePaymentOutputDto = { payment: PaymentDto };
|
||||
type GetMembershipFeesOutputDto = {
|
||||
fee: import('../types/generated').MembershipFeeDto | null;
|
||||
payments: import('../types/generated').MemberPaymentDto[]
|
||||
fee: MembershipFeeDto | null;
|
||||
payments: MemberPaymentDto[]
|
||||
};
|
||||
type GetPrizesOutputDto = { prizes: import('../types/generated').PrizeDto[] };
|
||||
type GetPrizesOutputDto = { prizes: PrizeDto[] };
|
||||
type GetWalletOutputDto = {
|
||||
wallet: import('../types/generated').WalletDto;
|
||||
transactions: import('../types/generated').TransactionDto[]
|
||||
wallet: WalletDto;
|
||||
transactions: TransactionDto[]
|
||||
};
|
||||
type ProcessWalletTransactionInputDto = {
|
||||
leagueId: string;
|
||||
@@ -29,8 +30,8 @@ type ProcessWalletTransactionInputDto = {
|
||||
referenceType?: 'sponsorship' | 'membership_fee' | 'prize';
|
||||
};
|
||||
type ProcessWalletTransactionOutputDto = {
|
||||
wallet: import('../types/generated').WalletDto;
|
||||
transaction: import('../types/generated').TransactionDto
|
||||
wallet: WalletDto;
|
||||
transaction: TransactionDto
|
||||
};
|
||||
type UpdateMemberPaymentInputDto = {
|
||||
feeId: string;
|
||||
@@ -38,8 +39,33 @@ type UpdateMemberPaymentInputDto = {
|
||||
status?: 'pending' | 'paid' | 'overdue';
|
||||
paidAt?: Date | string;
|
||||
};
|
||||
type UpdateMemberPaymentOutputDto = { payment: import('../types/generated').MemberPaymentDto };
|
||||
type GetWalletTransactionsOutputDto = { transactions: import('../types/generated').TransactionDto[] };
|
||||
type UpdateMemberPaymentOutputDto = { payment: MemberPaymentDto };
|
||||
type GetWalletTransactionsOutputDto = { transactions: TransactionDto[] };
|
||||
type UpdatePaymentStatusOutputDto = { payment: PaymentDto };
|
||||
type UpsertMembershipFeeInputDto = {
|
||||
leagueId: string;
|
||||
seasonId?: string;
|
||||
type: 'season' | 'monthly' | 'per_race';
|
||||
amount: number;
|
||||
};
|
||||
type UpsertMembershipFeeOutputDto = { fee: MembershipFeeDto };
|
||||
type CreatePrizeInputDto = {
|
||||
leagueId: string;
|
||||
seasonId: string;
|
||||
position: number;
|
||||
name: string;
|
||||
amount: number;
|
||||
type: 'cash' | 'merchandise' | 'other';
|
||||
description?: string;
|
||||
};
|
||||
type CreatePrizeOutputDto = { prize: PrizeDto };
|
||||
type AwardPrizeInputDto = {
|
||||
prizeId: string;
|
||||
driverId: string;
|
||||
};
|
||||
type AwardPrizeOutputDto = { prize: PrizeDto };
|
||||
type DeletePrizeInputDto = { prizeId: string };
|
||||
type DeletePrizeOutputDto = { success: boolean };
|
||||
|
||||
/**
|
||||
* Payments API Client
|
||||
@@ -48,12 +74,14 @@ type GetWalletTransactionsOutputDto = { transactions: import('../types/generated
|
||||
*/
|
||||
export class PaymentsApiClient extends BaseApiClient {
|
||||
/** Get payments */
|
||||
getPayments(leagueId?: string, driverId?: string): Promise<GetPaymentsOutputDto> {
|
||||
getPayments(query?: { leagueId?: string; payerId?: string; type?: 'sponsorship' | 'membership_fee'; status?: 'pending' | 'completed' | 'failed' | 'refunded' }): Promise<GetPaymentsOutputDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (leagueId) params.append('leagueId', leagueId);
|
||||
if (driverId) params.append('driverId', driverId);
|
||||
const query = params.toString();
|
||||
return this.get<GetPaymentsOutputDto>(`/payments${query ? `?${query}` : ''}`);
|
||||
if (query?.leagueId) params.append('leagueId', query.leagueId);
|
||||
if (query?.payerId) params.append('payerId', query.payerId);
|
||||
if (query?.type) params.append('type', query.type);
|
||||
if (query?.status) params.append('status', query.status);
|
||||
const queryString = params.toString();
|
||||
return this.get<GetPaymentsOutputDto>(`/payments${queryString ? `?${queryString}` : ''}`);
|
||||
}
|
||||
|
||||
/** Create a payment */
|
||||
@@ -62,21 +90,63 @@ export class PaymentsApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Get membership fees */
|
||||
getMembershipFees(leagueId: string): Promise<GetMembershipFeesOutputDto> {
|
||||
return this.get<GetMembershipFeesOutputDto>(`/payments/membership-fees?leagueId=${leagueId}`);
|
||||
getMembershipFees(query: { leagueId: string; driverId?: string }): Promise<GetMembershipFeesOutputDto> {
|
||||
const params = new URLSearchParams();
|
||||
params.append('leagueId', query.leagueId);
|
||||
if (query.driverId) params.append('driverId', query.driverId);
|
||||
const queryString = params.toString();
|
||||
return this.get<GetMembershipFeesOutputDto>(`/payments/membership-fees?${queryString}`);
|
||||
}
|
||||
|
||||
/** Get prizes */
|
||||
getPrizes(leagueId?: string, seasonId?: string): Promise<GetPrizesOutputDto> {
|
||||
getPrizes(query?: { leagueId?: string; seasonId?: string }): Promise<GetPrizesOutputDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (leagueId) params.append('leagueId', leagueId);
|
||||
if (seasonId) params.append('seasonId', seasonId);
|
||||
const query = params.toString();
|
||||
return this.get<GetPrizesOutputDto>(`/payments/prizes${query ? `?${query}` : ''}`);
|
||||
if (query?.leagueId) params.append('leagueId', query.leagueId);
|
||||
if (query?.seasonId) params.append('seasonId', query.seasonId);
|
||||
const queryString = params.toString();
|
||||
return this.get<GetPrizesOutputDto>(`/payments/prizes${queryString ? `?${queryString}` : ''}`);
|
||||
}
|
||||
|
||||
/** Get wallet */
|
||||
getWallet(driverId: string): Promise<GetWalletOutputDto> {
|
||||
return this.get<GetWalletOutputDto>(`/payments/wallets?driverId=${driverId}`);
|
||||
getWallet(query?: { leagueId?: string }): Promise<GetWalletOutputDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (query?.leagueId) params.append('leagueId', query.leagueId);
|
||||
const queryString = params.toString();
|
||||
return this.get<GetWalletOutputDto>(`/payments/wallets${queryString ? `?${queryString}` : ''}`);
|
||||
}
|
||||
|
||||
/** Update payment status */
|
||||
updatePaymentStatus(input: UpdatePaymentStatusInputDTO): Promise<UpdatePaymentStatusOutputDto> {
|
||||
return this.patch<UpdatePaymentStatusOutputDto>('/payments/status', input);
|
||||
}
|
||||
|
||||
/** Upsert membership fee */
|
||||
upsertMembershipFee(input: UpsertMembershipFeeInputDto): Promise<UpsertMembershipFeeOutputDto> {
|
||||
return this.post<UpsertMembershipFeeOutputDto>('/payments/membership-fees', input);
|
||||
}
|
||||
|
||||
/** Update member payment */
|
||||
updateMemberPayment(input: UpdateMemberPaymentInputDto): Promise<UpdateMemberPaymentOutputDto> {
|
||||
return this.patch<UpdateMemberPaymentOutputDto>('/payments/membership-fees/member-payment', input);
|
||||
}
|
||||
|
||||
/** Create prize */
|
||||
createPrize(input: CreatePrizeInputDto): Promise<CreatePrizeOutputDto> {
|
||||
return this.post<CreatePrizeOutputDto>('/payments/prizes', input);
|
||||
}
|
||||
|
||||
/** Award prize */
|
||||
awardPrize(input: AwardPrizeInputDto): Promise<AwardPrizeOutputDto> {
|
||||
return this.patch<AwardPrizeOutputDto>('/payments/prizes/award', input);
|
||||
}
|
||||
|
||||
/** Delete prize */
|
||||
deletePrize(prizeId: string): Promise<DeletePrizeOutputDto> {
|
||||
return this.delete<DeletePrizeOutputDto>(`/payments/prizes?prizeId=${prizeId}`);
|
||||
}
|
||||
|
||||
/** Process wallet transaction */
|
||||
processWalletTransaction(input: ProcessWalletTransactionInputDto): Promise<ProcessWalletTransactionOutputDto> {
|
||||
return this.post<ProcessWalletTransactionOutputDto>('/payments/wallets/transactions', input);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import { RacePenaltiesDTO } from '../../types/generated/RacePenaltiesDTO';
|
||||
import { ApplyPenaltyCommandDTO } from '../../types/generated/ApplyPenaltyCommandDTO';
|
||||
|
||||
/**
|
||||
* Penalties API Client
|
||||
@@ -7,12 +9,12 @@ import { BaseApiClient } from '../base/BaseApiClient';
|
||||
*/
|
||||
export class PenaltiesApiClient extends BaseApiClient {
|
||||
/** Get penalties for a race */
|
||||
getRacePenalties(raceId: string): Promise<{ penalties: any[] }> {
|
||||
return this.get<{ penalties: any[] }>(`/races/${raceId}/penalties`);
|
||||
getRacePenalties(raceId: string): Promise<RacePenaltiesDTO> {
|
||||
return this.get<RacePenaltiesDTO>(`/races/${raceId}/penalties`);
|
||||
}
|
||||
|
||||
/** Apply a penalty */
|
||||
applyPenalty(input: any): Promise<void> {
|
||||
applyPenalty(input: ApplyPenaltyCommandDTO): Promise<void> {
|
||||
return this.post<void>('/races/penalties/apply', input);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@ import type {
|
||||
LeagueAdminProtestsDTO,
|
||||
ApplyPenaltyCommandDTO,
|
||||
RequestProtestDefenseCommandDTO,
|
||||
} from '../../types';
|
||||
ReviewProtestCommandDTO,
|
||||
} from '../../types/generated';
|
||||
import type { RaceProtestsDTO } from '../../types';
|
||||
|
||||
/**
|
||||
* Protests API Client
|
||||
@@ -32,12 +34,12 @@ export class ProtestsApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Review protest */
|
||||
reviewProtest(input: { protestId: string; stewardId: string; decision: string; decisionNotes: string }): Promise<void> {
|
||||
reviewProtest(input: ReviewProtestCommandDTO): Promise<void> {
|
||||
return this.post<void>(`/protests/${input.protestId}/review`, input);
|
||||
}
|
||||
|
||||
/** Get protests for a race */
|
||||
getRaceProtests(raceId: string): Promise<{ protests: any[] }> {
|
||||
return this.get<{ protests: any[] }>(`/races/${raceId}/protests`);
|
||||
getRaceProtests(raceId: string): Promise<RaceProtestsDTO> {
|
||||
return this.get<RaceProtestsDTO>(`/races/${raceId}/protests`);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,34 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RaceStatsDto,
|
||||
RacesPageDataDto,
|
||||
RaceDetailDto,
|
||||
RaceResultsDetailDto,
|
||||
RaceWithSOFDto,
|
||||
RegisterForRaceInputDto,
|
||||
ImportRaceResultsInputDto,
|
||||
ImportRaceResultsSummaryDto,
|
||||
WithdrawFromRaceInputDto,
|
||||
} from '../../dtos';
|
||||
import type { RaceStatsDTO } from '../../types/generated/RaceStatsDTO';
|
||||
import type { RacesPageDataRaceDTO } from '../../types/generated/RacesPageDataRaceDTO';
|
||||
import type { RaceResultsDetailDTO } from '../../types/generated/RaceResultsDetailDTO';
|
||||
import type { RaceWithSOFDTO } from '../../types/generated/RaceWithSOFDTO';
|
||||
import type { RegisterForRaceParamsDTO } from '../../types/generated/RegisterForRaceParamsDTO';
|
||||
import type { ImportRaceResultsDTO } from '../../types/generated/ImportRaceResultsDTO';
|
||||
import type { WithdrawFromRaceParamsDTO } from '../../types/generated/WithdrawFromRaceParamsDTO';
|
||||
import type { RaceDetailRaceDTO } from '../../types/generated/RaceDetailRaceDTO';
|
||||
import type { RaceDetailLeagueDTO } from '../../types/generated/RaceDetailLeagueDTO';
|
||||
import type { RaceDetailEntryDTO } from '../../types/generated/RaceDetailEntryDTO';
|
||||
import type { RaceDetailRegistrationDTO } from '../../types/generated/RaceDetailRegistrationDTO';
|
||||
import type { RaceDetailUserResultDTO } from '../../types/generated/RaceDetailUserResultDTO';
|
||||
|
||||
// Define missing types
|
||||
type RacesPageDataDTO = { races: RacesPageDataRaceDTO[] };
|
||||
type RaceDetailDTO = {
|
||||
race: RaceDetailRaceDTO | null;
|
||||
league: RaceDetailLeagueDTO | null;
|
||||
entryList: RaceDetailEntryDTO[];
|
||||
registration: RaceDetailRegistrationDTO;
|
||||
userResult: RaceDetailUserResultDTO | null;
|
||||
error?: string;
|
||||
};
|
||||
type ImportRaceResultsSummaryDTO = {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Races API Client
|
||||
@@ -18,42 +37,42 @@ import type {
|
||||
*/
|
||||
export class RacesApiClient extends BaseApiClient {
|
||||
/** Get total number of races */
|
||||
getTotal(): Promise<RaceStatsDto> {
|
||||
return this.get<RaceStatsDto>('/races/total-races');
|
||||
getTotal(): Promise<RaceStatsDTO> {
|
||||
return this.get<RaceStatsDTO>('/races/total-races');
|
||||
}
|
||||
|
||||
/** Get races page data */
|
||||
getPageData(): Promise<RacesPageDataDto> {
|
||||
return this.get<RacesPageDataDto>('/races/page-data');
|
||||
getPageData(): Promise<RacesPageDataDTO> {
|
||||
return this.get<RacesPageDataDTO>('/races/page-data');
|
||||
}
|
||||
|
||||
/** Get race detail */
|
||||
getDetail(raceId: string, driverId: string): Promise<RaceDetailDto> {
|
||||
return this.get<RaceDetailDto>(`/races/${raceId}?driverId=${driverId}`);
|
||||
getDetail(raceId: string, driverId: string): Promise<RaceDetailDTO> {
|
||||
return this.get<RaceDetailDTO>(`/races/${raceId}?driverId=${driverId}`);
|
||||
}
|
||||
|
||||
/** Get race results detail */
|
||||
getResultsDetail(raceId: string): Promise<RaceResultsDetailDto> {
|
||||
return this.get<RaceResultsDetailDto>(`/races/${raceId}/results`);
|
||||
getResultsDetail(raceId: string): Promise<RaceResultsDetailDTO> {
|
||||
return this.get<RaceResultsDetailDTO>(`/races/${raceId}/results`);
|
||||
}
|
||||
|
||||
/** Get race with strength of field */
|
||||
getWithSOF(raceId: string): Promise<RaceWithSOFDto> {
|
||||
return this.get<RaceWithSOFDto>(`/races/${raceId}/sof`);
|
||||
getWithSOF(raceId: string): Promise<RaceWithSOFDTO> {
|
||||
return this.get<RaceWithSOFDTO>(`/races/${raceId}/sof`);
|
||||
}
|
||||
|
||||
/** Register for race */
|
||||
register(raceId: string, input: RegisterForRaceInputDto): Promise<void> {
|
||||
register(raceId: string, input: RegisterForRaceParamsDTO): Promise<void> {
|
||||
return this.post<void>(`/races/${raceId}/register`, input);
|
||||
}
|
||||
|
||||
/** Import race results */
|
||||
importResults(raceId: string, input: ImportRaceResultsInputDto): Promise<ImportRaceResultsSummaryDto> {
|
||||
return this.post<ImportRaceResultsSummaryDto>(`/races/${raceId}/import-results`, input);
|
||||
importResults(raceId: string, input: ImportRaceResultsDTO): Promise<ImportRaceResultsSummaryDTO> {
|
||||
return this.post<ImportRaceResultsSummaryDTO>(`/races/${raceId}/import-results`, input);
|
||||
}
|
||||
|
||||
/** Withdraw from race */
|
||||
withdraw(raceId: string, input: WithdrawFromRaceInputDto): Promise<void> {
|
||||
withdraw(raceId: string, input: WithdrawFromRaceParamsDTO): Promise<void> {
|
||||
return this.post<void>(`/races/${raceId}/withdraw`, input);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,15 @@ import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { CreateSponsorInputDTO } from '../../types/generated/CreateSponsorInputDTO';
|
||||
import type { SponsorDashboardDTO } from '../../types/generated/SponsorDashboardDTO';
|
||||
import type { SponsorSponsorshipsDTO } from '../../types/generated/SponsorSponsorshipsDTO';
|
||||
import type { GetPendingSponsorshipRequestsOutputDTO } from '../../types/generated/GetPendingSponsorshipRequestsOutputDTO';
|
||||
import type { AcceptSponsorshipRequestInputDTO } from '../../types/generated/AcceptSponsorshipRequestInputDTO';
|
||||
import type { RejectSponsorshipRequestInputDTO } from '../../types/generated/RejectSponsorshipRequestInputDTO';
|
||||
import type { GetSponsorOutputDTO } from '../../types/generated/GetSponsorOutputDTO';
|
||||
import type { SponsorDTO } from '../../types/generated/SponsorDTO';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
// Types that are not yet generated
|
||||
export type CreateSponsorOutputDto = { id: string; name: string };
|
||||
export type GetEntitySponsorshipPricingResultDto = { pricing: Array<{ entityType: string; price: number }> };
|
||||
export type SponsorDTO = { id: string; name: string; logoUrl?: string; websiteUrl?: string };
|
||||
export type GetSponsorsOutputDto = { sponsors: SponsorDTO[] };
|
||||
|
||||
/**
|
||||
@@ -41,22 +45,22 @@ export class SponsorsApiClient extends BaseApiClient {
|
||||
}
|
||||
|
||||
/** Get sponsor by ID */
|
||||
getSponsor(sponsorId: string): Promise<SponsorDTO | null> {
|
||||
return this.get<SponsorDTO | null>(`/sponsors/${sponsorId}`);
|
||||
getSponsor(sponsorId: string): Promise<GetSponsorOutputDTO | null> {
|
||||
return this.get<GetSponsorOutputDTO | null>(`/sponsors/${sponsorId}`);
|
||||
}
|
||||
|
||||
/** Get pending sponsorship requests for an entity */
|
||||
getPendingSponsorshipRequests(params: { entityType: string; entityId: string }): Promise<{ requests: any[] }> {
|
||||
return this.get<{ requests: any[] }>(`/sponsors/requests?entityType=${params.entityType}&entityId=${params.entityId}`);
|
||||
getPendingSponsorshipRequests(params: { entityType: string; entityId: string }): Promise<GetPendingSponsorshipRequestsOutputDTO> {
|
||||
return this.get<GetPendingSponsorshipRequestsOutputDTO>(`/sponsors/requests?entityType=${params.entityType}&entityId=${params.entityId}`);
|
||||
}
|
||||
|
||||
/** Accept a sponsorship request */
|
||||
acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<void> {
|
||||
return this.post(`/sponsors/requests/${requestId}/accept`, { respondedBy });
|
||||
acceptSponsorshipRequest(requestId: string, input: AcceptSponsorshipRequestInputDTO): Promise<void> {
|
||||
return this.post(`/sponsors/requests/${requestId}/accept`, input);
|
||||
}
|
||||
|
||||
/** Reject a sponsorship request */
|
||||
rejectSponsorshipRequest(requestId: string, respondedBy: string, reason?: string): Promise<void> {
|
||||
return this.post(`/sponsors/requests/${requestId}/reject`, { respondedBy, reason });
|
||||
rejectSponsorshipRequest(requestId: string, input: RejectSponsorshipRequestInputDTO): Promise<void> {
|
||||
return this.post(`/sponsors/requests/${requestId}/reject`, input);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
import { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
|
||||
import type {
|
||||
AllTeamsDto,
|
||||
CreateTeamInputDto,
|
||||
CreateTeamOutputDto,
|
||||
DriverTeamDto,
|
||||
TeamDetailsDto,
|
||||
TeamJoinRequestsDto,
|
||||
TeamMembersDto,
|
||||
UpdateTeamInputDto,
|
||||
UpdateTeamOutputDto,
|
||||
} from '../../dtos';
|
||||
import type { GetAllTeamsOutputDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
|
||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
||||
import type { GetTeamMembersOutputDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
|
||||
import type { GetTeamJoinRequestsOutputDTO } from '@/lib/types/generated/GetTeamJoinRequestsOutputDTO';
|
||||
import type { CreateTeamInputDTO } from '@/lib/types/generated/CreateTeamInputDTO';
|
||||
import type { CreateTeamOutputDTO } from '@/lib/types/generated/CreateTeamOutputDTO';
|
||||
import type { UpdateTeamInputDTO } from '@/lib/types/generated/UpdateTeamInputDTO';
|
||||
import type { UpdateTeamOutputDTO } from '@/lib/types/generated/UpdateTeamOutputDTO';
|
||||
import type { GetDriverTeamOutputDTO } from '@/lib/types/generated/GetDriverTeamOutputDTO';
|
||||
import type { GetTeamMembershipOutputDTO } from '@/lib/types/generated/GetTeamMembershipOutputDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
|
||||
/**
|
||||
@@ -19,42 +18,42 @@ import { BaseApiClient } from '../base/BaseApiClient';
|
||||
*/
|
||||
export class TeamsApiClient extends BaseApiClient {
|
||||
/** Get all teams */
|
||||
getAll(): Promise<AllTeamsDto> {
|
||||
return this.get<AllTeamsDto>('/teams/all');
|
||||
getAll(): Promise<GetAllTeamsOutputDTO> {
|
||||
return this.get<GetAllTeamsOutputDTO>('/teams/all');
|
||||
}
|
||||
|
||||
/** Get team details */
|
||||
getDetails(teamId: string): Promise<TeamDetailsDto | null> {
|
||||
return this.get<TeamDetailsDto | null>(`/teams/${teamId}`);
|
||||
getDetails(teamId: string): Promise<GetTeamDetailsOutputDTO | null> {
|
||||
return this.get<GetTeamDetailsOutputDTO | null>(`/teams/${teamId}`);
|
||||
}
|
||||
|
||||
/** Get team members */
|
||||
getMembers(teamId: string): Promise<TeamMembersDto> {
|
||||
return this.get<TeamMembersDto>(`/teams/${teamId}/members`);
|
||||
getMembers(teamId: string): Promise<GetTeamMembersOutputDTO> {
|
||||
return this.get<GetTeamMembersOutputDTO>(`/teams/${teamId}/members`);
|
||||
}
|
||||
|
||||
/** Get team join requests */
|
||||
getJoinRequests(teamId: string): Promise<TeamJoinRequestsDto> {
|
||||
return this.get<TeamJoinRequestsDto>(`/teams/${teamId}/join-requests`);
|
||||
getJoinRequests(teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
|
||||
return this.get<GetTeamJoinRequestsOutputDTO>(`/teams/${teamId}/join-requests`);
|
||||
}
|
||||
|
||||
/** Create a new team */
|
||||
create(input: CreateTeamInputDto): Promise<CreateTeamOutputDto> {
|
||||
return this.post<CreateTeamOutputDto>('/teams', input);
|
||||
create(input: CreateTeamInputDTO): Promise<CreateTeamOutputDTO> {
|
||||
return this.post<CreateTeamOutputDTO>('/teams', input);
|
||||
}
|
||||
|
||||
/** Update team */
|
||||
update(teamId: string, input: UpdateTeamInputDto): Promise<UpdateTeamOutputDto> {
|
||||
return this.patch<UpdateTeamOutputDto>(`/teams/${teamId}`, input);
|
||||
update(teamId: string, input: UpdateTeamInputDTO): Promise<UpdateTeamOutputDTO> {
|
||||
return this.patch<UpdateTeamOutputDTO>(`/teams/${teamId}`, input);
|
||||
}
|
||||
|
||||
/** Get driver's team */
|
||||
getDriverTeam(driverId: string): Promise<DriverTeamDto | null> {
|
||||
return this.get<DriverTeamDto | null>(`/teams/driver/${driverId}`);
|
||||
getDriverTeam(driverId: string): Promise<GetDriverTeamOutputDTO | null> {
|
||||
return this.get<GetDriverTeamOutputDTO | null>(`/teams/driver/${driverId}`);
|
||||
}
|
||||
|
||||
/** Get membership for a driver in a team */
|
||||
getMembership(teamId: string, driverId: string): Promise<LeagueMemberDTO | null> {
|
||||
return this.get<LeagueMemberDTO | null>(`/teams/${teamId}/members/${driverId}`);
|
||||
getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
|
||||
return this.get<GetTeamMembershipOutputDTO | null>(`/teams/${teamId}/members/${driverId}`);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { api } from '../apiClient';
|
||||
import type {
|
||||
AuthenticatedUserDTO,
|
||||
AuthSessionDTO,
|
||||
SignupParams,
|
||||
LoginParams,
|
||||
IracingAuthRedirectResult,
|
||||
LoginWithIracingCallbackParams,
|
||||
} from '../../../apps/api/src/modules/auth/dto/AuthDto'; // Using generated API DTOs
|
||||
|
||||
export class AuthApiClient {
|
||||
async getCurrentSession(): Promise<AuthSessionDTO | null> {
|
||||
try {
|
||||
return await api.get<AuthSessionDTO>('/auth/session');
|
||||
} catch (error) {
|
||||
// Handle error, e.g., if session is not found or API is down
|
||||
console.error('Error fetching current session:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async signupWithEmail(params: SignupParams): Promise<AuthSessionDTO> {
|
||||
return api.post<AuthSessionDTO>('/auth/signup', params);
|
||||
}
|
||||
|
||||
async loginWithEmail(params: LoginParams): Promise<AuthSessionDTO> {
|
||||
return api.post<AuthSessionDTO>('/auth/login', params);
|
||||
}
|
||||
|
||||
async startIracingAuthRedirect(returnTo?: string): Promise<IracingAuthRedirectResult> {
|
||||
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : '';
|
||||
return api.get<IracingAuthRedirectResult>(`/auth/iracing/start${query}`);
|
||||
}
|
||||
|
||||
async loginWithIracingCallback(params: LoginWithIracingCallbackParams): Promise<AuthSessionDTO> {
|
||||
const query = new URLSearchParams();
|
||||
query.append('code', params.code);
|
||||
query.append('state', params.state);
|
||||
if (params.returnTo) {
|
||||
query.append('returnTo', params.returnTo);
|
||||
}
|
||||
return await api.get<AuthSessionDTO>(`/auth/iracing/callback?${query.toString()}`);
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
return api.post<void>('/auth/logout', {});
|
||||
}
|
||||
}
|
||||
|
||||
export const authApiClient = new AuthApiClient();
|
||||
@@ -1,9 +1,31 @@
|
||||
import { WizardStep } from '@/lib/types/WizardStep';
|
||||
import { WizardErrors } from '@/lib/types/WizardErrors';
|
||||
import { CreateLeagueInputDTO } from '@/lib/types/CreateLeagueInputDTO';
|
||||
import { LeagueWizardValidationMessages } from '@/lib/display-objects/LeagueWizardValidationMessages';
|
||||
import { ScoringPresetApplier } from '@/lib/utilities/ScoringPresetApplier';
|
||||
|
||||
export type WizardStep = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
type LeagueWizardFormData = {
|
||||
leagueId: string | undefined;
|
||||
basics: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LeagueRole } from '@/lib/types/LeagueRole';
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
type LeagueRole = MembershipRole;
|
||||
|
||||
export interface LeagueRoleDisplayData {
|
||||
text: string;
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
import { AnalyticsApiClient } from '../../api/analytics/AnalyticsApiClient';
|
||||
import { RecordPageViewOutputViewModel } from '../../view-models/RecordPageViewOutputViewModel';
|
||||
import { RecordEngagementOutputViewModel } from '../../view-models/RecordEngagementOutputViewModel';
|
||||
|
||||
// TODO: Create proper DTOs in generated types
|
||||
interface RecordPageViewInputDTO {
|
||||
path: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
interface RecordEngagementInputDTO {
|
||||
eventType: string;
|
||||
userId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
import { RecordPageViewInputDTO } from '../../types/generated/RecordPageViewInputDTO';
|
||||
import { RecordEngagementInputDTO } from '../../types/generated/RecordEngagementInputDTO';
|
||||
|
||||
/**
|
||||
* Analytics Service
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import { LeagueMembership } from '@/lib/types/LeagueMembership';
|
||||
import { MembershipRole } from '@/lib/types/MembershipRole';
|
||||
import { MembershipStatus } from '@/lib/types/MembershipStatus';
|
||||
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';
|
||||
|
||||
export class LeagueMembershipService {
|
||||
// In-memory cache for memberships (populated via API calls)
|
||||
|
||||
@@ -15,7 +15,6 @@ import { LeagueDetailViewModel } from "@/lib/view-models/LeagueDetailViewModel";
|
||||
import { LeagueDetailPageViewModel, SponsorInfo } from "@/lib/view-models/LeagueDetailPageViewModel";
|
||||
import { RaceViewModel } from "@/lib/view-models/RaceViewModel";
|
||||
import { SubmitBlocker, ThrottleBlocker } from "@/lib/blockers";
|
||||
import { DriverDTO } from "@/lib/types/DriverDTO";
|
||||
import { RaceDTO } from "@/lib/types/generated/RaceDTO";
|
||||
import { LeagueScoringConfigDTO } from "@/lib/types/LeagueScoringConfigDTO";
|
||||
import { LeagueStatsDTO } from "@/lib/types/generated/LeagueStatsDTO";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient";
|
||||
import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
|
||||
import type { LeagueConfigFormModel } from "@/lib/types/LeagueConfigFormModel";
|
||||
import type { LeagueScoringPresetDTO } from "@/lib/types/LeagueScoringPresetDTO";
|
||||
import type { DriverDTO } from "@/lib/types/DriverDTO";
|
||||
import type { LeagueConfigFormModel } from "@core/racing/application";
|
||||
import type { LeagueScoringPresetDTO } from "@core/racing/application/ports/LeagueScoringPresetProvider";
|
||||
import type { GetDriverOutputDTO } from "@/lib/types/generated/GetDriverOutputDTO";
|
||||
import { LeagueSettingsViewModel } from "@/lib/view-models/LeagueSettingsViewModel";
|
||||
import { DriverSummaryViewModel } from "@/lib/view-models/DriverSummaryViewModel";
|
||||
|
||||
@@ -50,7 +50,7 @@ export class LeagueSettingsService {
|
||||
// TODO: get rating and rank from API
|
||||
owner = new DriverSummaryViewModel({
|
||||
driver: ownerDriver,
|
||||
rating: ownerDriver.rating ?? null,
|
||||
rating: null, // TODO: get from API
|
||||
rank: null, // TODO: get from API
|
||||
});
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export class LeagueSettingsService {
|
||||
if (driver) {
|
||||
members.push(new DriverSummaryViewModel({
|
||||
driver,
|
||||
rating: driver.rating ?? null,
|
||||
rating: null, // TODO: get from API
|
||||
rank: null, // TODO: get from API
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import { LeagueWizardCommandModel } from '@/lib/command-models/leagues/LeagueWizardCommandModel';
|
||||
import { CreateLeagueResult } from '@/lib/types/CreateLeagueResult';
|
||||
import { CreateLeagueOutputDTO } from '@/lib/types/generated/CreateLeagueOutputDTO';
|
||||
|
||||
export class LeagueWizardService {
|
||||
static async createLeague(
|
||||
form: LeagueWizardCommandModel,
|
||||
ownerId: string,
|
||||
): Promise<CreateLeagueResult> {
|
||||
): Promise<CreateLeagueOutputDTO> {
|
||||
const command = form.toCreateLeagueCommand(ownerId);
|
||||
const result = await apiClient.leagues.create(command);
|
||||
|
||||
return {
|
||||
leagueId: result.leagueId,
|
||||
success: result.success,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Static method for backward compatibility
|
||||
static async createLeagueFromConfig(
|
||||
form: LeagueWizardCommandModel,
|
||||
ownerId: string,
|
||||
): Promise<CreateLeagueResult> {
|
||||
): Promise<CreateLeagueOutputDTO> {
|
||||
return this.createLeague(form, ownerId);
|
||||
}
|
||||
}
|
||||
@@ -83,9 +83,9 @@ describe('MediaService', () => {
|
||||
const expectedOutput = {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image' as const,
|
||||
category: 'avatar' as const,
|
||||
uploadedAt: new Date('2023-01-15'),
|
||||
type: 'image',
|
||||
category: 'avatar',
|
||||
uploadedAt: '2023-01-15T00:00:00.000Z',
|
||||
size: 2048000,
|
||||
};
|
||||
mockApiClient.getMedia.mockResolvedValue(expectedOutput);
|
||||
@@ -98,7 +98,7 @@ describe('MediaService', () => {
|
||||
expect(result.url).toBe('https://example.com/image.jpg');
|
||||
expect(result.type).toBe('image');
|
||||
expect(result.category).toBe('avatar');
|
||||
expect(result.uploadedAt).toEqual(new Date('2023-01-15'));
|
||||
expect(result.uploadedAt).toEqual(new Date('2023-01-15T00:00:00.000Z'));
|
||||
expect(result.size).toBe(2048000);
|
||||
expect(result.formattedSize).toBe('2000.00 KB');
|
||||
});
|
||||
@@ -109,8 +109,8 @@ describe('MediaService', () => {
|
||||
const expectedOutput = {
|
||||
id: 'media-456',
|
||||
url: 'https://example.com/video.mp4',
|
||||
type: 'video' as const,
|
||||
uploadedAt: new Date('2023-02-20'),
|
||||
type: 'video',
|
||||
uploadedAt: '2023-02-20T00:00:00.000Z',
|
||||
};
|
||||
mockApiClient.getMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { MembershipFeeService, GetMembershipFeesOutputDto } from './MembershipFeeService';
|
||||
import { MembershipFeeService } from './MembershipFeeService';
|
||||
import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
import { MembershipFeeViewModel } from '../../view-models';
|
||||
import type { MembershipFeeDto } from '../../types/generated';
|
||||
@@ -17,36 +17,30 @@ describe('MembershipFeeService', () => {
|
||||
});
|
||||
|
||||
describe('getMembershipFees', () => {
|
||||
it('should call apiClient.getMembershipFees with correct leagueId and return mapped view models', async () => {
|
||||
it('should call apiClient.getMembershipFees with correct leagueId and return fee and payments', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const mockFees: MembershipFeeDto[] = [
|
||||
{ id: 'fee-1', leagueId: 'league-123' },
|
||||
{ id: 'fee-2', leagueId: 'league-123' },
|
||||
];
|
||||
const mockOutput: GetMembershipFeesOutputDto = { fees: mockFees };
|
||||
const mockFee: MembershipFeeDto = { id: 'fee-1', leagueId: 'league-123', seasonId: undefined, type: 'season', amount: 100, enabled: true, createdAt: new Date(), updatedAt: new Date() };
|
||||
const mockPayments: any[] = [];
|
||||
const mockOutput = { fee: mockFee, payments: mockPayments };
|
||||
mockApiClient.getMembershipFees.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getMembershipFees(leagueId);
|
||||
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith(leagueId);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toBeInstanceOf(MembershipFeeViewModel);
|
||||
expect(result[0].id).toEqual('fee-1');
|
||||
expect(result[0].leagueId).toEqual('league-123');
|
||||
expect(result[1]).toBeInstanceOf(MembershipFeeViewModel);
|
||||
expect(result[1].id).toEqual('fee-2');
|
||||
expect(result[1].leagueId).toEqual('league-123');
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith({ leagueId });
|
||||
expect(result.fee).toBeInstanceOf(MembershipFeeViewModel);
|
||||
expect(result.fee!.id).toEqual('fee-1');
|
||||
expect(result.payments).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty array when no fees are returned', async () => {
|
||||
it('should return null fee when no fee is returned', async () => {
|
||||
const leagueId = 'league-456';
|
||||
const mockOutput: GetMembershipFeesOutputDto = { fees: [] };
|
||||
const mockOutput = { fee: null, payments: [] };
|
||||
mockApiClient.getMembershipFees.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getMembershipFees(leagueId);
|
||||
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith(leagueId);
|
||||
expect(result).toEqual([]);
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith({ leagueId });
|
||||
expect(result.fee).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,8 @@ import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
|
||||
// TODO: This DTO should be generated from OpenAPI spec when the endpoint is added
|
||||
export interface GetMembershipFeesOutputDto {
|
||||
fees: MembershipFeeDto[];
|
||||
fee: MembershipFeeDto | null;
|
||||
payments: import('./MemberPaymentDto').MemberPaymentDto[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,8 +22,11 @@ export class MembershipFeeService {
|
||||
/**
|
||||
* Get membership fees by league ID with view model transformation
|
||||
*/
|
||||
async getMembershipFees(leagueId: string): Promise<MembershipFeeViewModel[]> {
|
||||
const dto = await this.apiClient.getMembershipFees(leagueId);
|
||||
return dto.fees.map((fee: MembershipFeeDto) => new MembershipFeeViewModel(fee));
|
||||
async getMembershipFees(leagueId: string): Promise<{ fee: MembershipFeeViewModel | null; payments: any[] }> {
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId });
|
||||
return {
|
||||
fee: dto.fee ? new MembershipFeeViewModel(dto.fee) : null,
|
||||
payments: dto.payments // TODO: map to view models if needed
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ describe('PaymentService', () => {
|
||||
|
||||
const result = await service.getPayments();
|
||||
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined, undefined);
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(PaymentViewModel);
|
||||
expect(result[0].id).toBe('payment-1');
|
||||
@@ -54,7 +54,7 @@ describe('PaymentService', () => {
|
||||
|
||||
await service.getPayments('league-1', 'user-1');
|
||||
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith('league-1', 'user-1');
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith({ leagueId: 'league-1', payerId: 'user-1' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,7 +189,7 @@ describe('PaymentService', () => {
|
||||
|
||||
const result = await service.getPrizes();
|
||||
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith(undefined, undefined);
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith(undefined);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(PrizeViewModel);
|
||||
expect(result[0].id).toBe('prize-1');
|
||||
@@ -201,7 +201,7 @@ describe('PaymentService', () => {
|
||||
|
||||
await service.getPrizes('league-1', 'season-1');
|
||||
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith('league-1', 'season-1');
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith({ leagueId: 'league-1', seasonId: 'season-1' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,9 +232,9 @@ describe('PaymentService', () => {
|
||||
|
||||
mockApiClient.getWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getWallet('user-1');
|
||||
const result = await service.getWallet('league-1');
|
||||
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith('user-1');
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(result).toBeInstanceOf(WalletViewModel);
|
||||
expect(result.id).toBe('wallet-1');
|
||||
expect(result.balance).toBe(1000);
|
||||
|
||||
@@ -31,8 +31,9 @@ export class PaymentService {
|
||||
/**
|
||||
* Get all payments with optional filters
|
||||
*/
|
||||
async getPayments(leagueId?: string, driverId?: string): Promise<PaymentViewModel[]> {
|
||||
const dto = await this.apiClient.getPayments(leagueId, driverId);
|
||||
async getPayments(leagueId?: string, payerId?: string): Promise<PaymentViewModel[]> {
|
||||
const query = leagueId || payerId ? { leagueId, payerId } : undefined;
|
||||
const dto = await this.apiClient.getPayments(query);
|
||||
return dto.payments.map((payment: PaymentDTO) => new PaymentViewModel(payment));
|
||||
}
|
||||
|
||||
@@ -60,8 +61,8 @@ export class PaymentService {
|
||||
/**
|
||||
* Get membership fees for a league
|
||||
*/
|
||||
async getMembershipFees(leagueId: string): Promise<MembershipFeeViewModel | null> {
|
||||
const dto = await this.apiClient.getMembershipFees(leagueId);
|
||||
async getMembershipFees(leagueId: string, driverId?: string): Promise<MembershipFeeViewModel | null> {
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId, driverId });
|
||||
return dto.fee ? new MembershipFeeViewModel(dto.fee) : null;
|
||||
}
|
||||
|
||||
@@ -69,22 +70,23 @@ export class PaymentService {
|
||||
* Get prizes with optional filters
|
||||
*/
|
||||
async getPrizes(leagueId?: string, seasonId?: string): Promise<PrizeViewModel[]> {
|
||||
const dto = await this.apiClient.getPrizes(leagueId, seasonId);
|
||||
const query = leagueId || seasonId ? { leagueId, seasonId } : undefined;
|
||||
const dto = await this.apiClient.getPrizes(query);
|
||||
return dto.prizes.map((prize: PrizeDto) => new PrizeViewModel(prize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get wallet for a driver
|
||||
* Get wallet for a league
|
||||
*/
|
||||
async getWallet(driverId: string): Promise<WalletViewModel> {
|
||||
const dto = await this.apiClient.getWallet(driverId);
|
||||
async getWallet(leagueId: string): Promise<WalletViewModel> {
|
||||
const dto = await this.apiClient.getWallet({ leagueId });
|
||||
return new WalletViewModel({ ...dto.wallet, transactions: dto.transactions });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payment history for a user (driver)
|
||||
*/
|
||||
async getPaymentHistory(driverId: string): Promise<PaymentViewModel[]> {
|
||||
return await this.getPayments(undefined, driverId);
|
||||
async getPaymentHistory(payerId: string): Promise<PaymentViewModel[]> {
|
||||
return await this.getPayments(undefined, payerId);
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,9 @@ describe('WalletService', () => {
|
||||
|
||||
mockApiClient.getWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getWallet('user-1');
|
||||
const result = await service.getWallet('league-1');
|
||||
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith('user-1');
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(result).toBeInstanceOf(WalletViewModel);
|
||||
expect(result.id).toBe('wallet-1');
|
||||
expect(result.balance).toBe(1000);
|
||||
@@ -69,7 +69,7 @@ describe('WalletService', () => {
|
||||
|
||||
mockApiClient.getWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getWallet('user-1');
|
||||
const result = await service.getWallet('league-1');
|
||||
|
||||
expect(result.transactions).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -102,8 +102,10 @@ describe('RaceResultsService', () => {
|
||||
const input = { raceId, results: [{ position: 1 }] };
|
||||
|
||||
const mockDto = {
|
||||
success: true,
|
||||
raceId,
|
||||
importedCount: 10,
|
||||
driversProcessed: 10,
|
||||
resultsRecorded: 10,
|
||||
errors: ['Error 1'],
|
||||
};
|
||||
|
||||
@@ -114,7 +116,8 @@ describe('RaceResultsService', () => {
|
||||
expect(mockApiClient.importResults).toHaveBeenCalledWith(raceId, input);
|
||||
expect(result).toBeInstanceOf(ImportRaceResultsSummaryViewModel);
|
||||
expect(result.raceId).toBe(raceId);
|
||||
expect(result.importedCount).toBe(10);
|
||||
expect(result.driversProcessed).toBe(10);
|
||||
expect(result.resultsRecorded).toBe(10);
|
||||
expect(result.errors).toEqual(['Error 1']);
|
||||
});
|
||||
|
||||
@@ -123,8 +126,10 @@ describe('RaceResultsService', () => {
|
||||
const input = { raceId, results: [] };
|
||||
|
||||
const mockDto = {
|
||||
success: true,
|
||||
raceId,
|
||||
importedCount: 5,
|
||||
driversProcessed: 5,
|
||||
resultsRecorded: 5,
|
||||
errors: [],
|
||||
};
|
||||
|
||||
@@ -132,7 +137,8 @@ describe('RaceResultsService', () => {
|
||||
|
||||
const result = await service.importResults(raceId, input);
|
||||
|
||||
expect(result.importedCount).toBe(5);
|
||||
expect(result.driversProcessed).toBe(5);
|
||||
expect(result.resultsRecorded).toBe(5);
|
||||
expect(result.errors).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,15 +2,16 @@ import { RacesApiClient } from '../../api/races/RacesApiClient';
|
||||
import { RaceResultsDetailViewModel } from '../../view-models/RaceResultsDetailViewModel';
|
||||
import { RaceWithSOFViewModel } from '../../view-models/RaceWithSOFViewModel';
|
||||
import { ImportRaceResultsSummaryViewModel } from '../../view-models/ImportRaceResultsSummaryViewModel';
|
||||
import type { ImportRaceResultsDTO } from '../../types/generated/ImportRaceResultsDTO';
|
||||
|
||||
// TODO: Move this type to apps/website/lib/types/generated when available
|
||||
type ImportRaceResultsInputDto = { raceId: string; results: Array<unknown> };
|
||||
|
||||
// TODO: Move this type to apps/website/lib/types/generated when available
|
||||
// Define types
|
||||
type ImportRaceResultsInputDto = ImportRaceResultsDTO;
|
||||
type ImportRaceResultsSummaryDto = {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
importedCount: number;
|
||||
errors: string[];
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -41,10 +42,10 @@ export class RaceResultsService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Import race results and get summary
|
||||
*/
|
||||
async importResults(raceId: string, input: ImportRaceResultsInputDto): Promise<ImportRaceResultsSummaryViewModel> {
|
||||
const dto = await this.apiClient.importResults(raceId, input) as ImportRaceResultsSummaryDto;
|
||||
return new ImportRaceResultsSummaryViewModel(dto);
|
||||
}
|
||||
* Import race results and get summary
|
||||
*/
|
||||
async importResults(raceId: string, input: ImportRaceResultsInputDto): Promise<ImportRaceResultsSummaryViewModel> {
|
||||
const dto = await this.apiClient.importResults(raceId, input);
|
||||
return new ImportRaceResultsSummaryViewModel(dto);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@ describe('SponsorService', () => {
|
||||
getSponsorships: vi.fn(),
|
||||
create: vi.fn(),
|
||||
getPricing: vi.fn(),
|
||||
getSponsor: vi.fn(),
|
||||
getPendingSponsorshipRequests: vi.fn(),
|
||||
acceptSponsorshipRequest: vi.fn(),
|
||||
rejectSponsorshipRequest: vi.fn(),
|
||||
} as Mocked<SponsorsApiClient>;
|
||||
|
||||
service = new SponsorService(mockApiClient);
|
||||
|
||||
@@ -29,11 +29,16 @@ describe('TeamService', () => {
|
||||
{
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
tag: 'TT',
|
||||
description: 'A test team',
|
||||
memberCount: 5,
|
||||
rating: 1500,
|
||||
leagues: ['league-1'],
|
||||
specialization: 'endurance' as const,
|
||||
region: 'EU',
|
||||
languages: ['en'],
|
||||
},
|
||||
],
|
||||
totalCount: 1,
|
||||
};
|
||||
|
||||
mockApiClient.getAll.mockResolvedValue(mockDto);
|
||||
@@ -45,6 +50,7 @@ describe('TeamService', () => {
|
||||
expect(result[0]).toBeInstanceOf(TeamSummaryViewModel);
|
||||
expect(result[0].id).toBe('team-1');
|
||||
expect(result[0].name).toBe('Test Team');
|
||||
expect(result[0].tag).toBe('TT');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getAll fails', async () => {
|
||||
@@ -58,13 +64,24 @@ describe('TeamService', () => {
|
||||
describe('getTeamDetails', () => {
|
||||
it('should call apiClient.getDetails and return TeamDetailsViewModel when data exists', async () => {
|
||||
const mockDto = {
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
description: 'A test team',
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
memberCount: 5,
|
||||
ownerId: 'owner-1',
|
||||
members: [],
|
||||
team: {
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
tag: 'TT',
|
||||
description: 'A test team',
|
||||
ownerId: 'owner-1',
|
||||
leagues: ['league-1'],
|
||||
createdAt: '2023-01-01T00:00:00Z',
|
||||
specialization: 'endurance',
|
||||
region: 'EU',
|
||||
languages: ['en'],
|
||||
},
|
||||
membership: {
|
||||
role: 'member',
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
isActive: true,
|
||||
},
|
||||
canManage: false,
|
||||
};
|
||||
|
||||
mockApiClient.getDetails.mockResolvedValue(mockDto);
|
||||
@@ -75,6 +92,7 @@ describe('TeamService', () => {
|
||||
expect(result).toBeInstanceOf(TeamDetailsViewModel);
|
||||
expect(result?.id).toBe('team-1');
|
||||
expect(result?.name).toBe('Test Team');
|
||||
expect(result?.tag).toBe('TT');
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getDetails returns null', async () => {
|
||||
@@ -100,11 +118,17 @@ describe('TeamService', () => {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: { id: 'driver-1', name: 'Driver One', avatarUrl: 'avatar.png', iracingId: '123', rating: 1400 },
|
||||
role: 'member',
|
||||
driverName: 'Driver One',
|
||||
role: 'member' as const,
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
isActive: true,
|
||||
avatarUrl: 'avatar.png',
|
||||
},
|
||||
],
|
||||
totalCount: 1,
|
||||
ownerCount: 0,
|
||||
managerCount: 0,
|
||||
memberCount: 1,
|
||||
};
|
||||
|
||||
mockApiClient.getMembers.mockResolvedValue(mockDto);
|
||||
@@ -176,14 +200,35 @@ describe('TeamService', () => {
|
||||
|
||||
describe('getDriverTeam', () => {
|
||||
it('should call apiClient.getDriverTeam and return the result', async () => {
|
||||
const mockOutput = { teamId: 'team-1', teamName: 'Test Team', role: 'member' };
|
||||
const mockOutput = {
|
||||
team: {
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
tag: 'TT',
|
||||
description: 'A test team',
|
||||
ownerId: 'owner-1',
|
||||
leagues: [],
|
||||
specialization: 'endurance' as const,
|
||||
region: 'EU',
|
||||
languages: ['en'],
|
||||
},
|
||||
membership: {
|
||||
role: 'member',
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
isActive: true,
|
||||
},
|
||||
isOwner: false,
|
||||
canManage: false,
|
||||
};
|
||||
|
||||
mockApiClient.getDriverTeam.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getDriverTeam('driver-1');
|
||||
|
||||
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith('driver-1');
|
||||
expect(result).toEqual(mockOutput);
|
||||
expect(result?.teamId).toBe('team-1');
|
||||
expect(result?.teamName).toBe('Test Team');
|
||||
expect(result?.role).toBe('member');
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getDriverTeam returns null', async () => {
|
||||
|
||||
@@ -6,16 +6,15 @@ import { UpdateTeamViewModel } from '@/lib/view-models/UpdateTeamViewModel';
|
||||
import { DriverTeamViewModel } from '@/lib/view-models/DriverTeamViewModel';
|
||||
import { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
|
||||
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type DriverDTO = { id: string; name: string; avatarUrl?: string; iracingId?: string; rating?: number };
|
||||
type CreateTeamInputDto = { name: string; tag: string; description?: string };
|
||||
type CreateTeamOutputDto = { id: string; success: boolean };
|
||||
type UpdateTeamInputDto = { name?: string; tag?: string; description?: string };
|
||||
type UpdateTeamOutputDto = { success: boolean };
|
||||
type DriverTeamDto = { teamId: string; teamName: string; role: string };
|
||||
type TeamSummaryDTO = { id: string; name: string; logoUrl?: string; memberCount: number; rating: number };
|
||||
type TeamMemberDTO = { driverId: string; driver?: DriverDTO; role: string; joinedAt: string };
|
||||
import type { GetAllTeamsOutputDTO, TeamListItemDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
|
||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
||||
import type { GetTeamMembersOutputDTO, TeamMemberDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
|
||||
import type { CreateTeamInputDTO } from '@/lib/types/generated/CreateTeamInputDTO';
|
||||
import type { CreateTeamOutputDTO } from '@/lib/types/generated/CreateTeamOutputDTO';
|
||||
import type { UpdateTeamInputDTO } from '@/lib/types/generated/UpdateTeamInputDTO';
|
||||
import type { UpdateTeamOutputDTO } from '@/lib/types/generated/UpdateTeamOutputDTO';
|
||||
import type { GetDriverTeamOutputDTO } from '@/lib/types/generated/GetDriverTeamOutputDTO';
|
||||
import type { GetTeamMembershipOutputDTO } from '@/lib/types/generated/GetTeamMembershipOutputDTO';
|
||||
|
||||
/**
|
||||
* Team Service
|
||||
@@ -32,15 +31,15 @@ export class TeamService {
|
||||
* Get all teams with view model transformation
|
||||
*/
|
||||
async getAllTeams(): Promise<TeamSummaryViewModel[]> {
|
||||
const dto = await this.apiClient.getAll();
|
||||
return dto.teams.map((team: TeamSummaryDTO) => new TeamSummaryViewModel(team));
|
||||
const dto: GetAllTeamsOutputDTO = await this.apiClient.getAll();
|
||||
return dto.teams.map((team: TeamListItemDTO) => new TeamSummaryViewModel(team));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get team details with view model transformation
|
||||
*/
|
||||
async getTeamDetails(teamId: string, currentUserId: string): Promise<TeamDetailsViewModel | null> {
|
||||
const dto = await this.apiClient.getDetails(teamId);
|
||||
const dto: GetTeamDetailsOutputDTO | null = await this.apiClient.getDetails(teamId);
|
||||
if (!dto) {
|
||||
return null;
|
||||
}
|
||||
@@ -51,40 +50,40 @@ export class TeamService {
|
||||
* Get team members with view model transformation
|
||||
*/
|
||||
async getTeamMembers(teamId: string, currentUserId: string, teamOwnerId: string): Promise<TeamMemberViewModel[]> {
|
||||
const dto = await this.apiClient.getMembers(teamId);
|
||||
const dto: GetTeamMembersOutputDTO = await this.apiClient.getMembers(teamId);
|
||||
return dto.members.map((member: TeamMemberDTO) => new TeamMemberViewModel(member, currentUserId, teamOwnerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new team with view model transformation
|
||||
*/
|
||||
async createTeam(input: CreateTeamInputDto): Promise<CreateTeamViewModel> {
|
||||
const dto = await this.apiClient.create(input);
|
||||
async createTeam(input: CreateTeamInputDTO): Promise<CreateTeamViewModel> {
|
||||
const dto: CreateTeamOutputDTO = await this.apiClient.create(input);
|
||||
return new CreateTeamViewModel(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update team with view model transformation
|
||||
*/
|
||||
async updateTeam(teamId: string, input: UpdateTeamInputDto): Promise<UpdateTeamViewModel> {
|
||||
const dto = await this.apiClient.update(teamId, input);
|
||||
async updateTeam(teamId: string, input: UpdateTeamInputDTO): Promise<UpdateTeamViewModel> {
|
||||
const dto: UpdateTeamOutputDTO = await this.apiClient.update(teamId, input);
|
||||
return new UpdateTeamViewModel(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get driver's team with view model transformation
|
||||
*/
|
||||
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
|
||||
const dto = await this.apiClient.getDriverTeam(driverId);
|
||||
return dto ? new DriverTeamViewModel(dto) : null;
|
||||
}
|
||||
* Get driver's team with view model transformation
|
||||
*/
|
||||
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
|
||||
const dto: GetDriverTeamOutputDTO | null = await this.apiClient.getDriverTeam(driverId);
|
||||
return dto ? new DriverTeamViewModel(dto) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get team membership for a driver
|
||||
*/
|
||||
async getMembership(teamId: string, driverId: string): Promise<LeagueMemberDTO | null> {
|
||||
return this.apiClient.getMembership(teamId, driverId);
|
||||
}
|
||||
/**
|
||||
* Get team membership for a driver
|
||||
*/
|
||||
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
|
||||
return this.apiClient.getMembership(teamId, driverId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a driver from the team
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export interface CreateLeagueInputDTO {
|
||||
name: string;
|
||||
description: string;
|
||||
isPublic: boolean;
|
||||
maxMembers: number;
|
||||
ownerId: string;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface CreateLeagueResult {
|
||||
leagueId: string;
|
||||
seasonId?: string;
|
||||
success: boolean;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
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;
|
||||
};
|
||||
stewarding: {
|
||||
decisionMode: 'owner_only' | 'admin_vote' | 'steward_panel';
|
||||
requiredVotes?: number;
|
||||
requireDefense: boolean;
|
||||
defenseTimeLimit: number;
|
||||
voteTimeLimit: number;
|
||||
protestDeadlineHours: number;
|
||||
stewardingClosesHours: number;
|
||||
notifyAccusedOnProtest: boolean;
|
||||
notifyOnVoteRequired: boolean;
|
||||
};
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { MembershipRole } from './MembershipRole';
|
||||
import { MembershipStatus } from './MembershipStatus';
|
||||
|
||||
/**
|
||||
* Lightweight league membership model for UI.
|
||||
*/
|
||||
export interface LeagueMembership {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
role: MembershipRole;
|
||||
status: MembershipStatus;
|
||||
joinedAt: string;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { MembershipRole } from './MembershipRole';
|
||||
|
||||
export type LeagueRole = MembershipRole;
|
||||
@@ -1,15 +0,0 @@
|
||||
export type LeagueScoringPresetPrimaryChampionshipType =
|
||||
| 'driver'
|
||||
| 'team'
|
||||
| 'nations'
|
||||
| 'trophy';
|
||||
|
||||
export interface LeagueScoringPresetDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
primaryChampionshipType: LeagueScoringPresetPrimaryChampionshipType;
|
||||
sessionSummary: string;
|
||||
bonusSummary: string;
|
||||
dropPolicySummary: string;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export type MembershipRole = 'owner' | 'admin' | 'steward' | 'member';
|
||||
@@ -1 +0,0 @@
|
||||
export type MembershipStatus = 'active' | 'inactive' | 'pending';
|
||||
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export type WizardStep = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
@@ -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 AcceptSponsorshipRequestInputDTO {
|
||||
respondedBy: string;
|
||||
}
|
||||
@@ -8,9 +8,5 @@ export interface ApplyPenaltyCommandDTO {
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
stewardId: string;
|
||||
type: string;
|
||||
value?: number;
|
||||
reason: string;
|
||||
protestId?: string;
|
||||
notes?: string;
|
||||
enum: string;
|
||||
}
|
||||
|
||||
10
apps/website/lib/types/generated/CreateTeamInputDTO.ts
Normal file
10
apps/website/lib/types/generated/CreateTeamInputDTO.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 CreateTeamInputDTO {
|
||||
name: string;
|
||||
tag: string;
|
||||
}
|
||||
10
apps/website/lib/types/generated/CreateTeamOutputDTO.ts
Normal file
10
apps/website/lib/types/generated/CreateTeamOutputDTO.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 CreateTeamOutputDTO {
|
||||
id: string;
|
||||
success: boolean;
|
||||
}
|
||||
9
apps/website/lib/types/generated/DeleteMediaOutputDTO.ts
Normal file
9
apps/website/lib/types/generated/DeleteMediaOutputDTO.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 DeleteMediaOutputDTO {
|
||||
success: boolean;
|
||||
}
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
export interface DriverDTO {
|
||||
id: string;
|
||||
iracingId: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
iracingId?: string;
|
||||
rating?: number;
|
||||
}
|
||||
country: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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 DriverProfileAchievementDTO {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -4,11 +4,9 @@
|
||||
* Do not edit manually - regenerate using: npm run api:sync-types
|
||||
*/
|
||||
|
||||
export interface RaceDetailEntryDTO {
|
||||
export interface DriverProfileDriverSummaryDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
rating: number | null;
|
||||
isCurrentUser: boolean;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 DriverProfileFinishDistributionDTO {
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
topTen: number;
|
||||
dnfs: number;
|
||||
other: number;
|
||||
}
|
||||
@@ -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 DriverProfileSocialFriendSummaryDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
@@ -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 DriverProfileSocialSummaryDTO {
|
||||
friendsCount: number;
|
||||
}
|
||||
12
apps/website/lib/types/generated/DriverProfileStatsDTO.ts
Normal file
12
apps/website/lib/types/generated/DriverProfileStatsDTO.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 DriverProfileStatsDTO {
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
podiums: number;
|
||||
dnfs: number;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 DriverProfileTeamMembershipDTO {
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
}
|
||||
22
apps/website/lib/types/generated/GetAllTeamsOutputDTO.ts
Normal file
22
apps/website/lib/types/generated/GetAllTeamsOutputDTO.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 TeamListItemDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
memberCount: number;
|
||||
leagues: string[];
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
export interface GetAllTeamsOutputDTO {
|
||||
teams: TeamListItemDTO[];
|
||||
totalCount: number;
|
||||
}
|
||||
@@ -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 GetAnalyticsMetricsOutputDTO {
|
||||
pageViews: number;
|
||||
uniqueVisitors: number;
|
||||
averageSessionDuration: number;
|
||||
bounceRate: number;
|
||||
}
|
||||
@@ -4,6 +4,6 @@
|
||||
* Do not edit manually - regenerate using: npm run api:sync-types
|
||||
*/
|
||||
|
||||
export interface GetAvatarOutputDto {
|
||||
export interface GetAvatarOutputDTO {
|
||||
avatarUrl: string;
|
||||
}
|
||||
}
|
||||
@@ -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 GetDashboardDataOutputDTO {
|
||||
totalUsers: number;
|
||||
activeUsers: number;
|
||||
totalRaces: number;
|
||||
totalLeagues: number;
|
||||
}
|
||||
14
apps/website/lib/types/generated/GetDriverOutputDTO.ts
Normal file
14
apps/website/lib/types/generated/GetDriverOutputDTO.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 GetDriverOutputDTO {
|
||||
id: string;
|
||||
iracingId: string;
|
||||
name: string;
|
||||
country: string;
|
||||
bio?: string;
|
||||
joinedAt: string;
|
||||
}
|
||||
31
apps/website/lib/types/generated/GetDriverTeamOutputDTO.ts
Normal file
31
apps/website/lib/types/generated/GetDriverTeamOutputDTO.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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 TeamDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt?: string;
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
export interface MembershipDTO {
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface GetDriverTeamOutputDTO {
|
||||
team: TeamDTO;
|
||||
membership: MembershipDTO;
|
||||
isOwner: boolean;
|
||||
canManage: boolean;
|
||||
}
|
||||
15
apps/website/lib/types/generated/GetMediaOutputDTO.ts
Normal file
15
apps/website/lib/types/generated/GetMediaOutputDTO.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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 GetMediaOutputDTO {
|
||||
id: string;
|
||||
url: string;
|
||||
type: string;
|
||||
category?: string;
|
||||
/** Format: date-time */
|
||||
uploadedAt: string;
|
||||
size?: number;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 GetPendingSponsorshipRequestsOutputDTO {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
}
|
||||
8
apps/website/lib/types/generated/GetSponsorOutputDTO.ts
Normal file
8
apps/website/lib/types/generated/GetSponsorOutputDTO.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export interface GetSponsorOutputDTO {
|
||||
sponsor: {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl?: string;
|
||||
websiteUrl?: string;
|
||||
};
|
||||
}
|
||||
30
apps/website/lib/types/generated/GetTeamDetailsOutputDTO.ts
Normal file
30
apps/website/lib/types/generated/GetTeamDetailsOutputDTO.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 TeamDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt?: string;
|
||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
export interface MembershipDTO {
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface GetTeamDetailsOutputDTO {
|
||||
team: TeamDTO;
|
||||
membership: MembershipDTO | null;
|
||||
canManage: boolean;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 TeamJoinRequestDTO {
|
||||
requestId: string;
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
teamId: string;
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
requestedAt: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface GetTeamJoinRequestsOutputDTO {
|
||||
requests: TeamJoinRequestDTO[];
|
||||
pendingCount: number;
|
||||
totalCount: number;
|
||||
}
|
||||
22
apps/website/lib/types/generated/GetTeamMembersOutputDTO.ts
Normal file
22
apps/website/lib/types/generated/GetTeamMembersOutputDTO.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 TeamMemberDTO {
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
avatarUrl: string;
|
||||
}
|
||||
|
||||
export interface GetTeamMembersOutputDTO {
|
||||
members: TeamMemberDTO[];
|
||||
totalCount: number;
|
||||
ownerCount: number;
|
||||
managerCount: number;
|
||||
memberCount: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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 GetTeamMembershipOutputDTO {
|
||||
role: string;
|
||||
joinedAt: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 IracingAuthRedirectResult {
|
||||
redirectUrl: string;
|
||||
state: string;
|
||||
}
|
||||
@@ -4,6 +4,20 @@
|
||||
* Do not edit manually - regenerate using: npm run api:sync-types
|
||||
*/
|
||||
|
||||
import type { LeagueConfigFormModelBasicsDTO } from './LeagueConfigFormModelBasicsDTO';
|
||||
import type { LeagueConfigFormModelStructureDTO } from './LeagueConfigFormModelStructureDTO';
|
||||
import type { LeagueConfigFormModelScoringDTO } from './LeagueConfigFormModelScoringDTO';
|
||||
import type { LeagueConfigFormModelDropPolicyDTO } from './LeagueConfigFormModelDropPolicyDTO';
|
||||
import type { LeagueConfigFormModelTimingsDTO } from './LeagueConfigFormModelTimingsDTO';
|
||||
import type { LeagueConfigFormModelStewardingDTO } from './LeagueConfigFormModelStewardingDTO';
|
||||
|
||||
export interface LeagueConfigFormModelDTO {
|
||||
leagueId: string;
|
||||
basics: LeagueConfigFormModelBasicsDTO;
|
||||
structure: LeagueConfigFormModelStructureDTO;
|
||||
championships: any[];
|
||||
scoring: LeagueConfigFormModelScoringDTO;
|
||||
dropPolicy: LeagueConfigFormModelDropPolicyDTO;
|
||||
timings: LeagueConfigFormModelTimingsDTO;
|
||||
stewarding: LeagueConfigFormModelStewardingDTO;
|
||||
}
|
||||
|
||||
10
apps/website/lib/types/generated/LoginParams.ts
Normal file
10
apps/website/lib/types/generated/LoginParams.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 LoginParams {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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 LoginWithIracingCallbackParams {
|
||||
code: string;
|
||||
state: string;
|
||||
returnTo?: string;
|
||||
}
|
||||
12
apps/website/lib/types/generated/RacePenaltiesDTO.ts
Normal file
12
apps/website/lib/types/generated/RacePenaltiesDTO.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
|
||||
*/
|
||||
|
||||
import { RacePenaltyDTO } from './RacePenaltyDTO';
|
||||
|
||||
export interface RacePenaltiesDTO {
|
||||
penalties: RacePenaltyDTO[];
|
||||
driverMap: Record<string, string>;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface RecordEngagementInputDTO {
|
||||
eventType: string;
|
||||
userId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface RecordPageViewInputDTO {
|
||||
path: string;
|
||||
userId?: string;
|
||||
}
|
||||
@@ -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 RejectSponsorshipRequestInputDTO {
|
||||
respondedBy: string;
|
||||
}
|
||||
11
apps/website/lib/types/generated/ReviewProtestCommandDTO.ts
Normal file
11
apps/website/lib/types/generated/ReviewProtestCommandDTO.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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 ReviewProtestCommandDTO {
|
||||
protestId: string;
|
||||
stewardId: string;
|
||||
enum: string;
|
||||
}
|
||||
14
apps/website/lib/types/generated/SignupParams.ts
Normal file
14
apps/website/lib/types/generated/SignupParams.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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 SignupParams {
|
||||
email: string;
|
||||
password: string;
|
||||
displayName: string;
|
||||
iracingCustomerId?: string;
|
||||
primaryDriverId?: string;
|
||||
avatarUrl?: string;
|
||||
}
|
||||
10
apps/website/lib/types/generated/SponsorDTO.ts
Normal file
10
apps/website/lib/types/generated/SponsorDTO.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 SponsorDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
11
apps/website/lib/types/generated/SponsorshipRequestDTO.ts
Normal file
11
apps/website/lib/types/generated/SponsorshipRequestDTO.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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 SponsorshipRequestDTO {
|
||||
id: string;
|
||||
sponsorId: string;
|
||||
sponsorName: string;
|
||||
}
|
||||
10
apps/website/lib/types/generated/UpdateAvatarInputDTO.ts
Normal file
10
apps/website/lib/types/generated/UpdateAvatarInputDTO.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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 UpdateAvatarInputDTO {
|
||||
driverId: string;
|
||||
avatarUrl: string;
|
||||
}
|
||||
@@ -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 UpdateAvatarOutputDTO {
|
||||
success: boolean;
|
||||
}
|
||||
11
apps/website/lib/types/generated/UpdateTeamInputDTO.ts
Normal file
11
apps/website/lib/types/generated/UpdateTeamInputDTO.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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 UpdateTeamInputDTO {
|
||||
name?: string;
|
||||
tag?: string;
|
||||
description?: string;
|
||||
}
|
||||
9
apps/website/lib/types/generated/UpdateTeamOutputDTO.ts
Normal file
9
apps/website/lib/types/generated/UpdateTeamOutputDTO.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 UpdateTeamOutputDTO {
|
||||
success: boolean;
|
||||
}
|
||||
9
apps/website/lib/types/generated/UploadMediaOutputDTO.ts
Normal file
9
apps/website/lib/types/generated/UploadMediaOutputDTO.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 UploadMediaOutputDTO {
|
||||
success: boolean;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { LeagueMembershipService } from '@/lib/services/leagues/LeagueMembershipService';
|
||||
import { LeagueRoleUtility } from '@/lib/utilities/LeagueRoleUtility';
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
type LeagueRole = MembershipRole;
|
||||
|
||||
export class LeagueMembershipUtility {
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LeagueRole } from '@/lib/types/LeagueRole';
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
type LeagueRole = MembershipRole;
|
||||
|
||||
export class LeagueRoleUtility {
|
||||
static isLeagueOwnerRole(role: LeagueRole): boolean {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { DriverDTO } from '../types/DriverDTO';
|
||||
import type { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
|
||||
/**
|
||||
* View Model for driver summary with rating and rank
|
||||
* Transform from DTO to ViewModel with UI fields
|
||||
*/
|
||||
export class DriverSummaryViewModel {
|
||||
driver: DriverDTO;
|
||||
driver: GetDriverOutputDTO;
|
||||
rating: number | null;
|
||||
rank: number | null;
|
||||
|
||||
constructor(dto: {
|
||||
driver: DriverDTO;
|
||||
driver: GetDriverOutputDTO;
|
||||
rating?: number | null;
|
||||
rank?: number | null;
|
||||
}) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { GetDriverTeamOutputDTO } from '@/lib/types/generated/GetDriverTeamOutputDTO';
|
||||
|
||||
/**
|
||||
* View Model for Driver's Team
|
||||
*
|
||||
@@ -6,21 +8,22 @@
|
||||
export class DriverTeamViewModel {
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
tag: string;
|
||||
role: string;
|
||||
isOwner: boolean;
|
||||
canManage: boolean;
|
||||
|
||||
constructor(dto: { teamId: string; teamName: string; role: string }) {
|
||||
this.teamId = dto.teamId;
|
||||
this.teamName = dto.teamName;
|
||||
this.role = dto.role;
|
||||
constructor(dto: GetDriverTeamOutputDTO) {
|
||||
this.teamId = dto.team.id;
|
||||
this.teamName = dto.team.name;
|
||||
this.tag = dto.team.tag;
|
||||
this.role = dto.membership.role;
|
||||
this.isOwner = dto.isOwner;
|
||||
this.canManage = dto.canManage;
|
||||
}
|
||||
|
||||
/** UI-specific: Display role */
|
||||
get displayRole(): string {
|
||||
return this.role.charAt(0).toUpperCase() + this.role.slice(1);
|
||||
}
|
||||
|
||||
/** UI-specific: Is owner */
|
||||
get isOwner(): boolean {
|
||||
return this.role === 'owner';
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,23 @@
|
||||
// TODO: Create ImportRaceResultsSummaryDTO in apps/website/lib/types/generated when available
|
||||
interface ImportRaceResultsSummaryDTO {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
importedCount: number;
|
||||
errors: string[];
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
}
|
||||
|
||||
export class ImportRaceResultsSummaryViewModel {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
importedCount: number;
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors: string[];
|
||||
|
||||
constructor(dto: ImportRaceResultsSummaryDTO) {
|
||||
this.success = dto.success;
|
||||
this.raceId = dto.raceId;
|
||||
this.importedCount = dto.importedCount;
|
||||
this.errors = dto.errors;
|
||||
this.driversProcessed = dto.driversProcessed;
|
||||
this.resultsRecorded = dto.resultsRecorded;
|
||||
this.errors = dto.errors || [];
|
||||
}
|
||||
|
||||
// TODO: Add additional UI-specific fields when DTO is available
|
||||
}
|
||||
@@ -3,7 +3,7 @@ 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 { DriverDTO } from '../types/DriverDTO';
|
||||
import { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
import { RaceDTO } from '../types/generated/RaceDTO';
|
||||
import { LeagueScoringConfigDTO } from '../types/LeagueScoringConfigDTO';
|
||||
import { RaceViewModel } from './RaceViewModel';
|
||||
@@ -20,7 +20,7 @@ export interface SponsorInfo {
|
||||
|
||||
// Driver summary for management section
|
||||
export interface DriverSummary {
|
||||
driver: DriverDTO;
|
||||
driver: GetDriverOutputDTO;
|
||||
rating: number | null;
|
||||
rank: number | null;
|
||||
}
|
||||
@@ -50,13 +50,13 @@ export class LeagueDetailPageViewModel {
|
||||
};
|
||||
|
||||
// Owner info
|
||||
owner: DriverDTO | null;
|
||||
owner: GetDriverOutputDTO | null;
|
||||
|
||||
// Scoring configuration
|
||||
scoringConfig: LeagueScoringConfigDTO | null;
|
||||
|
||||
// Drivers and memberships
|
||||
drivers: DriverDTO[];
|
||||
drivers: GetDriverOutputDTO[];
|
||||
memberships: LeagueMembershipWithRole[];
|
||||
|
||||
// Races
|
||||
@@ -93,9 +93,9 @@ export class LeagueDetailPageViewModel {
|
||||
|
||||
constructor(
|
||||
league: LeagueWithCapacityDTO,
|
||||
owner: DriverDTO | null,
|
||||
owner: GetDriverOutputDTO | null,
|
||||
scoringConfig: LeagueScoringConfigDTO | null,
|
||||
drivers: DriverDTO[],
|
||||
drivers: GetDriverOutputDTO[],
|
||||
memberships: LeagueMembershipsDTO,
|
||||
allRaces: RaceViewModel[],
|
||||
leagueStats: LeagueStatsDTO,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LeagueScoringPresetDTO } from '../types/LeagueScoringPresetDTO';
|
||||
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
|
||||
|
||||
/**
|
||||
* View Model for league scoring presets
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { LeagueConfigFormModel } from '../types/LeagueConfigFormModel';
|
||||
import type { LeagueScoringPresetDTO } from '../types/LeagueScoringPresetDTO';
|
||||
import type { LeagueConfigFormModel } from '@core/racing/application';
|
||||
import type { LeagueScoringPresetDTO } from '@core/racing/application/ports/LeagueScoringPresetProvider';
|
||||
import { LeagueScoringPresetsViewModel } from './LeagueScoringPresetsViewModel';
|
||||
import { DriverSummaryViewModel } from './DriverSummaryViewModel';
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { LeagueStandingDTO } from '../types/generated/LeagueStandingDTO';
|
||||
import { StandingEntryViewModel } from './StandingEntryViewModel';
|
||||
import { DriverDTO } from '../types/DriverDTO';
|
||||
import { GetDriverOutputDTO } from '../types/generated/GetDriverOutputDTO';
|
||||
import { LeagueMembership } from '../types/LeagueMembership';
|
||||
|
||||
export class LeagueStandingsViewModel {
|
||||
standings: StandingEntryViewModel[];
|
||||
drivers: DriverDTO[];
|
||||
drivers: GetDriverOutputDTO[];
|
||||
memberships: LeagueMembership[];
|
||||
|
||||
constructor(dto: { standings: LeagueStandingDTO[]; drivers: DriverDTO[]; memberships: LeagueMembership[] }, currentUserId: string, previousStandings?: LeagueStandingDTO[]) {
|
||||
constructor(dto: { standings: LeagueStandingDTO[]; drivers: GetDriverOutputDTO[]; memberships: LeagueMembership[] }, currentUserId: string, previousStandings?: LeagueStandingDTO[]) {
|
||||
const leaderPoints = dto.standings[0]?.points || 0;
|
||||
this.standings = dto.standings.map((entry, index) => {
|
||||
const nextPoints = dto.standings[index + 1]?.points || entry.points;
|
||||
|
||||
@@ -6,9 +6,9 @@ describe('MediaViewModel', () => {
|
||||
const dto = {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image' as const,
|
||||
category: 'avatar' as const,
|
||||
uploadedAt: new Date('2023-01-15'),
|
||||
type: 'image',
|
||||
category: 'avatar',
|
||||
uploadedAt: '2023-01-15T00:00:00.000Z',
|
||||
size: 2048000,
|
||||
};
|
||||
|
||||
@@ -26,8 +26,8 @@ describe('MediaViewModel', () => {
|
||||
const dto = {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image' as const,
|
||||
uploadedAt: new Date('2023-01-15'),
|
||||
type: 'image',
|
||||
uploadedAt: '2023-01-15T00:00:00.000Z',
|
||||
};
|
||||
|
||||
const viewModel = new MediaViewModel(dto);
|
||||
@@ -41,7 +41,7 @@ describe('MediaViewModel', () => {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
expect(viewModel.formattedSize).toBe('Unknown');
|
||||
@@ -52,7 +52,7 @@ describe('MediaViewModel', () => {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
size: 512000, // 500 KB
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('MediaViewModel', () => {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
size: 2048000, // 2 MB
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@ describe('MediaViewModel', () => {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
size: 1024, // 1 KB
|
||||
});
|
||||
|
||||
@@ -88,7 +88,7 @@ describe('MediaViewModel', () => {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/video.mp4',
|
||||
type: 'video',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
size: 104857600, // 100 MB
|
||||
});
|
||||
|
||||
@@ -100,19 +100,19 @@ describe('MediaViewModel', () => {
|
||||
id: '1',
|
||||
url: 'image.jpg',
|
||||
type: 'image',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
const videoVm = new MediaViewModel({
|
||||
id: '2',
|
||||
url: 'video.mp4',
|
||||
type: 'video',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
const docVm = new MediaViewModel({
|
||||
id: '3',
|
||||
url: 'doc.pdf',
|
||||
type: 'document',
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
expect(imageVm.type).toBe('image');
|
||||
@@ -129,7 +129,7 @@ describe('MediaViewModel', () => {
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image',
|
||||
category,
|
||||
uploadedAt: new Date(),
|
||||
uploadedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
expect(viewModel.category).toBe(category);
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
// Note: No generated DTO available for Media yet
|
||||
interface MediaDTO {
|
||||
id: string;
|
||||
url: string;
|
||||
type: 'image' | 'video' | 'document';
|
||||
category?: 'avatar' | 'team-logo' | 'league-cover' | 'race-result';
|
||||
uploadedAt: Date;
|
||||
size?: number;
|
||||
}
|
||||
import type { GetMediaOutputDTO } from '../types/generated';
|
||||
|
||||
/**
|
||||
* Media View Model
|
||||
@@ -21,12 +13,12 @@ export class MediaViewModel {
|
||||
uploadedAt: Date;
|
||||
size?: number;
|
||||
|
||||
constructor(dto: MediaDTO) {
|
||||
constructor(dto: GetMediaOutputDTO) {
|
||||
this.id = dto.id;
|
||||
this.url = dto.url;
|
||||
this.type = dto.type;
|
||||
this.uploadedAt = dto.uploadedAt;
|
||||
if (dto.category !== undefined) this.category = dto.category;
|
||||
this.type = dto.type as 'image' | 'video' | 'document';
|
||||
this.uploadedAt = new Date(dto.uploadedAt);
|
||||
if (dto.category !== undefined) this.category = dto.category as 'avatar' | 'team-logo' | 'league-cover' | 'race-result';
|
||||
if (dto.size !== undefined) this.size = dto.size;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,70 +1,53 @@
|
||||
import { TeamMemberViewModel } from './TeamMemberViewModel';
|
||||
|
||||
// Note: No generated DTO available for TeamDetails yet
|
||||
interface DriverDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
iracingId?: string;
|
||||
rating?: number;
|
||||
}
|
||||
interface TeamMemberDTO {
|
||||
driverId: string;
|
||||
driver?: DriverDTO;
|
||||
role: string;
|
||||
joinedAt: string;
|
||||
}
|
||||
interface TeamDetailsDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
logoUrl?: string;
|
||||
memberCount: number;
|
||||
ownerId: string;
|
||||
members: TeamMemberDTO[];
|
||||
}
|
||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
||||
|
||||
export class TeamDetailsViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description?: string;
|
||||
logoUrl?: string;
|
||||
memberCount: number;
|
||||
ownerId: string;
|
||||
members: TeamMemberViewModel[];
|
||||
|
||||
leagues: string[];
|
||||
createdAt?: string;
|
||||
specialization?: string;
|
||||
region?: string;
|
||||
languages?: string[];
|
||||
membership: { role: string; joinedAt: string; isActive: boolean } | null;
|
||||
canManage: boolean;
|
||||
private currentUserId: string;
|
||||
|
||||
constructor(dto: TeamDetailsDTO, currentUserId: string) {
|
||||
this.id = dto.id;
|
||||
this.name = dto.name;
|
||||
this.description = dto.description;
|
||||
this.logoUrl = dto.logoUrl;
|
||||
this.memberCount = dto.memberCount;
|
||||
this.ownerId = dto.ownerId;
|
||||
this.members = dto.members.map(m => new TeamMemberViewModel(m, currentUserId, dto.ownerId));
|
||||
constructor(dto: GetTeamDetailsOutputDTO, currentUserId: string) {
|
||||
this.id = dto.team.id;
|
||||
this.name = dto.team.name;
|
||||
this.tag = dto.team.tag;
|
||||
this.description = dto.team.description;
|
||||
this.ownerId = dto.team.ownerId;
|
||||
this.leagues = dto.team.leagues;
|
||||
this.createdAt = dto.team.createdAt;
|
||||
this.specialization = dto.team.specialization;
|
||||
this.region = dto.team.region;
|
||||
this.languages = dto.team.languages;
|
||||
this.membership = dto.membership;
|
||||
this.canManage = dto.canManage;
|
||||
this.currentUserId = currentUserId;
|
||||
}
|
||||
|
||||
/** UI-specific: Whether current user is owner */
|
||||
get isOwner(): boolean {
|
||||
return this.currentUserId === this.ownerId;
|
||||
return this.membership?.role === 'owner';
|
||||
}
|
||||
|
||||
/** UI-specific: Whether can add members */
|
||||
get canAddMembers(): boolean {
|
||||
return this.isOwner && this.memberCount < 10; // Assuming max 10
|
||||
/** UI-specific: Whether can manage team */
|
||||
get canManage(): boolean {
|
||||
return this.canManage;
|
||||
}
|
||||
|
||||
/** UI-specific: Member management actions available */
|
||||
get memberActionsAvailable(): boolean {
|
||||
return this.isOwner;
|
||||
/** UI-specific: Whether current user is member */
|
||||
get isMember(): boolean {
|
||||
return this.membership !== null;
|
||||
}
|
||||
|
||||
/** UI-specific: Team status */
|
||||
get teamStatus(): string {
|
||||
if (this.memberCount < 5) return 'Recruiting';
|
||||
if (this.memberCount < 10) return 'Active';
|
||||
return 'Full';
|
||||
/** UI-specific: Current user's role */
|
||||
get userRole(): string {
|
||||
return this.membership?.role || 'none';
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user