From 3bcac3d0bd9ccead12dfe36af1c86a370f7eefdc Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Wed, 12 Jun 2024 09:15:37 +0100 Subject: [PATCH 1/7] added consultaions des reservations --- .../consultation-reservations/PlaceUI.jsx | 53 +++++ .../consultation-reservations/TableUI.jsx | 47 +++++ .../consultation-reservations/ZoneUI.jsx | 17 ++ .../consultation-reservations/page.jsx | 188 ++++++++++++++++++ 4 files changed, 305 insertions(+) create mode 100644 src/app/(dashboard)/consultation-reservations/PlaceUI.jsx create mode 100644 src/app/(dashboard)/consultation-reservations/TableUI.jsx create mode 100644 src/app/(dashboard)/consultation-reservations/ZoneUI.jsx create mode 100644 src/app/(dashboard)/consultation-reservations/page.jsx diff --git a/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx b/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx new file mode 100644 index 0000000..b0b7ce2 --- /dev/null +++ b/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx @@ -0,0 +1,53 @@ +import React, { useContext, useState } from 'react'; +import { ReservationContext } from './page'; + +const colors = [ + '#FF5733', '#50cc65', '#3357FF', '#c9ce41', '#FF33A8', + '#24c4b8', '#FF8333', '#8333FF', '#3383FF', '#83FF33', + '#FF3383', '#2ac567', '#FF33F3', '#4cb7be', '#F333FF', + '#cdd927', '#FF33A8', '#80d3cd', '#FF8333', '#8333FF', + '#3383FF', '#92d965', '#FF3383', '#4ebb78', '#FF33F3', +]; + +const getColorForProject = (projectId) => { + const index = projectId % colors.length; + return colors[index]; +}; + +const PlaceUI = ({ id }) => { + const { allPlaces, bookedPlaces } = useContext(ReservationContext); + const [showTooltip, setShowTooltip] = useState(false); + const place = allPlaces?.find((place) => place.id === id); + const bookedPlace = bookedPlaces?.find((place) => place.id_place === id); + + if (place) { + const backgroundColor = getColorForProject(place.project_id); // Assuming place object has a project_id field + return ( +
setShowTooltip(true)} + onMouseLeave={() => setShowTooltip(false)} + > +

+ {place?.project_name || ""} +

+ {!showTooltip && bookedPlace && ( +
+

First Name: {bookedPlace.first_name}

+

Last Name: {bookedPlace.last_name}

+

Role: {bookedPlace.role}

+

Presence: {bookedPlace.presence}

+

Created At: {new Date(bookedPlace.created_at).toLocaleString()}

+
+ )} +
+ ); + } else { + return ( +
+ ); + } +}; + +export default PlaceUI; diff --git a/src/app/(dashboard)/consultation-reservations/TableUI.jsx b/src/app/(dashboard)/consultation-reservations/TableUI.jsx new file mode 100644 index 0000000..8c77ad5 --- /dev/null +++ b/src/app/(dashboard)/consultation-reservations/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)/consultation-reservations/ZoneUI.jsx b/src/app/(dashboard)/consultation-reservations/ZoneUI.jsx new file mode 100644 index 0000000..48ae7d1 --- /dev/null +++ b/src/app/(dashboard)/consultation-reservations/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)/consultation-reservations/page.jsx b/src/app/(dashboard)/consultation-reservations/page.jsx new file mode 100644 index 0000000..8b71f45 --- /dev/null +++ b/src/app/(dashboard)/consultation-reservations/page.jsx @@ -0,0 +1,188 @@ +'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/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, created_at: element.created_at, first_name: element.id_user.first_name, last_name: element.id_user.last_name, role: element.id_user.role.name }))) + } + 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 ( +
+
+ +
+ {(!isLoadingData) + ? <> +
+ + {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 -- GitLab From d702bfe1676d2ac52698f5bd6e2e67861d6445de Mon Sep 17 00:00:00 2001 From: Raed BOUAFIF Date: Wed, 12 Jun 2024 12:06:50 +0100 Subject: [PATCH 2/7] feature consultation with etage filter --- .../consultation-reservations/page.jsx | 70 ++++++++++++++++--- .../(dashboard)/etage/AddEtageComponent.jsx | 13 ++-- src/app/(dashboard)/etage/page.jsx | 8 ++- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src/app/(dashboard)/consultation-reservations/page.jsx b/src/app/(dashboard)/consultation-reservations/page.jsx index 8b71f45..5e0b8ae 100644 --- a/src/app/(dashboard)/consultation-reservations/page.jsx +++ b/src/app/(dashboard)/consultation-reservations/page.jsx @@ -18,6 +18,9 @@ const Reservation = () => { const [currentDateData, setCurrentDateData] = useState(null) const [datesData, setDatesData] = useState(null) const [bookedPlaces, setBookedPlaces] = useState([]) + const [ etages, setEtages ] = useState([]) + const [ isLoadingEtages, setIsLoadingEtages ] = useState(true) + const [ selectedEtage, setSelectedEtage ] = useState(null) useEffect(() => { const getPlan = async () => { try { @@ -40,6 +43,23 @@ const Reservation = () => { getPlan() }, []) + useEffect(() => { + const getAllEtages = async () => { + try { + const { isSuccess, errors, data } = await fetchRequest('/zoaning/etages/', { method: 'GET' }) + setIsLoadingEtages(false) + if (isSuccess) { + setEtages(data) + } else { + setEtages([]) + } + } catch (error) { + setIsLoadingEtages(false) + console.log(error) + } + } + getAllEtages() + }, []) useEffect(() => { @@ -123,17 +143,24 @@ const Reservation = () => { console.log("weekMonthly", dateSelected.weekMonthly); console.log("day", dateSelected.day); setDate({ day: dateSelected.day, week: dateSelected.weekMonthly, date: dateSelected.date }) + setSelectedEtage(null) } else { setDate({ day: null, week: null }) + setSelectedEtage(null) } } + const handleChangeEtage = (event) =>{ + const etage_id = event.target.value + if(selectedEtage !== etage_id){ + setSelectedEtage(etage_id) + } + } 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
@@ -146,6 +173,9 @@ const Reservation = () => { const month = date.getMonth() + 1 return (month === currentMonth) && (date.getDate() >= new Date().getDate() && date.getDate() <= new Date().getDate() + 14) }) + + console.log(floors) + console.log(selectedEtage) return (
@@ -157,19 +187,41 @@ const Reservation = () => { ))} +
{(!isLoadingData) ? <>
- {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 - })} -
- })} + { + (selectedEtage) ? + floors.filter((element) => + {if (concernedFloors.includes(element.id) && element.id == selectedEtage) {return element}} + ).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)).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

