274 lines
No EOL
8.3 KiB
JavaScript
274 lines
No EOL
8.3 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { Outlet } from 'react-router-dom';
|
|
import { Box, Container, AppBar, Toolbar, Typography, Button,
|
|
IconButton, Drawer, List, ListItem, ListItemIcon, ListItemText,
|
|
Divider, Badge, useMediaQuery } from '@mui/material';
|
|
import { useTheme } from '@mui/material/styles';
|
|
import MenuIcon from '@mui/icons-material/Menu';
|
|
import HomeIcon from '@mui/icons-material/Home';
|
|
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
|
|
import CategoryIcon from '@mui/icons-material/Category';
|
|
import PersonIcon from '@mui/icons-material/Person';
|
|
import LoginIcon from '@mui/icons-material/Login';
|
|
import LogoutIcon from '@mui/icons-material/Logout';
|
|
import DashboardIcon from '@mui/icons-material/Dashboard';
|
|
import Brightness4Icon from '@mui/icons-material/Brightness4';
|
|
import Brightness7Icon from '@mui/icons-material/Brightness7';
|
|
import ReceiptIcon from '@mui/icons-material/Receipt';
|
|
import BookIcon from '@mui/icons-material/Book';
|
|
import { Link as RouterLink, useNavigate } from 'react-router-dom';
|
|
import { useAuth, useCart, useDarkMode } from '../hooks/reduxHooks';
|
|
import Footer from '../components/Footer';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import apiClient from '@services/api';
|
|
import useBrandingSettings from '@hooks/brandingHooks';
|
|
import imageUtils from '@utils/imageUtils';
|
|
|
|
const MainLayout = () => {
|
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
|
const navigate = useNavigate();
|
|
|
|
const { isAuthenticated, isAdmin, logout } = useAuth();
|
|
const { itemCount } = useCart();
|
|
const [darkMode, toggleDarkMode] = useDarkMode();
|
|
|
|
const { data: brandingSettings } = useBrandingSettings();
|
|
|
|
const siteName = brandingSettings?.site_name || 'Rocks, Bones & Sticks';
|
|
|
|
const logoUrl = imageUtils.getImageUrl(brandingSettings?.logo_url)
|
|
const handleDrawerToggle = () => {
|
|
setDrawerOpen(!drawerOpen);
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
logout();
|
|
navigate('/');
|
|
};
|
|
|
|
let mainMenu = [
|
|
{ text: 'Home', icon: <HomeIcon />, path: '/' },
|
|
{ text: 'Products', icon: <CategoryIcon />, path: '/products' },
|
|
{ text: 'Blog', icon: <BookIcon />, path: '/blog' },
|
|
{ text: 'Cart', icon: <ShoppingCartIcon />, path: '/cart', badge: itemCount > 0 ? itemCount : null },
|
|
];
|
|
if (isAuthenticated) {
|
|
mainMenu.push(
|
|
{ text: 'My Orders', icon: <ReceiptIcon />, path: '/account/orders' }
|
|
);
|
|
}
|
|
|
|
|
|
const authMenu = isAuthenticated ?
|
|
[
|
|
{ text: 'Logout', icon: <LogoutIcon />, onClick: handleLogout }
|
|
] : [
|
|
{ text: 'Login', icon: <LoginIcon />, path: '/auth/login' },
|
|
{ text: 'Register', icon: <PersonIcon />, path: '/auth/register' }
|
|
];
|
|
|
|
// Add admin menu if user is admin
|
|
if (isAuthenticated && isAdmin) {
|
|
mainMenu.push(
|
|
{ text: 'Admin Dashboard', icon: <DashboardIcon />, path: '/admin' }
|
|
);
|
|
}
|
|
|
|
const drawer = (
|
|
<Box sx={{ width: 250 }} role="presentation" onClick={handleDrawerToggle}>
|
|
<Box sx={{ display: 'flex', p: 2, alignItems: 'center' }}>
|
|
{logoUrl ? (
|
|
<Box
|
|
component="img"
|
|
src={logoUrl}
|
|
alt={siteName}
|
|
sx={{
|
|
height: 40,
|
|
maxWidth: '100%',
|
|
objectFit: 'contain'
|
|
}}
|
|
/>
|
|
) : (
|
|
<Typography variant="h6" component="div">
|
|
{siteName}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
<Divider />
|
|
<List>
|
|
{mainMenu.map((item) => (
|
|
<ListItem
|
|
button
|
|
key={item.text}
|
|
component={item.path ? RouterLink : 'button'}
|
|
to={item.path}
|
|
onClick={item.onClick}
|
|
>
|
|
<ListItemIcon>
|
|
{item.badge ? (
|
|
<Badge badgeContent={item.badge} color="primary">
|
|
{item.icon}
|
|
</Badge>
|
|
) : (
|
|
item.icon
|
|
)}
|
|
</ListItemIcon>
|
|
<ListItemText primary={item.text} />
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
<Divider />
|
|
<List>
|
|
<ListItem button onClick={toggleDarkMode}>
|
|
<ListItemIcon>
|
|
{darkMode ? <Brightness7Icon /> : <Brightness4Icon />}
|
|
</ListItemIcon>
|
|
<ListItemText primary={darkMode ? "Light Mode" : "Dark Mode"} />
|
|
</ListItem>
|
|
{authMenu.map((item) => (
|
|
<ListItem
|
|
button
|
|
key={item.text}
|
|
component={item.path ? RouterLink : 'button'}
|
|
to={item.path}
|
|
onClick={item.onClick}
|
|
>
|
|
<ListItemIcon>{item.icon}</ListItemIcon>
|
|
<ListItemText primary={item.text} />
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</Box>
|
|
);
|
|
|
|
return (
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
|
<AppBar position="sticky" color="primary">
|
|
<Toolbar>
|
|
<IconButton
|
|
color="inherit"
|
|
aria-label="open drawer"
|
|
edge="start"
|
|
onClick={handleDrawerToggle}
|
|
sx={{ mr: 2, display: { sm: 'flex' } }}
|
|
>
|
|
<MenuIcon />
|
|
</IconButton>
|
|
|
|
{logoUrl ? (
|
|
<Box
|
|
component={RouterLink}
|
|
to="/"
|
|
sx={{
|
|
flexGrow: 1,
|
|
display: { xs: 'none', sm: 'flex' },
|
|
textDecoration: 'none',
|
|
color: 'white'
|
|
}}
|
|
>
|
|
<Box
|
|
component="img"
|
|
src={logoUrl}
|
|
alt={siteName}
|
|
sx={{
|
|
height: 40,
|
|
maxWidth: '200px',
|
|
objectFit: 'contain'
|
|
}}
|
|
/>
|
|
</Box>
|
|
) : (
|
|
<Typography
|
|
variant="h6"
|
|
component={RouterLink}
|
|
to="/"
|
|
sx={{
|
|
flexGrow: 1,
|
|
color: 'white',
|
|
textDecoration: 'none',
|
|
display: { xs: 'none', sm: 'block' }
|
|
}}
|
|
>
|
|
{siteName}
|
|
</Typography>
|
|
)}
|
|
|
|
{/* Desktop navigation */}
|
|
{!isMobile && (
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
{mainMenu.map((item) => (
|
|
<Button
|
|
key={item.text}
|
|
color="inherit"
|
|
component={RouterLink}
|
|
to={item.path}
|
|
sx={{ ml: 1 }}
|
|
startIcon={item.badge ? (
|
|
<Badge badgeContent={item.badge} color="secondary">
|
|
{item.icon}
|
|
</Badge>
|
|
) : item.icon}
|
|
>
|
|
{item.text}
|
|
</Button>
|
|
))}
|
|
|
|
<IconButton
|
|
sx={{ ml: 1 }}
|
|
onClick={toggleDarkMode}
|
|
color="inherit"
|
|
>
|
|
{darkMode ? <Brightness7Icon /> : <Brightness4Icon />}
|
|
</IconButton>
|
|
|
|
{authMenu.map((item) => (
|
|
item.path ? (
|
|
<Button
|
|
key={item.text}
|
|
color="inherit"
|
|
component={RouterLink}
|
|
to={item.path}
|
|
sx={{ ml: 1 }}
|
|
startIcon={item.icon}
|
|
>
|
|
{item.text}
|
|
</Button>
|
|
) : (
|
|
<Button
|
|
key={item.text}
|
|
color="inherit"
|
|
onClick={item.onClick}
|
|
sx={{ ml: 1 }}
|
|
startIcon={item.icon}
|
|
>
|
|
{item.text}
|
|
</Button>
|
|
)
|
|
))}
|
|
</Box>
|
|
)}
|
|
</Toolbar>
|
|
</AppBar>
|
|
|
|
<Drawer
|
|
anchor="left"
|
|
open={drawerOpen}
|
|
onClose={handleDrawerToggle}
|
|
>
|
|
{drawer}
|
|
</Drawer>
|
|
|
|
<Box component="main" sx={{ flexGrow: 1 }}>
|
|
<Container maxWidth="lg" sx={{ pt: 3, pb: 6 }}>
|
|
<Outlet />
|
|
</Container>
|
|
</Box>
|
|
|
|
<Footer brandingSettings={brandingSettings} />
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default MainLayout; |