import React, { useState, useRef } from 'react'; import { toast } from 'sonner'; import { Database, Upload, AlertTriangle, Loader2 } from 'lucide-react'; import { api } from '@/api'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { SectionCard, CountPill, fmt, importErrorState } from './dataShared'; export default function ImportMyDataSection({ onHistoryRefresh }) { const fileRef = useRef(null); const [file, setFile] = useState(null); const [preview, setPreview] = useState({ status: 'idle', data: null, error: null }); const [applyState, setApplyState] = useState({ status: 'idle', result: null, error: null }); const [confirmOpen, setConfirmOpen] = useState(false); const reset = () => { setFile(null); setPreview({ status: 'idle', data: null, error: null }); setApplyState({ status: 'idle', result: null, error: null }); if (fileRef.current) fileRef.current.value = ''; }; const handlePreview = async () => { if (!file) { toast.error('Choose a SQLite data export first.'); return; } setPreview({ status: 'loading', data: null, error: null }); setApplyState({ status: 'idle', result: null, error: null }); try { const data = await api.previewUserDbImport(file); setPreview({ status: 'ready', data, error: null }); toast.success('SQLite export preview ready.'); } catch (err) { setPreview({ status: 'error', data: null, error: importErrorState(err, 'SQLite import preview failed.') }); toast.error(err.message || 'SQLite import preview failed.'); } }; const handleApply = () => { if (!preview.data?.import_session_id) return; setConfirmOpen(true); }; const handleConfirmImport = async () => { setConfirmOpen(false); setApplyState({ status: 'loading', result: null, error: null }); try { const result = await api.applyUserDbImport({ import_session_id: preview.data.import_session_id, options: { overwrite: false }, }); setApplyState({ status: 'done', result, error: null }); toast.success('SQLite data import applied.'); onHistoryRefresh?.(); } catch (err) { setApplyState({ status: 'error', result: null, error: importErrorState(err, 'SQLite import apply failed.') }); toast.error(err.message || 'SQLite import apply failed.'); } }; const counts = preview.data?.counts || {}; const summary = preview.data?.summary || {}; return ( <>

Import a SQLite data export created by this app.

This is not a full system restore. Existing records are skipped by default, and admin/system data is never imported.

{preview.status === 'error' && (
{preview.error?.message || 'SQLite import preview failed.'} {preview.error?.details?.length > 0 && (
    {preview.error.details.map((d, i) => (
  • {d.message || d.table || JSON.stringify(d)}
  • ))}
)}
)} {preview.status === 'ready' && preview.data && (

Preview ready

Exported {fmt(preview.data.metadata?.exported_at)} · {preview.data.source_filename || 'SQLite export'}

User data only
{Object.entries(summary).filter(([, v]) => v && typeof v === 'object').map(([key, value]) => (

{key.replace(/_/g, ' ')}

create {value.create || 0} · skip {value.skip || 0} · conflict {value.conflict || 0}

))}
{preview.data.warnings?.length > 0 && (
{preview.data.warnings.map((warning, i) => (

{warning}

))}
)}

Review the preview before applying. Nothing is imported until you confirm.

)} {applyState.status === 'done' && applyState.result && (

SQLite import applied

)} {applyState.status === 'error' && (
{applyState.error?.message || 'SQLite import apply failed.'}
)}
{/* Import confirmation dialog */} Import SQLite data export? Import this SQLite data export into your account? Existing records will be skipped by default. Cancel Confirm Import ); }