diff --git a/src/app/(dashboard)/reservation/PlaceUI.jsx b/src/app/(dashboard)/reservation/PlaceUI.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..f3471558563f34ed71d9f6a2147178715fb10023
--- /dev/null
+++ b/src/app/(dashboard)/reservation/PlaceUI.jsx
@@ -0,0 +1,140 @@
+"use client"
+import React, { useContext, useMemo, useState } from 'react'
+import { ReservationContext } from './page'
+import fetchRequest from '@/app/lib/fetchRequest'
+import { useNotification } from '@/context/NotificationContext'
+import Cookies from 'js-cookie';
+import { decrypt } from '@/app/lib/session';
+import ConfirmationModal from '@/app/ui/ConfirmationModal'
+const PlaceUI = ({ id }) => {
+ const { allPlaces, selectedDate, bookedPlaces, setBookedPlaces } = useContext(ReservationContext)
+ const { toggleNotification } = useNotification()
+ const [isOpenBooking, setIsOpenBooking] = useState(false)
+ const [isOpenCanceling, setIsOpenCanceling] = useState(false)
+ const [authenticatedUserData, setAuthenticatedUserData] = useState(null)
+
+ const cookie = Cookies.get("session")
+ const getUserData = async () => {
+ try {
+ if (cookie) {
+ const data = await decrypt(cookie)
+ setAuthenticatedUserData(data)
+ }
+ } catch (error) {
+ console.log(error)
+ }
+ }
+ useMemo(getUserData, [cookie])
+ const place = allPlaces?.find((place) => place.id === id)
+ const bookedPlace = bookedPlaces?.find((bookedPlace) => bookedPlace.id_place === id)
+ const hasPlace = bookedPlaces?.some((p) => p.id_user === authenticatedUserData?.sessionData?.user_id)
+ const handleBooking = (event) => {
+ event.stopPropagation()
+ if (hasPlace) {
+ toggleNotification({
+ visible: true,
+ message: "Veuillez annuler votre réservation pour réserver une nouvelle place .",
+ type: "warning"
+ })
+ }
+ else setIsOpenBooking(true)
+ }
+ const handleBookingConfirmation = async () => {
+ const { isSuccess, errors, data } = await fetchRequest('/zoaning/reservations/', {
+ method: "POST", body: JSON.stringify(
+ {
+ "presence": false,
+ "date": selectedDate,
+ "id_place": id
+ }
+ )
+ });
+ if (isSuccess) {
+ console.log(data);
+ setBookedPlaces([...bookedPlaces, data])
+ toggleNotification({
+ visible: true,
+ message: "La réservation a été enregistrer avec succés",
+ type: "success"
+ })
+ setIsOpenBooking(false)
+ } else {
+ if (errors.type === "ValidationError" && errors.detail?.non_field_errors && errors.detail?.non_field_errors[0]?.indexOf("date, id_place must make a unique set") !== -1) {
+ toggleNotification({
+ visible: true,
+ message: "La place a été déjà réservée par votre collègue",
+ type: "warning"
+ })
+ }
+ else toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ console.log(errors)
+ }
+ }
+ const handleCancelingConfirmation = async (idReservation) => {
+ const { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/reservations/${idReservation}`, {
+ method: "DELETE"
+ });
+ if (isSuccess) {
+ console.log(data);
+ setBookedPlaces(bookedPlaces.filter((element) => element.id !== idReservation))
+ toggleNotification({
+ visible: true,
+ message: "La réservation a été annuler avec succés",
+ type: "success"
+ })
+ setIsOpenCanceling(false)
+ } else {
+ if (status === 404)
+ toggleNotification({
+ visible: true,
+ message: "La réservation n'existe pas",
+ type: "warning"
+ })
+ else toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ console.log(errors)
+ }
+ }
+
+ const handleCanceling = (event) => {
+ event.stopPropagation()
+ setIsOpenCanceling(true)
+ }
+ const closeCancelingPopup = (event) => {
+ event.stopPropagation()
+ setIsOpenCanceling(false)
+ }
+ const closeConfirmationPopup = (event) => {
+ event.stopPropagation()
+ setIsOpenBooking(false)
+ }
+
+
+ if (authenticatedUserData)
+ if (place)
+ if (bookedPlace)
+ if (bookedPlace.id_user === authenticatedUserData.sessionData?.user_id)
+ return
+
{place.project_name || ""}
+
handleCancelingConfirmation(bookedPlace.id)} onClose={closeCancelingPopup} />
+
+ else return
+
{place.project_name || ""}
+
+ else return
+
{place.project_name || ""}
+
+
+ else return
+ else return <>>
+}
+
+
+export default PlaceUI
\ No newline at end of file
diff --git a/src/app/(dashboard)/reservation/TableUI.jsx b/src/app/(dashboard)/reservation/TableUI.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8c77ad5b0e81c6cc7e2c99b2e1b91b3ebeb66fe9
--- /dev/null
+++ b/src/app/(dashboard)/reservation/TableUI.jsx
@@ -0,0 +1,47 @@
+import React from 'react'
+import PlaceUI from './PlaceUI';
+
+const TableUI = ({ id, numero, places }) => {
+ function groupConsecutive(arr) {
+
+ arr = arr.sort((a, b) => a.id - b.id)
+ if (arr.length === 0) {
+ return [];
+ }
+
+ const grouped = [];
+ var counter = 0
+ while (counter < arr.length) {
+ if (counter + 1 < arr.length) {
+ grouped.push([arr[counter], arr[counter + 1]])
+ }
+ else {
+ grouped.push([arr[counter]])
+ }
+ counter += 2;
+ }
+
+ return grouped;
+ }
+ const processedPlaces = groupConsecutive(places).reverse()
+
+
+ if (!processedPlaces || processedPlaces.length === 0) return <>>
+ return (
+
+ {processedPlaces.map((element, index) => {
+ return
+
+
+ {(element.length > 1) &&
}
+
+ })}
+
+ )
+}
+
+export default TableUI
\ No newline at end of file
diff --git a/src/app/(dashboard)/reservation/ZoneUI.jsx b/src/app/(dashboard)/reservation/ZoneUI.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..48ae7d19663b3c0b7c308a4271a2252e047b92c5
--- /dev/null
+++ b/src/app/(dashboard)/reservation/ZoneUI.jsx
@@ -0,0 +1,17 @@
+import React from 'react'
+import TableUI from './TableUI'
+
+const ZoneUI = ({ id, tables, nom }) => {
+ return (
+
+
{nom}
+
+ {tables.map((table) => {
+ return
+ })}
+
+
+ )
+}
+
+export default ZoneUI
\ No newline at end of file
diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..37921d1a0f944967bd26d7a1809c4ddb1d6f247d
--- /dev/null
+++ b/src/app/(dashboard)/reservation/page.jsx
@@ -0,0 +1,215 @@
+'use client'
+
+import React, { useEffect, useState } from 'react'
+import ZoneUI from './ZoneUI'
+import fetchRequest from '@/app/lib/fetchRequest'
+import Loader from '@/components/Loader/Loader'
+import { useNotification } from '@/context/NotificationContext'
+
+export const ReservationContext = React.createContext()
+
+const Reservation = () => {
+ const [isLoadingData, setIsLoadingData] = useState(false)
+ const { toggleNotification } = useNotification()
+ const [date, setDate] = useState({ day: null, week: null })
+ const [isLoadingSelectsData, setIsLoadingSelectsData] = useState(true)
+ const [floors, setFloors] = useState([])
+ const [projectsData, setProjectsData] = useState([])
+ const [currentDateData, setCurrentDateData] = useState(null)
+ const [datesData, setDatesData] = useState(null)
+ const [bookedPlaces, setBookedPlaces] = useState([])
+ useEffect(() => {
+ const getPlan = async () => {
+ try {
+ const { isSuccess, errors, data } = await fetchRequest('/zoaning/etage-zone-table-place/')
+ setIsLoadingSelectsData(false)
+ if (isSuccess) {
+ setFloors(data)
+ } else {
+ console.log(errors)
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ }
+ } catch (error) {
+ console.log(error)
+ }
+ }
+ getPlan()
+ }, [])
+
+
+
+ useEffect(() => {
+ const getSyncData = async () => {
+ const getUserPlan = async () => {
+ const { isSuccess, errors, data } = await fetchRequest(`/zoaning/user-projects-zones-places/${date.week}/${date.day}`)
+ if (isSuccess) {
+ setProjectsData(data)
+ } else {
+ console.log(errors)
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ }
+ }
+ const getBookedPlaces = async () => {
+ const { isSuccess, errors, data } = await fetchRequest(`/zoaning/reservations/date/${date.date}/`)
+ if (isSuccess) {
+ console.log("booked places : :", data)
+ setBookedPlaces(data.map((element) => ({ ...element, id_place: element.id_place.id, presence: element.presence, id_user: element.id_user.id })))
+ }
+ else {
+ console.log(errors)
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ }
+ }
+ setIsLoadingData(true)
+ await Promise.all([getUserPlan(), getBookedPlaces()])
+ setIsLoadingData(false)
+ }
+ if (date.week && date.day) getSyncData()
+ else {
+ setProjectsData([])
+ setBookedPlaces([])
+ }
+
+ }, [date.week, date.day])
+
+ useEffect(() => {
+ const getCurrentDateData = async () => {
+ const { isSuccess, errors, data } = await fetchRequest('/zoaning/current-date/');
+ if (isSuccess) {
+ setCurrentDateData(data)
+ } else {
+ toggleNotification({
+ visible: true,
+ message: "Failed to fetch current date",
+ type: "error"
+ })
+ }
+ }
+ const YearCalendar = async () => {
+ const { isSuccess, errors, data } = await fetchRequest('/zoaning/dates/');
+ if (isSuccess) {
+ console.log("dates data", data)
+ setDatesData(data)
+ } else {
+ console.log(errors)
+ toggleNotification({
+ visible: true,
+ message: "Failed to fetch current date",
+ type: "error"
+ })
+ }
+ }
+ const getSyncData = async () => {
+ await Promise.all([getCurrentDateData(), YearCalendar()])
+ }
+ getSyncData()
+ }, [])
+
+ const handleChangeDate = (event) => {
+ if (event.target.value) {
+ const dateSelected = JSON.parse(event.target.value);
+ console.log("weekMonthly", dateSelected.weekMonthly);
+ console.log("day", dateSelected.day);
+ setDate({ day: dateSelected.day, week: dateSelected.weekMonthly, date: dateSelected.date })
+ }
+ else {
+ setDate({ day: null, week: null })
+ }
+ }
+
+
+ const concernedZones = projectsData?.map((element) => element.zones.map((zone) => zone.id)).flatMap((element) => element) || []
+ const concernedFloors = floors?.filter((element) => element.zones.map((zone) => zone.id).some((element) => concernedZones.includes(element))).map(element => element.id) || []
+ const allPlaces = projectsData?.map((project) => project.zones.map((zone) => zone.places.map((place) => ({ ...place, project_name: project.project_name, project_id: project.project_id })))).flat(2) || []
+ console.log("all places", allPlaces)
+ if (isLoadingSelectsData)
+ return
+
+
+
+ const currentMonth = new Date().getMonth() + 1
+ // filter dates from now to 2 weeks later
+ const filteredDatesData = datesData?.filter((element) => {
+ const date = new Date(element.date)
+ const month = date.getMonth() + 1
+ return (month === currentMonth) && (date.getDate() >= new Date().getDate() && date.getDate() <= new Date().getDate() + 14)
+ })
+ return (
+
+
+
+
+
+
+
+
+
+
+
Réservé par un collègue
+
+
+
+ {(!isLoadingData)
+ ? <>
+ {(floors.filter((element) => concernedFloors.includes(element.id))?.length > 0) &&
+
+
}
+
+
+ {floors.filter((element) => concernedFloors.includes(element.id)).map((floor) => {
+ return
+
Etage {floor.numero}
+ {floor.zones.filter((element) => concernedZones.includes(element.id)).map((zone, index) => {
+ return
+ })}
+
+ })}
+ {floors.filter((element) => concernedFloors.includes(element.id)).length === 0
+ &&
+
Un exemple d'un message à l'utilisateur
+
}
+
+
+ >
+ :
+
+
+ }
+
+ )
+}
+
+export default Reservation
\ No newline at end of file
diff --git a/src/app/ui/ConfirmationModal.jsx b/src/app/ui/ConfirmationModal.jsx
index 1c667ac3e0990319217a3c6136941e21731b095b..ad5e6cdd61dac10323164be0121811edb0eb5421 100644
--- a/src/app/ui/ConfirmationModal.jsx
+++ b/src/app/ui/ConfirmationModal.jsx
@@ -1,8 +1,29 @@
+"use client"
+
import React from 'react';
-const ConfirmationModal = ({ isOpen, onClose, onConfirm, message }) => {
+const ConfirmationModal = ({ isOpen, onClose, onConfirm, message, type }) => {
if (!isOpen) return null;
-
+ else if (type === "create") return
+
+
Confirmation
+
{message}
+
+
+
+
+
+
return (
@@ -13,13 +34,13 @@ const ConfirmationModal = ({ isOpen, onClose, onConfirm, message }) => {
onClick={onClose}
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-md"
>
- Cancel
+ Annuler
diff --git a/src/app/ui/SideBar.jsx b/src/app/ui/SideBar.jsx
index 90f7df69a526a4a13afcb58546935f8216307676..4bab50a6d919bff0a44c48f3424867953bd6e4b7 100644
--- a/src/app/ui/SideBar.jsx
+++ b/src/app/ui/SideBar.jsx
@@ -25,6 +25,11 @@ const SideBar = () => {
link: "/projects"
, icon:
},
+ {
+ label: "Réservation",
+ link: "/reservation",
+ icon:
+ },
{
label: "Planning",
link: "/planning"