website refactor

This commit is contained in:
2026-01-14 02:02:24 +01:00
parent 8d7c709e0c
commit 4522d41aef
291 changed files with 12763 additions and 9309 deletions

View File

@@ -1,15 +0,0 @@
'use client';
import { AdminDashboardPageDto } from '@/lib/page-queries/AdminDashboardPageQuery';
import { AdminDashboardViewModel } from '@/lib/view-models/AdminDashboardViewModel';
/**
* AdminDashboardViewModelBuilder
*
* Transforms AdminDashboardPageDto into AdminDashboardViewModel
*/
export class AdminDashboardViewModelBuilder {
static build(dto: AdminDashboardPageDto): AdminDashboardViewModel {
return new AdminDashboardViewModel(dto);
}
}

View File

@@ -0,0 +1,135 @@
import type { GetDriverProfileOutputDTO } from '@/lib/types/generated/GetDriverProfileOutputDTO';
import type { DriverProfileDriverSummaryDTO } from '@/lib/types/generated/DriverProfileDriverSummaryDTO';
import type { DriverProfileStatsDTO } from '@/lib/types/generated/DriverProfileStatsDTO';
import type { DriverProfileFinishDistributionDTO } from '@/lib/types/generated/DriverProfileFinishDistributionDTO';
import type { DriverProfileTeamMembershipDTO } from '@/lib/types/generated/DriverProfileTeamMembershipDTO';
import type { DriverProfileSocialSummaryDTO } from '@/lib/types/generated/DriverProfileSocialSummaryDTO';
import type { DriverProfileExtendedProfileDTO } from '@/lib/types/generated/DriverProfileExtendedProfileDTO';
import { DriverProfileViewModel } from '@/lib/view-models/DriverProfileViewModel';
import type {
DriverProfileDriverSummaryViewModel,
DriverProfileStatsViewModel,
DriverProfileFinishDistributionViewModel,
DriverProfileTeamMembershipViewModel,
DriverProfileSocialSummaryViewModel,
DriverProfileExtendedProfileViewModel,
} from '@/lib/view-models/DriverProfileViewModel';
/**
* DriverProfileViewModelBuilder
*
* Transforms GetDriverProfileOutputDTO into DriverProfileViewModel.
* Deterministic, side-effect free, no HTTP calls.
*/
export class DriverProfileViewModelBuilder {
/**
* Build ViewModel from API DTO
*
* @param apiDto - The API transport DTO
* @returns ViewModel ready for template
*/
static build(apiDto: GetDriverProfileOutputDTO): DriverProfileViewModel {
return new DriverProfileViewModel({
currentDriver: apiDto.currentDriver ? this.transformCurrentDriver(apiDto.currentDriver) : null,
stats: apiDto.stats ? this.transformStats(apiDto.stats) : null,
finishDistribution: apiDto.finishDistribution ? this.transformFinishDistribution(apiDto.finishDistribution) : null,
teamMemberships: apiDto.teamMemberships.map(m => this.transformTeamMembership(m)),
socialSummary: this.transformSocialSummary(apiDto.socialSummary),
extendedProfile: apiDto.extendedProfile ? this.transformExtendedProfile(apiDto.extendedProfile) : null,
});
}
private static transformCurrentDriver(dto: DriverProfileDriverSummaryDTO): DriverProfileDriverSummaryViewModel {
return {
id: dto.id,
name: dto.name,
country: dto.country,
avatarUrl: dto.avatarUrl || '', // Handle undefined
iracingId: dto.iracingId || null,
joinedAt: dto.joinedAt,
rating: dto.rating ?? null,
globalRank: dto.globalRank ?? null,
consistency: dto.consistency ?? null,
bio: dto.bio || null,
totalDrivers: dto.totalDrivers ?? null,
};
}
private static transformStats(dto: DriverProfileStatsDTO): DriverProfileStatsViewModel {
return {
totalRaces: dto.totalRaces,
wins: dto.wins,
podiums: dto.podiums,
dnfs: dto.dnfs,
avgFinish: dto.avgFinish ?? null,
bestFinish: dto.bestFinish ?? null,
worstFinish: dto.worstFinish ?? null,
finishRate: dto.finishRate ?? null,
winRate: dto.winRate ?? null,
podiumRate: dto.podiumRate ?? null,
percentile: dto.percentile ?? null,
rating: dto.rating ?? null,
consistency: dto.consistency ?? null,
overallRank: dto.overallRank ?? null,
};
}
private static transformFinishDistribution(dto: DriverProfileFinishDistributionDTO): DriverProfileFinishDistributionViewModel {
return {
totalRaces: dto.totalRaces,
wins: dto.wins,
podiums: dto.podiums,
topTen: dto.topTen,
dnfs: dto.dnfs,
other: dto.other,
};
}
private static transformTeamMembership(dto: DriverProfileTeamMembershipDTO): DriverProfileTeamMembershipViewModel {
return {
teamId: dto.teamId,
teamName: dto.teamName,
teamTag: dto.teamTag || null,
role: dto.role,
joinedAt: dto.joinedAt,
isCurrent: dto.isCurrent,
};
}
private static transformSocialSummary(dto: DriverProfileSocialSummaryDTO): DriverProfileSocialSummaryViewModel {
return {
friendsCount: dto.friendsCount,
friends: dto.friends.map(f => ({
id: f.id,
name: f.name,
country: f.country,
avatarUrl: f.avatarUrl || '', // Handle undefined
})),
};
}
private static transformExtendedProfile(dto: DriverProfileExtendedProfileDTO): DriverProfileExtendedProfileViewModel {
return {
socialHandles: dto.socialHandles.map(h => ({
platform: h.platform as any, // Type assertion - assuming valid platform
handle: h.handle,
url: h.url,
})),
achievements: dto.achievements.map(a => ({
id: a.id,
title: a.title,
description: a.description,
icon: a.icon as any, // Type assertion - assuming valid icon
rarity: a.rarity as any, // Type assertion - assuming valid rarity
earnedAt: a.earnedAt,
})),
racingStyle: dto.racingStyle,
favoriteTrack: dto.favoriteTrack,
favoriteCar: dto.favoriteCar,
timezone: dto.timezone,
availableHours: dto.availableHours,
lookingForTeam: dto.lookingForTeam,
openToRequests: dto.openToRequests,
};
}
}

