امنیتِ حساب
پیشنمایش تمامصفحهامنیت: امتیازِ امنیت با کارِ پیشنهادی، ورودِ دومرحلهای با سوییچ، رمز با تاریخِ آخرین تغییر و دستگاههای فعال با خروجِ تکی یا همه (با تأیید).
۱. نصب کامپوننتها
این بلاک از ۱۰ آیتم دیگ استفاده میکند: alert-dialogbadgebuttoncardcontrol-sizeiconslabelpersianswitchtoast
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toast.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toast.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toast.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toast.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
account-security.tsx
"use client"
import * as React from "react"
import { GlobeIcon, MobileIcon, ShieldCheckIcon } from "@/components/icons"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { toast } from "@/components/ui/toast"
import { formatRelativeTime } from "@/lib/persian"
type Session = { id: string; device: string; place: string; at: string; mobile: boolean; current?: boolean }
const SESSIONS: Session[] = [
{ id: "1", device: "کروم روی مک", place: "تهران", at: "2026-10-06T09:10:00", mobile: false, current: true },
{ id: "2", device: "اپِ آوند، آیفون", place: "تهران", at: "2026-10-05T21:40:00", mobile: true },
{ id: "3", device: "فایرفاکس روی ویندوز", place: "اصفهان", at: "2026-09-28T13:05:00", mobile: false },
]
/**
* امنیتِ حساب: نوارِ امتیازِ امنیت با کارِ بعدی پیشنهادی، ورودِ دومرحلهای با کلید و
* توضیح، رمز با تاریخِ آخرین تغییر، و دستگاههای فعال با خروجِ تکی یا همه (با تأیید).
*/
export default function AccountSecurityBlock() {
const [twoFactor, setTwoFactor] = React.useState(false)
const [sessions, setSessions] = React.useState(SESSIONS)
const score = twoFactor ? 100 : 60
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-3xl flex-col gap-8 p-4 sm:p-6">
<header className="flex flex-col gap-1">
<h1 className="text-2xl font-bold">امنیتِ حساب</h1>
<p className="text-muted-foreground">رمز، ورودِ دومرحلهای و دستگاههایی که با آنها وارد شدهای.</p>
</header>
<section aria-label="وضعیتِ امنیت" className={`flex items-center gap-4 rounded-xl p-5 ${twoFactor ? "bg-success-subtle" : "bg-[color-mix(in_oklab,var(--dig-orange-500)_12%,var(--background))]"}`}>
<span aria-hidden className={`flex size-12 shrink-0 items-center justify-center rounded-xl bg-card [&>svg]:size-6 ${twoFactor ? "text-success" : "text-[var(--dig-orange-600)]"}`}>
<ShieldCheckIcon />
</span>
<div className="flex flex-1 flex-col gap-0.5">
<span className="font-bold">{twoFactor ? "حسابت کاملاً امن است" : "یک قدم تا امنیتِ کامل"}</span>
<span className="text-sm text-muted-foreground">{twoFactor ? "ورودِ دومرحلهای روشن است." : "ورودِ دومرحلهای را روشن کن تا رمزِ لورفته هم کافی نباشد."}</span>
</div>
<span className="text-2xl font-black tabular-nums">{score === 100 ? "۱۰۰٪" : "۶۰٪"}</span>
</section>
<Card>
<CardContent className="flex flex-col divide-y divide-border">
<div className="flex items-start justify-between gap-4 pb-5">
<div className="flex flex-col gap-1">
<Label htmlFor="two-factor" className="font-semibold">
ورودِ دومرحلهای
</Label>
<span className="text-sm leading-6 text-muted-foreground">بعد از رمز، یک کدِ پیامکی هم لازم میشود.</span>
</div>
<Switch
id="two-factor"
checked={twoFactor}
onCheckedChange={(checked) => {
setTwoFactor(checked)
toast.success(checked ? "ورودِ دومرحلهای روشن شد." : "ورودِ دومرحلهای خاموش شد.")
}}
/>
</div>
<div className="flex items-center justify-between gap-4 pt-5">
<div className="flex flex-col gap-1">
<span className="font-semibold">رمز عبور</span>
<span className="text-sm text-muted-foreground">آخرین تغییر {formatRelativeTime("2026-06-12T10:00:00")}</span>
</div>
<Button variant="outline">تغییرِ رمز</Button>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base">دستگاههای فعال</CardTitle>
<CardDescription>دستگاهی را نمیشناسی؟ همین حالا از آن خارج شو.</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-6">
<ul className="flex flex-col divide-y divide-border">
{sessions.map((session) => (
<li key={session.id} className="flex items-center gap-4 py-4 first:pt-0 last:pb-0">
<span aria-hidden className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-subtle [&>svg]:size-5">
{session.mobile ? <MobileIcon /> : <GlobeIcon />}
</span>
<span className="flex flex-1 flex-col gap-0.5">
<span className="flex items-center gap-2 font-medium">
{session.device}
{session.current && (
<Badge variant="soft" color="success">
همین دستگاه
</Badge>
)}
</span>
<span className="text-sm text-muted-foreground">
{session.place} · {session.current ? "الان" : formatRelativeTime(session.at)}
</span>
</span>
{!session.current && (
<Button variant="ghost" color="destructive" onClick={() => setSessions((list) => list.filter((item) => item.id !== session.id))}>
خروج
</Button>
)}
</li>
))}
</ul>
{sessions.length > 1 && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" color="destructive" className="self-start">
خروج از همهٔ دستگاههای دیگر
</Button>
</AlertDialogTrigger>
<ControlSizeProvider size="xl" asChild>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>از {sessions.length - 1 === 1 ? "یک دستگاهِ دیگر" : "همهٔ دستگاههای دیگر"} خارج شوی؟</AlertDialogTitle>
<AlertDialogDescription>روی آن دستگاهها باید دوباره وارد شوی. همین دستگاه وصل میماند.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>انصراف</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
setSessions((list) => list.filter((item) => item.current))
toast.success("از دستگاههای دیگر خارج شدی.")
}}
>
خروج از همه
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</ControlSizeProvider>
</AlertDialog>
)}
</CardContent>
</Card>
</main>
</ControlSizeProvider>
)
}