// app/(app)/users/UsersCsvImportButton.tsx 'use client'; import { useRef, useState } from 'react'; import { useRouter } from 'next/navigation'; import Button from '@/components/ui/Button'; type SimpleGroup = { id: string; name: string; }; type Props = { groups: SimpleGroup[]; }; type ParsedRow = { nwkennung: string; lastName: string; firstName: string; arbeitsname: string; groupName: string | null; rawLine: string; lineNumber: number; }; export default function UsersCsvImportButton({ groups }: Props) { const router = useRouter(); const fileInputRef = useRef(null); const [importing, setImporting] = useState(false); const [importError, setImportError] = useState(null); const [importSummary, setImportSummary] = useState(null); const [importProgress, setImportProgress] = useState(null); async function handleImportCsv( e: React.ChangeEvent, ): Promise { const file = e.target.files?.[0]; if (!file) return; setImporting(true); setImportError(null); setImportSummary(null); setImportProgress('Lese Datei …'); try { const text = await file.text(); setImportProgress('Analysiere CSV …'); const lines = text.split(/\r?\n/); const parsedRows: ParsedRow[] = []; let skippedCount = 0; for (let index = 0; index < lines.length; index++) { const raw = lines[index]; const trimmed = raw.trim(); if (!trimmed) continue; // erste Zeile = Header if (index === 0) continue; const parts = trimmed.split(';'); if (parts.length < 4) { console.warn('Zeile übersprungen (falsches Format):', trimmed); skippedCount++; continue; } const [ nwkennungRaw, lastNameRaw, firstNameRaw, arbeitsnameRaw, groupRaw, ] = parts; const nwkennung = (nwkennungRaw ?? '').trim().toLowerCase(); const lastName = (lastNameRaw ?? '').trim(); const firstName = (firstNameRaw ?? '').trim(); const arbeitsname = (arbeitsnameRaw ?? '').trim(); const groupNameRaw = (groupRaw ?? '').trim(); const groupName = groupNameRaw ? groupNameRaw : null; if (!nwkennung || !lastName || !firstName || !arbeitsname) { console.warn( 'Zeile übersprungen (Pflichtfelder leer):', trimmed, ); skippedCount++; continue; } parsedRows.push({ nwkennung, lastName, firstName, arbeitsname, groupName, rawLine: trimmed, lineNumber: index + 1, }); } if (parsedRows.length === 0) { setImportError( 'Keine gültigen Zeilen gefunden. Bitte CSV prüfen.', ); return; } // 1) Gruppen vorbereiten: bestehende + neue setImportProgress('Ermittle Gruppen …'); const groupCache = new Map(); for (const g of groups) { const name = g.name.trim(); if (name) groupCache.set(name, g.id); } const knownGroupNames = new Set( Array.from(groupCache.keys()), ); const newGroupNamesSet = new Set(); for (const row of parsedRows) { if (!row.groupName) continue; const gName = row.groupName.trim(); if (!gName) continue; if (!knownGroupNames.has(gName)) { newGroupNamesSet.add(gName); } } const newGroupNames = Array.from(newGroupNamesSet.values()); // 2) Neue Gruppen in einem Rutsch anlegen if (newGroupNames.length > 0) { setImportProgress( `Lege ${newGroupNames.length} neue Gruppe(n) an …`, ); const resGroups = await fetch('/api/user-groups', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ names: newGroupNames }), }); if (!resGroups.ok) { const data = await resGroups.json().catch(() => null); console.error( 'Fehler beim Bulk-Anlegen der Gruppen:', resGroups.status, data, ); setImportError( 'Fehler beim Anlegen der Gruppen. Import abgebrochen.', ); return; } const data = await resGroups.json(); const createdGroups = (data.groups ?? []) as SimpleGroup[]; for (const g of createdGroups) { const name = g.name.trim(); if (name) { groupCache.set(name, g.id); knownGroupNames.add(name); } } } // 3) Benutzer-Payload für Bulk-Import bauen setImportProgress('Bereite Benutzer-Daten für Import vor …'); type UserPayload = { nwkennung: string; firstName: string; lastName: string; arbeitsname: string; groupId?: string | null; }; const usersPayload: UserPayload[] = []; let skippedByGroup = 0; for (const row of parsedRows) { let groupId: string | null = null; if (row.groupName) { const gName = row.groupName.trim(); if (gName) { const id = groupCache.get(gName); if (!id) { console.warn( 'Zeile übersprungen (Gruppe nicht vorhanden):', row.rawLine, ); skippedByGroup++; continue; } groupId = id; } } usersPayload.push({ nwkennung: row.nwkennung, firstName: row.firstName, lastName: row.lastName, arbeitsname: row.arbeitsname, groupId: groupId ?? null, }); } if (usersPayload.length === 0) { setImportError( 'Keine gültigen Benutzer-Datensätze nach Gruppenzuordnung. Import abgebrochen.', ); return; } // 4) Bulk-Import der Benutzer setImportProgress( `Importiere ${usersPayload.length} Benutzer …`, ); const resUsers = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ users: usersPayload }), }); if (!resUsers.ok) { const data = await resUsers.json().catch(() => null); console.error( 'Fehler beim Bulk-Import der Benutzer:', resUsers.status, data, ); setImportError( 'Fehler beim Import der Benutzer. Details siehe Konsole.', ); return; } const result = await resUsers.json(); const createdCount = result.createdCount ?? 0; const skippedServer = result.skippedCount ?? 0; const totalSkipped = skippedCount + skippedByGroup + skippedServer; setImportSummary( `Import abgeschlossen: ${createdCount} Personen importiert, ${totalSkipped} Zeilen übersprungen.`, ); setImportProgress('Import abgeschlossen.'); router.refresh(); } catch (err: any) { console.error('Fehler beim CSV-Import', err); setImportError( err instanceof Error ? err.message : 'Fehler beim CSV-Import.', ); } finally { setImporting(false); if (e.target) { e.target.value = ''; } } } return (
{importProgress && (

{importProgress}

)} {importSummary && (

{importSummary}

)} {importError && (

{importError}

)}
); }