"use client";
import { useState, useEffect } from "react";
import { useTranslations } from "next-intl";
import { signOut, useSession } from "next-auth/react";
import { usePathname } from "@/i18n/navigation";
import { Link } from "@/i18n/navigation";
import { SessionProvider } from "next-auth/react";
import type { Session } from "next-auth";
import { LanguageSwitcher } from "@/components/ui/language-switcher";
import { Logo } from "@/components/ui/logo";
function NavBar() {
const t = useTranslations("common");
const { data: session } = useSession();
const pathname = usePathname();
const user = (session as any)?.platformUser;
const [mobileOpen, setMobileOpen] = useState(false);
// Close the mobile menu on any navigation. Without this the panel
// would stay open across route changes (the component doesn't
// unmount — it lives in the layout).
useEffect(() => {
setMobileOpen(false);
}, [pathname]);
// Hide the nav entirely on auth-only routes. These pages have no
// session yet — showing "Dashboard" / "Sign Out" is misleading at
// best (the buttons would 401 or redirect-loop). Keep this list
// narrow and route-exact: anything else we add to the auth flow
// (e.g. password reset) needs to be added here too.
const isAuthRoute = pathname === "/login" || pathname === "/register";
if (isAuthRoute) return null;
// ------------------------------------------------------------------
// Visibility gates — computed once, shared by the desktop nav and the
// mobile panel so the two can never diverge.
//
// - team: owner+platform only AND not a personal account (Bug 8 —
// personal accounts have no team). Matches `canMutate` /
// `user.isPersonal === false` server-side.
// - settings: anyone who can mutate org-level state (owners + platform).
// `user`-role customers don't see it (canMutate is false).
// - billing / support: any signed-in user (org-scoped server-side).
// - admin: platform only.
// ------------------------------------------------------------------
const isOwner =
user && Array.isArray(user.roles) && user.roles.includes("owner");
const showTeam = !!user && !user.isPersonal && (user.isPlatform || isOwner);
const showSettings = !!user && (user.isPlatform || isOwner);
const showBilling = !!user;
const showSupport = !!user;
const showAdmin = !!user?.isPlatform;
// Active-state helper. Dashboard/Admin previously used exact `===`,
// so sub-routes (/dashboard/new, /admin/billing, …) showed no active
// item. startsWith keeps the parent lit on its children too.
const isActive = (href: string) =>
pathname === href || pathname.startsWith(`${href}/`);
const links = [
{ href: "/dashboard", label: t("dashboard"), show: !!user },
{ href: "/team", label: t("team"), show: showTeam },
{ href: "/settings", label: t("settings"), show: showSettings },
{ href: "/billing", label: t("billing"), show: showBilling },
{ href: "/support", label: t("support"), show: showSupport },
{ href: "/admin", label: t("admin"), show: showAdmin },
].filter((l) => l.show);
const displayName = user
? user.isPersonal
? user.name || (user.email ? user.email.split("@")[0] : user.orgName)
: user.orgName
: "";
return (
{/* Logo / brand */}
{/* Brand mark */}
{t("appName")}
{t("tagline")}
{/* Desktop nav links */}
{/* Right side */}
{user && (
{displayName}
)}
{/* Mobile menu toggle — only shown below the `sm` breakpoint,
where the desktop nav and logout button are hidden. */}
{user && (
)}
{/* Mobile panel */}
{user && mobileOpen && (
)}
);
}
function NavLink({
href,
active,
children,
}: {
href: string;
active: boolean;
children: React.ReactNode;
}) {
return (
{children}
);
}
export function NavShell({
children,
session,
}: {
children: React.ReactNode;
// Server-resolved session passed down from the locale layout. Seeding
// SessionProvider with it means useSession() is populated on the first
// client render, so the nav links render immediately instead of
// popping in after the client-side session fetch (CLS / flash).
session: Session | null;
}) {
return (
{children}
);
}