51 lines
2.7 KiB
TypeScript
51 lines
2.7 KiB
TypeScript
import type { BottomNavItem } from "@/components/navigation/bottom-nav";
|
|
|
|
/**
|
|
* Single source of truth for the merged staff/owner/super-admin bottom nav
|
|
* and desktop sidebar (see docs/implementation-plan.md and the Phase 11
|
|
* "merged navigation" brief). Owner is a permission on a staff account, not
|
|
* a separate app — Owner and SuperAdmin nav reuse the exact same personal
|
|
* routes (`/staff/current`, `/staff/clients`, `/staff/settings`) a barber
|
|
* uses, rather than duplicating them under `/owner`. This is what lets
|
|
* owners reach barber tools and business tools from one menu with zero new
|
|
* routes.
|
|
*
|
|
* A Super Administrator has no StaffProfile by construction (see
|
|
* isSuperAdmin/getActingBusinessContext) — they get no "Now" tab, since
|
|
* there is no personal schedule to show, per the brief's explicit "do not
|
|
* fabricate a personal barber schedule for the Super Administrator".
|
|
*/
|
|
export type NavRole = "staff" | "owner" | "superadmin";
|
|
|
|
const CALENDAR: BottomNavItem = { href: "/staff", label: "Calendar", icon: "calendar-days" };
|
|
const OWNER_CALENDAR: BottomNavItem = { href: "/owner/calendar", label: "Calendar", icon: "calendar-days" };
|
|
const NOW: BottomNavItem = { href: "/staff/current", label: "Now", icon: "scissors" };
|
|
const CLIENTS: BottomNavItem = { href: "/staff/clients", label: "Clients", icon: "users" };
|
|
const MANAGE: BottomNavItem = { href: "/owner", label: "Manage", icon: "briefcase" };
|
|
const SETTINGS: BottomNavItem = { href: "/staff/settings", label: "Settings", icon: "settings" };
|
|
|
|
const NAV_ITEMS_BY_ROLE: Record<NavRole, BottomNavItem[]> = {
|
|
staff: [CALENDAR, NOW, CLIENTS, SETTINGS],
|
|
owner: [OWNER_CALENDAR, NOW, CLIENTS, MANAGE, SETTINGS],
|
|
superadmin: [OWNER_CALENDAR, CLIENTS, MANAGE, SETTINGS],
|
|
};
|
|
|
|
export function getNavItems(role: NavRole): BottomNavItem[] {
|
|
return NAV_ITEMS_BY_ROLE[role];
|
|
}
|
|
|
|
/**
|
|
* Longest-matching-href wins, rather than "every item whose href is a
|
|
* pathname prefix" (the previous per-component logic) — that scheme let
|
|
* `/staff/current` mark both "Calendar" (href `/staff`) and "Now" active at
|
|
* once, since `/staff/current` also starts with `/staff`. Sorting by href
|
|
* length and taking the first match makes only the most specific item win,
|
|
* which is what makes nested routes (e.g. `/owner/settings/loyalty`,
|
|
* `/staff/clients/[id]`) activate exactly one, correct primary tab.
|
|
*/
|
|
export function resolveActiveHref(pathname: string, items: BottomNavItem[]): string | null {
|
|
const matches = items.filter((item) => pathname === item.href || pathname.startsWith(`${item.href}/`));
|
|
if (matches.length === 0) return null;
|
|
return matches.reduce((longest, item) => (item.href.length > longest.href.length ? item : longest)).href;
|
|
}
|