Settings Backup
Settings Backup is a React and Tailwind CSS component from HextaUI, licensed MIT. Copy it and paste it into your project.
HextaUI
MIT
other
What it is
Settings Backup is a React and Tailwind CSS component from HextaUI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
No packages beyond the project baseline
Licence
This component comes from HextaUI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Settings Backup
"use client";
import {
Calendar,
Clock,
HardDrive,
Loader2,
Play,
RotateCcw,
Save,
Trash2,
} from "lucide-react";
import { useState } from "react";
import { cn } from "@/lib/utils";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/registry/new-york/ui/alert-dialog";
import { Badge } from "@/registry/new-york/ui/badge";
import { Button } from "@/registry/new-york/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/new-york/ui/card";
import { FieldDescription, FieldLabel } from "@/registry/new-york/ui/field";
import {
InputGroup,
InputGroupInput,
} from "@/registry/new-york/ui/input-group";
import { Progress } from "@/registry/new-york/ui/progress";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york/ui/select";
import { Separator } from "@/registry/new-york/ui/separator";
import { Switch } from "@/registry/new-york/ui/switch";
export interface Backup {
id: string;
name: string;
type: "automatic" | "manual";
status: "completed" | "in_progress" | "failed";
size: number;
createdAt: Date;
completedAt?: Date;
location: string;
retentionDays?: number;
}
export interface SettingsBackupProps {
backups?: Backup[];
autoBackupEnabled?: boolean;
autoBackupSchedule?: "daily" | "weekly" | "monthly";
retentionDays?: number;
storageLocation?: string;
onCreateBackup?: () => Promise<Backup>;
onRestore?: (backupId: string) => Promise<void>;
onDelete?: (backupId: string) => Promise<void>;
onUpdateSettings?: (settings: {
enabled: boolean;
schedule?: "daily" | "weekly" | "monthly";
retentionDays?: number;
storageLocation?: string;
}) => Promise<void>;
className?: string;
}
function formatBytes(bytes: number): string {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Math.round((bytes / k ** i) * 100) / 100} ${sizes[i]}`;
}
function formatDate(date: Date): string {
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
}).format(date);
}
export default function SettingsBackup({
backups = [],
autoBackupEnabled = false,
autoBackupSchedule = "daily",
retentionDays = 30,
storageLocation = "cloud",
onCreateBackup,
onRestore,
onDelete,
onUpdateSettings,
className,
}: SettingsBackupProps) {
const [isCreating, setIsCreating] = useState(false);
const [localSettings, setLocalSettings] = useState({
enabled: autoBackupEnabled,
schedule: autoBackupSchedule,
retentionDays,
storageLocation,
});
const handleCreateBackup = async () => {
setIsCreating(true);
try {
await onCreateBackup?.();
} finally {
setIsCreating(false);
}
};
const handleUpdateSettings = async () => {
await onUpdateSettings?.(localSettings);
};
const getStatusBadge = (status: Backup["status"]) => {
switch (status) {
case "completed":
return (
<Badge className="text-xs" variant="default">
Completed
</Badge>
);
case "in_progress":
return (
<Badge className="text-xs" variant="secondary">
In Progress
</Badge>
);
case "failed":
return (
<Badge className="text-xs" variant="destructive">
Failed
</Badge>
);
}
};
return (
<Card className={cn("w-full shadow-xs", className)}>
<CardHeader>
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="flex min-w-0 flex-1 flex-col gap-2">
<CardTitle className="wrap-break-word">Backup & Restore</CardTitle>
<CardDescription className="wrap-break-word">
Manage automatic backups and restore your data
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
<div className="flex flex-col gap-6">
{/* Backup Settings */}
<div className="grid gap-4 md:grid-cols-2">
<div className="flex flex-col gap-4 rounded-lg border p-4">
<div className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-lg bg-muted">
<Calendar className="size-4" />
</div>
<FieldLabel className="mb-0">Automatic Backups</FieldLabel>
</div>
<div className="flex items-center justify-between">
<FieldDescription>Enable automatic backups</FieldDescription>
<Switch
checked={localSettings.enabled}
onCheckedChange={(checked) =>
setLocalSettings((prev) => ({ ...prev, enabled: checked }))
}
/>
</div>
</div>
{localSettings.enabled && (
<>
<div className="flex flex-col gap-4 rounded-lg border p-4">
<div className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-lg bg-muted">
<Clock className="size-4" />
</div>
<FieldLabel className="mb-0" htmlFor="backup-schedule">
Schedule
</FieldLabel>
</div>
<Select
onValueChange={(value: "daily" | "weekly" | "monthly") =>
setLocalSettings((prev) => ({ ...prev, schedule: value }))
}
value={localSettings.schedule}
>
<SelectTrigger id="backup-schedule">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="daily">Daily</SelectItem>
<SelectItem value="weekly">Weekly</SelectItem>
<SelectItem value="monthly">Monthly</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-4 rounded-lg border p-4">
<div className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-lg bg-muted">
<Calendar className="size-4" />
</div>
<FieldLabel className="mb-0" htmlFor="retention-days">
Retention (Days)
</FieldLabel>
</div>
<InputGroup>
<InputGroupInput
id="retention-days"
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
retentionDays: Number.parseInt(e.target.value) || 30,
}))
}
type="number"
value={localSettings.retentionDays}
/>
</InputGroup>
</div>
<div className="flex flex-col gap-4 rounded-lg border p-4">
<div className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-lg bg-muted">
<HardDrive className="size-4" />
</div>
<FieldLabel className="mb-0" htmlFor="storage-location">
Storage Location
</FieldLabel>
</div>
<Select
onValueChange={(value) =>
setLocalSettings((prev) => ({
...prev,
storageLocation: value,
}))
}
value={localSettings.storageLocation}
>
<SelectTrigger id="storage-location">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="cloud">Cloud Storage</SelectItem>
<SelectItem value="local">Local Storage</SelectItem>
<SelectItem value="s3">Amazon S3</SelectItem>
</SelectContent>
</Select>
</div>
</>
)}
</div>
<div className="flex justify-end">
<Button
className="w-full sm:w-auto"
onClick={handleUpdateSettings}
type="button"
>
<Save className="size-4" />
Save Settings
</Button>
</div>
<Separator />
{/* Manual Backup */}
<div className="flex flex-col gap-4 rounded-lg border p-4">
<div className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-lg bg-muted">
<Play className="size-4" />
</div>
<FieldLabel className="mb-0">Manual Backup</FieldLabel>
</div>
<Button
className="w-full sm:w-auto"
disabled={isCreating}
onClick={handleCreateBackup}
type="button"
>
{isCreating ? (
<>
<Loader2 className="size-4 animate-spin" />
Creating Backup…
</>
) : (
<>
<Save className="size-4" />
Create Backup Now
</>
)}
</Button>
</div>
<Separator />
{/* Backup History */}
<div className="flex flex-col gap-4">
<h3 className="font-semibold text-base">Backup History</h3>
{backups.length === 0 ? (
<p className="text-muted-foreground text-sm">No backups yet</p>
) : (
<div className="flex flex-col gap-3">
{backups.map((backup) => (
<div
className="flex flex-col gap-3 rounded-lg border p-4 sm:flex-row sm:items-center"
key={backup.id}
>
<div className="flex min-w-0 flex-1 items-center gap-3">
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted">
<HardDrive className="size-5" />
</div>
<div className="flex min-w-0 flex-1 flex-col gap-2">
<div className="flex flex-wrap items-center gap-2">
<span className="font-medium text-sm">
{backup.name}
</span>
{getStatusBadge(backup.status)}
<Badge className="text-xs" variant="outline">
{backup.type}
</Badge>
</div>
<div className="flex flex-wrap items-center gap-2 text-muted-foreground text-xs">
<span>{formatBytes(backup.size)}</span>
<span>•</span>
<span>{backup.location}</span>
<span>•</span>
<span>Created: {formatDate(backup.createdAt)}</span>
{backup.completedAt && (
<>
<span>•</span>
<span>
Completed: {formatDate(backup.completedAt)}
</span>
</>
)}
</div>
{backup.status === "in_progress" && (
<div className="flex flex-col gap-2">
<Progress value={50} />
</div>
)}
</div>
</div>
<div className="flex shrink-0 flex-wrap gap-2">
{backup.status === "completed" && (
<Button
className="w-full sm:w-auto"
onClick={() => onRestore?.(backup.id)}
type="button"
variant="outline"
>
<RotateCcw className="size-4" />
Restore
</Button>
)}
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
className="w-full sm:w-auto"
type="button"
variant="destructive"
>
<Trash2 className="size-4" />
Delete
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete Backup?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete the backup{" "}
<strong>{backup.name}</strong>. This action cannot
be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => onDelete?.(backup.id)}
>
Delete Backup
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
))}
</div>
)}
</div>
</div>
</CardContent>
</Card>
);
}