All components

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.

Open in the catalogue

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

registry/new-york/blocks/settings/settings-backup.tsx
"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>
  );
}

Similar components