import type { IModuleClient, ModuleClientParams, ModuleRouteLayout, ModuleRouteObject, } from "@erp/core/client"; import type { ReactNode } from "react"; import type { RouteObject } from "react-router-dom"; import { RequireAuthRouteGuard } from "@/routes/require-auth-guard"; interface BuildModuleRoutesParams { modules: IModuleClient[]; params: ModuleClientParams; layout: ModuleRouteLayout; } interface NormalizeRouteParams { route: ModuleRouteObject; module: IModuleClient; targetLayout: ModuleRouteLayout; inheritedLayout?: ModuleRouteLayout; inheritedProtected?: boolean; } const DEFAULT_LAYOUT: ModuleRouteLayout = "app-sidebar"; const resolveRouteLayout = ( route: ModuleRouteObject, module: IModuleClient, inheritedLayout?: ModuleRouteLayout ): ModuleRouteLayout => { return route.handle?.layout ?? inheritedLayout ?? module.layout ?? DEFAULT_LAYOUT; }; const resolveRouteProtected = ( route: ModuleRouteObject, module: IModuleClient, inheritedProtected?: boolean ): boolean => { return route.handle?.protected ?? inheritedProtected ?? module.protected ?? false; }; const wrapProtectedElement = (element: ReactNode, isProtected: boolean): ReactNode => { if (!isProtected) { return element; } return {element}; }; const normalizeRoute = ({ route, module, targetLayout, inheritedLayout, inheritedProtected, }: NormalizeRouteParams): RouteObject | null => { const routeLayout = resolveRouteLayout(route, module, inheritedLayout); const routeProtected = resolveRouteProtected(route, module, inheritedProtected); const normalizedChildren = route.children ?.map((child) => normalizeRoute({ route: child, module, targetLayout, inheritedLayout: routeLayout, inheritedProtected: routeProtected, }) ) .filter((child): child is RouteObject => child !== null); const hasMatchingLayout = routeLayout === targetLayout; const hasChildren = Boolean(normalizedChildren?.length); const hasElement = route.element !== undefined || route.Component !== undefined; if (!(hasMatchingLayout || hasChildren)) { return null; } if (!hasMatchingLayout && hasChildren) { return { path: route.path, index: route.index, children: normalizedChildren, } as RouteObject; } const { children: _children, handle, element, ...rest } = route; const normalizedRoute = { ...rest, handle, element: element ? wrapProtectedElement(element, routeProtected) : undefined, children: normalizedChildren, } as RouteObject; if (!(hasElement || hasChildren)) { console.warn( `[ModuleRoutes] Ruta sin element ni children válidos: "${String(route.path ?? "index")}"` ); return null; } return normalizedRoute; }; export const buildModuleRoutes = ({ modules, params, layout, }: BuildModuleRoutesParams): RouteObject[] => { const routes: RouteObject[] = []; for (const module of modules) { if (typeof module.routes !== "function") { console.warn(`[ModuleRoutes] El módulo "${module.name}" no define una función routes().`); continue; } const moduleRoutes = module.routes(params); if (!Array.isArray(moduleRoutes)) { console.error( `[ModuleRoutes] El módulo "${module.name}" debe devolver un ModuleRouteObject[], pero devolvió:`, moduleRoutes ); continue; } for (const route of moduleRoutes) { const normalizedRoute = normalizeRoute({ route, module, targetLayout: layout, }); if (normalizedRoute) { routes.push(normalizedRoute); } } } return routes; };