import { useMutation } from "@tanstack/react-query"; import { useState } from "react"; import { toast } from "sonner"; import { Check, Save, Square, Stethoscope, Trash2, Unlock } from "lucide-react"; import { Card } from "~/client/components/ui/card"; import { Button } from "~/client/components/ui/button"; import { Input } from "~/client/components/ui/input"; import { Label } from "~/client/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "~/client/components/ui/alert-dialog"; import type { Repository } from "~/client/lib/types"; import { REPOSITORY_BASE } from "~/client/lib/constants"; import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime"; import { cancelDoctorMutation, deleteRepositoryMutation, startDoctorMutation, unlockRepositoryMutation, updateRepositoryMutation, } from "~/client/api-client/@tanstack/react-query.gen"; import type { CompressionMode, RepositoryConfig } from "~/schemas/restic"; import { DoctorReport } from "../components/doctor-report"; import { parseError } from "~/client/lib/errors"; import { useNavigate } from "@tanstack/react-router"; type Props = { repository: Repository; }; const getEffectiveLocalPath = (repository: Repository): string | null => { if (repository.type !== "local") return null; const config = repository.config as { name: string; path?: string; isExistingRepository?: boolean }; if (config.isExistingRepository) { return config.path ?? null; } const basePath = config.path || REPOSITORY_BASE; return `${basePath}/${config.name}`; }; export const RepositoryInfoTabContent = ({ repository }: Props) => { const [name, setName] = useState(repository.name); const [compressionMode, setCompressionMode] = useState(repository.compressionMode || "off"); const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const navigate = useNavigate(); const effectiveLocalPath = getEffectiveLocalPath(repository); const updateMutation = useMutation({ ...updateRepositoryMutation(), onSuccess: () => { toast.success("Repository updated successfully"); setShowConfirmDialog(false); }, onError: (error) => { toast.error("Failed to update repository", { description: error.message, richColors: true }); setShowConfirmDialog(false); }, }); const deleteRepo = useMutation({ ...deleteRepositoryMutation(), onSuccess: () => { toast.success("Repository deleted successfully"); void navigate({ to: "/repositories" }); }, onError: (error) => { toast.error("Failed to delete repository", { description: parseError(error)?.message, }); }, }); const startDoctor = useMutation({ ...startDoctorMutation(), onError: (error) => { toast.error("Failed to start doctor", { description: parseError(error)?.message, }); }, }); const cancelDoctor = useMutation({ ...cancelDoctorMutation(), onSuccess: () => { toast.info("Doctor operation cancelled"); }, onError: (error) => { toast.error("Failed to cancel doctor", { description: parseError(error)?.message, }); }, }); const unlockRepo = useMutation({ ...unlockRepositoryMutation(), onSuccess: () => { toast.success("Repository unlocked successfully"); }, onError: (error) => { toast.error("Failed to unlock repository", { description: parseError(error)?.message, }); }, }); const handleSubmit = (e: React.SubmitEvent) => { e.preventDefault(); setShowConfirmDialog(true); }; const confirmUpdate = () => { updateMutation.mutate({ path: { id: repository.id }, body: { name, compressionMode }, }); }; const handleConfirmDelete = () => { setShowDeleteConfirm(false); deleteRepo.mutate({ path: { id: repository.id } }); }; const hasChanges = name !== repository.name || compressionMode !== ((repository.compressionMode as CompressionMode) || "off"); const config = repository.config as RepositoryConfig; return ( <>
Repository Settings
{repository.status === "doctor" ? ( ) : ( )}
setName(e.target.value)} placeholder="Repository name" maxLength={32} minLength={2} />

Unique identifier for the repository.

Compression level for new data.

Repository Information

Backend

{repository.type}

Status

{repository.status || "unknown"}

{effectiveLocalPath && (
Local path

{effectiveLocalPath}

)}
Created at

{formatDateTime(repository.createdAt)}

Last checked

{formatTimeAgo(repository.lastChecked)}

{config.cacert && (
CA Certificate

configured

)} {"insecureTls" in config && (
TLS Certificate Validation

{config.insecureTls ? ( disabled ) : ( enabled )}

)}
{repository.lastError && (

Last Error

{repository.lastError}

)}

Configuration

{JSON.stringify(repository.config, null, 2)}
Update repository Are you sure you want to update the repository settings? Cancel Update Delete repository? Are you sure you want to delete the repository {repository.name}? This will not remove the actual data from the backend storage, only the repository configuration will be deleted.

All backup schedules associated with this repository will also be removed.
Cancel Delete repository
); };