ai_api / src /app /(dashboard) /dashboard /settings /components /DatabaseBackupRetentionCard.tsx
Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
5.38 kB
"use client";
import type { Dispatch, SetStateAction } from "react";
import { Badge, Button } from "@/shared/components";
export type BackupCleanupOptions = {
keepLatest: number;
retentionDays: number;
};
export type BackupRetentionStatus = {
type: string;
message: string;
};
export type StorageBackupHealth = {
backupCount?: number;
backupRetention: {
maxFiles: number;
days: number;
};
};
type DatabaseBackupRetentionCardProps = {
title: string;
className?: string;
storageHealth: StorageBackupHealth;
backupCleanupOptions: BackupCleanupOptions;
setBackupCleanupOptions: Dispatch<SetStateAction<BackupCleanupOptions>>;
saveBackupRetentionLoading: boolean;
backupRetentionStatus: BackupRetentionStatus;
setBackupRetentionStatus: Dispatch<SetStateAction<BackupRetentionStatus>>;
cleanupBackupsLoading: boolean;
cleanupBackupsStatus: BackupRetentionStatus;
onSaveRetention: () => void;
onCleanupBackups: () => void;
};
function StatusAlert({ status }: { status: BackupRetentionStatus }) {
if (!status.message) return null;
const isSuccess = status.type === "success";
return (
<div
className={`mt-3 p-3 rounded-lg text-sm ${
isSuccess
? "bg-green-500/10 text-green-500 border border-green-500/20"
: "bg-red-500/10 text-red-500 border border-red-500/20"
}`}
role="alert"
>
<div className="flex items-center gap-2">
<span className="material-symbols-outlined text-[16px]" aria-hidden="true">
{isSuccess ? "check_circle" : "error"}
</span>
{status.message}
</div>
</div>
);
}
export default function DatabaseBackupRetentionCard({
title,
className = "mb-4",
storageHealth,
backupCleanupOptions,
setBackupCleanupOptions,
saveBackupRetentionLoading,
backupRetentionStatus,
setBackupRetentionStatus,
cleanupBackupsLoading,
cleanupBackupsStatus,
onSaveRetention,
onCleanupBackups,
}: DatabaseBackupRetentionCardProps) {
return (
<div className={`p-3 rounded-lg bg-bg border border-border ${className}`}>
<div className="flex items-start justify-between gap-3 flex-wrap mb-3">
<div>
<p className="text-sm font-medium text-text-main">{title}</p>
<p className="text-xs text-text-muted">
Automatic SQLite backups are stored in <code>db_backups</code>. Configure how many
snapshots to keep and optionally delete backups older than N days.
</p>
</div>
<div className="flex items-center gap-2">
<Badge variant="default" size="sm">
{storageHealth.backupCount || 0} backups
</Badge>
<Badge variant="default" size="sm">
Max {storageHealth.backupRetention.maxFiles}
</Badge>
<Badge variant="default" size="sm">
{storageHealth.backupRetention.days > 0
? `${storageHealth.backupRetention.days}d retention`
: "Age retention off"}
</Badge>
</div>
</div>
<div className="flex flex-wrap items-end gap-3">
<label className="flex flex-col gap-1 text-xs text-text-muted">
Keep latest backups
<input
type="number"
min={1}
max={200}
value={backupCleanupOptions.keepLatest}
onChange={(e) => {
const parsed = Number.parseInt(e.target.value || "1", 10);
setBackupRetentionStatus({ type: "", message: "" });
setBackupCleanupOptions((prev) => ({
...prev,
keepLatest: Number.isFinite(parsed) ? Math.max(1, parsed) : 1,
}));
}}
className="h-9 w-32 rounded-lg border border-border bg-background px-3 text-sm text-text-main"
/>
</label>
<label className="flex flex-col gap-1 text-xs text-text-muted">
Delete older than days
<input
type="number"
min={0}
max={3650}
value={backupCleanupOptions.retentionDays}
onChange={(e) => {
const parsed = Number.parseInt(e.target.value || "0", 10);
setBackupRetentionStatus({ type: "", message: "" });
setBackupCleanupOptions((prev) => ({
...prev,
retentionDays: Number.isFinite(parsed) ? Math.max(0, parsed) : 0,
}));
}}
className="h-9 w-32 rounded-lg border border-border bg-background px-3 text-sm text-text-main"
/>
</label>
<Button
variant="outline"
size="sm"
onClick={onSaveRetention}
loading={saveBackupRetentionLoading}
>
<span className="material-symbols-outlined text-[14px] mr-1" aria-hidden="true">
save
</span>
Save retention
</Button>
<Button
variant="outline"
size="sm"
onClick={onCleanupBackups}
loading={cleanupBackupsLoading}
>
<span className="material-symbols-outlined text-[14px] mr-1" aria-hidden="true">
auto_delete
</span>
Clean old backups
</Button>
</div>
<StatusAlert status={backupRetentionStatus} />
<StatusAlert status={cleanupBackupsStatus} />
</div>
);
}