import { NavigationGroup, NavigationItem } from "./welcome-types.ts"; export type NavigationContext = "calendar" | "landlord" | "tenant" | "default"; function normalizePath(path: string): string { if (!path) return "/"; const [pathname] = path.split(/[?#]/); if (!pathname || pathname === "/") return "/"; return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname; } /** Placeholder replaced at runtime with server or client calendar path for hydration-safe nav. */ export const CALENDAR_PATH_PLACEHOLDER = "__SERVER_CALENDAR_PATH__"; function getCurrentFinancialCalendarPath(date = new Date()): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); return `/calendar/financial-calendar/${year}/${month}`; } export function resolveNavigationContext( currentRoute: string, ): NavigationContext { const route = normalizePath(currentRoute); if (route.startsWith("/calendar/")) return "calendar"; if (route.startsWith("/landlord/")) return "landlord"; if (route.startsWith("/tenant/")) return "tenant"; return "default"; } export function isNavigationItemActive( item: NavigationItem, currentRoute: string, ): boolean { if (item.isForm) return false; const route = normalizePath(currentRoute); const itemPath = normalizePath(item.path); if (route === itemPath) { return true; } if (item.matchPrefixes && item.matchPrefixes.length > 0) { return item.matchPrefixes.some((prefix) => { const normalizedPrefix = normalizePath(prefix); return route === normalizedPrefix || route.startsWith(`${normalizedPrefix}/`); }); } return false; } /** * Default navigation groups configuration. * Groups are filtered by current route context (calendar / landlord / tenant / default). */ export const defaultNavigationGroups: NavigationGroup[] = [ { id: "auth", label: "Session", items: [ { path: "/auth/login", label: "Sign In", requiresAuth: false, isForm: true, formAction: "/auth/login", formMethod: "get", }, { path: "/auth/logout", label: "Logout", requiresAuth: true, isForm: true, formAction: "/auth/logout", formMethod: "post", onClick: () => { localStorage.setItem("lastClickedButton", "logout"); }, }, ], }, { id: "apps", label: "App Switch", items: [ { path: CALENDAR_PATH_PLACEHOLDER, label: "Calendar", requiresAuth: true, matchPrefixes: ["/calendar/financial-calendar"], }, { path: "/landlord/dashboard", label: "Landlord", requiresAuth: true, }, { path: "/tenant/dashboard", label: "Tenant", requiresAuth: true, }, ], }, { id: "calendar", label: "Calendar", items: [ { path: CALENDAR_PATH_PLACEHOLDER, label: "Financial Calendar", requiresAuth: true, matchPrefixes: ["/calendar/financial-calendar"], }, { path: "/calendar/every-income", label: "Every Income", requiresAuth: true, }, { path: "/calendar/budget-obligations", label: "Budget Obligations", requiresAuth: true, }, ], }, { id: "landlord", label: "Landlord", items: [ { path: "/landlord/dashboard", label: "Dashboard", requiresAuth: true }, { path: "/landlord/properties", label: "Properties", requiresAuth: true }, { path: "/landlord/leases", label: "Leases", requiresAuth: true }, { path: "/landlord/payments", label: "Payments", requiresAuth: true }, { path: "/landlord/maintenance", label: "Maintenance", requiresAuth: true, }, { path: "/landlord/applications", label: "Applications", requiresAuth: true, }, { path: "/landlord/messages", label: "Messages", requiresAuth: true }, { path: "/landlord/reminders", label: "Reminders", requiresAuth: true }, { path: "/landlord/documents", label: "Documents", requiresAuth: true }, ], }, { id: "tenant", label: "Tenant", items: [ { path: "/tenant/dashboard", label: "Dashboard", requiresAuth: true }, { path: "/tenant/property", label: "Property", requiresAuth: true }, { path: "/tenant/payments", label: "Payments", requiresAuth: true }, { path: "/tenant/payment-methods", label: "Payment Methods", requiresAuth: true, }, { path: "/tenant/maintenance", label: "Maintenance", requiresAuth: true }, { path: "/tenant/messages", label: "Messages", requiresAuth: true }, { path: "/tenant/applications", label: "Applications", requiresAuth: true, }, { path: "/tenant/reminders", label: "Reminders", requiresAuth: true }, { path: "/tenant/documents", label: "Documents", requiresAuth: true }, { path: "/tenant/settings", label: "Settings", requiresAuth: true }, ], }, { id: "general", label: "General", items: [ { path: "/", label: "Home", requiresAuth: false, }, { path: "/user-profile", label: "Profile", requiresAuth: true, }, ], }, ]; const contextGroupOrder: Record = { default: ["auth", "apps", "general"], calendar: ["auth", "calendar", "apps", "general"], landlord: ["auth", "landlord", "apps", "general"], tenant: ["auth", "tenant", "apps", "general"], }; /** * Get navigation groups filtered by authentication status and current route context. * serverCalendarPath: use when provided so server and client render the same calendar links (avoids hydration mismatch). */ export function getNavigationGroups( isLoggedIn: boolean, currentRoute: string, customGroups?: NavigationGroup[], serverCalendarPath?: string, ): NavigationGroup[] { const calendarPath = serverCalendarPath ?? getCurrentFinancialCalendarPath(); const groups = customGroups || defaultNavigationGroups; const route = normalizePath(currentRoute); const context = resolveNavigationContext(route); const orderedGroupIds = contextGroupOrder[context]; const substituteCalendarPath = (items: NavigationItem[]): NavigationItem[] => items.map((item) => item.path === CALENDAR_PATH_PLACEHOLDER ? { ...item, path: calendarPath } : item ); return groups .filter((group) => { if (customGroups) return true; return orderedGroupIds.includes(group.id); }) .sort((a, b) => { if (customGroups) return 0; return orderedGroupIds.indexOf(a.id) - orderedGroupIds.indexOf(b.id); }) .map((group) => ({ ...group, items: substituteCalendarPath(group.items).filter((item) => { if (item.requiresAuth && !isLoggedIn) { return false; } if (group.id === "auth") { if (item.path === "/auth/login" && isLoggedIn) { return false; } if (item.path === "/auth/logout" && !isLoggedIn) { return false; } } if (isNavigationItemActive(item, route)) { return false; } return true; }), })) .filter((group) => group.items.length > 0); }