From e8bb014874aef083feef99dcd1069f88a945132a Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Mon, 9 Mar 2026 22:44:33 +0100 Subject: [PATCH] =?UTF-8?q?feat(backoffice):=20am=C3=A9lioration=20navigat?= =?UTF-8?q?ion=20mobile=20et=20tablette?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout d'un menu hamburger mobile (MobileNav) avec drawer animé via React Portal (évite le piège du backdrop-filter du header) - Popin JobsIndicator adaptée mobile : positionnement fixed plein-écran sur petit écran, backdrop semi-transparent - Navigation tablette (md→lg) : icônes seules avec tooltip natif, labels visibles uniquement sur lg+ Co-Authored-By: Claude Sonnet 4.6 --- .../app/components/JobsIndicator.tsx | 22 ++++- apps/backoffice/app/components/MobileNav.tsx | 93 +++++++++++++++++++ apps/backoffice/app/layout.tsx | 31 ++++--- 3 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 apps/backoffice/app/components/MobileNav.tsx diff --git a/apps/backoffice/app/components/JobsIndicator.tsx b/apps/backoffice/app/components/JobsIndicator.tsx index 65454fa..8bd793c 100644 --- a/apps/backoffice/app/components/JobsIndicator.tsx +++ b/apps/backoffice/app/components/JobsIndicator.tsx @@ -146,15 +146,27 @@ export function JobsIndicator() { /> + {/* Backdrop mobile */} + {isOpen && ( +
setIsOpen(false)} + aria-hidden="true" + /> + )} + {/* Popin/Dropdown with glassmorphism */} {isOpen && (
diff --git a/apps/backoffice/app/components/MobileNav.tsx b/apps/backoffice/app/components/MobileNav.tsx new file mode 100644 index 0000000..0e5ffa0 --- /dev/null +++ b/apps/backoffice/app/components/MobileNav.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { createPortal } from "react-dom"; +import Link from "next/link"; +import { NavIcon } from "./ui"; + +type NavItem = { + href: "/" | "/books" | "/libraries" | "/jobs" | "/tokens" | "/settings"; + label: string; + icon: "dashboard" | "books" | "libraries" | "jobs" | "tokens" | "settings"; +}; + +const HamburgerIcon = () => ( + + + +); + +const XIcon = () => ( + + + +); + +export function MobileNav({ navItems }: { navItems: NavItem[] }) { + const [isOpen, setIsOpen] = useState(false); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + const overlay = ( + <> + {/* Backdrop */} +
setIsOpen(false)} + aria-hidden="true" + /> + + {/* Drawer */} +
+
+ Navigation +
+ + +
+ + ); + + return ( + <> + {/* Hamburger button — reste dans le header */} + + + {/* Backdrop + Drawer portés directement sur document.body, + hors du header et de son backdrop-filter */} + {mounted && createPortal(overlay, document.body)} + + ); +} diff --git a/apps/backoffice/app/layout.tsx b/apps/backoffice/app/layout.tsx index 6cbb6dc..3d71b80 100644 --- a/apps/backoffice/app/layout.tsx +++ b/apps/backoffice/app/layout.tsx @@ -7,6 +7,7 @@ import { ThemeProvider } from "./theme-provider"; import { ThemeToggle } from "./theme-toggle"; import { JobsIndicator } from "./components/JobsIndicator"; import { NavIcon, Icon } from "./components/ui"; +import { MobileNav } from "./components/MobileNav"; export const metadata: Metadata = { title: "StripStream Backoffice", @@ -61,17 +62,17 @@ export default function RootLayout({ children }: { children: ReactNode }) {
{navItems.map((item) => ( - - - {item.label} + + + {item.label} ))}
- + {/* Actions */}
- +
@@ -95,18 +97,19 @@ export default function RootLayout({ children }: { children: ReactNode }) { } // Navigation Link Component -function NavLink({ href, children }: { href: NavItem["href"]; children: React.ReactNode }) { +function NavLink({ href, title, children }: { href: NavItem["href"]; title?: string; children: React.ReactNode }) { return ( -