import { Activity, FileText, Folder, Form, LayoutDashboard, MailCheck, Users, type LucideIcon } from "lucide-react"; import campaignModule from "@govoplan/campaign-webui"; import filesModule from "@govoplan/files-webui"; import mailModule from "@govoplan/mail-webui"; import type { AuthInfo, PlatformModuleInfo, PlatformNavItem, PlatformWebModule } from "../types"; import { adminReadScopes, hasAnyScope, hasScope } from "../utils/permissions"; export const shellNavItems: PlatformNavItem[] = [ { to: "/dashboard", label: "Dashboard", icon: LayoutDashboard, order: 10 } ]; const legacyModules: PlatformWebModule[] = [campaignModule, filesModule, mailModule]; const iconByName: Record = { activity: Activity, campaign: MailCheck, file: Folder, files: Folder, folder: Folder, form: Form, mail: MailCheck, reports: FileText, users: Users }; function navFromMetadata(item: PlatformModuleInfo["nav"][number]): PlatformNavItem { return { to: item.path, label: item.label, icon: item.icon ? iconByName[item.icon] : undefined, allOf: item.required_all, anyOf: item.required_any, order: item.order }; } function applyServerMetadata(module: PlatformWebModule, info: PlatformModuleInfo): PlatformWebModule { const backendNav = info.frontend?.nav.length ? info.frontend.nav : info.nav; return { ...module, label: info.name, version: info.version, dependencies: info.dependencies, optionalDependencies: info.optional_dependencies, navItems: backendNav.length ? backendNav.map(navFromMetadata) : module.navItems }; } export function resolveInstalledWebModules(platformModules: PlatformModuleInfo[] | null | undefined): PlatformWebModule[] { if (!platformModules?.length) return legacyModules; const localById = new Map(legacyModules.map((module) => [module.id, module])); return platformModules .filter((module) => module.enabled) .map((module) => { const local = localById.get(module.id); return local ? applyServerMetadata(local, module) : null; }) .filter((module): module is PlatformWebModule => module !== null); } export function moduleInstalled(moduleId: string, modules: PlatformWebModule[] = legacyModules): boolean { return modules.some((module) => module.id === moduleId); } export function moduleIntegrationEnabled(moduleId: string, dependencyId: string, modules: PlatformWebModule[] = legacyModules): boolean { const module = modules.find((item) => item.id === moduleId); if (!module) return false; return Boolean(module.dependencies?.includes(dependencyId) || (module.optionalDependencies?.includes(dependencyId) && moduleInstalled(dependencyId, modules))); } export function navItemsForModules(modules: PlatformWebModule[]): PlatformNavItem[] { return [...shellNavItems, ...modules.flatMap((module) => module.navItems ?? [])].sort((left, right) => (left.order ?? 100) - (right.order ?? 100)); } export function visibleNavItems(auth: AuthInfo | null | undefined, modules: PlatformWebModule[] = legacyModules): PlatformNavItem[] { return navItemsForModules(modules).filter((item) => { if (item.allOf?.length && !item.allOf.every((scope) => hasScope(auth, scope))) return false; if (item.anyOf?.length && !hasAnyScope(auth, item.anyOf)) return false; return true; }); } export function firstAccessibleRoute(auth: AuthInfo, modules: PlatformWebModule[] = legacyModules): string { const preferred = visibleNavItems(auth, modules).find((item) => item.to !== "/dashboard"); if (preferred) return preferred.to; if (hasAnyScope(auth, adminReadScopes)) return "/admin"; return "/dashboard"; }