api client refactor
This commit is contained in:
@@ -72,6 +72,27 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"import/no-restricted-imports": [
|
||||
2,
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "apps/website/lib/apiClient",
|
||||
"message": "Direct imports from apiClient are forbidden. Use services instead."
|
||||
},
|
||||
{
|
||||
"name": "apps/website/lib/dtos",
|
||||
"message": "Direct imports from dtos in UI components are forbidden. Use ViewModels instead.",
|
||||
"from": ["apps/website/app/**/*", "apps/website/components/**/*"]
|
||||
},
|
||||
{
|
||||
"name": "apps/website/lib/api",
|
||||
"message": "Direct imports from api/* in UI components are forbidden. Use services instead.",
|
||||
"from": ["apps/website/app/**/*", "apps/website/components/**/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals",
|
||||
"plugins": ["boundaries"],
|
||||
"extends": ["next/core-web-vitals", "plugin:import/recommended", "plugin:import/typescript"],
|
||||
"plugins": ["boundaries", "import"],
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"typescript": {}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react/no-unescaped-entities": "off",
|
||||
"@next/next/no-img-element": "warn",
|
||||
|
||||
361
apps/website/DATA_FLOW.md
Normal file
361
apps/website/DATA_FLOW.md
Normal file
@@ -0,0 +1,361 @@
|
||||
Frontend data shapes: View Models, Presenters, and API Client (Strict)
|
||||
|
||||
This document defines the exact placement and responsibilities for frontend data shapes.
|
||||
It is designed to leave no room for interpretation.
|
||||
|
||||
⸻
|
||||
|
||||
1. Definitions
|
||||
|
||||
API DTOs
|
||||
|
||||
Transport shapes owned by the API boundary (HTTP). They are not used directly by UI components.
|
||||
|
||||
View Models
|
||||
|
||||
UI-owned shapes. They represent exactly what the UI needs and nothing else.
|
||||
|
||||
Website Presenters
|
||||
|
||||
Pure mappers that convert API DTOs (or core use-case outputs) into View Models.
|
||||
|
||||
API Client
|
||||
|
||||
A thin HTTP wrapper that returns API DTOs only and performs no business logic.
|
||||
|
||||
⸻
|
||||
|
||||
2. Directory layout (exact)
|
||||
|
||||
apps/website
|
||||
├── app/ # Next.js routes/pages
|
||||
├── components/ # React components (UI only)
|
||||
├── lib/
|
||||
│ ├── api/ # API client (HTTP only)
|
||||
│ ├── dtos/ # API DTO types (transport shapes)
|
||||
│ ├── view-models/ # View Models (UI-owned shapes)
|
||||
│ ├── presenters/ # Presenters: DTO -> ViewModel mapping
|
||||
│ ├── services/ # UI orchestration (calls api + presenters)
|
||||
│ └── index.ts
|
||||
|
||||
No additional folders for these concerns are allowed.
|
||||
|
||||
⸻
|
||||
|
||||
3. View Models (placement and rules)
|
||||
|
||||
Where they live
|
||||
|
||||
View Models MUST live in:
|
||||
|
||||
apps/website/lib/view-models
|
||||
|
||||
What they may contain
|
||||
• UI-ready primitives (strings, numbers, booleans)
|
||||
• UI-specific derived fields (e.g., isOwner, badgeLabel, formattedDate)
|
||||
• UI-specific structures (e.g., grouped arrays, flattened objects)
|
||||
|
||||
What they must NOT contain
|
||||
• Domain entities or value objects
|
||||
• API transport metadata
|
||||
• Validation logic
|
||||
• Network or persistence concerns
|
||||
|
||||
Rule
|
||||
|
||||
Components consume only View Models.
|
||||
|
||||
⸻
|
||||
|
||||
4. API DTOs in the website (placement and rules)
|
||||
|
||||
Clarification
|
||||
|
||||
The website does have DTOs, but only API DTOs.
|
||||
|
||||
These DTOs exist exclusively to type HTTP communication with the backend API.
|
||||
They are not UI models.
|
||||
|
||||
⸻
|
||||
|
||||
Where they live
|
||||
|
||||
Website-side API DTO types MUST live in:
|
||||
|
||||
apps/website/lib/dtos
|
||||
|
||||
What they represent
|
||||
• Exact transport shapes sent/received via HTTP
|
||||
• Backend API contracts
|
||||
• No UI assumptions
|
||||
|
||||
Who may use them
|
||||
• API client
|
||||
• Website presenters
|
||||
|
||||
Who must NOT use them
|
||||
• React components
|
||||
• Pages
|
||||
• UI logic
|
||||
|
||||
Rule
|
||||
|
||||
API DTOs stop at the presenter boundary.
|
||||
|
||||
Components must never consume API DTOs directly.
|
||||
|
||||
⸻
|
||||
|
||||
5. Presenters (website) (placement and rules)
|
||||
|
||||
Where they live
|
||||
|
||||
Website presenters MUST live in:
|
||||
|
||||
apps/website/lib/presenters
|
||||
|
||||
What they do
|
||||
• Convert API DTOs into View Models
|
||||
• Perform UI-friendly formatting and structuring
|
||||
• Are pure and deterministic
|
||||
|
||||
What they must NOT do
|
||||
• Make API calls
|
||||
• Read from localStorage/cookies directly
|
||||
• Contain business rules or decisions
|
||||
• Perform side effects
|
||||
|
||||
Rule
|
||||
|
||||
Presenters output View Models. Presenters never output API DTOs.
|
||||
|
||||
⸻
|
||||
|
||||
6. Do website presenters use View Models?
|
||||
|
||||
Yes. Strictly:
|
||||
|
||||
Website presenters MUST output View Models and MUST NOT output API DTOs.
|
||||
|
||||
Flow is always:
|
||||
|
||||
API DTO -> Presenter -> View Model -> Component
|
||||
|
||||
|
||||
⸻
|
||||
|
||||
7. API client (website) (placement and rules)
|
||||
|
||||
Where it lives
|
||||
|
||||
The API client MUST live in:
|
||||
|
||||
apps/website/lib/api
|
||||
|
||||
What it does
|
||||
• Sends HTTP requests
|
||||
• Returns API DTOs
|
||||
• Performs authentication header/cookie handling only at transport level
|
||||
• Does not map to View Models
|
||||
|
||||
What it must NOT do
|
||||
• Format or reshape responses for UI
|
||||
• Contain business rules
|
||||
• Contain decision logic
|
||||
|
||||
Rule
|
||||
|
||||
The API client has no knowledge of View Models.
|
||||
|
||||
⸻
|
||||
|
||||
8. Website service layer (strict orchestration)
|
||||
|
||||
Where it lives
|
||||
|
||||
Website orchestration MUST live in:
|
||||
|
||||
apps/website/lib/services
|
||||
|
||||
What it does
|
||||
• Calls the API client
|
||||
• Calls presenters to map DTO -> View Model
|
||||
• Returns View Models to pages/components
|
||||
|
||||
What it must NOT do
|
||||
• Contain domain logic
|
||||
• Modify core invariants
|
||||
• Return API DTOs
|
||||
|
||||
Rule
|
||||
|
||||
Services are the only layer allowed to call both api/ and presenters/.
|
||||
|
||||
Components must not call the API client directly.
|
||||
|
||||
⸻
|
||||
|
||||
9. Allowed dependency directions (frontend)
|
||||
|
||||
Within apps/website:
|
||||
|
||||
components -> services -> (api + presenters) -> (dtos + view-models)
|
||||
|
||||
Strict rules:
|
||||
• components may import only view-models and services
|
||||
• presenters may import dtos and view-models only
|
||||
• api may import dtos only
|
||||
• services may import api, presenters, view-models
|
||||
|
||||
Forbidden:
|
||||
• components importing api
|
||||
• components importing dtos
|
||||
• presenters importing api
|
||||
• api importing view-models
|
||||
• any website code importing core domain entities
|
||||
|
||||
⸻
|
||||
|
||||
10. Naming rules (strict)
|
||||
• View Models end with ViewModel
|
||||
• API DTOs end with Dto
|
||||
• Presenters end with Presenter
|
||||
• Services end with Service
|
||||
• One export per file
|
||||
• File name equals exported symbol (PascalCase)
|
||||
|
||||
⸻
|
||||
|
||||
11. Final "no ambiguity" summary
|
||||
• View Models live in apps/website/lib/view-models
|
||||
• API DTOs live in apps/website/lib/dtos
|
||||
• Presenters live in apps/website/lib/presenters and map DTO -> ViewModel
|
||||
• API client lives in apps/website/lib/api and returns DTOs only
|
||||
• Services live in apps/website/lib/services and return View Models only
|
||||
• Components consume View Models only and never touch API DTOs or API clients
|
||||
|
||||
⸻
|
||||
|
||||
12. Clean Architecture Flow Diagram
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[UI Components] --> B[Services]
|
||||
B --> C[API Client]
|
||||
B --> D[Presenters]
|
||||
C --> E[API DTOs]
|
||||
D --> E
|
||||
D --> F[View Models]
|
||||
A --> F
|
||||
|
||||
style A fill:#e1f5fe
|
||||
style B fill:#f3e5f5
|
||||
style C fill:#fff3e0
|
||||
style D fill:#e8f5e8
|
||||
style E fill:#ffebee
|
||||
style F fill:#e3f2fd
|
||||
```
|
||||
|
||||
**Flow Explanation:**
|
||||
- UI Components consume only View Models
|
||||
- Services orchestrate API calls and presenter mappings
|
||||
- API Client returns raw API DTOs
|
||||
- Presenters transform API DTOs into UI-ready View Models
|
||||
- Strict dependency direction: UI → Services → (API + Presenters) → (DTOs + ViewModels)
|
||||
|
||||
⸻
|
||||
|
||||
13. Enforcement Guidelines
|
||||
|
||||
**ESLint Rules:**
|
||||
- Direct imports from `apiClient` are forbidden - use services instead
|
||||
- Direct imports from `dtos` in UI components are forbidden - use ViewModels instead
|
||||
- Direct imports from `api/*` in UI components are forbidden - use services instead
|
||||
|
||||
**TypeScript Path Mappings:**
|
||||
- Use `@/lib/dtos` for API DTO imports
|
||||
- Use `@/lib/view-models` for View Model imports
|
||||
- Use `@/lib/presenters` for Presenter imports
|
||||
- Use `@/lib/services` for Service imports
|
||||
- Use `@/lib/api` for API client imports
|
||||
|
||||
**Import Restrictions:**
|
||||
- Components may import only view-models and services
|
||||
- Presenters may import dtos and view-models only
|
||||
- API may import dtos only
|
||||
- Services may import api, presenters, view-models
|
||||
- Forbidden: components importing api, components importing dtos, presenters importing api, api importing view-models
|
||||
|
||||
**Verification Commands:**
|
||||
```bash
|
||||
npm run build # Ensure TypeScript compiles
|
||||
npm run lint # Ensure ESLint rules pass
|
||||
npm run test # Ensure all tests pass
|
||||
```
|
||||
|
||||
⸻
|
||||
|
||||
14. Architecture Examples
|
||||
|
||||
**Before (Violates Rules):**
|
||||
```typescript
|
||||
// In a page component - BAD
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import type { RaceResultDto } from '@/lib/dtos/RaceResultDto';
|
||||
|
||||
const RacePage = () => {
|
||||
const [data, setData] = useState<RaceResultDto[]>();
|
||||
// Direct API call and DTO usage in UI
|
||||
useEffect(() => {
|
||||
apiClient.getRaceResults().then(setData);
|
||||
}, []);
|
||||
return <div>{data?.map(d => d.position)}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
**After (Clean Architecture):**
|
||||
```typescript
|
||||
// In a page component - GOOD
|
||||
import { RaceResultsService } from '@/lib/services/RaceResultsService';
|
||||
import type { RaceResultViewModel } from '@/lib/view-models/RaceResultViewModel';
|
||||
|
||||
const RacePage = () => {
|
||||
const [data, setData] = useState<RaceResultViewModel[]>();
|
||||
useEffect(() => {
|
||||
RaceResultsService.getResults().then(setData);
|
||||
}, []);
|
||||
return <div>{data?.map(d => d.formattedPosition)}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
**Service Implementation:**
|
||||
```typescript
|
||||
// apps/website/lib/services/RaceResultsService.ts
|
||||
import { apiClient } from '@/lib/api';
|
||||
import { RaceResultsPresenter } from '@/lib/presenters/RaceResultsPresenter';
|
||||
|
||||
export class RaceResultsService {
|
||||
static async getResults(): Promise<RaceResultViewModel[]> {
|
||||
const dtos = await apiClient.getRaceResults();
|
||||
return RaceResultsPresenter.present(dtos);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Presenter Implementation:**
|
||||
```typescript
|
||||
// apps/website/lib/presenters/RaceResultsPresenter.ts
|
||||
import type { RaceResultDto } from '@/lib/dtos/RaceResultDto';
|
||||
import type { RaceResultViewModel } from '@/lib/view-models/RaceResultViewModel';
|
||||
|
||||
export class RaceResultsPresenter {
|
||||
static present(dtos: RaceResultDto[]): RaceResultViewModel[] {
|
||||
return dtos.map(dto => ({
|
||||
id: dto.id,
|
||||
formattedPosition: `${dto.position}${dto.position === 1 ? 'st' : dto.position === 2 ? 'nd' : dto.position === 3 ? 'rd' : 'th'}`,
|
||||
driverName: dto.driverName,
|
||||
// ... other UI-specific formatting
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -27,15 +27,11 @@ import Button from '@/components/ui/Button';
|
||||
import Input from '@/components/ui/Input';
|
||||
import Card from '@/components/ui/Card';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import { DriversLeaderboardPresenter } from '@/lib/presenters/DriversLeaderboardPresenter';
|
||||
import type { DriverLeaderboardItemViewModel, SkillLevel } from '@core/racing/application/presenters/IDriversLeaderboardPresenter';
|
||||
import { getDriverLeaderboard } from '@/lib/services/drivers/DriverService';
|
||||
import type { DriverLeaderboardViewModel } from '@/lib/view-models';
|
||||
import Image from 'next/image';
|
||||
|
||||
// ============================================================================
|
||||
// TYPES
|
||||
// ============================================================================
|
||||
|
||||
type DriverListItem = DriverLeaderboardItemViewModel;
|
||||
import type { DriverLeaderboardItemViewModel } from '@/lib/view-models/DriverLeaderboardItemViewModel';
|
||||
|
||||
// ============================================================================
|
||||
// DEMO DATA
|
||||
@@ -50,7 +46,7 @@ type DriverListItem = DriverLeaderboardItemViewModel;
|
||||
// ============================================================================
|
||||
|
||||
const SKILL_LEVELS: {
|
||||
id: SkillLevel;
|
||||
id: string;
|
||||
label: string;
|
||||
icon: React.ElementType;
|
||||
color: string;
|
||||
@@ -69,7 +65,7 @@ const SKILL_LEVELS: {
|
||||
// ============================================================================
|
||||
|
||||
interface FeaturedDriverCardProps {
|
||||
driver: DriverListItem;
|
||||
driver: DriverLeaderboardItemViewModel;
|
||||
position: number;
|
||||
onClick: () => void;
|
||||
}
|
||||
@@ -118,7 +114,7 @@ function FeaturedDriverCard({ driver, position, onClick }: FeaturedDriverCardPro
|
||||
{/* Avatar & Name */}
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className="relative w-16 h-16 rounded-full overflow-hidden border-2 border-charcoal-outline group-hover:border-primary-blue transition-colors">
|
||||
<Image src={driver.avatarUrl} alt={driver.name} fill className="object-cover" />
|
||||
<Image src={driver.avatarUrl || '/avatars/default.png'} alt={driver.name} fill className="object-cover" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white group-hover:text-primary-blue transition-colors">
|
||||
@@ -155,7 +151,7 @@ function FeaturedDriverCard({ driver, position, onClick }: FeaturedDriverCardPro
|
||||
// ============================================================================
|
||||
|
||||
interface SkillDistributionProps {
|
||||
drivers: DriverListItem[];
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
}
|
||||
|
||||
function SkillDistribution({ drivers }: SkillDistributionProps) {
|
||||
@@ -217,7 +213,7 @@ function SkillDistribution({ drivers }: SkillDistributionProps) {
|
||||
// ============================================================================
|
||||
|
||||
interface LeaderboardPreviewProps {
|
||||
drivers: DriverListItem[];
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
onDriverClick: (id: string) => void;
|
||||
}
|
||||
|
||||
@@ -286,7 +282,7 @@ function LeaderboardPreview({ drivers, onDriverClick }: LeaderboardPreviewProps)
|
||||
|
||||
{/* Avatar */}
|
||||
<div className="relative w-9 h-9 rounded-full overflow-hidden border-2 border-charcoal-outline">
|
||||
<Image src={driver.avatarUrl} alt={driver.name} fill className="object-cover" />
|
||||
<Image src={driver.avatarUrl || '/avatars/default.png'} alt={driver.name} fill className="object-cover" />
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
@@ -326,7 +322,7 @@ function LeaderboardPreview({ drivers, onDriverClick }: LeaderboardPreviewProps)
|
||||
// ============================================================================
|
||||
|
||||
interface RecentActivityProps {
|
||||
drivers: DriverListItem[];
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
onDriverClick: (id: string) => void;
|
||||
}
|
||||
|
||||
@@ -356,7 +352,7 @@ function RecentActivity({ drivers, onDriverClick }: RecentActivityProps) {
|
||||
className="p-3 rounded-xl bg-iron-gray/40 border border-charcoal-outline hover:border-performance-green/40 transition-all group text-center"
|
||||
>
|
||||
<div className="relative w-12 h-12 mx-auto rounded-full overflow-hidden border-2 border-charcoal-outline mb-2">
|
||||
<Image src={driver.avatarUrl} alt={driver.name} fill className="object-cover" />
|
||||
<Image src={driver.avatarUrl || '/avatars/default.png'} alt={driver.name} fill className="object-cover" />
|
||||
<div className="absolute bottom-0 right-0 w-3 h-3 rounded-full bg-performance-green border-2 border-iron-gray" />
|
||||
</div>
|
||||
<p className="text-sm font-medium text-white truncate group-hover:text-performance-green transition-colors">
|
||||
@@ -377,7 +373,8 @@ function RecentActivity({ drivers, onDriverClick }: RecentActivityProps) {
|
||||
|
||||
export default function DriversPage() {
|
||||
const router = useRouter();
|
||||
const [drivers, setDrivers] = useState<DriverListItem[]>([]);
|
||||
const [drivers, setDrivers] = useState<DriverLeaderboardItemViewModel[]>([]);
|
||||
const [viewModel, setViewModel] = useState<DriverLeaderboardViewModel | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [totalRaces, setTotalRaces] = useState(0);
|
||||
@@ -386,17 +383,12 @@ export default function DriversPage() {
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const useCase = getGetDriversLeaderboardUseCase();
|
||||
const presenter = new DriversLeaderboardPresenter();
|
||||
await useCase.execute(undefined as void, presenter);
|
||||
const viewModel = presenter.getViewModel();
|
||||
|
||||
if (viewModel) {
|
||||
setDrivers(viewModel.drivers);
|
||||
setTotalRaces(viewModel.totalRaces);
|
||||
setTotalWins(viewModel.totalWins);
|
||||
setActiveCount(viewModel.activeCount);
|
||||
}
|
||||
const vm = await getDriverLeaderboard();
|
||||
setViewModel(vm);
|
||||
setDrivers(vm.drivers);
|
||||
setTotalRaces(vm.totalRaces);
|
||||
setTotalWins(vm.totalWins);
|
||||
setActiveCount(vm.activeCount);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,70 +4,37 @@ import { useState, useEffect, useCallback } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import Card from '@/components/ui/Card';
|
||||
import StandingsTable from '@/components/leagues/StandingsTable';
|
||||
import {
|
||||
EntityMappers,
|
||||
type DriverDTO,
|
||||
type LeagueDriverSeasonStatsDTO,
|
||||
} from '@core/racing';
|
||||
import { LeagueDriverSeasonStatsPresenter } from '@/lib/presenters/LeagueDriverSeasonStatsPresenter';
|
||||
import { getLeagueStandings } from '@/lib/services/leagues/LeagueService';
|
||||
import { useEffectiveDriverId } from '@/lib/currentDriver';
|
||||
import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles';
|
||||
import type { MembershipRole, LeagueMembership } from '@/lib/leagueMembership';
|
||||
import type { LeagueStandingsViewModel } from '@/lib/view-models';
|
||||
import type { StandingEntryViewModel } from '@/lib/view-models/StandingEntryViewModel';
|
||||
import type { DriverDto } from '@/lib/dtos';
|
||||
import type { LeagueMembership } from '@/lib/dtos';
|
||||
|
||||
export default function LeagueStandingsPage() {
|
||||
const params = useParams();
|
||||
const leagueId = params.id as string;
|
||||
const currentDriverId = useEffectiveDriverId();
|
||||
|
||||
const [standings, setStandings] = useState<LeagueDriverSeasonStatsDTO[]>([]);
|
||||
const [drivers, setDrivers] = useState<DriverDTO[]>([]);
|
||||
const [standings, setStandings] = useState<StandingEntryViewModel[]>([]);
|
||||
const [drivers, setDrivers] = useState<DriverDto[]>([]);
|
||||
const [memberships, setMemberships] = useState<LeagueMembership[]>([]);
|
||||
const [viewModel, setViewModel] = useState<LeagueStandingsViewModel | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
const getLeagueDriverSeasonStatsUseCase = getGetLeagueDriverSeasonStatsUseCase();
|
||||
const driverRepo = getDriverRepository();
|
||||
const membershipRepo = getLeagueMembershipRepository();
|
||||
|
||||
const presenter = new LeagueDriverSeasonStatsPresenter();
|
||||
await getLeagueDriverSeasonStatsUseCase.execute({ leagueId }, presenter);
|
||||
const standingsViewModel = presenter.getViewModel();
|
||||
setStandings(standingsViewModel.stats);
|
||||
|
||||
const allDrivers = await driverRepo.findAll();
|
||||
const driverDtos: DriverDTO[] = allDrivers
|
||||
.map((driver) => EntityMappers.toDriverDTO(driver))
|
||||
.filter((dto): dto is DriverDTO => dto !== null);
|
||||
setDrivers(driverDtos);
|
||||
|
||||
// Load league memberships from repository (consistent with other data)
|
||||
const allMemberships = await membershipRepo.getLeagueMembers(leagueId);
|
||||
|
||||
type RawMembership = {
|
||||
id: string | number;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
role: MembershipRole;
|
||||
status: LeagueMembership['status'];
|
||||
joinedAt: string | Date;
|
||||
};
|
||||
|
||||
// Convert to the format expected by StandingsTable (website-level LeagueMembership)
|
||||
const membershipData: LeagueMembership[] = (allMemberships as RawMembership[]).map((m) => ({
|
||||
id: String(m.id),
|
||||
leagueId: m.leagueId,
|
||||
driverId: m.driverId,
|
||||
role: m.role,
|
||||
status: m.status,
|
||||
joinedAt: m.joinedAt instanceof Date ? m.joinedAt.toISOString() : String(m.joinedAt),
|
||||
}));
|
||||
setMemberships(membershipData);
|
||||
const vm = await getLeagueStandings(leagueId, currentDriverId);
|
||||
setViewModel(vm);
|
||||
setStandings(vm.standings);
|
||||
setDrivers(vm.drivers);
|
||||
setMemberships(vm.memberships);
|
||||
|
||||
// Check if current user is admin
|
||||
const membership = await membershipRepo.getMembership(leagueId, currentDriverId);
|
||||
const membership = vm.memberships.find(m => m.driverId === currentDriverId);
|
||||
setIsAdmin(membership ? isLeagueAdminOrHigherRole(membership.role) : false);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load standings');
|
||||
@@ -151,7 +118,7 @@ export default function LeagueStandingsPage() {
|
||||
}
|
||||
|
||||
const leader = standings[0];
|
||||
const totalRaces = Math.max(...standings.map(s => s.racesStarted), 0);
|
||||
const totalRaces = Math.max(...standings.map(s => s.races), 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -166,7 +133,7 @@ export default function LeagueStandingsPage() {
|
||||
<div>
|
||||
<p className="text-xs text-gray-400 mb-1">Championship Leader</p>
|
||||
<p className="font-bold text-white">{drivers.find(d => d.id === leader?.driverId)?.name || 'N/A'}</p>
|
||||
<p className="text-sm text-yellow-400 font-medium">{leader?.totalPoints || 0} points</p>
|
||||
<p className="text-sm text-yellow-400 font-medium">{leader?.points || 0} points</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -9,51 +9,10 @@ import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
||||
import ResultsTable from '@/components/races/ResultsTable';
|
||||
import ImportResultsForm from '@/components/races/ImportResultsForm';
|
||||
import QuickPenaltyModal from '@/components/leagues/QuickPenaltyModal';
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import { getRaceResults, getRaceSOF, importRaceResults } from '@/lib/services/races/RaceResultsService';
|
||||
import { useEffectiveDriverId } from '@/lib/currentDriver';
|
||||
import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles';
|
||||
import type { RaceResultsDetailViewModel, RaceWithSOFViewModel } from '@/lib/apiClient';
|
||||
|
||||
type PenaltyTypeDTO =
|
||||
| 'time_penalty'
|
||||
| 'grid_penalty'
|
||||
| 'points_deduction'
|
||||
| 'disqualification'
|
||||
| 'warning'
|
||||
| 'license_points'
|
||||
| string;
|
||||
|
||||
interface PenaltyData {
|
||||
driverId: string;
|
||||
type: PenaltyTypeDTO;
|
||||
value?: number;
|
||||
}
|
||||
|
||||
interface RaceResultRowDTO {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
fastestLap: number;
|
||||
incidents: number;
|
||||
startPosition: number;
|
||||
getPositionChange(): number;
|
||||
}
|
||||
|
||||
interface DriverRowDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ImportResultRowDTO {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
fastestLap: number;
|
||||
incidents: number;
|
||||
startPosition: number;
|
||||
}
|
||||
import type { RaceResultsDetailViewModel } from '@/lib/view-models';
|
||||
|
||||
export default function RaceResultsPage() {
|
||||
const router = useRouter();
|
||||
@@ -69,16 +28,16 @@ export default function RaceResultsPage() {
|
||||
const [importSuccess, setImportSuccess] = useState(false);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [showQuickPenaltyModal, setShowQuickPenaltyModal] = useState(false);
|
||||
const [preSelectedDriver, setPreSelectedDriver] = useState<DriverRowDTO | undefined>(undefined);
|
||||
const [preSelectedDriver, setPreSelectedDriver] = useState<{ id: string; name: string } | undefined>(undefined);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const raceData = await apiClient.races.getResultsDetail(raceId);
|
||||
const raceData = await getRaceResults(raceId, currentDriverId);
|
||||
setRaceData(raceData);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const sofData = await apiClient.races.getWithSOF(raceId);
|
||||
const sofData = await getRaceSOF(raceId);
|
||||
setRaceSOF(sofData.strengthOfField);
|
||||
} catch (sofErr) {
|
||||
console.error('Failed to load SOF:', sofErr);
|
||||
@@ -106,12 +65,12 @@ export default function RaceResultsPage() {
|
||||
}
|
||||
}, [raceData?.league?.id, currentDriverId]);
|
||||
|
||||
const handleImportSuccess = async (importedResults: ImportResultRowDTO[]) => {
|
||||
const handleImportSuccess = async (importedResults: any[]) => {
|
||||
setImporting(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await apiClient.races.importResults(raceId, {
|
||||
await importRaceResults(raceId, {
|
||||
resultsFileContent: JSON.stringify(importedResults), // Assuming the API expects JSON string
|
||||
});
|
||||
|
||||
@@ -128,7 +87,7 @@ export default function RaceResultsPage() {
|
||||
setError(errorMessage);
|
||||
};
|
||||
|
||||
const handlePenaltyClick = (driver: DriverRowDTO) => {
|
||||
const handlePenaltyClick = (driver: { id: string; name: string }) => {
|
||||
setPreSelectedDriver(driver);
|
||||
setShowQuickPenaltyModal(true);
|
||||
};
|
||||
@@ -241,7 +200,7 @@ export default function RaceResultsPage() {
|
||||
</span>
|
||||
<span className="flex items-center gap-2">
|
||||
<Users className="w-4 h-4" />
|
||||
{raceData.results.length} drivers classified
|
||||
{raceData.stats.totalDrivers} drivers classified
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
@@ -265,11 +224,11 @@ export default function RaceResultsPage() {
|
||||
<Card>
|
||||
{hasResults && raceData ? (
|
||||
<ResultsTable
|
||||
results={raceData.results as any}
|
||||
drivers={raceData.drivers as any}
|
||||
results={raceData.resultsByPosition}
|
||||
drivers={raceData.drivers}
|
||||
pointsSystem={raceData.pointsSystem ?? {}}
|
||||
fastestLapTime={raceData.fastestLapTime ?? 0}
|
||||
penalties={raceData.penalties as any}
|
||||
penalties={raceData.penalties}
|
||||
currentDriverId={raceData.currentDriverId ?? ''}
|
||||
isAdmin={isAdmin}
|
||||
onPenaltyClick={handlePenaltyClick}
|
||||
|
||||
24
apps/website/lib/api/analytics/AnalyticsApiClient.ts
Normal file
24
apps/website/lib/api/analytics/AnalyticsApiClient.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RecordPageViewInputDto,
|
||||
RecordPageViewOutputDto,
|
||||
RecordEngagementInputDto,
|
||||
RecordEngagementOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Analytics API Client
|
||||
*
|
||||
* Handles all analytics-related API operations.
|
||||
*/
|
||||
export class AnalyticsApiClient extends BaseApiClient {
|
||||
/** Record a page view */
|
||||
recordPageView(input: RecordPageViewInputDto): Promise<RecordPageViewOutputDto> {
|
||||
return this.post<RecordPageViewOutputDto>('/analytics/page-view', input);
|
||||
}
|
||||
|
||||
/** Record an engagement event */
|
||||
recordEngagement(input: RecordEngagementInputDto): Promise<RecordEngagementOutputDto> {
|
||||
return this.post<RecordEngagementOutputDto>('/analytics/engagement', input);
|
||||
}
|
||||
}
|
||||
40
apps/website/lib/api/auth/AuthApiClient.ts
Normal file
40
apps/website/lib/api/auth/AuthApiClient.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
LoginParamsDto,
|
||||
SignupParamsDto,
|
||||
SessionDataDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Auth API Client
|
||||
*
|
||||
* Handles all authentication-related API operations.
|
||||
*/
|
||||
export class AuthApiClient extends BaseApiClient {
|
||||
/** Sign up with email */
|
||||
signup(params: SignupParamsDto): Promise<SessionDataDto> {
|
||||
return this.post<SessionDataDto>('/auth/signup', params);
|
||||
}
|
||||
|
||||
/** Login with email */
|
||||
login(params: LoginParamsDto): Promise<SessionDataDto> {
|
||||
return this.post<SessionDataDto>('/auth/login', params);
|
||||
}
|
||||
|
||||
/** Get current session */
|
||||
getSession(): Promise<SessionDataDto | null> {
|
||||
return this.get<SessionDataDto | 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}`;
|
||||
}
|
||||
}
|
||||
68
apps/website/lib/api/base/BaseApiClient.ts
Normal file
68
apps/website/lib/api/base/BaseApiClient.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Base API Client for HTTP operations
|
||||
*
|
||||
* Provides generic HTTP methods with common request/response handling,
|
||||
* error handling, and authentication.
|
||||
*/
|
||||
|
||||
export 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);
|
||||
}
|
||||
}
|
||||
29
apps/website/lib/api/drivers/DriversApiClient.ts
Normal file
29
apps/website/lib/api/drivers/DriversApiClient.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
DriversLeaderboardDto,
|
||||
CompleteOnboardingInputDto,
|
||||
CompleteOnboardingOutputDto,
|
||||
DriverDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Drivers API Client
|
||||
*
|
||||
* Handles all driver-related API operations.
|
||||
*/
|
||||
export class DriversApiClient extends BaseApiClient {
|
||||
/** Get drivers leaderboard */
|
||||
getLeaderboard(): Promise<DriversLeaderboardDto> {
|
||||
return this.get<DriversLeaderboardDto>('/drivers/leaderboard');
|
||||
}
|
||||
|
||||
/** Complete driver onboarding */
|
||||
completeOnboarding(input: CompleteOnboardingInputDto): Promise<CompleteOnboardingOutputDto> {
|
||||
return this.post<CompleteOnboardingOutputDto>('/drivers/complete-onboarding', input);
|
||||
}
|
||||
|
||||
/** Get current driver (based on session) */
|
||||
getCurrent(): Promise<DriverDto | null> {
|
||||
return this.get<DriverDto | null>('/drivers/current');
|
||||
}
|
||||
}
|
||||
46
apps/website/lib/api/index.ts
Normal file
46
apps/website/lib/api/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { LeaguesApiClient } from './leagues/LeaguesApiClient';
|
||||
import { RacesApiClient } from './races/RacesApiClient';
|
||||
import { DriversApiClient } from './drivers/DriversApiClient';
|
||||
import { TeamsApiClient } from './teams/TeamsApiClient';
|
||||
import { SponsorsApiClient } from './sponsors/SponsorsApiClient';
|
||||
import { MediaApiClient } from './media/MediaApiClient';
|
||||
import { AnalyticsApiClient } from './analytics/AnalyticsApiClient';
|
||||
import { AuthApiClient } from './auth/AuthApiClient';
|
||||
import { PaymentsApiClient } from './payments/PaymentsApiClient';
|
||||
|
||||
/**
|
||||
* Main API Client
|
||||
*
|
||||
* Orchestrates all domain-specific API clients with consistent configuration.
|
||||
*/
|
||||
export class ApiClient {
|
||||
public readonly leagues: LeaguesApiClient;
|
||||
public readonly races: RacesApiClient;
|
||||
public readonly drivers: DriversApiClient;
|
||||
public readonly teams: TeamsApiClient;
|
||||
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.races = new RacesApiClient(baseUrl);
|
||||
this.drivers = new DriversApiClient(baseUrl);
|
||||
this.teams = new TeamsApiClient(baseUrl);
|
||||
this.sponsors = new SponsorsApiClient(baseUrl);
|
||||
this.media = new MediaApiClient(baseUrl);
|
||||
this.analytics = new AnalyticsApiClient(baseUrl);
|
||||
this.auth = new AuthApiClient(baseUrl);
|
||||
this.payments = new PaymentsApiClient(baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Singleton Instance
|
||||
// ============================================================================
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
|
||||
|
||||
export const api = new ApiClient(API_BASE_URL);
|
||||
52
apps/website/lib/api/leagues/LeaguesApiClient.ts
Normal file
52
apps/website/lib/api/leagues/LeaguesApiClient.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
AllLeaguesWithCapacityDto,
|
||||
LeagueStatsDto,
|
||||
LeagueStandingsDto,
|
||||
LeagueScheduleDto,
|
||||
LeagueMembershipsDto,
|
||||
CreateLeagueInputDto,
|
||||
CreateLeagueOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Leagues API Client
|
||||
*
|
||||
* Handles all league-related API operations.
|
||||
*/
|
||||
export class LeaguesApiClient extends BaseApiClient {
|
||||
/** Get all leagues with capacity information */
|
||||
getAllWithCapacity(): Promise<AllLeaguesWithCapacityDto> {
|
||||
return this.get<AllLeaguesWithCapacityDto>('/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<LeagueStandingsDto> {
|
||||
return this.get<LeagueStandingsDto>(`/leagues/${leagueId}/standings`);
|
||||
}
|
||||
|
||||
/** Get league schedule */
|
||||
getSchedule(leagueId: string): Promise<LeagueScheduleDto> {
|
||||
return this.get<LeagueScheduleDto>(`/leagues/${leagueId}/schedule`);
|
||||
}
|
||||
|
||||
/** Get league memberships */
|
||||
getMemberships(leagueId: string): Promise<LeagueMembershipsDto> {
|
||||
return this.get<LeagueMembershipsDto>(`/leagues/${leagueId}/memberships`);
|
||||
}
|
||||
|
||||
/** Create a new league */
|
||||
create(input: CreateLeagueInputDto): Promise<CreateLeagueOutputDto> {
|
||||
return this.post<CreateLeagueOutputDto>('/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 });
|
||||
}
|
||||
}
|
||||
17
apps/website/lib/api/media/MediaApiClient.ts
Normal file
17
apps/website/lib/api/media/MediaApiClient.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RequestAvatarGenerationInputDto,
|
||||
RequestAvatarGenerationOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Media API Client
|
||||
*
|
||||
* Handles all media-related API operations.
|
||||
*/
|
||||
export class MediaApiClient extends BaseApiClient {
|
||||
/** Request avatar generation */
|
||||
requestAvatarGeneration(input: RequestAvatarGenerationInputDto): Promise<RequestAvatarGenerationOutputDto> {
|
||||
return this.post<RequestAvatarGenerationOutputDto>('/media/avatar/generate', input);
|
||||
}
|
||||
}
|
||||
49
apps/website/lib/api/payments/PaymentsApiClient.ts
Normal file
49
apps/website/lib/api/payments/PaymentsApiClient.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
GetPaymentsOutputDto,
|
||||
CreatePaymentInputDto,
|
||||
CreatePaymentOutputDto,
|
||||
GetMembershipFeesOutputDto,
|
||||
GetPrizesOutputDto,
|
||||
GetWalletOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Payments API Client
|
||||
*
|
||||
* Handles all payment-related API operations.
|
||||
*/
|
||||
export class PaymentsApiClient extends BaseApiClient {
|
||||
/** Get payments */
|
||||
getPayments(leagueId?: string, driverId?: string): 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}` : ''}`);
|
||||
}
|
||||
|
||||
/** Create a payment */
|
||||
createPayment(input: CreatePaymentInputDto): Promise<CreatePaymentOutputDto> {
|
||||
return this.post<CreatePaymentOutputDto>('/payments', input);
|
||||
}
|
||||
|
||||
/** Get membership fees */
|
||||
getMembershipFees(leagueId: string): Promise<GetMembershipFeesOutputDto> {
|
||||
return this.get<GetMembershipFeesOutputDto>(`/payments/membership-fees?leagueId=${leagueId}`);
|
||||
}
|
||||
|
||||
/** Get prizes */
|
||||
getPrizes(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}` : ''}`);
|
||||
}
|
||||
|
||||
/** Get wallet */
|
||||
getWallet(driverId: string): Promise<GetWalletOutputDto> {
|
||||
return this.get<GetWalletOutputDto>(`/payments/wallets?driverId=${driverId}`);
|
||||
}
|
||||
}
|
||||
53
apps/website/lib/api/races/RacesApiClient.ts
Normal file
53
apps/website/lib/api/races/RacesApiClient.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RaceStatsDto,
|
||||
RacesPageDataDto,
|
||||
RaceDetailDto,
|
||||
RaceResultsDetailDto,
|
||||
RaceWithSOFDto,
|
||||
RegisterForRaceInputDto,
|
||||
ImportRaceResultsInputDto,
|
||||
ImportRaceResultsSummaryDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Races API Client
|
||||
*
|
||||
* Handles all race-related API operations.
|
||||
*/
|
||||
export class RacesApiClient extends BaseApiClient {
|
||||
/** Get total number of 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');
|
||||
}
|
||||
|
||||
/** Get race detail */
|
||||
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`);
|
||||
}
|
||||
|
||||
/** Get race with strength of field */
|
||||
getWithSOF(raceId: string): Promise<RaceWithSOFDto> {
|
||||
return this.get<RaceWithSOFDto>(`/races/${raceId}/sof`);
|
||||
}
|
||||
|
||||
/** Register for race */
|
||||
register(raceId: string, input: RegisterForRaceInputDto): 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);
|
||||
}
|
||||
}
|
||||
41
apps/website/lib/api/sponsors/SponsorsApiClient.ts
Normal file
41
apps/website/lib/api/sponsors/SponsorsApiClient.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
GetEntitySponsorshipPricingResultDto,
|
||||
GetSponsorsOutputDto,
|
||||
CreateSponsorInputDto,
|
||||
CreateSponsorOutputDto,
|
||||
SponsorDashboardDto,
|
||||
SponsorSponsorshipsDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Sponsors API Client
|
||||
*
|
||||
* Handles all sponsor-related API operations.
|
||||
*/
|
||||
export class SponsorsApiClient extends BaseApiClient {
|
||||
/** Get sponsorship pricing */
|
||||
getPricing(): Promise<GetEntitySponsorshipPricingResultDto> {
|
||||
return this.get<GetEntitySponsorshipPricingResultDto>('/sponsors/pricing');
|
||||
}
|
||||
|
||||
/** Get all sponsors */
|
||||
getAll(): Promise<GetSponsorsOutputDto> {
|
||||
return this.get<GetSponsorsOutputDto>('/sponsors');
|
||||
}
|
||||
|
||||
/** Create a new sponsor */
|
||||
create(input: CreateSponsorInputDto): Promise<CreateSponsorOutputDto> {
|
||||
return this.post<CreateSponsorOutputDto>('/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`);
|
||||
}
|
||||
}
|
||||
54
apps/website/lib/api/teams/TeamsApiClient.ts
Normal file
54
apps/website/lib/api/teams/TeamsApiClient.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
AllTeamsDto,
|
||||
TeamDetailsDto,
|
||||
TeamMembersDto,
|
||||
TeamJoinRequestsDto,
|
||||
CreateTeamInputDto,
|
||||
CreateTeamOutputDto,
|
||||
UpdateTeamInputDto,
|
||||
UpdateTeamOutputDto,
|
||||
DriverTeamDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Teams API Client
|
||||
*
|
||||
* Handles all team-related API operations.
|
||||
*/
|
||||
export class TeamsApiClient extends BaseApiClient {
|
||||
/** Get all teams */
|
||||
getAll(): Promise<AllTeamsDto> {
|
||||
return this.get<AllTeamsDto>('/teams/all');
|
||||
}
|
||||
|
||||
/** Get team details */
|
||||
getDetails(teamId: string): Promise<TeamDetailsDto | null> {
|
||||
return this.get<TeamDetailsDto | null>(`/teams/${teamId}`);
|
||||
}
|
||||
|
||||
/** Get team members */
|
||||
getMembers(teamId: string): Promise<TeamMembersDto> {
|
||||
return this.get<TeamMembersDto>(`/teams/${teamId}/members`);
|
||||
}
|
||||
|
||||
/** Get team join requests */
|
||||
getJoinRequests(teamId: string): Promise<TeamJoinRequestsDto> {
|
||||
return this.get<TeamJoinRequestsDto>(`/teams/${teamId}/join-requests`);
|
||||
}
|
||||
|
||||
/** Create a new team */
|
||||
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);
|
||||
}
|
||||
|
||||
/** Get driver's team */
|
||||
getDriverTeam(driverId: string): Promise<DriverTeamDto | null> {
|
||||
return this.get<DriverTeamDto | null>(`/teams/driver/${driverId}`);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
9
apps/website/lib/dtos/AllLeaguesWithCapacityDto.ts
Normal file
9
apps/website/lib/dtos/AllLeaguesWithCapacityDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { LeagueSummaryDto } from './LeagueSummaryDto';
|
||||
|
||||
/**
|
||||
* All leagues with capacity transport object
|
||||
* Contains a list of leagues with their capacity information
|
||||
*/
|
||||
export interface AllLeaguesWithCapacityDto {
|
||||
leagues: LeagueSummaryDto[];
|
||||
}
|
||||
9
apps/website/lib/dtos/AllRacesPageDto.ts
Normal file
9
apps/website/lib/dtos/AllRacesPageDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { RaceListItemDto } from './RaceListItemDto';
|
||||
|
||||
/**
|
||||
* All races page data transfer object
|
||||
* List of all races for the races page
|
||||
*/
|
||||
export interface AllRacesPageDto {
|
||||
races: RaceListItemDto[];
|
||||
}
|
||||
9
apps/website/lib/dtos/AllTeamsDto.ts
Normal file
9
apps/website/lib/dtos/AllTeamsDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { TeamSummaryDto } from './TeamSummaryDto';
|
||||
|
||||
/**
|
||||
* All teams data transfer object
|
||||
* List of all teams
|
||||
*/
|
||||
export interface AllTeamsDto {
|
||||
teams: TeamSummaryDto[];
|
||||
}
|
||||
8
apps/website/lib/dtos/CompleteOnboardingInputDto.ts
Normal file
8
apps/website/lib/dtos/CompleteOnboardingInputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Complete onboarding input data transfer object
|
||||
* Input for completing driver onboarding
|
||||
*/
|
||||
export interface CompleteOnboardingInputDto {
|
||||
iracingId: string;
|
||||
displayName: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/CompleteOnboardingOutputDto.ts
Normal file
8
apps/website/lib/dtos/CompleteOnboardingOutputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Complete onboarding output data transfer object
|
||||
* Output from completing driver onboarding
|
||||
*/
|
||||
export interface CompleteOnboardingOutputDto {
|
||||
driverId: string;
|
||||
success: boolean;
|
||||
}
|
||||
11
apps/website/lib/dtos/CreateLeagueInputDto.ts
Normal file
11
apps/website/lib/dtos/CreateLeagueInputDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Create league input transport object
|
||||
* Input data for creating a new league
|
||||
*/
|
||||
export interface CreateLeagueInputDto {
|
||||
name: string;
|
||||
description?: string;
|
||||
isPublic: boolean;
|
||||
maxMembers: number;
|
||||
ownerId: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/CreateLeagueOutputDto.ts
Normal file
8
apps/website/lib/dtos/CreateLeagueOutputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Create league output transport object
|
||||
* Output data after creating a new league
|
||||
*/
|
||||
export interface CreateLeagueOutputDto {
|
||||
leagueId: string;
|
||||
success: boolean;
|
||||
}
|
||||
11
apps/website/lib/dtos/CreatePaymentInputDto.ts
Normal file
11
apps/website/lib/dtos/CreatePaymentInputDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Create payment input data transfer object
|
||||
* Input for creating a payment
|
||||
*/
|
||||
export interface CreatePaymentInputDto {
|
||||
amount: number;
|
||||
currency: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
description?: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/CreatePaymentOutputDto.ts
Normal file
8
apps/website/lib/dtos/CreatePaymentOutputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Create payment output data transfer object
|
||||
* Output from creating a payment
|
||||
*/
|
||||
export interface CreatePaymentOutputDto {
|
||||
paymentId: string;
|
||||
success: boolean;
|
||||
}
|
||||
10
apps/website/lib/dtos/CreateSponsorInputDto.ts
Normal file
10
apps/website/lib/dtos/CreateSponsorInputDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Create sponsor input data transfer object
|
||||
* Input for creating a new sponsor
|
||||
*/
|
||||
export interface CreateSponsorInputDto {
|
||||
name: string;
|
||||
logoUrl?: string;
|
||||
websiteUrl?: string;
|
||||
userId: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/CreateSponsorOutputDto.ts
Normal file
8
apps/website/lib/dtos/CreateSponsorOutputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Create sponsor output data transfer object
|
||||
* Output from creating a sponsor
|
||||
*/
|
||||
export interface CreateSponsorOutputDto {
|
||||
sponsorId: string;
|
||||
success: boolean;
|
||||
}
|
||||
9
apps/website/lib/dtos/CreateTeamInputDto.ts
Normal file
9
apps/website/lib/dtos/CreateTeamInputDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Create team input data transfer object
|
||||
* Input for creating a new team
|
||||
*/
|
||||
export interface CreateTeamInputDto {
|
||||
name: string;
|
||||
description?: string;
|
||||
ownerId: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/CreateTeamOutputDto.ts
Normal file
8
apps/website/lib/dtos/CreateTeamOutputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Create team output data transfer object
|
||||
* Output from creating a team
|
||||
*/
|
||||
export interface CreateTeamOutputDto {
|
||||
teamId: string;
|
||||
success: boolean;
|
||||
}
|
||||
11
apps/website/lib/dtos/DriverDto.ts
Normal file
11
apps/website/lib/dtos/DriverDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Driver transport object
|
||||
* Represents a driver as received from the API
|
||||
*/
|
||||
export interface DriverDto {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
iracingId?: string;
|
||||
rating?: number;
|
||||
}
|
||||
16
apps/website/lib/dtos/DriverLeaderboardItemDto.ts
Normal file
16
apps/website/lib/dtos/DriverLeaderboardItemDto.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Driver leaderboard item data transfer object
|
||||
* Represents a driver in the global leaderboard
|
||||
*/
|
||||
export interface DriverLeaderboardItemDto {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
rating: number;
|
||||
wins: number;
|
||||
races: number;
|
||||
skillLevel: string;
|
||||
isActive: boolean;
|
||||
nationality: string;
|
||||
podiums: number;
|
||||
}
|
||||
9
apps/website/lib/dtos/DriverRegistrationStatusDto.ts
Normal file
9
apps/website/lib/dtos/DriverRegistrationStatusDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Driver registration status data transfer object
|
||||
* Represents a driver's registration status for a race
|
||||
*/
|
||||
export interface DriverRegistrationStatusDto {
|
||||
isRegistered: boolean;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/DriverRowDto.ts
Normal file
8
apps/website/lib/dtos/DriverRowDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Driver row data transfer object
|
||||
* Represents a driver in a table row
|
||||
*/
|
||||
export interface DriverRowDto {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
7
apps/website/lib/dtos/DriverStatsDto.ts
Normal file
7
apps/website/lib/dtos/DriverStatsDto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Driver stats data transfer object
|
||||
* Global driver statistics
|
||||
*/
|
||||
export interface DriverStatsDto {
|
||||
totalDrivers: number;
|
||||
}
|
||||
10
apps/website/lib/dtos/DriverTeamDto.ts
Normal file
10
apps/website/lib/dtos/DriverTeamDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Driver team data transfer object
|
||||
* Represents a driver's team membership
|
||||
*/
|
||||
export interface DriverTeamDto {
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
role: string;
|
||||
joinedAt: Date;
|
||||
}
|
||||
9
apps/website/lib/dtos/DriversLeaderboardDto.ts
Normal file
9
apps/website/lib/dtos/DriversLeaderboardDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { DriverLeaderboardItemDto } from './DriverLeaderboardItemDto';
|
||||
|
||||
/**
|
||||
* Drivers leaderboard data transfer object
|
||||
* Contains the list of top drivers
|
||||
*/
|
||||
export interface DriversLeaderboardDto {
|
||||
drivers: DriverLeaderboardItemDto[];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Get entity sponsorship pricing result data transfer object
|
||||
* Pricing information for sponsorship slots
|
||||
*/
|
||||
export interface GetEntitySponsorshipPricingResultDto {
|
||||
mainSlotPrice: number;
|
||||
secondarySlotPrice: number;
|
||||
currency: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/GetMembershipFeesOutputDto.ts
Normal file
11
apps/website/lib/dtos/GetMembershipFeesOutputDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { MembershipFeeDto } from './MembershipFeeDto';
|
||||
import type { MemberPaymentDto } from './MemberPaymentDto';
|
||||
|
||||
/**
|
||||
* Get membership fees output data transfer object
|
||||
* Output containing membership fees and payments
|
||||
*/
|
||||
export interface GetMembershipFeesOutputDto {
|
||||
fees: MembershipFeeDto[];
|
||||
memberPayments: MemberPaymentDto[];
|
||||
}
|
||||
9
apps/website/lib/dtos/GetPaymentsOutputDto.ts
Normal file
9
apps/website/lib/dtos/GetPaymentsOutputDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { PaymentDto } from './PaymentDto';
|
||||
|
||||
/**
|
||||
* Get payments output data transfer object
|
||||
* Output containing list of payments
|
||||
*/
|
||||
export interface GetPaymentsOutputDto {
|
||||
payments: PaymentDto[];
|
||||
}
|
||||
9
apps/website/lib/dtos/GetPrizesOutputDto.ts
Normal file
9
apps/website/lib/dtos/GetPrizesOutputDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { PrizeDto } from './PrizeDto';
|
||||
|
||||
/**
|
||||
* Get prizes output data transfer object
|
||||
* Output containing list of prizes
|
||||
*/
|
||||
export interface GetPrizesOutputDto {
|
||||
prizes: PrizeDto[];
|
||||
}
|
||||
9
apps/website/lib/dtos/GetSponsorsOutputDto.ts
Normal file
9
apps/website/lib/dtos/GetSponsorsOutputDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { SponsorDto } from './SponsorDto';
|
||||
|
||||
/**
|
||||
* Get sponsors output data transfer object
|
||||
* Output containing list of sponsors
|
||||
*/
|
||||
export interface GetSponsorsOutputDto {
|
||||
sponsors: SponsorDto[];
|
||||
}
|
||||
9
apps/website/lib/dtos/GetWalletOutputDto.ts
Normal file
9
apps/website/lib/dtos/GetWalletOutputDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { WalletDto } from './WalletDto';
|
||||
|
||||
/**
|
||||
* Get wallet output data transfer object
|
||||
* Output containing wallet information
|
||||
*/
|
||||
export interface GetWalletOutputDto {
|
||||
wallet: WalletDto;
|
||||
}
|
||||
7
apps/website/lib/dtos/ImportRaceResultsInputDto.ts
Normal file
7
apps/website/lib/dtos/ImportRaceResultsInputDto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Import race results input data transfer object
|
||||
* Input for importing race results
|
||||
*/
|
||||
export interface ImportRaceResultsInputDto {
|
||||
resultsFileContent: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/ImportRaceResultsSummaryDto.ts
Normal file
11
apps/website/lib/dtos/ImportRaceResultsSummaryDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Import race results summary data transfer object
|
||||
* Summary of race results import operation
|
||||
*/
|
||||
export interface ImportRaceResultsSummaryDto {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
}
|
||||
13
apps/website/lib/dtos/ImportResultRowDto.ts
Normal file
13
apps/website/lib/dtos/ImportResultRowDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Import result row data transfer object
|
||||
* Represents a row in imported race results
|
||||
*/
|
||||
export interface ImportResultRowDto {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
fastestLap: number;
|
||||
incidents: number;
|
||||
startPosition: number;
|
||||
}
|
||||
13
apps/website/lib/dtos/LeagueAdminDto.ts
Normal file
13
apps/website/lib/dtos/LeagueAdminDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { LeagueMemberDto } from './LeagueMemberDto';
|
||||
import type { LeagueJoinRequestDto } from './LeagueJoinRequestDto';
|
||||
import type { LeagueConfigFormModelDto } from './LeagueConfigFormModelDto';
|
||||
|
||||
/**
|
||||
* League admin transport object
|
||||
* Contains all data needed for league administration
|
||||
*/
|
||||
export interface LeagueAdminDto {
|
||||
config: LeagueConfigFormModelDto;
|
||||
members: LeagueMemberDto[];
|
||||
joinRequests: LeagueJoinRequestDto[];
|
||||
}
|
||||
12
apps/website/lib/dtos/LeagueAdminPermissionsDto.ts
Normal file
12
apps/website/lib/dtos/LeagueAdminPermissionsDto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* League admin permissions transport object
|
||||
* Defines the administrative permissions for a user in a league
|
||||
*/
|
||||
export interface LeagueAdminPermissionsDto {
|
||||
canManageMembers: boolean;
|
||||
canManageRaces: boolean;
|
||||
canManageSettings: boolean;
|
||||
canManageProtests: boolean;
|
||||
isOwner: boolean;
|
||||
isAdmin: boolean;
|
||||
}
|
||||
9
apps/website/lib/dtos/LeagueAdminProtestsDto.ts
Normal file
9
apps/website/lib/dtos/LeagueAdminProtestsDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { ProtestViewModel } from '../apiClient';
|
||||
|
||||
/**
|
||||
* League admin protests transport object
|
||||
* Contains protests for league administration
|
||||
*/
|
||||
export interface LeagueAdminProtestsDto {
|
||||
protests: ProtestViewModel[];
|
||||
}
|
||||
12
apps/website/lib/dtos/LeagueConfigFormModelDto.ts
Normal file
12
apps/website/lib/dtos/LeagueConfigFormModelDto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* League configuration form model transport object
|
||||
* Used for league configuration forms
|
||||
*/
|
||||
export interface LeagueConfigFormModelDto {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
isPublic: boolean;
|
||||
maxMembers: number;
|
||||
// Add other config fields as needed
|
||||
}
|
||||
11
apps/website/lib/dtos/LeagueJoinRequestDto.ts
Normal file
11
apps/website/lib/dtos/LeagueJoinRequestDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* League join request transport object
|
||||
* Represents a driver's request to join a league
|
||||
*/
|
||||
export interface LeagueJoinRequestDto {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
requestedAt: Date;
|
||||
message?: string;
|
||||
}
|
||||
12
apps/website/lib/dtos/LeagueMemberDto.ts
Normal file
12
apps/website/lib/dtos/LeagueMemberDto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { DriverDto } from './DriverDto';
|
||||
|
||||
/**
|
||||
* League member data transfer object
|
||||
* Represents a driver's membership in a league
|
||||
*/
|
||||
export interface LeagueMemberDto {
|
||||
driverId: string;
|
||||
driver?: DriverDto;
|
||||
role: string;
|
||||
joinedAt: string;
|
||||
}
|
||||
9
apps/website/lib/dtos/LeagueMembershipsDto.ts
Normal file
9
apps/website/lib/dtos/LeagueMembershipsDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { LeagueMemberViewModel } from '../apiClient';
|
||||
|
||||
/**
|
||||
* League memberships transport object
|
||||
* Contains the list of league members
|
||||
*/
|
||||
export interface LeagueMembershipsDto {
|
||||
members: LeagueMemberViewModel[];
|
||||
}
|
||||
10
apps/website/lib/dtos/LeagueOwnerSummaryDto.ts
Normal file
10
apps/website/lib/dtos/LeagueOwnerSummaryDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* League owner summary transport object
|
||||
* Summary information for league owners
|
||||
*/
|
||||
export interface LeagueOwnerSummaryDto {
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
memberCount: number;
|
||||
pendingRequests: number;
|
||||
}
|
||||
9
apps/website/lib/dtos/LeagueScheduleDto.ts
Normal file
9
apps/website/lib/dtos/LeagueScheduleDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { ScheduledRaceViewModel } from '../apiClient';
|
||||
|
||||
/**
|
||||
* League schedule transport object
|
||||
* Contains the scheduled races for a league
|
||||
*/
|
||||
export interface LeagueScheduleDto {
|
||||
races: ScheduledRaceViewModel[];
|
||||
}
|
||||
11
apps/website/lib/dtos/LeagueSeasonSummaryDto.ts
Normal file
11
apps/website/lib/dtos/LeagueSeasonSummaryDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* League season summary data transfer object
|
||||
* Represents a season within a league
|
||||
*/
|
||||
export interface LeagueSeasonSummaryDto {
|
||||
id: string;
|
||||
name: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
status: string;
|
||||
}
|
||||
13
apps/website/lib/dtos/LeagueStandingsDto.ts
Normal file
13
apps/website/lib/dtos/LeagueStandingsDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { StandingEntryDto } from './StandingEntryDto';
|
||||
import type { DriverDto } from './DriverDto';
|
||||
import type { LeagueMembership } from './LeagueMembershipDto';
|
||||
|
||||
/**
|
||||
* League standings transport object
|
||||
* Contains the current league standings
|
||||
*/
|
||||
export interface LeagueStandingsDto {
|
||||
standings: StandingEntryDto[];
|
||||
drivers: DriverDto[];
|
||||
memberships: LeagueMembership[];
|
||||
}
|
||||
13
apps/website/lib/dtos/LeagueStatsDto.ts
Normal file
13
apps/website/lib/dtos/LeagueStatsDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* League stats DTO
|
||||
* Contains statistical information about a league's races
|
||||
*/
|
||||
export interface LeagueStatsDto {
|
||||
leagueId: string;
|
||||
totalRaces: number;
|
||||
completedRaces: number;
|
||||
scheduledRaces: number;
|
||||
averageSOF?: number;
|
||||
highestSOF?: number;
|
||||
lowestSOF?: number;
|
||||
}
|
||||
18
apps/website/lib/dtos/LeagueSummaryDto.ts
Normal file
18
apps/website/lib/dtos/LeagueSummaryDto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* League summary transport object
|
||||
* Contains basic league information for list views
|
||||
*/
|
||||
export interface LeagueSummaryDto {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
logoUrl?: string;
|
||||
coverImage?: string;
|
||||
memberCount: number;
|
||||
maxMembers: number;
|
||||
isPublic: boolean;
|
||||
ownerId: string;
|
||||
ownerName?: string;
|
||||
scoringType?: string;
|
||||
status?: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/LoginParamsDto.ts
Normal file
8
apps/website/lib/dtos/LoginParamsDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Login parameters data transfer object
|
||||
* Parameters for user login
|
||||
*/
|
||||
export interface LoginParamsDto {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
10
apps/website/lib/dtos/MemberPaymentDto.ts
Normal file
10
apps/website/lib/dtos/MemberPaymentDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Member payment data transfer object
|
||||
* Represents a payment made by a league member
|
||||
*/
|
||||
export interface MemberPaymentDto {
|
||||
driverId: string;
|
||||
amount: number;
|
||||
paidAt: string;
|
||||
status: string;
|
||||
}
|
||||
10
apps/website/lib/dtos/MembershipFeeDto.ts
Normal file
10
apps/website/lib/dtos/MembershipFeeDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Membership fee data transfer object
|
||||
* Represents a membership fee for a league
|
||||
*/
|
||||
export interface MembershipFeeDto {
|
||||
leagueId: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
period: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/PaymentDto.ts
Normal file
11
apps/website/lib/dtos/PaymentDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Payment data transfer object
|
||||
* Represents a payment transaction
|
||||
*/
|
||||
export interface PaymentDto {
|
||||
id: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/PenaltyDataDto.ts
Normal file
11
apps/website/lib/dtos/PenaltyDataDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { PenaltyTypeDto } from './PenaltyTypeDto';
|
||||
|
||||
/**
|
||||
* Penalty data structure
|
||||
* Used when creating or updating penalties
|
||||
*/
|
||||
export interface PenaltyDataDto {
|
||||
driverId: string;
|
||||
type: PenaltyTypeDto;
|
||||
value?: number;
|
||||
}
|
||||
11
apps/website/lib/dtos/PenaltyTypeDto.ts
Normal file
11
apps/website/lib/dtos/PenaltyTypeDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Penalty type enumeration
|
||||
* Defines all possible penalty types in the system
|
||||
*/
|
||||
export type PenaltyTypeDto =
|
||||
| 'time_penalty'
|
||||
| 'grid_penalty'
|
||||
| 'points_deduction'
|
||||
| 'disqualification'
|
||||
| 'warning'
|
||||
| 'license_points';
|
||||
11
apps/website/lib/dtos/PrizeDto.ts
Normal file
11
apps/website/lib/dtos/PrizeDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Prize data transfer object
|
||||
* Represents a prize in a league
|
||||
*/
|
||||
export interface PrizeDto {
|
||||
id: string;
|
||||
name: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
position?: number;
|
||||
}
|
||||
13
apps/website/lib/dtos/ProtestDto.ts
Normal file
13
apps/website/lib/dtos/ProtestDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Protest data transfer object
|
||||
* Represents a protest filed in a race
|
||||
*/
|
||||
export interface ProtestDto {
|
||||
id: string;
|
||||
raceId: string;
|
||||
complainantId: string;
|
||||
defendantId: string;
|
||||
description: string;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
}
|
||||
18
apps/website/lib/dtos/RaceDetailDto.ts
Normal file
18
apps/website/lib/dtos/RaceDetailDto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { RaceDetailRaceDto } from './RaceDetailRaceDto';
|
||||
import type { RaceDetailLeagueDto } from './RaceDetailLeagueDto';
|
||||
import type { RaceDetailEntryDto } from './RaceDetailEntryDto';
|
||||
import type { RaceDetailRegistrationDto } from './RaceDetailRegistrationDto';
|
||||
import type { RaceDetailUserResultDto } from './RaceDetailUserResultDto';
|
||||
|
||||
/**
|
||||
* Race detail data transfer object
|
||||
* Complete race details view
|
||||
*/
|
||||
export interface RaceDetailDto {
|
||||
race: RaceDetailRaceDto | null;
|
||||
league: RaceDetailLeagueDto | null;
|
||||
entryList: RaceDetailEntryDto[];
|
||||
registration: RaceDetailRegistrationDto;
|
||||
userResult: RaceDetailUserResultDto | null;
|
||||
error?: string;
|
||||
}
|
||||
12
apps/website/lib/dtos/RaceDetailEntryDto.ts
Normal file
12
apps/website/lib/dtos/RaceDetailEntryDto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Race detail entry data transfer object
|
||||
* Represents an entry in race details
|
||||
*/
|
||||
export interface RaceDetailEntryDto {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
avatarUrl: string;
|
||||
rating: number | null;
|
||||
isCurrentUser: boolean;
|
||||
}
|
||||
13
apps/website/lib/dtos/RaceDetailLeagueDto.ts
Normal file
13
apps/website/lib/dtos/RaceDetailLeagueDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Race detail league data transfer object
|
||||
* League information in race details
|
||||
*/
|
||||
export interface RaceDetailLeagueDto {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
settings: {
|
||||
maxDrivers?: number;
|
||||
qualifyingFormat?: string;
|
||||
};
|
||||
}
|
||||
16
apps/website/lib/dtos/RaceDetailRaceDto.ts
Normal file
16
apps/website/lib/dtos/RaceDetailRaceDto.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Race detail race data transfer object
|
||||
* Race information in race details
|
||||
*/
|
||||
export interface RaceDetailRaceDto {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
sessionType: string;
|
||||
status: string;
|
||||
strengthOfField: number | null;
|
||||
registeredCount?: number;
|
||||
maxParticipants?: number;
|
||||
}
|
||||
8
apps/website/lib/dtos/RaceDetailRegistrationDto.ts
Normal file
8
apps/website/lib/dtos/RaceDetailRegistrationDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Race detail registration data transfer object
|
||||
* Registration information in race details
|
||||
*/
|
||||
export interface RaceDetailRegistrationDto {
|
||||
isUserRegistered: boolean;
|
||||
canRegister: boolean;
|
||||
}
|
||||
14
apps/website/lib/dtos/RaceDetailUserResultDto.ts
Normal file
14
apps/website/lib/dtos/RaceDetailUserResultDto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Race detail user result data transfer object
|
||||
* Represents the current user's result in race details
|
||||
*/
|
||||
export interface RaceDetailUserResultDto {
|
||||
position: number;
|
||||
startPosition: number;
|
||||
incidents: number;
|
||||
fastestLap: number;
|
||||
positionChange: number;
|
||||
isPodium: boolean;
|
||||
isClean: boolean;
|
||||
ratingChange: number | null;
|
||||
}
|
||||
13
apps/website/lib/dtos/RaceListItemDto.ts
Normal file
13
apps/website/lib/dtos/RaceListItemDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Race list item data transfer object
|
||||
* Represents a race in list views
|
||||
*/
|
||||
export interface RaceListItemDto {
|
||||
id: string;
|
||||
name: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
scheduledTime: string;
|
||||
status: string;
|
||||
trackName?: string;
|
||||
}
|
||||
10
apps/website/lib/dtos/RacePenaltiesDto.ts
Normal file
10
apps/website/lib/dtos/RacePenaltiesDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { RacePenaltyDto } from './RacePenaltyDto';
|
||||
|
||||
/**
|
||||
* Race penalties data transfer object
|
||||
* List of penalties for a race
|
||||
*/
|
||||
export interface RacePenaltiesDto {
|
||||
penalties: RacePenaltyDto[];
|
||||
driverMap: Record<string, string>;
|
||||
}
|
||||
14
apps/website/lib/dtos/RacePenaltyDto.ts
Normal file
14
apps/website/lib/dtos/RacePenaltyDto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Race penalty data transfer object
|
||||
* Represents a penalty issued in a race
|
||||
*/
|
||||
export interface RacePenaltyDto {
|
||||
id: string;
|
||||
driverId: string;
|
||||
type: string;
|
||||
value: number;
|
||||
reason: string;
|
||||
issuedBy: string;
|
||||
issuedAt: string;
|
||||
notes?: string;
|
||||
}
|
||||
15
apps/website/lib/dtos/RaceProtestDto.ts
Normal file
15
apps/website/lib/dtos/RaceProtestDto.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Race protest data transfer object
|
||||
* Represents a protest filed for a race
|
||||
*/
|
||||
export interface RaceProtestDto {
|
||||
id: string;
|
||||
protestingDriverId: string;
|
||||
accusedDriverId: string;
|
||||
incident: {
|
||||
lap: number;
|
||||
description: string;
|
||||
};
|
||||
status: string;
|
||||
filedAt: string;
|
||||
}
|
||||
10
apps/website/lib/dtos/RaceProtestsDto.ts
Normal file
10
apps/website/lib/dtos/RaceProtestsDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { RaceProtestDto } from './RaceProtestDto';
|
||||
|
||||
/**
|
||||
* Race protests data transfer object
|
||||
* List of protests for a race
|
||||
*/
|
||||
export interface RaceProtestsDto {
|
||||
protests: RaceProtestDto[];
|
||||
driverMap: Record<string, string>;
|
||||
}
|
||||
18
apps/website/lib/dtos/RaceResultDto.ts
Normal file
18
apps/website/lib/dtos/RaceResultDto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Race result data transfer object
|
||||
* Represents a driver's result in a race
|
||||
*/
|
||||
export interface RaceResultDto {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
avatarUrl: string;
|
||||
position: number;
|
||||
startPosition: number;
|
||||
incidents: number;
|
||||
fastestLap: number;
|
||||
positionChange: number;
|
||||
isPodium: boolean;
|
||||
isClean: boolean;
|
||||
}
|
||||
13
apps/website/lib/dtos/RaceResultRowDto.ts
Normal file
13
apps/website/lib/dtos/RaceResultRowDto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Race result row data transfer object
|
||||
* Represents a row in race results table
|
||||
*/
|
||||
export interface RaceResultRowDto {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
fastestLap: number;
|
||||
incidents: number;
|
||||
startPosition: number;
|
||||
}
|
||||
18
apps/website/lib/dtos/RaceResultsDetailDto.ts
Normal file
18
apps/website/lib/dtos/RaceResultsDetailDto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { RaceResultDto } from './RaceResultDto';
|
||||
|
||||
/**
|
||||
* Race results detail data transfer object
|
||||
* Detailed results for a race
|
||||
*/
|
||||
export interface RaceResultsDetailDto {
|
||||
raceId: string;
|
||||
track: string;
|
||||
results: RaceResultDto[];
|
||||
league?: { id: string; name: string };
|
||||
race?: { id: string; track: string; scheduledAt: string };
|
||||
drivers: { id: string; name: string }[];
|
||||
pointsSystem: Record<number, number>;
|
||||
fastestLapTime: number;
|
||||
penalties: { driverId: string; type: string; value?: number }[];
|
||||
currentDriverId: string;
|
||||
}
|
||||
7
apps/website/lib/dtos/RaceStatsDto.ts
Normal file
7
apps/website/lib/dtos/RaceStatsDto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Race stats data transfer object
|
||||
* Global race statistics
|
||||
*/
|
||||
export interface RaceStatsDto {
|
||||
totalRaces: number;
|
||||
}
|
||||
9
apps/website/lib/dtos/RaceWithSOFDto.ts
Normal file
9
apps/website/lib/dtos/RaceWithSOFDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Race with strength of field data transfer object
|
||||
* Race information including SOF
|
||||
*/
|
||||
export interface RaceWithSOFDto {
|
||||
id: string;
|
||||
track: string;
|
||||
strengthOfField: number | null;
|
||||
}
|
||||
9
apps/website/lib/dtos/RacesPageDataDto.ts
Normal file
9
apps/website/lib/dtos/RacesPageDataDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { RacesPageDataRaceDto } from './RacesPageDataRaceDto';
|
||||
|
||||
/**
|
||||
* Races page data data transfer object
|
||||
* Data for the races page
|
||||
*/
|
||||
export interface RacesPageDataDto {
|
||||
races: RacesPageDataRaceDto[];
|
||||
}
|
||||
17
apps/website/lib/dtos/RacesPageDataRaceDto.ts
Normal file
17
apps/website/lib/dtos/RacesPageDataRaceDto.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Races page data race data transfer object
|
||||
* Race information for the races page
|
||||
*/
|
||||
export interface RacesPageDataRaceDto {
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
status: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
strengthOfField: number | null;
|
||||
isUpcoming: boolean;
|
||||
isLive: boolean;
|
||||
isPast: boolean;
|
||||
}
|
||||
10
apps/website/lib/dtos/RecordEngagementInputDto.ts
Normal file
10
apps/website/lib/dtos/RecordEngagementInputDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Record engagement input data transfer object
|
||||
* Input for recording an engagement event
|
||||
*/
|
||||
export interface RecordEngagementInputDto {
|
||||
eventType: string;
|
||||
eventData?: Record<string, unknown>;
|
||||
userId?: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
7
apps/website/lib/dtos/RecordEngagementOutputDto.ts
Normal file
7
apps/website/lib/dtos/RecordEngagementOutputDto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Record engagement output data transfer object
|
||||
* Output from recording an engagement event
|
||||
*/
|
||||
export interface RecordEngagementOutputDto {
|
||||
success: boolean;
|
||||
}
|
||||
9
apps/website/lib/dtos/RecordPageViewInputDto.ts
Normal file
9
apps/website/lib/dtos/RecordPageViewInputDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Record page view input data transfer object
|
||||
* Input for recording a page view event
|
||||
*/
|
||||
export interface RecordPageViewInputDto {
|
||||
path: string;
|
||||
userId?: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
7
apps/website/lib/dtos/RecordPageViewOutputDto.ts
Normal file
7
apps/website/lib/dtos/RecordPageViewOutputDto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Record page view output data transfer object
|
||||
* Output from recording a page view
|
||||
*/
|
||||
export interface RecordPageViewOutputDto {
|
||||
success: boolean;
|
||||
}
|
||||
8
apps/website/lib/dtos/RegisterForRaceInputDto.ts
Normal file
8
apps/website/lib/dtos/RegisterForRaceInputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Register for race input data transfer object
|
||||
* Input for registering a driver for a race
|
||||
*/
|
||||
export interface RegisterForRaceInputDto {
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
}
|
||||
8
apps/website/lib/dtos/RequestAvatarGenerationInputDto.ts
Normal file
8
apps/website/lib/dtos/RequestAvatarGenerationInputDto.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Request avatar generation input data transfer object
|
||||
* Input for requesting avatar generation
|
||||
*/
|
||||
export interface RequestAvatarGenerationInputDto {
|
||||
driverId: string;
|
||||
style?: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Request avatar generation output data transfer object
|
||||
* Output from avatar generation request
|
||||
*/
|
||||
export interface RequestAvatarGenerationOutputDto {
|
||||
success: boolean;
|
||||
avatarUrl?: string;
|
||||
error?: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/ScheduledRaceDto.ts
Normal file
11
apps/website/lib/dtos/ScheduledRaceDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Scheduled race data transfer object
|
||||
* Represents a race scheduled in a league
|
||||
*/
|
||||
export interface ScheduledRaceDto {
|
||||
id: string;
|
||||
name: string;
|
||||
scheduledTime: string;
|
||||
status: string;
|
||||
trackName?: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/SessionDataDto.ts
Normal file
11
apps/website/lib/dtos/SessionDataDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Session data data transfer object
|
||||
* User session information
|
||||
*/
|
||||
export interface SessionDataDto {
|
||||
userId: string;
|
||||
email: string;
|
||||
displayName?: string;
|
||||
driverId?: string;
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
9
apps/website/lib/dtos/SignupParamsDto.ts
Normal file
9
apps/website/lib/dtos/SignupParamsDto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Signup parameters data transfer object
|
||||
* Parameters for user signup
|
||||
*/
|
||||
export interface SignupParamsDto {
|
||||
email: string;
|
||||
password: string;
|
||||
displayName: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/SponsorDashboardDto.ts
Normal file
11
apps/website/lib/dtos/SponsorDashboardDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Sponsor dashboard data transfer object
|
||||
* Dashboard information for a sponsor
|
||||
*/
|
||||
export interface SponsorDashboardDto {
|
||||
sponsorId: string;
|
||||
sponsorName: string;
|
||||
totalSponsorships: number;
|
||||
activeSponsorships: number;
|
||||
totalInvestment: number;
|
||||
}
|
||||
10
apps/website/lib/dtos/SponsorDto.ts
Normal file
10
apps/website/lib/dtos/SponsorDto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Sponsor data transfer object
|
||||
* Represents a sponsor entity
|
||||
*/
|
||||
export interface SponsorDto {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl?: string;
|
||||
websiteUrl?: string;
|
||||
}
|
||||
11
apps/website/lib/dtos/SponsorSponsorshipsDto.ts
Normal file
11
apps/website/lib/dtos/SponsorSponsorshipsDto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { SponsorshipDetailDto } from './SponsorshipDetailDto';
|
||||
|
||||
/**
|
||||
* Sponsor sponsorships data transfer object
|
||||
* Sponsorships associated with a sponsor
|
||||
*/
|
||||
export interface SponsorSponsorshipsDto {
|
||||
sponsorId: string;
|
||||
sponsorName: string;
|
||||
sponsorships: SponsorshipDetailDto[];
|
||||
}
|
||||
14
apps/website/lib/dtos/SponsorshipDetailDto.ts
Normal file
14
apps/website/lib/dtos/SponsorshipDetailDto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Sponsorship detail data transfer object
|
||||
* Details of a sponsorship
|
||||
*/
|
||||
export interface SponsorshipDetailDto {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
leagueName: string;
|
||||
seasonId: string;
|
||||
tier: 'main' | 'secondary';
|
||||
status: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user