refactor: adaptation mobile

This commit is contained in:
Luc SORIGNET
2025-03-01 14:54:25 +01:00
parent d62be6b309
commit 4b8f85e68d
6 changed files with 237 additions and 89 deletions

View File

@ -12,10 +12,12 @@ import {
Calendar,
Settings,
FileText,
LogOut
LogOut,
Menu,
X
} from 'lucide-react';
import DropdownMenu from '@/components/DropdownMenu';
import Logo from '@/components/Logo';
import Popup from '@/components/Popup';
import {
FE_ADMIN_HOME_URL,
@ -31,11 +33,13 @@ import { useSession } from 'next-auth/react';
import { fetchEstablishment } from '@/app/actions/schoolAction';
import ProtectedRoute from '@/components/ProtectedRoute';
import { getGravatarUrl } from '@/utils/gravatar';
import Footer from '@/components/Footer';
export default function Layout({
children,
}) {
const t = useTranslations('sidebar');
const t = useTranslations('sidebar');
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const sidebarItems = {
"admin": { "id": "admin", "name": t('dashboard'), "url": FE_ADMIN_HOME_URL, "icon": Home },
@ -50,7 +54,7 @@ export default function Layout({
const [isLoading, setIsLoading] = useState(false);
const [isPopupVisible, setIsPopupVisible] = useState(false);
const [user, setUser] = useState(null);
const { data: session } = useSession();
const { data: session } = useSession();
const pathname = usePathname();
const currentPage = pathname.split('/').pop();
@ -58,7 +62,7 @@ export default function Layout({
const headerTitle = sidebarItems[currentPage]?.name || t('dashboard');
const softwareName = "N3WT School";
const softwareVersion = `v${process.env.NEXT_PUBLIC_APP_VERSION}`;
const softwareVersion = `${process.env.NEXT_PUBLIC_APP_VERSION}`;
const handleDisconnect = () => {
setIsPopupVisible(true);
@ -77,6 +81,15 @@ export default function Layout({
},
];
const toggleSidebar = () => {
setIsSidebarOpen(!isSidebarOpen);
};
useEffect(() => {
// Fermer la sidebar quand on change de page sur mobile
setIsSidebarOpen(false);
}, [pathname]);
useEffect(() => {
setIsLoading(true);
fetchEstablishment()
@ -85,7 +98,7 @@ export default function Layout({
})
.catch(error => console.error('Error fetching establishment : ', error))
.finally(() => setIsLoading(false));
}, []);
}, []);
useEffect(() => {
const fetchUser = async () => {
@ -99,54 +112,79 @@ export default function Layout({
}, [session]);
return (
<ProtectedRoute>
{!isLoading && (
<div className="flex min-h-screen bg-gray-50">
<Sidebar establishment={establishment} currentPage={currentPage} items={Object.values(sidebarItems)} className="h-full" />
<div className="flex flex-col flex-1">
{/* Header - h-16 = 64px */}
<header className="h-16 bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between z-9">
<div className="text-xl font-semibold">{headerTitle}</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-48 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">
{children}
</div>
{/* Footer - h-16 = 64px */}
<footer className="h-16 bg-white border-t border-gray-200 px-8 py-4 flex items-center justify-between">
<div className="text-sm font-light">
<span>&copy; {new Date().getFullYear()} N3WT-INNOV Tous droits réservés.</span>
<div className="text-sm font-light">{softwareName} - {softwareVersion}</div>
</div>
<Logo className="w-8 h-8" />
</footer>
<ProtectedRoute>
{!isLoading && (
<div className="flex min-h-screen bg-gray-50 relative">
{/* 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
establishment={establishment}
currentPage={currentPage}
items={Object.values(sidebarItems)}
onCloseMobile={toggleSidebar}
/>
</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-48 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>
)}
<Popup
visible={isPopupVisible}
message="Êtes-vous sûr(e) de vouloir vous déconnecter ?"
onConfirm={confirmDisconnect}
onCancel={() => setIsPopupVisible(false)}
/>
</ProtectedRoute>
</div>
)}
<Popup
visible={isPopupVisible}
message="Êtes-vous sûr(e) de vouloir vous déconnecter ?"
onConfirm={confirmDisconnect}
onCancel={() => setIsPopupVisible(false)}
/>
</ProtectedRoute>
);
}

View File

@ -66,48 +66,45 @@ export default function Layout({
<ProtectedRoute>
<div className="flex flex-col min-h-screen bg-gray-50">
{/* Entête */}
<header className="bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between fixed top-0 left-0 right-0 z-10">
<header className="bg-white border-b border-gray-200 px-4 py-2 md:px-8 md:py-4 flex items-center justify-between fixed top-0 left-0 right-0 z-10">
<div className="flex items-center space-x-2">
<Logo className="h-8 w-8" /> {/* Utilisation du composant Logo */}
<Logo className="h-6 w-6 md:h-8 md:w-8" /> {/* Utilisation du composant Logo */}
<div className="text-xl font-semibold">Accueil</div>
<div className="text-lg md:text-xl font-semibold">Accueil</div>
</div>
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2 md:space-x-4">
<button
className="p-2 rounded-full hover:bg-gray-200"
className="p-1 md:p-2 rounded-full hover:bg-gray-200"
onClick={() => { router.push(FE_PARENTS_HOME_URL); }} // Utilisation de router pour revenir à l'accueil parent
>
<Home />
<Home className="h-5 w-5 md:h-6 md:w-6" />
</button>
<div className="relative">
<button
className="p-2 rounded-full hover:bg-gray-200"
className="p-1 md:p-2 rounded-full hover:bg-gray-200"
onClick={() => { router.push(FE_PARENTS_MESSAGERIE_URL); }} // Utilisation de router
>
<MessageSquare />
<MessageSquare className="h-5 w-5 md:h-6 md:w-6" />
</button>
{messages.length > 0 && (
<span className="absolute top-0 right-0 block h-2 w-2 rounded-full bg-emerald-600"></span>
)}
</div>
<DropdownMenu
buttonContent={<User />}
buttonContent={<User className="h-5 w-5 md:h-6 md:w-6" />}
items={[
{ label: 'Se déconnecter', icon: LogOut, onClick: handleDisconnect },
{ label: 'Settings', icon: Settings , onClick: () => { router.push(FE_PARENTS_SETTINGS_URL); } }
]}
buttonClassName="p-2 rounded-full hover:bg-gray-200"
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg"
buttonClassName="p-1 md:p-2 rounded-full hover:bg-gray-200"
menuClassName="absolute right-0 mt-2 w-36 md:w-48 bg-white border border-gray-200 rounded-md shadow-lg"
/>
</div>
</header>
{/* Content */}
<div className="pt-20 p-8 flex-1"> {/* Ajout de flex-1 pour utiliser toute la hauteur disponible */}
<div className="pt-16 md:pt-20 p-4 md:p-8 flex-1"> {/* Ajout de flex-1 pour utiliser toute la hauteur disponible */}
{children}
</div>
</div>

View File

@ -2,8 +2,8 @@
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import Table from '@/components/Table';
import { Edit } from 'lucide-react';
import StatusLabel from '@/components/StatusLabel';
import { Edit, Users } from 'lucide-react';
import StatusLabel from '@/components/StatusLabel';
import { FE_PARENTS_EDIT_INSCRIPTION_URL } from '@/utils/Url';
import { fetchChildren } from '@/app/actions/subscriptionAction';
import logger from '@/utils/logger';
@ -11,10 +11,11 @@ import { useSession } from 'next-auth/react';
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
export default function ParentHomePage() {
const [actions, setActions] = useState([]);
const [children, setChildren] = useState([]);
const { data: session, status } = useSession();
const [userId, setUserId] = useState(null);
const [currentPage, setCurrentPage] = useState(1);
const router = useRouter();
@ -61,29 +62,62 @@ export default function ParentHomePage() {
}
};
return (
<div>
<div>
<h2 className="text-xl font-semibold mb-4">Dernières actions à effectuer</h2>
<Table data={actions} columns={actionColumns} itemsPerPage={5} />
</div>
// Définir les colonnes du tableau
const childrenColumns = [
{ name: 'Nom', transform: (row) => `${row.student.last_name} ${row.student.first_name}` },
{
name: 'Statut',
transform: (row) => (
<div className="flex justify-center items-center">
<StatusLabel status={row.status} showDropdown={false}/>
</div>
)
},
{
name: 'Actions',
transform: (row) => (
<div className="flex justify-center">
<button
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
onClick={(e) => {
e.stopPropagation();
handleEdit(row.student.id);
}}
aria-label="Modifier"
>
<Edit className="h-5 w-5" />
</button>
</div>
)
}
];
const itemsPerPage = 5;
const totalPages = Math.ceil(children.length / itemsPerPage) || 1;
const handlePageChange = (newPage) => {
setCurrentPage(newPage);
};
return (
<div className="px-2 py-4 md:px-4 max-w-full">
<div>
<h2 className="text-xl font-semibold mb-4">Enfants</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{children.map((child) => (
<div key={child.student.id} className={`border p-4 rounded shadow ${getShadowColor(child.status)}`}>
<div className="flex justify-between items-center">
<h3 className="text-lg font-semibold">{child.student.last_name} {child.student.first_name}</h3>
<Edit className="cursor-pointer" onClick={() => handleEdit(child.student.id)} />
</div>
<StatusLabel status={child.status } showDropdown={false}/>
</div>
))}
<h2 className="text-xl font-semibold mb-3 px-1 flex items-center gap-2">
<Users className="h-6 w-6 text-emerald-600" />
Enfants
</h2>
<div className="overflow-x-auto">
<Table
data={children}
columns={childrenColumns}
itemsPerPage={itemsPerPage}
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
defaultTheme="bg-gray-50"
/>
</div>
</div>
</div>
);
}