import { zodResolver } from "@hookform/resolvers/zod"; import { useCallback, useState } from "react"; import { useForm } from "react-hook-form"; import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error"; import { Form } from "~/client/components/ui/form"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/client/components/ui/collapsible"; import type { BackupSchedule, Volume } from "~/client/lib/types"; import { AdvancedSection } from "./advanced-section"; import { BasicInfoSection } from "./basic-info-section"; import { ExcludeSection } from "./exclude-section"; import { FrequencySection } from "./frequency-section"; import { PathsSection } from "./paths-section"; import { RetentionSection } from "./retention-section"; import { SummarySection } from "./summary-section"; import { internalFormSchema, type BackupScheduleFormValues, type InternalFormValues } from "./types"; import { backupScheduleToFormValues, parseMultilineEntries } from "./utils"; export type { BackupScheduleFormValues }; type Props = { volume: Volume; initialValues?: BackupSchedule; onSubmit: (data: BackupScheduleFormValues) => void; loading?: boolean; summaryContent?: React.ReactNode; formId: string; }; export const CreateScheduleForm = ({ initialValues, formId, onSubmit, volume }: Props) => { const form = useForm({ resolver: zodResolver(internalFormSchema), defaultValues: backupScheduleToFormValues(initialValues), }); const scrollToFirstError = useScrollToFormError(); const handleSubmit = useCallback( (data: InternalFormValues) => { const { excludePatternsText, excludeIfPresentText, includePatterns, customResticParamsText, includePaths, cronExpression, ...rest } = data; const excludePatterns = parseMultilineEntries(excludePatternsText); const excludeIfPresent = parseMultilineEntries(excludeIfPresentText); const parsedIncludePatterns = parseMultilineEntries(includePatterns); const customResticParams = parseMultilineEntries(customResticParamsText); onSubmit({ ...rest, cronExpression, includePaths: includePaths?.length ? includePaths : [], includePatterns: parsedIncludePatterns.length > 0 ? parsedIncludePatterns : [], excludePatterns, excludeIfPresent, customResticParams, }); }, [onSubmit], ); const frequency = form.watch("frequency"); const formValues = form.watch(); const [selectedPaths, setSelectedPaths] = useState>(new Set(initialValues?.includePaths || [])); const [showAllSelectedPaths, setShowAllSelectedPaths] = useState(false); const handleSelectionChange = useCallback( (paths: Set) => { setSelectedPaths(paths); form.setValue("includePaths", Array.from(paths)); }, [form], ); const handleRemovePath = useCallback( (pathToRemove: string) => { const newPaths = new Set(selectedPaths); newPaths.delete(pathToRemove); setSelectedPaths(newPaths); form.setValue("includePaths", Array.from(newPaths)); }, [selectedPaths, form], ); return (
Backup automation Schedule automated backups of {volume.name} to a secure repository. Backup paths Select which folders or files to include in the backup. If no paths are selected, the entire volume will be backed up. setShowAllSelectedPaths(!showAllSelectedPaths)} /> Exclude patterns Optionally specify patterns to exclude from backups. Enter one pattern per line (e.g., *.tmp, node_modules/**, .cache/). Retention policy Define how many snapshots to keep. Leave empty to keep all. Advanced
); };