From 5024acce693e53ff25fb7e8c273541e7fa9a9247 Mon Sep 17 00:00:00 2001 From: Baligh ZOGHLAMI Date: Thu, 6 Jun 2024 13:24:36 +0100 Subject: [PATCH 01/11] reservation components --- .../privilege/CreatePrivilegeForm.jsx | 2 +- src/app/(dashboard)/reservation/TableUI.jsx | 45 +++++++ src/app/(dashboard)/reservation/ZoneUI.jsx | 17 +++ src/app/(dashboard)/reservation/page.jsx | 120 ++++++++++++++++++ 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/app/(dashboard)/reservation/TableUI.jsx create mode 100644 src/app/(dashboard)/reservation/ZoneUI.jsx create mode 100644 src/app/(dashboard)/reservation/page.jsx diff --git a/src/app/(dashboard)/privilege/CreatePrivilegeForm.jsx b/src/app/(dashboard)/privilege/CreatePrivilegeForm.jsx index 97c87c0..861c65b 100644 --- a/src/app/(dashboard)/privilege/CreatePrivilegeForm.jsx +++ b/src/app/(dashboard)/privilege/CreatePrivilegeForm.jsx @@ -59,7 +59,7 @@ const CreatePrivilegeForm = ({ appendPrivilege }) => { } } return ( -
+

Ajout d'habilitation

diff --git a/src/app/(dashboard)/reservation/TableUI.jsx b/src/app/(dashboard)/reservation/TableUI.jsx new file mode 100644 index 0000000..6a22027 --- /dev/null +++ b/src/app/(dashboard)/reservation/TableUI.jsx @@ -0,0 +1,45 @@ +import React from 'react' + +const TableUI = ({ tableName, places }) => { + function groupConsecutive(arr) { + 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 proccessedPlaces = groupConsecutive(places).reverse() + return ( +
+ {proccessedPlaces.map((element, index) => { + return
+
+
+ {element[0]} +
+
+
+ {(element.length > 1) &&
+
+ {element[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 0000000..f30e8b0 --- /dev/null +++ b/src/app/(dashboard)/reservation/ZoneUI.jsx @@ -0,0 +1,17 @@ +import React from 'react' +import TableUI from './TableUI' + +const ZoneUI = ({ tables, zoneName }) => { + return ( +
+

{zoneName}

+
+ {tables.map((table, index) => { + 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 0000000..414ce0d --- /dev/null +++ b/src/app/(dashboard)/reservation/page.jsx @@ -0,0 +1,120 @@ +'use client' + +import React, { useEffect, useState } from 'react' +import ZoneUI from './ZoneUI' +import fetchRequest from '@/app/lib/fetchRequest' +import Loader from '@/components/Loader/Loader' + +const Reservation = () => { + const [selectedFloor, setSelectedFloor] = useState(5) + const [floors, setFloors] = useState([]) + const [isLoadingSelectsData, setIsLoadingSelectsData] = useState(true) + const [isLoadingData, setIsLoadingData] = useState(false) + + useEffect(() => { + const getAllFloors = async () => { + try { + const { isSuccess, errors, data } = await fetchRequest('/zoaning/etages/', { method: 'GET' }) + setIsLoadingSelectsData(false) + if (isSuccess) { + setFloors(data) + } else { + setFloors([]) + } + } catch (error) { + setIsLoadingSelectsData(false) + console.log(error) + } + } + getAllFloors() + }, []) + + const [zones, setZones] = useState([ + { + zoneName: "A", + tables: [ + { + tableName: 1, + places: [1, 2, 3, 4, 5, 6, 7, 8] + }, + // { + // tableName: 2, + // places: [1, 2, 3, 4, 5, 6, 7, 8] + // }, + // { + // tableName: 3, + // places: [1, 2, 3, 4, 5, 6, 7] + // } + ] + }, + { + zoneName: "B", + tables: [ + { + tableName: 1, + places: [1, 2, 3, 4, 5, 6, 7, 8] + }, + // { + // tableName: 2, + // places: [1, 2, 3, 4, 5, 6, 8] + // } + ] + } + ]) + if (isLoadingSelectsData) + return
+ +
+ return ( +
+
+ + + + + +
+
+
+
+

Indisponible

+
+
+
+

Disponible

+
+
+
+

Réservé par vous

+
+
+
+

Confirmé

+
+
+ {(!isLoadingData) + ?
+ {zones.map((zone, index) => { + return + })} +
+ :
+ +
+ } +
+ ) +} + +export default Reservation \ No newline at end of file -- GitLab From fac19be80fa6b44240c8db47c5c042aafb46c941 Mon Sep 17 00:00:00 2001 From: Raed BOUAFIF Date: Fri, 7 Jun 2024 09:17:54 +0100 Subject: [PATCH 02/11] zone exception full availability --- .../assign_zone_project/AssignProject.jsx | 23 +++++++++++++++---- .../(dashboard)/assign_zone_project/page.jsx | 6 +---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/app/(dashboard)/assign_zone_project/AssignProject.jsx b/src/app/(dashboard)/assign_zone_project/AssignProject.jsx index 60fd26d..afafe64 100644 --- a/src/app/(dashboard)/assign_zone_project/AssignProject.jsx +++ b/src/app/(dashboard)/assign_zone_project/AssignProject.jsx @@ -9,7 +9,7 @@ import AddIcon from "@/static/image/svg/add.svg" import fetchRequest from "@/app/lib/fetchRequest"; -const AssignProject = ({ setIsOpen, listProjects }) => { +const AssignProject = ({ setIsOpen, listProjects, affectations }) => { const [loading, setLoading] = useState(false) const [projects, setProjects] = useState([]) const [zones, setZones] = useState([]) @@ -22,11 +22,12 @@ const AssignProject = ({ setIsOpen, listProjects }) => { const [ collabsAttributed, setCollabsAttributed ] = useState(0) const [ selectedOtherZone, setSelectedOtherZone ] = useState(null) const [ otherPlaces, setOtherPlaces ] = useState([]) + const [ affectaionsRelatedToZones, setAffectationsRelatedToZones ] = useState([]) const { toggleNotification } = useNotification() const attributedCollabsRef = useRef() - + console.log(affectations) useEffect(() => { const fetchProjectsandZones = async () => { setLoading(true) @@ -80,11 +81,18 @@ const AssignProject = ({ setIsOpen, listProjects }) => { if(selectedDay && selectedWeek) fetchProjectsandZones() }, [selectedDay, selectedWeek]) + const handleZoneSelection = async (e) => { const zone_id = e.target.value + const related_affecations = affectations.filter( (element) => element.jour == selectedDay && element.semaine == selectedWeek && element.id_zone.id == zone_id).map(element => element.id) try{ - const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces/${zone_id}`, {method: 'GET'}) + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces`, + {method: 'POST', body: JSON.stringify({ + related_affecations: related_affecations, + zone_id: zone_id + })}) if(isSuccess){ + console.log(data.places) setSelectedZone(zone_id) setPlaces(data.places) setOtherPlaces([]) @@ -144,9 +152,15 @@ const AssignProject = ({ setIsOpen, listProjects }) => { const handleOtherZoneSelection = async (e) => { const zone_id = e.target.value + const related_affecations = affectations.filter( (element) => element.jour == selectedDay && element.semaine == selectedWeek && element.id_zone.id == zone_id).map(element => element.id) setSelectedOtherZone(zone_id) try{ - const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces/${zone_id}`, {method: 'GET'}) + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces`, + {method: 'POST', body: JSON.stringify({ + related_affecations: related_affecations, + zone_id: zone_id + })} + ) if(isSuccess){ setOtherPlaces(data.places) }else{ @@ -237,7 +251,6 @@ const AssignProject = ({ setIsOpen, listProjects }) => { } - console.log("other zone ::::" , selectedOtherZone) return (
diff --git a/src/app/(dashboard)/assign_zone_project/page.jsx b/src/app/(dashboard)/assign_zone_project/page.jsx index 1c49160..85c5bee 100644 --- a/src/app/(dashboard)/assign_zone_project/page.jsx +++ b/src/app/(dashboard)/assign_zone_project/page.jsx @@ -67,7 +67,6 @@ const AffectingZoneProject = () => { const handleDeleteAffectation = async () => { try{ - console.log("qsdsqdqsdsqdq") var { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/deteleAffectedProject/${selectedAffectaionToDelete.id}`, {method: 'DELETE'}) if(isSuccess){ toggleNotification({ @@ -106,18 +105,15 @@ const AffectingZoneProject = () => { const handleConfirmDelete = () => { - console.log("qsdsq") handleDeleteAffectation(); - console.log("fdsfsd") setModalOpen(false); setSelectedAffectationToDelete(null); }; - return (
- {isOpen && } + {isOpen && }

List des Projets attribuer

Il y a des projets qui ne sont pas complètement affecter. -- GitLab From 7feda36126564253cff447d279b9748931affef6 Mon Sep 17 00:00:00 2001 From: Baligh ZOGHLAMI Date: Mon, 10 Jun 2024 10:41:16 +0100 Subject: [PATCH 03/11] to fix date --- .../assign_zone_project/AssignProject.jsx | 142 ++++++------- .../(dashboard)/assign_zone_project/page.jsx | 66 +++---- src/app/(dashboard)/reservation/TableUI.jsx | 58 +++++- src/app/(dashboard)/reservation/ZoneUI.jsx | 8 +- src/app/(dashboard)/reservation/page.jsx | 187 ++++++++++++------ 5 files changed, 289 insertions(+), 172 deletions(-) diff --git a/src/app/(dashboard)/assign_zone_project/AssignProject.jsx b/src/app/(dashboard)/assign_zone_project/AssignProject.jsx index afafe64..a5e90b0 100644 --- a/src/app/(dashboard)/assign_zone_project/AssignProject.jsx +++ b/src/app/(dashboard)/assign_zone_project/AssignProject.jsx @@ -13,16 +13,16 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { const [loading, setLoading] = useState(false) const [projects, setProjects] = useState([]) const [zones, setZones] = useState([]) - const [ selectedDay, setSelectedDay ] = useState('') - const [ selectedWeek, setSelectedWeek ] = useState('') - const [ selectedZone, setSelectedZone ] = useState(null) - const [ selectedProject, setSelectedProject ] = useState(null) - const [ places , setPlaces ] = useState([]) - const [ nbrCollabs, setNbrCollabs ] = useState(0) - const [ collabsAttributed, setCollabsAttributed ] = useState(0) - const [ selectedOtherZone, setSelectedOtherZone ] = useState(null) - const [ otherPlaces, setOtherPlaces ] = useState([]) - const [ affectaionsRelatedToZones, setAffectationsRelatedToZones ] = useState([]) + const [selectedDay, setSelectedDay] = useState('') + const [selectedWeek, setSelectedWeek] = useState('') + const [selectedZone, setSelectedZone] = useState(null) + const [selectedProject, setSelectedProject] = useState(null) + const [places, setPlaces] = useState([]) + const [nbrCollabs, setNbrCollabs] = useState(0) + const [collabsAttributed, setCollabsAttributed] = useState(0) + const [selectedOtherZone, setSelectedOtherZone] = useState(null) + const [otherPlaces, setOtherPlaces] = useState([]) + const [affectaionsRelatedToZones, setAffectationsRelatedToZones] = useState([]) const { toggleNotification } = useNotification() @@ -32,12 +32,12 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { const fetchProjectsandZones = async () => { setLoading(true) try { - const {isSuccess, errors, data} = await fetchRequest(`/zoaning/affectingProject/${selectedWeek}/${selectedDay}`, {method: 'GET'}) - if(isSuccess){ + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/affectingProject/${selectedWeek}/${selectedDay}`, { method: 'GET' }) + if (isSuccess) { setCollabsAttributed(0) setNbrCollabs(0) setPlaces([]) - if ( (data.projects && data.projects.length === 0) && (data.zones && data.zones.length === 0)){ + if ((data.projects && data.projects.length === 0) && (data.zones && data.zones.length === 0)) { toggleNotification({ visible: true, message: "Il y'a pas de projets et de zones pour cette semaine et ce jour.", @@ -45,29 +45,29 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { }) setProjects([]) setZones([]) - }else{ - if(data.projects && data.projects.length === 0){ + } else { + if (data.projects && data.projects.length === 0) { toggleNotification({ visible: true, message: "Il y'a pas de projets pour cette semaine et ce jour.", type: "warning" }) setProjects([]) - }else{ + } else { setProjects(data.projects) } - if(data.zones && data.zones.length === 0){ + if (data.zones && data.zones.length === 0) { toggleNotification({ visible: true, message: "Il y'a pas de zones pour cette semaine et ce jour.", type: "warning" }) setZones([]) - }else{ + } else { setZones(data.zones) } } - }else{ + } else { // handle error setLoading(false) } @@ -78,58 +78,60 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { setLoading(false) } } - if(selectedDay && selectedWeek) fetchProjectsandZones() + if (selectedDay && selectedWeek) fetchProjectsandZones() }, [selectedDay, selectedWeek]) const handleZoneSelection = async (e) => { const zone_id = e.target.value - const related_affecations = affectations.filter( (element) => element.jour == selectedDay && element.semaine == selectedWeek && element.id_zone.id == zone_id).map(element => element.id) - try{ + const related_affecations = affectations.filter((element) => element.jour == selectedDay && element.semaine == selectedWeek && element.id_zone.id == zone_id).map(element => element.id) + try { const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces`, - {method: 'POST', body: JSON.stringify({ - related_affecations: related_affecations, - zone_id: zone_id - })}) - if(isSuccess){ + { + method: 'POST', body: JSON.stringify({ + related_affecations: related_affecations, + zone_id: zone_id + }) + }) + if (isSuccess) { console.log(data.places) setSelectedZone(zone_id) setPlaces(data.places) setOtherPlaces([]) setSelectedOtherZone(null) - }else{ + } else { // handle error setPlaces([]) } - }catch(error){ + } catch (error) { console.log(error) } } const handleProjectSelection = async (e) => { const project_id = e.target.value - try{ - const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingCollabs/${project_id}`, {method: 'GET'}) - if(isSuccess){ + try { + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingCollabs/${project_id}`, { method: 'GET' }) + if (isSuccess) { setSelectedProject(project_id) setNbrCollabs(data.user_count) setOtherPlaces([]) setSelectedOtherZone(null) - }else{ + } else { // handle error setNbrCollabs(0) } - }catch(error){ + } catch (error) { console.log(error) } } - useEffect( () => { - if(nbrCollabs > 0 && places.length > 0){ - if( nbrCollabs <= places.length){ + useEffect(() => { + if (nbrCollabs > 0 && places.length > 0) { + if (nbrCollabs <= places.length) { setCollabsAttributed(nbrCollabs) attributedCollabsRef.current = nbrCollabs - }else{ + } else { setCollabsAttributed(places.length) attributedCollabsRef.current = places.length } @@ -137,12 +139,12 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { }, [nbrCollabs, places]) - const handleAddCollab = () =>{ + const handleAddCollab = () => { setCollabsAttributed(collabsAttributed + 1) attributedCollabsRef.current = collabsAttributed + 1 } - const handleMinusCollab = () =>{ + const handleMinusCollab = () => { setCollabsAttributed(collabsAttributed - 1) attributedCollabsRef.current = collabsAttributed - 1 } @@ -152,21 +154,23 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { const handleOtherZoneSelection = async (e) => { const zone_id = e.target.value - const related_affecations = affectations.filter( (element) => element.jour == selectedDay && element.semaine == selectedWeek && element.id_zone.id == zone_id).map(element => element.id) + const related_affecations = affectations.filter((element) => element.jour == selectedDay && element.semaine == selectedWeek && element.id_zone.id == zone_id).map(element => element.id) setSelectedOtherZone(zone_id) - try{ + try { const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces`, - {method: 'POST', body: JSON.stringify({ - related_affecations: related_affecations, - zone_id: zone_id - })} + { + method: 'POST', body: JSON.stringify({ + related_affecations: related_affecations, + zone_id: zone_id + }) + } ) - if(isSuccess){ + if (isSuccess) { setOtherPlaces(data.places) - }else{ + } else { // handle error } - }catch(error){ + } catch (error) { console.log(error) toggleNotification({ visible: true, @@ -176,8 +180,8 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { } } - useEffect( () => { - if( (collabsAttributed - places.length) <= 0 && selectedOtherZone ) { + useEffect(() => { + if ((collabsAttributed - places.length) <= 0 && selectedOtherZone) { setSelectedOtherZone(null) setOtherPlaces([]) } @@ -193,27 +197,27 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { nombre_personnes: collabsAttributed, places_disponibles: (collabsAttributed > places.length) ? 0 : places.length - collabsAttributed, places_occuper: (collabsAttributed > places.length) ? places.length : collabsAttributed, - places: (collabsAttributed > places.length) ? places.map( (element) => element.id) : places.map( (element, index) => index < collabsAttributed && element.id).filter(id => id !== false) + places: (collabsAttributed > places.length) ? places.map((element) => element.id) : places.map((element, index) => index < collabsAttributed && element.id).filter(id => id !== false) } - if( selectedOtherZone && otherPlaces.length > 0){ + if (selectedOtherZone && otherPlaces.length > 0) { finalData.otherZone = { id_zone: selectedOtherZone, - places_disponibles: ( (collabsAttributed - places.length ) > otherPlaces.length ) ? 0 : otherPlaces.length - (collabsAttributed - places.length), - places_occuper: ( (collabsAttributed - places.length ) > otherPlaces.length ) ? otherPlaces.length : collabsAttributed - places.length, - places: ( (collabsAttributed - places.length ) > otherPlaces.length ) ? otherPlaces.map( (element) => element.id) : otherPlaces.map( (element, index) => (index < (collabsAttributed - places.length)) && element.id).filter(id => id !== false) + places_disponibles: ((collabsAttributed - places.length) > otherPlaces.length) ? 0 : otherPlaces.length - (collabsAttributed - places.length), + places_occuper: ((collabsAttributed - places.length) > otherPlaces.length) ? otherPlaces.length : collabsAttributed - places.length, + places: ((collabsAttributed - places.length) > otherPlaces.length) ? otherPlaces.map((element) => element.id) : otherPlaces.map((element, index) => (index < (collabsAttributed - places.length)) && element.id).filter(id => id !== false) } } - try{ - const { isSuccess, errors, data } = await fetchRequest(`/zoaning/affectingProject`, {method: 'POST', body: JSON.stringify(finalData)}) - if(isSuccess){ - listProjects( (prevListProjects) => [...prevListProjects, data.main_zone, ...(data.second_zone ? [data.second_zone] : [])]) + try { + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/affectingProject`, { method: 'POST', body: JSON.stringify(finalData) }) + if (isSuccess) { + listProjects((prevListProjects) => [...prevListProjects, data.main_zone, ...(data.second_zone ? [data.second_zone] : [])]) toggleNotification({ visible: true, message: "Projet affecté avec succès.", type: "success" }) closePopup(false) - }else{ + } else { console.log(errors) toggleNotification({ visible: true, @@ -221,7 +225,7 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { type: "error" }) } - }catch(error){ + } catch (error) { console.log(error) toggleNotification({ visible: true, @@ -255,7 +259,7 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => {

Attribuer un projet à une zone.

- {setIsOpen(false)}} className="h-8 w-8 cursor-pointer absolute top-2 right-2 fill-neutral-600" /> + { setIsOpen(false) }} className="h-8 w-8 cursor-pointer absolute top-2 right-2 fill-neutral-600" />

Veuillez sélectionner une date

@@ -285,7 +289,7 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => {
{(!loading && (projects && projects.length)) ? - { handleProjectSelection(e) }} disabled={projects.length === 0 || loading} className='rounded-md px-3 duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none'> {projects.map((element, index) => )} @@ -313,7 +317,7 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => {

: {places.length}

- {collabsAttributed > 0 && + {collabsAttributed > 0 && <>
Collaborateurs attribués @@ -323,7 +327,7 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { -
@@ -335,7 +339,7 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { 0) ? "fill-red-400" : "fill-sushi-500"}`} />
-
0) ? "text-red-400" : "text-sushi-500"}`}>{(places.length+ otherPlaces.length) - collabsAttributed}
+
0) ? "text-red-400" : "text-sushi-500"}`}>{(places.length + otherPlaces.length) - collabsAttributed}
0) ? "fill-red-400" : "fill-sushi-500"}`} />
@@ -345,9 +349,9 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { {((collabsAttributed - places.length) > 0) &&

Veuillez sélectionner une autre zonne pour compléter l'affectation (optionnel)

- handleOtherZoneSelection(e)} className='rounded-md px-3 duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none'> - {zones.filter( (element) => element.id != selectedZone)?.map( (element, index) => )} + {zones.filter((element) => element.id != selectedZone)?.map((element, index) => )}

: {otherPlaces.length}

diff --git a/src/app/(dashboard)/assign_zone_project/page.jsx b/src/app/(dashboard)/assign_zone_project/page.jsx index 85c5bee..615a681 100644 --- a/src/app/(dashboard)/assign_zone_project/page.jsx +++ b/src/app/(dashboard)/assign_zone_project/page.jsx @@ -1,6 +1,6 @@ "use client" import React, { useEffect, useState } from 'react'; -import AddIcon from "@/static/image/svg/add.svg"; +import AddIcon from "@/static/image/svg/add.svg"; import AssignProject from './AssignProject'; import { useNotification } from '@/context/NotificationContext' @@ -15,31 +15,31 @@ import ConfirmationModal from "@/app/ui/ConfirmationModal"; const AffectingZoneProject = () => { const [isOpen, setIsOpen] = useState(false) - const [ listProjectsAffected, setListProjectsAffected ] = useState([]) - const [ isLoadingListProjects, setIsLoadingListProjects ] = useState(false) + const [listProjectsAffected, setListProjectsAffected] = useState([]) + const [isLoadingListProjects, setIsLoadingListProjects] = useState(false) const { toggleNotification } = useNotification() - const [ selectedWeek, setSelectedWeek ] = useState(null) - const [ selectedDay, setSelectedDay ] = useState(null) - const [ selectedAffectaionToDelete, setSelectedAffectationToDelete ] = useState(null) + const [selectedWeek, setSelectedWeek] = useState(null) + const [selectedDay, setSelectedDay] = useState(null) + const [selectedAffectaionToDelete, setSelectedAffectationToDelete] = useState(null) const [isModalOpen, setModalOpen] = useState(false); useEffect(() => { const getListOfAffectedProjects = async () => { setIsLoadingListProjects(true) - try{ - if(selectedDay && selectedWeek){ - var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjects/${selectedDay}/${selectedWeek}/`, {method: 'GET'}) - }else if (selectedWeek){ - var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjectsByWeek/${selectedWeek}/`, {method: 'GET'}) - }else if (selectedDay){ - var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjectsByDay/${selectedDay}/`, {method: 'GET'}) - }else{ - var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjects/`, {method: 'GET'}) + try { + if (selectedDay && selectedWeek) { + var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjects/${selectedDay}/${selectedWeek}/`, { method: 'GET' }) + } else if (selectedWeek) { + var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjectsByWeek/${selectedWeek}/`, { method: 'GET' }) + } else if (selectedDay) { + var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjectsByDay/${selectedDay}/`, { method: 'GET' }) + } else { + var { isSuccess, errors, data } = await fetchRequest(`/zoaning/getListAffectedProjects/`, { method: 'GET' }) } - if(isSuccess){ + if (isSuccess) { setListProjectsAffected(data) - }else{ + } else { toggleNotification({ visible: true, message: errors[0].message, @@ -47,7 +47,7 @@ const AffectingZoneProject = () => { }) } setIsLoadingListProjects(false) - }catch(error){ + } catch (error) { setIsLoadingListProjects(false) console.log(error) toggleNotification({ @@ -66,29 +66,29 @@ const AffectingZoneProject = () => { } const handleDeleteAffectation = async () => { - try{ - var { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/deteleAffectedProject/${selectedAffectaionToDelete.id}`, {method: 'DELETE'}) - if(isSuccess){ + try { + var { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/deteleAffectedProject/${selectedAffectaionToDelete.id}`, { method: 'DELETE' }) + if (isSuccess) { toggleNotification({ visible: true, message: "Affectation supprimer avec succès", type: "success" }) setListProjectsAffected(listProjectsAffected.filter(affected => affected.id !== selectedAffectaionToDelete.id)) - }else if(status === 404){ + } else if (status === 404) { toggleNotification({ visible: true, message: "Affectation introuvable", type: "error" }) - }else{ + } else { toggleNotification({ visible: true, message: errors[0].message, type: "error" }) } - }catch(error){ + } catch (error) { console.log(error) toggleNotification({ visible: true, @@ -107,7 +107,7 @@ const AffectingZoneProject = () => { const handleConfirmDelete = () => { handleDeleteAffectation(); setModalOpen(false); - setSelectedAffectationToDelete(null); + setSelectedAffectationToDelete(null); }; return ( @@ -118,7 +118,7 @@ const AffectingZoneProject = () => {
Il y a des projets qui ne sont pas complètement affecter.
@@ -129,7 +129,7 @@ const AffectingZoneProject = () => { - +
setSelectedFloor(event.target.value)} className='rounded-md text-sm w-full sm:w-fit px-3 duration-150 delay-75 border h-9 border-neutral-300 disabled:bg-neutral-200 outline-none' name="etage"> - - {floors?.map((floor) => { - return - })} - - + + + + + + - - + + + + + + +
@@ -105,9 +173,16 @@ const Reservation = () => {
{(!isLoadingData) ?
- {zones.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 + })} +
+ })} +
:
-- GitLab From cc94c788641ad5706f6c7c8c2c4b1013d4101cd5 Mon Sep 17 00:00:00 2001 From: Baligh ZOGHLAMI Date: Mon, 10 Jun 2024 14:53:30 +0100 Subject: [PATCH 04/11] add & cancel reservation --- src/app/(dashboard)/reservation/PlaceUI.jsx | 118 ++++++++++++++++++++ src/app/(dashboard)/reservation/TableUI.jsx | 52 ++------- src/app/(dashboard)/reservation/page.jsx | 9 +- src/app/ui/ConfirmationModal.jsx | 29 ++++- 4 files changed, 158 insertions(+), 50 deletions(-) create mode 100644 src/app/(dashboard)/reservation/PlaceUI.jsx diff --git a/src/app/(dashboard)/reservation/PlaceUI.jsx b/src/app/(dashboard)/reservation/PlaceUI.jsx new file mode 100644 index 0000000..17a18ab --- /dev/null +++ b/src/app/(dashboard)/reservation/PlaceUI.jsx @@ -0,0 +1,118 @@ +"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, currentDateData, 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": currentDateData.date, + "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 { + console.log(errors) + } + } + const handleCancelingConfirmation = async (idReservation) => { + const { isSuccess, errors, data } = 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 { + 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 index 33a55c9..8c77ad5 100644 --- a/src/app/(dashboard)/reservation/TableUI.jsx +++ b/src/app/(dashboard)/reservation/TableUI.jsx @@ -1,8 +1,7 @@ -import React, { useContext } from 'react' -import { ReservationContext } from './page'; -import fetchRequest from '@/app/lib/fetchRequest'; +import React from 'react' +import PlaceUI from './PlaceUI'; -const TableUI = ({ id, numero, places, bookedPlaces }) => { +const TableUI = ({ id, numero, places }) => { function groupConsecutive(arr) { arr = arr.sort((a, b) => a.id - b.id) @@ -24,55 +23,20 @@ const TableUI = ({ id, numero, places, bookedPlaces }) => { return grouped; } - const proccessedPlaces = groupConsecutive(places).reverse() - const { allPlaces, currentDateData } = useContext(ReservationContext) + const processedPlaces = groupConsecutive(places).reverse() - const reservePlace = async (id) => { - const { isSuccess, errors, data } = await fetchRequest('/zoaning/reservations/', { - method: "POST", body: JSON.stringify( - { - "presence": false, - "date": currentDateData.date, - "id_place": id - } - ) - }); - if (isSuccess) { - console.log(data); - toggleNotification({ - visible: true, - message: "La réservation a été enregistrer avec succés", - type: "success" - }) - } else { - console.log(errors) - } - } - - const findPlace = (id) => { - return allPlaces?.find((place) => place.id === id) - } + if (!processedPlaces || processedPlaces.length === 0) return <> return (
- {proccessedPlaces.map((element, index) => { + {processedPlaces.map((element, index) => { return
- {(allPlaces?.find((place) => place.id === element[0].id)) - ?
reservePlace(element[0].id)} className='absolute text-white items-center flex justify-center h-full w-full px-[2px] text-sm bg-blue-500/80'> -

{allPlaces.find((place) => place.id === element[0].id).project_name}

-
- :
- } +
{(element.length > 1) &&
- {findPlace(element[1].id) - ?
reservePlace(element[1].id)} className='absolute text-white items-center flex justify-center h-full px-[2px] w-full text-sm bg-blue-500/80'> -

{allPlaces.find((place) => place.id === element[1].id).project_name}

-
- :
- } +
}
})} diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx index 2fbab06..2ff4d3a 100644 --- a/src/app/(dashboard)/reservation/page.jsx +++ b/src/app/(dashboard)/reservation/page.jsx @@ -5,6 +5,7 @@ import ZoneUI from './ZoneUI' import fetchRequest from '@/app/lib/fetchRequest' import Loader from '@/components/Loader/Loader' import { useNotification } from '@/context/NotificationContext' +import ConfirmationModal from '@/app/ui/ConfirmationModal' export const ReservationContext = React.createContext() @@ -166,14 +167,18 @@ const Reservation = () => {

Réservé par vous

+
+
+

Réservé par un collègue

+
-

Confirmé

+

Présent

{(!isLoadingData) ?
- + {floors.filter((element) => concernedFloors.includes(element.id)).map((floor) => { return

Etage {floor.numero}

diff --git a/src/app/ui/ConfirmationModal.jsx b/src/app/ui/ConfirmationModal.jsx index 1c667ac..ad5e6cd 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
-- GitLab From 555d08bee5c31b87d9a0a8c7d44c71c3ee62c0dc Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Mon, 10 Jun 2024 14:56:18 +0100 Subject: [PATCH 05/11] fixed reservation dates --- .../(dashboard)/planning/PlanningTable.jsx | 6 ++-- src/app/(dashboard)/reservation/page.jsx | 36 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/app/(dashboard)/planning/PlanningTable.jsx b/src/app/(dashboard)/planning/PlanningTable.jsx index 2fbdf40..2bf1d00 100644 --- a/src/app/(dashboard)/planning/PlanningTable.jsx +++ b/src/app/(dashboard)/planning/PlanningTable.jsx @@ -30,12 +30,12 @@ const PlanningTable = ({ data, typePresences, onTypePresenceChange }) => { diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx index 2fbab06..00608c3 100644 --- a/src/app/(dashboard)/reservation/page.jsx +++ b/src/app/(dashboard)/reservation/page.jsx @@ -118,40 +118,40 @@ const Reservation = () => { }, []) const handleChangeDate = (event) => { - const target = event.target - setDate({ ...date, [target.name]: target.value }) + const date = JSON.parse(event.target.value); + console.log("weekMonthly", date.weekMonthly); + console.log("day", date.day); + setDate({ day: date.day, week: date.weekMonthly }) } 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 (
- - -
-- GitLab From 5a31baf40f202079ad4ebe9c7be7e4d5b2e83f49 Mon Sep 17 00:00:00 2001 From: Baligh ZOGHLAMI Date: Mon, 10 Jun 2024 15:20:06 +0100 Subject: [PATCH 06/11] handle reservation exceptions --- src/app/(dashboard)/reservation/PlaceUI.jsx | 27 +++++++++++++++++++-- src/app/(dashboard)/reservation/ZoneUI.jsx | 2 +- src/app/(dashboard)/reservation/page.jsx | 1 - 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/app/(dashboard)/reservation/PlaceUI.jsx b/src/app/(dashboard)/reservation/PlaceUI.jsx index 17a18ab..cf831dd 100644 --- a/src/app/(dashboard)/reservation/PlaceUI.jsx +++ b/src/app/(dashboard)/reservation/PlaceUI.jsx @@ -7,7 +7,6 @@ import Cookies from 'js-cookie'; import { decrypt } from '@/app/lib/session'; import ConfirmationModal from '@/app/ui/ConfirmationModal' const PlaceUI = ({ id }) => { - const { allPlaces, currentDateData, bookedPlaces, setBookedPlaces } = useContext(ReservationContext) const { toggleNotification } = useNotification() const [isOpenBooking, setIsOpenBooking] = useState(false) @@ -29,6 +28,7 @@ const PlaceUI = ({ id }) => { 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) + if (bookedPlace) console.log("place id", id) const handleBooking = (event) => { event.stopPropagation() if (hasPlace) { @@ -60,11 +60,23 @@ const PlaceUI = ({ id }) => { }) 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 } = await fetchRequest(`/zoaning/reservations/${idReservation}`, { + const { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/reservations/${idReservation}`, { method: "DELETE" }); if (isSuccess) { @@ -77,6 +89,17 @@ const PlaceUI = ({ id }) => { }) 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) } } diff --git a/src/app/(dashboard)/reservation/ZoneUI.jsx b/src/app/(dashboard)/reservation/ZoneUI.jsx index cd387ac..5fb60ee 100644 --- a/src/app/(dashboard)/reservation/ZoneUI.jsx +++ b/src/app/(dashboard)/reservation/ZoneUI.jsx @@ -3,7 +3,7 @@ import TableUI from './TableUI' const ZoneUI = ({ id, tables, nom }) => { return ( -
+

{nom}

{tables.map((table) => { diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx index 2ff4d3a..f1ddaf7 100644 --- a/src/app/(dashboard)/reservation/page.jsx +++ b/src/app/(dashboard)/reservation/page.jsx @@ -5,7 +5,6 @@ import ZoneUI from './ZoneUI' import fetchRequest from '@/app/lib/fetchRequest' import Loader from '@/components/Loader/Loader' import { useNotification } from '@/context/NotificationContext' -import ConfirmationModal from '@/app/ui/ConfirmationModal' export const ReservationContext = React.createContext() -- GitLab From af0babff47bd535570f908596725463055033224 Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Mon, 10 Jun 2024 15:47:10 +0100 Subject: [PATCH 07/11] fixed reservation dates --- src/app/(dashboard)/reservation/page.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx index f31f2cd..a9d0e7b 100644 --- a/src/app/(dashboard)/reservation/page.jsx +++ b/src/app/(dashboard)/reservation/page.jsx @@ -58,7 +58,7 @@ const Reservation = () => { } } const getBookedPlaces = async () => { - const { isSuccess, errors, data } = await fetchRequest(`/zoaning/reservations/date/${currentDateData.date}/`) + 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 }))) @@ -118,10 +118,10 @@ const Reservation = () => { }, []) const handleChangeDate = (event) => { - const date = JSON.parse(event.target.value); - console.log("weekMonthly", date.weekMonthly); - console.log("day", date.day); - setDate({ day: date.day, week: date.weekMonthly }) + 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}) } -- GitLab From 1fc4f0bd95182af174534e3556b35519af6b8f23 Mon Sep 17 00:00:00 2001 From: Baligh ZOGHLAMI Date: Mon, 10 Jun 2024 16:59:29 +0100 Subject: [PATCH 08/11] stable reservation UI --- src/app/(dashboard)/reservation/PlaceUI.jsx | 5 +- src/app/(dashboard)/reservation/ZoneUI.jsx | 2 +- src/app/(dashboard)/reservation/page.jsx | 56 +++++++++++++-------- src/app/ui/SideBar.jsx | 5 ++ 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/app/(dashboard)/reservation/PlaceUI.jsx b/src/app/(dashboard)/reservation/PlaceUI.jsx index cf831dd..f347155 100644 --- a/src/app/(dashboard)/reservation/PlaceUI.jsx +++ b/src/app/(dashboard)/reservation/PlaceUI.jsx @@ -7,7 +7,7 @@ import Cookies from 'js-cookie'; import { decrypt } from '@/app/lib/session'; import ConfirmationModal from '@/app/ui/ConfirmationModal' const PlaceUI = ({ id }) => { - const { allPlaces, currentDateData, bookedPlaces, setBookedPlaces } = useContext(ReservationContext) + const { allPlaces, selectedDate, bookedPlaces, setBookedPlaces } = useContext(ReservationContext) const { toggleNotification } = useNotification() const [isOpenBooking, setIsOpenBooking] = useState(false) const [isOpenCanceling, setIsOpenCanceling] = useState(false) @@ -28,7 +28,6 @@ const PlaceUI = ({ id }) => { 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) - if (bookedPlace) console.log("place id", id) const handleBooking = (event) => { event.stopPropagation() if (hasPlace) { @@ -45,7 +44,7 @@ const PlaceUI = ({ id }) => { method: "POST", body: JSON.stringify( { "presence": false, - "date": currentDateData.date, + "date": selectedDate, "id_place": id } ) diff --git a/src/app/(dashboard)/reservation/ZoneUI.jsx b/src/app/(dashboard)/reservation/ZoneUI.jsx index 5fb60ee..48ae7d1 100644 --- a/src/app/(dashboard)/reservation/ZoneUI.jsx +++ b/src/app/(dashboard)/reservation/ZoneUI.jsx @@ -3,7 +3,7 @@ import TableUI from './TableUI' const ZoneUI = ({ id, tables, nom }) => { return ( -
+

{nom}

{tables.map((table) => { diff --git a/src/app/(dashboard)/reservation/page.jsx b/src/app/(dashboard)/reservation/page.jsx index a9d0e7b..37921d1 100644 --- a/src/app/(dashboard)/reservation/page.jsx +++ b/src/app/(dashboard)/reservation/page.jsx @@ -118,10 +118,15 @@ const Reservation = () => { }, []) const handleChangeDate = (event) => { - 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}) + 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 }) + } } @@ -137,9 +142,9 @@ const Reservation = () => { 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) + 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 (
@@ -156,7 +161,7 @@ const Reservation = () => {
-

Indisponible

+

Autres Projets

@@ -176,18 +181,29 @@ 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 - })} -
- })} -
-
+ ? <> + {(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

+
} +
+
+ :
diff --git a/src/app/ui/SideBar.jsx b/src/app/ui/SideBar.jsx index 90f7df6..4bab50a 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" -- GitLab From ccb712c2586a45654a6e7893af5cd2cc6e9cac84 Mon Sep 17 00:00:00 2001 From: Raed BOUAFIF Date: Mon, 10 Jun 2024 18:15:22 +0100 Subject: [PATCH 09/11] version affectation all exceptions handled --- .../assign_zone_project/AssignProject.jsx | 13 +- .../CompleteAffectation.jsx | 303 ++++++++++++++++++ .../(dashboard)/assign_zone_project/page.jsx | 130 +++++++- 3 files changed, 428 insertions(+), 18 deletions(-) create mode 100644 src/app/(dashboard)/assign_zone_project/CompleteAffectation.jsx diff --git a/src/app/(dashboard)/assign_zone_project/AssignProject.jsx b/src/app/(dashboard)/assign_zone_project/AssignProject.jsx index afafe64..7eb743d 100644 --- a/src/app/(dashboard)/assign_zone_project/AssignProject.jsx +++ b/src/app/(dashboard)/assign_zone_project/AssignProject.jsx @@ -9,7 +9,7 @@ import AddIcon from "@/static/image/svg/add.svg" import fetchRequest from "@/app/lib/fetchRequest"; -const AssignProject = ({ setIsOpen, listProjects, affectations }) => { +const AssignProject = ({ setIsOpen, listProjects, affectations, mutateProjectAffection }) => { const [loading, setLoading] = useState(false) const [projects, setProjects] = useState([]) const [zones, setZones] = useState([]) @@ -22,12 +22,10 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { const [ collabsAttributed, setCollabsAttributed ] = useState(0) const [ selectedOtherZone, setSelectedOtherZone ] = useState(null) const [ otherPlaces, setOtherPlaces ] = useState([]) - const [ affectaionsRelatedToZones, setAffectationsRelatedToZones ] = useState([]) const { toggleNotification } = useNotification() const attributedCollabsRef = useRef() - console.log(affectations) useEffect(() => { const fetchProjectsandZones = async () => { setLoading(true) @@ -92,7 +90,6 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { zone_id: zone_id })}) if(isSuccess){ - console.log(data.places) setSelectedZone(zone_id) setPlaces(data.places) setOtherPlaces([]) @@ -183,14 +180,13 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { } }, [collabsAttributed]) - const handleAssignProject = async () => { const finalData = { id_zone: selectedZone, id_project: selectedProject, jour: selectedDay, semaine: selectedWeek, - nombre_personnes: collabsAttributed, + nombre_personnes: nbrCollabs, places_disponibles: (collabsAttributed > places.length) ? 0 : places.length - collabsAttributed, places_occuper: (collabsAttributed > places.length) ? places.length : collabsAttributed, places: (collabsAttributed > places.length) ? places.map( (element) => element.id) : places.map( (element, index) => index < collabsAttributed && element.id).filter(id => id !== false) @@ -206,7 +202,9 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { try{ const { isSuccess, errors, data } = await fetchRequest(`/zoaning/affectingProject`, {method: 'POST', body: JSON.stringify(finalData)}) if(isSuccess){ - listProjects( (prevListProjects) => [...prevListProjects, data.main_zone, ...(data.second_zone ? [data.second_zone] : [])]) + const newAffectations = [...affectations, data.main_zone, ...(data.second_zone ? [data.second_zone] : [])] + listProjects(newAffectations) + mutateProjectAffection(newAffectations) toggleNotification({ visible: true, message: "Projet affecté avec succès.", @@ -229,7 +227,6 @@ const AssignProject = ({ setIsOpen, listProjects, affectations }) => { type: "error" }) } - } const closePopup = () => { diff --git a/src/app/(dashboard)/assign_zone_project/CompleteAffectation.jsx b/src/app/(dashboard)/assign_zone_project/CompleteAffectation.jsx new file mode 100644 index 0000000..16d9adf --- /dev/null +++ b/src/app/(dashboard)/assign_zone_project/CompleteAffectation.jsx @@ -0,0 +1,303 @@ +"use client" +import React, { useState, useEffect, useRef } from 'react' +import { useNotification } from '@/context/NotificationContext' +import CancelIcon from "@/static/image/svg/cancel.svg" +import UserIcon from "@/static/image/svg/user.svg" +import DeskIcon from "@/static/image/svg/study-desk.svg" +import AddIcon from "@/static/image/svg/add.svg" +import fetchRequest from "@/app/lib/fetchRequest"; +import Loader from '@/components/Loader/Loader' + + + +const CompleteAffectation = ({ setIsOpen, listAffectationsState, affectations, fullAffectations, mutateProjectAffection }) => { + const [ selectedProject, setSelectedProject ] = useState(null) + const [ zones, setZones ] = useState([]) + const [ loading, setLoading ] = useState(false) + const [ places , setPlaces ] = useState([]) + const [ nbrCollabs, setNbrCollabs ] = useState(0) + const [ collabsAttributed, setCollabsAttributed ] = useState(0) + const { toggleNotification } = useNotification() + const [ selectedZone, setSelectedZone ] = useState(null) + const [ otherPlaces, setOtherPlaces ] = useState([]) + const [ selectedOtherZone, setSelectedOtherZone ] = useState(null) + + + const getZones = async (day, week) => { + console.log("day, week", day, week) + setLoading(true) + try { + const {isSuccess, errors, data} = await fetchRequest(`/zoaning/affectingProject/${day}/${week}`, {method: 'GET'}) + if(isSuccess){ + setCollabsAttributed(0) + setPlaces([]) + if(data.zones && data.zones.length === 0){ + toggleNotification({ + visible: true, + message: "Il y'a pas de zones pour cette semaine et ce jour.", + type: "warning" + }) + setZones([]) + }else{ + console.log("dsqqqqqdsqdqs") + setZones(data.zones) + } + + }else{ + // handle error + setLoading(false) + } + } catch (error) { + console.log(error) + toggleNotification({ title: 'Erreur', content: error.message, type: 'error' }) + } finally { + setLoading(false) + } + } + + const handleProjectSelection = (project) => { + if(selectedProject && selectedProject.project.id === project.project.id){ + setSelectedProject(null) + setZones([]) + setPlaces([]) + setCollabsAttributed(0) + return + } + console.log(project) + setSelectedProject(project) + getZones(project.jour, project.semaine) + setNbrCollabs(project.nbr_personnes_restant) + } + + + useEffect( () => { + if(nbrCollabs > 0 && places.length > 0){ + if( nbrCollabs <= places.length){ + setCollabsAttributed(nbrCollabs) + }else{ + setCollabsAttributed(places.length) + } + } + }, [nbrCollabs, places]) + + const handleSelectionZone = async (e) => { + const zone_id = e.target.value + const related_affecations = fullAffectations.filter( (element) => element.jour == selectedProject.jour && element.semaine == selectedProject.week && element.id_zone.id == zone_id).map(element => element.id) + try{ + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces`, + {method: 'POST', body: JSON.stringify({ + related_affecations: related_affecations, + zone_id: zone_id + })}) + if(isSuccess){ + console.log(data.places) + setSelectedZone(zone_id) + setPlaces(data.places) + setOtherPlaces([]) + setSelectedOtherZone(null) + }else{ + // handle error + setPlaces([]) + } + }catch(error){ + console.log(error) + } + } + + const handleOtherZoneSelection = async (e) => { + const zone_id = e.target.value + const related_affecations = fullAffectations.filter( (element) => element.jour == selectedProject.jour && element.semaine == selectedProject.semaine && element.id_zone.id == zone_id).map(element => element.id) + setSelectedOtherZone(zone_id) + try{ + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/countingPlaces`, + {method: 'POST', body: JSON.stringify({ + related_affecations: related_affecations, + zone_id: zone_id + })} + ) + if(isSuccess){ + setOtherPlaces(data.places) + }else{ + // handle error + } + }catch(error){ + console.log(error) + toggleNotification({ + visible: true, + message: "Internal Server Error", + type: "error" + }) + } + } + const handleAddCollab = () =>{ + setCollabsAttributed(collabsAttributed + 1) + } + + const handleMinusCollab = () =>{ + setCollabsAttributed(collabsAttributed - 1) + } + + console.log("collabsAttributed", collabsAttributed) + console.log("nbrCollabs", nbrCollabs) + console.log("places", places) + + + + const handleAssignProject = async () => { + const finalData = { + id_zone: selectedZone, + id_project: selectedProject.project.id, + jour: selectedProject.jour, + semaine: selectedProject.semaine, + nombre_personnes: nbrCollabs, + places_disponibles: (collabsAttributed > places.length) ? 0 : places.length - collabsAttributed, + places_occuper: (collabsAttributed > places.length) ? places.length : collabsAttributed, + places: (collabsAttributed > places.length) ? places.map( (element) => element.id) : places.map( (element, index) => index < collabsAttributed && element.id).filter(id => id !== false) + } + if( selectedOtherZone && otherPlaces.length > 0){ + finalData.otherZone = { + id_zone: selectedOtherZone, + places_disponibles: ( (collabsAttributed - places.length ) > otherPlaces.length ) ? 0 : otherPlaces.length - (collabsAttributed - places.length), + places_occuper: ( (collabsAttributed - places.length ) > otherPlaces.length ) ? otherPlaces.length : collabsAttributed - places.length, + places: ( (collabsAttributed - places.length ) > otherPlaces.length ) ? otherPlaces.map( (element) => element.id) : otherPlaces.map( (element, index) => (index < (collabsAttributed - places.length)) && element.id).filter(id => id !== false) + } + } + console.log(finalData) + try{ + const { isSuccess, errors, data } = await fetchRequest(`/zoaning/affectingProject`, {method: 'POST', body: JSON.stringify(finalData)}) + if(isSuccess){ + const newAffectations = [...fullAffectations, data.main_zone, ...(data.second_zone ? [data.second_zone] : [])] + listAffectationsState(newAffectations) + mutateProjectAffection(newAffectations) + toggleNotification({ + visible: true, + message: "Projet affecté avec succès.", + type: "success" + }) + closePopup(false) + }else{ + console.log(errors) + toggleNotification({ + visible: true, + message: "Erreur lors de l'affectation du projet", + type: "error" + }) + } + }catch(error){ + console.log(error) + toggleNotification({ + visible: true, + message: "Internal Server Error", + type: "error" + }) + } + } + + + const closePopup = () => { + setIsOpen() + setSelectedProject(null) + setSelectedZone(null) + setCollabsAttributed(0) + setPlaces([]) + setNbrCollabs(0) + setSelectedOtherZone(null) + setOtherPlaces([]) + setZones([]) + setLoading(false) + } + + + + return ( +
+
+

Compléter l'affectation

+ {setIsOpen(false)}} className="h-8 w-8 cursor-pointer absolute top-2 right-2 fill-neutral-600" /> +
+
+

Veuillez sélectionner un projet

+
+ {(affectations) && + affectations.map((element, index) => +
handleProjectSelection(element)} key={index} className='border-b border-gray-200'> + Projet: {element?.project?.nom} -- Collaborateurs: {element.nbr_personnes_restant} +
+ ) + } +
+
+
+

Veuillez sélectionner une zone

+
+ {(!loading) ? + (zones && zones.length) ? + + : +
+ Aucune zone disponible +
+ : +
+ +
+ + } +
+

: {places.length}

+
+
+
+ {(collabsAttributed > 0) &&
+

Collaborateurs affectées

+
+
+ {(collabsAttributed) ? collabsAttributed : 0} +
+ + +
+
+
+
+
0) ? "text-red-400" : "text-sushi-500"}`}>{nbrCollabs - collabsAttributed}
+ 0) ? "fill-red-400" : "fill-sushi-500"}`} /> +
+
+
0) ? "text-red-400" : "text-sushi-500"}`}>{(places.length+ otherPlaces.length) - collabsAttributed}
+ 0) ? "fill-red-400" : "fill-sushi-500"}`} /> +
+
+
+
} + {((collabsAttributed - places.length) > 0) &&
+

Veuillez sélectionner une autre zonne pour compléter l'affectation (optionnel)

+
+ +
+

: {otherPlaces.length}

+
+
+
} +
+
+ +
+
+
+ ) +} + +export default CompleteAffectation \ No newline at end of file diff --git a/src/app/(dashboard)/assign_zone_project/page.jsx b/src/app/(dashboard)/assign_zone_project/page.jsx index 85c5bee..758252e 100644 --- a/src/app/(dashboard)/assign_zone_project/page.jsx +++ b/src/app/(dashboard)/assign_zone_project/page.jsx @@ -9,12 +9,14 @@ import EditIcon from "@/static/image/svg/edit.svg"; import DeleteIcon from "@/static/image/svg/delete.svg"; import fetchRequest from "@/app/lib/fetchRequest"; import ConfirmationModal from "@/app/ui/ConfirmationModal"; +import CompleteAffectation from './CompleteAffectation'; const AffectingZoneProject = () => { const [isOpen, setIsOpen] = useState(false) + const [ isOpenCompleteAffectation, setIsOpenCompleteAffectation ] = useState(false) const [ listProjectsAffected, setListProjectsAffected ] = useState([]) const [ isLoadingListProjects, setIsLoadingListProjects ] = useState(false) const { toggleNotification } = useNotification() @@ -22,9 +24,55 @@ const AffectingZoneProject = () => { const [ selectedDay, setSelectedDay ] = useState(null) const [ selectedAffectaionToDelete, setSelectedAffectationToDelete ] = useState(null) const [isModalOpen, setModalOpen] = useState(false); + const [ listProjectsSemiAffected, setListProjectsSemiAffected ] = useState([]) useEffect(() => { + + // this function is to detect if there is a project that is not fully affected and return the corresponding data to use + function filterAndGroupProjects(data) { + // Step 1: Create an object to aggregate data by id_project, semaine, and jour + const aggregatedProjects = {}; + + data.forEach(project => { + const projectId = project.id_project.id; + const week = project.semaine; + const day = project.jour; + const key = `${projectId}-${week}-${day}`; + + if (!aggregatedProjects[key]) { + aggregatedProjects[key] = { + id_project: project.id_project, + semaine: week, + jour: day, + places_disponibles: 0, + places_occuper: 0, + nombre_personnes: project.nombre_personnes + }; + } + + aggregatedProjects[key].places_disponibles += project.places_disponibles; + aggregatedProjects[key].places_occuper += project.places_occuper; + }); + + // Step 2: Filter out projects that don't meet the condition + const filteredProjects = Object.values(aggregatedProjects).filter(project => { + return project.nombre_personnes - project.places_occuper > 0; + }); + + // Step 3: Prepare the final result with additional fields + const result = filteredProjects.map(project => { + return { + project: project.id_project, + semaine: project.semaine, + jour: project.jour, + nbr_personnes_restant: project.nombre_personnes - project.places_occuper + }; + }); + + return result; + } + const getListOfAffectedProjects = async () => { setIsLoadingListProjects(true) try{ @@ -39,6 +87,7 @@ const AffectingZoneProject = () => { } if(isSuccess){ setListProjectsAffected(data) + setListProjectsSemiAffected(filterAndGroupProjects(data)) }else{ toggleNotification({ visible: true, @@ -65,6 +114,10 @@ const AffectingZoneProject = () => { setIsOpen(!isOpen) } + const handleOpenCompleteAffectation = () => { + setIsOpenCompleteAffectation(!isOpenCompleteAffectation) + } + const handleDeleteAffectation = async () => { try{ var { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/deteleAffectedProject/${selectedAffectaionToDelete.id}`, {method: 'DELETE'}) @@ -74,7 +127,9 @@ const AffectingZoneProject = () => { message: "Affectation supprimer avec succès", type: "success" }) - setListProjectsAffected(listProjectsAffected.filter(affected => affected.id !== selectedAffectaionToDelete.id)) + const filteredProjectsAffected = listProjectsAffected.filter(affected => affected.id !== selectedAffectaionToDelete.id) + setListProjectsAffected(filteredProjectsAffected) + mutateProjectsAffectaionCheck(filteredProjectsAffected) }else if(status === 404){ toggleNotification({ visible: true, @@ -110,17 +165,63 @@ const AffectingZoneProject = () => { setSelectedAffectationToDelete(null); }; + + // function to detect if there is a project not fully affected ( outside the use effect to mutate the changement of the state) + function filterAndGroupProjects(data) { + // Step 1: Create an object to aggregate data by id_project, semaine, and jour + const aggregatedProjects = {}; + + data.forEach(project => { + const projectId = project.id_project.id; + const week = project.semaine; + const day = project.jour; + const key = `${projectId}-${week}-${day}`; + + if (!aggregatedProjects[key]) { + aggregatedProjects[key] = { + id_project: project.id_project, + semaine: week, + jour: day, + places_disponibles: 0, + places_occuper: 0, + nombre_personnes: project.nombre_personnes + }; + } + + aggregatedProjects[key].places_disponibles += project.places_disponibles; + aggregatedProjects[key].places_occuper += project.places_occuper; + }); + + // Step 2: Filter out projects that don't meet the condition + const filteredProjects = Object.values(aggregatedProjects).filter(project => { + return project.nombre_personnes - project.places_occuper > 0; + }); + + // Step 3: Prepare the final result with additional fields + const result = filteredProjects.map(project => { + return { + project: project.id_project, + semaine: project.semaine, + jour: project.jour, + nbr_personnes_restant: project.nombre_personnes - project.places_occuper + }; + }); + + return result; + } + + const mutateProjectsAffectaionCheck = (passedData) => { + console.log("mutaion triggered") + setListProjectsSemiAffected(filterAndGroupProjects(passedData)) + } + + return ( -
-
- {isOpen && } +
+
+ {isOpen && } + {(isOpenCompleteAffectation) && }

List des Projets attribuer

-
- Il y a des projets qui ne sont pas complètement affecter. - -