import { Link, usePage } from "@inertiajs/react"; import { ChevronDownIcon, HomeIcon, MoreHorizontalIcon, SquareArrowOutUpRightIcon, TelescopeIcon, } from "lucide-react"; import { type ElementType, type ReactNode, useEffect, useRef } from "react"; import ApplicationLogo from "@/back/Components/ApplicationLogo"; import { SidebarRegistry } from "@/back/Components/Layout/Sidebar/SidebarRegistry"; import Views from "@/back/lib/actions"; import type { AuthProp, ConfigBackend, UserModel } from "@/back/types/global"; import { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, useSidebar, } from "@/components/ui/sidebar"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import str from "@/hooks/use-string"; import trans from "@/hooks/use-translations"; export interface NavListLinksProps { /** Title of the button. */ title: string; /** Main model of the button (used for route checking). */ model?: string; /** External link of the button (if it's a not CRUD, just a simple link). */ link?: string; /** Define some conditions for active or not the button (in addition to the route model checking if defined). */ activeCondition?: boolean[]; /** Condition to set the visibility of the button. */ show: boolean; /** Lucide icon of the button. */ icon: ElementType; /** Define some actions in side popover (like create/trash). */ actions?: (model: string) => ReactNode; } export type NavListProps = { /** Title of the group. */ title: string; /** Condition to set the visibility of the group. */ show: boolean; /** Links presents in the group. */ links: NavListLinksProps[]; }[]; /** * Get sidenav actions. * * @param ReactNode items * @param string model * @return */ export const filterActions = ({ auth, configBackend, items, model, }: { auth: AuthProp; configBackend: ConfigBackend; items?: ReactNode[]; model?: string; }): ReactNode => { let computedItems = items; const transDefaultSideAction = str( trans(`uwil-backend::crud.actions.${Views.CREATION.label}`), ) .ucFirst() .value(); // Set create action by default. if (!computedItems && model) { const modelCamelCase = str(model).camelCase().value(); computedItems = [ auth.policies[modelCamelCase].create && ( ), ]; } const visibleItems = computedItems ? computedItems.filter(Boolean) : null; return visibleItems && visibleItems.length > 0 ? visibleItems : null; }; export default function AppSidebar() { const { appEnv, auth, configBackend, enumUserRole } = usePage() .props as unknown as { appEnv: AppEnv; auth: AuthProp; configBackend: ConfigBackend; enumUserRole: UserModel["role"][]; }; const { state } = useSidebar(); const prevStateRef = useRef(state); const isConceptor = enumUserRole.find((role) => role.name === "conceptor"); const isCollapsed = state === "collapsed"; // Translations. const transPages = trans("uwil-backend::back.pages"); const transDashboard = trans("uwil-backend::back.dashboard"); const transWebsite = trans("uwil-backend::back.your_website"); const transModules = trans("uwil-backend::back.modules"); const transAdmin = trans("uwil-backend::enums.user-role.admin"); const transTelescope = trans("uwil-backend::back.telescope"); const navList: NavListProps = [ { title: transPages, show: true, links: [ { title: transDashboard, model: "dashboard", show: true, icon: HomeIcon, }, { title: transWebsite, link: "/", show: true, icon: SquareArrowOutUpRightIcon, }, ...SidebarRegistry.getAll(auth, configBackend, "pages"), ], }, { title: transModules, show: SidebarRegistry.getAll(auth, configBackend, "modules").length > 0, links: SidebarRegistry.getAll(auth, configBackend, "modules"), }, { title: str(transAdmin).plural().value(), show: auth.user.role.value === isConceptor?.value, links: [ ...SidebarRegistry.getAll(auth, configBackend, "admin"), { title: transTelescope, link: route().has("telescope") ? route("telescope") : undefined, icon: TelescopeIcon, show: appEnv.mode === "local" && route().has("telescope"), }, ], }, ]; useEffect(() => { if (prevStateRef.current !== state) { const elmts = document.querySelectorAll(".sidebar-transition"); const timeoutIds: NodeJS.Timeout[] = []; elmts.forEach((element) => { element.classList.add("animation-transition"); const id = setTimeout(() => { element.classList.remove("animation-transition"); }, 400); timeoutIds.push(id); }); prevStateRef.current = state; return () => { timeoutIds.forEach((id) => { clearTimeout(id); }); }; } }, [state]); return ( {navList.map( (navGroup) => navGroup.show && ( {!isCollapsed && ( {navGroup.title} )} {navGroup.links .filter((navLink) => navLink.show) .map((navLink) => { const isActive = navLink.model ? route().current( `${configBackend.routes.name}${navLink.model}.*`, ) : navLink.activeCondition?.some(Boolean); const sidebarButton = ( {navLink.link ? ( {navLink.title} ) : ( {navLink.title} )} ); return ( {isCollapsed ? ( {sidebarButton}

{navLink.title}

) : ( sidebarButton )} {navLink.model && navLink.actions?.(navLink.model) && ( {navLink.actions(navLink.model)} )}
); })}
), )}
); }