89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
"use client";
|
|
import { useEffect, useState, useCallback } from "react";
|
|
import Sidebar from "./Sidebar";
|
|
|
|
export default function MobileSidebarOverlay() {
|
|
const [open, setOpen] = useState(false);
|
|
const [closing, setClosing] = useState(false);
|
|
const [clientInfo, setClientInfo] = useState<any>(null);
|
|
const [isStaff, setIsStaff] = useState(false);
|
|
|
|
const close = useCallback(() => {
|
|
setClosing(true);
|
|
setTimeout(() => {
|
|
setOpen(false);
|
|
setClosing(false);
|
|
}, 180); // Durée de l'animation
|
|
}, []);
|
|
|
|
const openEvt = useCallback(() => setOpen(true), []);
|
|
|
|
// Récupérer les infos du client et le statut staff
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
async function fetchUserInfo() {
|
|
try {
|
|
const res = await fetch('/api/me', { credentials: 'include', cache: 'no-store' });
|
|
if (!res.ok) throw new Error(String(res.status));
|
|
const data = await res.json();
|
|
if (!cancelled) {
|
|
// Transformer la réponse API en format clientInfo attendu par Sidebar
|
|
const clientInfo = {
|
|
id: data.active_org_id || '',
|
|
name: data.active_org_name || 'Organisation',
|
|
api_name: data.active_org_api_name || null,
|
|
user: data.user || null
|
|
};
|
|
setClientInfo(clientInfo);
|
|
setIsStaff(data.is_staff || false);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error fetching user info:', err);
|
|
}
|
|
}
|
|
fetchUserInfo();
|
|
return () => { cancelled = true; };
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const onOpen = () => openEvt();
|
|
const onClose = () => close();
|
|
window.addEventListener("open-mobile-sidebar" as any, onOpen as any);
|
|
window.addEventListener("close-mobile-sidebar" as any, onClose as any);
|
|
return () => {
|
|
window.removeEventListener("open-mobile-sidebar" as any, onOpen as any);
|
|
window.removeEventListener("close-mobile-sidebar" as any, onClose as any);
|
|
};
|
|
}, [openEvt, close]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-[1000] md:hidden" aria-modal="true" role="dialog">
|
|
<div
|
|
className={`absolute inset-0 bg-black/40 transition-opacity duration-180 ${closing ? 'opacity-0' : 'opacity-100'}`}
|
|
onClick={close}
|
|
/>
|
|
<div
|
|
className={`absolute top-0 left-0 h-full w-[86vw] max-w-[360px] bg-white shadow-xl border-r will-change-transform transition-transform duration-180 ease-out ${
|
|
closing ? '-translate-x-full' : 'translate-x-0 animate-slideIn'
|
|
}`}
|
|
role="complementary"
|
|
>
|
|
<div className="h-[var(--header-h)] border-b flex items-center justify-between px-3">
|
|
<div className="font-semibold">Menu</div>
|
|
<button onClick={close} className="px-3 py-1.5 rounded-md text-sm border hover:bg-slate-50">Fermer</button>
|
|
</div>
|
|
<div className="overflow-y-auto h-[calc(100%-var(--header-h))] p-2">
|
|
{/* Sidebar réutilisée en mobile */}
|
|
<Sidebar clientInfo={clientInfo} isStaff={isStaff} mobile onNavigate={close} />
|
|
</div>
|
|
</div>
|
|
<style jsx>{`
|
|
@keyframes slideIn { from { transform: translateX(-100%); } to { transform: translateX(0); } }
|
|
.animate-slideIn { animation: slideIn .18s ease-out; }
|
|
.duration-180 { transition-duration: 180ms; }
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|