import { useCallback, useState } from "react"; import { ChevronDown, FolderOpen, RotateCcw } from "lucide-react"; import { Button } from "~/client/components/ui/button"; import { Checkbox } from "~/client/components/ui/checkbox"; import { Label } from "~/client/components/ui/label"; import { Input } from "~/client/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "~/client/components/ui/alert-dialog"; import { PathSelector } from "~/client/components/path-selector"; import { OVERWRITE_MODES, type OverwriteMode } from "~/schemas/restic"; type RestoreLocation = "original" | "custom"; export interface RestoreSnapshotOptions { include?: string[]; delete?: boolean; excludeXattr?: string[]; targetPath?: string; overwrite?: OverwriteMode; } interface Props { /** Number of selected items to restore (0 means restore everything) */ selectedCount?: number; /** Callback when restore is confirmed */ onConfirm: (options: RestoreSnapshotOptions) => void; /** Trigger element for the dialog */ trigger: React.ReactNode; /** Pre-selected paths to include in restore (already transformed) */ includePaths?: string[]; } export const RestoreSnapshotDialog = (props: Props) => { const { selectedCount = 0, onConfirm, trigger, includePaths } = props; const [open, setOpen] = useState(false); const [deleteExtraFiles, setDeleteExtraFiles] = useState(false); const [showAdvanced, setShowAdvanced] = useState(false); const [excludeXattr, setExcludeXattr] = useState(""); const [restoreLocation, setRestoreLocation] = useState("original"); const [customTargetPath, setCustomTargetPath] = useState(""); const [overwriteMode, setOverwriteMode] = useState("always"); const handleConfirmRestore = useCallback(() => { const excludeXattrArray = excludeXattr ?.split(",") .map((s) => s.trim()) .filter(Boolean); const isCustomLocation = restoreLocation === "custom"; const targetPath = isCustomLocation && customTargetPath.trim() ? customTargetPath.trim() : undefined; onConfirm({ include: includePaths && includePaths.length > 0 ? includePaths : undefined, delete: deleteExtraFiles, excludeXattr: excludeXattrArray && excludeXattrArray.length > 0 ? excludeXattrArray : undefined, targetPath, overwrite: overwriteMode, }); setOpen(false); }, [excludeXattr, restoreLocation, customTargetPath, includePaths, deleteExtraFiles, overwriteMode, onConfirm]); return ( {trigger} Confirm Restore {selectedCount > 0 ? `This will restore ${selectedCount} selected ${selectedCount === 1 ? "item" : "items"} from the snapshot.` : "This will restore everything from the snapshot."}
{restoreLocation === "custom" && (

Files will be restored directly to this path

)}

{overwriteMode === OVERWRITE_MODES.always && "Existing files will always be replaced with the snapshot version."} {overwriteMode === OVERWRITE_MODES.ifChanged && "Files are only replaced if their content differs from the snapshot."} {overwriteMode === OVERWRITE_MODES.ifNewer && "Files are only replaced if the snapshot version has a newer modification time."} {overwriteMode === OVERWRITE_MODES.never && "Existing files will never be replaced, only missing files are restored."}

{showAdvanced && (
setExcludeXattr(e.target.value)} />

Exclude specific extended attributes during restore (comma-separated)

setDeleteExtraFiles(checked === true)} />
)}
Cancel Confirm Restore
); };