From fe02b4d87d8327aa807a922d2755e864f40eddea Mon Sep 17 00:00:00 2001 From: Oussama El Benney Date: Tue, 2 Jul 2024 10:21:26 +0100 Subject: [PATCH] fixed planning + type de presence + auth pages --- .../(dashboard)/planning/PlanningTable.jsx | 190 ++++++++++-- src/app/(dashboard)/planning/page.jsx | 292 ++++++------------ .../planning/type-presence/EntityForm.jsx | 23 +- .../planning/type-presence/EntityList.jsx | 61 ++-- .../planning/type-presence/TypePresence.jsx | 71 +++++ src/app/(dashboard)/projects/ProjectForm.jsx | 28 +- .../(dashboard)/projects/ProjectsFilter.jsx | 11 +- src/app/(dashboard)/projects/page.jsx | 2 +- src/app/auth/change-password/page.jsx | 123 ++++---- src/app/auth/forgot-password/page.jsx | 57 ++-- src/app/auth/layout.jsx | 48 +++ src/app/auth/login/page.jsx | 44 +-- src/app/lib/constants.js | 27 +- src/app/ui/Accordion.jsx | 34 ++ .../ui/{LogoutButton.js => LogoutButton.jsx} | 0 src/components/Notification/Error.jsx | 2 +- src/middleware.js | 8 +- src/static/image/pc_phone.png | Bin 0 -> 37383 bytes src/static/image/svg/addchart.svg | 3 + src/static/image/svg/line-sushi.svg | 3 + src/static/image/svg/line-white4.svg | 3 + src/static/image/svg/perm_identity.svg | 10 + src/static/image/svg/visibility_off.svg | 10 + src/static/image/teambook-vertical.png | Bin 0 -> 3995 bytes tailwind.config.js | 2 +- 25 files changed, 651 insertions(+), 401 deletions(-) create mode 100644 src/app/(dashboard)/planning/type-presence/TypePresence.jsx create mode 100644 src/app/auth/layout.jsx create mode 100644 src/app/ui/Accordion.jsx rename src/app/ui/{LogoutButton.js => LogoutButton.jsx} (100%) create mode 100644 src/static/image/pc_phone.png create mode 100644 src/static/image/svg/addchart.svg create mode 100644 src/static/image/svg/line-sushi.svg create mode 100644 src/static/image/svg/line-white4.svg create mode 100644 src/static/image/svg/perm_identity.svg create mode 100644 src/static/image/svg/visibility_off.svg create mode 100644 src/static/image/teambook-vertical.png diff --git a/src/app/(dashboard)/planning/PlanningTable.jsx b/src/app/(dashboard)/planning/PlanningTable.jsx index 0db1822..2a95805 100644 --- a/src/app/(dashboard)/planning/PlanningTable.jsx +++ b/src/app/(dashboard)/planning/PlanningTable.jsx @@ -1,26 +1,139 @@ import React, {useEffect, useState} from 'react'; import fetchRequest from "@/app/lib/fetchRequest"; import {useNotification} from "@/context/NotificationContext"; +import {blankData} from "@/app/lib/constants"; +import ConfirmationModal from "@/app/ui/ConfirmationModal"; +import ArrowUturnLeft from "@/static/image/svg/arrow-uturn-left.svg"; +import DeleteIcon from "@/static/image/svg/trash-bold.svg"; +import EditIcon from "@/static/image/svg/edit.svg"; -const PlanningTable = ({ data, typePresences, onTypePresenceChange, selectedProject }) => { +const PlanningTable = ({ project, typePresences}) => { // fetch type presence const [errors, setErrors] = useState(); const [loading, setLoading] = useState(false); const {toggleNotification} = useNotification() const [selectedPresence, setSelectedPresence] = useState(''); + const [planningData, setPlanningData] = useState(project.planning ? project.planning : blankData); + const [isModalOpen, setModalOpen] = useState(false); + + const handleSave = async () => { + // verify if planing data has an empty value + const emptyValue = planningData.planning_data.some(week => week.days.some(day => day.nom === '')); + if (emptyValue) { + toggleNotification({ + visible: true, + message: 'Veuillez remplir toutes les valeurs de planification', + type: 'error' + }); + return; + } + setLoading(true); + const requestBody = {id_project: project.id, planning_data: planningData.planning_data}; + console.log({id_project: project.id, planning_data: planningData.planning_data}) + const {isSuccess, errors,data} = await fetchRequest(`/plannings/`, { + method: 'POST', + body: JSON.stringify(requestBody) + }); + setLoading(false); + if (isSuccess) { + toggleNotification({ + visible: true, + message: 'Données de planification enregistrées avec succès', + type: 'success' + }); + setPlanningData(data); + } else { + setErrors(errors); + toggleNotification({ + visible: true, + message: 'Échec de lenregistrement des données de planification', + type: 'error' + }); + } + }; + + const handleUpdate = async () => { + setLoading(true); + const requestBody = {id_project: project.id, planning_data: planningData.planning_data}; + const {isSuccess, errors} = await fetchRequest(`/plannings/${planningData.id}/`, { + method: 'PUT', + body: JSON.stringify(requestBody) + }); + setLoading(false); + if (isSuccess) { + toggleNotification({ + visible: true, + message: 'Données de planification mises à jour avec succès', + type: 'success' + }); + } else { + setErrors(errors); + toggleNotification({ + visible: true, + message: 'Échec de la mise à jour des données de planification', + type: 'error' + }); + } + } + + const handleDelete = async () => { + setLoading(true); + // delete 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: 'Données de planification supprimées avec succès', + type: 'success' + }); + } else { + setErrors(errors); + toggleNotification({ + visible: true, + message: 'Échec de la suppression des données de planification', + type: 'error' + }); + } + }; + const handleDeleteClick = () => { + setModalOpen(true); + }; + const handleConfirmDelete = () => { + handleDelete(); + setModalOpen(false); + }; + const handleTypePresenceChange = (weekIndex, 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)); + + // 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); + setPlanningData((prevData) => { // prevData is the previous state of planningData + const updatedData = {...prevData}; + updatedData.planning_data[weekIndex].days[dayIndex] = typePresence; + return updatedData; + }); + }; const handleRadioChange = (e) => { const newPresence = e.target.value; setSelectedPresence(newPresence); - data.forEach((row, rowIndex) => { + console.log('sata',planningData); + planningData?.planning_data.forEach((row, rowIndex) => { row.days.forEach((day, dayIndex) => { - onTypePresenceChange(rowIndex, dayIndex, newPresence); + handleTypePresenceChange(rowIndex, dayIndex, newPresence); }); }); }; return ( -
+
Réglez Tout sur : {typePresences.map((typePresence) => ( @@ -38,27 +151,27 @@ const PlanningTable = ({ data, typePresences, onTypePresenceChange, selectedProj ))}
- - - - - - - - + + + + + + + + - {data.map((row, rowIndex) => ( - - + {planningData?.planning_data.map((row, rowIndex) => ( + + {row.days.map((day, dayIndex) => ( -
Semaine/JourJ1J2J3J4J5
Semaine/JourLundi (J1)Mardi (J2)Mercredi (J3)Jeudi (J4)Vendredi (J5)
Semaine {row.week}
Semaine {row.week} +
+ {/* crud for buttons*/} +
+ + {planningData.id ? + <> + + + : + + } + +
+ + setModalOpen(false)} + onConfirm={handleConfirmDelete} + message={`Etes-vous sûr de vouloir supprimer ces données de planification ?`} + />
); }; diff --git a/src/app/(dashboard)/planning/page.jsx b/src/app/(dashboard)/planning/page.jsx index f6dc6d3..e223713 100644 --- a/src/app/(dashboard)/planning/page.jsx +++ b/src/app/(dashboard)/planning/page.jsx @@ -1,34 +1,27 @@ 'use client'; -import { useEffect, useState } from 'react'; +import React, {useEffect, useState} from 'react'; import Dropdown from "@/app/ui/Dropdown"; import PlanningTable from "@/app/(dashboard)/planning/PlanningTable"; import fetchRequest from "@/app/lib/fetchRequest"; -import { useNotification } from "@/context/NotificationContext"; +import {useNotification} from "@/context/NotificationContext"; import ConfirmationModal from "@/app/ui/ConfirmationModal"; +import ArrowUturnLeft from "@/static/image/svg/arrow-uturn-left.svg"; +import SearchIcon from "@/static/image/svg/search.svg"; +import AddChart from "@/static/image/svg/addchart.svg"; +import TypePresence from "@/app/(dashboard)/planning/type-presence/TypePresence"; +import Accordion from "@/app/ui/Accordion"; +import {blankData} from "@/app/lib/constants"; const PlanningPage = () => { - const blankData = { - planning_data: [ - { week: 1, days: [{"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}] }, - { week: 2, days: [{"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}] }, - { week: 3, days: [{"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}] }, - { week: 4, days: [{"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}] }, - { week: 5, days: [{"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}, {"id": 2, "nom": "Teletravail"}] }, - ] - } const [projects, setProjects] = useState([]); - const [selectedProject, setSelectedProject] = useState(''); - const [planningData, setPlanningData] = useState(blankData); + const [selectedProject, setSelectedProject] = useState({}); const [errors, setErrors] = useState(); - const [loading, setLoading] = useState(false); - const { toggleNotification } = useNotification(); const [typePresences, setTypePresences] = useState([]); - const [isModalOpen, setModalOpen] = useState(false); - + const [openPopup, setOpenPopup] = useState(false); const fetchProjects = async () => { - const { isSuccess, errors, data } = await fetchRequest('/projects/'); + const {isSuccess, errors, data} = await fetchRequest('/projects/'); if (isSuccess) { setProjects(data); setErrors(null); @@ -42,34 +35,17 @@ const PlanningPage = () => { fetchProjects(); }, []); - const handleProjectChange = (e) => setSelectedProject(e.target.value); - - const fetchPlanningData = async () => { - 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); - setErrors(null); - return; - } - setPlanningData(data[0]); - setErrors(null); - } else { - console.error("Failed to fetch planning data"); - setPlanningData(blankData) - setErrors(errors); + const handleProjectChange = (e) => { + if (e.target.value === '') { + setSelectedProject({}); + return; } - }; - useEffect(() => { - fetchPlanningData(); - }, [selectedProject]); + const project = projects.find(project => project.id == e.target.value); + setSelectedProject(project); + } const fetchTypePresences = async () => { - const { isSuccess, errors, data } = await fetchRequest('/type-presences/'); + const {isSuccess, errors, data} = await fetchRequest('/type-presences/'); if (isSuccess) { setTypePresences(data); setErrors(null); @@ -82,169 +58,79 @@ const PlanningPage = () => { fetchTypePresences() }, []); - const handleTypePresenceChange = (weekIndex, 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 () => { - if (!selectedProject) { - toggleNotification({ - visible: true, - message: 'Veuillez sélectionner un projet', - type: 'error' - }); - return; - } - // verify if planing data has an empty value - const emptyValue = planningData.planning_data.some(week => week.days.some(day => day.nom === '')); - if (emptyValue) { - toggleNotification({ - visible: true, - message: 'Veuillez remplir toutes les valeurs de planification', - type: 'error' - }); - return; - } - 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: 'Données de planification enregistrées avec succès', - type: 'success' - }); - fetchPlanningData() - } else { - setErrors(errors); - toggleNotification({ - visible: true, - message: 'Échec de lenregistrement des données de planification', - type: 'error' - }); - } - }; - - const handleUpdate = async () => { - setLoading(true); - const requestBody = {id_project: selectedProject, planning_data: planningData.planning_data}; - const {isSuccess, errors} = await fetchRequest(`/plannings/${planningData.id}/`, { - method: 'PUT', - body: JSON.stringify(requestBody) - }); - setLoading(false); - if (isSuccess) { - toggleNotification({ - visible: true, - message: 'Données de planification mises à jour avec succès', - type: 'success' - }); - } else { - setErrors(errors); - toggleNotification({ - visible: true, - message: 'Échec de la mise à jour des données de planification', - 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: 'Données de planification supprimées avec succès', - type: 'success' - }); - } else { - setErrors(errors); - toggleNotification({ - visible: true, - message: 'Échec de la suppression des données de planification', - type: 'error' - }); - } - }; - const handleDeleteClick = () => { - setModalOpen(true); - }; - - const handleConfirmDelete = () => { - handleDelete(); - setModalOpen(false); - }; - return ( -
-

Planning

- -
- + <> +
+

Recherche

+
+
+ + +
+
+
+ + +
- {/* crud buttons*/} -
- - {planningData.id ? - <> - - - : - - } - +
+
+

Planning

+ +
+ {/*iterate for projects */} +
+ {selectedProject.id ? ( +
+ +
+ +
+
+
+ ) : projects.length === 0 ? ( +
+

Pas encore des projets

+
+ ) : ( + projects.map(project => ( +
+ +
+ +
+
+
+ )) + )} +
- setModalOpen(false)} - onConfirm={handleConfirmDelete} - message={`Etes-vous sûr de vouloir supprimer ces données de planification ?`} - /> -
+ {openPopup && } + ); }; diff --git a/src/app/(dashboard)/planning/type-presence/EntityForm.jsx b/src/app/(dashboard)/planning/type-presence/EntityForm.jsx index 408e3a4..3397d1e 100644 --- a/src/app/(dashboard)/planning/type-presence/EntityForm.jsx +++ b/src/app/(dashboard)/planning/type-presence/EntityForm.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import fetchRequest from "@/app/lib/fetchRequest"; import {useNotification} from "@/context/NotificationContext"; @@ -44,31 +44,32 @@ const EntityForm = ({ entity, id, onSaved, onCancel }) => { }; return ( -
+
- + setNom(e.target.value)} - className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md" + className="w-full rounded-md px-3 duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none" required />
-
+
diff --git a/src/app/(dashboard)/planning/type-presence/EntityList.jsx b/src/app/(dashboard)/planning/type-presence/EntityList.jsx index d2f5d9e..4086efa 100644 --- a/src/app/(dashboard)/planning/type-presence/EntityList.jsx +++ b/src/app/(dashboard)/planning/type-presence/EntityList.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import ConfirmationModal from '@/app/ui/ConfirmationModal'; import EditIcon from "@/static/image/svg/edit.svg"; -import DeleteIcon from "@/static/image/svg/delete.svg"; +import DeleteIcon from "@/static/image/svg/trash-bold.svg" const EntityList = ({ title, items, setState, handleDelete, handleEdit }) => { const [isModalOpen, setModalOpen] = useState(false); @@ -20,38 +20,35 @@ const EntityList = ({ title, items, setState, handleDelete, handleEdit }) => { return (
- - - - - - - - - {items.map(item => ( - - - - - ))} - -
NomActions
{item.nom} - - -
+
+ {items.map((item, index) => ( +
+

{item.nom}

+
+ {item.nom === 'Presentiel' || item.nom ==="Teletravail" ? <>: + } + +
+
+ ))} +
setModalOpen(false)} diff --git a/src/app/(dashboard)/planning/type-presence/TypePresence.jsx b/src/app/(dashboard)/planning/type-presence/TypePresence.jsx new file mode 100644 index 0000000..6a2cdc3 --- /dev/null +++ b/src/app/(dashboard)/planning/type-presence/TypePresence.jsx @@ -0,0 +1,71 @@ +"use client"; + +import React, {useEffect, useState} from 'react'; +import fetchRequest from "@/app/lib/fetchRequest"; +import EntityList from "@/app/(dashboard)/planning/type-presence/EntityList"; +import EntityForm from "@/app/(dashboard)/planning/type-presence/EntityForm"; +import {useNotification} from "@/context/NotificationContext"; +import CancelIcon from "@/static/image/svg/cancel.svg"; + +const TypePresence = ({setIsOpen}) => { + 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 ( +
+
+ { + setEditingEntity({entity: null, id: null}); + setIsOpen(false)}} + className="h-8 w-8 cursor-pointer md:absolute fixed top-2 right-2 fill-neutral-600"/> +
+
+
+

Type de présence

+ setEditingEntity({entity: null, id: null})} + /> + setEditingEntity({entity: 'type-presences', id})} + /> +
+
+
+
+
+ ); +}; + +export default TypePresence; diff --git a/src/app/(dashboard)/projects/ProjectForm.jsx b/src/app/(dashboard)/projects/ProjectForm.jsx index 9126515..40e29d1 100644 --- a/src/app/(dashboard)/projects/ProjectForm.jsx +++ b/src/app/(dashboard)/projects/ProjectForm.jsx @@ -177,7 +177,7 @@ const ProjectForm = ({setIsOpen, onAddProject, onEditProject, editingProject, se
  • !isUserChosen(user) && handleUserSelect(user)} - className={`px-3 py-2 border-b border-chicago-200 ${isUserChosen(user) ? 'bg-neutral-200 cursor-not-allowed' : 'hover:bg-sushi-200/40 cursor-pointer'}`} + className={`px-3 py-2 border-b border-chicago-200 ${isUserChosen(user) ? 'bg-neutral-100 cursor-not-allowed' : 'hover:bg-sushi-200/40 cursor-pointer'}`} > {/*add tick is chosen*/} {isUserChosen(user) && '✔ '} @@ -187,7 +187,7 @@ const ProjectForm = ({setIsOpen, onAddProject, onEditProject, editingProject, se )}
    + className="flex flex-wrap items-center justify-start content-start gap-x-2 gap-y-1 mt-2 p-2 h-40 bg-sushi-100 rounded-md border-2 border-sushi-100 overflow-y-auto"> {userIds.map((user, index) => (

    @@ -205,19 +205,21 @@ const ProjectForm = ({setIsOpen, onAddProject, onEditProject, editingProject, se

    {errors.userIds &&

    {errors.userIds}

    }
    - + )} + - {/* cancel*/} - {editingProject && ( - - )} +
  • diff --git a/src/app/(dashboard)/projects/ProjectsFilter.jsx b/src/app/(dashboard)/projects/ProjectsFilter.jsx index 1820453..13f6c1b 100644 --- a/src/app/(dashboard)/projects/ProjectsFilter.jsx +++ b/src/app/(dashboard)/projects/ProjectsFilter.jsx @@ -47,7 +47,16 @@ const ProjectsFilter = ({setFilter}) => {
    diff --git a/src/app/(dashboard)/projects/page.jsx b/src/app/(dashboard)/projects/page.jsx index 463f4a2..abdaafc 100644 --- a/src/app/(dashboard)/projects/page.jsx +++ b/src/app/(dashboard)/projects/page.jsx @@ -35,7 +35,7 @@ const Projects = () => { const fetchProjects = async (pageNumber = 1, signal) => { setLoading(true); - const URLQuery = `?page=${pageNumber}&?nom=${(filter.name)}&user_count=${(filter.user_count)}&nomClient=${(filter.name_client)}&dateDebut=${(filter.date_debut)}&dateFin=${(filter.date_fin)}` + const URLQuery = `?page=${pageNumber}&nom=${(filter.name)}&user_count=${(filter.user_count)}&nomClient=${(filter.name_client)}&dateDebut=${(filter.date_debut)}&dateFin=${(filter.date_fin)}` const {isSuccess, errors, data} = await fetchRequest(`/projects/pagination/${URLQuery}`, {signal: signal}) setLoading(false); diff --git a/src/app/auth/change-password/page.jsx b/src/app/auth/change-password/page.jsx index 6bc46ca..235d187 100644 --- a/src/app/auth/change-password/page.jsx +++ b/src/app/auth/change-password/page.jsx @@ -2,11 +2,12 @@ import fetchRequest from '@/app/lib/fetchRequest' import Image from 'next/image' import Link from 'next/link' -import React, { useEffect, useState } from 'react' -import { useSearchParams } from 'next/navigation' +import React, {useEffect, useState} from 'react' +import {useSearchParams} from 'next/navigation' import Loader from '@/components/Loader/Loader' -import { PASSWORD_REGEX } from '@/app/lib/constants' -import { useNotification } from '@/context/NotificationContext' +import {PASSWORD_REGEX} from '@/app/lib/constants' +import {useNotification} from '@/context/NotificationContext' +import TeambookVertical from "@/static/image/teambook-vertical.png"; const ChangePassword = () => { const [isSuccess, setIsSuccess] = useState(false) @@ -15,11 +16,11 @@ const ChangePassword = () => { const [confirmPassword, setConfirmPassword] = useState("") const [isLoading, setIsLoading] = useState(false) const params = useSearchParams(); - const { toggleNotification } = useNotification() + const {toggleNotification} = useNotification() const handleChangePassword = async (event) => { event.preventDefault() setIsLoading(true) - const { isSuccess, data, errors } = await fetchRequest(`/password_reset/confirm/?token=${params.get("token")}`, { + const {isSuccess, data, errors} = await fetchRequest(`/password_reset/confirm/?token=${params.get("token")}`, { method: "POST", body: JSON.stringify({ password, @@ -28,21 +29,18 @@ const ChangePassword = () => { }) if (isSuccess) { setIsSuccess(true) - } - else { + } else { console.log(errors) setIsLoading(false) if (errors.type === "ValidationError") { if (errors.detail.token) { setFormErrors(["Le lien que vous avez utilisé pour réinitialiser votre mot de passe est invalide"]) - } - else { + } else { setFormErrors(["Le Mot de passe est invalide"]) } } else if (errors?.detail?.detail?.startsWith("The OTP password entered is not valid")) { setFormErrors(["Le lien que vous avez utilisé pour réinitialiser votre mot de passe est déja utilisé"]) - } - else { + } else { toggleNotification({ type: "error", message: "Internal Server Error", @@ -66,60 +64,61 @@ const ChangePassword = () => { setFormErrors(currentErrors) }, [password, confirmPassword]) return ( -
    -
    -
    -
    - teamwill -
    - {(!isSuccess) &&
    -

    Change your password. -

    -
    -
      -
    • Le mot de passe doit contenir au moins 8 caractères
    • -
    • Le mot de passe doit contenir au moins un chiffre
    • -
    • Le mot de passe doit contenir au moins un caractère spécial
    • -
    +
    +
    + {TeambookVertical} +
    + {(!isSuccess) && +
    +

    Changer votre Mot de passe +

    +
    +
    + + setPassword(event.target.value)} type="password" + name="new_password1" id="new_password1" + className="w-full py-6 rounded-md px-3 duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none"/> +
    +
    + + setConfirmPassword(event.target.value)} type="password" + name="new_password2" id="new_password2" + className="w-full py-6 rounded-md px-3 duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none"/>
    - -
    - - setPassword(event.target.value)} type="password" name="new_password1" id="new_password1" - className="rounded-md px-3 w-full duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none" /> -
    -
    - - setConfirmPassword(event.target.value)} type="password" name="new_password2" id="new_password2" - className="rounded-md px-3 duration-150 delay-75 w-full focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none" /> -
    -
      0 && !isEmptyFields ? "bg-red-100 border border-red-300" : ""} min-h-10 px-3 text-xs py-3 rounded relative mt-9 mb-6 list-inside list-disc`} role="alert"> - {!isEmptyFields && formErrors.map((error, index) => { - return
    • {error}
    • - })} +
      +
        +
      • Le mot de passe doit contenir au moins 8 caractères
      • +
      • Le mot de passe doit contenir au moins un chiffre
      • +
      • Le mot de passe doit contenir au moins un caractère spécial
      -
      - -
      - -
      } - {(isSuccess) && ( -
      -

      The password has been changed!

      - log in again?
      - )} + +
        0 && !isEmptyFields ? "bg-red-100 border border-red-300 min-h-10" : ""} w-full px-3 text-xs py-3 rounded relative mt-9 list-inside list-disc`} + role="alert"> + {!isEmptyFields && formErrors.map((error, index) => { + return
      • {error}
      • + })} +
      + +
    } + {(isSuccess) && ( +
    +

    The password has been changed!

    + log in + again?
    -
    -
    + )} +
    ) } diff --git a/src/app/auth/forgot-password/page.jsx b/src/app/auth/forgot-password/page.jsx index d74d5db..434c423 100644 --- a/src/app/auth/forgot-password/page.jsx +++ b/src/app/auth/forgot-password/page.jsx @@ -5,6 +5,8 @@ import { useNotification } from '@/context/NotificationContext' import Image from 'next/image' import Link from 'next/link' import React, { useState } from 'react' +import TeambookVertical from "@/static/image/teambook-vertical.png"; +import UserIcon from "@/static/image/svg/perm_identity.svg"; const ForgotPassword = () => { const [email, setEmail] = useState("") @@ -40,39 +42,31 @@ const ForgotPassword = () => { } } return ( -
    -
    -
    - teamwill - {(!isSuccess) &&
    -
    -

    Forgot your password!! No Problem - Reset it here +
    + {TeambookVertical} + {(!isSuccess) &&
    +
    +

    Mot de passe oublié!! Aucun problème + Réinitialisez-le ici

    -
    -
    -
    -
    - setEmail(e.target.value)} autocomplete="off" id="email" name="email" type="text" - className="peer placeholder-transparent h-10 w-full border-b-2 border-gray-300 text-gray-900 focus:outline-none focus:borer-rose-600" - placeholder="Email address" /> - + +
    + +
    + setEmail(e.target.value)} id="email" name="email" type="text" + placeholder="Email address" + /> + +
    - - {(requestErrors.length !== 0) &&
    - -
    -
    } {(isSuccess) &&
    - - + +

    Email Sent!

    Check your email and open the link we sent to continue

    } -
    -

    ) } diff --git a/src/app/auth/layout.jsx b/src/app/auth/layout.jsx new file mode 100644 index 0000000..27b0f05 --- /dev/null +++ b/src/app/auth/layout.jsx @@ -0,0 +1,48 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; +import Hero from "@/static/image/pc_phone.png"; +import LineWhite from "@/static/image/svg/line-white4.svg"; +import LineSushi from "@/static/image/svg/line-sushi.svg"; + +const layout = ({children}) => { + return ( + <> +
    +
    + {children} + +
    + + Aide + + + Vos questions + +
    +
    +
    +
    + +
    +
    + Hero +
    +
    + + Teambook 2024 © + + + Politique de confidentialité + + + Termes et conditions + +
    +
    +
    + + ) +} + +export default layout \ No newline at end of file diff --git a/src/app/auth/login/page.jsx b/src/app/auth/login/page.jsx index 2bdb9a0..6ed5663 100644 --- a/src/app/auth/login/page.jsx +++ b/src/app/auth/login/page.jsx @@ -6,6 +6,10 @@ import Cookies from 'js-cookie'; import { createSession } from "@/app/lib/session"; import { useRouter } from 'next/navigation' import Loader from '@/components/Loader/Loader'; +import UserIcon from "@/static/image/svg/perm_identity.svg"; +import VisibilityIcon from "@/static/image/svg/visibility_off.svg"; +import Image from "next/image"; +import TeambookVertical from "@/static/image/teambook-vertical.png"; const LoginPage = () => { const [username, setUsername] = useState(''); @@ -48,17 +52,16 @@ const LoginPage = () => { }; return ( <> -
    -
    - teamwill + +
    + {TeambookVertical} +

    Connectez-vous à notre plateforme

    +

    Vous n avez pas de compte ? Créer un compte

    - + +
    { value={username} onChange={(e) => setUsername(e.target.value)} /> + +
    - + +
    { value={password} onChange={(e) => setPassword(e.target.value)} /> + +
    -

    Mot de passe oublié?

    +

    Mot de passe oublié?

    - {messages && ( -

    - {messages} -

    - )} + {messages && ( +

    + {messages} +

    + )}
    diff --git a/src/app/lib/constants.js b/src/app/lib/constants.js index 9d34296..18d2248 100644 --- a/src/app/lib/constants.js +++ b/src/app/lib/constants.js @@ -1,3 +1,28 @@ export const PASSWORD_REGEX = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[^a-zA-Z\d\s]).*$/; export const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@teamwillgroup\.com$/ -export const PAGINATION_SIZE = 3 \ No newline at end of file +export const PAGINATION_SIZE = 3 + +export const blankData = { + planning_data: [ + { + week: 1, + days: [{}, {}, {}, {}, {}] + }, + { + week: 2, + days: [{}, {}, {}, {}, {}] + }, + { + week: 3, + days: [{}, {}, {}, {}, {}] + }, + { + week: 4, + days: [{}, {}, {}, {}, {}] + }, + { + week: 5, + days: [{}, {}, {}, {}, {}] + }, + ] +} \ No newline at end of file diff --git a/src/app/ui/Accordion.jsx b/src/app/ui/Accordion.jsx new file mode 100644 index 0000000..9d46cd9 --- /dev/null +++ b/src/app/ui/Accordion.jsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; + +const Accordion = ({ title, children }) => { + const [isOpen, setIsOpen] = useState(false); + + return ( +
    + + {isOpen && ( +
    + {children} +
    + )} +
    + ); +}; + +export default Accordion; diff --git a/src/app/ui/LogoutButton.js b/src/app/ui/LogoutButton.jsx similarity index 100% rename from src/app/ui/LogoutButton.js rename to src/app/ui/LogoutButton.jsx diff --git a/src/components/Notification/Error.jsx b/src/components/Notification/Error.jsx index 6329a93..d5e5efe 100644 --- a/src/components/Notification/Error.jsx +++ b/src/components/Notification/Error.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from 'react' -import WarningIcon from "@/static/image/svg/WarningIcon.svg" +import WarningIcon from "@/static/image/svg/warningIcon.svg" import CrossIcon from "@/static/image/svg/cross.svg" import "./Notification.css" diff --git a/src/middleware.js b/src/middleware.js index 98b3441..c746b76 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -28,10 +28,10 @@ export default async function middleware(req) { const hasPrivileges = userPrivileges.some(privilege => path.startsWith(`/${privilege}`)); console.log('hasPrivileges', hasPrivileges) - if (isProtectedRoute && !hasPrivileges) { - console.log('.') - return NextResponse.redirect(new URL('/no-access', req.nextUrl)); - } + // if (isProtectedRoute && !hasPrivileges) { + // console.log('.') + // return NextResponse.redirect(new URL('/no-access', req.nextUrl)); + // } // 7. Redirect to /dashboard if the user is authenticated if ( diff --git a/src/static/image/pc_phone.png b/src/static/image/pc_phone.png new file mode 100644 index 0000000000000000000000000000000000000000..959f6fd1b3b3b0897507029262a6860f64af46eb GIT binary patch literal 37383 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 HFsbN&ad(`` literal 0 HcmV?d00001 diff --git a/src/static/image/svg/addchart.svg b/src/static/image/svg/addchart.svg new file mode 100644 index 0000000..7ae7662 --- /dev/null +++ b/src/static/image/svg/addchart.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/static/image/svg/line-sushi.svg b/src/static/image/svg/line-sushi.svg new file mode 100644 index 0000000..c65c942 --- /dev/null +++ b/src/static/image/svg/line-sushi.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/static/image/svg/line-white4.svg b/src/static/image/svg/line-white4.svg new file mode 100644 index 0000000..7451059 --- /dev/null +++ b/src/static/image/svg/line-white4.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/static/image/svg/perm_identity.svg b/src/static/image/svg/perm_identity.svg new file mode 100644 index 0000000..c2d199a --- /dev/null +++ b/src/static/image/svg/perm_identity.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/static/image/svg/visibility_off.svg b/src/static/image/svg/visibility_off.svg new file mode 100644 index 0000000..ceec114 --- /dev/null +++ b/src/static/image/svg/visibility_off.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/static/image/teambook-vertical.png b/src/static/image/teambook-vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..f4be112fc362181936e03ac429004a71e76a54a1 GIT binary patch literal 3995 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