streamline components

This commit is contained in:
2026-01-07 14:16:02 +01:00
parent 94d60527f4
commit 3b3971e653
16 changed files with 685 additions and 667 deletions

View File

@@ -0,0 +1,125 @@
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
import type { LeagueScoringPresetViewModel } from '@/lib/view-models/LeagueScoringPresetViewModel';
import type { CustomPointsConfig } from '@/lib/view-models/ScoringConfigurationViewModel';
/**
* LeagueScoringSectionViewModel
*
* View model for the league scoring section UI state and operations
*/
export class LeagueScoringSectionViewModel {
readonly form: LeagueConfigFormModel;
readonly presets: LeagueScoringPresetViewModel[];
readonly readOnly: boolean;
readonly patternOnly: boolean;
readonly championshipsOnly: boolean;
readonly disabled: boolean;
readonly currentPreset: LeagueScoringPresetViewModel | null;
readonly isCustom: boolean;
constructor(
form: LeagueConfigFormModel,
presets: LeagueScoringPresetViewModel[],
options: {
readOnly?: boolean;
patternOnly?: boolean;
championshipsOnly?: boolean;
} = {}
) {
this.form = form;
this.presets = presets;
this.readOnly = options.readOnly || false;
this.patternOnly = options.patternOnly || false;
this.championshipsOnly = options.championshipsOnly || false;
this.disabled = this.readOnly;
this.currentPreset = form.scoring.patternId
? presets.find(p => p.id === form.scoring.patternId) || null
: null;
this.isCustom = form.scoring.customScoringEnabled || false;
}
/**
* Get default custom points configuration
*/
static getDefaultCustomPoints(): CustomPointsConfig {
return {
racePoints: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1],
poleBonusPoints: 1,
fastestLapPoints: 1,
leaderLapPoints: 0,
};
}
/**
* Check if form can be modified
*/
canModify(): boolean {
return !this.readOnly;
}
/**
* Get available presets for display
*/
getAvailablePresets(): LeagueScoringPresetViewModel[] {
return this.presets;
}
/**
* Get championships configuration for display
*/
getChampionshipsConfig() {
const isTeamsMode = this.form.structure.mode === 'fixedTeams';
return [
{
key: 'enableDriverChampionship' as const,
label: 'Driver Standings',
description: 'Track individual driver points',
enabled: this.form.championships.enableDriverChampionship,
available: true,
},
{
key: 'enableTeamChampionship' as const,
label: 'Team Standings',
description: 'Combined team points',
enabled: this.form.championships.enableTeamChampionship,
available: isTeamsMode,
unavailableHint: 'Teams mode only',
},
{
key: 'enableNationsChampionship' as const,
label: 'Nations Cup',
description: 'By nationality',
enabled: this.form.championships.enableNationsChampionship,
available: true,
},
{
key: 'enableTrophyChampionship' as const,
label: 'Trophy Cup',
description: 'Special category',
enabled: this.form.championships.enableTrophyChampionship,
available: true,
},
];
}
/**
* Get panel visibility based on flags
*/
shouldShowPatternPanel(): boolean {
return !this.championshipsOnly;
}
shouldShowChampionshipsPanel(): boolean {
return !this.patternOnly;
}
/**
* Get the active custom points configuration
*/
getActiveCustomPoints(): CustomPointsConfig {
// This would be stored separately in the form model
// For now, return defaults
return LeagueScoringSectionViewModel.getDefaultCustomPoints();
}
}

View File

