Better app bar, mobile, and role based render

This commit is contained in:
lumijiez
2025-06-24 01:39:33 +03:00
parent 4dd71382bb
commit 8940ad1caf

View File

@@ -1,66 +1,383 @@
'use client' 'use client'
import {AppBar, Button, Toolbar, Typography, Avatar, Box} from "@mui/material"; import {
AppBar,
Button,
Toolbar,
Typography,
Avatar,
Box,
IconButton,
Menu,
MenuItem,
Divider,
useMediaQuery,
useTheme,
Paper
} from "@mui/material";
import { useState, useEffect } from "react";
import { useUser } from "@auth0/nextjs-auth0"; import { useUser } from "@auth0/nextjs-auth0";
import {
Menu as MenuIcon,
Home,
PhotoLibrary,
ShoppingBag,
Store,
Dashboard,
AdminPanelSettings,
Api,
BugReport
} from "@mui/icons-material";
import ThemeToggleButton from "@/app/components/theme/ThemeToggleButton"; import ThemeToggleButton from "@/app/components/theme/ThemeToggleButton";
import clientApi from "@/lib/clientApi";
export default function ImprinkAppBar() { export default function ImprinkAppBar() {
const { user, error, isLoading } = useUser(); const { user, error, isLoading } = useUser();
const [anchorEl, setAnchorEl] = useState(null);
const [userRoles, setUserRoles] = useState([]);
const [rolesLoading, setRolesLoading] = useState(false);
const theme = useTheme();
const { isDarkMode, toggleTheme } = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
return ( useEffect(() => {
<AppBar position="static"> const fetchUserRoles = async () => {
<Toolbar> if (!user) {
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}> setUserRoles([]);
Modern App return;
</Typography> }
<ThemeToggleButton />
{isLoading ? ( setRolesLoading(true);
<Box sx={{ ml: 2 }}> try {
<Typography variant="body2">Loading...</Typography> const response = await clientApi.get('/users/me/roles');
</Box> const roles = response.data.map(role => role.roleName.toLowerCase());
) : user ? ( setUserRoles(roles);
<Box sx={{ display: 'flex', alignItems: 'center', ml: 2 }}> } catch (error) {
<Avatar console.error('Failed to fetch user roles:', error);
src={user.picture} setUserRoles([]);
alt={user.name} } finally {
sx={{ width: 32, height: 32, mr: 1 }} setRolesLoading(false);
/> }
<Typography variant="body2" sx={{ mr: 2, color: 'inherit' }}> };
{user.name}
</Typography> fetchUserRoles();
}, [user]);
const isMerchant = userRoles.includes('merchant') || userRoles.includes('admin');
const isAdmin = userRoles.includes('admin');
const handleMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setAnchorEl(null);
};
// Regular navigation links (excluding admin-specific ones)
const navigationLinks = [
{ label: 'Home', href: '/', icon: <Home />, show: true },
{ label: 'Gallery', href: '/gallery', icon: <PhotoLibrary />, show: true },
{ label: 'Orders', href: '/orders', icon: <ShoppingBag />, show: true },
{ label: 'Merchant', href: '/merchant', icon: <Store />, show: isMerchant },
];
// Admin-specific links for the separate bar
const adminLinks = [
{ label: 'Dashboard', href: '/dashboard', icon: <Dashboard />, show: isMerchant },
{ label: 'Admin', href: '/admin', icon: <AdminPanelSettings />, show: isAdmin },
{ label: 'Swagger', href: '/swagger', icon: <Api />, show: isAdmin },
{ label: 'SEQ', href: '/seq', icon: <BugReport />, show: isAdmin },
];
const visibleLinks = navigationLinks.filter(link => link.show);
const visibleAdminLinks = adminLinks.filter(link => link.show);
const renderDesktopNavigation = () => (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{visibleLinks.map((link) => (
<Button
key={link.label}
color="inherit"
href={link.href}
startIcon={link.icon}
sx={{
minWidth: 'auto',
px: 2,
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
{link.label}
</Button>
))}
</Box>
);
const renderAdminBar = () => {
if (!visibleAdminLinks.length || isMobile) return null;
return (
<Paper
elevation={1}
sx={{
backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.02)',
borderTop: `1px solid ${theme.palette.divider}`,
borderRadius: 0,
py: 1.5,
px: 2
}}
>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 3,
justifyContent: 'center',
maxWidth: '1200px',
mx: 'auto'
}}>
<Typography
variant="caption"
sx={{
mr: 2,
color: 'text.secondary',
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: 0.5
}}
>
Admin Tools
</Typography>
{visibleAdminLinks.map((link) => (
<Button <Button
color="inherit" key={link.label}
href="/auth/logout" href={link.href}
> startIcon={link.icon}
Logout variant="text"
</Button> size="small"
</Box>
) : (
<Box sx={{ ml: 2 }}>
<Button
color="inherit"
href="/auth/login"
sx={{ mr: 1 }}
>
Login
</Button>
<Button
variant="contained"
href="/auth/login"
sx={{ sx={{
bgcolor: 'primary.main', minWidth: 'auto',
color: 'primary.contrastText', px: 2,
py: 0.5,
color: 'text.primary',
'&:hover': { '&:hover': {
bgcolor: 'primary.dark' backgroundColor: 'action.hover'
} }
}} }}
> >
Sign Up {link.label}
</Button> </Button>
</Box> ))}
</Box>
</Paper>
);
};
const renderMobileMenu = () => (
<>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
onClick={handleMenuOpen}
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleMenuClose}
PaperProps={{
sx: {
mt: 1,
minWidth: 200,
'& .MuiMenuItem-root': {
px: 2,
py: 1.5,
gap: 2
}
}
}}
transformOrigin={{ horizontal: 'left', vertical: 'top' }}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
>
<Divider />
{/* All navigation links - regular and admin mixed together */}
{[...visibleLinks, ...visibleAdminLinks].map((link) => (
<MenuItem
key={link.label}
onClick={handleMenuClose}
component="a"
href={link.href}
sx={{
color: 'inherit',
textDecoration: 'none',
'&:hover': {
backgroundColor: 'action.hover'
}
}}
>
{link.icon}
<Typography variant="body2">{link.label}</Typography>
</MenuItem>
))}
{!isLoading && (
<>
<Divider />
{user ? (
<>
<MenuItem
sx={{
opacity: 1,
'&:hover': { backgroundColor: 'transparent' },
cursor: 'default'
}}
onClick={(e) => e.preventDefault()}
>
<Avatar
src={user.picture}
alt={user.name}
sx={{ width: 24, height: 24 }}
/>
<Typography variant="body2" noWrap>
{user.name}
</Typography>
</MenuItem>
<MenuItem onClick={(e) => e.stopPropagation()}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, width: '100%' }}>
<Typography variant="body2">Theme</Typography>
<Box sx={{ ml: 'auto' }}>
<ThemeToggleButton />
</Box>
</Box>
</MenuItem>
<MenuItem
component="a"
href="/auth/logout"
onClick={handleMenuClose}
sx={{ color: 'error.main' }}
>
<Typography variant="body2">Logout</Typography>
</MenuItem>
</>
) : (
<>
<MenuItem
component="a"
href="/auth/login"
onClick={handleMenuClose}
>
<Typography variant="body2">Login</Typography>
</MenuItem>
<MenuItem
component="a"
href="/auth/login"
onClick={handleMenuClose}
sx={{
color: 'primary.main',
fontWeight: 'bold'
}}
>
<Typography variant="body2">Sign Up</Typography>
</MenuItem>
<MenuItem onClick={toggleTheme}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, width: '100%' }}>
<Typography variant="body2">Theme</Typography>
<Box sx={{ ml: 'auto' }}>
{isDarkMode ? '🌙' : '☀️'}
</Box>
</Box>
</MenuItem>
</>
)}
</>
)} )}
</Toolbar> </Menu>
</AppBar> </>
);
return (
<>
<AppBar position="static">
<Toolbar>
{isMobile && renderMobileMenu()}
<Typography
variant="h6"
component="div"
sx={{
flexGrow: isMobile ? 1 : 0,
mr: isMobile ? 0 : 4
}}
>
Imprink
</Typography>
{!isMobile && (
<>
<Box sx={{ flexGrow: 1 }}>
{renderDesktopNavigation()}
</Box>
<ThemeToggleButton sx={{ mr: 2 }} />
{isLoading ? (
<Box sx={{ ml: 2 }}>
<Typography variant="body2">Loading...</Typography>
</Box>
) : user ? (
<Box sx={{ display: 'flex', alignItems: 'center', ml: 2 }}>
<Avatar
src={user.picture}
alt={user.name}
sx={{ width: 32, height: 32, mr: 1 }}
/>
<Typography variant="body2" sx={{ mr: 2, color: 'inherit' }}>
{user.name}
</Typography>
<Button
color="inherit"
href="/auth/logout"
>
Logout
</Button>
</Box>
) : (
<Box sx={{ ml: 2 }}>
<Button
color="inherit"
href="/auth/login"
sx={{ mr: 1 }}
>
Login
</Button>
<Button
variant="contained"
href="/auth/login"
sx={{
bgcolor: 'primary.main',
color: 'primary.contrastText',
'&:hover': {
bgcolor: 'primary.dark'
}
}}
>
Sign Up
</Button>
</Box>
)}
</>
)}
</Toolbar>
</AppBar>
{/* Admin Bar - appears below main app bar */}
{renderAdminBar()}
</>
) )
} }