// ───────────────────────────────────────────────────────────────────────────── // Mock data — Navigation // Uses .tsx because icons are JSX ( elements). // ───────────────────────────────────────────────────────────────────────────── import type * as React from "react" import { logoDevUrl } from "@/lib/logo-dev" import { stockPortraitUrl } from "@/lib/stock-portrait" import { LIBRARY_ENTRY_PATH, LIBRARY_HUB_FIND_PATH, LIBRARY_ALL_PATH, } from "@/lib/library-nav" import { productSlug, type Product } from "@/stores/app-store" import { customProductSlugFromSuffix } from "@/lib/product-routing" import { primaryNavLinksForSlug } from "@exxatdesignux/product-framework" // ── Types ───────────────────────────────────────────────────────────────────── /** * Drill-in slot for sidebar rows that lead into deep, focused sections * (e.g. Settings → Profile / Organization / Billing). When a row carries * `drillIn` and the URL matches `sectionRouteRoot`, the primary sidebar * content area swaps to a stacked view rendered by `SidebarDrillIn` — * `[← Back] · ` followed by the section's nav list. * * Mutually exclusive with `secondaryPanel` — a row opts into EITHER the * drill-in pattern (deep section, user "going into" it) OR the secondary- * panel pattern (hub-scoped catalog, user "scoping" it). See * `apps/web/components/sidebar/sidebar-drill-in.tsx` and the * SidebarDrillIn primitive doc for the decision criteria. */ export interface NavDrillInConfig { /** Heading rendered above the drilled-in nav list. */ sectionTitle: string /** Pathname prefix that activates the drilled-in view (e.g. `"/settings"`). */ sectionRouteRoot: string /** * Custom matcher for routes whose product slug is part of the URL — * e.g. Leo lives at `//leo`, which `sectionRouteRoot` alone * can't express. When set, OVERRIDES the default * `pathname.startsWith(sectionRouteRoot)` check. */ sectionRouteMatch?: (pathname: string) => boolean /** * Nav rows shown inside the drilled-in pane. Ignored when the consumer * renders custom drill-in content (see Leo — `app-sidebar.tsx` switches * on the row key to render `` instead). */ items: NavLinkItem[] } /** Labelled primary-nav group (Prism Program / Curriculum / Placement blocks). */ export interface NavSection { key: string /** Section heading — omit or leave empty to render items without a label row. */ label: string items: NavLinkItem[] } /** Primary nav layout — preamble rows, labelled sections, optional trailing rows. */ export interface NavPrimaryLayout { preamble: NavLinkItem[] sections: NavSection[] /** Primary rows after all sections — not grouped under a section label. */ epilogue?: NavLinkItem[] } /** Flat sidebar link — primary + Documents + utilities groups (DS shell; not legacy screenshot styling) */ export interface NavLinkItem { key: string title: string url: string icon: React.ReactNode iconActive?: React.ReactNode /** Optional child links — renders as a collapsible sub-menu */ children?: NavLinkItem[] /** Optional inline badge — count (number) or label text (string). * Special string values get distinct colors: * "New" → green, "Beta" → amber, other strings → brand/primary */ badge?: number | string /** * When this collapsible section uses `SecondaryPanel`, set the panel id so * child links can reopen the panel on click while already on the same route * (react-router-dom `Link` does not navigate on same href). */ secondaryPanel?: string /** * When set, clicking this row leads INTO a deep section that swaps the * sidebar content area to a `SidebarDrillIn` stack. The row's own URL * MUST land inside `drillIn.sectionRouteRoot` so the drilled-in pane * shows on first navigation. Mutually exclusive with `secondaryPanel`. */ drillIn?: NavDrillInConfig /** * When several children share the same `url` (hub route), only this child’s * key receives `data-active` for that pathname — otherwise every sub-row * looks selected (same issue as duplicate `href` in any nav). */ primaryHubChildKey?: string } // ── Team / scope switcher mock data ────────────────────────────────────────── // // The sidebar header picker (TeamSwitcher) is **product-aware** per the // product-context rule (`.cursor/rules/exxat-product-context.mdc`): // // • Prism / One — Schools / Custom → school > program // • One — Sites → site > location // // Two parallel mock-data shapes live here — one for each scope hierarchy — // so the switcher can render either flavor without forking the chrome. // School + Program (school-side scope) ──────────────────────────────────────── export interface NavProgram { id: string; name: string } export interface NavSchool { id: string name: string /** URL of school logo — used as the avatar image */ logo: string /** Two-letter abbreviation shown as AvatarFallback */ initials: string programs: NavProgram[] } export const NAV_SCHOOLS: NavSchool[] = [ { id: "jhu", name: "Johns Hopkins University", logo: logoDevUrl("jhu.edu"), initials: "JH", programs: [ { id: "som", name: "School of Medicine" }, { id: "son", name: "School of Nursing" }, { id: "sph", name: "Bloomberg School of Public Health" }, ], }, { id: "mayo", name: "Mayo Clinic Alix School of Medicine", logo: logoDevUrl("mayoclinic.org"), initials: "MC", programs: [ { id: "md", name: "Doctor of Medicine" }, { id: "bms", name: "Biomedical Sciences" }, ], }, ] export const NAV_SCHOOL_DEFAULT = NAV_SCHOOLS[0] export const NAV_PROGRAM_DEFAULT = NAV_SCHOOLS[0].programs[0] // Site + Location (partner-side scope, Exxat One — Sites) ───────────────────── export interface NavLocation { id: string; name: string } export interface NavSite { id: string name: string /** URL of site logo — used as the avatar image */ logo: string /** Two-letter abbreviation shown as AvatarFallback */ initials: string locations: NavLocation[] } export const NAV_SITES: NavSite[] = [ { id: "mgb", name: "Mass General Brigham", logo: logoDevUrl("massgeneralbrigham.org"), initials: "MG", locations: [ { id: "mgh-main", name: "Mass General — Boston" }, { id: "bwh", name: "Brigham and Women's — Boston" }, { id: "mgh-salem", name: "Salem Hospital" }, ], }, { id: "ccf", name: "Cleveland Clinic", logo: logoDevUrl("clevelandclinic.org"), initials: "CC", locations: [ { id: "ccf-main", name: "Main Campus — Cleveland" }, { id: "ccf-fairview", name: "Fairview Hospital" }, ], }, ] export const NAV_SITE_DEFAULT = NAV_SITES[0] export const NAV_LOCATION_DEFAULT = NAV_SITES[0].locations[0] // ── Primary navigation ──────────────────────────────────────────────────────── // // The primary nav is **product-keyed** (`NAV_BY_PRODUCT[productId]`) per Rule // 5 of the multi-product routing pattern // (`apps/web/docs/multi-product-routing-pattern.md`). `AppSidebar` reads // `useProduct().product` and renders the matching tree. Switching products // in the sidebar swaps the entire primary nav. // // Product-owned hubs live under the active product root. Shell-global routes // like Help remain at the workspace root. const dashboardItem = (slug: string): NavLinkItem => ({ key: "dashboard", title: "Dashboard", url: `/${slug}/dashboard`, icon: