streamline components
This commit is contained in:
125
apps/website/lib/view-models/LeagueScoringSectionViewModel.ts
Normal file
125
apps/website/lib/view-models/LeagueScoringSectionViewModel.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
105
apps/website/lib/view-models/RaceResultsDataTransformer.ts
Normal file
105
apps/website/lib/view-models/RaceResultsDataTransformer.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user