diff --git a/src/app/(dashboard)/etage/AddEtageComponent.jsx b/src/app/(dashboard)/etage/AddEtageComponent.jsx index 42cfdca..aaf361f 100644 --- a/src/app/(dashboard)/etage/AddEtageComponent.jsx +++ b/src/app/(dashboard)/etage/AddEtageComponent.jsx @@ -56,16 +56,13 @@ const AddEtageComponent = ({ etagesState }) => { } return ( -
-
- Nouveau étage: -
- -
- +
+ Nouveau étage: +
+
+
- ) } diff --git a/src/app/(dashboard)/etage/page.jsx b/src/app/(dashboard)/etage/page.jsx index ca6ba13..d969987 100644 --- a/src/app/(dashboard)/etage/page.jsx +++ b/src/app/(dashboard)/etage/page.jsx @@ -80,8 +80,8 @@ const Etage = () => { }; return ( -
-
+
+

Liste des étages

@@ -108,7 +108,9 @@ const Etage = () => { }
- +
+ +
Date: Wed, 12 Jun 2024 12:18:57 +0100 Subject: [PATCH 3/7] finished adding consultation reservations --- .../consultation-reservations/PlaceUI.jsx | 12 +++++++---- .../consultation-reservations/TableUI.jsx | 21 ++++++++++++------- src/app/globals.css | 8 +++++++ src/app/ui/LogoutButton.js | 18 ++++++++-------- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx b/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx index b0b7ce2..7e294fa 100644 --- a/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx +++ b/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx @@ -14,7 +14,7 @@ const getColorForProject = (projectId) => { return colors[index]; }; -const PlaceUI = ({ id }) => { +const PlaceUI = ({ id, isTop }) => { const { allPlaces, bookedPlaces } = useContext(ReservationContext); const [showTooltip, setShowTooltip] = useState(false); const place = allPlaces?.find((place) => place.id === id); @@ -24,7 +24,8 @@ const PlaceUI = ({ id }) => { const backgroundColor = getColorForProject(place.project_id); // Assuming place object has a project_id field return (
setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)} @@ -32,8 +33,11 @@ const PlaceUI = ({ id }) => {

{place?.project_name || ""}

- {!showTooltip && bookedPlace && ( -
+ {bookedPlace && ( +
+ )} + {showTooltip && bookedPlace && ( +

First Name: {bookedPlace.first_name}

Last Name: {bookedPlace.last_name}

Role: {bookedPlace.role}

diff --git a/src/app/(dashboard)/consultation-reservations/TableUI.jsx b/src/app/(dashboard)/consultation-reservations/TableUI.jsx index 8c77ad5..ce702a2 100644 --- a/src/app/(dashboard)/consultation-reservations/TableUI.jsx +++ b/src/app/(dashboard)/consultation-reservations/TableUI.jsx @@ -1,7 +1,7 @@ import React from 'react' import PlaceUI from './PlaceUI'; -const TableUI = ({ id, numero, places }) => { +const TableUI = ({id, numero, places}) => { function groupConsecutive(arr) { arr = arr.sort((a, b) => a.id - b.id) @@ -14,8 +14,7 @@ const TableUI = ({ id, numero, places }) => { while (counter < arr.length) { if (counter + 1 < arr.length) { grouped.push([arr[counter], arr[counter + 1]]) - } - else { + } else { grouped.push([arr[counter]]) } counter += 2; @@ -23,6 +22,7 @@ const TableUI = ({ id, numero, places }) => { return grouped; } + const processedPlaces = groupConsecutive(places).reverse() @@ -31,12 +31,17 @@ const TableUI = ({ id, numero, places }) => {
{processedPlaces.map((element, index) => { return
-
- +
+
-
- {(element.length > 1) &&
- +
+ {(element.length > 1) &&
+
}
})} diff --git a/src/app/globals.css b/src/app/globals.css index 20a7710..771e450 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -201,4 +201,12 @@ width: 0; transition-duration: 300ms } +} + +.tooltip { + @apply invisible absolute; +} + +.has-tooltip:hover .tooltip { + @apply visible z-50; } \ No newline at end of file diff --git a/src/app/ui/LogoutButton.js b/src/app/ui/LogoutButton.js index 1ffc0e4..5dbae38 100644 --- a/src/app/ui/LogoutButton.js +++ b/src/app/ui/LogoutButton.js @@ -3,30 +3,30 @@ import Cookies from 'js-cookie'; import fetchRequest from "@/app/lib/fetchRequest"; import LogoutIcon from "@/static/image/svg/logout.svg" -const LogoutButton = ({ isButton = false }) => { + +const LogoutButton = ({isButton = false}) => { const logout = async () => { const response = await fetchRequest(`/logout`, { method: 'GET' }); console.log(response); - if (response.isSuccess) { - console.log('logout successful'); - Cookies.remove('session'); - window.location.href = '/'; - } + console.log('logout successful'); + Cookies.remove('session'); + window.location.href = '/auth/login'; }; if (isButton) return
-
-

+

+

Se déconnecter

return ( ); }; -- GitLab From fac22b5d1ae80bd9c311584dad2a76b4271463fb Mon Sep 17 00:00:00 2001 From: Baligh ZOGHLAMI Date: Wed, 12 Jun 2024 13:11:28 +0100 Subject: [PATCH 4/7] add presence with geolocation conditions --- src/app/(dashboard)/reservation/PlaceUI.jsx | 24 +-- .../reservation/PresenceButton.jsx | 105 +++++++++++++ src/app/(dashboard)/reservation/page.jsx | 140 ++++++++++++++++-- src/app/(dashboard)/role/RoleTableRows.jsx | 1 + 4 files changed, 237 insertions(+), 33 deletions(-) create mode 100644 src/app/(dashboard)/reservation/PresenceButton.jsx diff --git a/src/app/(dashboard)/reservation/PlaceUI.jsx b/src/app/(dashboard)/reservation/PlaceUI.jsx index f347155..976bc4b 100644 --- a/src/app/(dashboard)/reservation/PlaceUI.jsx +++ b/src/app/(dashboard)/reservation/PlaceUI.jsx @@ -1,35 +1,21 @@ "use client" -import React, { useContext, useMemo, useState } from 'react' +import React, { useContext, 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 { allPlaces, selectedDate, bookedPlaces, setBookedPlaces, authenticatedUserData, hasPlace } = 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) + console.log(hasPlace); const handleBooking = (event) => { event.stopPropagation() + if (hasPlace && hasPlace.presence) return; if (hasPlace) { toggleNotification({ visible: true, @@ -121,7 +107,7 @@ const PlaceUI = ({ id }) => { if (place) if (bookedPlace) if (bookedPlace.id_user === authenticatedUserData.sessionData?.user_id) - return
+ return
{ }} className={`${bookedPlace.presence ? "bg-green-500/80" : "bg-amber-500/80 cursor-pointer"} absolute items-center flex justify-center h-full px-[2px] w-full text-sm`}>

{place.project_name || ""}

handleCancelingConfirmation(bookedPlace.id)} onClose={closeCancelingPopup} />
diff --git a/src/app/(dashboard)/reservation/PresenceButton.jsx b/src/app/(dashboard)/reservation/PresenceButton.jsx new file mode 100644 index 0000000..ae58677 --- /dev/null +++ b/src/app/(dashboard)/reservation/PresenceButton.jsx @@ -0,0 +1,105 @@ +"use client" +import fetchRequest from '@/app/lib/fetchRequest' +import Loader from '@/components/Loader/Loader' +import { useNotification } from '@/context/NotificationContext' +import React, { useContext, useEffect, useState } from 'react' +import { ReservationContext } from './page' + +const PresenceButton = ({ processUserLocation, geolocationError, isInside }) => { + const { toggleNotification } = useNotification() + const [isLoading, setIsLoading] = useState(false) + const { setBookedPlaces, hasPlace } = useContext(ReservationContext) + const handlePresenceSave = async () => { + if (hasPlace) { + setIsLoading(true) + const { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/reservations/${hasPlace.id}/`, { + method: "PATCH", + body: JSON.stringify({ presence: true }) + }) + setIsLoading(false) + if (isSuccess) { + toggleNotification({ + visible: true, + message: "Votre présence a été enregistrée avec succès.", + type: "success" + }) + console.log(data) + setBookedPlaces((elements) => elements.map((element) => element.id === data.data?.id ? data.data : element)) + } + else { + console.log(errors) + if (status === 404) + toggleNotification({ + visible: true, + message: "Aucune réservation existe dans cette date.", + type: "warning" + }) + else + toggleNotification({ + visible: true, + message: "Internal Server Error", + type: "error" + }) + } + } + } + useEffect(() => { + if ("User denied the request for Geolocation." === geolocationError) { + toggleNotification({ + visible: true, + message: "Vous devez activer la géolocalisation pour enregistrer la présence", + type: "warning" + }) + } + else if (geolocationError === "The request to get user location timed out.") { + toggleNotification({ + visible: true, + message: "Vous devez activer la géolocalisation pour enregistrer la présence", + type: "warning" + }) + } + else if (geolocationError === "Location information is unavailable.") { + toggleNotification({ + visible: true, + message: "Erreur de géolocation", + type: "error" + }) + } + }, [geolocationError]) + + + if ("User denied the request for Geolocation." === geolocationError) { + return
+

Autorisez la géolocalisation pour enregistrer votre présence

+ +
+ } + else if (geolocationError === "The request to get user location timed out.") { + return
+

Autorisez la géolocalisation pour enregistrer votre présence

+ +
+ } + else if (geolocationError === "Location information is unavailable.") { + return + } + else if (isInside) + return + else return
+

Vous n'êtes pas géolocalisé à l'intérieur de Teamwill.

+ +
+} + + +export default PresenceButton \ No newline at end of file diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx index 37921d1..e899e40 100644 --- a/src/app/(dashboard)/reservation/page.jsx +++ b/src/app/(dashboard)/reservation/page.jsx @@ -1,10 +1,13 @@ 'use client' -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useMemo } from 'react' import ZoneUI from './ZoneUI' import fetchRequest from '@/app/lib/fetchRequest' import Loader from '@/components/Loader/Loader' import { useNotification } from '@/context/NotificationContext' +import PresenceButton from './PresenceButton' +import Cookies from 'js-cookie'; +import { decrypt } from '@/app/lib/session'; export const ReservationContext = React.createContext() @@ -18,6 +21,115 @@ const Reservation = () => { const [currentDateData, setCurrentDateData] = useState(null) const [datesData, setDatesData] = useState(null) const [bookedPlaces, setBookedPlaces] = useState([]) + const [isInside, setIsInside] = useState(null) + const [geolocationError, setGeolocationError] = useState(null) + + 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 processUserLocation = () => { + const companyLatitude = 36.8402141; // Example: Company's latitude + const companyLongitude = 10.2432573; // Example: Company's longitude + const allowedRadius = 400; // Example: Radius in meters + + + function getDistanceFromLatLonInMeters(lat1, lon1, lat2, lon2) { + const R = 6371000; // Radius of the Earth in meters + const dLat = (lat2 - lat1) * Math.PI / 180; + const dLon = (lon2 - lon1) * Math.PI / 180; + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + const distance = R * c; // Distance in meters + return distance; + } + + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + function (position) { + const userLatitude = position.coords.latitude; + const userLongitude = position.coords.longitude; + + const distance = getDistanceFromLatLonInMeters( + userLatitude, + userLongitude, + companyLatitude, + companyLongitude + ); + + if (distance <= allowedRadius) { + setIsInside(true) + } else { + setIsInside(false) + } + setGeolocationError(null) + }, + function (error) { + switch (error.code) { + case error.PERMISSION_DENIED: + setGeolocationError("User denied the request for Geolocation."); + break; + case error.POSITION_UNAVAILABLE: + setGeolocationError("Location information is unavailable."); + break; + case error.TIMEOUT: + setGeolocationError("The request to get user location timed out."); + break; + case error.UNKNOWN_ERROR: + setGeolocationError("An unknown error occurred."); + break; + } + } + ); + } else { + toggleNotification({ + message: "La géolocalisation n'est pas supporté par votre navigateur.", + type: "warning", + visible: true + }) + } + } + + + useEffect(() => { + const handlePermissionChange = (permissionStatus) => { + if (permissionStatus.state === 'granted' || permissionStatus.state === 'prompt') { + processUserLocation(); + } else { + setGeolocationError("User denied the request for Geolocation."); + } + }; + + navigator.permissions.query({ name: 'geolocation' }).then((permissionStatus) => { + handlePermissionChange(permissionStatus); + + permissionStatus.onchange = () => { + handlePermissionChange(permissionStatus); + }; + }).catch((error) => { + console.error('Failed to query geolocation permission:', error); + }); + return () => { + navigator.permissions.query({ name: 'geolocation' }).then((permissionStatus) => { + permissionStatus.onchange = null; + }); + }; + }, []) + useEffect(() => { const getPlan = async () => { try { @@ -129,11 +241,12 @@ const Reservation = () => { } } - + const currentDate = new Date().toJSON()?.split("T")[0] || 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) + const hasPlace = bookedPlaces?.find((p) => p.id_user === authenticatedUserData?.sessionData?.user_id) + if (isLoadingSelectsData) return
@@ -181,14 +294,13 @@ const Reservation = () => {
{(!isLoadingData) - ? <> - {(floors.filter((element) => concernedFloors.includes(element.id))?.length > 0) &&
- -
} -
- + ? + + <> + {(floors.filter((element) => concernedFloors.includes(element.id))?.length > 0 && hasPlace && currentDate && currentDate === date?.date) &&
+ +
} +
{floors.filter((element) => concernedFloors.includes(element.id)).map((floor) => { return

Etage {floor.numero}

@@ -201,9 +313,9 @@ const Reservation = () => { &&

Un exemple d'un message à l'utilisateur

} - -
- + +
+
:
diff --git a/src/app/(dashboard)/role/RoleTableRows.jsx b/src/app/(dashboard)/role/RoleTableRows.jsx index 89fcaa4..ccdcad9 100644 --- a/src/app/(dashboard)/role/RoleTableRows.jsx +++ b/src/app/(dashboard)/role/RoleTableRows.jsx @@ -57,6 +57,7 @@ const RoleTableRows = ({ name, setRoles, id, privileges, setRoleToUpdate }) => { {privileges?.map((element, index) => { return
{element.name}
})} + {!privileges || privileges.length == 0 &&

-

}
-- GitLab From 802176bcfeb322b1dc694dade70518f1b75d1991 Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Wed, 12 Jun 2024 14:10:15 +0100 Subject: [PATCH 5/7] finished adding consultation reservations --- .env.local | 1 + .gitignore | 1 + .../consultation-reservations/PlaceUI.jsx | 2 +- .../consultation-reservations/page.jsx | 2 +- src/app/ui/SideBar.jsx | 29 +++++++++++-------- 5 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 .env.local diff --git a/.env.local b/.env.local new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.env.local @@ -0,0 +1 @@ + diff --git a/.gitignore b/.gitignore index 10e85c1..fe4d87f 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env +.env.local # vercel .vercel diff --git a/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx b/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx index 7e294fa..f60e74f 100644 --- a/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx +++ b/src/app/(dashboard)/consultation-reservations/PlaceUI.jsx @@ -49,7 +49,7 @@ const PlaceUI = ({ id, isTop }) => { ); } else { return ( -
+
); } }; diff --git a/src/app/(dashboard)/consultation-reservations/page.jsx b/src/app/(dashboard)/consultation-reservations/page.jsx index 5e0b8ae..4edaac4 100644 --- a/src/app/(dashboard)/consultation-reservations/page.jsx +++ b/src/app/(dashboard)/consultation-reservations/page.jsx @@ -180,7 +180,7 @@ const Reservation = () => {
- {etages?.map((etage, index) => ( ))} +
{(!isLoadingData) ? <> @@ -207,9 +239,17 @@ const Reservation = () => { ).map((floor) => { return

Etage {floor.numero}

- {floor.zones.filter((element) => concernedZones.includes(element.id)).map((zone, index) => { - return - })} + {(selectedZone)? + floor.zones.filter((element) => + {if (concernedZones.includes(element.id) && element.id == selectedZone) {return element}} + ).map((zone, index) => { + return + }) + : + floor.zones.filter((element) => concernedZones.includes(element.id)).map((zone, index) => { + return + }) + }
}) : diff --git a/src/app/(dashboard)/etage/page.jsx b/src/app/(dashboard)/etage/page.jsx index d969987..9d69312 100644 --- a/src/app/(dashboard)/etage/page.jsx +++ b/src/app/(dashboard)/etage/page.jsx @@ -81,7 +81,7 @@ const Etage = () => { return (
-
+

Liste des étages

-- GitLab From 36b4fef6e43898e8054e34c6d64047c8d2e16081 Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Wed, 12 Jun 2024 14:30:25 +0100 Subject: [PATCH 7/7] added consultaions des reservations-finished --- src/app/(dashboard)/consultation-reservations/page.jsx | 2 ++ src/app/auth/login/page.jsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/(dashboard)/consultation-reservations/page.jsx b/src/app/(dashboard)/consultation-reservations/page.jsx index 5e1ed3e..dad6282 100644 --- a/src/app/(dashboard)/consultation-reservations/page.jsx +++ b/src/app/(dashboard)/consultation-reservations/page.jsx @@ -213,6 +213,7 @@ const Reservation = () => {