View File

@@ -0,0 +1,33 @@
/**
* Forgot Password ViewModel Builder
*
* Transforms API DTOs into ForgotPasswordViewModel for client-side state management.
* Deterministic, side-effect free, no business logic.
*/
import { ForgotPasswordViewData } from '@/lib/builders/view-data/ForgotPasswordViewDataBuilder';
import { ForgotPasswordViewModel, ForgotPasswordFormState } from '@/lib/view-models/auth/ForgotPasswordViewModel';
export class ForgotPasswordViewModelBuilder {
static build(viewData: ForgotPasswordViewData): ForgotPasswordViewModel {
const formState: ForgotPasswordFormState = {
fields: {
email: { value: '', error: undefined, touched: false, validating: false },
},
isValid: true,
isSubmitting: false,
submitError: undefined,
submitCount: 0,
};
return new ForgotPasswordViewModel(
viewData.returnTo,
formState,
false,
null,
null,
false,
null
);
}
}

View File

@@ -0,0 +1,39 @@
/**
* Login ViewModel Builder
*
* Transforms API DTOs into LoginViewModel for client-side state management.
* Deterministic, side-effect free, no business logic.
*/
import { LoginViewData } from '@/lib/builders/view-data/LoginViewDataBuilder';
import { LoginViewModel, LoginFormState, LoginUIState } from '@/lib/view-models/auth/LoginViewModel';
export class LoginViewModelBuilder {
static build(viewData: LoginViewData): LoginViewModel {
const formState: LoginFormState = {
fields: {
email: { value: '', error: undefined, touched: false, validating: false },
password: { value: '', error: undefined, touched: false, validating: false },
rememberMe: { value: false, error: undefined, touched: false, validating: false },
},
isValid: true,
isSubmitting: false,
submitError: undefined,
submitCount: 0,
};
const uiState: LoginUIState = {
showPassword: false,
showErrorDetails: false,
};
return new LoginViewModel(
viewData.returnTo,
viewData.hasInsufficientPermissions,
formState,
uiState,
false,
null
);
}
}

View File

@@ -0,0 +1,23 @@
/**
* Onboarding ViewModel Builder
*
* Transforms API DTOs into ViewModels for client-side state management.
* Deterministic, side-effect free.
*/
import { Result } from '@/lib/contracts/Result';
import { DomainError } from '@/lib/contracts/services/Service';
import { OnboardingViewModel } from '@/lib/view-models/OnboardingViewModel';
export class OnboardingViewModelBuilder {
static build(apiDto: { isAlreadyOnboarded: boolean }): Result<OnboardingViewModel, DomainError> {
try {
return Result.ok({
isAlreadyOnboarded: apiDto.isAlreadyOnboarded || false,
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to build ViewModel';
return Result.err({ type: 'unknown', message: errorMessage });
}
}
}

View File

@@ -0,0 +1,40 @@
/**
* Reset Password ViewModel Builder
*
* Transforms API DTOs into ResetPasswordViewModel for client-side state management.
* Deterministic, side-effect free, no business logic.
*/
import { ResetPasswordViewData } from '@/lib/builders/view-data/ResetPasswordViewDataBuilder';
import { ResetPasswordViewModel, ResetPasswordFormState, ResetPasswordUIState } from '@/lib/view-models/auth/ResetPasswordViewModel';
export class ResetPasswordViewModelBuilder {
static build(viewData: ResetPasswordViewData): ResetPasswordViewModel {
const formState: ResetPasswordFormState = {
fields: {
newPassword: { value: '', error: undefined, touched: false, validating: false },
confirmPassword: { value: '', error: undefined, touched: false, validating: false },
},
isValid: true,
isSubmitting: false,
submitError: undefined,
submitCount: 0,
};
const uiState: ResetPasswordUIState = {
showPassword: false,
showConfirmPassword: false,
};
return new ResetPasswordViewModel(
viewData.token,
viewData.returnTo,
formState,
uiState,
false,
null,
false,
null
);
}
}

View File

@@ -0,0 +1,40 @@
/**
* Signup ViewModel Builder
*
* Transforms API DTOs into SignupViewModel for client-side state management.
* Deterministic, side-effect free, no business logic.
*/
import { SignupViewData } from '@/lib/builders/view-data/SignupViewDataBuilder';
import { SignupViewModel, SignupFormState, SignupUIState } from '@/lib/view-models/auth/SignupViewModel';
export class SignupViewModelBuilder {
static build(viewData: SignupViewData): SignupViewModel {
const formState: SignupFormState = {
fields: {
firstName: { value: '', error: undefined, touched: false, validating: false },
lastName: { value: '', error: undefined, touched: false, validating: false },
email: { value: '', error: undefined, touched: false, validating: false },
password: { value: '', error: undefined, touched: false, validating: false },
confirmPassword: { value: '', error: undefined, touched: false, validating: false },
},
isValid: true,
isSubmitting: false,
submitError: undefined,
submitCount: 0,
};
const uiState: SignupUIState = {
showPassword: false,
showConfirmPassword: false,
};
return new SignupViewModel(
viewData.returnTo,
formState,
uiState,
false,
null
);
}
}