بلاک‌ها · پنل مدیریت

نقش‌ها و دسترسی‌ها

پیش‌نمایش تمام‌صفحه

ماتریس نقش و دسترسی (سطر نقش، ستون دسترسی) با چک‌باکس، بازنشانی و ذخیره.

۱. نصب کامپوننت‌ها

این بلاک از ۵ آیتم دیگ استفاده می‌کند: buttoncheckboxcontrol-sizetabletoast

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.json

۲. کد صفحه

کد را در app/<route>/page.tsx بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

roles-permissions.tsx
"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Checkbox } from "@/components/ui/checkbox"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { toast } from "@/components/ui/toast"

const ROLES = [
  { id: "admin", title: "مدیر" },
  { id: "editor", title: "ویرایشگر" },
  { id: "support", title: "پشتیبان" },
  { id: "viewer", title: "بیننده" },
]
const PERMISSIONS = [
  { id: "orders.view", title: "مشاهدهٔ سفارش‌ها" },
  { id: "orders.edit", title: "ویرایش سفارش‌ها" },
  { id: "refunds", title: "بازگرداندن وجه" },
  { id: "users.manage", title: "مدیریت کاربران" },
  { id: "reports.export", title: "خروجی گزارش‌ها" },
]
const INITIAL: Record<string, string[]> = {
  admin: PERMISSIONS.map((permission) => permission.id),
  editor: ["orders.view", "orders.edit", "reports.export"],
  support: ["orders.view", "refunds"],
  viewer: ["orders.view"],
}

export default function RolesPermissionsBlock() {
  const [grants, setGrants] = React.useState(INITIAL)
  const [saving, setSaving] = React.useState(false)
  const [dirty, setDirty] = React.useState(false)

  function toggle(role: string, permission: string, on: boolean) {
    setDirty(true)
    setGrants((current) => ({
      ...current,
      [role]: on ? [...current[role], permission] : current[role].filter((id) => id !== permission),
    }))
  }

  async function save() {
    setSaving(true)
    await new Promise((resolve) => setTimeout(resolve, 700))
    setSaving(false)
    setDirty(false)
    toast.success("دسترسی‌ها ذخیره شد.")
  }

  return (
    <ControlSizeProvider size="xl" asChild>
    <main className="mx-auto flex w-full min-w-0 max-w-4xl flex-col gap-6 p-4 sm:p-6">
      {/* سلسله‌مراتب: عنوان و توضیح یک گروهِ نزدیک، اکشن‌ها هم‌ردیفِ عنوان. */}
      <header className="flex flex-wrap items-end justify-between gap-x-4 gap-y-8">
        <div className="flex flex-col gap-1">
          <h1 className="text-2xl font-bold">نقش‌ها و دسترسی‌ها</h1>
          <p className="text-sm text-muted-foreground">
            دسترسی هر نقش را تیک بزنید. نقش مدیر همهٔ دسترسی‌ها را دارد و قابل تغییر نیست.
          </p>
        </div>
        <div className="flex gap-2">
          <Button
            variant="outline"
            disabled={!dirty}
            onClick={() => {
              setGrants(INITIAL)
              setDirty(false)
            }}
          >
            بازنشانی
          </Button>
          <Button onClick={save} loading={saving} disabled={!dirty}>
            ذخیرهٔ تغییرات
          </Button>
        </div>
      </header>

      {/* هر سطر یک نقش، هر ستون یک دسترسی؛ چک‌باکس‌ها وسطِ ستون. */}
      <div className="mt-2">
        <Table stackOnMobile>
          <TableHeader>
            <TableRow>
              <TableHead className="min-w-32">نقش</TableHead>
              {PERMISSIONS.map((permission) => (
                <TableHead key={permission.id} className="text-center whitespace-nowrap">
                  {permission.title}
                </TableHead>
              ))}
            </TableRow>
          </TableHeader>
          <TableBody>
            {ROLES.map((role) => (
              <TableRow key={role.id}>
                <TableCell className="font-medium">{role.title}</TableCell>
                {PERMISSIONS.map((permission) => (
                  <TableCell key={permission.id}>
                    <div className="flex justify-center">
                      <Checkbox
                        aria-label={`${permission.title} برای ${role.title}`}
                        checked={grants[role.id].includes(permission.id)}
                        disabled={role.id === "admin"}
                        onCheckedChange={(checked) => toggle(role.id, permission.id, checked === true)}
                      />
                    </div>
                  </TableCell>
                ))}
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </div>
    </main>
    </ControlSizeProvider>
  )
}