diff --git a/src/app/assign_zone_project/AssignProject.jsx b/src/app/assign_zone_project/AssignProject.jsx new file mode 100644 index 0000000000000000000000000000000000000000..8e9ac5407ce08249e51f55ee0368ee87f0de235a --- /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 0000000000000000000000000000000000000000..02831647fd8869b268d4294c2d346193a01f1554 --- /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/globals.css b/src/app/globals.css index b6ba7d37334c09788aa0026af48f37b048eed1d8..83bb86b29ca5fa8da57892f56d4c513438b7b93b 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/place/CreateNewPlace.jsx b/src/app/place/CreateNewPlace.jsx index 8036b0a0a4ee1a6020c82522f941ac346eb23e74..061a0cb3d4cad3464963e83934da27ff82441106 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/app/planning/PlanningTable.jsx b/src/app/planning/PlanningTable.jsx index 4ddc99b741dcfdbafb870ec1d7a94ed81f1c2d3e..6b90c54e48199a56202614573a81d03384486426 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 42a6408e254349ea6db8a7494b09c90699758185..f8c140aac292a7b396a1cac50663c1b3cc2272f3 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 c29923c376a4f4b8b348331bd333698179f01078..0000000000000000000000000000000000000000 --- 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 a829627bd3e8d27b61e7ec801211939711058be8..372907bca0bed35d0ba49c209a54a95dc87be3ae 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 0000000000000000000000000000000000000000..2f383cf8af8fac1879dd2893cdcbb684ef6a662b --- /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 0c4f1d1c4d52d89123f26d592f4f6dbfc749fd24..425cb09badb9664502865be810eaf05a12cc5fe6 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 35e67e9ef9c957caba4dd59c0a72c688a2920c34..6696cca9298180003b366377dfe1fcada019d4cf 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 = () => {
    - -
    - - - -
    diff --git a/src/static/image/svg/desk.svg b/src/static/image/svg/desk.svg new file mode 100644 index 0000000000000000000000000000000000000000..9c413fe224320ed9ba78ee0dd6d5d8aee2acfa5f --- /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 0000000000000000000000000000000000000000..7b2fcbedc7de74fe5b5e9cdaa4293325512f7ff2 --- /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 0000000000000000000000000000000000000000..e501f458077e266a936e8adf199ed99e2dbe70a6 --- /dev/null +++ b/src/static/image/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file