From ab6092ae8267da868ba5066728619965b20f44be Mon Sep 17 00:00:00 2001 From: Xor290 Date: Fri, 12 Jun 2026 18:02:24 +0200 Subject: [PATCH] chore: build --- frontend-prep/src/components/Navbar.css | 125 +++++++++++++++++------- frontend-prep/src/components/Navbar.tsx | 115 ++++++++++++---------- 2 files changed, 150 insertions(+), 90 deletions(-) diff --git a/frontend-prep/src/components/Navbar.css b/frontend-prep/src/components/Navbar.css index f87e4f0f..49f0c6a8 100644 --- a/frontend-prep/src/components/Navbar.css +++ b/frontend-prep/src/components/Navbar.css @@ -443,59 +443,106 @@ body { } /* ============================================================ - Notification panel + Notification bottom-sheet modal ============================================================ */ -.notif-panel { - position: absolute; - top: calc(var(--topbar-h) - 4px); - right: 0; - width: 300px; - max-height: 380px; +.notif-modal-overlay { + position: fixed; + inset: 0; + z-index: 1100; + background: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); + display: flex; + align-items: flex-end; + justify-content: center; + animation: fadeIn 0.18s ease; +} + +.notif-modal { + width: 100%; + max-width: 640px; + height: 70vh; background: var(--surface); - border: 1px solid var(--border); - border-radius: 14px; - overflow: hidden; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55); + border-top-left-radius: 20px; + border-top-right-radius: 20px; display: flex; flex-direction: column; - animation: slideDown 0.18s ease; + overflow: hidden; + box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.5); + animation: slideUp 0.26s cubic-bezier(0.4, 0, 0.2, 1); } -@keyframes slideDown { - from { opacity: 0; transform: translateY(-6px); } - to { opacity: 1; transform: translateY(0); } +@keyframes slideUp { + from { transform: translateY(100%); } + to { transform: translateY(0); } } -.notif-panel-header { - padding: 0.7rem 1rem; +.notif-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1.1rem 1.25rem; border-bottom: 1px solid var(--border); - color: var(--text); - font-weight: 600; - font-size: 0.85rem; - letter-spacing: 0.02em; + flex-shrink: 0; } +.notif-modal-title { + color: var(--text); + font-size: 1.05rem; + font-weight: 700; +} + +.notif-modal-close { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text-muted); + font-size: 0.9rem; + cursor: pointer; + transition: all var(--transition); +} + +.notif-modal-close:hover { + background: var(--surface-2); + color: var(--text); +} + +.notif-modal-body { + flex: 1; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: var(--border) transparent; +} + +.notif-modal-body::-webkit-scrollbar { width: 4px; } +.notif-modal-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; } + .notif-empty { - padding: 1.5rem; - text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem 1.5rem; color: var(--text-muted); - font-size: 0.85rem; + font-size: 0.9rem; + gap: 0.5rem; + height: 100%; } -.notif-list { - list-style: none; - margin: 0; - padding: 0; - overflow-y: auto; - max-height: 320px; -} +.notif-empty p { margin: 0; } .notif-item { display: flex; flex-direction: column; - gap: 0.2rem; - padding: 0.7rem 1rem; + gap: 0.3rem; + padding: 0.9rem 1.25rem; border-bottom: 1px solid var(--border); + border-left: 3px solid transparent; transition: background var(--transition); } @@ -503,20 +550,22 @@ body { .notif-unread { background: var(--primary-soft); - border-left: 3px solid var(--primary); + border-left-color: var(--primary); } -.notif-read { opacity: 0.55; } +.notif-read { + opacity: 0.55; +} .notif-message { color: var(--text); - font-size: 0.83rem; - line-height: 1.45; + font-size: 0.88rem; + line-height: 1.5; } .notif-time { color: var(--text-muted); - font-size: 0.72rem; + font-size: 0.74rem; } /* ============================================================ diff --git a/frontend-prep/src/components/Navbar.tsx b/frontend-prep/src/components/Navbar.tsx index f0f3477d..5e6adfd7 100644 --- a/frontend-prep/src/components/Navbar.tsx +++ b/frontend-prep/src/components/Navbar.tsx @@ -31,6 +31,22 @@ interface MenuItem { path: string; } +function formatNotifDate(dateStr: string): string { + try { + const diffMs = Date.now() - new Date(dateStr).getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return "À l'instant"; + if (diffMin < 60) return `Il y a ${diffMin} min`; + const diffH = Math.floor(diffMin / 60); + if (diffH < 24) return `Il y a ${diffH}h`; + const diffD = Math.floor(diffH / 24); + if (diffD === 1) return "Hier"; + return `Il y a ${diffD} jours`; + } catch { + return ""; + } +} + function Navbar() { const { theme, toggleTheme } = useTheme(); const [isMenuOpen, setIsMenuOpen] = useState(false); @@ -42,7 +58,6 @@ function Navbar() { const [shopName, setShopName] = useState("Milieu-Nantais"); const seenKeysRef = useRef>(new Set()); const isFirstLoadRef = useRef(true); - const notifPanelRef = useRef(null); const { cartCount } = useCart(); const navigate = useNavigate(); const location = useLocation(); @@ -81,25 +96,17 @@ function Navbar() { }); }, []); - useEffect(() => { - const handleClickOutside = (e: MouseEvent) => { - if (notifPanelRef.current && !notifPanelRef.current.contains(e.target as Node)) { - setShowNotifPanel(false); - } - }; - document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); - }, []); - const handleNotifBellClick = async () => { - setShowNotifPanel((prev) => !prev); - if (!showNotifPanel && unreadCount > 0) { + setShowNotifPanel(true); + if (unreadCount > 0) { await markNotificationsRead(); setUnreadCount(0); setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); } }; + const closeNotifPanel = () => setShowNotifPanel(false); + const menuItems: MenuItem[] = [ { id: "accueil", label: "Accueil", icon: faHome, path: "/user/accueil" }, { id: "produits", label: "Nos Produits", icon: faBox, path: "/user/nos-produits" }, @@ -173,46 +180,18 @@ function Navbar() { {/* Notifications */} -
- - - {showNotifPanel && ( -
-
Notifications
- {notifications.length === 0 ? ( -
Aucune notification
- ) : ( -
    - {notifications.map((n, i) => ( -
  • - {n.message} - - {new Date(n.created_at).toLocaleTimeString("fr-FR", { - hour: "2-digit", - minute: "2-digit", - })} - -
  • - ))} -
- )} -
+
+ {/* Panier */} + +
+ {notifications.length === 0 ? ( +
+ +

Aucune notification

+
+ ) : ( + notifications.map((n, i) => ( +
+ {n.message} + {formatNotifDate(n.created_at)} +
+ )) + )} +
+ + + )} + {/* ── Overlay ─────────────────────────────────── */} {isMenuOpen &&
}