This commit is contained in:
2025-12-11 00:57:32 +01:00
parent 1303a14493
commit 6a427eab57
112 changed files with 6148 additions and 2272 deletions

View File

@@ -1,13 +1,22 @@
'use client';
import { useState } from 'react';
import Button from '../ui/Button';
import { Result } from '@gridpilot/racing/domain/entities/Result';
import { v4 as uuidv4 } from 'uuid';
import Button from '../ui/Button';
interface ImportResultRowDTO {
id: string;
raceId: string;
driverId: string;
position: number;
fastestLap: number;
incidents: number;
startPosition: number;
}
interface ImportResultsFormProps {
raceId: string;
onSuccess: (results: Result[]) => void;
onSuccess: (results: ImportResultRowDTO[]) => void;
onError: (error: string) => void;
}
@@ -25,36 +34,35 @@ export default function ImportResultsForm({ raceId, onSuccess, onError }: Import
const parseCSV = (content: string): CSVRow[] => {
const lines = content.trim().split('\n');
if (lines.length < 2) {
throw new Error('CSV file is empty or invalid');
}
// Parse header
const header = lines[0].toLowerCase().split(',').map(h => h.trim());
const header = lines[0].toLowerCase().split(',').map((h) => h.trim());
const requiredFields = ['driverid', 'position', 'fastestlap', 'incidents', 'startposition'];
for (const field of requiredFields) {
if (!header.includes(field)) {
throw new Error(`Missing required field: ${field}`);
}
}
// Parse rows
const rows: CSVRow[] = [];
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(',').map(v => v.trim());
const values = lines[i].split(',').map((v) => v.trim());
if (values.length !== header.length) {
throw new Error(`Invalid row ${i}: expected ${header.length} columns, got ${values.length}`);
throw new Error(
`Invalid row ${i}: expected ${header.length} columns, got ${values.length}`,
);
}
const row: any = {};
const row: Record<string, string> = {};
header.forEach((field, index) => {
row[field] = values[index];
row[field] = values[index] ?? '';
});
// Validate and convert types
const driverId = row.driverid;
const position = parseInt(row.position, 10);
const fastestLap = parseFloat(row.fastestlap);
@@ -65,34 +73,32 @@ export default function ImportResultsForm({ raceId, onSuccess, onError }: Import
throw new Error(`Row ${i}: driverId is required`);
}
if (isNaN(position) || position < 1) {
if (Number.isNaN(position) || position < 1) {
throw new Error(`Row ${i}: position must be a positive integer`);
}
if (isNaN(fastestLap) || fastestLap < 0) {
if (Number.isNaN(fastestLap) || fastestLap < 0) {
throw new Error(`Row ${i}: fastestLap must be a non-negative number`);
}
if (isNaN(incidents) || incidents < 0) {
if (Number.isNaN(incidents) || incidents < 0) {
throw new Error(`Row ${i}: incidents must be a non-negative integer`);
}
if (isNaN(startPosition) || startPosition < 1) {
if (Number.isNaN(startPosition) || startPosition < 1) {
throw new Error(`Row ${i}: startPosition must be a positive integer`);
}
rows.push({ driverId, position, fastestLap, incidents, startPosition });
}
// Validate no duplicate positions
const positions = rows.map(r => r.position);
const positions = rows.map((r) => r.position);
const uniquePositions = new Set(positions);
if (positions.length !== uniquePositions.size) {
throw new Error('Duplicate positions found in CSV');
}
// Validate no duplicate drivers
const driverIds = rows.map(r => r.driverId);
const driverIds = rows.map((r) => r.driverId);
const uniqueDrivers = new Set(driverIds);
if (driverIds.length !== uniqueDrivers.size) {
throw new Error('Duplicate driver IDs found in CSV');
@@ -109,33 +115,27 @@ export default function ImportResultsForm({ raceId, onSuccess, onError }: Import
setError(null);
try {
// Read file
const content = await file.text();
// Parse CSV
const rows = parseCSV(content);
// Create Result entities
const results = rows.map(row =>
Result.create({
id: uuidv4(),
raceId,
driverId: row.driverId,
position: row.position,
fastestLap: row.fastestLap,
incidents: row.incidents,
startPosition: row.startPosition,
})
);
const results: ImportResultRowDTO[] = rows.map((row) => ({
id: uuidv4(),
raceId,
driverId: row.driverId,
position: row.position,
fastestLap: row.fastestLap,
incidents: row.incidents,
startPosition: row.startPosition,
}));
onSuccess(results);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to parse CSV file';
const errorMessage =
err instanceof Error ? err.message : 'Failed to parse CSV file';
setError(errorMessage);
onError(errorMessage);
} finally {
setUploading(false);
// Reset file input
event.target.value = '';
}
};