website refactor
This commit is contained in:
@@ -30,7 +30,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|||||||
|
|
||||||
function isRaceDTO(value: unknown): value is RaceDTO {
|
function isRaceDTO(value: unknown): value is RaceDTO {
|
||||||
if (!isRecord(value)) return false;
|
if (!isRecord(value)) return false;
|
||||||
return typeof value.id === 'string' && typeof value.name === 'string' && typeof value.date === 'string';
|
return typeof value.id === 'string' && typeof value.name === 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRaceDTOArray(value: unknown): RaceDTO[] {
|
function parseRaceDTOArray(value: unknown): RaceDTO[] {
|
||||||
@@ -71,8 +71,11 @@ export class LeaguesApiClient extends BaseApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Get league memberships */
|
/** Get league memberships */
|
||||||
getMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
|
async getMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
|
||||||
return this.get<LeagueMembershipsDTO>(`/leagues/${leagueId}/memberships`);
|
const response = await this.get<any>(`/leagues/${leagueId}/memberships`);
|
||||||
|
if (Array.isArray(response)) return { members: response };
|
||||||
|
if (response?.members) return response;
|
||||||
|
return { members: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a new league */
|
/** Create a new league */
|
||||||
@@ -160,8 +163,9 @@ export class LeaguesApiClient extends BaseApiClient {
|
|||||||
|
|
||||||
/** Get races for a league */
|
/** Get races for a league */
|
||||||
async getRaces(leagueId: string): Promise<{ races: RaceDTO[] }> {
|
async getRaces(leagueId: string): Promise<{ races: RaceDTO[] }> {
|
||||||
const response = await this.get<{ races?: unknown }>(`/leagues/${leagueId}/races`);
|
const response = await this.get<any>(`/leagues/${leagueId}/races`);
|
||||||
return { races: parseRaceDTOArray(response?.races) };
|
const races = Array.isArray(response) ? response : (response?.races || []);
|
||||||
|
return { races: parseRaceDTOArray(races) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin roster: list current members (admin/owner only; actor derived from session) */
|
/** Admin roster: list current members (admin/owner only; actor derived from session) */
|
||||||
|
|||||||
@@ -35,10 +35,16 @@ export class LeagueDetailViewDataBuilder {
|
|||||||
|
|
||||||
// Calculate info data
|
// Calculate info data
|
||||||
const membersCount = Array.isArray(memberships.members) ? memberships.members.length : 0;
|
const membersCount = Array.isArray(memberships.members) ? memberships.members.length : 0;
|
||||||
const completedRacesCount = races.filter(r => (r as any).status === 'completed').length;
|
const completedRacesCount = races.filter(r => {
|
||||||
|
const status = (r as any).status;
|
||||||
|
return status === 'completed' || status === 'past';
|
||||||
|
}).length;
|
||||||
|
|
||||||
// Compute real avgSOF from races
|
// Compute real avgSOF from races
|
||||||
const racesWithSOF = races.filter(r => typeof (r as any).strengthOfField === 'number' && (r as any).strengthOfField > 0);
|
const racesWithSOF = races.filter(r => {
|
||||||
|
const sof = (r as any).strengthOfField;
|
||||||
|
return typeof sof === 'number' && sof > 0;
|
||||||
|
});
|
||||||
const avgSOF = racesWithSOF.length > 0
|
const avgSOF = racesWithSOF.length > 0
|
||||||
? Math.round(racesWithSOF.reduce((sum, r) => sum + ((r as any).strengthOfField || 0), 0) / racesWithSOF.length)
|
? Math.round(racesWithSOF.reduce((sum, r) => sum + ((r as any).strengthOfField || 0), 0) / racesWithSOF.length)
|
||||||
: null;
|
: null;
|
||||||
|
|||||||
@@ -1,37 +1,31 @@
|
|||||||
import { injectable, unmanaged } from 'inversify';
|
|
||||||
import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient";
|
|
||||||
import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
|
import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
|
||||||
import { SponsorsApiClient } from "@/lib/api/sponsors/SponsorsApiClient";
|
import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient";
|
||||||
import { RacesApiClient } from "@/lib/api/races/RacesApiClient";
|
import { RacesApiClient } from "@/lib/api/races/RacesApiClient";
|
||||||
import { CreateLeagueInputDTO } from "@/lib/types/generated/CreateLeagueInputDTO";
|
import { SponsorsApiClient } from "@/lib/api/sponsors/SponsorsApiClient";
|
||||||
import { CreateLeagueOutputDTO } from "@/lib/types/generated/CreateLeagueOutputDTO";
|
|
||||||
import type { MembershipRole } from "@/lib/types/MembershipRole";
|
|
||||||
import type { LeagueRosterJoinRequestDTO } from "@/lib/types/generated/LeagueRosterJoinRequestDTO";
|
|
||||||
import type { TotalLeaguesDTO } from '@/lib/types/generated/TotalLeaguesDTO';
|
|
||||||
import type { LeagueSeasonSummaryDTO } from '@/lib/types/generated/LeagueSeasonSummaryDTO';
|
|
||||||
import type { LeagueScheduleDTO } from '@/lib/types/generated/LeagueScheduleDTO';
|
|
||||||
import type { CreateLeagueScheduleRaceInputDTO } from '@/lib/types/generated/CreateLeagueScheduleRaceInputDTO';
|
|
||||||
import type { CreateLeagueScheduleRaceOutputDTO } from '@/lib/types/generated/CreateLeagueScheduleRaceOutputDTO';
|
|
||||||
import type { UpdateLeagueScheduleRaceInputDTO } from '@/lib/types/generated/UpdateLeagueScheduleRaceInputDTO';
|
|
||||||
import type { LeagueScheduleRaceMutationSuccessDTO } from '@/lib/types/generated/LeagueScheduleRaceMutationSuccessDTO';
|
|
||||||
import type { LeagueSeasonSchedulePublishOutputDTO } from '@/lib/types/generated/LeagueSeasonSchedulePublishOutputDTO';
|
|
||||||
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
|
|
||||||
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
|
|
||||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
|
||||||
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
|
||||||
import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
|
|
||||||
import type { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
|
|
||||||
import { TeamMemberViewModel } from '@/lib/view-models/TeamMemberViewModel';
|
|
||||||
import { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
|
|
||||||
import { TeamDetailsViewModel } from '@/lib/view-models/TeamDetailsViewModel';
|
|
||||||
import { Result } from '@/lib/contracts/Result';
|
|
||||||
import { DomainError, Service } from '@/lib/contracts/services/Service';
|
|
||||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
|
||||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
|
||||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||||
import { isProductionEnvironment } from '@/lib/config/env';
|
import { isProductionEnvironment } from '@/lib/config/env';
|
||||||
|
import { Result } from '@/lib/contracts/Result';
|
||||||
|
import { DomainError, Service } from '@/lib/contracts/services/Service';
|
||||||
|
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||||
|
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||||
import { AllLeaguesWithCapacityAndScoringDTO } from '@/lib/types/AllLeaguesWithCapacityAndScoringDTO';
|
import { AllLeaguesWithCapacityAndScoringDTO } from '@/lib/types/AllLeaguesWithCapacityAndScoringDTO';
|
||||||
|
import { CreateLeagueInputDTO } from "@/lib/types/generated/CreateLeagueInputDTO";
|
||||||
|
import type { CreateLeagueScheduleRaceInputDTO } from '@/lib/types/generated/CreateLeagueScheduleRaceInputDTO';
|
||||||
|
import type { CreateLeagueScheduleRaceOutputDTO } from '@/lib/types/generated/CreateLeagueScheduleRaceOutputDTO';
|
||||||
|
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||||
|
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
|
||||||
|
import type { LeagueRosterJoinRequestDTO } from "@/lib/types/generated/LeagueRosterJoinRequestDTO";
|
||||||
|
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
|
||||||
|
import type { LeagueScheduleDTO } from '@/lib/types/generated/LeagueScheduleDTO';
|
||||||
|
import type { LeagueScheduleRaceMutationSuccessDTO } from '@/lib/types/generated/LeagueScheduleRaceMutationSuccessDTO';
|
||||||
|
import type { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
|
||||||
|
import type { LeagueSeasonSchedulePublishOutputDTO } from '@/lib/types/generated/LeagueSeasonSchedulePublishOutputDTO';
|
||||||
|
import type { LeagueSeasonSummaryDTO } from '@/lib/types/generated/LeagueSeasonSummaryDTO';
|
||||||
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
||||||
|
import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
|
||||||
|
import type { UpdateLeagueScheduleRaceInputDTO } from '@/lib/types/generated/UpdateLeagueScheduleRaceInputDTO';
|
||||||
|
import type { MembershipRole } from "@/lib/types/MembershipRole";
|
||||||
|
import { injectable, unmanaged } from 'inversify';
|
||||||
|
|
||||||
export interface LeagueScheduleAdminData {
|
export interface LeagueScheduleAdminData {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
@@ -328,6 +322,7 @@ export class LeagueService implements Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO wtf what a stupid method??
|
||||||
async getLeagueScheduleDto(leagueId: string, seasonId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
|
async getLeagueScheduleDto(leagueId: string, seasonId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
|
||||||
return this.getAdminSchedule(leagueId, seasonId);
|
return this.getAdminSchedule(leagueId, seasonId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,20 +64,23 @@ export function LeagueOverviewTemplate({ viewData }: LeagueOverviewTemplateProps
|
|||||||
<Box border borderColor="zinc-800" bg="zinc-900/30" overflow="hidden">
|
<Box border borderColor="zinc-800" bg="zinc-900/30" overflow="hidden">
|
||||||
<table className="w-full text-left border-collapse">
|
<table className="w-full text-left border-collapse">
|
||||||
<tbody>
|
<tbody>
|
||||||
{viewData.adminSummaries.concat(viewData.stewardSummaries).concat(viewData.memberSummaries).slice(0, 5).map((member) => (
|
{[viewData.ownerSummary, ...viewData.adminSummaries, ...viewData.stewardSummaries, ...viewData.memberSummaries]
|
||||||
<tr key={member.driverId} className="border-b border-zinc-800/50">
|
.filter((m): m is any => m !== null)
|
||||||
<td className="px-6 py-3">
|
.slice(0, 5)
|
||||||
<Box display="flex" alignItems="center" gap={3}>
|
.map((member) => (
|
||||||
<Box w="6" h="6" bg="zinc-800" rounded="full" />
|
<tr key={member.driverId} className="border-b border-zinc-800/50">
|
||||||
<Text size="sm" weight="bold" color="text-white">{member.driverName}</Text>
|
<td className="px-6 py-3">
|
||||||
</Box>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
</td>
|
<Box w="6" h="6" bg="zinc-800" rounded="full" />
|
||||||
<td className="px-6 py-3 text-right">
|
<Text size="sm" weight="bold" color="text-white">{member.driverName}</Text>
|
||||||
<Text size="xs" color="text-zinc-500" uppercase weight="bold">{member.roleBadgeText}</Text>
|
</Box>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<td className="px-6 py-3 text-right">
|
||||||
))}
|
<Text size="xs" color="text-zinc-500" uppercase weight="bold">{member.roleBadgeText}</Text>
|
||||||
{viewData.adminSummaries.length === 0 && viewData.stewardSummaries.length === 0 && viewData.memberSummaries.length === 0 && (
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
{viewData.adminSummaries.length === 0 && viewData.stewardSummaries.length === 0 && viewData.memberSummaries.length === 0 && !viewData.ownerSummary && (
|
||||||
<tr>
|
<tr>
|
||||||
<td className="px-6 py-8 text-center">
|
<td className="px-6 py-8 text-center">
|
||||||
<Text size="sm" color="text-zinc-600" italic>No members to display</Text>
|
<Text size="sm" color="text-zinc-600" italic>No members to display</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user