78 خطوط
4.4 KiB
TypeScript
78 خطوط
4.4 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useMemo, useState } from "react";
|
||
import type { UserRole } from "@/config/homeAppsConfig";
|
||
import { rolesAccessExplorerItems } from "@/config/rolesAccessExplorerConfig";
|
||
import { rolesAccessService } from "@/services/api/rolesAccessService";
|
||
import type { AccessTab, CustomOrganizationRole, RoleAccessSectionId, RolePermissionMatrix } from "@/types/rolesAccess";
|
||
import { CompactExplorerChrome } from "@/components/shared/CompactExplorerChrome";
|
||
import { CustomOrganizationRolesSection } from "./sections/CustomOrganizationRolesSection";
|
||
import { PermissionsMatrixSection } from "./sections/PermissionsMatrixSection";
|
||
import { ExplorerIconTile } from "@/components/shared/GlassIcon";
|
||
|
||
function isAllowedRole(role: UserRole): role is "super_admin" | "org_admin" {
|
||
return role === "super_admin" || role === "org_admin";
|
||
}
|
||
|
||
export function RolesAccessExplorer({ role }: { role: UserRole }) {
|
||
const [query, setQuery] = useState("");
|
||
const [tabs, setTabs] = useState<AccessTab[]>([]);
|
||
const [activeTabId, setActiveTabId] = useState<string | null>(null);
|
||
const [matrix, setMatrix] = useState<RolePermissionMatrix[]>([]);
|
||
const [customRoles, setCustomRoles] = useState<CustomOrganizationRole[]>([]);
|
||
const [toast, setToast] = useState<{ message: string; tone: "success" | "error" } | null>(null);
|
||
|
||
useEffect(() => {
|
||
if (!isAllowedRole(role)) return;
|
||
setMatrix(rolesAccessService.listMatrix());
|
||
setCustomRoles(rolesAccessService.listCustomRoles());
|
||
}, [role]);
|
||
|
||
const availableItems = role === "org_admin" ? rolesAccessExplorerItems.filter((item) => item.id === "permissions-matrix") : rolesAccessExplorerItems;
|
||
const filteredItems = useMemo(() => availableItems.filter((item) => item.titleFa.includes(query) || item.descriptionFa.includes(query)), [availableItems, query]);
|
||
const activeTab = tabs.find((tab) => tab.id === activeTabId) || null;
|
||
const title = activeTab?.titleFa || "خانه";
|
||
|
||
if (!isAllowedRole(role)) return <div className="grid h-full place-items-center p-6"><section className="liquid-glass max-w-md rounded-[28px] p-6 text-center"><h2 className="text-xl font-black">دسترسی غیرمجاز</h2><p className="mt-3">شما به این بخش دسترسی ندارید.</p></section></div>;
|
||
const openSection = (sectionId: RoleAccessSectionId) => {
|
||
const item = availableItems.find((entry) => entry.id === sectionId);
|
||
if (!item) return;
|
||
const id = `tab-${sectionId}`;
|
||
setTabs((current) => current.some((tab) => tab.id === id) ? current : [...current, { id, titleFa: item.titleFa, sectionId, isClosable: true }]);
|
||
setActiveTabId(id);
|
||
};
|
||
const closeTab = (tabId: string) => {
|
||
const next = tabs.filter((tab) => tab.id !== tabId);
|
||
setTabs(next);
|
||
if (activeTabId === tabId) setActiveTabId(next.at(-1)?.id || null);
|
||
};
|
||
const showToast = (message: string, tone: "success" | "error" = "success") => {
|
||
setToast({ message, tone });
|
||
window.setTimeout(() => setToast(null), 2600);
|
||
};
|
||
return (
|
||
<div className="relative h-full min-h-0">
|
||
<CompactExplorerChrome
|
||
titleFa="نقشها و دسترسیها"
|
||
tabs={tabs.map((tab) => ({ id: tab.id, titleFa: tab.titleFa, isClosable: tab.isClosable }))}
|
||
activeTabId={activeTabId}
|
||
query={query}
|
||
searchPlaceholder="جستجو..."
|
||
onQuery={setQuery}
|
||
onHome={() => setActiveTabId(null)}
|
||
onSelectTab={setActiveTabId}
|
||
onCloseTab={closeTab}
|
||
>
|
||
{!activeTab ? (
|
||
<div className="grid grid-cols-[repeat(auto-fill,minmax(148px,1fr))] gap-5 p-5">{filteredItems.map((item) => <ExplorerIconTile key={item.id} titleFa={item.titleFa} descriptionFa={item.descriptionFa} Icon={item.icon} seed={item.id} onOpen={() => openSection(item.id)} />)}</div>
|
||
) : activeTab.sectionId === "permissions-matrix" ? (
|
||
<PermissionsMatrixSection role={role} matrix={matrix} onMatrixChange={setMatrix} onToast={showToast} />
|
||
) : (
|
||
<CustomOrganizationRolesSection role={role} customRoles={customRoles} onToast={showToast} onChange={(items) => { setCustomRoles(items); rolesAccessService.saveCustomRoles(items); }} />
|
||
)}
|
||
</CompactExplorerChrome>
|
||
{toast && <div className={`pointer-events-none absolute left-4 top-14 z-50 rounded-2xl border border-white/20 px-4 py-2 text-sm shadow-2xl backdrop-blur-xl ${toast.tone === "success" ? "bg-emerald-500/90 text-white" : "bg-rose-500/90 text-white"}`}>{toast.message}</div>}
|
||
</div>
|
||
);
|
||
}
|