Gowrisankar
Remove tags functionality from the portal UI and demo seed.
4f30b1f
import {
Award,
Briefcase,
CalendarDays,
ChevronDown,
ClipboardList,
LayoutDashboard,
Shield,
UserRound,
Users,
} from "lucide-react";
import { useEffect, useRef, useState, type ComponentType } from "react";
import { NavLink, Outlet, useLocation } from "react-router-dom";
import { useAuth } from "../../auth";
const navItems = [
{ to: "/people", label: "People" },
{ to: "/projects", label: "Projects" },
];
interface ManageItem {
to: string;
label: string;
icon: ComponentType<{ size?: number; className?: string }>;
}
const manageGroups: ManageItem[][] = [
[
{ to: "/manage", label: "Manage Hub", icon: LayoutDashboard },
],
[
{ to: "/manage/people", label: "People", icon: UserRound },
{ to: "/manage/projects", label: "Projects", icon: ClipboardList },
],
[
{ to: "/manage/roles", label: "Roles", icon: Briefcase },
{ to: "/manage/teams", label: "Teams", icon: Users },
{ to: "/manage/skills", label: "Skills", icon: Award },
],
[
{ to: "/manage/holidays", label: "Holidays", icon: CalendarDays },
{ to: "/manage/users", label: "Users", icon: Shield },
],
];
const tailNavItems = [
{ to: "/reports", label: "Reports" },
{ to: "/insights", label: "Insights" },
];
export function Layout() {
const { logout, user } = useAuth();
const location = useLocation();
const [menuOpen, setMenuOpen] = useState(false);
const [manageOpen, setManageOpen] = useState(false);
const manageRef = useRef<HTMLDivElement>(null);
const closeTimer = useRef<number | undefined>(undefined);
const isManageActive = location.pathname === "/manage" || location.pathname.startsWith("/manage/");
useEffect(() => {
setManageOpen(false);
}, [location.pathname]);
useEffect(() => {
if (!menuOpen) return;
const onClick = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (!target.closest(".topnav-actions")) setMenuOpen(false);
};
document.addEventListener("mousedown", onClick);
return () => document.removeEventListener("mousedown", onClick);
}, [menuOpen]);
useEffect(() => {
if (!manageOpen) return;
const handler = (event: MouseEvent) => {
if (manageRef.current && !manageRef.current.contains(event.target as Node)) {
setManageOpen(false);
}
};
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, [manageOpen]);
const openManage = () => {
if (closeTimer.current) window.clearTimeout(closeTimer.current);
setManageOpen(true);
};
const scheduleCloseManage = () => {
if (closeTimer.current) window.clearTimeout(closeTimer.current);
closeTimer.current = window.setTimeout(() => setManageOpen(false), 140);
};
return (
<div className="app-shell">
<header className="topnav">
<div className="topnav-brand">
<div className="brand-mark">R</div>
<div>
<strong>Resource Portal</strong>
<small>Internal IT</small>
</div>
</div>
<nav className="topnav-tabs">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) => `topnav-tab${isActive ? " active" : ""}`}
>
{item.label}
</NavLink>
))}
<div
className="topnav-dropdown"
onMouseEnter={openManage}
onMouseLeave={scheduleCloseManage}
ref={manageRef}
>
<NavLink
to="/manage"
end
className={`topnav-tab${isManageActive ? " active" : ""}`}
onClick={() => setManageOpen(false)}
>
Manage
<ChevronDown size={14} className="topnav-tab-caret" />
</NavLink>
{manageOpen ? (
<div className="topnav-menu" role="menu">
{manageGroups.map((group, groupIndex) => (
<div className="topnav-menu-group" key={groupIndex}>
{group.map((item) => (
<NavLink
key={item.to}
to={item.to}
end={item.to === "/manage"}
className={({ isActive }) => `topnav-menu-item${isActive ? " active" : ""}`}
role="menuitem"
>
<item.icon size={16} className="topnav-menu-icon" />
<span>{item.label}</span>
</NavLink>
))}
</div>
))}
</div>
) : null}
</div>
{tailNavItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) => `topnav-tab${isActive ? " active" : ""}`}
>
{item.label}
</NavLink>
))}
</nav>
<div className="topnav-actions">
<button
aria-haspopup="menu"
aria-expanded={menuOpen}
className="user-chip"
onClick={() => setMenuOpen((open) => !open)}
type="button"
>
<span className="user-avatar">{user?.username?.[0]?.toUpperCase() ?? "?"}</span>
<span className="user-meta">
<strong>{user?.username}</strong>
<small className="badge live">{user?.role}</small>
</span>
</button>
{menuOpen ? (
<div className="user-menu" role="menu">
<button
onClick={() => {
setMenuOpen(false);
logout();
}}
role="menuitem"
type="button"
>
Sign out
</button>
</div>
) : null}
</div>
</header>
<main className="content">
<Outlet />
</main>
</div>
);
}