"use client";
import { useEffect, useState, useTransition } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { MenuIcon, LogOutIcon, Loader2Icon } from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { logoutAction } from "@/actions/auth";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import {
NAV_ITEMS,
navItemsForRole,
pickActiveNavKey,
type NavItem,
type NavRole,
} from "@/components/nav-config";
// ---------------------------------------------------------------------------
// Mobile header (sm:hidden)
//
// Single-row layout:
// ┌──┐ ┌────┐
// │cm│ Page title │menu│
// └──┘ └────┘
//
// The brand mark on the left links home. The page title (derived from
// the current nav route) gives the user a "you are here" cue without
// waiting for the page content to render. The menu button on the right
// opens a Sheet with the full nav list and the theme toggle.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Sign-out button used by both the desktop sidebar footer and the mobile
// drawer footer. Server-action under the hood: clears the session
// cookie and redirects to /login. Disabled while in flight so a
// double-click doesn't fire two redirects.
// ---------------------------------------------------------------------------
function SignOutButton({ username }: { username: string | null }) {
const [pending, start] = useTransition();
return (
{username && (
Signed in as {username}
)}
);
}
function MobileHeader({
items,
username,
}: {
items: NavItem[];
username: string | null;
}) {
const pathname = usePathname();
const activeKey = pickActiveNavKey(items, pathname);
const [open, setOpen] = useState(false);
// Close the drawer when the route changes (i.e. the user picked a nav
// item). Without this, navigating leaves the sheet open over the new
// page until the user dismisses it manually.
useEffect(() => {
setOpen(false);
}, [pathname]);
// Use the full list (not the role-filtered one) for the title lookup
// so the page title still shows up correctly when a 'user' role hits
// a route they wouldn't normally see in the nav (e.g. arrives via a
// direct link), even though they can't navigate there from the menu.
const currentItem = NAV_ITEMS.find(({ href }) =>
href === "/" ? pathname === "/" : pathname.startsWith(href),
);
const title = currentItem?.label ?? "WhatsApp Bot";
return (
cm
{title}
cm
WhatsApp Bot
Primary navigation menu
);
}
// ---------------------------------------------------------------------------
// Sidebar (desktop only — hidden below sm)
// ---------------------------------------------------------------------------
function Sidebar({
items,
username,
}: {
items: NavItem[];
username: string | null;
}) {
const pathname = usePathname();
const activeKey = pickActiveNavKey(items, pathname);
return (
);
}
// ---------------------------------------------------------------------------
// Bare header for unauthenticated routes (/login). No sidebar, no mobile
// menu, no nav — just the centered brand mark + name. The user explicitly
// asked for nothing else here so the sign-in screen feels like a separate
// surface from the authenticated app.
// ---------------------------------------------------------------------------
function BareHeader() {
return (
cm
WhatsApp Bot
);
}
// ---------------------------------------------------------------------------
// AppShell — the outer container
// ---------------------------------------------------------------------------
interface AppShellProps {
children: React.ReactNode;
/** Role of the signed-in user, or null when unauthenticated. */
role: NavRole | null;
/** Username of the signed-in user, surfaced in the footer + sign-out hint. */
username: string | null;
}
export function AppShell({ children, role, username }: AppShellProps) {
const pathname = usePathname();
const isAuthRoute = pathname === "/login";
if (isAuthRoute) {
return (
<>
{children}
>
);
}
// Treat unauthenticated render of a protected route (shouldn't happen
// because middleware redirects, but defense-in-depth) as 'user': hides
// the admin-only entries.
const items = navItemsForRole(role ?? "user");
return (
<>
{/* Desktop sidebar */}
{/* Mobile header (single row: brand · title · menu) */}
{/* Main content
Mobile: push down for the h-14 header (56px) plus a small gap
so page titles don't kiss the bottom edge of the nav.
Desktop: push right for the sidebar (sm:pl-56), no top offset. */}
{children}
>
);
}