diff --git a/apps/api/openapi.json b/apps/api/openapi.json index b7d9ada72..29bd3d848 100644 --- a/apps/api/openapi.json +++ b/apps/api/openapi.json @@ -19,6 +19,275 @@ "success" ] }, + "UpdateTeamInputDTO": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "TeamMembershipDTO": { + "type": "object", + "properties": { + "role": { + "type": "string" + }, + "joinedAt": { + "type": "string" + }, + "isActive": { + "type": "boolean" + } + }, + "required": [ + "role", + "joinedAt", + "isActive" + ] + }, + "TeamMemberDTO": { + "type": "object", + "properties": { + "driverId": { + "type": "string" + }, + "driverName": { + "type": "string" + }, + "role": { + "type": "string" + }, + "joinedAt": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "avatarUrl": { + "type": "string" + } + }, + "required": [ + "driverId", + "driverName", + "role", + "joinedAt", + "isActive", + "avatarUrl" + ] + }, + "TeamListItemDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "description": { + "type": "string" + }, + "memberCount": { + "type": "number" + }, + "leagues": { + "type": "array", + "items": { + "type": "string" + } + }, + "specialization": { + "type": "string" + }, + "region": { + "type": "string" + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "tag", + "description", + "memberCount", + "leagues" + ] + }, + "TeamLeaderboardItemDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "memberCount": { + "type": "number" + }, + "rating": { + "type": "number", + "nullable": true + }, + "totalWins": { + "type": "number" + }, + "totalRaces": { + "type": "number" + }, + "performanceLevel": { + "type": "string" + }, + "isRecruiting": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "specialization": { + "type": "string" + }, + "region": { + "type": "string" + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "memberCount", + "totalWins", + "totalRaces", + "performanceLevel", + "isRecruiting", + "createdAt" + ] + }, + "TeamJoinRequestDTO": { + "type": "object", + "properties": { + "requestId": { + "type": "string" + }, + "driverId": { + "type": "string" + }, + "driverName": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "requestedAt": { + "type": "string" + }, + "avatarUrl": { + "type": "string" + } + }, + "required": [ + "requestId", + "driverId", + "driverName", + "teamId", + "status", + "requestedAt", + "avatarUrl" + ] + }, + "TeamDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "description": { + "type": "string" + }, + "ownerId": { + "type": "string" + }, + "leagues": { + "type": "array", + "items": { + "type": "string" + } + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "tag", + "description", + "ownerId", + "leagues" + ] + }, + "GetTeamsLeaderboardOutputDTO": { + "type": "object", + "properties": { + "teams": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamLeaderboardItemDTO" + } + }, + "recruitingCount": { + "type": "number" + }, + "groupsBySkillLevel": { + "type": "string" + }, + "topTeams": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamLeaderboardItemDTO" + } + } + }, + "required": [ + "teams", + "recruitingCount", + "groupsBySkillLevel", + "topTeams" + ] + }, "GetTeamMembershipOutputDTO": { "type": "object", "properties": { @@ -38,6 +307,117 @@ "isActive" ] }, + "GetTeamMembersOutputDTO": { + "type": "object", + "properties": { + "members": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamMemberDTO" + } + }, + "totalCount": { + "type": "number" + }, + "ownerCount": { + "type": "number" + }, + "managerCount": { + "type": "number" + }, + "memberCount": { + "type": "number" + } + }, + "required": [ + "members", + "totalCount", + "ownerCount", + "managerCount", + "memberCount" + ] + }, + "GetTeamJoinRequestsOutputDTO": { + "type": "object", + "properties": { + "requests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamJoinRequestDTO" + } + }, + "pendingCount": { + "type": "number" + }, + "totalCount": { + "type": "number" + } + }, + "required": [ + "requests", + "pendingCount", + "totalCount" + ] + }, + "GetTeamDetailsOutputDTO": { + "type": "object", + "properties": { + "team": { + "$ref": "#/components/schemas/TeamDTO" + }, + "membership": { + "$ref": "#/components/schemas/TeamMembershipDTO" + }, + "canManage": { + "type": "boolean" + } + }, + "required": [ + "team", + "canManage" + ] + }, + "GetDriverTeamOutputDTO": { + "type": "object", + "properties": { + "team": { + "$ref": "#/components/schemas/TeamDTO" + }, + "membership": { + "$ref": "#/components/schemas/TeamMembershipDTO" + }, + "isOwner": { + "type": "boolean" + }, + "canManage": { + "type": "boolean" + } + }, + "required": [ + "team", + "membership", + "isOwner", + "canManage" + ] + }, + "GetAllTeamsOutputDTO": { + "type": "object", + "properties": { + "teams": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamListItemDTO" + } + }, + "totalCount": { + "type": "number" + } + }, + "required": [ + "teams", + "totalCount" + ] + }, "CreateTeamOutputDTO": { "type": "object", "properties": { @@ -61,6 +441,9 @@ }, "tag": { "type": "string" + }, + "description": { + "type": "string" } }, "required": [ @@ -79,12 +462,47 @@ }, "sponsorName": { "type": "string" + }, + "sponsorLogo": { + "type": "string" + }, + "tier": { + "type": "string" + }, + "offeredAmount": { + "type": "number" + }, + "currency": { + "type": "string" + }, + "formattedAmount": { + "type": "string" + }, + "message": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "platformFee": { + "type": "number" + }, + "netAmount": { + "type": "number" } }, "required": [ "id", "sponsorId", - "sponsorName" + "sponsorName", + "tier", + "offeredAmount", + "currency", + "formattedAmount", + "createdAt", + "platformFee", + "netAmount" ] }, "SponsorshipPricingItemDTO": { @@ -97,7 +515,7 @@ "type": "string" }, "price": { - "type": "string" + "type": "number" }, "currency": { "type": "string" @@ -127,6 +545,58 @@ }, "seasonName": { "type": "string" + }, + "seasonStartDate": { + "type": "string", + "format": "date-time" + }, + "seasonEndDate": { + "type": "string", + "format": "date-time" + }, + "tier": { + "type": "string" + }, + "status": { + "type": "string" + }, + "pricing": { + "type": "object" + }, + "amount": { + "type": "number" + }, + "currency": { + "type": "string" + }, + "platformFee": { + "type": "object" + }, + "netAmount": { + "type": "object" + }, + "metrics": { + "type": "object" + }, + "drivers": { + "type": "number" + }, + "races": { + "type": "number" + }, + "completedRaces": { + "type": "number" + }, + "impressions": { + "type": "number" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "activatedAt": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -134,7 +604,24 @@ "leagueId", "leagueName", "seasonId", - "seasonName" + "seasonName", + "tier", + "status", + "pricing", + "amount", + "currency", + "platformFee", + "amount", + "currency", + "netAmount", + "amount", + "currency", + "metrics", + "drivers", + "races", + "completedRaces", + "impressions", + "createdAt" ] }, "SponsorshipDTO": { @@ -142,10 +629,69 @@ "properties": { "id": { "type": "string" + }, + "type": { + "type": "string" + }, + "entityId": { + "type": "string" + }, + "entityName": { + "type": "string" + }, + "tier": { + "type": "string" + }, + "status": { + "type": "string" + }, + "applicationDate": { + "type": "string" + }, + "approvalDate": { + "type": "string" + }, + "rejectionReason": { + "type": "string" + }, + "startDate": { + "type": "string" + }, + "endDate": { + "type": "string" + }, + "price": { + "type": "number" + }, + "impressions": { + "type": "number" + }, + "impressionsChange": { + "type": "number" + }, + "engagement": { + "type": "number" + }, + "details": { + "type": "string" + }, + "entityOwner": { + "type": "string" + }, + "applicationMessage": { + "type": "string" } }, "required": [ - "id" + "id", + "type", + "entityId", + "entityName", + "status", + "startDate", + "endDate", + "price", + "impressions" ] }, "SponsoredLeagueDTO": { @@ -156,11 +702,31 @@ }, "name": { "type": "string" + }, + "tier": { + "type": "string" + }, + "drivers": { + "type": "number" + }, + "races": { + "type": "number" + }, + "impressions": { + "type": "number" + }, + "status": { + "type": "string" } }, "required": [ "id", - "name" + "name", + "tier", + "drivers", + "races", + "impressions", + "status" ] }, "SponsorSponsorshipsDTO": { @@ -171,11 +737,47 @@ }, "sponsorName": { "type": "string" + }, + "sponsorships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SponsorshipDetailDTO" + } + }, + "summary": { + "type": "object" + }, + "totalSponsorships": { + "type": "string" + }, + "activeSponsorships": { + "type": "string" + }, + "totalInvestment": { + "type": "string" + }, + "totalPlatformFees": { + "type": "string" + }, + "currency": { + "type": "string" } }, "required": [ "sponsorId", - "sponsorName" + "sponsorName", + "sponsorships", + "summary", + "totalSponsorships", + "activeSponsorships", + "totalInvestment", + "totalPlatformFees", + "currency", + "totalSponsorships", + "activeSponsorships", + "totalInvestment", + "totalPlatformFees", + "currency" ] }, "SponsorProfileDTO": { @@ -198,6 +800,42 @@ }, "description": { "type": "string" + }, + "logoUrl": { + "type": "string" + }, + "industry": { + "type": "string" + }, + "address": { + "type": "object" + }, + "street": { + "type": "string" + }, + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "postalCode": { + "type": "string" + }, + "taxId": { + "type": "string" + }, + "socialLinks": { + "type": "object" + }, + "twitter": { + "type": "string" + }, + "linkedin": { + "type": "string" + }, + "instagram": { + "type": "string" } }, "required": [ @@ -206,35 +844,81 @@ "contactEmail", "contactPhone", "website", - "description" + "description", + "industry", + "address", + "street", + "city", + "country", + "postalCode", + "taxId", + "socialLinks", + "twitter", + "linkedin", + "instagram" + ] + }, + "SponsorDriverDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "country": { + "type": "string" + }, + "position": { + "type": "number" + }, + "races": { + "type": "number" + }, + "impressions": { + "type": "number" + }, + "team": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "country", + "position", + "races", + "impressions", + "team" ] }, "SponsorDashboardMetricsDTO": { "type": "object", "properties": { "impressions": { - "type": "string" + "type": "number" }, "impressionsChange": { - "type": "string" + "type": "number" }, "uniqueViewers": { - "type": "string" + "type": "number" }, "viewersChange": { - "type": "string" + "type": "number" }, "races": { - "type": "string" + "type": "number" }, "drivers": { - "type": "string" + "type": "number" }, "exposure": { - "type": "string" + "type": "number" }, "exposureChange": { - "type": "string" + "type": "number" } }, "required": [ @@ -252,13 +936,13 @@ "type": "object", "properties": { "activeSponsorships": { - "type": "string" + "type": "number" }, "totalInvestment": { - "type": "string" + "type": "number" }, "costPerThousandViews": { - "type": "string" + "type": "number" } }, "required": [ @@ -275,11 +959,72 @@ }, "sponsorName": { "type": "string" + }, + "metrics": { + "$ref": "#/components/schemas/SponsorDashboardMetricsDTO" + }, + "sponsoredLeagues": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SponsoredLeagueDTO" + } + }, + "investment": { + "$ref": "#/components/schemas/SponsorDashboardInvestmentDTO" + }, + "sponsorships": { + "type": "object" + }, + "leagues": { + "type": "string" + }, + "teams": { + "type": "string" + }, + "drivers": { + "type": "string" + }, + "races": { + "type": "string" + }, + "platform": { + "type": "array", + "items": { + "type": "string" + } + }, + "recentActivity": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActivityItemDTO" + } + }, + "upcomingRenewals": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RenewalAlertDTO" + } } }, "required": [ "sponsorId", - "sponsorName" + "sponsorName", + "metrics", + "sponsoredLeagues", + "investment", + "sponsorships", + "leagues", + "teams", + "drivers", + "races", + "platform", + "leagues", + "teams", + "drivers", + "races", + "platform", + "recentActivity", + "upcomingRenewals" ] }, "SponsorDTO": { @@ -290,6 +1035,19 @@ }, "name": { "type": "string" + }, + "contactEmail": { + "type": "string" + }, + "logoUrl": { + "type": "string" + }, + "websiteUrl": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -305,11 +1063,23 @@ }, "name": { "type": "string" + }, + "type": { + "type": "string" + }, + "renewDate": { + "type": "string" + }, + "price": { + "type": "number" } }, "required": [ "id", - "name" + "name", + "type", + "renewDate", + "price" ] }, "RejectSponsorshipRequestInputDTO": { @@ -317,13 +1087,16 @@ "properties": { "respondedBy": { "type": "string" + }, + "reason": { + "type": "string" } }, "required": [ "respondedBy" ] }, - "RaceDTO": { + "SponsorRaceDTO": { "type": "object", "properties": { "id": { @@ -336,6 +1109,9 @@ "type": "string" }, "views": { + "type": "number" + }, + "status": { "type": "string" } }, @@ -343,23 +1119,24 @@ "id", "name", "date", - "views" + "views", + "status" ] }, "PrivacySettingsDTO": { "type": "object", "properties": { "publicProfile": { - "type": "string" + "type": "boolean" }, "showStats": { - "type": "string" + "type": "boolean" }, "showActiveSponsorships": { - "type": "string" + "type": "boolean" }, "allowDirectContact": { - "type": "string" + "type": "boolean" } }, "required": [ @@ -374,32 +1151,56 @@ "properties": { "id": { "type": "string" + }, + "type": { + "type": "string" + }, + "last4": { + "type": "string" + }, + "brand": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "expiryMonth": { + "type": "number" + }, + "expiryYear": { + "type": "number" + }, + "bankName": { + "type": "string" } }, "required": [ - "id" + "id", + "type", + "last4", + "isDefault" ] }, "NotificationSettingsDTO": { "type": "object", "properties": { "emailNewSponsorships": { - "type": "string" + "type": "boolean" }, "emailWeeklyReport": { - "type": "string" + "type": "boolean" }, "emailRaceAlerts": { - "type": "string" + "type": "boolean" }, "emailPaymentAlerts": { - "type": "string" + "type": "boolean" }, "emailNewOpportunities": { - "type": "string" + "type": "boolean" }, "emailContractExpiry": { - "type": "string" + "type": "boolean" } }, "required": [ @@ -422,12 +1223,112 @@ }, "game": { "type": "string" + }, + "tier": { + "type": "string" + }, + "season": { + "type": "string" + }, + "description": { + "type": "string" + }, + "drivers": { + "type": "number" + }, + "races": { + "type": "number" + }, + "completedRaces": { + "type": "number" + }, + "totalImpressions": { + "type": "number" + }, + "avgViewsPerRace": { + "type": "number" + }, + "engagement": { + "type": "number" + }, + "rating": { + "type": "number" + }, + "seasonStatus": { + "type": "string" + }, + "seasonDates": { + "type": "object" + }, + "start": { + "type": "string" + }, + "end": { + "type": "string" + }, + "nextRace": { + "type": "object" + }, + "date": { + "type": "string" + }, + "sponsorSlots": { + "type": "object" + }, + "main": { + "type": "object" + }, + "available": { + "type": "number" + }, + "price": { + "type": "number" + }, + "benefits": { + "type": "array", + "items": { + "type": "string" + } + }, + "secondary": { + "type": "object" + }, + "total": { + "type": "number" } }, "required": [ "id", "name", - "game" + "game", + "tier", + "season", + "description", + "drivers", + "races", + "completedRaces", + "totalImpressions", + "avgViewsPerRace", + "engagement", + "rating", + "seasonStatus", + "seasonDates", + "start", + "end", + "name", + "date", + "sponsorSlots", + "main", + "available", + "price", + "benefits", + "secondary", + "available", + "total", + "price", + "benefits", + "main", + "secondary" ] }, "InvoiceDTO": { @@ -446,12 +1347,24 @@ "type": "string" }, "amount": { - "type": "string" + "type": "number" }, "vatAmount": { - "type": "string" + "type": "number" }, "totalAmount": { + "type": "number" + }, + "status": { + "type": "string" + }, + "description": { + "type": "string" + }, + "sponsorshipType": { + "type": "string" + }, + "pdfUrl": { "type": "string" } }, @@ -462,7 +1375,25 @@ "dueDate", "amount", "vatAmount", - "totalAmount" + "totalAmount", + "status", + "description", + "sponsorshipType", + "pdfUrl" + ] + }, + "GetSponsorsOutputDTO": { + "type": "object", + "properties": { + "sponsors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SponsorDTO" + } + } + }, + "required": [ + "sponsors" ] }, "GetSponsorSponsorshipsQueryParamsDTO": { @@ -476,6 +1407,17 @@ "sponsorId" ] }, + "GetSponsorOutputDTO": { + "type": "object", + "properties": { + "sponsor": { + "$ref": "#/components/schemas/SponsorDTO" + } + }, + "required": [ + "sponsor" + ] + }, "GetSponsorDashboardQueryParamsDTO": { "type": "object", "properties": { @@ -495,11 +1437,22 @@ }, "entityId": { "type": "string" + }, + "requests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SponsorshipRequestDTO" + } + }, + "totalCount": { + "type": "number" } }, "required": [ "entityType", - "entityId" + "entityId", + "requests", + "totalCount" ] }, "GetEntitySponsorshipPricingResultDTO": { @@ -510,46 +1463,29 @@ }, "entityId": { "type": "string" + }, + "pricing": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SponsorshipPricingItemDTO" + } } }, "required": [ "entityType", - "entityId" + "entityId", + "pricing" ] }, - "DriverDTO": { + "CreateSponsorOutputDTO": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "country": { - "type": "string" - }, - "position": { - "type": "string" - }, - "races": { - "type": "string" - }, - "impressions": { - "type": "string" - }, - "team": { - "type": "string" + "sponsor": { + "$ref": "#/components/schemas/SponsorDTO" } }, "required": [ - "id", - "name", - "country", - "position", - "races", - "impressions", - "team" + "sponsor" ] }, "CreateSponsorInputDTO": { @@ -560,6 +1496,12 @@ }, "contactEmail": { "type": "string" + }, + "websiteUrl": { + "type": "string" + }, + "logoUrl": { + "type": "string" } }, "required": [ @@ -571,22 +1513,22 @@ "type": "object", "properties": { "totalSpent": { - "type": "string" + "type": "number" }, "pendingAmount": { - "type": "string" + "type": "number" }, "nextPaymentDate": { "type": "string" }, "nextPaymentAmount": { - "type": "string" + "type": "number" }, "activeSponsorships": { - "type": "string" + "type": "number" }, "averageMonthlySpend": { - "type": "string" + "type": "number" } }, "required": [ @@ -611,9 +1553,39 @@ "type": "string" }, "drivers": { - "type": "string" + "type": "number" }, "avgViewsPerRace": { + "type": "number" + }, + "mainSponsorSlot": { + "type": "object" + }, + "available": { + "type": "number" + }, + "price": { + "type": "number" + }, + "secondarySlots": { + "type": "object" + }, + "total": { + "type": "number" + }, + "rating": { + "type": "number" + }, + "tier": { + "type": "string" + }, + "nextRace": { + "type": "string" + }, + "seasonStatus": { + "type": "string" + }, + "description": { "type": "string" } }, @@ -622,7 +1594,18 @@ "name", "game", "drivers", - "avgViewsPerRace" + "avgViewsPerRace", + "mainSponsorSlot", + "available", + "price", + "secondarySlots", + "available", + "total", + "price", + "rating", + "tier", + "seasonStatus", + "description" ] }, "ActivityItemDTO": { @@ -630,10 +1613,25 @@ "properties": { "id": { "type": "string" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + }, + "time": { + "type": "string" + }, + "impressions": { + "type": "number" } }, "required": [ - "id" + "id", + "type", + "message", + "time" ] }, "AcceptSponsorshipRequestInputDTO": { @@ -673,12 +1671,20 @@ }, "enum": { "type": "string" + }, + "decision": { + "type": "string" + }, + "decisionNotes": { + "type": "string" } }, "required": [ "protestId", "stewardId", - "enum" + "enum", + "decision", + "decisionNotes" ] }, "RequestProtestDefenseCommandDTO": { @@ -738,6 +1744,19 @@ }, "leagueName": { "type": "string" + }, + "strengthOfField": { + "type": "number", + "nullable": true + }, + "isUpcoming": { + "type": "boolean" + }, + "isLive": { + "type": "boolean" + }, + "isPast": { + "type": "boolean" } }, "required": [ @@ -747,7 +1766,24 @@ "scheduledAt", "status", "leagueId", - "leagueName" + "leagueName", + "isUpcoming", + "isLive", + "isPast" + ] + }, + "RacesPageDataDTO": { + "type": "object", + "properties": { + "races": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RacesPageDataRaceDTO" + } + } + }, + "required": [ + "races" ] }, "RaceWithSOFDTO": { @@ -758,6 +1794,10 @@ }, "track": { "type": "string" + }, + "strengthOfField": { + "type": "number", + "nullable": true } }, "required": [ @@ -784,11 +1824,18 @@ }, "track": { "type": "string" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RaceResultDTO" + } } }, "required": [ "raceId", - "track" + "track", + "results" ] }, "RaceResultDTO": { @@ -838,6 +1885,27 @@ "isClean" ] }, + "RaceProtestsDTO": { + "type": "object", + "properties": { + "protests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RaceProtestDTO" + } + }, + "driverMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "protests", + "driverMap" + ] + }, "RaceProtestDTO": { "type": "object", "properties": { @@ -858,6 +1926,12 @@ }, "description": { "type": "string" + }, + "status": { + "type": "string" + }, + "filedAt": { + "type": "string" } }, "required": [ @@ -866,7 +1940,9 @@ "accusedDriverId", "incident", "lap", - "description" + "description", + "status", + "filedAt" ] }, "RacePenaltyDTO": { @@ -892,6 +1968,10 @@ }, "issuedAt": { "type": "string" + }, + "notes": { + "type": "string", + "nullable": true } }, "required": [ @@ -904,6 +1984,27 @@ "issuedAt" ] }, + "RacePenaltiesDTO": { + "type": "object", + "properties": { + "penalties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RacePenaltyDTO" + } + }, + "driverMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "penalties", + "driverMap" + ] + }, "RaceDetailUserResultDTO": { "type": "object", "properties": { @@ -927,6 +2028,10 @@ }, "isClean": { "type": "boolean" + }, + "ratingChange": { + "type": "number", + "nullable": true } }, "required": [ @@ -977,6 +2082,16 @@ }, "status": { "type": "string" + }, + "strengthOfField": { + "type": "number", + "nullable": true + }, + "registeredCount": { + "type": "number" + }, + "maxParticipants": { + "type": "number" } }, "required": [ @@ -1032,13 +2147,77 @@ }, "avatarUrl": { "type": "string" + }, + "rating": { + "type": "number", + "nullable": true + }, + "isCurrentUser": { + "type": "boolean" } }, "required": [ "id", "name", "country", - "avatarUrl" + "avatarUrl", + "isCurrentUser" + ] + }, + "RaceDetailDTO": { + "type": "object", + "properties": { + "race": { + "$ref": "#/components/schemas/RaceDetailRaceDTO", + "nullable": true + }, + "league": { + "$ref": "#/components/schemas/RaceDetailLeagueDTO", + "nullable": true + }, + "entryList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RaceDetailEntryDTO" + } + }, + "registration": { + "$ref": "#/components/schemas/RaceDetailRegistrationDTO" + }, + "userResult": { + "$ref": "#/components/schemas/RaceDetailUserResultDTO", + "nullable": true + }, + "error": { + "type": "string" + } + }, + "required": [ + "entryList", + "registration" + ] + }, + "RaceDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "date": { + "type": "string" + }, + "leagueName": { + "type": "string", + "nullable": true + } + }, + "required": [ + "id", + "name", + "date" ] }, "RaceActionParamsDTO": { @@ -1063,12 +2242,41 @@ }, "adminId": { "type": "string" + }, + "infractionType": { + "type": "string" + }, + "severity": { + "type": "string" + }, + "notes": { + "type": "string" } }, "required": [ "raceId", "driverId", - "adminId" + "adminId", + "infractionType", + "severity" + ] + }, + "ProtestIncidentDTO": { + "type": "object", + "properties": { + "lap": { + "type": "number" + }, + "description": { + "type": "string" + }, + "timeInRace": { + "type": "number" + } + }, + "required": [ + "lap", + "description" ] }, "ImportRaceResultsSummaryDTO": { @@ -1085,6 +2293,12 @@ }, "resultsRecorded": { "type": "number" + }, + "errors": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ @@ -1137,25 +2351,20 @@ "type": "string" }, "incident": { - "type": "object" + "$ref": "#/components/schemas/ProtestIncidentDTO" }, - "lap": { - "type": "number" - }, - "description": { + "comment": { "type": "string" }, - "timeInRace": { - "type": "number" + "proofVideoUrl": { + "type": "string" } }, "required": [ "raceId", "protestingDriverId", "accusedDriverId", - "incident", - "lap", - "description" + "incident" ] }, "DashboardRecentResultDTO": { @@ -1213,6 +2422,12 @@ }, "scheduledAt": { "type": "string" + }, + "status": { + "type": "string" + }, + "isMyLeague": { + "type": "boolean" } }, "required": [ @@ -1221,7 +2436,74 @@ "leagueName", "track", "car", - "scheduledAt" + "scheduledAt", + "status", + "isMyLeague" + ] + }, + "DashboardOverviewDTO": { + "type": "object", + "properties": { + "currentDriver": { + "$ref": "#/components/schemas/DashboardDriverSummaryDTO", + "nullable": true + }, + "myUpcomingRaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardRaceSummaryDTO" + } + }, + "otherUpcomingRaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardRaceSummaryDTO" + } + }, + "upcomingRaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardRaceSummaryDTO" + } + }, + "activeLeaguesCount": { + "type": "number" + }, + "nextRace": { + "$ref": "#/components/schemas/DashboardRaceSummaryDTO", + "nullable": true + }, + "recentResults": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardRecentResultDTO" + } + }, + "leagueStandingsSummaries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardLeagueStandingSummaryDTO" + } + }, + "feedSummary": { + "$ref": "#/components/schemas/DashboardFeedSummaryDTO" + }, + "friends": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardFriendSummaryDTO" + } + } + }, + "required": [ + "myUpcomingRaces", + "otherUpcomingRaces", + "upcomingRaces", + "activeLeaguesCount", + "recentResults", + "leagueStandingsSummaries", + "feedSummary", + "friends" ] }, "DashboardLeagueStandingSummaryDTO": { @@ -1279,10 +2561,17 @@ "properties": { "notificationCount": { "type": "number" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardFeedItemSummaryDTO" + } } }, "required": [ - "notificationCount" + "notificationCount", + "items" ] }, "DashboardFeedItemSummaryDTO": { @@ -1293,11 +2582,32 @@ }, "enum": { "type": "string" + }, + "type": { + "type": "string" + }, + "headline": { + "type": "string" + }, + "body": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "ctaLabel": { + "type": "string" + }, + "ctaHref": { + "type": "string" } }, "required": [ "id", - "enum" + "enum", + "type", + "headline", + "timestamp" ] }, "DashboardDriverSummaryDTO": { @@ -1314,13 +2624,37 @@ }, "avatarUrl": { "type": "string" + }, + "rating": { + "type": "number", + "nullable": true + }, + "globalRank": { + "type": "number", + "nullable": true + }, + "totalRaces": { + "type": "number" + }, + "wins": { + "type": "number" + }, + "podiums": { + "type": "number" + }, + "consistency": { + "type": "number", + "nullable": true } }, "required": [ "id", "name", "country", - "avatarUrl" + "avatarUrl", + "totalRaces", + "wins", + "podiums" ] }, "ApplyPenaltyCommandDTO": { @@ -1337,13 +2671,30 @@ }, "enum": { "type": "string" + }, + "type": { + "type": "string" + }, + "value": { + "type": "number" + }, + "reason": { + "type": "string" + }, + "protestId": { + "type": "string" + }, + "notes": { + "type": "string" } }, "required": [ "raceId", "driverId", "stewardId", - "enum" + "enum", + "type", + "reason" ] }, "AllRacesListItemDTO": { @@ -1369,6 +2720,10 @@ }, "leagueName": { "type": "string" + }, + "strengthOfField": { + "type": "number", + "nullable": true } }, "required": [ @@ -1411,111 +2766,65 @@ "name" ] }, - "UploadMediaOutputDTO": { + "AllRacesFilterOptionsDTO": { "type": "object", "properties": { - "success": { - "type": "string" + "statuses": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AllRacesStatusFilterDTO" + } + }, + "leagues": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AllRacesLeagueFilterDTO" + } } }, "required": [ - "success" + "statuses", + "leagues" ] }, - "UpdateAvatarOutputDTO": { + "AllRacesPageDTO": { "type": "object", "properties": { - "success": { - "type": "string" + "races": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AllRacesListItemDTO" + } + }, + "filters": { + "$ref": "#/components/schemas/AllRacesFilterOptionsDTO" } }, "required": [ - "success" + "races", + "filters" ] }, - "UpdateAvatarInputDTO": { + "UpsertMembershipFeeResultDTO": { "type": "object", "properties": { - "driverId": { - "type": "string" - }, - "avatarUrl": { - "type": "string" + "fee": { + "$ref": "#/components/schemas/MembershipFeeDTO" } }, "required": [ - "driverId", - "avatarUrl" + "fee" ] }, - "RequestAvatarGenerationInputDTO": { + "UpdatePaymentStatusOutputDTO": { "type": "object", "properties": { - "userId": { - "type": "string" - }, - "facePhotoData": { - "type": "string" - }, - "suitColor": { - "type": "string" + "payment": { + "$ref": "#/components/schemas/PaymentDTO" } }, "required": [ - "userId", - "facePhotoData", - "suitColor" - ] - }, - "GetMediaOutputDTO": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "url": { - "type": "string" - }, - "type": { - "type": "string" - }, - "category": { - "type": "string" - }, - "uploadedAt": { - "type": "string" - }, - "size": { - "type": "number" - } - }, - "required": [ - "id", - "url", - "type", - "uploadedAt" - ] - }, - "GetAvatarOutputDTO": { - "type": "object", - "properties": { - "avatarUrl": { - "type": "string" - } - }, - "required": [ - "avatarUrl" - ] - }, - "DeleteMediaOutputDTO": { - "type": "object", - "properties": { - "success": { - "type": "string" - } - }, - "required": [ - "success" + "payment" ] }, "UpdatePaymentStatusInputDTO": { @@ -1523,10 +2832,40 @@ "properties": { "paymentId": { "type": "string" + }, + "status": { + "type": "string" } }, "required": [ - "paymentId" + "paymentId", + "status" + ] + }, + "UpdateMemberPaymentResultDTO": { + "type": "object", + "properties": { + "payment": { + "$ref": "#/components/schemas/MemberPaymentDTO" + } + }, + "required": [ + "payment" + ] + }, + "ProcessWalletTransactionResultDTO": { + "type": "object", + "properties": { + "wallet": { + "$ref": "#/components/schemas/WalletDTO" + }, + "transaction": { + "$ref": "#/components/schemas/TransactionDTO" + } + }, + "required": [ + "wallet", + "transaction" ] }, "PaymentDTO": { @@ -1534,10 +2873,54 @@ "properties": { "id": { "type": "string" + }, + "type": { + "type": "string" + }, + "amount": { + "type": "number" + }, + "platformFee": { + "type": "number" + }, + "netAmount": { + "type": "number" + }, + "payerId": { + "type": "string" + }, + "payerType": { + "type": "string" + }, + "leagueId": { + "type": "string" + }, + "seasonId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "completedAt": { + "type": "string", + "format": "date-time" } }, "required": [ - "id" + "id", + "type", + "amount", + "platformFee", + "netAmount", + "payerId", + "payerType", + "leagueId", + "status", + "createdAt" ] }, "MembershipFeeDTO": { @@ -1548,11 +2931,36 @@ }, "leagueId": { "type": "string" + }, + "seasonId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "amount": { + "type": "number" + }, + "enabled": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" } }, "required": [ "id", - "leagueId" + "leagueId", + "type", + "amount", + "enabled", + "createdAt", + "updatedAt" ] }, "MemberPaymentDTO": { @@ -1575,6 +2983,17 @@ }, "netAmount": { "type": "number" + }, + "status": { + "type": "string" + }, + "dueDate": { + "type": "string", + "format": "date-time" + }, + "paidAt": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -1583,7 +3002,9 @@ "driverId", "amount", "platformFee", - "netAmount" + "netAmount", + "status", + "dueDate" ] }, "PrizeDTO": { @@ -1606,6 +3027,26 @@ }, "amount": { "type": "number" + }, + "type": { + "type": "string" + }, + "description": { + "type": "string" + }, + "awarded": { + "type": "boolean" + }, + "awardedTo": { + "type": "string" + }, + "awardedAt": { + "type": "string", + "format": "date-time" + }, + "createdAt": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -1614,7 +3055,10 @@ "seasonId", "position", "name", - "amount" + "amount", + "type", + "awarded", + "createdAt" ] }, "WalletDTO": { @@ -1665,11 +3109,83 @@ }, "walletId": { "type": "string" + }, + "type": { + "type": "string" + }, + "amount": { + "type": "number" + }, + "description": { + "type": "string" + }, + "referenceId": { + "type": "string" + }, + "referenceType": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" } }, "required": [ "id", - "walletId" + "walletId", + "type", + "amount", + "description", + "createdAt" + ] + }, + "GetWalletResultDTO": { + "type": "object", + "properties": { + "wallet": { + "$ref": "#/components/schemas/WalletDTO" + }, + "transactions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionDTO" + } + } + }, + "required": [ + "wallet", + "transactions" + ] + }, + "GetPrizesResultDTO": { + "type": "object", + "properties": { + "prizes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PrizeDTO" + } + } + }, + "required": [ + "prizes" + ] + }, + "GetMembershipFeesResultDTO": { + "type": "object", + "properties": { + "fee": { + "$ref": "#/components/schemas/MembershipFeeDTO" + }, + "payments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberPaymentDTO" + } + } + }, + "required": [ + "payments" ] }, "DeletePrizeResultDTO": { @@ -1683,11 +3199,322 @@ "success" ] }, + "CreatePrizeResultDTO": { + "type": "object", + "properties": { + "prize": { + "$ref": "#/components/schemas/PrizeDTO" + } + }, + "required": [ + "prize" + ] + }, + "CreatePaymentOutputDTO": { + "type": "object", + "properties": { + "payment": { + "$ref": "#/components/schemas/PaymentDTO" + } + }, + "required": [ + "payment" + ] + }, + "CreatePaymentInputDTO": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "amount": { + "type": "number" + }, + "payerId": { + "type": "string" + }, + "payerType": { + "type": "string" + }, + "leagueId": { + "type": "string" + }, + "seasonId": { + "type": "string" + } + }, + "required": [ + "type", + "amount", + "payerId", + "payerType", + "leagueId" + ] + }, + "AwardPrizeResultDTO": { + "type": "object", + "properties": { + "prize": { + "$ref": "#/components/schemas/PrizeDTO" + } + }, + "required": [ + "prize" + ] + }, + "UploadMediaOutputDTO": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "mediaId": { + "type": "string" + }, + "url": { + "type": "string" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + }, + "UploadMediaInputDTO": { + "type": "object", + "properties": { + "file": { + "type": "object" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + } + }, + "required": [ + "file", + "type" + ] + }, + "UpdateAvatarOutputDTO": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + }, + "UpdateAvatarInputDTO": { + "type": "object", + "properties": { + "driverId": { + "type": "string" + }, + "avatarUrl": { + "type": "string" + } + }, + "required": [ + "driverId", + "avatarUrl" + ] + }, + "RequestAvatarGenerationOutputDTO": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "requestId": { + "type": "string" + }, + "avatarUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "errorMessage": { + "type": "string" + } + }, + "required": [ + "success" + ] + }, + "RequestAvatarGenerationInputDTO": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "facePhotoData": { + "type": "string" + }, + "suitColor": { + "type": "string" + } + }, + "required": [ + "userId", + "facePhotoData", + "suitColor" + ] + }, + "GetMediaOutputDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "uploadedAt": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "number" + } + }, + "required": [ + "id", + "url", + "type", + "uploadedAt" + ] + }, + "GetAvatarOutputDTO": { + "type": "object", + "properties": { + "avatarUrl": { + "type": "string" + } + }, + "required": [ + "avatarUrl" + ] + }, + "DeleteMediaOutputDTO": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + }, + "WizardStepDTO": { + "type": "object", + "properties": { + "value": { + "type": "string" + } + }, + "required": [ + "value" + ] + }, + "WizardErrorsBasicsDTO": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "visibility": { + "type": "string" + } + } + }, + "WizardErrorsStructureDTO": { + "type": "object", + "properties": { + "maxDrivers": { + "type": "string" + }, + "maxTeams": { + "type": "string" + }, + "driversPerTeam": { + "type": "string" + } + } + }, + "WizardErrorsTimingsDTO": { + "type": "object", + "properties": { + "qualifyingMinutes": { + "type": "string" + }, + "mainRaceMinutes": { + "type": "string" + }, + "roundsPlanned": { + "type": "string" + } + } + }, + "WizardErrorsScoringDTO": { + "type": "object", + "properties": { + "patternId": { + "type": "string" + } + } + }, + "WizardErrorsDTO": { + "type": "object", + "properties": { + "basics": { + "$ref": "#/components/schemas/WizardErrorsBasicsDTO" + }, + "structure": { + "$ref": "#/components/schemas/WizardErrorsStructureDTO" + }, + "timings": { + "$ref": "#/components/schemas/WizardErrorsTimingsDTO" + }, + "scoring": { + "$ref": "#/components/schemas/WizardErrorsScoringDTO" + }, + "submit": { + "type": "string" + } + } + }, "WithdrawFromLeagueWalletOutputDTO": { "type": "object", "properties": { "success": { "type": "boolean" + }, + "message": { + "type": "string" } }, "required": [ @@ -1722,6 +3549,9 @@ "properties": { "success": { "type": "boolean" + }, + "error": { + "type": "string" } }, "required": [ @@ -1739,12 +3569,16 @@ }, "targetDriverId": { "type": "string" + }, + "newRole": { + "type": "string" } }, "required": [ "leagueId", "performerDriverId", - "targetDriverId" + "targetDriverId", + "newRole" ] }, "TotalLeaguesDTO": { @@ -1769,12 +3603,31 @@ }, "leagueId": { "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + }, + "isPrimary": { + "type": "boolean" + }, + "seasonGroupId": { + "type": "string" } }, "required": [ "seasonId", "name", - "leagueId" + "leagueId", + "status", + "isPrimary" ] }, "RemoveLeagueMemberOutputDTO": { @@ -1782,6 +3635,9 @@ "properties": { "success": { "type": "boolean" + }, + "error": { + "type": "string" } }, "required": [ @@ -1812,6 +3668,9 @@ "properties": { "success": { "type": "boolean" + }, + "message": { + "type": "string" } }, "required": [ @@ -1857,6 +3716,9 @@ }, "description": { "type": "string" + }, + "status": { + "type": "string" } }, "required": [ @@ -1866,7 +3728,30 @@ "protestingDriverId", "accusedDriverId", "submittedAt", - "description" + "description", + "status" + ] + }, + "MembershipStatusDTO": { + "type": "object", + "properties": { + "value": { + "type": "string" + } + }, + "required": [ + "value" + ] + }, + "MembershipRoleDTO": { + "type": "object", + "properties": { + "value": { + "type": "string" + } + }, + "required": [ + "value" ] }, "LeagueWithCapacityDTO": { @@ -1877,11 +3762,43 @@ }, "name": { "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "ownerId": { + "type": "string" + }, + "settings": { + "$ref": "#/components/schemas/LeagueSettingsDTO" + }, + "createdAt": { + "type": "string" + }, + "usedSlots": { + "type": "number" + }, + "socialLinks": { + "type": "object" + }, + "discordUrl": { + "type": "string" + }, + "youtubeUrl": { + "type": "string" + }, + "websiteUrl": { + "type": "string" } }, "required": [ "id", - "name" + "name", + "ownerId", + "settings", + "createdAt", + "usedSlots" ] }, "LeagueSummaryDTO": { @@ -1892,11 +3809,51 @@ }, "name": { "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "logoUrl": { + "type": "string", + "nullable": true + }, + "coverImage": { + "type": "string", + "nullable": true + }, + "memberCount": { + "type": "number" + }, + "maxMembers": { + "type": "number" + }, + "isPublic": { + "type": "boolean" + }, + "ownerId": { + "type": "string" + }, + "ownerName": { + "type": "string", + "nullable": true + }, + "scoringType": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "nullable": true } }, "required": [ "id", - "name" + "name", + "memberCount", + "maxMembers", + "isPublic", + "ownerId" ] }, "LeagueStatsDTO": { @@ -1918,17 +3875,64 @@ "averageRating" ] }, + "LeagueStandingsDTO": { + "type": "object", + "properties": { + "standings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueStandingDTO" + } + } + }, + "required": [ + "standings" + ] + }, "LeagueStandingDTO": { "type": "object", "properties": { "driverId": { "type": "string" + }, + "driver": { + "$ref": "#/components/schemas/DriverDTO" + }, + "points": { + "type": "number" + }, + "position": { + "type": "number" + }, + "wins": { + "type": "number" + }, + "podiums": { + "type": "number" + }, + "races": { + "type": "number" } }, "required": [ - "driverId" + "driverId", + "driver", + "points", + "position", + "wins", + "podiums", + "races" ] }, + "LeagueSettingsDTO": { + "type": "object", + "properties": { + "maxDrivers": { + "type": "number", + "nullable": true + } + } + }, "LeagueSeasonSummaryDTO": { "type": "object", "properties": { @@ -1940,12 +3944,28 @@ }, "status": { "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "isPrimary": { + "type": "boolean" + }, + "isParallelActive": { + "type": "boolean" } }, "required": [ "seasonId", "name", - "status" + "status", + "isPrimary", + "isParallelActive" ] }, "LeagueScoringPresetDTO": { @@ -1959,12 +3979,28 @@ }, "description": { "type": "string" + }, + "primaryChampionshipType": { + "type": "string" + }, + "sessionSummary": { + "type": "string" + }, + "bonusSummary": { + "type": "string" + }, + "dropPolicySummary": { + "type": "string" } }, "required": [ "id", "name", - "description" + "description", + "primaryChampionshipType", + "sessionSummary", + "bonusSummary", + "dropPolicySummary" ] }, "LeagueScoringChampionshipDTO": { @@ -1986,6 +4022,18 @@ } }, "pointsPreview": { + "type": "array", + "items": { + "type": "object" + } + }, + "bonusSummary": { + "type": "array", + "items": { + "type": "string" + } + }, + "dropPolicyDescription": { "type": "string" } }, @@ -1994,7 +4042,9 @@ "name", "type", "sessionTypes", - "pointsPreview" + "pointsPreview", + "bonusSummary", + "dropPolicyDescription" ] }, "LeagueScoringConfigDTO": { @@ -2011,13 +4061,88 @@ }, "gameName": { "type": "string" + }, + "scoringPresetId": { + "type": "string" + }, + "scoringPresetName": { + "type": "string" + }, + "dropPolicySummary": { + "type": "string" + }, + "championships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueScoringChampionshipDTO" + } } }, "required": [ "leagueId", "seasonId", "gameId", - "gameName" + "gameName", + "dropPolicySummary", + "championships" + ] + }, + "LeagueScheduleDTO": { + "type": "object", + "properties": { + "races": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RaceDTO" + } + } + }, + "required": [ + "races" + ] + }, + "LeagueRoleDTO": { + "type": "object", + "properties": { + "value": { + "type": "string" + } + }, + "required": [ + "value" + ] + }, + "LeagueOwnerSummaryDTO": { + "type": "object", + "properties": { + "driver": { + "$ref": "#/components/schemas/DriverDTO" + }, + "rating": { + "type": "number", + "nullable": true + }, + "rank": { + "type": "number", + "nullable": true + } + }, + "required": [ + "driver" + ] + }, + "LeagueMembershipsDTO": { + "type": "object", + "properties": { + "members": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueMemberDTO" + } + } + }, + "required": [ + "members" ] }, "LeagueMembershipDTO": { @@ -2031,12 +4156,24 @@ }, "driverId": { "type": "string" + }, + "role": { + "type": "string" + }, + "status": { + "type": "string" + }, + "joinedAt": { + "type": "string" } }, "required": [ "id", "leagueId", - "driverId" + "driverId", + "role", + "status", + "joinedAt" ] }, "LeagueMemberDTO": { @@ -2044,10 +4181,23 @@ "properties": { "driverId": { "type": "string" + }, + "driver": { + "$ref": "#/components/schemas/DriverDTO" + }, + "role": { + "type": "string" + }, + "joinedAt": { + "type": "string", + "format": "date-time" } }, "required": [ - "driverId" + "driverId", + "driver", + "role", + "joinedAt" ] }, "LeagueJoinRequestDTO": { @@ -2065,13 +4215,27 @@ "requestedAt": { "type": "string", "format": "date-time" + }, + "message": { + "type": "string" + }, + "required": { + "type": "string" + }, + "type": { + "type": "string" + }, + "driver": { + "type": "string" } }, "required": [ "id", "leagueId", "driverId", - "requestedAt" + "requestedAt", + "required", + "type" ] }, "LeagueConfigFormModelTimingsDTO": { @@ -2104,6 +4268,48 @@ "mode" ] }, + "LeagueConfigFormModelStewardingDTO": { + "type": "object", + "properties": { + "decisionMode": { + "type": "string" + }, + "requiredVotes": { + "type": "number" + }, + "requireDefense": { + "type": "boolean" + }, + "defenseTimeLimit": { + "type": "number" + }, + "voteTimeLimit": { + "type": "number" + }, + "protestDeadlineHours": { + "type": "number" + }, + "stewardingClosesHours": { + "type": "number" + }, + "notifyAccusedOnProtest": { + "type": "boolean" + }, + "notifyOnVoteRequired": { + "type": "boolean" + } + }, + "required": [ + "decisionMode", + "requireDefense", + "defenseTimeLimit", + "voteTimeLimit", + "protestDeadlineHours", + "stewardingClosesHours", + "notifyAccusedOnProtest", + "notifyOnVoteRequired" + ] + }, "LeagueConfigFormModelScoringDTO": { "type": "object", "properties": { @@ -2119,15 +4325,60 @@ "points" ] }, + "LeagueConfigFormModelDropPolicyDTO": { + "type": "object", + "properties": { + "strategy": { + "type": "string" + }, + "n": { + "type": "number" + } + }, + "required": [ + "strategy" + ] + }, "LeagueConfigFormModelDTO": { "type": "object", "properties": { "leagueId": { "type": "string" + }, + "basics": { + "$ref": "#/components/schemas/LeagueConfigFormModelBasicsDTO" + }, + "structure": { + "$ref": "#/components/schemas/LeagueConfigFormModelStructureDTO" + }, + "championships": { + "type": "array", + "items": { + "type": "object" + } + }, + "scoring": { + "$ref": "#/components/schemas/LeagueConfigFormModelScoringDTO" + }, + "dropPolicy": { + "$ref": "#/components/schemas/LeagueConfigFormModelDropPolicyDTO" + }, + "timings": { + "$ref": "#/components/schemas/LeagueConfigFormModelTimingsDTO" + }, + "stewarding": { + "$ref": "#/components/schemas/LeagueConfigFormModelStewardingDTO" } }, "required": [ - "leagueId" + "leagueId", + "basics", + "structure", + "championships", + "scoring", + "dropPolicy", + "timings", + "stewarding" ] }, "LeagueConfigFormModelBasicsDTO": { @@ -2138,11 +4389,37 @@ }, "description": { "type": "string" + }, + "visibility": { + "type": "string" } }, "required": [ "name", - "description" + "description", + "visibility" + ] + }, + "LeagueAdminProtestsDTO": { + "type": "object", + "properties": { + "protests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProtestDTO" + } + }, + "racesById": { + "$ref": "#/components/schemas/RaceDTO" + }, + "driversById": { + "$ref": "#/components/schemas/DriverDTO" + } + }, + "required": [ + "protests", + "racesById", + "driversById" ] }, "LeagueAdminPermissionsDTO": { @@ -2160,15 +4437,100 @@ "canUpdateRoles" ] }, + "LeagueAdminDTO": { + "type": "object", + "properties": { + "joinRequests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueJoinRequestDTO" + } + }, + "ownerSummary": { + "$ref": "#/components/schemas/LeagueOwnerSummaryDTO" + }, + "config": { + "$ref": "#/components/schemas/LeagueAdminConfigDTO" + }, + "protests": { + "$ref": "#/components/schemas/LeagueAdminProtestsDTO" + }, + "seasons": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueSeasonSummaryDTO" + } + } + }, + "required": [ + "joinRequests", + "config", + "protests", + "seasons" + ] + }, + "LeagueAdminConfigDTO": { + "type": "object", + "properties": { + "form": { + "$ref": "#/components/schemas/LeagueConfigFormModelDTO" + } + } + }, + "GetSeasonSponsorshipsOutputDTO": { + "type": "object", + "properties": { + "sponsorships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SponsorshipDetailDTO" + } + } + }, + "required": [ + "sponsorships" + ] + }, "WalletTransactionDTO": { "type": "object", "properties": { "id": { "type": "string" + }, + "type": { + "type": "string" + }, + "description": { + "type": "string" + }, + "amount": { + "type": "number" + }, + "fee": { + "type": "number" + }, + "netAmount": { + "type": "number" + }, + "date": { + "type": "string" + }, + "status": { + "type": "string" + }, + "reference": { + "type": "string" } }, "required": [ - "id" + "id", + "type", + "description", + "amount", + "fee", + "netAmount", + "date", + "status" ] }, "GetLeagueWalletOutputDTO": { @@ -2194,6 +4556,15 @@ }, "canWithdraw": { "type": "boolean" + }, + "withdrawalBlockReason": { + "type": "string" + }, + "transactions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WalletTransactionDTO" + } } }, "required": [ @@ -2203,7 +4574,8 @@ "totalFees", "totalWithdrawals", "pendingPayouts", - "canWithdraw" + "canWithdraw", + "transactions" ] }, "GetLeagueSeasonsQueryDTO": { @@ -2217,6 +4589,20 @@ "leagueId" ] }, + "GetLeagueRacesOutputDTO": { + "type": "object", + "properties": { + "races": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RaceDTO" + } + } + }, + "required": [ + "races" + ] + }, "GetLeagueProtestsQueryDTO": { "type": "object", "properties": { @@ -2280,6 +4666,14 @@ "leagueId" ] }, + "GetLeagueAdminConfigOutputDTO": { + "type": "object", + "properties": { + "form": { + "$ref": "#/components/schemas/LeagueConfigFormModelDTO" + } + } + }, "CreateLeagueOutputDTO": { "type": "object", "properties": { @@ -2303,11 +4697,19 @@ }, "description": { "type": "string" + }, + "visibility": { + "type": "string" + }, + "ownerId": { + "type": "string" } }, "required": [ "name", - "description" + "description", + "visibility", + "ownerId" ] }, "ApproveJoinRequestOutputDTO": { @@ -2315,6 +4717,9 @@ "properties": { "success": { "type": "boolean" + }, + "message": { + "type": "string" } }, "required": [ @@ -2336,6 +4741,42 @@ "leagueId" ] }, + "AllLeaguesWithCapacityDTO": { + "type": "object", + "properties": { + "leagues": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueWithCapacityDTO" + } + }, + "totalCount": { + "type": "number" + } + }, + "required": [ + "leagues", + "totalCount" + ] + }, + "AllLeaguesWithCapacityAndScoringDTO": { + "type": "object", + "properties": { + "leagues": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeagueSummaryDTO" + } + }, + "totalCount": { + "type": "number" + } + }, + "required": [ + "leagues", + "totalCount" + ] + }, "GetDriverRegistrationStatusQueryDTO": { "type": "object", "properties": { @@ -2351,6 +4792,36 @@ "driverId" ] }, + "GetDriverProfileOutputDTO": { + "type": "object", + "properties": { + "currentDriver": { + "$ref": "#/components/schemas/DriverProfileDriverSummaryDTO" + }, + "stats": { + "$ref": "#/components/schemas/DriverProfileStatsDTO" + }, + "finishDistribution": { + "$ref": "#/components/schemas/DriverProfileFinishDistributionDTO" + }, + "teamMemberships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DriverProfileTeamMembershipDTO" + } + }, + "socialSummary": { + "$ref": "#/components/schemas/DriverProfileSocialSummaryDTO" + }, + "extendedProfile": { + "$ref": "#/components/schemas/DriverProfileExtendedProfileDTO" + } + }, + "required": [ + "teamMemberships", + "socialSummary" + ] + }, "GetDriverOutputDTO": { "type": "object", "properties": { @@ -2381,6 +4852,32 @@ "joinedAt" ] }, + "DriversLeaderboardDTO": { + "type": "object", + "properties": { + "drivers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DriverLeaderboardItemDTO" + } + }, + "totalRaces": { + "type": "number" + }, + "totalWins": { + "type": "number" + }, + "activeCount": { + "type": "number" + } + }, + "required": [ + "drivers", + "totalRaces", + "totalWins", + "activeCount" + ] + }, "DriverStatsDTO": { "type": "object", "properties": { @@ -2419,11 +4916,27 @@ }, "teamName": { "type": "string" + }, + "teamTag": { + "type": "string", + "nullable": true + }, + "role": { + "type": "string" + }, + "joinedAt": { + "type": "string" + }, + "isCurrent": { + "type": "boolean" } }, "required": [ "teamId", - "teamName" + "teamName", + "role", + "joinedAt", + "isCurrent" ] }, "DriverProfileStatsDTO": { @@ -2440,6 +4953,46 @@ }, "dnfs": { "type": "number" + }, + "avgFinish": { + "type": "number", + "nullable": true + }, + "bestFinish": { + "type": "number", + "nullable": true + }, + "worstFinish": { + "type": "number", + "nullable": true + }, + "finishRate": { + "type": "number", + "nullable": true + }, + "winRate": { + "type": "number", + "nullable": true + }, + "podiumRate": { + "type": "number", + "nullable": true + }, + "percentile": { + "type": "number", + "nullable": true + }, + "rating": { + "type": "number", + "nullable": true + }, + "consistency": { + "type": "number", + "nullable": true + }, + "overallRank": { + "type": "number", + "nullable": true } }, "required": [ @@ -2454,10 +5007,36 @@ "properties": { "friendsCount": { "type": "number" + }, + "friends": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DriverProfileSocialFriendSummaryDTO" + } } }, "required": [ - "friendsCount" + "friendsCount", + "friends" + ] + }, + "DriverProfileSocialHandleDTO": { + "type": "object", + "properties": { + "platform": { + "type": "string" + }, + "handle": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "platform", + "handle", + "url" ] }, "DriverProfileSocialFriendSummaryDTO": { @@ -2514,6 +5093,55 @@ "other" ] }, + "DriverProfileExtendedProfileDTO": { + "type": "object", + "properties": { + "socialHandles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DriverProfileSocialHandleDTO" + } + }, + "achievements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DriverProfileAchievementDTO" + } + }, + "racingStyle": { + "type": "string" + }, + "favoriteTrack": { + "type": "string" + }, + "favoriteCar": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "availableHours": { + "type": "string" + }, + "lookingForTeam": { + "type": "boolean" + }, + "openToRequests": { + "type": "boolean" + } + }, + "required": [ + "socialHandles", + "achievements", + "racingStyle", + "favoriteTrack", + "favoriteCar", + "timezone", + "availableHours", + "lookingForTeam", + "openToRequests" + ] + }, "DriverProfileDriverSummaryDTO": { "type": "object", "properties": { @@ -2528,13 +5156,41 @@ }, "avatarUrl": { "type": "string" + }, + "iracingId": { + "type": "string", + "nullable": true + }, + "joinedAt": { + "type": "string" + }, + "rating": { + "type": "number", + "nullable": true + }, + "globalRank": { + "type": "number", + "nullable": true + }, + "consistency": { + "type": "number", + "nullable": true + }, + "bio": { + "type": "string", + "nullable": true + }, + "totalDrivers": { + "type": "number", + "nullable": true } }, "required": [ "id", "name", "country", - "avatarUrl" + "avatarUrl", + "joinedAt" ] }, "DriverProfileAchievementDTO": { @@ -2548,12 +5204,24 @@ }, "description": { "type": "string" + }, + "icon": { + "type": "string" + }, + "rarity": { + "type": "string" + }, + "earnedAt": { + "type": "string" } }, "required": [ "id", "title", - "description" + "description", + "icon", + "rarity", + "earnedAt" ] }, "DriverLeaderboardItemDTO": { @@ -2588,6 +5256,10 @@ }, "rank": { "type": "number" + }, + "avatarUrl": { + "type": "string", + "nullable": true } }, "required": [ @@ -2603,11 +5275,47 @@ "rank" ] }, + "DriverDTO": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "iracingId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "country": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "joinedAt": { + "type": "string" + } + }, + "required": [ + "id", + "iracingId", + "name", + "country", + "joinedAt" + ] + }, "CompleteOnboardingOutputDTO": { "type": "object", "properties": { "success": { "type": "boolean" + }, + "driverId": { + "type": "string" + }, + "errorMessage": { + "type": "string" } }, "required": [ @@ -2628,6 +5336,12 @@ }, "country": { "type": "string" + }, + "timezone": { + "type": "string" + }, + "bio": { + "type": "string" } }, "required": [ @@ -2671,6 +5385,82 @@ "user" ] }, + "SignupParamsDTO": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "iracingCustomerId": { + "type": "string" + }, + "primaryDriverId": { + "type": "string" + }, + "avatarUrl": { + "type": "string" + } + }, + "required": [ + "email", + "password", + "displayName" + ] + }, + "LoginParamsDTO": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "email", + "password" + ] + }, + "IracingAuthRedirectResultDTO": { + "type": "object", + "properties": { + "redirectUrl": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "redirectUrl", + "state" + ] + }, + "LoginWithIracingCallbackParamsDTO": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "state": { + "type": "string" + }, + "returnTo": { + "type": "string" + } + }, + "required": [ + "code", + "state" + ] + }, "RecordPageViewOutputDTO": { "type": "object", "properties": { @@ -2682,6 +5472,41 @@ "pageViewId" ] }, + "RecordPageViewInputDTO": { + "type": "object", + "properties": { + "entityType": { + "type": "string" + }, + "entityId": { + "type": "string" + }, + "visitorId": { + "type": "string" + }, + "visitorType": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "referrer": { + "type": "string" + }, + "userAgent": { + "type": "string" + }, + "country": { + "type": "string" + } + }, + "required": [ + "entityType", + "entityId", + "visitorType", + "sessionId" + ] + }, "RecordEngagementOutputDTO": { "type": "object", "properties": { @@ -2697,6 +5522,42 @@ "engagementWeight" ] }, + "RecordEngagementInputDTO": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "entityType": { + "type": "string" + }, + "entityId": { + "type": "string" + }, + "actorId": { + "type": "string" + }, + "actorType": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "action", + "entityType", + "entityId", + "actorType", + "sessionId" + ] + }, "GetDashboardDataOutputDTO": { "type": "object", "properties": { diff --git a/apps/api/src/domain/auth/AuthController.ts b/apps/api/src/domain/auth/AuthController.ts index 99da68fef..a36800901 100644 --- a/apps/api/src/domain/auth/AuthController.ts +++ b/apps/api/src/domain/auth/AuthController.ts @@ -1,6 +1,6 @@ import { Controller, Get, Post, Body, Query } from '@nestjs/common'; import { AuthService } from './AuthService'; -import { LoginParams, SignupParams, AuthSessionDTO } from './dtos/AuthDto'; +import { LoginParamsDTO, SignupParamsDTO, AuthSessionDTO } from './dtos/AuthDto'; import type { CommandResultDTO } from './presenters/CommandResultPresenter'; @Controller('auth') @@ -8,12 +8,12 @@ export class AuthController { constructor(private readonly authService: AuthService) {} @Post('signup') - async signup(@Body() params: SignupParams): Promise { + async signup(@Body() params: SignupParamsDTO): Promise { return this.authService.signupWithEmail(params); } @Post('login') - async login(@Body() params: LoginParams): Promise { + async login(@Body() params: LoginParamsDTO): Promise { return this.authService.loginWithEmail(params); } diff --git a/apps/api/src/domain/auth/AuthService.ts b/apps/api/src/domain/auth/AuthService.ts index ca1a39fe5..bbb14e997 100644 --- a/apps/api/src/domain/auth/AuthService.ts +++ b/apps/api/src/domain/auth/AuthService.ts @@ -26,7 +26,7 @@ import { SIGNUP_USE_CASE_TOKEN, } from './AuthProviders'; import type { AuthSessionDTO } from './dtos/AuthDto'; -import { LoginParams, SignupParams } from './dtos/AuthDto'; +import { LoginParamsDTO, SignupParamsDTO } from './dtos/AuthDto'; import { AuthSessionPresenter } from './presenters/AuthSessionPresenter'; import type { CommandResultDTO } from './presenters/CommandResultPresenter'; import { CommandResultPresenter } from './presenters/CommandResultPresenter'; @@ -67,7 +67,7 @@ export class AuthService { }; } - async signupWithEmail(params: SignupParams): Promise { + async signupWithEmail(params: SignupParamsDTO): Promise { this.logger.debug(`[AuthService] Attempting signup for email: ${params.email}`); this.authSessionPresenter.reset(); @@ -98,7 +98,7 @@ export class AuthService { }; } - async loginWithEmail(params: LoginParams): Promise { + async loginWithEmail(params: LoginParamsDTO): Promise { this.logger.debug(`[AuthService] Attempting login for email: ${params.email}`); this.authSessionPresenter.reset(); @@ -142,4 +142,51 @@ export class AuthService { return this.commandResultPresenter.responseModel; } + + /** + * Start iRacing OAuth flow. + * + * NOTE: This is a placeholder implementation for the current alpha build. + * A production implementation should delegate to a dedicated iRacing OAuth port + * and persist/validate state server-side. + */ + async startIracingAuth(returnTo?: string): Promise { + this.logger.debug('[AuthService] Starting iRacing auth flow', { returnTo }); + + const state = Math.random().toString(36).slice(2); + const base = 'https://example.com/iracing/auth'; + const query = new URLSearchParams(); + query.set('state', state); + if (returnTo) { + query.set('returnTo', returnTo); + } + + return `${base}?${query.toString()}`; + } + + /** + * Handle iRacing OAuth callback. + * + * NOTE: Placeholder implementation that creates a demo session. + */ + async iracingCallback(code: string, state: string, returnTo?: string): Promise { + this.logger.debug('[AuthService] iRacing callback received', { hasCode: !!code, state, returnTo }); + + const userId = `iracing-${state || code}`.slice(0, 64); + + const session = await this.identitySessionPort.createSession({ + id: userId, + displayName: 'iRacing User', + email: '', + }); + + return { + token: session.token, + user: { + userId, + email: '', + displayName: 'iRacing User', + }, + }; + } } diff --git a/apps/api/src/domain/auth/dtos/AuthDto.ts b/apps/api/src/domain/auth/dtos/AuthDto.ts index 9c6f592c3..a1ddd86a7 100644 --- a/apps/api/src/domain/auth/dtos/AuthDto.ts +++ b/apps/api/src/domain/auth/dtos/AuthDto.ts @@ -16,7 +16,7 @@ export class AuthSessionDTO { user!: AuthenticatedUserDTO; } -export class SignupParams { +export class SignupParamsDTO { @ApiProperty() email!: string; @ApiProperty() @@ -31,21 +31,21 @@ export class SignupParams { avatarUrl?: string; } -export class LoginParams { +export class LoginParamsDTO { @ApiProperty() email!: string; @ApiProperty() password!: string; } -export class IracingAuthRedirectResult { +export class IracingAuthRedirectResultDTO { @ApiProperty() redirectUrl!: string; @ApiProperty() state!: string; } -export class LoginWithIracingCallbackParams { +export class LoginWithIracingCallbackParamsDTO { @ApiProperty() code!: string; @ApiProperty() diff --git a/apps/api/src/domain/league/dtos/WizardErrorsDTO.ts b/apps/api/src/domain/league/dtos/WizardErrorsDTO.ts index 015131064..6d0b869bd 100644 --- a/apps/api/src/domain/league/dtos/WizardErrorsDTO.ts +++ b/apps/api/src/domain/league/dtos/WizardErrorsDTO.ts @@ -2,7 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsOptional, IsString, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -class WizardErrorsBasicsDTO { +export class WizardErrorsBasicsDTO { @ApiProperty({ required: false }) @IsOptional() @IsString() @@ -19,7 +19,7 @@ class WizardErrorsBasicsDTO { visibility?: string; } -class WizardErrorsStructureDTO { +export class WizardErrorsStructureDTO { @ApiProperty({ required: false }) @IsOptional() @IsString() @@ -36,7 +36,7 @@ class WizardErrorsStructureDTO { driversPerTeam?: string; } -class WizardErrorsTimingsDTO { +export class WizardErrorsTimingsDTO { @ApiProperty({ required: false }) @IsOptional() @IsString() @@ -53,7 +53,7 @@ class WizardErrorsTimingsDTO { roundsPlanned?: string; } -class WizardErrorsScoringDTO { +export class WizardErrorsScoringDTO { @ApiProperty({ required: false }) @IsOptional() @IsString() @@ -89,4 +89,4 @@ export class WizardErrorsDTO { @IsOptional() @IsString() submit?: string; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/league/presenters/LeagueStandingsPresenter.ts b/apps/api/src/domain/league/presenters/LeagueStandingsPresenter.ts index 1dd5d2f01..5923b2539 100644 --- a/apps/api/src/domain/league/presenters/LeagueStandingsPresenter.ts +++ b/apps/api/src/domain/league/presenters/LeagueStandingsPresenter.ts @@ -21,7 +21,12 @@ export class LeagueStandingsPresenter implements Presenter ProtestIncidentDTO) + incident!: ProtestIncidentDTO; @ApiProperty({ required: false }) @IsOptional() @@ -34,4 +34,4 @@ export class FileProtestCommandDTO { @IsString() @IsUrl() proofVideoUrl?: string; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/race/dtos/ProtestIncidentDTO.ts b/apps/api/src/domain/race/dtos/ProtestIncidentDTO.ts new file mode 100644 index 000000000..a212c41c7 --- /dev/null +++ b/apps/api/src/domain/race/dtos/ProtestIncidentDTO.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt, IsNotEmpty, IsOptional, IsString, Min } from 'class-validator'; + +export class ProtestIncidentDTO { + @ApiProperty() + @IsInt() + @Min(0) + lap!: number; + + @ApiProperty() + @IsString() + @IsNotEmpty() + description!: string; + + @ApiProperty({ required: false, description: 'Seconds from race start' }) + @IsOptional() + @IsInt() + @Min(0) + timeInRace?: number; +} + diff --git a/apps/api/src/domain/sponsor/SponsorController.ts b/apps/api/src/domain/sponsor/SponsorController.ts index 1bc76a4e7..07301ec93 100644 --- a/apps/api/src/domain/sponsor/SponsorController.ts +++ b/apps/api/src/domain/sponsor/SponsorController.ts @@ -18,8 +18,8 @@ import { InvoiceDTO } from './dtos/InvoiceDTO'; import { BillingStatsDTO } from './dtos/BillingStatsDTO'; import { AvailableLeagueDTO } from './dtos/AvailableLeagueDTO'; import { LeagueDetailDTO } from './dtos/LeagueDetailDTO'; -import { DriverDTO } from './dtos/DriverDTO'; -import { RaceDTO } from './dtos/RaceDTO'; +import { SponsorDriverDTO } from './dtos/SponsorDriverDTO'; +import { SponsorRaceDTO } from './dtos/RaceDTO'; import { SponsorProfileDTO } from './dtos/SponsorProfileDTO'; import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO'; import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO'; @@ -194,8 +194,8 @@ export class SponsorController { @Param('leagueId') leagueId: string, ): Promise<{ league: LeagueDetailDTO; - drivers: DriverDTO[]; - races: RaceDTO[]; + drivers: SponsorDriverDTO[]; + races: SponsorRaceDTO[]; } | null> { const presenter = await this.sponsorService.getLeagueDetail(leagueId); return presenter.viewModel; diff --git a/apps/api/src/domain/sponsor/SponsorService.ts b/apps/api/src/domain/sponsor/SponsorService.ts index 038fc3aab..af4d577cb 100644 --- a/apps/api/src/domain/sponsor/SponsorService.ts +++ b/apps/api/src/domain/sponsor/SponsorService.ts @@ -11,8 +11,8 @@ import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshi import { GetSponsorsOutputDTO } from './dtos/GetSponsorsOutputDTO'; import { AvailableLeagueDTO } from './dtos/AvailableLeagueDTO'; import { LeagueDetailDTO } from './dtos/LeagueDetailDTO'; -import { DriverDTO } from './dtos/DriverDTO'; -import { RaceDTO } from './dtos/RaceDTO'; +import { SponsorDriverDTO } from './dtos/SponsorDriverDTO'; +import { SponsorRaceDTO } from './dtos/RaceDTO'; import { SponsorProfileDTO } from './dtos/SponsorProfileDTO'; import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO'; import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO'; @@ -399,7 +399,7 @@ export class SponsorService { }, }; - const drivers: DriverDTO[] = [ + const drivers: SponsorDriverDTO[] = [ { id: 'd1', name: 'Max Verstappen', @@ -420,7 +420,7 @@ export class SponsorService { }, ]; - const races: RaceDTO[] = [ + const races: SponsorRaceDTO[] = [ { id: 'r1', name: 'Spa-Francorchamps', @@ -508,4 +508,4 @@ export class SponsorService { presenter.present({ success: true }); return presenter; } -} \ No newline at end of file +} diff --git a/apps/api/src/domain/sponsor/dtos/RaceDTO.ts b/apps/api/src/domain/sponsor/dtos/RaceDTO.ts index 2b741ff87..462c51b52 100644 --- a/apps/api/src/domain/sponsor/dtos/RaceDTO.ts +++ b/apps/api/src/domain/sponsor/dtos/RaceDTO.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator'; -export class RaceDTO { +export class SponsorRaceDTO { @ApiProperty() @IsString() id: string = ''; @@ -21,4 +21,4 @@ export class RaceDTO { @ApiProperty({ enum: ['upcoming', 'completed'] }) @IsEnum(['upcoming', 'completed']) status: 'upcoming' | 'completed' = 'upcoming'; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/sponsor/dtos/DriverDTO.ts b/apps/api/src/domain/sponsor/dtos/SponsorDriverDTO.ts similarity index 93% rename from apps/api/src/domain/sponsor/dtos/DriverDTO.ts rename to apps/api/src/domain/sponsor/dtos/SponsorDriverDTO.ts index dce2103fc..c98dfc14d 100644 --- a/apps/api/src/domain/sponsor/dtos/DriverDTO.ts +++ b/apps/api/src/domain/sponsor/dtos/SponsorDriverDTO.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsNumber } from 'class-validator'; -export class DriverDTO { +export class SponsorDriverDTO { @ApiProperty() @IsString() id: string = ''; @@ -29,4 +29,5 @@ export class DriverDTO { @ApiProperty() @IsString() team: string = ''; -} \ No newline at end of file +} + diff --git a/apps/api/src/domain/sponsor/presenters/LeagueDetailPresenter.ts b/apps/api/src/domain/sponsor/presenters/LeagueDetailPresenter.ts index 996c55e9a..96481fae6 100644 --- a/apps/api/src/domain/sponsor/presenters/LeagueDetailPresenter.ts +++ b/apps/api/src/domain/sponsor/presenters/LeagueDetailPresenter.ts @@ -1,11 +1,11 @@ import { LeagueDetailDTO } from '../dtos/LeagueDetailDTO'; -import { DriverDTO } from '../dtos/DriverDTO'; -import { RaceDTO } from '../dtos/RaceDTO'; +import { SponsorDriverDTO } from '../dtos/SponsorDriverDTO'; +import { SponsorRaceDTO } from '../dtos/RaceDTO'; export interface LeagueDetailViewModel { league: LeagueDetailDTO; - drivers: DriverDTO[]; - races: RaceDTO[]; + drivers: SponsorDriverDTO[]; + races: SponsorRaceDTO[]; } export class LeagueDetailPresenter { diff --git a/apps/api/src/domain/team/dtos/GetAllTeamsOutputDTO.ts b/apps/api/src/domain/team/dtos/GetAllTeamsOutputDTO.ts index 4f230d5a1..80625c6bc 100644 --- a/apps/api/src/domain/team/dtos/GetAllTeamsOutputDTO.ts +++ b/apps/api/src/domain/team/dtos/GetAllTeamsOutputDTO.ts @@ -1,33 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -class TeamListItemDTO { - @ApiProperty() - id!: string; - - @ApiProperty() - name!: string; - - @ApiProperty() - tag!: string; - - @ApiProperty() - description!: string; - - @ApiProperty() - memberCount!: number; - - @ApiProperty({ type: [String] }) - leagues!: string[]; - - @ApiProperty({ required: false }) - specialization?: 'endurance' | 'sprint' | 'mixed'; - - @ApiProperty({ required: false }) - region?: string; - - @ApiProperty({ type: [String], required: false }) - languages?: string[]; -} +import { TeamListItemDTO } from './TeamListItemDTO'; export class GetAllTeamsOutputDTO { @ApiProperty({ type: [TeamListItemDTO] }) @@ -35,4 +8,4 @@ export class GetAllTeamsOutputDTO { @ApiProperty() totalCount!: number; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/team/dtos/GetDriverTeamOutputDTO.ts b/apps/api/src/domain/team/dtos/GetDriverTeamOutputDTO.ts index f584bce31..31e325cb1 100644 --- a/apps/api/src/domain/team/dtos/GetDriverTeamOutputDTO.ts +++ b/apps/api/src/domain/team/dtos/GetDriverTeamOutputDTO.ts @@ -1,58 +1,18 @@ import { ApiProperty } from '@nestjs/swagger'; -class TeamDTO { - @ApiProperty() - id!: string; - - @ApiProperty() - name!: string; - - @ApiProperty() - tag!: string; - - @ApiProperty() - description!: string; - - @ApiProperty() - ownerId!: string; - - @ApiProperty({ type: [String] }) - leagues!: string[]; - - @ApiProperty({ required: false }) - createdAt?: string; - - @ApiProperty({ required: false }) - specialization?: 'endurance' | 'sprint' | 'mixed'; - - @ApiProperty({ required: false }) - region?: string; - - @ApiProperty({ type: [String], required: false }) - languages?: string[]; -} - -class MembershipDTO { - @ApiProperty() - role!: 'owner' | 'manager' | 'member'; - - @ApiProperty() - joinedAt!: string; - - @ApiProperty() - isActive!: boolean; -} +import { TeamDTO } from './TeamDTO'; +import { TeamMembershipDTO } from './TeamMembershipDTO'; export class GetDriverTeamOutputDTO { @ApiProperty({ type: TeamDTO }) team!: TeamDTO; - @ApiProperty({ type: MembershipDTO }) - membership!: MembershipDTO; + @ApiProperty({ type: TeamMembershipDTO }) + membership!: TeamMembershipDTO; @ApiProperty() isOwner!: boolean; @ApiProperty() canManage!: boolean; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/team/dtos/GetTeamDetailsOutputDTO.ts b/apps/api/src/domain/team/dtos/GetTeamDetailsOutputDTO.ts index 4bb46dd19..dae2a1554 100644 --- a/apps/api/src/domain/team/dtos/GetTeamDetailsOutputDTO.ts +++ b/apps/api/src/domain/team/dtos/GetTeamDetailsOutputDTO.ts @@ -1,55 +1,15 @@ import { ApiProperty } from '@nestjs/swagger'; -class TeamDTO { - @ApiProperty() - id!: string; - - @ApiProperty() - name!: string; - - @ApiProperty() - tag!: string; - - @ApiProperty() - description!: string; - - @ApiProperty() - ownerId!: string; - - @ApiProperty({ type: [String] }) - leagues!: string[]; - - @ApiProperty({ required: false }) - createdAt?: string; - - @ApiProperty({ required: false }) - specialization?: 'endurance' | 'sprint' | 'mixed'; - - @ApiProperty({ required: false }) - region?: string; - - @ApiProperty({ type: [String], required: false }) - languages?: string[]; -} - -class MembershipDTO { - @ApiProperty() - role!: 'owner' | 'manager' | 'member'; - - @ApiProperty() - joinedAt!: string; - - @ApiProperty() - isActive!: boolean; -} +import { TeamDTO } from './TeamDTO'; +import { TeamMembershipDTO } from './TeamMembershipDTO'; export class GetTeamDetailsOutputDTO { @ApiProperty({ type: TeamDTO }) team!: TeamDTO; - @ApiProperty({ type: MembershipDTO, nullable: true }) - membership!: MembershipDTO | null; + @ApiProperty({ type: TeamMembershipDTO, nullable: true }) + membership!: TeamMembershipDTO | null; @ApiProperty() canManage!: boolean; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/team/dtos/GetTeamJoinRequestsOutputDTO.ts b/apps/api/src/domain/team/dtos/GetTeamJoinRequestsOutputDTO.ts index 035ade6f3..dc4b3c761 100644 --- a/apps/api/src/domain/team/dtos/GetTeamJoinRequestsOutputDTO.ts +++ b/apps/api/src/domain/team/dtos/GetTeamJoinRequestsOutputDTO.ts @@ -1,27 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -class TeamJoinRequestDTO { - @ApiProperty() - requestId!: string; - - @ApiProperty() - driverId!: string; - - @ApiProperty() - driverName!: string; - - @ApiProperty() - teamId!: string; - - @ApiProperty() - status!: 'pending' | 'approved' | 'rejected'; - - @ApiProperty() - requestedAt!: string; - - @ApiProperty() - avatarUrl!: string; -} +import { TeamJoinRequestDTO } from './TeamJoinRequestDTO'; export class GetTeamJoinRequestsOutputDTO { @ApiProperty({ type: [TeamJoinRequestDTO] }) @@ -32,4 +11,4 @@ export class GetTeamJoinRequestsOutputDTO { @ApiProperty() totalCount!: number; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/team/dtos/GetTeamMembersOutputDTO.ts b/apps/api/src/domain/team/dtos/GetTeamMembersOutputDTO.ts index d3688a1b4..5f68b9887 100644 --- a/apps/api/src/domain/team/dtos/GetTeamMembersOutputDTO.ts +++ b/apps/api/src/domain/team/dtos/GetTeamMembersOutputDTO.ts @@ -1,24 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -class TeamMemberDTO { - @ApiProperty() - driverId!: string; - - @ApiProperty() - driverName!: string; - - @ApiProperty() - role!: 'owner' | 'manager' | 'member'; - - @ApiProperty() - joinedAt!: string; - - @ApiProperty() - isActive!: boolean; - - @ApiProperty() - avatarUrl!: string; -} +import { TeamMemberDTO } from './TeamMemberDTO'; export class GetTeamMembersOutputDTO { @ApiProperty({ type: [TeamMemberDTO] }) @@ -35,4 +17,4 @@ export class GetTeamMembersOutputDTO { @ApiProperty() memberCount!: number; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/team/dtos/GetTeamsLeaderboardOutputDTO.ts b/apps/api/src/domain/team/dtos/GetTeamsLeaderboardOutputDTO.ts index c1fbf7864..f3c6b5092 100644 --- a/apps/api/src/domain/team/dtos/GetTeamsLeaderboardOutputDTO.ts +++ b/apps/api/src/domain/team/dtos/GetTeamsLeaderboardOutputDTO.ts @@ -1,47 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro'; - -class TeamLeaderboardItemDTO { - @ApiProperty() - id!: string; - - @ApiProperty() - name!: string; - - @ApiProperty() - memberCount!: number; - - @ApiProperty({ nullable: true }) - rating!: number | null; - - @ApiProperty() - totalWins!: number; - - @ApiProperty() - totalRaces!: number; - - @ApiProperty({ enum: ['beginner', 'intermediate', 'advanced', 'pro'] }) - performanceLevel!: SkillLevel; - - @ApiProperty() - isRecruiting!: boolean; - - @ApiProperty() - createdAt!: string; - - @ApiProperty({ required: false }) - description?: string; - - @ApiProperty({ enum: ['endurance', 'sprint', 'mixed'], required: false }) - specialization?: 'endurance' | 'sprint' | 'mixed'; - - @ApiProperty({ required: false }) - region?: string; - - @ApiProperty({ type: [String], required: false }) - languages?: string[]; -} +import { TeamLeaderboardItemDTO, type SkillLevel } from './TeamLeaderboardItemDTO'; export class GetTeamsLeaderboardOutputDTO { @ApiProperty({ type: [TeamLeaderboardItemDTO] }) @@ -55,4 +14,4 @@ export class GetTeamsLeaderboardOutputDTO { @ApiProperty({ type: [TeamLeaderboardItemDTO] }) topTeams!: TeamLeaderboardItemDTO[]; -} \ No newline at end of file +} diff --git a/apps/api/src/domain/team/dtos/TeamDto.ts b/apps/api/src/domain/team/dtos/TeamDto.ts index 2a1d21d00..1c4961614 100644 --- a/apps/api/src/domain/team/dtos/TeamDto.ts +++ b/apps/api/src/domain/team/dtos/TeamDto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString, IsNotEmpty, IsBoolean, IsOptional } from 'class-validator'; +import { IsString, IsNotEmpty, IsBoolean, IsOptional, IsArray } from 'class-validator'; export class TeamListItemViewModel { @ApiProperty() @@ -296,3 +296,38 @@ export class RejectTeamJoinRequestOutput { @IsBoolean() success!: boolean; } + +// --- +// DTOs used by the public API surface (consumed by the website via generated types) +// --- + +export class TeamDTO { + @ApiProperty() + @IsString() + id!: string; + + @ApiProperty() + @IsString() + name!: string; + + @ApiProperty() + @IsString() + tag!: string; + + @ApiProperty() + @IsString() + description!: string; + + @ApiProperty() + @IsString() + ownerId!: string; + + @ApiProperty({ type: [String] }) + @IsArray() + leagues!: string[]; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + createdAt?: string; +} diff --git a/apps/api/src/domain/team/dtos/TeamJoinRequestDTO.ts b/apps/api/src/domain/team/dtos/TeamJoinRequestDTO.ts new file mode 100644 index 000000000..c9c231c45 --- /dev/null +++ b/apps/api/src/domain/team/dtos/TeamJoinRequestDTO.ts @@ -0,0 +1,25 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TeamJoinRequestDTO { + @ApiProperty() + requestId!: string; + + @ApiProperty() + driverId!: string; + + @ApiProperty() + driverName!: string; + + @ApiProperty() + teamId!: string; + + @ApiProperty({ enum: ['pending', 'approved', 'rejected'] }) + status!: 'pending' | 'approved' | 'rejected'; + + @ApiProperty() + requestedAt!: string; + + @ApiProperty() + avatarUrl!: string; +} + diff --git a/apps/api/src/domain/team/dtos/TeamLeaderboardItemDTO.ts b/apps/api/src/domain/team/dtos/TeamLeaderboardItemDTO.ts new file mode 100644 index 000000000..fb0c08860 --- /dev/null +++ b/apps/api/src/domain/team/dtos/TeamLeaderboardItemDTO.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro'; + +export class TeamLeaderboardItemDTO { + @ApiProperty() + id!: string; + + @ApiProperty() + name!: string; + + @ApiProperty() + memberCount!: number; + + @ApiProperty({ nullable: true }) + rating!: number | null; + + @ApiProperty() + totalWins!: number; + + @ApiProperty() + totalRaces!: number; + + @ApiProperty({ enum: ['beginner', 'intermediate', 'advanced', 'pro'] }) + performanceLevel!: SkillLevel; + + @ApiProperty() + isRecruiting!: boolean; + + @ApiProperty() + createdAt!: string; + + @ApiProperty({ required: false }) + description?: string; + + @ApiProperty({ enum: ['endurance', 'sprint', 'mixed'], required: false }) + specialization?: 'endurance' | 'sprint' | 'mixed'; + + @ApiProperty({ required: false }) + region?: string; + + @ApiProperty({ type: [String], required: false }) + languages?: string[]; +} + diff --git a/apps/api/src/domain/team/dtos/TeamListItemDTO.ts b/apps/api/src/domain/team/dtos/TeamListItemDTO.ts new file mode 100644 index 000000000..c758ebb76 --- /dev/null +++ b/apps/api/src/domain/team/dtos/TeamListItemDTO.ts @@ -0,0 +1,31 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TeamListItemDTO { + @ApiProperty() + id!: string; + + @ApiProperty() + name!: string; + + @ApiProperty() + tag!: string; + + @ApiProperty() + description!: string; + + @ApiProperty() + memberCount!: number; + + @ApiProperty({ type: [String] }) + leagues!: string[]; + + @ApiProperty({ required: false, enum: ['endurance', 'sprint', 'mixed'] }) + specialization?: 'endurance' | 'sprint' | 'mixed'; + + @ApiProperty({ required: false }) + region?: string; + + @ApiProperty({ type: [String], required: false }) + languages?: string[]; +} + diff --git a/apps/api/src/domain/team/dtos/TeamMemberDTO.ts b/apps/api/src/domain/team/dtos/TeamMemberDTO.ts new file mode 100644 index 000000000..a3f1cc836 --- /dev/null +++ b/apps/api/src/domain/team/dtos/TeamMemberDTO.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TeamMemberDTO { + @ApiProperty() + driverId!: string; + + @ApiProperty() + driverName!: string; + + @ApiProperty({ enum: ['owner', 'manager', 'member'] }) + role!: 'owner' | 'manager' | 'member'; + + @ApiProperty() + joinedAt!: string; + + @ApiProperty() + isActive!: boolean; + + @ApiProperty() + avatarUrl!: string; +} + diff --git a/apps/api/src/domain/team/dtos/TeamMembershipDTO.ts b/apps/api/src/domain/team/dtos/TeamMembershipDTO.ts new file mode 100644 index 000000000..79406bd92 --- /dev/null +++ b/apps/api/src/domain/team/dtos/TeamMembershipDTO.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TeamMembershipDTO { + @ApiProperty({ enum: ['owner', 'manager', 'member'] }) + role!: 'owner' | 'manager' | 'member'; + + @ApiProperty() + joinedAt!: string; + + @ApiProperty() + isActive!: boolean; +} + diff --git a/apps/website/app/leagues/[id]/standings/page.tsx b/apps/website/app/leagues/[id]/standings/page.tsx index 9df2a0302..3118a03a2 100644 --- a/apps/website/app/leagues/[id]/standings/page.tsx +++ b/apps/website/app/leagues/[id]/standings/page.tsx @@ -8,9 +8,11 @@ import type { LeagueMembership } from '@/lib/types/LeagueMembership'; import type { MembershipRoleDTO } from '@/lib/types/generated/MembershipRoleDTO'; import { LeagueRoleUtility } from '@/lib/utilities/LeagueRoleUtility'; import { useServices } from '@/lib/services/ServiceProvider'; +import type { DriverDTO } from '@/lib/types/generated/DriverDTO'; import { DriverViewModel } from '@/lib/view-models/DriverViewModel'; import { LeagueStandingsViewModel } from '@/lib/view-models/LeagueStandingsViewModel'; import { StandingEntryViewModel } from '@/lib/view-models/StandingEntryViewModel'; +import type { LeagueStandingDTO } from '@/lib/types/generated/LeagueStandingDTO'; import { useParams } from 'next/navigation'; import { useCallback, useEffect, useState } from 'react'; @@ -21,7 +23,8 @@ export default function LeagueStandingsPage() { const { leagueService } = useServices(); const [standings, setStandings] = useState([]); - const [drivers, setDrivers] = useState([]); + const [drivers, setDrivers] = useState([]); + const [driverVms, setDriverVms] = useState([]); const [memberships, setMemberships] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -31,7 +34,8 @@ export default function LeagueStandingsPage() { try { const vm = await leagueService.getLeagueStandings(leagueId, currentDriverId); setStandings(vm.standings); - setDrivers(vm.drivers.map(d => new DriverViewModel(d))); + setDrivers(vm.drivers as unknown as DriverDTO[]); + setDriverVms(vm.drivers.map((d) => new DriverViewModel(d))); setMemberships(vm.memberships); // Check if current user is admin @@ -89,12 +93,33 @@ export default function LeagueStandingsPage() { return (
{/* Championship Stats */} - +

Championship Standings

({ + leagueId, + driverId: s.driverId, + position: s.position, + totalPoints: s.points, + racesFinished: s.races, + racesStarted: s.races, + avgFinish: null, + penaltyPoints: 0, + bonusPoints: 0, + }) satisfies { + leagueId: string; + driverId: string; + position: number; + totalPoints: number; + racesFinished: number; + racesStarted: number; + avgFinish: number | null; + penaltyPoints: number; + bonusPoints: number; + teamName?: string; + })} drivers={drivers} leagueId={leagueId} memberships={memberships} @@ -106,4 +131,4 @@ export default function LeagueStandingsPage() {
); -} \ No newline at end of file +} diff --git a/apps/website/app/page.tsx b/apps/website/app/page.tsx index 2dbafb7bd..3263ef467 100644 --- a/apps/website/app/page.tsx +++ b/apps/website/app/page.tsx @@ -14,27 +14,13 @@ import SimPlatformMockup from '@/components/mockups/SimPlatformMockup'; import MockupStack from '@/components/ui/MockupStack'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; -import { LandingService } from '@/lib/services/landing/LandingService'; -import { SessionService } from '@/lib/services/auth/SessionService'; -import { AuthApiClient } from '@/lib/api/auth/AuthApiClient'; -import { RacesApiClient } from '@/lib/api/races/RacesApiClient'; -import { LeaguesApiClient } from '@/lib/api/leagues/LeaguesApiClient'; -import { TeamsApiClient } from '@/lib/api/teams/TeamsApiClient'; -import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter'; -import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger'; +import { ServiceFactory } from '@/lib/services/ServiceFactory'; export default async function HomePage() { const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001'; - const errorReporter = new ConsoleErrorReporter(); - const logger = new ConsoleLogger(); - - const authApiClient = new AuthApiClient(baseUrl, errorReporter, logger); - const racesApiClient = new RacesApiClient(baseUrl, errorReporter, logger); - const leaguesApiClient = new LeaguesApiClient(baseUrl, errorReporter, logger); - const teamsApiClient = new TeamsApiClient(baseUrl, errorReporter, logger); - - const sessionService = new SessionService(authApiClient); - const landingService = new LandingService(racesApiClient, leaguesApiClient, teamsApiClient); + const serviceFactory = new ServiceFactory(baseUrl); + const sessionService = serviceFactory.createSessionService(); + const landingService = serviceFactory.createLandingService(); const session = await sessionService.getSession(); if (session) { @@ -368,4 +354,4 @@ export default async function HomePage() {