import { useId, useState } from "react"; import { useMutation, useQuery } from "@tanstack/react-query"; import { Database, HardDrive, Plus } from "lucide-react"; import { Link, useNavigate } from "react-router"; import { toast } from "sonner"; import { createBackupScheduleMutation, listRepositoriesOptions, listVolumesOptions, } from "~/client/api-client/@tanstack/react-query.gen"; import { Button } from "~/client/components/ui/button"; import { Card, CardContent } from "~/client/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select"; import { parseError } from "~/client/lib/errors"; import { EmptyState } from "~/client/components/empty-state"; import { getCronExpression } from "~/utils/utils"; import { CreateScheduleForm, type BackupScheduleFormValues } from "../components/create-schedule-form"; import type { Route } from "./+types/create-backup"; import { listRepositories, listVolumes } from "~/client/api-client"; export const handle = { breadcrumb: () => [{ label: "Backups", href: "/backups" }, { label: "Create" }], }; export function meta(_: Route.MetaArgs) { return [ { title: "Zerobyte - Create Backup Job" }, { name: "description", content: "Create a new automated backup job for your volumes.", }, ]; } export const clientLoader = async () => { const volumes = await listVolumes(); const repositories = await listRepositories(); if (volumes.data && repositories.data) return { volumes: volumes.data, repositories: repositories.data }; return { volumes: [], repositories: [] }; }; export default function CreateBackup({ loaderData }: Route.ComponentProps) { const navigate = useNavigate(); const formId = useId(); const [selectedVolumeId, setSelectedVolumeId] = useState(); const { data: volumesData, isLoading: loadingVolumes } = useQuery({ ...listVolumesOptions(), initialData: loaderData.volumes, }); const { data: repositoriesData } = useQuery({ ...listRepositoriesOptions(), initialData: loaderData.repositories, }); const createSchedule = useMutation({ ...createBackupScheduleMutation(), onSuccess: (data) => { toast.success("Backup job created successfully"); navigate(`/backups/${data.id}`); }, onError: (error) => { toast.error("Failed to create backup job", { description: parseError(error)?.message, }); }, }); const handleSubmit = (formValues: BackupScheduleFormValues) => { if (!selectedVolumeId) return; const cronExpression = getCronExpression(formValues.frequency, formValues.dailyTime, formValues.weeklyDay); const retentionPolicy: Record = {}; if (formValues.keepLast) retentionPolicy.keepLast = formValues.keepLast; if (formValues.keepHourly) retentionPolicy.keepHourly = formValues.keepHourly; if (formValues.keepDaily) retentionPolicy.keepDaily = formValues.keepDaily; if (formValues.keepWeekly) retentionPolicy.keepWeekly = formValues.keepWeekly; if (formValues.keepMonthly) retentionPolicy.keepMonthly = formValues.keepMonthly; if (formValues.keepYearly) retentionPolicy.keepYearly = formValues.keepYearly; createSchedule.mutate({ body: { name: formValues.name, volumeId: selectedVolumeId, repositoryId: formValues.repositoryId, enabled: true, cronExpression, retentionPolicy: Object.keys(retentionPolicy).length > 0 ? retentionPolicy : undefined, includePatterns: formValues.includePatterns, excludePatterns: formValues.excludePatterns, excludeIfPresent: formValues.excludeIfPresent, }, }); }; const selectedVolume = volumesData.find((v) => v.id === selectedVolumeId); if (loadingVolumes) { return (

Loading...

); } if (!volumesData.length) { return ( Go to volumes } /> ); } if (!repositoriesData?.length) { return ( Go to repositories } /> ); } return (
{selectedVolume ? ( <>
) : (

Select a volume

Choose a volume from the dropdown above to configure its backup schedule.

)}
); }