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) => (
|
{c}
|
))}
{sampleRows.slice(0, 5).map((row, i) => (
{showCols.map((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 …
) : (
)}
{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
)}
)
}