Todos os componentes

Settings Account

Settings Account é um componente para React e Tailwind CSS, da biblioteca HextaUI, com licença MIT. Copia e cola no teu projeto.

HextaUI MIT other

O que é

Settings Account é um componente para React e Tailwind CSS, da biblioteca HextaUI, com licença MIT. Copia e cola no teu projeto.

Como usar

Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.

Abrir no catálogo

Cinco cópias grátis por mês. Sem cartão.

Não precisa de pacotes além do que o projeto já tem

Licença

Este componente vem de HextaUI e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.

Settings Account

registry/new-york/blocks/settings/settings-account.tsx
"use client";

import {
  AlertTriangle,
  ArrowUpRight,
  Crown,
  Loader2,
  Trash2,
  Users,
} 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 { Progress } from "@/registry/new-york/ui/progress";
import { Separator } from "@/registry/new-york/ui/separator";

export interface AccountInfo {
  type: "free" | "pro" | "enterprise";
  status: "active" | "trial" | "suspended" | "cancelled";
  trialEndsAt?: Date;
  memberCount?: number;
  memberLimit?: number;
  storageUsed?: number;
  storageLimit?: number;
}

export interface SettingsAccountProps {
  account?: AccountInfo;
  onUpgrade?: () => Promise<void>;
  onDelete?: () => Promise<void>;
  onTransfer?: () => 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",
  }).format(date);
}

