"use client"; import { Box, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerHeader, DrawerOverlay, HStack, Icon, IconButton, Link, SimpleGrid, Stack, Text, Tooltip, VStack, useDisclosure, } from "@chakra-ui/react"; import { Children, forwardRef, isValidElement, useRef } from "react"; import { FiMoreHorizontal } from "react-icons/fi/index.js"; import { IoExit } from "react-icons/io5/index.js"; import { logoutKeycloak } from "../core/keycloak.jsx"; import { usePermission } from "../core/permission.jsx"; const normalizeSidebarPath = (value = "") => { const path = String(value).split(/[?#]/)[0] || ""; if (!path || path === "/") return path || "/"; return path.replace(/\/+$/, ""); }; export const isSidebarPathActive = (currentPath, item) => { const normalizedPath = normalizeSidebarPath(currentPath); const candidatePaths = [item?.path, ...(item?.aliases || [])] .filter(Boolean) .map(normalizeSidebarPath); return candidatePaths.some( (path) => normalizedPath === path || normalizedPath.startsWith(`${path}/`), ); }; export const getActiveSidebarItem = (items = [], currentPath) => items.find((item) => isSidebarPathActive(currentPath, item)); const SidebarLink = forwardRef( ({ linkComponent, href, children, ...props }, ref) => { const linkProps = linkComponent ? { as: linkComponent } : {}; return ( {children} ); }, ); SidebarLink.displayName = "SidebarLink"; const DesktopNavItem = ({ item, isActive, isDisabled, linkComponent, onNavigate, }) => { const commonProps = { variant: "ghost", position: "relative", bg: "transparent", _before: isActive ? { content: '""', position: "absolute", top: 0, bottom: 0, insetInlineStart: 0, insetInlineEnd: 0, bg: "gray.200", borderRadius: "md", zIndex: 0, } : undefined, sx: { "> svg": { position: "relative", zIndex: 1, }, }, icon: , fontSize: "xl", size: { base: "sm", lg: "md" }, "aria-label": item.title, "aria-current": isActive ? "page" : undefined, }; let content; if (isDisabled || isActive) { content = ( ); } else { const linkProps = linkComponent ? { as: linkComponent, href: item.path } : { as: "a", href: item.path }; content = ( ); } return ( {content} ); }; const MobileNavigationButton = ({ item, isActive, isDisabled, linkComponent, onNavigate, }) => { const content = ( {item.mobileTitle || item.title} ); if (isDisabled || isActive) { return ( {content} ); } return ( {content} ); }; const SidebarExit = () => null; const SidebarRoot = ({ items = [], currentPath, linkComponent, onNavigate, children, primaryMobileCount = 4, moreTitle = "بیشتر", moreDrawerTitle = "سایر بخش‌های سامانه", desktopProps, mobileProps, }) => { const { isOpen, onOpen, onClose } = useDisclosure(); const moreButtonRef = useRef(); const { cannot } = usePermission(); const exitElement = Children.toArray(children).find( (child) => isValidElement(child) && child.type === SidebarExit, ); const hasExit = Boolean(exitElement); const logoutTitle = exitElement?.props?.title || "خروج"; const logoutIcon = exitElement?.props?.icon || IoExit; const resolvedCurrentPath = currentPath ?? (typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : ""); const primaryItems = items.slice(0, primaryMobileCount); const secondaryItems = items.slice(primaryMobileCount); const disabled = (item) => { const permissions = Array.isArray(item?.perm) ? item.perm : [item?.perm]; return permissions.filter(Boolean).some((permission) => cannot(permission)); }; const active = (item) => isSidebarPathActive(resolvedCurrentPath, item); const isSecondaryItemActive = secondaryItems.some(active); const hasMoreContent = secondaryItems.length > 0 || hasExit; const handleNavigate = (item) => { onNavigate?.(item); onClose(); }; const handleLogout = () => { onClose(); logoutKeycloak(); }; return ( <> {items.map((item) => ( onNavigate?.(item)} /> ))} {hasExit && ( } fontSize="xl" size={{ base: "sm", lg: "lg" }} aria-label={logoutTitle} onClick={handleLogout} /> )} {primaryItems.map((item) => ( handleNavigate(item)} /> ))} {hasMoreContent && ( {moreTitle} )} {hasMoreContent && ( {moreDrawerTitle} {secondaryItems.map((item) => { const isDisabled = disabled(item); const isActive = active(item); const content = ( {item.mobileTitle || item.title} ); if (isDisabled || isActive) { return ( {content} ); } return ( handleNavigate(item)} aria-current={isActive ? "page" : undefined} > {content} ); })} {hasExit && ( {logoutTitle} )} )} ); }; export const Sidebar = Object.assign(SidebarRoot, { Exit: SidebarExit, });