import { useState, useEffect, useCallback } from 'react' import { useNavigate } from 'react-router-dom' import { ArrowLeft, FileSpreadsheet, Loader2 } from 'lucide-react' import { api } from '../utils/api' /** Module mit funktionierendem Universal-Executor; Aktivität: Legacy-Import auf der Aktivitätsseite. */ const UNIVERSAL_IMPORT_IDS = new Set(['nutrition', 'weight', 'blood_pressure']) function SampleTable({ sampleRows, columns }) { if (!sampleRows?.length || !columns?.length) return null const showCols = columns.slice(0, 8) return (
{showCols.map((c) => ( ))} {sampleRows.slice(0, 5).map((row, i) => ( {showCols.map((c) => ( ))} ))}
{c}
{row[c] ?? '—'}
) } export default function UniversalCsvImportPage() { const navigate = useNavigate() const [modules, setModules] = useState([]) const [moduleId, setModuleId] = useState('') const [file, setFile] = useState(null) const [analyzeResult, setAnalyzeResult] = useState(null) const [mappingsList, setMappingsList] = useState({ system_templates: [], user_mappings: [] }) const [mappingId, setMappingId] = useState('') const [loadingModules, setLoadingModules] = useState(true) const [loadingAnalyze, setLoadingAnalyze] = useState(false) const [loadingImport, setLoadingImport] = useState(false) const [error, setError] = useState(null) const [success, setSuccess] = useState(null) const loadMappings = useCallback(async (mid) => { if (!mid) { setMappingsList({ system_templates: [], user_mappings: [] }) return } const data = await api.getCsvMappings(mid) setMappingsList({ system_templates: data.system_templates || [], user_mappings: data.user_mappings || [], }) }, []) useEffect(() => { let cancel = false ;(async () => { setLoadingModules(true) setError(null) try { const data = await api.getCsvModules() const list = (data.modules || []).filter((m) => UNIVERSAL_IMPORT_IDS.has(m.id)) if (!cancel) { setModules(list) if (list.length && !moduleId) setModuleId(list[0].id) } } catch (e) { if (!cancel) setError(e.message || 'Module konnten nicht geladen werden') } finally { if (!cancel) setLoadingModules(false) } })() return () => { cancel = true } }, []) useEffect(() => { if (moduleId) { loadMappings(moduleId) setAnalyzeResult(null) setMappingId('') setSuccess(null) } }, [moduleId, loadMappings]) const allMappingOptions = [ ...(mappingsList.system_templates || []).map((m) => ({ id: m.id, label: `${m.name} (System)`, isSystem: true, })), ...(mappingsList.user_mappings || []).map((m) => ({ id: m.id, label: m.name, isSystem: false, })), ] const handleAnalyze = async () => { if (!file || !moduleId) { setError('Bitte Modul und Datei wählen') return } setLoadingAnalyze(true) setError(null) setSuccess(null) try { const res = await api.analyzeCsv(file, moduleId) setAnalyzeResult(res) const mapData = await api.getCsvMappings(moduleId) const sys = mapData.system_templates || [] const usr = mapData.user_mappings || [] setMappingsList({ system_templates: sys, user_mappings: usr }) const options = [ ...sys.map((m) => ({ id: m.id, name: m.name, isSystem: true })), ...usr.map((m) => ({ id: m.id, name: m.name, isSystem: false })), ] const detected = res.detected_mappings || [] const best = detected[0] let pick = options[0]?.id if (best && (best.confidence || 0) >= 0.5 && options.some((o) => o.id === best.mapping_id)) { pick = best.mapping_id } if (pick != null) setMappingId(String(pick)) else setMappingId('') } catch (e) { setError(e.message || 'Analyse fehlgeschlagen') setAnalyzeResult(null) } finally { setLoadingAnalyze(false) } } const handleImport = async () => { if (!file || !moduleId || !mappingId) { setError('Bitte Datei, Modul und Vorlage wählen') return } setLoadingImport(true) setError(null) setSuccess(null) try { const res = await api.importUniversalCsv(file, moduleId, Number(mappingId)) const st = res.stats || {} setSuccess( `Import abgeschlossen: ${st.imported ?? 0} neu, ${st.updated ?? 0} aktualisiert, ` + `${st.skipped ?? 0} übersprungen, ${st.errors ?? 0} Zeilenfehler.` ) } catch (e) { setError(e.message || 'Import fehlgeschlagen') } finally { setLoadingImport(false) } } return (

CSV-Import

Bekannte Formate (FDDB, Apple Health, Omron, …) per Vorlage zuordnen und importieren. Trainingseinheiten weiterhin unter{' '} .

{error && (
{error}
)} {success && (
{success}
)}
1. Modul
{loadingModules ? (
Lade …
) : ( )}
2. CSV-Datei
{ setFile(e.target.files?.[0] || null) setAnalyzeResult(null) setSuccess(null) }} />
{analyzeResult && (
3. Vorschau
Trennzeichen: {analyzeResult.delimiter} · Spalten: {analyzeResult.columns?.length ?? 0}
{(analyzeResult.detected_mappings || []).length > 0 && (
Erkannte Vorlagen:
    {analyzeResult.detected_mappings.map((d) => (
  • {d.mapping_name} · Übereinstimmung ca. {Math.round((d.confidence || 0) * 100)} %
  • ))}
)}
Import-Vorlage
)}
) }