website refactor

This commit is contained in:
2026-01-16 01:00:03 +01:00
parent ce7be39155
commit a98e3e3166
286 changed files with 5522 additions and 5261 deletions

View File

@@ -21,8 +21,27 @@ 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 { getWebsiteServerEnv } from '@/lib/config/env';
import { isProductionEnvironment } from '@/lib/config/env';
import { AllLeaguesWithCapacityAndScoringDTO } from '@/lib/types/AllLeaguesWithCapacityAndScoringDTO';
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
export interface LeagueScheduleAdminData {
leagueId: string;
seasonId: string;
seasons: LeagueSeasonSummaryDTO[];
schedule: LeagueScheduleDTO;
}
export interface LeagueRosterAdminData {
leagueId: string;
members: LeagueRosterMemberDTO[];
joinRequests: LeagueRosterJoinRequestDTO[];
}
export interface LeagueDetailData {
league: LeagueWithCapacityAndScoringDTO;
apiDto: AllLeaguesWithCapacityAndScoringDTO;
}
/**
* League Service - DTO Only
@@ -40,11 +59,10 @@ export class LeagueService implements Service {
constructor() {
const baseUrl = getWebsiteApiBaseUrl();
const logger = new ConsoleLogger();
const { NODE_ENV } = getWebsiteServerEnv();
const errorReporter = new EnhancedErrorReporter(logger, {
showUserNotifications: false,
logToConsole: true,
reportToExternal: NODE_ENV === 'production',
reportToExternal: isProductionEnvironment(),
});
this.apiClient = new LeaguesApiClient(baseUrl, errorReporter, logger);
// Optional clients can be initialized if needed
@@ -54,9 +72,73 @@ export class LeagueService implements Service {
try {
const dto = await this.apiClient.getAllWithCapacityAndScoring();
return Result.ok(dto);
} catch (error) {
} catch (error: unknown) {
console.error('LeagueService.getAllLeagues failed:', error);
return Result.err({ type: 'serverError', message: 'Failed to fetch leagues' });
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch leagues' });
}
}
async getLeagueDetailData(leagueId: string): Promise<Result<LeagueDetailData, DomainError>> {
try {
const apiDto = await this.apiClient.getAllWithCapacityAndScoring();
if (!apiDto || !apiDto.leagues) {
return Result.err({ type: 'notFound', message: 'Leagues not found' });
}
const league = apiDto.leagues.find(l => l.id === leagueId);
if (!league) {
return Result.err({ type: 'notFound', message: 'League not found' });
}
return Result.ok({
league,
apiDto,
});
} catch (error: unknown) {
console.error('LeagueService.getLeagueDetailData failed:', error);
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch league detail' });
}
}
async getScheduleAdminData(leagueId: string, seasonId?: string): Promise<Result<LeagueScheduleAdminData, DomainError>> {
try {
const seasons = await this.apiClient.getSeasons(leagueId);
if (!seasons || seasons.length === 0) {
return Result.err({ type: 'notFound', message: 'No seasons found for league' });
}
const targetSeasonId = seasonId || (seasons.find(s => s.status === 'active')?.seasonId || seasons[0].seasonId);
const schedule = await this.apiClient.getSchedule(leagueId, targetSeasonId);
return Result.ok({
leagueId,
seasonId: targetSeasonId,
seasons,
schedule,
});
} catch (error: unknown) {
console.error('LeagueService.getScheduleAdminData failed:', error);
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch schedule admin data' });
}
}
async getRosterAdminData(leagueId: string): Promise<Result<LeagueRosterAdminData, DomainError>> {
try {
const [members, joinRequests] = await Promise.all([
this.apiClient.getAdminRosterMembers(leagueId),
this.apiClient.getAdminRosterJoinRequests(leagueId),
]);
return Result.ok({
leagueId,
members,
joinRequests,
});
} catch (error: unknown) {
console.error('LeagueService.getRosterAdminData failed:', error);
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch roster data' });
}
}
@@ -64,41 +146,76 @@ export class LeagueService implements Service {
return Result.err({ type: 'notImplemented', message: 'League standings endpoint not implemented' });
}
async getLeagueStats(): Promise<TotalLeaguesDTO> {
return this.apiClient.getTotal();
async getLeagueStats(): Promise<Result<TotalLeaguesDTO, DomainError>> {
try {
const data = await this.apiClient.getTotal();
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch league stats' });
}
}
async getLeagueSchedule(leagueId: string): Promise<LeagueScheduleDTO> {
return this.apiClient.getSchedule(leagueId);
async getLeagueSchedule(leagueId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
try {
const data = await this.apiClient.getSchedule(leagueId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch league schedule' });
}
}
async getLeagueSeasons(leagueId: string): Promise<LeagueSeasonSummaryDTO[]> {
return this.apiClient.getSeasons(leagueId);
async getLeagueSeasons(leagueId: string): Promise<Result<LeagueSeasonSummaryDTO[], DomainError>> {
try {
const data = await this.apiClient.getSeasons(leagueId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch league seasons' });
}
}
async getLeagueSeasonSummaries(leagueId: string): Promise<LeagueSeasonSummaryDTO[]> {
return this.apiClient.getSeasons(leagueId);
async getLeagueSeasonSummaries(leagueId: string): Promise<Result<LeagueSeasonSummaryDTO[], DomainError>> {
return this.getLeagueSeasons(leagueId);
}
async getAdminSchedule(leagueId: string, seasonId: string): Promise<LeagueScheduleDTO> {
return this.apiClient.getSchedule(leagueId, seasonId);
async getAdminSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
try {
const data = await this.apiClient.getSchedule(leagueId, seasonId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch admin schedule' });
}
}
async publishAdminSchedule(leagueId: string, seasonId: string): Promise<LeagueSeasonSchedulePublishOutputDTO> {
return this.apiClient.publishSeasonSchedule(leagueId, seasonId);
async publishAdminSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
try {
const data = await this.apiClient.publishSeasonSchedule(leagueId, seasonId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to publish schedule' });
}
}
async unpublishAdminSchedule(leagueId: string, seasonId: string): Promise<LeagueSeasonSchedulePublishOutputDTO> {
return this.apiClient.unpublishSeasonSchedule(leagueId, seasonId);
async unpublishAdminSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
try {
const data = await this.apiClient.unpublishSeasonSchedule(leagueId, seasonId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to unpublish schedule' });
}
}
async createAdminScheduleRace(
leagueId: string,
seasonId: string,
input: { track: string; car: string; scheduledAtIso: string },
): Promise<CreateLeagueScheduleRaceOutputDTO> {
const payload: CreateLeagueScheduleRaceInputDTO = { ...input, example: '' };
return this.apiClient.createSeasonScheduleRace(leagueId, seasonId, payload);
): Promise<Result<CreateLeagueScheduleRaceOutputDTO, DomainError>> {
try {
const payload: CreateLeagueScheduleRaceInputDTO = { ...input, example: '' };
const data = await this.apiClient.createSeasonScheduleRace(leagueId, seasonId, payload);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to create race' });
}
}
async updateAdminScheduleRace(
@@ -106,33 +223,48 @@ export class LeagueService implements Service {
seasonId: string,
raceId: string,
input: Partial<{ track: string; car: string; scheduledAtIso: string }>,
): Promise<LeagueScheduleRaceMutationSuccessDTO> {
const payload: UpdateLeagueScheduleRaceInputDTO = { ...input, example: '' };
return this.apiClient.updateSeasonScheduleRace(leagueId, seasonId, raceId, payload);
): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
try {
const payload: UpdateLeagueScheduleRaceInputDTO = { ...input, example: '' };
const data = await this.apiClient.updateSeasonScheduleRace(leagueId, seasonId, raceId, payload);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to update race' });
}
}
async deleteAdminScheduleRace(leagueId: string, seasonId: string, raceId: string): Promise<LeagueScheduleRaceMutationSuccessDTO> {
return this.apiClient.deleteSeasonScheduleRace(leagueId, seasonId, raceId);
async deleteAdminScheduleRace(leagueId: string, seasonId: string, raceId: string): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
try {
const data = await this.apiClient.deleteSeasonScheduleRace(leagueId, seasonId, raceId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to delete race' });
}
}
async getLeagueScheduleDto(leagueId: string, seasonId: string): Promise<LeagueScheduleDTO> {
return this.apiClient.getSchedule(leagueId, seasonId);
async getLeagueScheduleDto(leagueId: string, seasonId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
return this.getAdminSchedule(leagueId, seasonId);
}
async publishLeagueSeasonSchedule(leagueId: string, seasonId: string): Promise<LeagueSeasonSchedulePublishOutputDTO> {
return this.apiClient.publishSeasonSchedule(leagueId, seasonId);
async publishLeagueSeasonSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
return this.publishAdminSchedule(leagueId, seasonId);
}
async unpublishLeagueSeasonSchedule(leagueId: string, seasonId: string): Promise<LeagueSeasonSchedulePublishOutputDTO> {
return this.apiClient.unpublishSeasonSchedule(leagueId, seasonId);
async unpublishLeagueSeasonSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
return this.unpublishAdminSchedule(leagueId, seasonId);
}
async createLeagueSeasonScheduleRace(
leagueId: string,
seasonId: string,
input: CreateLeagueScheduleRaceInputDTO,
): Promise<CreateLeagueScheduleRaceOutputDTO> {
return this.apiClient.createSeasonScheduleRace(leagueId, seasonId, input);
): Promise<Result<CreateLeagueScheduleRaceOutputDTO, DomainError>> {
try {
const data = await this.apiClient.createSeasonScheduleRace(leagueId, seasonId, input);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to create race' });
}
}
async updateLeagueSeasonScheduleRace(
@@ -140,52 +272,93 @@ export class LeagueService implements Service {
seasonId: string,
raceId: string,
input: UpdateLeagueScheduleRaceInputDTO,
): Promise<LeagueScheduleRaceMutationSuccessDTO> {
return this.apiClient.updateSeasonScheduleRace(leagueId, seasonId, raceId, input);
): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
try {
const data = await this.apiClient.updateSeasonScheduleRace(leagueId, seasonId, raceId, input);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to update race' });
}
}
async deleteLeagueSeasonScheduleRace(
leagueId: string,
seasonId: string,
raceId: string,
): Promise<LeagueScheduleRaceMutationSuccessDTO> {
return this.apiClient.deleteSeasonScheduleRace(leagueId, seasonId, raceId);
): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
return this.deleteAdminScheduleRace(leagueId, seasonId, raceId);
}
async getLeagueMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
return this.apiClient.getMemberships(leagueId);
async getLeagueMemberships(leagueId: string): Promise<Result<LeagueMembershipsDTO, DomainError>> {
try {
const data = await this.apiClient.getMemberships(leagueId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch memberships' });
}
}
async createLeague(input: CreateLeagueInputDTO): Promise<CreateLeagueOutputDTO> {
return this.apiClient.create(input);
async createLeague(input: CreateLeagueInputDTO): Promise<Result<CreateLeagueOutputDTO, DomainError>> {
try {
const data = await this.apiClient.create(input);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to create league' });
}
}
async removeMember(leagueId: string, targetDriverId: string): Promise<{ success: boolean }> {
const dto = await this.apiClient.removeRosterMember(leagueId, targetDriverId);
return { success: dto.success };
async removeMember(leagueId: string, targetDriverId: string): Promise<Result<{ success: boolean }, DomainError>> {
try {
const dto = await this.apiClient.removeRosterMember(leagueId, targetDriverId);
return Result.ok({ success: dto.success });
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to remove member' });
}
}
async updateMemberRole(leagueId: string, targetDriverId: string, newRole: MembershipRole): Promise<{ success: boolean }> {
const dto = await this.apiClient.updateRosterMemberRole(leagueId, targetDriverId, newRole);
return { success: dto.success };
async updateMemberRole(leagueId: string, targetDriverId: string, newRole: MembershipRole): Promise<Result<{ success: boolean }, DomainError>> {
try {
const dto = await this.apiClient.updateRosterMemberRole(leagueId, targetDriverId, newRole);
return Result.ok({ success: dto.success });
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to update member role' });
}
}
async getAdminRosterMembers(leagueId: string): Promise<LeagueRosterMemberDTO[]> {
return this.apiClient.getAdminRosterMembers(leagueId);
async getAdminRosterMembers(leagueId: string): Promise<Result<LeagueRosterMemberDTO[], DomainError>> {
try {
const data = await this.apiClient.getAdminRosterMembers(leagueId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch roster members' });
}
}
async getAdminRosterJoinRequests(leagueId: string): Promise<LeagueRosterJoinRequestDTO[]> {
return this.apiClient.getAdminRosterJoinRequests(leagueId);
async getAdminRosterJoinRequests(leagueId: string): Promise<Result<LeagueRosterJoinRequestDTO[], DomainError>> {
try {
const data = await this.apiClient.getAdminRosterJoinRequests(leagueId);
return Result.ok(data);
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch join requests' });
}
}
async approveJoinRequest(leagueId: string, joinRequestId: string): Promise<{ success: boolean }> {
const dto = await this.apiClient.approveRosterJoinRequest(leagueId, joinRequestId);
return { success: dto.success };
async approveJoinRequest(leagueId: string, joinRequestId: string): Promise<Result<{ success: boolean }, DomainError>> {
try {
const dto = await this.apiClient.approveRosterJoinRequest(leagueId, joinRequestId);
return Result.ok({ success: dto.success });
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to approve join request' });
}
}
async rejectJoinRequest(leagueId: string, joinRequestId: string): Promise<{ success: boolean }> {
const dto = await this.apiClient.rejectRosterJoinRequest(leagueId, joinRequestId);
return { success: dto.success };
async rejectJoinRequest(leagueId: string, joinRequestId: string): Promise<Result<{ success: boolean }, DomainError>> {
try {
const dto = await this.apiClient.rejectRosterJoinRequest(leagueId, joinRequestId);
return Result.ok({ success: dto.success });
} catch (error: unknown) {
return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to reject join request' });
}
}
async getLeagueDetail(): Promise<Result<never, DomainError>> {
@@ -199,4 +372,4 @@ export class LeagueService implements Service {
async getScoringPresets(): Promise<Result<never, DomainError>> {
return Result.err({ type: 'notImplemented', message: 'Scoring presets endpoint not implemented' });
}
}
}