import { Check, ChevronDown, Database, Eraser, HardDrive, Pencil, Play, Square, Trash2, X } from "lucide-react"; import { useMemo, useState } from "react"; import { OnOff } from "~/client/components/onoff"; import { Button } from "~/client/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogHeader, AlertDialogTitle, } from "~/client/components/ui/alert-dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "~/client/components/ui/dropdown-menu"; import type { BackupSchedule } from "~/client/lib/types"; import { BackupProgressCard } from "./backup-progress-card"; import { getBackupProgressOptions, runForgetMutation } from "~/client/api-client/@tanstack/react-query.gen"; import { useMutation, useSuspenseQuery } from "@tanstack/react-query"; import { Link, useNavigate } from "@tanstack/react-router"; import { toast } from "sonner"; import { handleRepositoryError } from "~/client/lib/errors"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/client/components/ui/collapsible"; import { TimeAgo } from "~/client/components/time-ago"; import { useTimeFormat } from "~/client/lib/datetime"; import { cn } from "~/client/lib/utils"; type Props = { schedule: BackupSchedule; handleToggleEnabled: (enabled: boolean) => void; handleRunBackupNow: () => void; handleStopBackup: () => void; handleDeleteSchedule: () => void; }; export const ScheduleSummary = (props: Props) => { const { schedule, handleToggleEnabled, handleRunBackupNow, handleStopBackup, handleDeleteSchedule } = props; const { formatShortDateTime } = useTimeFormat(); const navigate = useNavigate(); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [showForgetConfirm, setShowForgetConfirm] = useState(false); const [showStopConfirm, setShowStopConfirm] = useState(false); const { data: initialProgress } = useSuspenseQuery({ ...getBackupProgressOptions({ path: { shortId: schedule.shortId } }), }); const runForget = useMutation({ ...runForgetMutation(), onError: (error) => { handleRepositoryError("Failed to apply retention policy", error, schedule.repository.shortId); }, }); const summary = useMemo(() => { const scheduleLabel = schedule ? schedule.cronExpression || "Manual only" : "-"; const retentionParts: string[] = []; if (schedule?.retentionPolicy) { const rp = schedule.retentionPolicy; if (rp.keepLast) retentionParts.push(`${rp.keepLast} last`); if (rp.keepHourly) retentionParts.push(`${rp.keepHourly} hourly`); if (rp.keepDaily) retentionParts.push(`${rp.keepDaily} daily`); if (rp.keepWeekly) retentionParts.push(`${rp.keepWeekly} weekly`); if (rp.keepMonthly) retentionParts.push(`${rp.keepMonthly} monthly`); if (rp.keepYearly) retentionParts.push(`${rp.keepYearly} yearly`); } return { vol: schedule.volume.name, scheduleLabel, repositoryLabel: schedule.repositoryId || "No repository selected", retentionLabel: retentionParts.length > 0 ? retentionParts.join(" • ") : "No retention policy", }; }, [schedule]); const handleConfirmDelete = () => { setShowDeleteConfirm(false); handleDeleteSchedule(); }; const handleConfirmForget = () => { setShowForgetConfirm(false); toast.promise(runForget.mutateAsync({ path: { shortId: schedule.shortId } }), { loading: "Running cleanup...", success: "Retention policy applied successfully", }); }; const handleConfirmStop = () => { setShowStopConfirm(false); if (schedule.lastBackupStatus !== "in_progress") return; handleStopBackup(); }; return (
{schedule.name} {schedule.volume.name} {schedule.repository.name}
{schedule.lastBackupStatus === "in_progress" ? ( ) : ( )} {schedule.retentionPolicy && ( setShowForgetConfirm(true)} disabled={runForget.isPending}> Run cleanup )} navigate({ to: "/backups/$backupId/edit", params: { backupId: schedule.shortId } })} > Edit schedule setShowDeleteConfirm(true)}> Delete

Schedule

{summary.scheduleLabel}

Repository

{schedule.repository.name}

Last backup

Next backup

{formatShortDateTime(schedule.nextBackupAt)}

Status

{schedule.lastBackupStatus === "success" && "✓ Success"} {schedule.lastBackupStatus === "error" && "✗ Error"} {schedule.lastBackupStatus === "in_progress" && "⟳ in progress..."} {schedule.lastBackupStatus === "warning" && "! Warning"} {!schedule.lastBackupStatus && "—"}

{(schedule.lastBackupStatus === "warning" || schedule.lastBackupStatus === "error") && (
{schedule.lastBackupStatus === "warning" ? "Warning details" : "Error details"}

{schedule.lastBackupError ?? "No additional details available. check your container logs for more information."}

)}
{schedule.lastBackupStatus === "in_progress" && ( )} Delete backup schedule? Are you sure you want to delete this backup schedule for {schedule.volume.name}? This action cannot be undone. Existing snapshots will not be deleted.
Cancel Delete schedule
Run retention policy cleanup? This will apply the retention policy and permanently delete old snapshots according to the configured rules ({summary.retentionLabel}). This action cannot be undone.
Cancel Run cleanup
Stop running backup? Are you sure you want to stop the current backup for {schedule.name}?
Cancel Stop backup
); };