mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
198 lines
6.7 KiB
JavaScript
198 lines
6.7 KiB
JavaScript
'use client'
|
|
import React, { useState, useEffect } from 'react';
|
|
import Sidebar from '@/components/Sidebar';
|
|
import { usePathname } from 'next/navigation';
|
|
import { useTranslations } from 'next-intl';
|
|
import Image from 'next/image';
|
|
import {
|
|
LayoutDashboard,
|
|
FileText,
|
|
School,
|
|
Users,
|
|
Award,
|
|
Calendar,
|
|
Settings,
|
|
LogOut,
|
|
Menu,
|
|
X
|
|
} from 'lucide-react';
|
|
import DropdownMenu from '@/components/DropdownMenu';
|
|
|
|
import Popup from '@/components/Popup';
|
|
import {
|
|
FE_ADMIN_HOME_URL,
|
|
FE_ADMIN_SUBSCRIPTIONS_URL,
|
|
FE_ADMIN_STRUCTURE_URL,
|
|
FE_ADMIN_DIRECTORY_URL,
|
|
FE_ADMIN_GRADES_URL,
|
|
FE_ADMIN_PLANNING_URL,
|
|
FE_ADMIN_SETTINGS_URL
|
|
} from '@/utils/Url';
|
|
|
|
import { disconnect } from '@/app/actions/authAction';
|
|
import { useSession } from 'next-auth/react';
|
|
import ProtectedRoute from '@/components/ProtectedRoute';
|
|
import { getGravatarUrl } from '@/utils/gravatar';
|
|
import Footer from '@/components/Footer';
|
|
import { getRightStr, RIGHTS } from '@/utils/rights';
|
|
import logger from '@/utils/logger';
|
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
|
|
|
export default function Layout({
|
|
children,
|
|
}) {
|
|
const t = useTranslations('sidebar');
|
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
|
const { data: session } = useSession();
|
|
const { selectedEstablishmentId, setSelectedEstablishmentId, profileRole, setProfileRole, establishments, user } = useEstablishment();
|
|
|
|
|
|
|
|
// Déplacer le reste du code ici...
|
|
const sidebarItems = {
|
|
"admin": { "id": "admin", "name": t('dashboard'), "url": FE_ADMIN_HOME_URL, "icon": LayoutDashboard },
|
|
"subscriptions": { "id": "subscriptions", "name": t('subscriptions'), "url": FE_ADMIN_SUBSCRIPTIONS_URL, "icon": FileText },
|
|
"structure": { "id": "structure", "name": t('structure'), "url": FE_ADMIN_STRUCTURE_URL, "icon": School },
|
|
"directory": { "id": "directory", "name": t('directory'), "url": FE_ADMIN_DIRECTORY_URL, "icon": Users },
|
|
"grades": { "id": "grades", "name": t('grades'), "url": FE_ADMIN_GRADES_URL, "icon": Award },
|
|
"planning": { "id": "planning", "name": t('events'), "url": FE_ADMIN_PLANNING_URL, "icon": Calendar },
|
|
"settings": { "id": "settings", "name": t('settings'), "url": FE_ADMIN_SETTINGS_URL, "icon": Settings }
|
|
};
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
|
|
|
const pathname = usePathname();
|
|
const currentPage = pathname.split('/').pop();
|
|
|
|
const headerTitle = sidebarItems[currentPage]?.name || t('dashboard');
|
|
|
|
const softwareName = "N3WT School";
|
|
const softwareVersion = `${process.env.NEXT_PUBLIC_APP_VERSION}`;
|
|
|
|
const handleDisconnect = () => {
|
|
setIsPopupVisible(true);
|
|
};
|
|
|
|
const confirmDisconnect = () => {
|
|
setIsPopupVisible(false);
|
|
disconnect();
|
|
};
|
|
|
|
const dropdownItems = [
|
|
{
|
|
type: 'info',
|
|
content: (
|
|
<div className="px-4 py-2">
|
|
<div className="font-medium">{user?.email || 'Utilisateur'}</div>
|
|
<div className="text-xs text-gray-400">{getRightStr(profileRole) || ''}</div>
|
|
</div>
|
|
)
|
|
},
|
|
{
|
|
type: 'separator',
|
|
content: <hr className="my-2 border-gray-200" />
|
|
},
|
|
{
|
|
type: 'item',
|
|
label: 'Déconnexion',
|
|
onClick: handleDisconnect,
|
|
icon: LogOut,
|
|
},
|
|
];
|
|
|
|
const toggleSidebar = () => {
|
|
setIsSidebarOpen(!isSidebarOpen);
|
|
};
|
|
|
|
useEffect(() => {
|
|
// Fermer la sidebar quand on change de page sur mobile
|
|
setIsSidebarOpen(false);
|
|
}, [pathname]);
|
|
|
|
return (
|
|
<ProtectedRoute requiredRight={RIGHTS.ADMIN}>
|
|
<div className="flex min-h-screen bg-gray-50 relative">
|
|
{/* Retirer la condition !isLoading car on gère déjà le chargement au début */}
|
|
{/* Sidebar avec hauteur forcée */}
|
|
<div
|
|
className={`md:block ${isSidebarOpen ? 'block' : 'hidden'} fixed md:relative inset-y-0 left-0 z-30 h-full`}
|
|
style={{ height: '100vh' }} // Force la hauteur à 100% de la hauteur de la vue
|
|
>
|
|
<Sidebar
|
|
establishments={establishments}
|
|
currentPage={currentPage}
|
|
items={Object.values(sidebarItems)}
|
|
onCloseMobile={toggleSidebar}
|
|
onEstablishmentChange={(establishmentId) => {
|
|
const parsedEstablishmentId = parseInt(establishmentId, 10);
|
|
setSelectedEstablishmentId(parsedEstablishmentId);
|
|
let roleIndex = session.user.roles.findIndex(role => role.establishment__id === parsedEstablishmentId)
|
|
if (roleIndex === -1) {
|
|
roleIndex = 0;
|
|
}
|
|
const role = session.user.roles[roleIndex].role_type;
|
|
setProfileRole(role);
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
{/* Overlay pour fermer la sidebar en cliquant à l'extérieur sur mobile */}
|
|
{isSidebarOpen && (
|
|
<div
|
|
className="fixed inset-0 bg-black bg-opacity-50 z-20 md:hidden"
|
|
onClick={toggleSidebar}
|
|
/>
|
|
)}
|
|
|
|
<div className="flex-1 flex flex-col">
|
|
{/* Header responsive */}
|
|
<header className="h-16 bg-white border-b border-gray-200 px-4 md:px-8 py-4 flex items-center justify-between z-10">
|
|
<div className="flex items-center">
|
|
<button
|
|
className="mr-4 md:hidden text-gray-600 hover:text-gray-900"
|
|
onClick={toggleSidebar}
|
|
aria-label="Toggle menu"
|
|
>
|
|
{isSidebarOpen ? <X size={24} /> : <Menu size={24} />}
|
|
</button>
|
|
<div className="text-lg md:text-xl font-semibold">{headerTitle}</div>
|
|
</div>
|
|
<DropdownMenu
|
|
buttonContent={
|
|
<Image
|
|
src={getGravatarUrl(user?.email)}
|
|
alt="Profile"
|
|
className="w-8 h-8 rounded-full cursor-pointer"
|
|
width={32}
|
|
height={32}
|
|
/>
|
|
}
|
|
items={dropdownItems}
|
|
buttonClassName=""
|
|
menuClassName="absolute right-0 mt-2 w-64 bg-white border border-gray-200 rounded shadow-lg"
|
|
/>
|
|
</header>
|
|
{/* Main Content */}
|
|
<div className="flex-1 flex flex-col">
|
|
{/* Content avec scroll si nécessaire */}
|
|
<div className="flex-1 overflow-auto p-4 md:p-6">
|
|
{children}
|
|
</div>
|
|
{/* Footer responsive */}
|
|
<Footer softwareName={softwareName} softwareVersion={softwareVersion} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Popup
|
|
visible={isPopupVisible}
|
|
message="Êtes-vous sûr(e) de vouloir vous déconnecter ?"
|
|
onConfirm={confirmDisconnect}
|
|
onCancel={() => setIsPopupVisible(false)}
|
|
/>
|
|
</ProtectedRoute>
|
|
);
|
|
}
|
|
|
|
|