import React, { useRef, useState } from 'react'; import { Upload, Check, AlertCircle, FileArchive } from 'lucide-react'; import { ImportSummary } from '../../lib/types'; import { api } from '../../lib/api'; interface ImportCardProps { source: 'mindsift' | 'obsidian'; title: string; description: React.ReactNode; accept: string; } const summaryParts = (s: ImportSummary) => [ s.folders && `${s.folders} Ordner`, s.notes && `${s.notes} ${s.notes === 1 ? 'Notiz' : 'Notizen'}`, s.taskLists && `${s.taskLists} Aufgabenliste${s.taskLists === 1 ? '' : 'n'}`, s.tasks && `${s.tasks} Aufgabe${s.tasks === 1 ? '' : 'n'}`, s.lists && `${s.lists} Liste${s.lists === 1 ? '' : 'n'} mit ${s.listItems} Einträgen`, s.inbox && `${s.inbox} Gedanke${s.inbox === 1 ? '' : 'n'} für die Triage`, ].filter(Boolean) as string[]; // One import path: drop or pick a file, see a preview of what would be created, then import. // Touch targets are large enough for phones; the file picker works on mobile browsers. export const ImportCard: React.FC = ({ source, title, description, accept }) => { const inputRef = useRef(null); const [file, setFile] = useState(null); const [preview, setPreview] = useState(null); const [result, setResult] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [dragOver, setDragOver] = useState(false); // MindSift data can replace the current state instead of being added next to it const [mode, setMode] = useState<'merge' | 'restore'>('merge'); const [confirmRestore, setConfirmRestore] = useState(false); const restoring = source === 'mindsift' && mode === 'restore'; const reset = () => { setFile(null); setPreview(null); setError(null); setConfirmRestore(false); }; const pick = async (f?: File) => { if (!f) return; reset(); setResult(null); setFile(f); setBusy(true); try { setPreview(await api.importData(source, f, true)); } catch (err: any) { setError(err.message || 'Datei konnte nicht gelesen werden'); } finally { setBusy(false); } }; const runImport = async () => { if (!file) return; setBusy(true); setError(null); try { setResult(await api.importData(source, file, false, restoring ? 'restore' : 'merge')); setFile(null); setPreview(null); setConfirmRestore(false); } catch (err: any) { setError(err.message || 'Import fehlgeschlagen'); } finally { setBusy(false); } }; const shown = result || preview; return (

{title}

{description}

{source === 'mindsift' && (
{( [ ['merge', 'Hinzufügen', 'Bestehende Daten behalten, Backup daneben anlegen.'], ['restore', 'Wiederherstellen', 'Eigene Notizen, Aufgaben und Listen vorher löschen.'], ] as const ).map(([value, label, text]) => ( ))}
)}
{ e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={(e) => { e.preventDefault(); setDragOver(false); pick(e.dataTransfer.files?.[0]); }} className={`p-4 border border-dashed rounded-card flex flex-col sm:flex-row items-center justify-center gap-3 text-center transition-colors ${ dragOver ? 'border-accent bg-accent/10' : 'border-border' }`} > Datei hierher ziehen oder { pick(e.target.files?.[0]); e.target.value = ''; }} />
{busy &&

{preview ? 'Importiere…' : 'Lese Datei…'}

} {error && (

{error}

)} {shown && !busy && (

{result ? : } {result ? 'Import abgeschlossen' : `Vorschau: ${file?.name}`}

{result ? 'Angelegt: ' : 'Wird angelegt: '} {summaryParts(shown).join(', ') || 'nichts'}.

{shown.skipped.length > 0 && (
{shown.skipped.length} übersprungen
    {shown.skipped.map((s, i) => (
  • {s.path} – {s.reason}
  • ))}
)} {!result && restoring && ( )} {!result && (
)}
)}
); };