"use client" import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useZodForm } from "@/components/ui/form"; import {RetentionSettings, RetentionSettingsSchema} from "./backup-retention-settings.schema"; import {useMutation, useQueryClient} from "@tanstack/react-query"; import {useRouter} from "next/navigation"; import {updateOrCreateBackupRetentionPolicyAction} from "./backup-retention-settings.action"; import {DatabaseWith, RetentionPolicy} from "@/db/schema/07_database"; import {toast} from "sonner"; import {RadioGroup, RadioGroupItem} from "@/components/ui/radio-group"; import {Badge} from "@/components/ui/badge"; import {Separator} from "@/components/ui/separator"; import {Input} from "@/components/ui/input"; import {Button} from "@/components/ui/button"; import {Calendar, Save} from "lucide-react"; export type BackupRetentionSettingsFormProps = { defaultValues?: RetentionPolicy; database: DatabaseWith; }; export const BackupRetentionSettingsForm = ({defaultValues, database}: BackupRetentionSettingsFormProps) => { const queryClient = useQueryClient(); const router = useRouter(); const defaultValuesFormatted: RetentionSettings = { type: defaultValues?.type, count: defaultValues?.count ?? 7, days: defaultValues?.days ?? 30, gfs: { daily: defaultValues?.gfsDaily ?? 7, weekly: defaultValues?.gfsWeekly ?? 4, monthly: defaultValues?.gfsMonthly ?? 12, yearly: defaultValues?.gfsYearly ?? 3, }, }; const form = useZodForm({ schema: RetentionSettingsSchema, defaultValues: defaultValuesFormatted, }); const mutation = useMutation({ mutationFn: async (payload: RetentionSettings) => await updateOrCreateBackupRetentionPolicyAction({ databaseId: database.id, settings: payload, }), onSuccess: () => { toast.success("Retention policy updated successfully."); queryClient.invalidateQueries({queryKey: ["database-data", database.id]}); router.refresh(); }, onError: () => { toast.error("An error occurred while updating retention policy."); }, }); const calculateTotalFiles = (values: RetentionSettings) => { if (values.type === "gfs" && values.gfs) { return ( (values.gfs.daily ?? 0) + (values.gfs.weekly ?? 0) + (values.gfs.monthly ?? 0) + (values.gfs.yearly ?? 0) ); } return values.type === "count" ? values.count ?? 0 : values.type === "days" ? values.days ?? 0 : 0; }; const getStorageEstimate = (totalFiles: number) => { if (totalFiles <= 10) return "Low"; if (totalFiles <= 30) return "Medium"; return "High"; }; return (
{ await mutation.mutateAsync(values); }} > ( Retention Policy Type {[ { id: "count", label: "Keep last N backups", desc: "Simple count-based retention (e.g., keep last 10 backups)", }, { id: "days", label: "Keep backups for X days", desc: "Time-based retention (e.g., keep backups for 30 days)", }, { id: "gfs", label: "GFS Rotation", desc: "Grandfather-Father-Son rotation for enterprise/critical systems", badge: "Recommended", }, ].map((opt) => (
{opt.label} {opt.badge && ( {opt.badge} )}

{opt.desc}

{database.retentionPolicy?.type === opt.id && ( Actual )}
))}
)} /> {form.watch("type") && ( )} {form.watch("type") === "count" && ( ( Number of backups to keep field.onChange(e.target.valueAsNumber)} /> Older backups beyond this count will be automatically deleted. )} /> )} {form.watch("type") === "days" && ( ( Retention period (days) field.onChange(e.target.valueAsNumber)} /> Backups older than {field.value} days will be automatically deleted. )} /> )} {form.watch("type") === "gfs" && (
{["daily", "weekly", "monthly", "yearly"].map((key) => ( ( {key.charAt(0).toUpperCase() + key.slice(1)} backups field.onChange(e.target.valueAsNumber)} /> Keep N {key} backups )} /> ))}
)} {form.watch("type") && ( <>
Storage Impact Summary
{(() => { const totalFiles = calculateTotalFiles(form.getValues()); const estimate = getStorageEstimate(totalFiles); return ( {estimate} Usage ); })()}
Estimated files per database:

{calculateTotalFiles(form.getValues())} backup files

Policy type:

{form.watch("type") === "gfs" ? "GFS Rotation" : form.watch("type") === "count" ? "Count-based" : "Time-based"}

)}
); };