+ :
| Rôle |
+ Habilitations |
Action |
- {roles.map((element) => {
- return
+ {roles?.map((element) => {
+ return
})}
diff --git a/src/app/user/CreateUserForm.jsx b/src/app/user/CreateUserForm.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..f06c5ca134640ced18395b607c20c204dee24ca6
--- /dev/null
+++ b/src/app/user/CreateUserForm.jsx
@@ -0,0 +1,216 @@
+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"
+
+
+
+
+function generateRandomPassword() {
+ const length = Math.floor(Math.random() * 3) + 8; // Length between 8 and 10
+ const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ const lowercase = 'abcdefghijklmnopqrstuvwxyz';
+ const numbers = '0123456789';
+ const symbols = '!@#$%^&*()_+[]{}|;:,.<>?';
+
+ let password = '';
+
+ password += uppercase[Math.floor(Math.random() * uppercase.length)];
+ password += numbers[Math.floor(Math.random() * numbers.length)];
+ password += symbols[Math.floor(Math.random() * symbols.length)];
+
+ const allCharacters = uppercase + lowercase + numbers + symbols;
+
+ for (let i = 3; i < length; i++) {
+ const randomIndex = Math.floor(Math.random() * allCharacters.length);
+ password += allCharacters[randomIndex];
+ }
+
+ password = password.split('').sort(() => 0.5 - Math.random()).join('');
+
+ return password;
+}
+const CreateUserForm = ({ setIsOpen, appendUser }) => {
+ const [isLoading, setIsLoading] = useState(false)
+ const [selectedRole, setSelectedRole] = useState(null)
+ const { toggleNotification } = useNotification()
+ const [selectProjects, setSelectedProjects] = useState([])
+
+ const [roles, setRoles] = useState(null)
+ const [projects, setProjects] = useState(null)
+ useEffect(() => {
+ const getRoles = async () => {
+ const { data, errors, isSuccess } = await fetchRequest("/roles/")
+ if (isSuccess) {
+ setRoles(data)
+ } else {
+ setRoles([])
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ console.log(errors)
+ }
+ }
+ const getProjects = async () => {
+ const { isSuccess, errors, data } = await fetchRequest("/projects/");
+ if (isSuccess) {
+ setProjects(data);
+ } else {
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ console.log(errors)
+ }
+ }
+ getRoles()
+ getProjects()
+ }, [])
+ const handleFieldChange = (event) => setUserData({ ...userData, [event.target.name]: event.target.value })
+
+ const [userData, setUserData] = useState({ email: "", password: generateRandomPassword(), first_name: "", last_name: "" })
+ const handleSubmit = async (event) => {
+ event.preventDefault()
+ setIsLoading(true)
+ const { data, errors, isSuccess, status } = await fetchRequest("/users/", {
+ method: "POST",
+ body: JSON.stringify({ ...userData, username: userData.email, role: selectedRole.id, project_ids: selectProjects.map((element) => element.id) })
+ })
+ if (isSuccess) {
+ setIsLoading(false)
+ setSelectedRole(null)
+ appendUser(data.data)
+ toggleNotification({
+ visible: true,
+ message: "Le rôle a été créé avec succès",
+ type: "success"
+ })
+ setIsOpen(false)
+ } else {
+ setIsLoading(false)
+ if (errors.type === "ValidationError") {
+ if (errors.detail.name) {
+ toggleNotification({
+ visible: true,
+ message: "Le rôle existe déjà",
+ type: "warning"
+ })
+ }
+ else {
+ toggleNotification({
+ visible: true,
+ message: "Erreur de validation de rôle",
+ type: "warning"
+ })
+ setIsOpen(false)
+ }
+ }
+ else if (status === 409) {
+ toggleNotification({
+ visible: true,
+ message: "role created with 2 or more same privileges",
+ type: "error"
+ })
+ setIsOpen(false)
+ } else if (errors.detail === "Privilege matching query does not exist.") {
+ toggleNotification({
+ visible: true,
+ message: "Des privilèges que vous avez utilisés ont été supprimés.",
+ type: "warning"
+ })
+ setIsOpen(false)
+ } else {
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ setIsOpen(false)
+ }
+ console.log(errors)
+ }
+ }
+ const handleRoleClick = (newRole) => {
+ if (selectedRole?.id === newRole.id) {
+ setSelectedRole(null)
+ } else {
+ setSelectedRole(newRole)
+ }
+ }
+ const handleProjectClick = (project) => {
+ if (selectProjects.find((element) => element.id === project.id)) {
+ setSelectedProjects(selectProjects.filter((element) => element.id !== project.id))
+ } else {
+ setSelectedProjects([...selectProjects, project])
+ }
+ }
+ return (
+
+
+
setIsOpen(false)} className="h-8 w-8 cursor-pointer absolute top-2 right-2 fill-neutral-600" />
+
+
+
+ )
+}
+
+export default CreateUserForm
\ No newline at end of file
diff --git a/src/app/user/UpdateUserForm.jsx b/src/app/user/UpdateUserForm.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d214f1851c494a1c39ed8432decfdc39e102c944
--- /dev/null
+++ b/src/app/user/UpdateUserForm.jsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const UpdateUserForm = () => {
+ return (
+
UpdateUserForm
+ )
+}
+
+export default UpdateUserForm
\ No newline at end of file
diff --git a/src/app/user/UserTableRow.jsx b/src/app/user/UserTableRow.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..1860ee8ae70d4c6a07ffdcba015f733f2fb85490
--- /dev/null
+++ b/src/app/user/UserTableRow.jsx
@@ -0,0 +1,88 @@
+import React, { useState } from 'react'
+import DeleteIcon from "@/static/image/svg/delete.svg"
+import EditIcon from "@/static/image/svg/edit.svg"
+import ConfirmationModal from '../ui/ConfirmationModal'
+import { useNotification } from '@/context/NotificationContext'
+import fetchRequest from '../lib/fetchRequest'
+
+const UserTableRow = ({ email, last_name, first_name, id, setUsers, role, projects }) => {
+ const { toggleNotification } = useNotification()
+ const [isModalOpen, setModalOpen] = useState(false);
+ const showDeletePopup = () => {
+ setModalOpen(true);
+ }
+ const handleDelete = async () => {
+ const { isSuccess, errors, status } = await fetchRequest(`/users/${id}/`, { method: "DELETE" })
+ if (isSuccess) {
+ setUsers((users) => users.filter((element) => element.id !== id))
+ toggleNotification({
+ visible: true,
+ message: "L'utilisateur a été supprimé avec succès",
+ type: "success"
+ })
+ } else if (status == 404) {
+ toggleNotification({
+ visible: true,
+ message: "L'utilisateur n'a pas été trouvé",
+ type: "warning"
+ })
+ } else if (errors.detail?.indexOf("Cannot delete some instances of model 'Role'") !== -1) {
+ toggleNotification({
+ visible: true,
+ message: "Impossible de supprimer cet utilisateur car il est attribué à des utilisateurs",
+ type: "warning"
+ })
+ }
+ else {
+ console.log(errors)
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ }
+ setModalOpen(false)
+ console.log(errors)
+ }
+ return (
+ <>
+
+ |
+ {first_name} {last_name}
+ |
+
+ {email}
+ |
+
+ {role?.name || ""}
+ |
+
+
+ {(!projects || projects.length === 0) && - }
+ {projects?.map((project) => {
+ return - {project.nom}
+ })}
+
+ |
+
+
+
+
+
+ |
+
+
setModalOpen(false)}
+ onConfirm={handleDelete}
+ message={`Voulez-vous vraiment supprimer le rôle ?`}
+ />
+ >
+ )
+}
+
+export default UserTableRow
\ No newline at end of file
diff --git a/src/app/user/page.jsx b/src/app/user/page.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..ff8ef679f0d7958bb6da219df3c92ac8e5740c95
--- /dev/null
+++ b/src/app/user/page.jsx
@@ -0,0 +1,77 @@
+'use client';
+import React, { useEffect, useState } from 'react'
+import CreateUserForm from './CreateUserForm'
+import UpdateUserForm from './UpdateUserForm'
+import AddIcon from "@/static/image/svg/add.svg"
+import Loader from '@/components/Loader/Loader'
+import fetchRequest from '../lib/fetchRequest';
+import { isArray } from '../lib/TypesHelper';
+import { useNotification } from '@/context/NotificationContext';
+import UserTableRow from './UserTableRow';
+
+const UserPage = () => {
+ const [users, setUsers] = useState([])
+ const [isLoading, setIsLoading] = useState(true)
+ const [openCreatePopup, setOpenCreatePopup] = useState(null)
+ const [userToUpdate, setUserToUpdate] = useState(null)
+ const { toggleNotification } = useNotification()
+
+ useEffect(() => {
+ const getUsers = async () => {
+ const { data, errors, isSuccess } = await fetchRequest("/users")
+ setIsLoading(false)
+ if (isSuccess) {
+ console.log(data)
+ setUsers(data)
+ } else {
+ toggleNotification({
+ visible: true,
+ message: "Internal Server Error",
+ type: "error"
+ })
+ }
+ }
+ getUsers()
+ }, [])
+ const appendUser = (newUser) => {
+ setUsers([newUser, ...users])
+ }
+ return (
+
+
+ {openCreatePopup &&
}
+ {userToUpdate &&
}
+
+
List des Utilisateurs
+
+
+ {(isLoading) &&
}
+ {(!isLoading) && <> {(!isArray(users) || users?.length === 0)
+ ?
+
Pas encore des utilisateurs
+
+ :
+
+
+ | Nom |
+ Email |
+ Rôle |
+ Projects |
+ Action |
+
+ {users?.map((element) => {
+ return
+ })}
+
+
+ }>}
+
+
+
+ )
+}
+
+export default UserPage
\ No newline at end of file
diff --git a/src/static/image/svg/add.svg b/src/static/image/svg/add.svg
new file mode 100644
index 0000000000000000000000000000000000000000..37eaf783c6b70ff9297a84dfff632105ad288efe
--- /dev/null
+++ b/src/static/image/svg/add.svg
@@ -0,0 +1 @@
+
\ No newline at end of file