From 24876e59ff8ff43ceaa78fff0a63b8cf244d9176 Mon Sep 17 00:00:00 2001 From: Raed BOUAFIF Date: Fri, 24 May 2024 19:41:48 +0100 Subject: [PATCH] zoaning completed etage/zone --- src/app/etage/AddEtageComponent.jsx | 24 +++- src/app/etage/page.jsx | 45 +++++- src/app/zone/CreateNewZone.jsx | 69 ++++----- src/app/zone/RowZone.jsx | 210 +++++++++++++++++++++++++--- src/app/zone/page.jsx | 69 ++++++--- 5 files changed, 333 insertions(+), 84 deletions(-) diff --git a/src/app/etage/AddEtageComponent.jsx b/src/app/etage/AddEtageComponent.jsx index d84ae41..17e46b8 100644 --- a/src/app/etage/AddEtageComponent.jsx +++ b/src/app/etage/AddEtageComponent.jsx @@ -2,18 +2,20 @@ import Loader from '@/components/Loader/Loader' import React, { useState, useRef } from 'react' import fetchRequest from '../lib/fetchRequest' +import { useNotification } from '@/context/NotificationContext' + const AddEtageComponent = ({ etagesState })=> { const [ numeroEtage, setNumeroEtage ] = useState("") const [isLoading, setIsLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState(null) const inputRef = useRef(null) + const { toggleNotification } = useNotification() + const handleNewEtage = (e) => { - setErrorMessage(null) setNumeroEtage(e.target.value) } @@ -28,12 +30,25 @@ const AddEtageComponent = ({ etagesState })=> { inputRef.current.value = "" console.log(data) etagesState((prevEtagesState) => [...prevEtagesState, data]); + toggleNotification({ + type: "success", + message: "L'étage a été créer avec succès.", + visible: true, + }) } else { setIsLoading(false) if(errors.type == "ValidationError") - {setErrorMessage(errors.detail.numero)} + toggleNotification({ + type: "warning", + message: "Le numéro détage déja existe.", + visible: true, + }) else{ - setErrorMessage(errorMessage.type) + toggleNotification({ + type: "error", + message: "Une erreur s'est produite lors de la création de la zone.", + visible: true, + }) } console.log(errors) } @@ -49,7 +64,6 @@ const AddEtageComponent = ({ etagesState })=> { - {errorMessage && {errorMessage}} ) diff --git a/src/app/etage/page.jsx b/src/app/etage/page.jsx index 5177625..94f79ae 100644 --- a/src/app/etage/page.jsx +++ b/src/app/etage/page.jsx @@ -4,12 +4,20 @@ import AddEtageComponent from './AddEtageComponent' import fetchRequest from '../lib/fetchRequest' import { useState, useEffect } from 'react'; import Loader from '@/components/Loader/Loader' +import { useNotification } from '@/context/NotificationContext' +import ConfirmationModal from "@/app/ui/ConfirmationModal"; + const Etage = ()=> { const [etages, setEtages] = useState([]) const [ isLoadingData, setIsLoadingData ] = useState(true) + const { toggleNotification } = useNotification() + const [isModalOpen, setModalOpen] = useState(false); + const [etageToDelete, setEtageToDelete] = useState(null); + + // Fetch data from external API useEffect(() => { const getAllEtages = async () => { @@ -39,11 +47,38 @@ const Etage = ()=> { console.log(etages) console.log("etage: ", etage) setEtages((prevEtages) => prevEtages.filter((e) => e.id !== etage.id)); + toggleNotification({ + type: "success", + message: "L'étage a été supprimer avec succès.", + visible: true, + }) + }else{ + toggleNotification({ + type: "error", + message: "Une erreur s'est produite lors de la suppression de l'étage.", + visible: true, + }) } }catch(error){ - console.log(error) + toggleNotification({ + type: "error", + message: "Internal Server Error", + visible: true, + }) } } + + const handleDeleteClick = (etage) => { + setEtageToDelete(etage); + setModalOpen(true); + } + + const handleConfirmDelete = () => { + handleDeleteEtage(etageToDelete); + setModalOpen(false); + setProjectToDelete(null); + }; + return(
@@ -56,7 +91,7 @@ const Etage = ()=> { (
  • Etage numéro: {etage.numero} -
    {handleDeleteEtage(etage)}}> +
    {handleDeleteClick(etage)}}>
  • @@ -75,6 +110,12 @@ const Etage = ()=> {
    + setModalOpen(false)} + onConfirm={handleConfirmDelete} + message={`Êtes-vous sûr de vouloir supprimer l'étage numéro "${etageToDelete?.numero}"?`} + /> ) diff --git a/src/app/zone/CreateNewZone.jsx b/src/app/zone/CreateNewZone.jsx index cdfb8cb..d02c3f5 100644 --- a/src/app/zone/CreateNewZone.jsx +++ b/src/app/zone/CreateNewZone.jsx @@ -3,54 +3,56 @@ import React, { useState, useEffect, useRef } from 'react' import fetchRequest from '../lib/fetchRequest' import Loader from '@/components/Loader/Loader' import { Island_Moments } from 'next/font/google' +import { useNotification } from '@/context/NotificationContext' -const CreateNewZone = () => { - const [etages, setEtages] = useState([]) + +const CreateNewZone = ({ zoneState, etages }) => { const [error, setError] = useState(null) - const [isLoadingEtages, setIsLoadingEtages] = useState(true) const [isLoadingAction, setIsLoadingAction] = useState(false) - const [nomZone, setNomZone] = useState("") + const [nomZone, setNomZone] = useState(null) const [selectedEtage, setSelectedEtage] = useState(null) const inputRef = useRef(null) const selectRef = useRef(null) - 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() - }, []) + const { toggleNotification } = useNotification() + const handleSubmit = async (event) => { event.preventDefault() - isLoadingAction(true) - const { data, errors, isSuccess } = await fetchRequest("/zoning/zones/", { + setIsLoadingAction(true) + const { data, errors, isSuccess } = await fetchRequest("/zoaning/zones/", { method: "POST", - body: JSON.stringify({ name: nomZone }) + body: JSON.stringify({ nom: nomZone, id_etage: selectedEtage }) }) if (isSuccess) { - isLoadingAction(false) - appendPrivilege(data) + setIsLoadingAction(false) + zoneState((prevZoneValue) => [...prevZoneValue, {...data, id_etage: etages.find(etage => etage.id === data.id_etage)}]); inputRef.current.value = "" - setPrivilegeName("") + selectRef.current.value = "" + setNomZone(null) + setSelectedEtage(null) + toggleNotification({ + visible: true, + message: "La zone a été créer avec succès.", + type: "success" + }) } else { - isLoadingAction(false) + setIsLoadingAction(false) if (errors.type === "ValidationError") { - if (errors.detail.name) { - setError("Le privilège existe déjà") + if (errors.detail.non_field_errors) { + toggleNotification({ + type: "warning", + message: "Le nom de la zone saisie existe déjà dans l'étage sélectionné.", + visible: true, + }) } + }else{ + toggleNotification({ + type: "error", + message: "Une erreur s'est produite lors de la création de la zone.", + visible: true, + }) } console.log(errors) } @@ -67,6 +69,9 @@ const CreateNewZone = () => { setSelectedEtage(event.target.value) } + console.log(selectedEtage) + console.log(nomZone) + return( @@ -79,7 +84,7 @@ const CreateNewZone = () => {
    - {(etages && etages?.length) && etages.map((etage, index) => ( @@ -91,7 +96,7 @@ const CreateNewZone = () => {

    -
    diff --git a/src/app/zone/RowZone.jsx b/src/app/zone/RowZone.jsx index 1d71ca2..5ef8172 100644 --- a/src/app/zone/RowZone.jsx +++ b/src/app/zone/RowZone.jsx @@ -1,47 +1,213 @@ "use client" -import React from 'react' +import React, { useState, useEffect, useRef } from 'react'; import fetchRequest from '../lib/fetchRequest' -import { useState, useEffect } from 'react'; import Loader from '@/components/Loader/Loader' -import CreateNewZone from './CreateNewZone' import DeleteIcon from "@/static/image/svg/delete.svg" import EditIcon from "@/static/image/svg/edit.svg" import CancelIcon from "@/static/image/svg/cancel.svg" import CheckIcon from "@/static/image/svg/check.svg" +import { useNotification } from '@/context/NotificationContext' +import ConfirmationModal from "@/app/ui/ConfirmationModal"; -const RowZone = ({ id, nom, etage }) => { + + +const RowZone = ({ id, nom, etage, zonesState, etages }) => { + //states + const [isUpdating, setIsUpdating] = useState(false) + const [zoneName, setZoneName] = useState(nom) + const [selectedEtage, setSelectedEtage] = useState(etage) + const [loadingStatus, setLoadingStatus] = useState(false) + const [isModalOpen, setModalOpen] = useState(false); + const { toggleNotification } = useNotification() + //refs + const inputRef = useRef(null) + const selectRef = useRef(null) + const rowRef = useRef(null) + + //Logic + useEffect(() => { + setZoneName(nom) + setSelectedEtage(etage?.id) + selectRef.current.value = etage?.id + inputRef.current.value = nom + }, [nom, etage]) + + const handleUpdateZone = async () => { + setLoadingStatus(true) + const { isSuccess, errors, data, status } = await fetchRequest(`/zoaning/zones/${id}/`, { + method: "PATCH", + body: JSON.stringify({ nom: zoneName, id_etage: selectedEtage }) + }) + setLoadingStatus(false) + if (isSuccess) { + console.log(data.data) + zonesState((prevZonesValue) => prevZonesValue.map( (element) => element.id === id ? {...data.data, id_etage: etages.find(etage => etage.id === data.data.id_etage)} : element )) + setIsUpdating(false) + toggleNotification({ + visible: true, + message: "La zone a été modifiée avec succès.", + type: "success" + }) + } else { + if (errors.type === "ValidationError") { + if (errors.detail.name) { + toggleNotification({ + type: "warning", + message: "Le nom de la zone existe déjà", + visible: true, + }) + }else if (errors.detail.non_field_errors) { + toggleNotification({ + type: "warning", + message: "Le nom de la zone saisie existe déjà dans l'étage sélectionné.", + visible: true, + }) + } + else { + toggleNotification({ + visible: true, + message: "Erreur de validation de la zone", + type: "warning" + }) + } + } else if (status === 404) { + toggleNotification({ + visible: true, + message: "La zone n'a pas été trouvé", + type: "warning" + }) + } else { + toggleNotification({ + visible: true, + message: "Internal Server Error", + type: "error" + }) + } + console.log(errors) + } + + } + + const handleDelete = async () => { + const { isSuccess, errors, status } = await fetchRequest(`/zoaning/zones/${id}/`, { method: "DELETE" }) + if (isSuccess) { + zonesState((prevZonesState) => prevZonesState.filter((element) => element.id !== id)) + toggleNotification({ + visible: true, + message: "La zone a été supprimée avec succès", + type: "success" + }) + } else if (status == 404) { + toggleNotification({ + visible: true, + message: "La zone n'a pas été trouvé", + type: "warning" + }) + } else { + console.log(errors) + toggleNotification({ + visible: true, + message: "Internal Server Error", + type: "error" + }) + } + } + + const cancelUpdate = () => { + setIsUpdating(false) + setZoneName(nom) + setSelectedEtage(etage.id) + selectRef.current.value = etage.id + inputRef.current.value = nom + } + + const handleUpdateBlur = (event) => { + const eventTarget = event.target + let isInsideRowRef = false; + let element = eventTarget; + while (element !== null) { + if (element === rowRef.current) { + isInsideRowRef = true; + break; + } + if (element.parentElement === null) { + isInsideRowRef = false; + break; + } + element = element.parentElement; + } + if (!isInsideRowRef && element?.classList.contains("zoneRowSVG")) return; + if (!isInsideRowRef) { + cancelUpdate(); + document.removeEventListener("click", handleUpdateBlur); + } + } + + useEffect(() => { + if (isUpdating && inputRef?.current && selectRef?.current) { + inputRef.current.focus() + selectRef.current.focus() + document.addEventListener("click", handleUpdateBlur) + } + return () => { + document.removeEventListener("click", handleUpdateBlur) + } + }, [isUpdating]) + + const handleDeleteClick = () => { + setModalOpen(true); + } + + const handleConfirmDelete = () => { + handleDelete(); + setModalOpen(false); + }; + return( - + - + setZoneName(event.target.value)} defaultValue={nom} type='text' className='disabled:bg-white border-0 rounded-md px-2 enabled:drop-shadow border-none enabled:bg-gray-100 duration-100 h-10 outline-none' /> - setSelectedEtage(event.target.value)} name="listEtage" id="listEtage" className="disabled:bg-white border-0 rounded-md px-2 enabled:drop-shadow border-none enabled:bg-gray-100 duration-100 h-10 outline-none"> + {(etages && etages?.length) && + etages.map((element,index) => ( + + )) + } -
    - -
    -
    - - -
    + + } + setModalOpen(false)} + onConfirm={handleConfirmDelete} + message={`Êtes-vous sûr de vouloir supprimer la zone "${nom}"?`} + /> ) } diff --git a/src/app/zone/page.jsx b/src/app/zone/page.jsx index a1c067e..5fc1e53 100644 --- a/src/app/zone/page.jsx +++ b/src/app/zone/page.jsx @@ -8,54 +8,77 @@ import DeleteIcon from "@/static/image/svg/delete.svg" import EditIcon from "@/static/image/svg/edit.svg" import CancelIcon from "@/static/image/svg/cancel.svg" import CheckIcon from "@/static/image/svg/check.svg" +import { isArray } from '../lib/TypesHelper' import RowZone from './RowZone' const Zone = ()=> { - const [zones, setZones] = useState([]) + const [ zones, setZones ] = useState([]) const [ isLoadingData, setIsLoadingData ] = useState(true) + const [etages, setEtages] = useState([]) + + // Fetch data from external API useEffect(() => { const getAllZones = async () => { try{ const {isSuccess, errors, data} = await fetchRequest('/zoaning/zones/', {method: 'GET'}) - setIsLoadingData(false) if(isSuccess){ setZones(data) }else{ setZones([]) } }catch(error){ - setIsLoadingData(false) + console.log(error) + } + } + const getAllEtages = async () => { + try{ + const {isSuccess, errors, data} = await fetchRequest('/zoaning/etages/', {method: 'GET'}) + if(isSuccess){ + setEtages(data) + }else{ + setEtages([]) + } + }catch(error){ console.log(error) } } getAllZones() + getAllEtages() + setIsLoadingData(false) }, []) + + console.log(zones) return(
    - -

    List des Zones

    - {zones ? -
    -

    Pas encore des habilitations

    -
    - : -
    - - - - - - - {zones.map((element) => { - return - })} -
    ZoneEtageAction
    -
    - } + {!isLoadingData ? + <> + +

    List des Zones

    + {isArray(zones) && zones?.length !== 0 && isArray(etages) && etages?.length !== 0 ? +
    + + + + + + + {zones?.map((element) => { + return + })} +
    ZoneEtageAction
    +
    + : +
    +

    Pas encore des habilitations

    +
    } + + : +
    + }
    -- GitLab