- move reusable domain, data, state, ui code into src/lib - update host screens to consume new library exports - document architecture and configure path aliases - bump astro integration dependencies for compatibility Refs #30
99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
import { APP_CONFIG, VALIDATION_MESSAGES } from './constants';
|
|
import type { NewGameData } from './types';
|
|
|
|
export interface ValidationResult {
|
|
isValid: boolean;
|
|
errors: string[];
|
|
}
|
|
|
|
export function validatePlayerName(name: string): ValidationResult {
|
|
const errors: string[] = [];
|
|
const trimmedName = name.trim();
|
|
|
|
if (!trimmedName) {
|
|
errors.push(VALIDATION_MESSAGES.PLAYER_NAME_REQUIRED);
|
|
}
|
|
|
|
if (trimmedName.length > APP_CONFIG.MAX_PLAYER_NAME_LENGTH) {
|
|
errors.push(VALIDATION_MESSAGES.PLAYER_NAME_TOO_LONG);
|
|
}
|
|
|
|
return {
|
|
isValid: errors.length === 0,
|
|
errors,
|
|
};
|
|
}
|
|
|
|
export function validateGameData(data: NewGameData): ValidationResult {
|
|
const errors: string[] = [];
|
|
|
|
try {
|
|
// Validate player names
|
|
const player1Validation = validatePlayerName(data.player1);
|
|
const player2Validation = validatePlayerName(data.player2);
|
|
|
|
errors.push(...player1Validation.errors);
|
|
errors.push(...player2Validation.errors);
|
|
|
|
// Check for duplicate player names
|
|
const playerNames = [data.player1.trim(), data.player2.trim()];
|
|
if (data.player3?.trim()) {
|
|
const player3Validation = validatePlayerName(data.player3);
|
|
errors.push(...player3Validation.errors);
|
|
playerNames.push(data.player3.trim());
|
|
}
|
|
|
|
const uniqueNames = new Set(playerNames.filter(name => name.length > 0));
|
|
if (uniqueNames.size !== playerNames.filter(name => name.length > 0).length) {
|
|
errors.push(VALIDATION_MESSAGES.DUPLICATE_PLAYER_NAMES);
|
|
}
|
|
|
|
// Validate game type
|
|
if (!data.gameType?.trim()) {
|
|
errors.push(VALIDATION_MESSAGES.GAME_TYPE_REQUIRED);
|
|
}
|
|
|
|
// Validate race to
|
|
if (!data.raceTo?.trim()) {
|
|
errors.push(VALIDATION_MESSAGES.RACE_TO_REQUIRED);
|
|
} else {
|
|
const raceToNumber = parseInt(data.raceTo, 10);
|
|
if (isNaN(raceToNumber) || raceToNumber <= 0) {
|
|
errors.push(VALIDATION_MESSAGES.RACE_TO_INVALID);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Validation error:', error);
|
|
errors.push('Ein unerwarteter Validierungsfehler ist aufgetreten');
|
|
}
|
|
|
|
return {
|
|
isValid: errors.length === 0,
|
|
errors,
|
|
};
|
|
}
|
|
|
|
export function sanitizePlayerName(name: string): string {
|
|
return name
|
|
.trim()
|
|
.slice(0, APP_CONFIG.MAX_PLAYER_NAME_LENGTH)
|
|
.replace(/[^\w\s-]/g, ''); // Remove special characters except spaces and hyphens
|
|
}
|
|
|
|
export function validateRaceTo(value: string): ValidationResult {
|
|
const errors: string[] = [];
|
|
|
|
if (!value?.trim()) {
|
|
errors.push(VALIDATION_MESSAGES.RACE_TO_REQUIRED);
|
|
} else {
|
|
const numValue = parseInt(value, 10);
|
|
if (isNaN(numValue) || numValue <= 0) {
|
|
errors.push(VALIDATION_MESSAGES.RACE_TO_INVALID);
|
|
}
|
|
}
|
|
|
|
return {
|
|
isValid: errors.length === 0,
|
|
errors,
|
|
};
|
|
} |