grocery-app/frontend/src/components/layout/Navbar.jsx
Nico 77ae5be445
All checks were successful
Build & Deploy Costco Grocery List / build (push) Successful in 1m10s
Build & Deploy Costco Grocery List / verify-images (push) Successful in 3s
Build & Deploy Costco Grocery List / deploy (push) Successful in 11s
Build & Deploy Costco Grocery List / notify (push) Successful in 1s
refactor
2026-02-22 01:27:03 -08:00

118 lines
3.8 KiB
JavaScript

import "../../styles/components/Navbar.css";
import { useContext, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { logoutRequest } from "../../api/auth";
import { AuthContext } from "../../context/AuthContext";
import useActionToast from "../../hooks/useActionToast";
import getApiErrorMessage from "../../lib/getApiErrorMessage";
import HouseholdSwitcher from "../household/HouseholdSwitcher";
export default function Navbar() {
const navigate = useNavigate();
const { role, logout, username } = useContext(AuthContext);
const toast = useActionToast();
const [showUserMenu, setShowUserMenu] = useState(false);
const closeMenus = () => {
setShowUserMenu(false);
};
const handleLogout = async () => {
let loggedOutRemotely = true;
let fallbackReason = "";
try {
await logoutRequest();
} catch (error) {
// Clear local auth state even if server logout fails.
loggedOutRemotely = false;
fallbackReason = getApiErrorMessage(error, "Unable to end server session");
} finally {
logout();
closeMenus();
if (loggedOutRemotely) {
toast.success("Logged out", "Logged out successfully");
} else {
toast.info("Logged out locally", `Server logout failed: ${fallbackReason}`);
}
navigate("/login");
}
};
return (
<nav className="navbar">
<div className="navbar-section navbar-spacer" aria-hidden="true"></div>
{/* Center: Home + Household + Manage */}
<div className="navbar-section navbar-center">
<div className="navbar-center-nav">
<Link
to="/"
className="navbar-icon-link navbar-icon-left"
title="Home"
aria-label="Home"
onClick={closeMenus}
>
<span aria-hidden="true">&#8962;</span>
</Link>
<div className="navbar-household-wrap">
<HouseholdSwitcher />
</div>
<Link
to="/manage"
className="navbar-icon-link navbar-icon-right"
title="Manage"
aria-label="Manage"
onClick={closeMenus}
>
<span aria-hidden="true">&#9881;</span>
</Link>
</div>
</div>
{/* Right: User Menu */}
<div className="navbar-section navbar-right">
<button
className="navbar-user-btn"
onClick={() => {
setShowUserMenu(!showUserMenu);
}}
aria-label="User menu"
>
<span className="navbar-user-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false" aria-hidden="true">
<path d="M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.14 0-7.5 2.69-7.5 6v1h15v-1c0-3.31-3.36-6-7.5-6Z" />
</svg>
</span>
<span className="navbar-user-name">{username}</span>
</button>
{showUserMenu && (
<>
<div className="menu-overlay" onClick={closeMenus}></div>
<div className="navbar-dropdown user-dropdown">
<div className="user-dropdown-info">
<span className="user-dropdown-username">{username}</span>
<span className="user-dropdown-role">{role}</span>
</div>
<Link to="/settings" className="user-dropdown-link" onClick={closeMenus}>
User Settings
</Link>
{role === "system_admin" && (
<Link to="/admin" className="user-dropdown-link" onClick={closeMenus}>
Admin Settings
</Link>
)}
<button className="user-dropdown-logout" onClick={handleLogout}>
Logout
</button>
</div>
</>
)}
</div>
</nav>
);
}