import { useState } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { useRestoreSnapshot, useReadSnapshotMetadata } from "@/hooks/use-snapshots"; import { toast } from "sonner"; import { open as openFile } from "@tauri-apps/plugin-dialog"; import { Loader2, CheckCircle2, XCircle, Upload, AlertTriangle, FileJson, } from "lucide-react"; import type { SnapshotMetadata } from "@/types"; interface Props { open: boolean; onOpenChange: (open: boolean) => void; connectionId: string; } type Step = "select" | "confirm" | "progress" | "done"; export function RestoreSnapshotDialog({ open, onOpenChange, connectionId }: Props) { const [step, setStep] = useState("select"); const [filePath, setFilePath] = useState(null); const [metadata, setMetadata] = useState(null); const [truncate, setTruncate] = useState(false); const readMeta = useReadSnapshotMetadata(); const { restore, rowsRestored, error, isRestoring, progress, reset } = useRestoreSnapshot(); const [prevOpen, setPrevOpen] = useState(false); if (open !== prevOpen) { setPrevOpen(open); if (open) { setStep("select"); setFilePath(null); setMetadata(null); setTruncate(false); reset(); } } const [prevProgress, setPrevProgress] = useState(progress); if (progress !== prevProgress) { setPrevProgress(progress); if (progress?.stage === "done" || progress?.stage === "error") { setStep("done"); } } const handleSelectFile = async () => { const selected = await openFile({ filters: [{ name: "JSON Snapshot", extensions: ["json"] }], multiple: false, }); if (!selected) return; const path = typeof selected === "string" ? selected : (selected as { path: string }).path; setFilePath(path); readMeta.mutate(path, { onSuccess: (meta) => { setMetadata(meta); setStep("confirm"); }, onError: (err) => toast.error("Invalid snapshot file", { description: String(err) }), }); }; const handleRestore = () => { if (!filePath) return; setStep("progress"); const snapshotId = crypto.randomUUID(); restore({ params: { connection_id: connectionId, file_path: filePath, truncate_before_restore: truncate, }, snapshotId, }); }; function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } return ( Restore Snapshot {step === "select" && ( <>

Select a snapshot file to restore

)} {step === "confirm" && metadata && ( <>
Name {metadata.name}
Created {new Date(metadata.created_at).toLocaleString()}
Tables {metadata.tables.length}
Total Rows {metadata.total_rows.toLocaleString()}
File Size {formatBytes(metadata.file_size_bytes)}

Tables included:

{metadata.tables.map((t) => ( {t.schema}.{t.table} ({t.row_count}) ))}
{truncate && (

This will DELETE all existing data in the affected tables before restoring.

)}
)} {step === "progress" && (
{progress?.message || "Starting..."} {progress?.percent ?? 0}%
{isRestoring && (
{progress?.detail || progress?.stage || "Restoring..."}
)}
)} {step === "done" && (
{error ? (

Restore Failed

{error}

) : (

Restore Completed

{rowsRestored?.toLocaleString()} rows restored successfully.

)} {error && }
)}
); }