@@ -0,0 +1,105 @@
import type { LeagueMembershipsViewModel } from '@/lib/view-models/LeagueMembershipsViewModel';
import type { RaceResultsDetailViewModel } from '@/lib/view-models/RaceResultsDetailViewModel';
import type { RaceWithSOFViewModel } from '@/lib/view-models/RaceWithSOFViewModel';
// TODO fucking violating our architecture, it should be a ViewModel
export interface TransformedRaceResultsData {
raceTrack?: string;
raceScheduledAt?: string;
totalDrivers?: number;
leagueName?: string;
raceSOF: number | null;
results: Array<{
position: number;
driverId: string;
driverName: string;
driverAvatar: string;
country: string;
car: string;
laps: number;
time: string;
fastestLap: string;
points: number;
incidents: number;
isCurrentUser: boolean;
}>;
penalties: Array<{
driverId: string;
driverName: string;
type: 'time_penalty' | 'grid_penalty' | 'points_deduction' | 'disqualification' | 'warning' | 'license_points';
value: number;
reason: string;
notes?: string;
}>;
pointsSystem: Record<string, number>;
fastestLapTime: number;
memberships?: Array<{
driverId: string;
role: string;
}>;
}
export class RaceResultsDataTransformer {
static transform(
resultsData: RaceResultsDetailViewModel | null,
sofData: RaceWithSOFViewModel | null,
currentDriverId: string,
membershipsData?: LeagueMembershipsViewModel
): TransformedRaceResultsData {
if (!resultsData) {
return {
raceSOF: null,
results: [],
penalties: [],
pointsSystem: {},
fastestLapTime: 0,
};
}
// Transform results
const results = resultsData.results.map((result: any) => ({
position: result.position,
driverId: result.driverId,
driverName: result.driverName,
driverAvatar: result.avatarUrl,
country: 'US', // Default since view model doesn't have car
car: 'Unknown', // Default since view model doesn't have car
laps: 0, // Default since view model doesn't have laps
time: '0:00.00', // Default since view model doesn't have time
fastestLap: result.fastestLap.toString(), // Convert number to string
points: 0, // Default since view model doesn't have points
incidents: result.incidents,
isCurrentUser: result.driverId === currentDriverId,
}));
// Transform penalties
const penalties = resultsData.penalties.map((penalty: any) => ({
driverId: penalty.driverId,
driverName: resultsData.results.find((r: any) => r.driverId === penalty.driverId)?.driverName || 'Unknown',
type: penalty.type as 'time_penalty' | 'grid_penalty' | 'points_deduction' | 'disqualification' | 'warning' | 'license_points',
value: penalty.value || 0,
reason: 'Penalty applied', // Default since view model doesn't have reason
notes: undefined, // Default since view model doesn't have notes
}));
// Transform memberships
const memberships = membershipsData?.memberships.map((membership: any) => ({
driverId: membership.driverId,
role: membership.role || 'member',
}));
return {
raceTrack: resultsData.race?.track,
raceScheduledAt: resultsData.race?.scheduledAt,
totalDrivers: resultsData.stats?.totalDrivers,
leagueName: resultsData.league?.name,
raceSOF: sofData?.strengthOfField || null,
results,
penalties,
pointsSystem: resultsData.pointsSystem || {},
fastestLapTime: resultsData.fastestLapTime || 0,
memberships,
};
}
}

View File

@@ -0,0 +1,50 @@
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
import type { LeagueScoringPresetViewModel } from '@/lib/view-models/LeagueScoringPresetViewModel';
export interface CustomPointsConfig {
racePoints: number[];
poleBonusPoints: number;
fastestLapPoints: number;
leaderLapPoints: number;
}
/**
* ScoringConfigurationViewModel
*
* View model for scoring configuration including presets and custom points
*/
export class ScoringConfigurationViewModel {
readonly patternId?: string;
readonly customScoringEnabled: boolean;
readonly customPoints?: CustomPointsConfig;
readonly currentPreset?: LeagueScoringPresetViewModel;
constructor(
config: LeagueConfigFormModel['scoring'],
presets: LeagueScoringPresetViewModel[],
customPoints?: CustomPointsConfig
) {
this.patternId = config.patternId;
this.customScoringEnabled = config.customScoringEnabled || false;
this.customPoints = customPoints;
this.currentPreset = config.patternId
? presets.find(p => p.id === config.patternId)
: undefined;
}
/**
* Get the active points configuration
*/
getActivePointsConfig(): CustomPointsConfig {
if (this.customScoringEnabled && this.customPoints) {
return this.customPoints;
}
// Return default points if no custom config
return {
racePoints: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1],
poleBonusPoints: 1,
fastestLapPoints: 1,
leaderLapPoints: 0,
};
}
}

View File

@@ -57,6 +57,7 @@ export * from './RaceDetailEntryViewModel';
export * from './RaceDetailUserResultViewModel';
export * from './RaceDetailViewModel';
export * from './RaceListItemViewModel';
export * from './RaceResultsDataTransformer';
export * from './RaceResultsDetailViewModel';
export * from './RaceResultViewModel';
export * from './RacesPageViewModel';