'use client'; import { useState } from 'react'; 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: ImportResultRowDTO[]) => void; onError: (error: string) => void; } interface CSVRow { driverId: string; position: number; fastestLap: number; incidents: number; startPosition: number; } export default function ImportResultsForm({ raceId, onSuccess, onError }: ImportResultsFormProps) { const [uploading, setUploading] = useState(false); const [error, setError] = useState(null); const parseCSV = (content: string): CSVRow[] => { const lines = content.trim().split('\n'); if (lines.length < 2) { throw new Error('CSV file is empty or invalid'); } const headerLine = lines[0]!; const header = headerLine.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}`); } } const rows: CSVRow[] = []; for (let i = 1; i < lines.length; i++) { const line = lines[i]; if (!line) { continue; } const values = line.split(',').map((v) => v.trim()); if (values.length !== header.length) { throw new Error( `Invalid row ${i}: expected ${header.length} columns, got ${values.length}`, ); } const row: Record = {}; header.forEach((field, index) => { row[field] = values[index] ?? ''; }); const driverId = row['driverid'] ?? ''; const position = parseInt(row['position'] ?? '', 10); const fastestLap = parseFloat(row['fastestlap'] ?? ''); const incidents = parseInt(row['incidents'] ?? '', 10); const startPosition = parseInt(row['startposition'] ?? '', 10); if (!driverId || driverId.length === 0) { throw new Error(`Row ${i}: driverId is required`); } if (Number.isNaN(position) || position < 1) { throw new Error(`Row ${i}: position must be a positive integer`); } if (Number.isNaN(fastestLap) || fastestLap < 0) { throw new Error(`Row ${i}: fastestLap must be a non-negative number`); } if (Number.isNaN(incidents) || incidents < 0) { throw new Error(`Row ${i}: incidents must be a non-negative integer`); } if (Number.isNaN(startPosition) || startPosition < 1) { throw new Error(`Row ${i}: startPosition must be a positive integer`); } rows.push({ driverId, position, fastestLap, incidents, startPosition }); } 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'); } 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'); } return rows; }; const handleFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; setUploading(true); setError(null); try { const content = await file.text(); const rows = parseCSV(content); 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'; setError(errorMessage); onError(errorMessage); } finally { setUploading(false); event.target.value = ''; } }; return ( <>

CSV format: driverId, position, fastestLap, incidents, startPosition

{error && (
Error: {error}
)} {uploading && (
Parsing CSV and importing results...
)}

CSV Example:

{`driverId,position,fastestLap,incidents,startPosition
550e8400-e29b-41d4-a716-446655440001,1,92.456,0,3
550e8400-e29b-41d4-a716-446655440002,2,92.789,1,1
550e8400-e29b-41d4-a716-446655440003,3,93.012,2,2`}
        
); }