313 lines
7.4 KiB
JavaScript
313 lines
7.4 KiB
JavaScript
import React, { useEffect, useRef } from 'react';
|
|
import { Link, NavLink } from 'react-router-dom';
|
|
import { useAuth } from '../context/useAuth';
|
|
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarFooter,
|
|
SidebarGroup,
|
|
SidebarGroupContent,
|
|
SidebarHeader,
|
|
SidebarMenu,
|
|
SidebarMenuButton,
|
|
SidebarMenuItem,
|
|
useSidebar,
|
|
} from '@/components/ui/sidebar';
|
|
|
|
import {
|
|
User,
|
|
Clock,
|
|
CalendarDays,
|
|
Users,
|
|
LogOut,
|
|
Wheat,
|
|
} from 'lucide-react';
|
|
|
|
const NavBar = () => {
|
|
const { user, logout } = useAuth();
|
|
const { isMobile, toggleSidebar, setOpen } = useSidebar();
|
|
|
|
const closeTimeout = useRef(null);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (closeTimeout.current) {
|
|
clearTimeout(closeTimeout.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
const handleMouseEnter = () => {
|
|
if (isMobile) return;
|
|
|
|
if (closeTimeout.current) {
|
|
clearTimeout(closeTimeout.current);
|
|
closeTimeout.current = null;
|
|
}
|
|
|
|
setOpen(true);
|
|
};
|
|
|
|
const handleMouseLeave = () => {
|
|
if (isMobile) return;
|
|
|
|
closeTimeout.current = setTimeout(() => {
|
|
setOpen(false);
|
|
}, 150);
|
|
};
|
|
|
|
const handleCloseMenu = () => {
|
|
if (isMobile) {
|
|
toggleSidebar();
|
|
}
|
|
};
|
|
|
|
const navigation = [
|
|
{
|
|
title: 'Profil',
|
|
href: '/profile',
|
|
icon: User,
|
|
},
|
|
{
|
|
title: 'Résumé hebdomadaire',
|
|
href: '/weekly-summary',
|
|
icon: Clock,
|
|
},
|
|
{
|
|
title: 'Pointage',
|
|
href: '/pointage',
|
|
icon: CalendarDays,
|
|
},
|
|
];
|
|
|
|
/*
|
|
* Hauteur d'une ligne de navigation.
|
|
*
|
|
* Le SidebarMenuButton natif fait 32px.
|
|
* On lui donne une ligne de 48px et on le centre.
|
|
*
|
|
* Le header de groupe fait lui aussi 32px.
|
|
* En mode plié, il est remplacé visuellement par
|
|
* une séparation, mais conserve exactement ses 32px.
|
|
*/
|
|
const menuItemClass = 'h-12 min-h-12 flex items-center';
|
|
|
|
const buttonClass = `
|
|
w-full
|
|
h-8
|
|
min-h-8
|
|
group-data-[collapsible=icon]:size-8!
|
|
group-data-[collapsible=icon]:p-2!
|
|
`;
|
|
|
|
const linkClass = `
|
|
flex
|
|
h-full
|
|
w-full
|
|
items-center
|
|
gap-2
|
|
text-base
|
|
`;
|
|
|
|
/*
|
|
* Header de groupe :
|
|
*
|
|
* Déplié :
|
|
* [ Navigation ] 32px
|
|
*
|
|
* Plié :
|
|
* [ ---------------------- ] 32px
|
|
*
|
|
* Même hauteur dans les deux cas.
|
|
*/
|
|
const GroupHeader = ({ children }) => (
|
|
<div
|
|
className="
|
|
flex
|
|
h-8
|
|
min-h-8
|
|
w-full
|
|
items-center
|
|
px-2
|
|
"
|
|
>
|
|
{/* Nom du groupe */}
|
|
<span
|
|
className="
|
|
truncate
|
|
text-lg
|
|
font-medium
|
|
group-data-[collapsible=icon]:hidden
|
|
"
|
|
>
|
|
{children}
|
|
</span>
|
|
|
|
{/* Séparation en mode plié */}
|
|
<div
|
|
className="
|
|
hidden
|
|
w-full
|
|
border-t
|
|
border-sidebar-border
|
|
group-data-[collapsible=icon]:block
|
|
"
|
|
/>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<Sidebar
|
|
className="border-0"
|
|
collapsible="icon"
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={handleMouseLeave}
|
|
>
|
|
{/* Logo / nom de la boulangerie */}
|
|
<SidebarHeader className="h-16 p-2 pl-0">
|
|
<SidebarMenu>
|
|
<SidebarMenuItem className="h-12 flex items-center">
|
|
<SidebarMenuButton
|
|
size="lg"
|
|
className="
|
|
h-12
|
|
w-full
|
|
group-data-[collapsible=icon]:h-8!
|
|
group-data-[collapsible=icon]:w-10!
|
|
group-data-[collapsible=icon]:p-2!
|
|
"
|
|
render={
|
|
<Link
|
|
to="/"
|
|
className="flex w-full items-center gap-2"
|
|
>
|
|
<div className="flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
|
|
<Wheat className="size-4" />
|
|
</div>
|
|
|
|
<div className="grid min-w-0 flex-1 text-left text-sm leading-tight">
|
|
<span className="truncate font-semibold">
|
|
Le Fournil du Bocage
|
|
</span>
|
|
|
|
<span className="truncate text-xs">
|
|
Gestion
|
|
</span>
|
|
</div>
|
|
</Link>
|
|
}
|
|
/>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarHeader>
|
|
|
|
<SidebarContent>
|
|
{/* Navigation */}
|
|
<SidebarGroup className="p-2">
|
|
<GroupHeader>
|
|
Navigation
|
|
</GroupHeader>
|
|
|
|
<SidebarGroupContent>
|
|
<SidebarMenu>
|
|
{navigation.map((item) => {
|
|
const Icon = item.icon;
|
|
|
|
return (
|
|
<SidebarMenuItem
|
|
key={item.href}
|
|
className={menuItemClass}
|
|
>
|
|
<SidebarMenuButton
|
|
tooltip={item.title}
|
|
onClick={handleCloseMenu}
|
|
className={buttonClass}
|
|
render={
|
|
<NavLink
|
|
to={item.href}
|
|
className={linkClass}
|
|
>
|
|
<Icon className="size-6 shrink-0" />
|
|
|
|
<span className="truncate text-base">
|
|
{item.title}
|
|
</span>
|
|
</NavLink>
|
|
}
|
|
/>
|
|
</SidebarMenuItem>
|
|
);
|
|
})}
|
|
</SidebarMenu>
|
|
</SidebarGroupContent>
|
|
</SidebarGroup>
|
|
|
|
{/* Administration */}
|
|
{user.is_admin && (
|
|
<SidebarGroup className="p-2">
|
|
<GroupHeader>
|
|
Administration
|
|
</GroupHeader>
|
|
|
|
<SidebarGroupContent>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem className={menuItemClass}>
|
|
<SidebarMenuButton
|
|
tooltip="Comptes existants"
|
|
onClick={handleCloseMenu}
|
|
className={buttonClass}
|
|
render={
|
|
<NavLink
|
|
to="/users"
|
|
className={linkClass}
|
|
>
|
|
<Users className="size-6 shrink-0" />
|
|
|
|
<span className="truncate text-base">
|
|
Comptes existants
|
|
</span>
|
|
</NavLink>
|
|
}
|
|
/>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarGroupContent>
|
|
</SidebarGroup>
|
|
)}
|
|
</SidebarContent>
|
|
|
|
{/* Déconnexion */}
|
|
<SidebarFooter>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem className={menuItemClass}>
|
|
<SidebarMenuButton
|
|
tooltip="Déconnexion"
|
|
onClick={handleCloseMenu}
|
|
className={buttonClass}
|
|
render={
|
|
<button
|
|
type="button"
|
|
onClick={logout}
|
|
className={linkClass}
|
|
>
|
|
<LogOut className="size-6 shrink-0" />
|
|
|
|
<span className="truncate text-base">
|
|
Déconnexion
|
|
</span>
|
|
</button>
|
|
}
|
|
/>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarFooter>
|
|
</Sidebar>
|
|
);
|
|
};
|
|
|
|
export default NavBar; |