mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
First of a series to make the app mobile-friendly. The sidebar used to be a fixed 240px column always visible, eating most of a 375px phone screen and leaving ~135px for content. Now: - Sidebar becomes a slide-in drawer on <lg, static on ≥lg. Controlled via a drawer-open prop wired from the shell; transform + duration feels native on touch devices. - Backdrop overlay closes it on tap; Escape closes it; body scroll locks while open; route changes auto-close it. - Header gains a 44×44 hamburger button on <lg that opens the drawer, hidden on ≥lg. Title shrinks slightly on narrow widths so the hamburger + avatar + sign-out row never wraps. - Nav rows are taller (py-2.5) on mobile for comfortable touch targets, slim (py-2) back on desktop. - Main content padding is p-4 on mobile, p-6 on desktop so cards have room to breathe without shouldering into the viewport edge. No desktop changes visible beyond tighter padding on very narrow lg breakpoints (main content width is unchanged). Next PRs in this series will adapt: - Inbox (3-pane → single-pane with back) - Pipelines (kanban snap-scroll) - Automation builder (card widths + bottom-sheet edit) - Tables + forms polish Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
"use client";
|
||
|
||
import { usePathname } from "next/navigation";
|
||
import { useAuth } from "@/hooks/use-auth";
|
||
import { LogOut, Menu } from "lucide-react";
|
||
|
||
const pageTitles: Record<string, string> = {
|
||
"/dashboard": "Dashboard",
|
||
"/inbox": "Inbox",
|
||
"/contacts": "Contacts",
|
||
"/pipelines": "Pipelines",
|
||
"/broadcasts": "Broadcasts",
|
||
"/automations": "Automations",
|
||
"/settings": "Settings",
|
||
};
|
||
|
||
function getPageTitle(pathname: string): string {
|
||
if (pageTitles[pathname]) return pageTitles[pathname];
|
||
const match = Object.entries(pageTitles).find(([path]) =>
|
||
pathname.startsWith(path),
|
||
);
|
||
return match ? match[1] : "Dashboard";
|
||
}
|
||
|
||
interface HeaderProps {
|
||
/** Wired to the shell's drawer state. Used only on mobile — the
|
||
* hamburger button is hidden on lg+. */
|
||
onOpenSidebar?: () => void;
|
||
}
|
||
|
||
export function Header({ onOpenSidebar }: HeaderProps) {
|
||
const pathname = usePathname();
|
||
const { profile, signOut } = useAuth();
|
||
const title = getPageTitle(pathname);
|
||
|
||
return (
|
||
<header className="flex h-14 shrink-0 items-center justify-between gap-3 border-b border-slate-800 bg-slate-950 px-4 lg:px-6">
|
||
<div className="flex min-w-0 items-center gap-2">
|
||
{/* Hamburger — mobile only. 44×44 hit target per Apple HIG. */}
|
||
<button
|
||
type="button"
|
||
onClick={onOpenSidebar}
|
||
aria-label="Open menu"
|
||
className="flex h-10 w-10 items-center justify-center rounded-md text-slate-300 transition-colors hover:bg-slate-800 hover:text-white lg:hidden"
|
||
>
|
||
<Menu className="h-5 w-5" />
|
||
</button>
|
||
<h1 className="truncate text-base font-semibold text-white sm:text-lg">
|
||
{title}
|
||
</h1>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 sm:gap-3">
|
||
<div className="flex items-center gap-2 sm:gap-3">
|
||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-emerald-500/10 text-sm font-medium text-emerald-500">
|
||
{profile?.full_name?.charAt(0)?.toUpperCase() ?? "U"}
|
||
</div>
|
||
<div className="hidden sm:block">
|
||
<p className="text-sm font-medium text-white">
|
||
{profile?.full_name ?? "User"}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={signOut}
|
||
className="flex h-10 w-10 items-center justify-center rounded-md text-slate-400 transition-colors hover:bg-slate-800 hover:text-white"
|
||
title="Sign out"
|
||
aria-label="Sign out"
|
||
>
|
||
<LogOut className="h-4 w-4" />
|
||
</button>
|
||
</div>
|
||
</header>
|
||
);
|
||
}
|