feat: Gestion du planning [3]

This commit is contained in:
Luc SORIGNET
2025-05-03 15:12:17 +02:00
parent cb4fe74a9e
commit 58144ba0d0
39 changed files with 939 additions and 1864 deletions

View File

@ -43,7 +43,6 @@ export default function Layout({ children }) {
const { profileRole, establishments, user, clearContext } =
useEstablishment();
// Déplacer le reste du code ici...
const sidebarItems = {
admin: {
id: 'admin',
@ -144,12 +143,40 @@ export default function Layout({ children }) {
return (
<ProtectedRoute requiredRight={[RIGHTS.ADMIN, RIGHTS.TEACHER]}>
<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 */}
{/* Topbar */}
<header className="absolute top-0 left-0 right-0 h-16 bg-white border-b border-gray-200 px-4 md:px-8 flex items-center justify-between z-10 box-border">
<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>
{/* Sidebar */}
<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
className={`absolute top-16 bottom-16 left-0 z-30 w-64 bg-white border-r border-gray-200 box-border ${
isSidebarOpen ? 'block' : 'hidden md:block'
}`}
>
<Sidebar
establishments={establishments}
@ -159,7 +186,7 @@ export default function Layout({ children }) {
/>
</div>
{/* Overlay pour fermer la sidebar en cliquant à l'extérieur sur mobile */}
{/* Overlay for mobile */}
{isSidebarOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-50 z-20 md:hidden"
@ -167,48 +194,19 @@ export default function Layout({ children }) {
/>
)}
<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>
{/* Main container */}
<div className="absolute overflow-auto bg-gray-50 top-16 bottom-16 left-64 right-0 ">
{children}
</div>
</div>
{/* Footer */}
<Footer
softwareName={softwareName}
softwareVersion={softwareVersion}
/>
<Popup
visible={isPopupVisible}
message="Êtes-vous sûr(e) de vouloir vous déconnecter ?"

View File

@ -77,7 +77,7 @@ export default function DashboardPage() {
});
// Fetch des événements à venir
fetchUpcomingEvents()
fetchUpcomingEvents(selectedEstablishmentId)
.then((data) => {
setUpcomingEvents(data);
})

View File

@ -1,9 +1,10 @@
'use client';
import { PlanningProvider } from '@/context/PlanningContext';
import Calendar from '@/components/Calendar';
import EventModal from '@/components/EventModal';
import ScheduleNavigation from '@/components/ScheduleNavigation';
import { PlanningModes, PlanningProvider, RecurrenceType } from '@/context/PlanningContext';
import Calendar from '@/components/Calendar/Calendar';
import EventModal from '@/components/Calendar/EventModal';
import ScheduleNavigation from '@/components/Calendar/ScheduleNavigation';
import { useState } from 'react';
import { useEstablishment } from '@/context/EstablishmentContext';
export default function Page() {
const [isModalOpen, setIsModalOpen] = useState(false);
@ -14,13 +15,14 @@ export default function Page() {
end: '',
location: '',
planning: '', // Enlever la valeur par défaut ici
recurrence: 'none',
recursionType: RecurrenceType.NONE,
selectedDays: [],
recurrenceEnd: '',
recursionEnd: '',
customInterval: 1,
customUnit: 'days',
viewType: 'week', // Ajouter la vue semaine par défaut
});
const { selectedEstablishmentId } = useEstablishment();
const initializeNewEvent = (date = new Date()) => {
// S'assurer que date est un objet Date valide
@ -33,9 +35,11 @@ export default function Page() {
end: new Date(eventDate.getTime() + 2 * 60 * 60 * 1000).toISOString(),
location: '',
planning: '', // Ne pas définir de valeur par défaut ici non plus
recurrence: 'none',
recursionType: RecurrenceType.NONE,
selectedDays: [],
recurrenceEnd: '',
recursionEnd: new Date(
eventDate.getTime() + 2 * 60 * 60 * 1000
).toISOString(),
customInterval: 1,
customUnit: 'days',
});
@ -43,7 +47,8 @@ export default function Page() {
};
return (
<PlanningProvider>
<PlanningProvider establishmentId={selectedEstablishmentId} modeSet={PlanningModes.PLANNING}>
{/* <div className="flex h-full overflow-hidden"> */}
<div className="flex h-full overflow-hidden">
<ScheduleNavigation />
<Calendar

View File

@ -29,12 +29,12 @@ import FilesGroupsManagement from '@/components/Structure/Files/FilesGroupsManag
import { fetchRegistrationSchoolFileMasters } from '@/app/actions/registerFileGroupAction';
import logger from '@/utils/logger';
import { useEstablishment } from '@/context/EstablishmentContext';
import { PlanningProvider, PlanningModes } from '@/context/PlanningContext';
export default function Page() {
const [specialities, setSpecialities] = useState([]);
const [classes, setClasses] = useState([]);
const [teachers, setTeachers] = useState([]);
const [schedules, setSchedules] = useState([]);
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
const [registrationFees, setRegistrationFees] = useState([]);
@ -60,8 +60,6 @@ export default function Page() {
// Fetch data for classes
handleClasses();
// Fetch data for schedules
handleSchedules();
// Fetch data for registration discounts
handleRegistrationDiscounts();
@ -128,13 +126,6 @@ export default function Page() {
.catch((error) => logger.error('Error fetching classes:', error));
};
const handleSchedules = () => {
fetchSchedules()
.then((data) => {
setSchedules(data);
})
.catch((error) => logger.error('Error fetching schedules:', error));
};
const handleRegistrationDiscounts = () => {
fetchRegistrationDiscounts(selectedEstablishmentId)
@ -299,6 +290,8 @@ export default function Page() {
<ScheduleManagement
handleUpdatePlanning={handleUpdatePlanning}
classes={classes}
specialities={specialities}
teachers={teachers}
/>
</ClassesProvider>
),
@ -343,12 +336,12 @@ export default function Page() {
];
return (
<div className="p-4">
<>
<PlanningProvider establishmentId={selectedEstablishmentId} modeSet={PlanningModes.CLASS_SCHEDULE}>
<DjangoCSRFToken csrfToken={csrfToken} />
<SidebarTabs tabs={tabs} />
</PlanningProvider>
</>
<div className="w-full p-4">
<SidebarTabs tabs={tabs} />
</div>
</div>
);
}