export default function SettingsAccount({
  account,
  onUpgrade,
  onDelete,
  onTransfer,
  className,
}: SettingsAccountProps) {
  const [isDeleting, setIsDeleting] = useState(false);

  if (!account) {
    return null;
  }

  const getStatusBadge = (status: AccountInfo["status"]) => {
    switch (status) {
      case "active":
        return (
          <Badge className="text-xs" variant="default">
            Active
          </Badge>
        );
      case "trial":
        return (
          <Badge className="text-xs" variant="secondary">
            Trial
          </Badge>
        );
      case "suspended":
        return (
          <Badge className="text-xs" variant="destructive">
            Suspended
          </Badge>
        );
      case "cancelled":
        return (
          <Badge className="text-xs" variant="outline">
            Cancelled
          </Badge>
        );
    }
  };

  const getPlanBadge = (type: AccountInfo["type"]) => {
    switch (type) {
      case "free":
        return (
          <Badge className="text-xs" variant="outline">
            Free
          </Badge>
        );
      case "pro":
        return (
          <Badge className="text-xs" variant="default">
            Pro
          </Badge>
        );
      case "enterprise":
        return (
          <Badge className="flex items-center gap-1 text-xs" variant="default">
            <Crown className="size-3" />
            <span>Enterprise</span>
          </Badge>
        );
    }
  };

  const memberPercentage =
    account.memberLimit && account.memberCount
      ? (account.memberCount / account.memberLimit) * 100
      : 0;
  const storagePercentage =
    account.storageLimit && account.storageUsed
      ? (account.storageUsed / account.storageLimit) * 100
      : 0;

  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">Account</CardTitle>
            <CardDescription className="wrap-break-word">
              Manage your account settings and subscription
            </CardDescription>
          </div>
        </div>
      </CardHeader>
      <CardContent>
        <div className="flex flex-col gap-6">
          {/* Account Overview */}
          <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">
                  <Crown className="size-4" />
                </div>
                <FieldLabel className="mb-0">Plan</FieldLabel>
              </div>
              <div className="flex flex-col gap-2">
                <div className="flex items-center gap-2">
                  {getPlanBadge(account.type)}
                  {getStatusBadge(account.status)}
                </div>
                {account.status === "trial" && account.trialEndsAt && (
                  <p className="text-muted-foreground text-xs">
                    Trial ends on {formatDate(account.trialEndsAt)}
                  </p>
                )}
                {account.type !== "enterprise" && (
                  <Button
                    className="w-full sm:w-auto"
                    onClick={onUpgrade}
                    type="button"
                    variant="outline"
                  >
                    <ArrowUpRight className="size-4" />
                    Upgrade Plan
                  </Button>
                )}
              </div>
            </div>

            {account.memberLimit !== undefined && (
              <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">
                    <Users className="size-4" />
                  </div>
                  <FieldLabel className="mb-0">Team Members</FieldLabel>
                </div>
                <div className="flex flex-col gap-2">
                  <div className="flex items-center justify-between">
                    <span className="text-muted-foreground text-sm">
                      {account.memberCount || 0} / {account.memberLimit}
                    </span>
                    <span className="font-medium text-sm">
                      {memberPercentage.toFixed(0)}%
                    </span>
                  </div>
                  <Progress value={memberPercentage} />
                </div>
              </div>
            )}
          </div>

          {account.storageLimit !== undefined && (
            <>
              <Separator />
              <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">
                    <Crown className="size-4" />
                  </div>
                  <FieldLabel className="mb-0">Storage</FieldLabel>
                </div>
                <div className="flex flex-col gap-2">
                  <div className="flex items-center justify-between">
                    <span className="text-muted-foreground text-sm">
                      {formatBytes(account.storageUsed || 0)} /{" "}
                      {formatBytes(account.storageLimit)}
                    </span>
                    <span className="font-medium text-sm">
                      {storagePercentage.toFixed(1)}%
                    </span>
                  </div>
                  <Progress value={storagePercentage} />
                </div>
              </div>
            </>
          )}

          <Separator />

          {/* Account Actions */}
          <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">
                  <Users className="size-4" />
                </div>
                <FieldLabel className="mb-0">Transfer Account</FieldLabel>
              </div>
              <FieldDescription>
                Transfer ownership of this account to another user
              </FieldDescription>
              <Button
                className="w-full sm:w-auto"
                onClick={onTransfer}
                type="button"
                variant="outline"
              >
                Transfer Ownership
              </Button>
            </div>

            <div className="flex flex-col gap-4 rounded-lg border border-destructive/50 p-4">
              <div className="flex items-center gap-2">
                <div className="flex size-8 items-center justify-center rounded-lg bg-destructive/10">
                  <AlertTriangle className="size-4 text-destructive" />
                </div>
                <FieldLabel className="mb-0 text-destructive">
                  Danger Zone
                </FieldLabel>
              </div>
              <FieldDescription>
                Permanently delete your account and all associated data
              </FieldDescription>
              <AlertDialog>
                <AlertDialogTrigger asChild>
                  <Button
                    className="w-full sm:w-auto"
                    disabled={isDeleting}
                    type="button"
                    variant="destructive"
                  >
                    {isDeleting ? (
                      <>
                        <Loader2 className="size-4 animate-spin" />
                        Deleting…
                      </>
                    ) : (
                      <>
                        <Trash2 className="size-4" />
                        Delete Account
                      </>
                    )}
                  </Button>
                </AlertDialogTrigger>
                <AlertDialogContent>
                  <AlertDialogHeader>
                    <AlertDialogTitle>Delete Account?</AlertDialogTitle>
                    <AlertDialogDescription>
                      This will permanently delete your account and all
                      associated data. This action cannot be undone. Please type{" "}
                      <strong>DELETE</strong> to confirm.
                    </AlertDialogDescription>
                  </AlertDialogHeader>
                  <AlertDialogFooter>
                    <AlertDialogCancel>Cancel</AlertDialogCancel>
                    <AlertDialogAction
                      className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
                      onClick={async () => {
                        setIsDeleting(true);
                        try {
                          await onDelete?.();
                        } finally {
                          setIsDeleting(false);
                        }
                      }}
                    >
                      Delete Account
                    </AlertDialogAction>
                  </AlertDialogFooter>
                </AlertDialogContent>
              </AlertDialog>
            </div>
          </div>
        </div>
      </CardContent>
    </Card>
  );
}

Componentes parecidos