From 5cb3513e1ef15378af5ce8ec297d6970bb15f3ff Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Thu, 30 May 2024 15:42:05 +0100 Subject: [PATCH 1/2] updated planning interfaces --- src/app/globals.css | 62 +++++ src/app/planning/PlanningTable.jsx | 48 ++-- src/app/planning/page.jsx | 222 +++++++++++++++--- .../type-presence-semaines-jours/page.jsx | 100 -------- .../EntityForm.jsx | 0 .../EntityList.jsx | 6 +- src/app/planning/type-presence/page.jsx | 62 +++++ src/app/projects/ProjectForm.jsx | 4 +- src/app/ui/Header.jsx | 4 +- src/app/ui/LogoutButton.js | 2 +- src/app/ui/SideBar.jsx | 64 +---- 11 files changed, 345 insertions(+), 229 deletions(-) delete mode 100644 src/app/planning/type-presence-semaines-jours/page.jsx rename src/app/planning/{type-presence-semaines-jours => type-presence}/EntityForm.jsx (100%) rename src/app/planning/{type-presence-semaines-jours => type-presence}/EntityList.jsx (79%) create mode 100644 src/app/planning/type-presence/page.jsx diff --git a/src/app/globals.css b/src/app/globals.css index b6ba7d3..83bb86b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -30,4 +30,66 @@ ::-webkit-scrollbar-track { background-color: rgba(0, 0, 0, 0.1); /* Color of the track */ +} + +/* Tab content - closed */ +.tab-content { + max-height: 0; + -webkit-transition: max-height .35s; + -o-transition: max-height .35s; + transition: max-height .35s; +} +/* :checked - resize to full height */ +.tab input:checked ~ .tab-content { + max-height: 100%; +} +/* Label formatting when open */ +.tab input:checked + label{ + /*@apply text-xl p-5 border-l-2 border-indigo-500 bg-gray-100 text-indigo*/ + font-size: 1.25rem; /*.text-xl*/ + padding: 1.25rem; /*.p-5*/ + /*border-left-width: 2px; !*.border-l-2*!*/ + /*border-color: #93a84c; !*.border-indigo*!*/ + /*color: #93a84c; !*.text-indigo*!*/ +} +/* Icon */ +.tab label::after { + float:right; + right: 0; + top: 0; + display: block; + width: 1.5em; + height: 1.5em; + line-height: 1.25; + font-size: 1.25rem; + text-align: center; + -webkit-transition: all .35s; + -o-transition: all .35s; + transition: all .35s; +} +/* Icon formatting - closed */ +.tab input[type=checkbox] + label::after { + content: "+"; + font-weight:bold; /*.font-bold*/ + border-width: 1px; /*.border*/ + border-radius: 9999px; /*.rounded-full */ + border-color: #5c5c5c; /*.border-grey*/ +} +.tab input[type=radio] + label::after { + content: "\25BE"; + font-weight:bold; /*.font-bold*/ + border-width: 1px; /*.border*/ + border-radius: 9999px; /*.rounded-full */ + border-color: #5c5c5c; /*.border-grey*/ +} +/* Icon formatting - open */ +.tab input[type=checkbox]:checked + label::after { + transform: rotate(315deg); + background-color: #93a84c; /*.bg-indigo*/ + color: #f8fafc; /*.text-grey-lightest*/ +} +.tab input[type=radio]:checked + label::after { + transform: rotateX(180deg); + background-color: #93a84c; /*.bg-indigo*/ + color: #f8fafc; /*.text-grey-lightest*/ } \ No newline at end of file diff --git a/src/app/planning/PlanningTable.jsx b/src/app/planning/PlanningTable.jsx index 4ddc99b..6b90c54 100644 --- a/src/app/planning/PlanningTable.jsx +++ b/src/app/planning/PlanningTable.jsx @@ -2,35 +2,18 @@ import React, {useEffect, useState} from 'react'; import fetchRequest from "@/app/lib/fetchRequest"; import {useNotification} from "@/context/NotificationContext"; -const PlanningTable = ({ data, onTypePresenceChange }) => { +const PlanningTable = ({ data, typePresences, onTypePresenceChange }) => { // fetch type presence - const [typePresences, setTypePresences] = useState([]); const [errors, setErrors] = useState(); const [loading, setLoading] = useState(false); const {toggleNotification} = useNotification() - const [selectedTypePresence, setSelectedTypePresence] = useState(); - - const fetchTypePresences = async () => { - const { isSuccess, errors, data } = await fetchRequest('/type-presences/'); - if (isSuccess) { - setTypePresences(data); - setErrors(null); - } - else { - console.error("Failed to fetch type presences"); - setErrors(errors) - } - } - useEffect(() => { - fetchTypePresences() - }, []); return (
- + - + @@ -40,19 +23,20 @@ const PlanningTable = ({ data, onTypePresenceChange }) => { {data.map((row, rowIndex) => ( - - + + {row.days.map((day, dayIndex) => ( - ))} diff --git a/src/app/planning/page.jsx b/src/app/planning/page.jsx index 42a6408..f8c140a 100644 --- a/src/app/planning/page.jsx +++ b/src/app/planning/page.jsx @@ -4,20 +4,28 @@ import Dropdown from "@/app/ui/Dropdown"; import PlanningTable from "@/app/planning/PlanningTable"; import fetchRequest from "@/app/lib/fetchRequest"; import {useNotification} from "@/context/NotificationContext"; +import ConfirmationModal from "@/app/ui/ConfirmationModal"; const PlanningPage = () => { + const blankData = { + planning_data: [ + {week: 1, days: [{}, {}, {}, {}, {}]}, + {week: 2, days: [{}, {}, {}, {}, {}]}, + {week: 3, days: [{}, {}, {}, {}, {}]}, + {week: 4, days: [{}, {}, {}, {}, {}]}, + {week: 5, days: [{}, {}, {}, {}, {}]}, + ] + } const [projects, setProjects] = useState([]); const [selectedProject, setSelectedProject] = useState(''); - const [planningData, setPlanningData] = useState([ - {week: 'Semaine 1', days: ['', '', '', '', '']}, - {week: 'Semaine 2', days: ['Présentiel', '', 'Présentiel', '', 'Présentiel']}, - {week: 'Semaine 3', days: ['', '', '', '', '']}, - {week: 'Semaine 4', days: ['Présentiel', 'Présentiel', '', '', '']}, - {week: 'Semaine 5', days: ['Présentiel', 'Présentiel', '', '', '']}, - ]); + const [planningData, setPlanningData] = useState(blankData); const [errors, setErrors] = useState(); const [loading, setLoading] = useState(false); - const {toggleNotification} = useNotification() + const {toggleNotification} = useNotification(); + const [typePresences, setTypePresences] = useState([]); + const [isModalOpen, setModalOpen] = useState(false); + + const fetchProjects = async () => { const {isSuccess, errors, data} = await fetchRequest('/projects/'); @@ -37,39 +45,148 @@ const PlanningPage = () => { const handleProjectChange = (e) => setSelectedProject(e.target.value); const fetchPlanningData = async () => { - // Fetch planning data from your API based on selectedProject, selectedWeek, and selectedDay - // Here we'll use dummy data for demonstration purposes - const data = [ - {week: 'Semaine 1', days: ['', '', '', '', '']}, - {week: 'Semaine 2', days: ['Présentiel', '', 'Présentiel', '', 'Présentiel']}, - {week: 'Semaine 3', days: ['', '', '', '', '']}, - {week: 'Semaine 4', days: ['Présentiel', 'Présentiel', '', '', '']}, - {week: 'Semaine 5', days: ['Présentiel', 'Présentiel', '', '', '']}, - ]; - setPlanningData(data); + if (!selectedProject) return; + + // const { isSuccess, errors, data } = await fetchRequest(`/planning/?project=${selectedProject}`); + const {isSuccess, errors, data} = await fetchRequest(`/plannings/project/${selectedProject}/`); + if (isSuccess) { + // if the project have no data the response is [] in this case we should set the blankData + if (data.length === 0) { + setPlanningData(blankData); + toggleNotification({ + visible: true, + message: `Projet ${selectedProject} n'a pas de données de planification`, + type: "warning" + }) + setErrors(null); + return; + } + setPlanningData(data[0]); + setErrors(null); + } else { + console.error("Failed to fetch planning data"); + setPlanningData(blankData) + setErrors(errors); + } }; + useEffect(() => { + fetchPlanningData(); + }, [selectedProject]); + + const fetchTypePresences = async () => { + const {isSuccess, errors, data} = await fetchRequest('/type-presences/'); + if (isSuccess) { + setTypePresences(data); + setErrors(null); + } else { + console.error("Failed to fetch type presences"); + setErrors(errors) + } + } + useEffect(() => { + fetchTypePresences() + }, []); const handleTypePresenceChange = (weekIndex, dayIndex, value) => { - const updatedData = [...planningData]; - updatedData[weekIndex].days[dayIndex] = value; + const updatedData = {...planningData}; + // get the presence type by id (value is a string convert it to int + const typePresence = typePresences.find(typePresence => typePresence.id === parseInt(value)); + console.log(typePresence) + + // the value I want to add should be {"id": 1, "nom": "Travail à Temps Partiel"} and not just the id + updatedData.planning_data[weekIndex].days[dayIndex] = typePresence; setPlanningData(updatedData); + console.log(value) + console.log(updatedData); + }; + + const handleSave = async () => { + setLoading(true); + const requestBody = {id_project: selectedProject, planning_data: planningData.planning_data}; + console.log({id_project: selectedProject, planning_data: planningData.planning_data}) + const {isSuccess, errors} = await fetchRequest(`/plannings/`, { + method: 'POST', + body: JSON.stringify(requestBody) + }); + setLoading(false); + if (isSuccess) { + toggleNotification({ + visible: true, + message: 'Planning data saved successfully', + type: 'success' + }); + fetchPlanningData() + } else { + setErrors(errors); + toggleNotification({ + visible: true, + message: 'Failed to save planning data', + type: 'error' + }); + } + }; + + + + const handleUpdate = async () => { + setLoading(true); + const requestBody = {id_project: selectedProject, planning_data: planningData.planning_data}; + const {isSuccess, errors} = await fetchRequest(`/plannings/`, { + method: 'PUT', + body: JSON.stringify(requestBody) + }); + setLoading(false); + if (isSuccess) { + toggleNotification({ + visible: true, + message: 'Planning data updated successfully', + type: 'success' + }); + } else { + setErrors(errors); + toggleNotification({ + visible: true, + message: 'Failed to update planning data', + type: 'error' + }); + } + } + + const handleDelete = async () => { + setLoading(true); + // dlete by planning id not project id + const {isSuccess, errors} = await fetchRequest(`/plannings/${planningData.id}/`, { + method: 'DELETE' + }); + setLoading(false); + if (isSuccess) { + setPlanningData(blankData); + toggleNotification({ + visible: true, + message: 'Planning data deleted successfully', + type: 'success' + }); + } else { + setErrors(errors); + toggleNotification({ + visible: true, + message: 'Failed to delete planning data', + type: 'error' + }); + } + }; + const handleDeleteClick = () => { + setModalOpen(true); + }; + + const handleConfirmDelete = () => { + handleDelete(); + setModalOpen(false); }; return (

Planning

-
- -
- {/*project using select*/} -
- + +
+ {/* crud buttons*/} +
+ + {planningData.id ? + <> + + + : + + } +
+ setModalOpen(false)} + onConfirm={handleConfirmDelete} + message={`Are you sure you want to delete this planning data?`} + />
); }; diff --git a/src/app/planning/type-presence-semaines-jours/page.jsx b/src/app/planning/type-presence-semaines-jours/page.jsx deleted file mode 100644 index c29923c..0000000 --- a/src/app/planning/type-presence-semaines-jours/page.jsx +++ /dev/null @@ -1,100 +0,0 @@ -"use client"; - -import {useEffect, useState} from 'react'; -import fetchRequest from "@/app/lib/fetchRequest"; -import EntityList from "@/app/planning/type-presence-semaines-jours/EntityList"; -import EntityForm from "@/app/planning/type-presence-semaines-jours/EntityForm"; -import {useNotification} from "@/context/NotificationContext"; - -const ManagePage = () => { - const [typePresences, setTypePresences] = useState([]); - const [semaines, setSemaines] = useState([]); - const [jours, setJours] = useState([]); - const [editingEntity, setEditingEntity] = useState({ entity: null, id: null }); - const { toggleNotification } = useNotification() - - const fetchData = async () => { - const typePresencesResponse = await fetchRequest('/type-presences/'); - const semainesResponse = await fetchRequest('/semaines/'); - const joursResponse = await fetchRequest('/jours/'); - - if (typePresencesResponse.isSuccess) setTypePresences(typePresencesResponse.data); - if (semainesResponse.isSuccess) setSemaines(semainesResponse.data); - if (joursResponse.isSuccess) setJours(joursResponse.data); - }; - - useEffect(() => { - fetchData(); - }, []); - - const handleDelete = async (endpoint, id, setState, currentState) => { - const response = await fetchRequest(`/${endpoint}/${id}/`, {method: 'DELETE'}); - if (response.isSuccess) { - setState(currentState.filter(item => item.id !== id)); - toggleNotification({ - visible: true, - message: `${currentState.find(item => item.id === id).nom} a été supprimé avec succès`, - type: "success" - }) - } - await fetchData(); - }; - - return ( - <> -

Manage Entities

-
-
-

Type Presence

- setEditingEntity({ entity: null, id: null })} - /> - setEditingEntity({ entity: 'type-presences', id })} - /> -
- {/*
*/} - {/*

Semaines

*/} - {/* setEditingEntity({ entity: null, id: null })}*/} - {/* />*/} - {/* setEditingEntity({ entity: 'semaines', id })}*/} - {/* />*/} - {/*
*/} - {/*
*/} - {/*

Jours

*/} - {/* setEditingEntity({ entity: null, id: null })}*/} - {/* />*/} - {/* setEditingEntity({ entity: 'jours', id })}*/} - {/* />*/} - {/*
*/} -
- - ); -}; - -export default ManagePage; diff --git a/src/app/planning/type-presence-semaines-jours/EntityForm.jsx b/src/app/planning/type-presence/EntityForm.jsx similarity index 100% rename from src/app/planning/type-presence-semaines-jours/EntityForm.jsx rename to src/app/planning/type-presence/EntityForm.jsx diff --git a/src/app/planning/type-presence-semaines-jours/EntityList.jsx b/src/app/planning/type-presence/EntityList.jsx similarity index 79% rename from src/app/planning/type-presence-semaines-jours/EntityList.jsx rename to src/app/planning/type-presence/EntityList.jsx index a829627..372907b 100644 --- a/src/app/planning/type-presence-semaines-jours/EntityList.jsx +++ b/src/app/planning/type-presence/EntityList.jsx @@ -32,13 +32,15 @@ const EntityList = ({ title, items, setState, handleDelete, handleEdit }) => {
Semaine / JourSemaine/Jour J1 J2 J3
{row.week}
Semaine {row.week} - + +
diff --git a/src/app/planning/type-presence/page.jsx b/src/app/planning/type-presence/page.jsx new file mode 100644 index 0000000..2f383cf --- /dev/null +++ b/src/app/planning/type-presence/page.jsx @@ -0,0 +1,62 @@ +"use client"; + +import {useEffect, useState} from 'react'; +import fetchRequest from "@/app/lib/fetchRequest"; +import EntityList from "@/app/planning/type-presence/EntityList"; +import EntityForm from "@/app/planning/type-presence/EntityForm"; +import {useNotification} from "@/context/NotificationContext"; + +const ManagePage = () => { + const [typePresences, setTypePresences] = useState([]); + const [editingEntity, setEditingEntity] = useState({ entity: null, id: null }); + const { toggleNotification } = useNotification() + + const fetchData = async () => { + const typePresencesResponse = await fetchRequest('/type-presences/'); + + if (typePresencesResponse.isSuccess) setTypePresences(typePresencesResponse.data); + }; + + useEffect(() => { + fetchData(); + }, []); + + const handleDelete = async (endpoint, id, setState, currentState) => { + const response = await fetchRequest(`/${endpoint}/${id}/`, {method: 'DELETE'}); + if (response.isSuccess) { + setState(currentState.filter(item => item.id !== id)); + toggleNotification({ + visible: true, + message: `${currentState.find(item => item.id === id).nom} a été supprimé avec succès`, + type: "success" + }) + } + await fetchData(); + }; + + return ( + <> +

Gérer Les Entités

+
+
+

Type Presence

+ setEditingEntity({ entity: null, id: null })} + /> + setEditingEntity({ entity: 'type-presences', id })} + /> +
+
+ + ); +}; + +export default ManagePage; diff --git a/src/app/projects/ProjectForm.jsx b/src/app/projects/ProjectForm.jsx index 0c4f1d1..425cb09 100644 --- a/src/app/projects/ProjectForm.jsx +++ b/src/app/projects/ProjectForm.jsx @@ -87,7 +87,7 @@ const ProjectForm = ({ onAddProject, onEditProject, editingProject, setEditingPr }; return ( -
+
)} -
+
{userIds.map((user, index) => (
{ console.log('isAuth', isAuth) console.log('sessionData', sessionData) return ( -
+

TeamBook

@@ -25,7 +25,7 @@ const Header = async () => { {isAuth ? ( <>
  • -

    {sessionData.username}

    +

    {sessionData.username}

  • {/*
  • */} {/* diff --git a/src/app/ui/SideBar.jsx b/src/app/ui/SideBar.jsx index 35e67e9..6696cca 100644 --- a/src/app/ui/SideBar.jsx +++ b/src/app/ui/SideBar.jsx @@ -1,23 +1,15 @@ const SideBar = () => { return ( -
    -
    +
    +
    -
    - - L - -
    - -
    +
    • General @@ -26,7 +18,7 @@ const SideBar = () => {
    • Administration @@ -50,7 +42,7 @@ const SideBar = () => {
    • Banned Users @@ -59,7 +51,7 @@ const SideBar = () => {
    • Calendar @@ -71,7 +63,7 @@ const SideBar = () => {
    • Billing @@ -80,7 +72,7 @@ const SideBar = () => {
    • Invoices @@ -89,7 +81,7 @@ const SideBar = () => {
    • Consultaion @@ -113,7 +105,7 @@ const SideBar = () => {
    • Details @@ -122,7 +114,7 @@ const SideBar = () => {
    • Security @@ -132,7 +124,7 @@ const SideBar = () => { @@ -145,36 +137,6 @@ const SideBar = () => {
    - -
    - - - -
    -- GitLab From 2caf89aaad7e0bb1430b293a8ffdcd3a39d824aa Mon Sep 17 00:00:00 2001 From: Raed BOUAFIF Date: Thu, 30 May 2024 15:42:27 +0100 Subject: [PATCH 2/2] preparing feature/affecting/project-zone --- src/app/assign_zone_project/AssignProject.jsx | 112 ++++++++++++++ src/app/assign_zone_project/page.jsx | 146 ++++++++++++++++++ src/app/place/CreateNewPlace.jsx | 2 +- src/static/image/svg/desk.svg | 1 + src/static/image/svg/study-desk.svg | 1 + src/static/image/svg/user.svg | 1 + 6 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 src/app/assign_zone_project/AssignProject.jsx create mode 100644 src/app/assign_zone_project/page.jsx create mode 100644 src/static/image/svg/desk.svg create mode 100644 src/static/image/svg/study-desk.svg create mode 100644 src/static/image/svg/user.svg diff --git a/src/app/assign_zone_project/AssignProject.jsx b/src/app/assign_zone_project/AssignProject.jsx new file mode 100644 index 0000000..8e9ac54 --- /dev/null +++ b/src/app/assign_zone_project/AssignProject.jsx @@ -0,0 +1,112 @@ +"use client" +import React, { useState, useEffect } from 'react' +import Loader from '@/components/Loader/Loader' +import fetchRequest from '../lib/fetchRequest' +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" + + +const AssignProject = ({ setIsOpen }) => { + + return ( +
    +
    +

    Attribuer un projet à une zone.

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

    Veuillez sélectionner une date

    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +

    : 50

    +
    +
    +
    + +
    +

    : 50

    +
    +
    +
    +
    + + +
    +
    +
    20
    + +
    + restant +
    +
    +
    +
    +

    Veuillez sélectionner une autre zonne (optionnel)

    +
    + +
    +

    Places: 50

    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + ) +} + +export default AssignProject \ No newline at end of file diff --git a/src/app/assign_zone_project/page.jsx b/src/app/assign_zone_project/page.jsx new file mode 100644 index 0000000..0283164 --- /dev/null +++ b/src/app/assign_zone_project/page.jsx @@ -0,0 +1,146 @@ +"use client" +import React, { useState } from 'react'; +import AddIcon from "@/static/image/svg/add.svg"; +import AssignProject from './AssignProject'; + + +const AffectingZoneProject = () => { + const [isOpen, setIsOpen] = useState(false) + + + const handleOpenAssignProject = () => { + setIsOpen(!isOpen) + } + return ( +
    +
    + {isOpen && } +

    List des Projets attribuer

    +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Date + + Plateau + + Projet + + Places occupées + + Nombre des personnes + + Places disponible + + Actions +
    + Apple MacBook Pro 17" + + Silver + + Laptop + + $2999 + + $2999 + + $2999 + + Edit +
    + Apple MacBook Pro 17" + + Silver + + Laptop + + $2999 + + $2999 + + $2999 + + Edit +
    + Apple MacBook Pro 17" + + Silver + + Laptop + + $2999 + + $2999 + + $2999 + + Edit +
    +
    + + ); +} + + +export default AffectingZoneProject; \ No newline at end of file diff --git a/src/app/place/CreateNewPlace.jsx b/src/app/place/CreateNewPlace.jsx index 8036b0a..061a0cb 100644 --- a/src/app/place/CreateNewPlace.jsx +++ b/src/app/place/CreateNewPlace.jsx @@ -88,7 +88,7 @@ const CreateNewPlace = ({placesState, tables}) => { {(tables && tables?.length) && tables.map((table, index) => ( - + )) } diff --git a/src/static/image/svg/desk.svg b/src/static/image/svg/desk.svg new file mode 100644 index 0000000..9c413fe --- /dev/null +++ b/src/static/image/svg/desk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/static/image/svg/study-desk.svg b/src/static/image/svg/study-desk.svg new file mode 100644 index 0000000..7b2fcbe --- /dev/null +++ b/src/static/image/svg/study-desk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/static/image/svg/user.svg b/src/static/image/svg/user.svg new file mode 100644 index 0000000..e501f45 --- /dev/null +++ b/src/static/image/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file -- GitLab