946 lines
25 KiB
TypeScript
946 lines
25 KiB
TypeScript
/**
|
|
* Domain-specific API Client for GridPilot Website
|
|
*
|
|
* This module provides a strongly-typed HTTP client for all API operations.
|
|
* The website should use these methods instead of directly importing core use cases.
|
|
*/
|
|
|
|
// ============================================================================
|
|
// Types - These mirror the API DTOs
|
|
// ============================================================================
|
|
|
|
// Common Types
|
|
export interface DriverDTO {
|
|
id: string;
|
|
name: string;
|
|
avatarUrl?: string;
|
|
iracingId?: string;
|
|
rating?: number;
|
|
}
|
|
|
|
export interface ProtestViewModel {
|
|
id: string;
|
|
raceId: string;
|
|
complainantId: string;
|
|
defendantId: string;
|
|
description: string;
|
|
status: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface LeagueMemberViewModel {
|
|
driverId: string;
|
|
driver?: DriverDTO;
|
|
role: string;
|
|
joinedAt: string;
|
|
}
|
|
|
|
export interface StandingEntryViewModel {
|
|
driverId: string;
|
|
driver?: DriverDTO;
|
|
position: number;
|
|
points: number;
|
|
wins: number;
|
|
podiums: number;
|
|
races: number;
|
|
}
|
|
|
|
export interface ScheduledRaceViewModel {
|
|
id: string;
|
|
name: string;
|
|
scheduledTime: string;
|
|
status: string;
|
|
trackName?: string;
|
|
}
|
|
|
|
// League Types
|
|
export interface LeagueSummaryViewModel {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
logoUrl?: string;
|
|
coverImage?: string;
|
|
memberCount: number;
|
|
maxMembers: number;
|
|
isPublic: boolean;
|
|
ownerId: string;
|
|
ownerName?: string;
|
|
scoringType?: string;
|
|
status?: string;
|
|
}
|
|
|
|
export interface AllLeaguesWithCapacityViewModel {
|
|
leagues: LeagueSummaryViewModel[];
|
|
}
|
|
|
|
export interface LeagueStatsDto {
|
|
totalLeagues: number;
|
|
}
|
|
|
|
export interface LeagueJoinRequestViewModel {
|
|
id: string;
|
|
leagueId: string;
|
|
driverId: string;
|
|
requestedAt: Date;
|
|
message?: string;
|
|
}
|
|
|
|
export interface LeagueAdminPermissionsViewModel {
|
|
canManageMembers: boolean;
|
|
canManageRaces: boolean;
|
|
canManageSettings: boolean;
|
|
canManageProtests: boolean;
|
|
isOwner: boolean;
|
|
isAdmin: boolean;
|
|
}
|
|
|
|
export interface LeagueOwnerSummaryViewModel {
|
|
leagueId: string;
|
|
leagueName: string;
|
|
memberCount: number;
|
|
pendingRequests: number;
|
|
}
|
|
|
|
export interface LeagueConfigFormModelDto {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
isPublic: boolean;
|
|
maxMembers: number;
|
|
// Add other config fields as needed
|
|
}
|
|
|
|
export interface LeagueAdminProtestsViewModel {
|
|
protests: ProtestViewModel[];
|
|
}
|
|
|
|
export interface LeagueSeasonSummaryViewModel {
|
|
id: string;
|
|
name: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface LeagueMembershipsViewModel {
|
|
members: LeagueMemberViewModel[];
|
|
}
|
|
|
|
export interface LeagueStandingsViewModel {
|
|
standings: StandingEntryViewModel[];
|
|
}
|
|
|
|
export interface LeagueScheduleViewModel {
|
|
races: ScheduledRaceViewModel[];
|
|
}
|
|
|
|
export interface LeagueStatsViewModel {
|
|
leagueId: string;
|
|
totalRaces: number;
|
|
completedRaces: number;
|
|
scheduledRaces: number;
|
|
averageSOF?: number;
|
|
highestSOF?: number;
|
|
lowestSOF?: number;
|
|
}
|
|
|
|
export interface LeagueAdminViewModel {
|
|
config: LeagueConfigFormModelDto;
|
|
members: LeagueMemberViewModel[];
|
|
joinRequests: LeagueJoinRequestViewModel[];
|
|
}
|
|
|
|
export interface CreateLeagueInput {
|
|
name: string;
|
|
description?: string;
|
|
isPublic: boolean;
|
|
maxMembers: number;
|
|
ownerId: string;
|
|
}
|
|
|
|
export interface CreateLeagueOutput {
|
|
leagueId: string;
|
|
success: boolean;
|
|
}
|
|
|
|
// Driver Types
|
|
export interface DriverLeaderboardItemViewModel {
|
|
id: string;
|
|
name: string;
|
|
avatarUrl?: string;
|
|
rating: number;
|
|
wins: number;
|
|
races: number;
|
|
skillLevel: string;
|
|
}
|
|
|
|
export interface DriversLeaderboardViewModel {
|
|
drivers: DriverLeaderboardItemViewModel[];
|
|
}
|
|
|
|
export interface DriverStatsDto {
|
|
totalDrivers: number;
|
|
}
|
|
|
|
export interface CompleteOnboardingInput {
|
|
iracingId: string;
|
|
displayName: string;
|
|
}
|
|
|
|
export interface CompleteOnboardingOutput {
|
|
driverId: string;
|
|
success: boolean;
|
|
}
|
|
|
|
export interface DriverRegistrationStatusViewModel {
|
|
isRegistered: boolean;
|
|
raceId: string;
|
|
driverId: string;
|
|
}
|
|
|
|
// Team Types
|
|
export interface TeamSummaryViewModel {
|
|
id: string;
|
|
name: string;
|
|
logoUrl?: string;
|
|
memberCount: number;
|
|
rating: number;
|
|
}
|
|
|
|
export interface AllTeamsViewModel {
|
|
teams: TeamSummaryViewModel[];
|
|
}
|
|
|
|
export interface TeamMemberViewModel {
|
|
driverId: string;
|
|
driver?: DriverDTO;
|
|
role: string;
|
|
joinedAt: string;
|
|
}
|
|
|
|
export interface TeamJoinRequestItemViewModel {
|
|
id: string;
|
|
teamId: string;
|
|
driverId: string;
|
|
requestedAt: string;
|
|
message?: string;
|
|
}
|
|
|
|
export interface TeamDetailsViewModel {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
logoUrl?: string;
|
|
memberCount: number;
|
|
ownerId: string;
|
|
members: TeamMemberViewModel[];
|
|
}
|
|
|
|
export interface TeamMembersViewModel {
|
|
members: TeamMemberViewModel[];
|
|
}
|
|
|
|
export interface TeamJoinRequestsViewModel {
|
|
requests: TeamJoinRequestItemViewModel[];
|
|
}
|
|
|
|
export interface DriverTeamViewModel {
|
|
teamId: string;
|
|
teamName: string;
|
|
role: string;
|
|
joinedAt: Date;
|
|
}
|
|
|
|
export interface CreateTeamInput {
|
|
name: string;
|
|
description?: string;
|
|
ownerId: string;
|
|
}
|
|
|
|
export interface CreateTeamOutput {
|
|
teamId: string;
|
|
success: boolean;
|
|
}
|
|
|
|
export interface UpdateTeamInput {
|
|
name?: string;
|
|
description?: string;
|
|
logoUrl?: string;
|
|
}
|
|
|
|
export interface UpdateTeamOutput {
|
|
success: boolean;
|
|
}
|
|
|
|
// Race Types
|
|
export interface RaceListItemViewModel {
|
|
id: string;
|
|
name: string;
|
|
leagueId: string;
|
|
leagueName: string;
|
|
scheduledTime: string;
|
|
status: string;
|
|
trackName?: string;
|
|
}
|
|
|
|
export interface AllRacesPageViewModel {
|
|
races: RaceListItemViewModel[];
|
|
}
|
|
|
|
export interface RaceStatsDto {
|
|
totalRaces: number;
|
|
}
|
|
|
|
// Sponsor Types
|
|
export interface GetEntitySponsorshipPricingResultDto {
|
|
mainSlotPrice: number;
|
|
secondarySlotPrice: number;
|
|
currency: string;
|
|
}
|
|
|
|
export interface SponsorViewModel {
|
|
id: string;
|
|
name: string;
|
|
logoUrl?: string;
|
|
websiteUrl?: string;
|
|
}
|
|
|
|
export interface GetSponsorsOutput {
|
|
sponsors: SponsorViewModel[];
|
|
}
|
|
|
|
export interface CreateSponsorInput {
|
|
name: string;
|
|
logoUrl?: string;
|
|
websiteUrl?: string;
|
|
userId: string;
|
|
}
|
|
|
|
export interface CreateSponsorOutput {
|
|
sponsorId: string;
|
|
success: boolean;
|
|
}
|
|
|
|
export interface SponsorDashboardDTO {
|
|
sponsorId: string;
|
|
sponsorName: string;
|
|
totalSponsorships: number;
|
|
activeSponsorships: number;
|
|
totalInvestment: number;
|
|
}
|
|
|
|
export interface SponsorshipDetailViewModel {
|
|
id: string;
|
|
leagueId: string;
|
|
leagueName: string;
|
|
seasonId: string;
|
|
tier: 'main' | 'secondary';
|
|
status: string;
|
|
amount: number;
|
|
currency: string;
|
|
}
|
|
|
|
export interface SponsorSponsorshipsDTO {
|
|
sponsorId: string;
|
|
sponsorName: string;
|
|
sponsorships: SponsorshipDetailViewModel[];
|
|
}
|
|
|
|
// Media Types
|
|
export interface RequestAvatarGenerationInput {
|
|
driverId: string;
|
|
style?: string;
|
|
}
|
|
|
|
export interface RequestAvatarGenerationOutput {
|
|
success: boolean;
|
|
avatarUrl?: string;
|
|
error?: string;
|
|
}
|
|
|
|
// Analytics Types
|
|
export interface RecordPageViewInput {
|
|
path: string;
|
|
userId?: string;
|
|
sessionId?: string;
|
|
}
|
|
|
|
export interface RecordPageViewOutput {
|
|
success: boolean;
|
|
}
|
|
|
|
export interface RecordEngagementInput {
|
|
eventType: string;
|
|
eventData?: Record<string, unknown>;
|
|
userId?: string;
|
|
sessionId?: string;
|
|
}
|
|
|
|
export interface RecordEngagementOutput {
|
|
success: boolean;
|
|
}
|
|
|
|
// Auth Types
|
|
export interface LoginParams {
|
|
email: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface SignupParams {
|
|
email: string;
|
|
password: string;
|
|
displayName: string;
|
|
}
|
|
|
|
export interface SessionData {
|
|
userId: string;
|
|
email: string;
|
|
displayName?: string;
|
|
driverId?: string;
|
|
isAuthenticated: boolean;
|
|
}
|
|
|
|
// Payments Types
|
|
export interface PaymentViewModel {
|
|
id: string;
|
|
amount: number;
|
|
currency: string;
|
|
status: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface GetPaymentsOutput {
|
|
payments: PaymentViewModel[];
|
|
}
|
|
|
|
export interface CreatePaymentInput {
|
|
amount: number;
|
|
currency: string;
|
|
leagueId: string;
|
|
driverId: string;
|
|
description?: string;
|
|
}
|
|
|
|
export interface CreatePaymentOutput {
|
|
paymentId: string;
|
|
success: boolean;
|
|
}
|
|
|
|
export interface MembershipFeeViewModel {
|
|
leagueId: string;
|
|
amount: number;
|
|
currency: string;
|
|
period: string;
|
|
}
|
|
|
|
export interface MemberPaymentViewModel {
|
|
driverId: string;
|
|
amount: number;
|
|
paidAt: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface GetMembershipFeesOutput {
|
|
fees: MembershipFeeViewModel[];
|
|
memberPayments: MemberPaymentViewModel[];
|
|
}
|
|
|
|
export interface PrizeViewModel {
|
|
id: string;
|
|
name: string;
|
|
amount: number;
|
|
currency: string;
|
|
position?: number;
|
|
}
|
|
|
|
export interface GetPrizesOutput {
|
|
prizes: PrizeViewModel[];
|
|
}
|
|
|
|
export interface WalletTransactionViewModel {
|
|
id: string;
|
|
type: 'deposit' | 'withdrawal';
|
|
amount: number;
|
|
description?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface WalletViewModel {
|
|
driverId: string;
|
|
balance: number;
|
|
currency: string;
|
|
transactions: WalletTransactionViewModel[];
|
|
}
|
|
|
|
export interface GetWalletOutput {
|
|
wallet: WalletViewModel;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Base API Client
|
|
// ============================================================================
|
|
|
|
class BaseApiClient {
|
|
private baseUrl: string;
|
|
|
|
constructor(baseUrl: string) {
|
|
this.baseUrl = baseUrl;
|
|
}
|
|
|
|
protected async request<T>(method: string, path: string, data?: object): Promise<T> {
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
|
|
const config: RequestInit = {
|
|
method,
|
|
headers,
|
|
credentials: 'include', // Include cookies for auth
|
|
};
|
|
|
|
if (data) {
|
|
config.body = JSON.stringify(data);
|
|
}
|
|
|
|
const response = await fetch(`${this.baseUrl}${path}`, config);
|
|
|
|
if (!response.ok) {
|
|
let errorData: { message?: string } = { message: response.statusText };
|
|
try {
|
|
errorData = await response.json();
|
|
} catch {
|
|
// Keep default error message
|
|
}
|
|
throw new Error(errorData.message || `API request failed with status ${response.status}`);
|
|
}
|
|
|
|
const text = await response.text();
|
|
if (!text) {
|
|
return null as T;
|
|
}
|
|
return JSON.parse(text) as T;
|
|
}
|
|
|
|
protected get<T>(path: string): Promise<T> {
|
|
return this.request<T>('GET', path);
|
|
}
|
|
|
|
protected post<T>(path: string, data: object): Promise<T> {
|
|
return this.request<T>('POST', path, data);
|
|
}
|
|
|
|
protected put<T>(path: string, data: object): Promise<T> {
|
|
return this.request<T>('PUT', path, data);
|
|
}
|
|
|
|
protected delete<T>(path: string): Promise<T> {
|
|
return this.request<T>('DELETE', path);
|
|
}
|
|
|
|
protected patch<T>(path: string, data: object): Promise<T> {
|
|
return this.request<T>('PATCH', path, data);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Domain-Specific API Clients
|
|
// ============================================================================
|
|
|
|
class LeaguesApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Get all leagues with capacity information */
|
|
getAllWithCapacity(): Promise<AllLeaguesWithCapacityViewModel> {
|
|
return this.get<AllLeaguesWithCapacityViewModel>('/leagues/all-with-capacity');
|
|
}
|
|
|
|
/** Get total number of leagues */
|
|
getTotal(): Promise<LeagueStatsDto> {
|
|
return this.get<LeagueStatsDto>('/leagues/total-leagues');
|
|
}
|
|
|
|
/** Get league standings */
|
|
getStandings(leagueId: string): Promise<LeagueStandingsViewModel> {
|
|
return this.get<LeagueStandingsViewModel>(`/leagues/${leagueId}/standings`);
|
|
}
|
|
|
|
/** Get league schedule */
|
|
getSchedule(leagueId: string): Promise<LeagueScheduleViewModel> {
|
|
return this.get<LeagueScheduleViewModel>(`/leagues/${leagueId}/schedule`);
|
|
}
|
|
|
|
/** Get league stats */
|
|
getStats(leagueId: string): Promise<LeagueStatsViewModel> {
|
|
return this.get<LeagueStatsViewModel>(`/leagues/${leagueId}/stats`);
|
|
}
|
|
|
|
/** Get league memberships */
|
|
getMemberships(leagueId: string): Promise<LeagueMembershipsViewModel> {
|
|
return this.get<LeagueMembershipsViewModel>(`/leagues/${leagueId}/memberships`);
|
|
}
|
|
|
|
/** Get league join requests */
|
|
getJoinRequests(leagueId: string): Promise<LeagueJoinRequestViewModel[]> {
|
|
return this.get<LeagueJoinRequestViewModel[]>(`/leagues/${leagueId}/join-requests`);
|
|
}
|
|
|
|
/** Approve a join request */
|
|
approveJoinRequest(leagueId: string, requestId: string): Promise<{ success: boolean }> {
|
|
return this.post<{ success: boolean }>(`/leagues/${leagueId}/join-requests/approve`, { requestId });
|
|
}
|
|
|
|
/** Reject a join request */
|
|
rejectJoinRequest(leagueId: string, requestId: string, reason?: string): Promise<{ success: boolean }> {
|
|
return this.post<{ success: boolean }>(`/leagues/${leagueId}/join-requests/reject`, { requestId, reason });
|
|
}
|
|
|
|
/** Get league admin permissions */
|
|
getAdminPermissions(leagueId: string, performerDriverId: string): Promise<LeagueAdminPermissionsViewModel> {
|
|
return this.get<LeagueAdminPermissionsViewModel>(`/leagues/${leagueId}/permissions/${performerDriverId}`);
|
|
}
|
|
|
|
/** Get league owner summary */
|
|
getOwnerSummary(leagueId: string, ownerId: string): Promise<LeagueOwnerSummaryViewModel | null> {
|
|
return this.get<LeagueOwnerSummaryViewModel | null>(`/leagues/${leagueId}/owner-summary/${ownerId}`);
|
|
}
|
|
|
|
/** Get league full config */
|
|
getConfig(leagueId: string): Promise<LeagueConfigFormModelDto | null> {
|
|
return this.get<LeagueConfigFormModelDto | null>(`/leagues/${leagueId}/config`);
|
|
}
|
|
|
|
/** Get league protests */
|
|
getProtests(leagueId: string): Promise<LeagueAdminProtestsViewModel> {
|
|
return this.get<LeagueAdminProtestsViewModel>(`/leagues/${leagueId}/protests`);
|
|
}
|
|
|
|
/** Get league seasons */
|
|
getSeasons(leagueId: string): Promise<LeagueSeasonSummaryViewModel[]> {
|
|
return this.get<LeagueSeasonSummaryViewModel[]>(`/leagues/${leagueId}/seasons`);
|
|
}
|
|
|
|
/** Get league admin data */
|
|
getAdmin(leagueId: string): Promise<LeagueAdminViewModel> {
|
|
return this.get<LeagueAdminViewModel>(`/leagues/${leagueId}/admin`);
|
|
}
|
|
|
|
/** Create a new league */
|
|
create(input: CreateLeagueInput): Promise<CreateLeagueOutput> {
|
|
return this.post<CreateLeagueOutput>('/leagues', input);
|
|
}
|
|
|
|
/** Remove a member from league */
|
|
removeMember(leagueId: string, performerDriverId: string, targetDriverId: string): Promise<{ success: boolean }> {
|
|
return this.patch<{ success: boolean }>(`/leagues/${leagueId}/members/${targetDriverId}/remove`, { performerDriverId });
|
|
}
|
|
|
|
/** Update member role */
|
|
updateMemberRole(leagueId: string, performerDriverId: string, targetDriverId: string, newRole: string): Promise<{ success: boolean }> {
|
|
return this.patch<{ success: boolean }>(`/leagues/${leagueId}/members/${targetDriverId}/role`, { performerDriverId, newRole });
|
|
}
|
|
}
|
|
|
|
class DriversApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Get drivers leaderboard */
|
|
getLeaderboard(): Promise<DriversLeaderboardViewModel> {
|
|
return this.get<DriversLeaderboardViewModel>('/drivers/leaderboard');
|
|
}
|
|
|
|
/** Get total number of drivers */
|
|
getTotal(): Promise<DriverStatsDto> {
|
|
return this.get<DriverStatsDto>('/drivers/total-drivers');
|
|
}
|
|
|
|
/** Get current driver (based on session) */
|
|
getCurrent(): Promise<DriverDTO | null> {
|
|
return this.get<DriverDTO | null>('/drivers/current');
|
|
}
|
|
|
|
/** Complete driver onboarding */
|
|
completeOnboarding(input: CompleteOnboardingInput): Promise<CompleteOnboardingOutput> {
|
|
return this.post<CompleteOnboardingOutput>('/drivers/complete-onboarding', input);
|
|
}
|
|
|
|
/** Get driver registration status for a race */
|
|
getRegistrationStatus(driverId: string, raceId: string): Promise<DriverRegistrationStatusViewModel> {
|
|
return this.get<DriverRegistrationStatusViewModel>(`/drivers/${driverId}/races/${raceId}/registration-status`);
|
|
}
|
|
}
|
|
|
|
class TeamsApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Get all teams */
|
|
getAll(): Promise<AllTeamsViewModel> {
|
|
return this.get<AllTeamsViewModel>('/teams/all');
|
|
}
|
|
|
|
/** Get team details */
|
|
getDetails(teamId: string): Promise<TeamDetailsViewModel | null> {
|
|
return this.get<TeamDetailsViewModel | null>(`/teams/${teamId}`);
|
|
}
|
|
|
|
/** Get team members */
|
|
getMembers(teamId: string): Promise<TeamMembersViewModel> {
|
|
return this.get<TeamMembersViewModel>(`/teams/${teamId}/members`);
|
|
}
|
|
|
|
/** Get team join requests */
|
|
getJoinRequests(teamId: string): Promise<TeamJoinRequestsViewModel> {
|
|
return this.get<TeamJoinRequestsViewModel>(`/teams/${teamId}/join-requests`);
|
|
}
|
|
|
|
/** Approve a join request */
|
|
approveJoinRequest(teamId: string, requestId: string): Promise<{ success: boolean }> {
|
|
return this.post<{ success: boolean }>(`/teams/${teamId}/join-requests/approve`, { requestId });
|
|
}
|
|
|
|
/** Reject a join request */
|
|
rejectJoinRequest(teamId: string, requestId: string, reason?: string): Promise<{ success: boolean }> {
|
|
return this.post<{ success: boolean }>(`/teams/${teamId}/join-requests/reject`, { requestId, reason });
|
|
}
|
|
|
|
/** Create a new team */
|
|
create(input: CreateTeamInput): Promise<CreateTeamOutput> {
|
|
return this.post<CreateTeamOutput>('/teams', input);
|
|
}
|
|
|
|
/** Update team */
|
|
update(teamId: string, input: UpdateTeamInput): Promise<UpdateTeamOutput> {
|
|
return this.patch<UpdateTeamOutput>(`/teams/${teamId}`, input);
|
|
}
|
|
|
|
/** Get driver's team */
|
|
getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
|
|
return this.get<DriverTeamViewModel | null>(`/teams/driver/${driverId}`);
|
|
}
|
|
}
|
|
|
|
class RacesApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Get all races */
|
|
getAll(): Promise<AllRacesPageViewModel> {
|
|
return this.get<AllRacesPageViewModel>('/races/all');
|
|
}
|
|
|
|
/** Get total number of races */
|
|
getTotal(): Promise<RaceStatsDto> {
|
|
return this.get<RaceStatsDto>('/races/total-races');
|
|
}
|
|
}
|
|
|
|
class SponsorsApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Get sponsorship pricing */
|
|
getPricing(): Promise<GetEntitySponsorshipPricingResultDto> {
|
|
return this.get<GetEntitySponsorshipPricingResultDto>('/sponsors/pricing');
|
|
}
|
|
|
|
/** Get all sponsors */
|
|
getAll(): Promise<GetSponsorsOutput> {
|
|
return this.get<GetSponsorsOutput>('/sponsors');
|
|
}
|
|
|
|
/** Create a new sponsor */
|
|
create(input: CreateSponsorInput): Promise<CreateSponsorOutput> {
|
|
return this.post<CreateSponsorOutput>('/sponsors', input);
|
|
}
|
|
|
|
/** Get sponsor dashboard */
|
|
getDashboard(sponsorId: string): Promise<SponsorDashboardDTO | null> {
|
|
return this.get<SponsorDashboardDTO | null>(`/sponsors/dashboard/${sponsorId}`);
|
|
}
|
|
|
|
/** Get sponsor sponsorships */
|
|
getSponsorships(sponsorId: string): Promise<SponsorSponsorshipsDTO | null> {
|
|
return this.get<SponsorSponsorshipsDTO | null>(`/sponsors/${sponsorId}/sponsorships`);
|
|
}
|
|
}
|
|
|
|
class MediaApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Request avatar generation */
|
|
requestAvatarGeneration(input: RequestAvatarGenerationInput): Promise<RequestAvatarGenerationOutput> {
|
|
return this.post<RequestAvatarGenerationOutput>('/media/avatar/generate', input);
|
|
}
|
|
}
|
|
|
|
class AnalyticsApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Record a page view */
|
|
recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
|
|
return this.post<RecordPageViewOutput>('/analytics/page-view', input);
|
|
}
|
|
|
|
/** Record an engagement event */
|
|
recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
|
|
return this.post<RecordEngagementOutput>('/analytics/engagement', input);
|
|
}
|
|
}
|
|
|
|
class AuthApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Sign up with email */
|
|
signup(params: SignupParams): Promise<SessionData> {
|
|
return this.post<SessionData>('/auth/signup', params);
|
|
}
|
|
|
|
/** Login with email */
|
|
login(params: LoginParams): Promise<SessionData> {
|
|
return this.post<SessionData>('/auth/login', params);
|
|
}
|
|
|
|
/** Get current session */
|
|
getSession(): Promise<SessionData | null> {
|
|
return this.get<SessionData | null>('/auth/session');
|
|
}
|
|
|
|
/** Logout */
|
|
logout(): Promise<void> {
|
|
return this.post<void>('/auth/logout', {});
|
|
}
|
|
|
|
/** 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}`;
|
|
}
|
|
}
|
|
|
|
class PaymentsApiClient extends BaseApiClient {
|
|
constructor(baseUrl: string) {
|
|
super(baseUrl);
|
|
}
|
|
|
|
/** Get payments */
|
|
getPayments(leagueId?: string, driverId?: string): Promise<GetPaymentsOutput> {
|
|
const params = new URLSearchParams();
|
|
if (leagueId) params.append('leagueId', leagueId);
|
|
if (driverId) params.append('driverId', driverId);
|
|
const query = params.toString();
|
|
return this.get<GetPaymentsOutput>(`/payments${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
/** Create a payment */
|
|
createPayment(input: CreatePaymentInput): Promise<CreatePaymentOutput> {
|
|
return this.post<CreatePaymentOutput>('/payments', input);
|
|
}
|
|
|
|
/** Update payment status */
|
|
updatePaymentStatus(paymentId: string, status: string): Promise<{ success: boolean }> {
|
|
return this.patch<{ success: boolean }>('/payments/status', { paymentId, status });
|
|
}
|
|
|
|
/** Get membership fees */
|
|
getMembershipFees(leagueId: string): Promise<GetMembershipFeesOutput> {
|
|
return this.get<GetMembershipFeesOutput>(`/payments/membership-fees?leagueId=${leagueId}`);
|
|
}
|
|
|
|
/** Upsert membership fee */
|
|
upsertMembershipFee(leagueId: string, amount: number, currency: string, period: string): Promise<{ success: boolean }> {
|
|
return this.post<{ success: boolean }>('/payments/membership-fees', { leagueId, amount, currency, period });
|
|
}
|
|
|
|
/** Update member payment */
|
|
updateMemberPayment(leagueId: string, driverId: string, amount: number, paidAt: string): Promise<{ success: boolean }> {
|
|
return this.patch<{ success: boolean }>('/payments/membership-fees/member-payment', { leagueId, driverId, amount, paidAt });
|
|
}
|
|
|
|
/** Get prizes */
|
|
getPrizes(leagueId?: string, seasonId?: string): Promise<GetPrizesOutput> {
|
|
const params = new URLSearchParams();
|
|
if (leagueId) params.append('leagueId', leagueId);
|
|
if (seasonId) params.append('seasonId', seasonId);
|
|
const query = params.toString();
|
|
return this.get<GetPrizesOutput>(`/payments/prizes${query ? `?${query}` : ''}`);
|
|
}
|
|
|
|
/** Create a prize */
|
|
createPrize(name: string, amount: number, currency: string, leagueId: string, position?: number): Promise<{ prizeId: string; success: boolean }> {
|
|
return this.post<{ prizeId: string; success: boolean }>('/payments/prizes', { name, amount, currency, leagueId, position });
|
|
}
|
|
|
|
/** Award a prize */
|
|
awardPrize(prizeId: string, driverId: string): Promise<{ success: boolean }> {
|
|
return this.patch<{ success: boolean }>('/payments/prizes/award', { prizeId, driverId });
|
|
}
|
|
|
|
/** Delete a prize */
|
|
deletePrize(prizeId: string): Promise<{ success: boolean }> {
|
|
return this.delete<{ success: boolean }>(`/payments/prizes?prizeId=${prizeId}`);
|
|
}
|
|
|
|
/** Get wallet */
|
|
getWallet(driverId: string): Promise<GetWalletOutput> {
|
|
return this.get<GetWalletOutput>(`/payments/wallets?driverId=${driverId}`);
|
|
}
|
|
|
|
/** Process wallet transaction */
|
|
processWalletTransaction(driverId: string, type: 'deposit' | 'withdrawal', amount: number, description?: string): Promise<{ success: boolean }> {
|
|
return this.post<{ success: boolean }>('/payments/wallets/transactions', { driverId, type, amount, description });
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Main API Client with Domain Namespaces
|
|
// ============================================================================
|
|
|
|
class ApiClient {
|
|
public readonly leagues: LeaguesApiClient;
|
|
public readonly drivers: DriversApiClient;
|
|
public readonly teams: TeamsApiClient;
|
|
public readonly races: RacesApiClient;
|
|
public readonly sponsors: SponsorsApiClient;
|
|
public readonly media: MediaApiClient;
|
|
public readonly analytics: AnalyticsApiClient;
|
|
public readonly auth: AuthApiClient;
|
|
public readonly payments: PaymentsApiClient;
|
|
|
|
constructor(baseUrl: string) {
|
|
this.leagues = new LeaguesApiClient(baseUrl);
|
|
this.drivers = new DriversApiClient(baseUrl);
|
|
this.teams = new TeamsApiClient(baseUrl);
|
|
this.races = new RacesApiClient(baseUrl);
|
|
this.sponsors = new SponsorsApiClient(baseUrl);
|
|
this.media = new MediaApiClient(baseUrl);
|
|
this.analytics = new AnalyticsApiClient(baseUrl);
|
|
this.auth = new AuthApiClient(baseUrl);
|
|
this.payments = new PaymentsApiClient(baseUrl);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Singleton Instance
|
|
// ============================================================================
|
|
|
|
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
|
|
|
|
export const apiClient = new ApiClient(API_BASE_URL);
|
|
|
|
// Default export for convenience
|
|
export default apiClient;
|