shoc-frontend-new/src/components/layout/app-topbar.tsx

199 lines
5.9 KiB
TypeScript
Raw Normal View History

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
import LogoutOutlinedIcon from "@mui/icons-material/LogoutOutlined";
import MenuIcon from "@mui/icons-material/Menu";
import NotificationsNoneOutlinedIcon from "@mui/icons-material/NotificationsNoneOutlined";
import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined";
import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
import {
AppBar,
Avatar,
Box,
Divider,
IconButton,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
Toolbar,
Typography,
} from "@mui/material";
import { useAuthContext } from "@/providers/auth-context";
type AppTopbarProps = {
onToggleNav: () => void;
};
export function AppTopbar({ onToggleNav }: AppTopbarProps) {
const navigate = useNavigate();
const { user, logout } = useAuthContext();
const [notifAnchor, setNotifAnchor] = useState<null | HTMLElement>(null);
const [userAnchor, setUserAnchor] = useState<null | HTMLElement>(null);
const handleLogout = () => {
setUserAnchor(null);
logout();
};
const userInitials = user?.name
? user.name
.split(" ")
.map((part) => part[0])
.join("")
.slice(0, 2)
.toUpperCase()
: "U";
return (
<AppBar
position="sticky"
elevation={0}
sx={{
height: "var(--spacing-topbar)",
background: "var(--color-header-gradient)",
boxShadow: "var(--color-header-shadow)",
color: "var(--color-header-text)",
}}
>
<Toolbar
disableGutters
className="flex justify-between px-4"
sx={{ minHeight: "var(--spacing-topbar) !important" }}
>
<Box className="flex items-center">
<IconButton
onClick={onToggleNav}
aria-label="Toggle navigation"
sx={{
color: "var(--color-header-icon)",
mr: 1.5,
"&:hover": { backgroundColor: "var(--color-header-surface-hover)" },
}}
>
<MenuIcon />
</IconButton>
</Box>
<Box className="flex items-center gap-1">
<IconButton
title="Notifications"
onClick={(event) => setNotifAnchor(event.currentTarget)}
sx={{
color: "var(--color-header-text-muted)",
"&:hover": {
color: "var(--color-header-text)",
backgroundColor: "var(--color-header-chip-hover)",
},
}}
>
<NotificationsNoneOutlinedIcon fontSize="small" />
</IconButton>
<Divider
orientation="vertical"
flexItem
sx={{
mx: 0.5,
borderColor: "var(--color-header-surface)",
alignSelf: "center",
height: 24,
}}
/>
<IconButton
title={user?.name || "User"}
onClick={(event) => setUserAnchor(event.currentTarget)}
sx={{
color: "var(--color-header-text-muted)",
"&:hover": {
color: "var(--color-header-text)",
backgroundColor: "var(--color-header-chip-hover)",
},
}}
>
<PersonOutlineOutlinedIcon fontSize="small" />
</IconButton>
</Box>
</Toolbar>
<Menu
anchorEl={notifAnchor}
open={Boolean(notifAnchor)}
onClose={() => setNotifAnchor(null)}
transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
slotProps={{
paper: {
sx: { width: 300, mt: 1, boxShadow: "var(--shadow-lg)" },
},
}}
>
<Box sx={{ px: 2, py: 1.5, borderBottom: "1px solid var(--border)" }}>
<Typography variant="subtitle2">Notifications</Typography>
</Box>
<Box className="flex flex-col items-center gap-2 px-4 py-6 text-muted-foreground">
<CheckCircleOutlineIcon sx={{ fontSize: 28, color: "var(--muted)" }} />
<Typography variant="body2" color="text.secondary">
No new notifications
</Typography>
</Box>
</Menu>
<Menu
anchorEl={userAnchor}
open={Boolean(userAnchor)}
onClose={() => setUserAnchor(null)}
transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
slotProps={{
paper: {
sx: { width: 260, mt: 1, boxShadow: "var(--shadow-lg)" },
},
}}
>
<Box className="flex items-center gap-3 px-4 py-3">
<Avatar
sx={{
width: 40,
height: 40,
bgcolor: "var(--accent)",
fontSize: "0.875rem",
fontWeight: 600,
border: "2px solid var(--color-header-avatar-ring)",
}}
>
{userInitials}
</Avatar>
<Box>
<Typography variant="subtitle2">{user?.name || "User"}</Typography>
<Typography variant="caption" color="text.secondary">
{user?.email || ""}
</Typography>
</Box>
</Box>
<Divider />
<MenuItem
onClick={() => {
setUserAnchor(null);
navigate("/settings/dropdowns");
}}
>
<ListItemIcon>
<SettingsOutlinedIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Settings</ListItemText>
</MenuItem>
<MenuItem onClick={handleLogout} sx={{ color: "error.main" }}>
<ListItemIcon>
<LogoutOutlinedIcon fontSize="small" color="error" />
</ListItemIcon>
<ListItemText>Logout</ListItemText>
</MenuItem>
</Menu>
</AppBar>
);
}