Merge branch 'main' of https://github.com/Charles40130/Projet-Dev-Web-Ing1
This commit is contained in:
commit
16fd7fe9ff
@ -143,7 +143,7 @@ function AdminObjet() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-screen">
|
<div className="flex h-screen">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div className="flex-1 bg-gradient-to-br from-blue-50 to-indigo-50 p-8 overflow-auto">
|
<div className="flex-1 bg-gradient-to-br from-blue-50 to-indigo-50 p-8 overflow-auto scrollbar-hide">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<h1 className="text-4xl font-bold text-gray-900 text-center mb-12">
|
<h1 className="text-4xl font-bold text-gray-900 text-center mb-12">
|
||||||
Administration des Objets et Outils/Services
|
Administration des Objets et Outils/Services
|
||||||
@ -207,9 +207,9 @@ function AdminObjet() {
|
|||||||
<option value="status">Status</option>
|
<option value="status">Status</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Conteneur pour le scroll horizontal */}
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="w-full min-w-[640px] divide-y divide-gray-200">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
@ -278,8 +278,7 @@ function AdminObjet() {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
{/* RÈGLES GLOBALES */}
|
||||||
{/* Règles globales */}
|
|
||||||
<section className="bg-white p-6 rounded-xl shadow-md mt-12">
|
<section className="bg-white p-6 rounded-xl shadow-md mt-12">
|
||||||
<h2 className="text-2xl font-semibold mb-4">Règles Globales</h2>
|
<h2 className="text-2xl font-semibold mb-4">Règles Globales</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||||
|
|||||||
@ -1,10 +1,47 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState } from "react";
|
||||||
import Sidebar from "./sidebar.jsx";
|
import Sidebar from "./sidebar.jsx";
|
||||||
|
import { RadioTower, ArrowRight, BadgePlus, Settings } from "lucide-react";
|
||||||
import { API_BASE_URL } from "../../config.js";
|
import { API_BASE_URL } from "../../config.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
|
// Widgets initiaux pour le dashboard
|
||||||
|
const initialWidgets = [
|
||||||
|
{ id: 1, type: "summary" },
|
||||||
|
{ id: 2, type: "users" },
|
||||||
|
{ id: 3, type: "objects" },
|
||||||
|
];
|
||||||
|
|
||||||
function Dashboard() {
|
function Dashboard() {
|
||||||
const [users, setUsers] = useState([]);
|
// États simulés (données fictives pour l'exemple)
|
||||||
|
const [users, setUsers] = useState([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
username: "Alice",
|
||||||
|
email: "alice@example.com",
|
||||||
|
accessLevel: "Admin",
|
||||||
|
},
|
||||||
|
{ id: 2, username: "Bob", email: "bob@example.com", accessLevel: "User" },
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
username: "Charlie",
|
||||||
|
email: "charlie@example.com",
|
||||||
|
accessLevel: "Guest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
username: "David",
|
||||||
|
email: "david@example.com",
|
||||||
|
accessLevel: "User",
|
||||||
|
},
|
||||||
|
{ id: 5, username: "Eva", email: "eva@example.com", accessLevel: "User" },
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
username: "Frank",
|
||||||
|
email: "frank@example.com",
|
||||||
|
accessLevel: "Admin",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const [logs, setLogs] = useState([
|
const [logs, setLogs] = useState([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -20,57 +57,165 @@ function Dashboard() {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// État pour simuler les objets présents dans la section AdminObjet.jsx
|
||||||
|
const [adminObjects, setAdminObjects] = useState([
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
nom: "Objet A",
|
||||||
|
description: "Description A",
|
||||||
|
type: "Type A",
|
||||||
|
localisation: "Localisation A",
|
||||||
|
proprietaire: "Propriétaire A",
|
||||||
|
status: "active",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 102,
|
||||||
|
nom: "Objet B",
|
||||||
|
description: "Description B",
|
||||||
|
type: "Type B",
|
||||||
|
localisation: "Localisation B",
|
||||||
|
proprietaire: "Propriétaire B",
|
||||||
|
status: "inactive",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 103,
|
||||||
|
nom: "Objet C",
|
||||||
|
description: "Description C",
|
||||||
|
type: "Type C",
|
||||||
|
localisation: "Localisation C",
|
||||||
|
proprietaire: "Propriétaire C",
|
||||||
|
status: "active",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Gestion du mode de gestion et des widgets
|
||||||
|
const [manageMode, setManageMode] = useState(false);
|
||||||
|
const [widgets, setWidgets] = useState(initialWidgets);
|
||||||
|
|
||||||
|
// Contrôle d'affichage du modal de sélection de widget
|
||||||
|
const [showAddWidgetModal, setShowAddWidgetModal] = useState(false);
|
||||||
|
|
||||||
|
// Suppression d'un widget
|
||||||
|
const handleDeleteWidget = (id) => {
|
||||||
|
setWidgets(widgets.filter((widget) => widget.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ouvre le modal pour ajouter un widget
|
||||||
|
const openAddWidgetModal = () => {
|
||||||
|
setShowAddWidgetModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ajoute le widget sélectionné en fonction du type choisi
|
||||||
|
const handleWidgetSelection = (widgetType) => {
|
||||||
|
const newWidget = { id: Date.now(), type: widgetType };
|
||||||
|
setWidgets([...widgets, newWidget]);
|
||||||
|
setShowAddWidgetModal(false);
|
||||||
|
};
|
||||||
|
/*
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
axios.get(`${API_BASE_URL}/users`).then((response) => {
|
axios.get(`${API_BASE_URL}/users`).then((response) => {
|
||||||
setUsers(response.data);
|
setUsers(response.data);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
*/
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen">
|
<div className="flex min-h-screen">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<main className="flex-grow p-5">
|
<main className="flex-1 bg-gradient-to-br from-blue-50 to-indigo-50 p-8 overflow-auto">
|
||||||
<h1 className="text-2xl font-bold mb-6">Dashboard</h1>
|
{/* En-tête : titre + bouton pour activer/désactiver le mode gestion */}
|
||||||
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="flex gap-5 mb-6">
|
<h1 className="text-3xl font-bold">Dashboard</h1>
|
||||||
<div className="bg-gray-100 p-6 flex-1 rounded-lg text-center">
|
<button
|
||||||
<h2 className="text-xl font-semibold">Total Users</h2>
|
onClick={() => setManageMode(!manageMode)}
|
||||||
<p className="text-2xl mt-2">{users.length}</p>
|
className="flex items-center px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md"
|
||||||
|
>
|
||||||
|
<Settings className="mr-2" size={20} />
|
||||||
|
{manageMode ? "Terminer la gestion" : "Gérer les widgets"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-100 p-6 flex-1 rounded-lg text-center">
|
|
||||||
<h2 className="text-xl font-semibold">Dernier Log</h2>
|
{/* Grille des widgets */}
|
||||||
|
<section>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{widgets.map((widget) => (
|
||||||
|
<div
|
||||||
|
key={widget.id}
|
||||||
|
className="relative bg-white p-6 rounded-xl shadow hover:shadow-md"
|
||||||
|
>
|
||||||
|
{/* Bouton de suppression, visible en mode gestion */}
|
||||||
|
{manageMode && (
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteWidget(widget.id)}
|
||||||
|
className="absolute top-2 right-2 bg-red-600 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
–
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Contenu du widget selon son type */}
|
||||||
|
{widget.type === "summary" && (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold mb-4">
|
||||||
|
Résumé du tableau de bord
|
||||||
|
</h2>
|
||||||
|
<div className="mb-4">
|
||||||
|
<h3 className="text-lg font-medium">Total Utilisateur</h3>
|
||||||
|
<p className="text-2xl">{users.length}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">Dernier Log</h3>
|
||||||
{logs.length > 0 ? (
|
{logs.length > 0 ? (
|
||||||
<p className="mt-2">
|
<p>
|
||||||
{logs[logs.length - 1].username} - {logs[logs.length - 1].action}
|
{logs[logs.length - 1].username} -{" "}
|
||||||
|
{logs[logs.length - 1].action}
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p className="mt-2">Aucun log</p>
|
<p>Aucun log</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<section>
|
{widget.type === "users" && (
|
||||||
<h2 className="text-xl font-semibold mb-4">Aperçu des Utilisateurs</h2>
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold mb-4">
|
||||||
|
Gestion des Utilisateurs
|
||||||
|
</h2>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full border border-gray-300">
|
<table className="min-w-[320px] w-full border border-gray-200">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="bg-gray-100">
|
<tr>
|
||||||
<th className="p-3 border text-left">Username</th>
|
<th className="px-2 py-1 border border-gray-200 bg-gray-100 text-left">
|
||||||
<th className="p-3 border text-left">Email</th>
|
Username
|
||||||
<th className="p-3 border text-left">Niveau d'accès</th>
|
</th>
|
||||||
|
<th className="px-2 py-1 border border-gray-200 bg-gray-100 text-left">
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1 border border-gray-200 bg-gray-100 text-left">
|
||||||
|
Access
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{users.slice(0, 5).map((user) => (
|
{users.slice(0, 5).map((user) => (
|
||||||
<tr key={user.id} className="hover:bg-gray-50">
|
<tr key={user.id}>
|
||||||
<td className="p-3 border text-left">{user.name}</td>
|
<td className="px-2 py-1 border border-gray-200">
|
||||||
<td className="p-3 border text-left">{user.email}</td>
|
{user.username}
|
||||||
<td className="p-3 border text-left">{user.role}</td>
|
</td>
|
||||||
|
<td className="px-2 py-1 border border-gray-200">
|
||||||
|
{user.email}
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1 border border-gray-200">
|
||||||
|
{user.accessLevel}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
{users.length === 0 && (
|
{users.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="3" className="p-3 border text-center">
|
<td
|
||||||
|
colSpan="3"
|
||||||
|
className="px-2 py-1 border border-gray-200 text-center"
|
||||||
|
>
|
||||||
Aucun utilisateur disponible
|
Aucun utilisateur disponible
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -79,12 +224,126 @@ function Dashboard() {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md"
|
||||||
onClick={() => (window.location.href = "/user")}
|
onClick={() => (window.location.href = "/user")}
|
||||||
>
|
>
|
||||||
Voir plus
|
Voir plus
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "objects" && (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold mb-4">
|
||||||
|
Gestion des Objets Connectés
|
||||||
|
</h2>
|
||||||
|
<div className="mb-4">
|
||||||
|
<a
|
||||||
|
href="/gestionObjets"
|
||||||
|
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||||
|
>
|
||||||
|
<RadioTower size={24} className="mr-2" />
|
||||||
|
Consulter les objets connectés
|
||||||
|
<ArrowRight size={16} className="ml-2" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="/adminobjet"
|
||||||
|
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||||
|
>
|
||||||
|
<BadgePlus size={24} className="mr-2" />
|
||||||
|
Ajouter un nouvel objet
|
||||||
|
<ArrowRight size={16} className="ml-2" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "adminobjet" && (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold mb-4">
|
||||||
|
Liste des Objets et Outils/Services
|
||||||
|
</h2>
|
||||||
|
{/* Afficher seulement les 2 premiers objets */}
|
||||||
|
<ul className="mb-4 space-y-2">
|
||||||
|
{adminObjects.slice(0, 2).map((obj) => (
|
||||||
|
<li
|
||||||
|
key={obj.id}
|
||||||
|
className="border border-gray-200 p-2 rounded"
|
||||||
|
>
|
||||||
|
<p className="font-medium">{obj.nom}</p>
|
||||||
|
<p className="text-sm text-gray-500">{obj.type}</p>
|
||||||
|
<p className="text-sm text-gray-500">{obj.status}</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<button
|
||||||
|
onClick={() => (window.location.href = "/adminobjet")}
|
||||||
|
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md"
|
||||||
|
>
|
||||||
|
Voir plus
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Case pour ajouter un widget */}
|
||||||
|
<div
|
||||||
|
onClick={openAddWidgetModal}
|
||||||
|
className="flex items-center justify-center border-2 border-dashed border-gray-300 rounded-xl p-6 hover:bg-gray-50 cursor-pointer"
|
||||||
|
>
|
||||||
|
<button className="flex items-center">
|
||||||
|
<span className="text-3xl font-bold mr-2">+</span>
|
||||||
|
<span>Ajouter un widget</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Modal de sélection du type de widget */}
|
||||||
|
{showAddWidgetModal && (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||||
|
<div className="bg-white rounded-lg p-6 w-80">
|
||||||
|
<h3 className="text-xl font-semibold mb-4">
|
||||||
|
Choisir un type de widget
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<button
|
||||||
|
onClick={() => handleWidgetSelection("summary")}
|
||||||
|
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md text-left"
|
||||||
|
>
|
||||||
|
Dashboard Summary
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleWidgetSelection("users")}
|
||||||
|
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md text-left"
|
||||||
|
>
|
||||||
|
Gestion des Utilisateurs
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleWidgetSelection("objects")}
|
||||||
|
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md text-left"
|
||||||
|
>
|
||||||
|
Gestion des Objets Connectés
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleWidgetSelection("adminobjet")}
|
||||||
|
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded-md text-left"
|
||||||
|
>
|
||||||
|
Liste des Objets et Outils/Services
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAddWidgetModal(false)}
|
||||||
|
className="mt-4 px-4 py-2 bg-red-500 text-white rounded-md w-full"
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,35 +3,45 @@ import Sidebar from "./sidebar.jsx";
|
|||||||
import { API_BASE_URL } from "../../config.js";
|
import { API_BASE_URL } from "../../config.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
|
// Définition de styles utilisés (si nécessaire – vous pouvez convertir ces styles en classes Tailwind)
|
||||||
const thTd = "p-2 border border-gray-300 text-left";
|
const thTd = "p-2 border border-gray-300 text-left";
|
||||||
const th = `${thTd} bg-gray-100`;
|
const th = `${thTd} bg-gray-100`;
|
||||||
|
|
||||||
function User() {
|
function User() {
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
const [name, setname] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [pointsInput, setPointsInput] = useState({});
|
const [pointsInput, setPointsInput] = useState({});
|
||||||
|
|
||||||
|
// Ajout d'un utilisateur
|
||||||
const handleAddUser = (e) => {
|
const handleAddUser = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const newUser = {
|
const newUser = {
|
||||||
id: Date.now(),
|
id: Date.now(), // ID généré temporairement ; en production, il sera géré par la BDD.
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
accessLevel: "User",
|
accessLevel: "User",
|
||||||
points: 0,
|
points: 0,
|
||||||
};
|
};
|
||||||
setUsers([...users, newUser]);
|
setUsers([...users, newUser]);
|
||||||
logAction(name, "Utilisateur ajouté");
|
logAction(name, "User added");
|
||||||
setname("");
|
setName("");
|
||||||
setEmail("");
|
setEmail("");
|
||||||
|
// TODO : Envoyer newUser à l'API si nécessaire.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Chargement des utilisateurs depuis l'API au montage du composant
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
axios.get(`${API_BASE_URL}/users`).then((response) => {
|
axios
|
||||||
setUsers(response.data);
|
.get(`${API_BASE_URL}/users`)
|
||||||
});
|
.then((response) => setUsers(response.data))
|
||||||
|
.catch((error) =>
|
||||||
|
console.error("Erreur lors de la récupération des utilisateurs:", error)
|
||||||
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Suppression d'un utilisateur
|
||||||
const handleDeleteUser = (userId) => {
|
const handleDeleteUser = (userId) => {
|
||||||
const user = users.find((u) => u.id === userId);
|
const user = users.find((u) => u.id === userId);
|
||||||
|
|
||||||
@ -65,6 +75,7 @@ function User() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Changement du niveau d'accès
|
||||||
const handleChangeAccessLevel = (userId, newLevel) => {
|
const handleChangeAccessLevel = (userId, newLevel) => {
|
||||||
setUsers(
|
setUsers(
|
||||||
users.map((user) => {
|
users.map((user) => {
|
||||||
@ -73,10 +84,8 @@ function User() {
|
|||||||
user.role = newLevel;
|
user.role = newLevel;
|
||||||
if (user.role === "user") {
|
if (user.role === "user") {
|
||||||
user.points = 60;
|
user.points = 60;
|
||||||
} else if (user.role === "complexe") {
|
} else if (newLevel === "admin") {
|
||||||
user.points = 100;
|
user.points = 100;
|
||||||
} else if (user.role === "admin") {
|
|
||||||
user.points = 200;
|
|
||||||
}
|
}
|
||||||
axios
|
axios
|
||||||
.post(`${API_BASE_URL}/setUserPoints`, {
|
.post(`${API_BASE_URL}/setUserPoints`, {
|
||||||
@ -85,42 +94,42 @@ function User() {
|
|||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
alert("Le changement de niveau a bien été enregistré !");
|
alert("Le changement de niveau a bien été enregistré !");
|
||||||
console.log("Changement de niveau réussit :", response.data);
|
console.log("Niveau changé:", response.data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("Il y a eu une erreur dans le changement de niveau !");
|
alert("Erreur lors du changement de niveau !");
|
||||||
console.error("Erreur lors du changement de niveau :", error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
logAction(
|
logAction(user.name, `Access level changed from ${oldLevel} to ${newLevel}`);
|
||||||
user.name,
|
|
||||||
`Niveau d'accés changé de ${oldLevel} à ${newLevel}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ajustement des points : additionne les points saisis aux points existants
|
||||||
const handleAdjustPoints = (userId) => {
|
const handleAdjustPoints = (userId) => {
|
||||||
const pointsToAdd = parseInt(pointsInput[userId]) || 0;
|
const pointsToAdd = parseInt(pointsInput[userId]) || 0;
|
||||||
setUsers(
|
setUsers(
|
||||||
users.map((user) => {
|
users.map((user) => {
|
||||||
if (user.id === userId) {
|
if (user.id === userId) {
|
||||||
user.points = pointsToAdd;
|
user.points = pointsToAdd;
|
||||||
|
// On additionne au lieu de remplacer
|
||||||
|
user.points = (user.points || 0) + pointsToAdd;
|
||||||
axios
|
axios
|
||||||
.post(`${API_BASE_URL}/setUserPoints`, {
|
.post(`${API_BASE_URL}/setUserPoints`, {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
points: user.points,
|
points: user.points,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
alert("Les points ont bien été enregistré !");
|
alert("Les points ont bien été enregistrés !");
|
||||||
console.log("Ajout des points réussit :", response.data);
|
console.log("Points mis à jour :", response.data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
alert("Il y a eu une erreur dans l'ajout des points!");
|
alert("Erreur lors de l'ajustement des points !");
|
||||||
console.error("Erreur lors de l'ajout des points :", error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
logAction(user.name, `Points ajustés par ${pointsToAdd}`);
|
logAction(user.name, `Points adjusted by ${pointsToAdd}, new total: ${user.points}`);
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
})
|
})
|
||||||
@ -128,107 +137,121 @@ function User() {
|
|||||||
setPointsInput({ ...pointsInput, [userId]: "" });
|
setPointsInput({ ...pointsInput, [userId]: "" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fonction de journalisation des actions
|
||||||
const logAction = (name, action) => {
|
const logAction = (name, action) => {
|
||||||
/*TODO*/
|
|
||||||
/*Ajouter le suivi dans un journal de log*/
|
|
||||||
const timestamp = new Date().toLocaleString();
|
const timestamp = new Date().toLocaleString();
|
||||||
setLogs([...logs, { id: Date.now(), name, action, timestamp }]);
|
setLogs([...logs, { id: Date.now(), name, action, timestamp }]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fonction pour générer et télécharger les logs dans un fichier texte
|
||||||
|
const downloadLogs = () => {
|
||||||
|
const logText = logs
|
||||||
|
.map((log) => `${log.timestamp} - ${log.name} - ${log.action}`)
|
||||||
|
.join("\n");
|
||||||
|
const blob = new Blob([logText], { type: "text/plain;charset=utf-8" });
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
link.download = "logs.txt";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen">
|
<div className="flex h-screen">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<main className="flex-grow p-5">
|
<main className="flex-grow p-5">
|
||||||
<section className="mt-5">
|
<section className="mt-5">
|
||||||
<h1 className="text-2xl font-bold text-gray-900 mb-5">
|
<h1 className="text-2xl font-bold mb-4">Gestion des utilisateurs</h1>
|
||||||
User Management
|
<p className="mb-5">Gérez les utilisateurs à partir de ce panneau.</p>
|
||||||
</h1>
|
|
||||||
<p>Manage users from this panel.</p>
|
|
||||||
{/* Formulaire d'ajout d'utilisateur */}
|
{/* Formulaire d'ajout d'utilisateur */}
|
||||||
<form
|
<form
|
||||||
className="gap-3 mb-5 grid grid-cols-[1fr_1fr_auto]"
|
className="grid grid-cols-[1fr_1fr_auto] gap-2 mb-5"
|
||||||
onSubmit={handleAddUser}
|
onSubmit={handleAddUser}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
className="p-3 border rounded-md"
|
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
placeholder="name"
|
placeholder="Name"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setname(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
|
className="p-2 border border-gray-300 rounded-md"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className="p-3 border rounded-md"
|
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
required
|
||||||
|
className="p-2 border border-gray-300 rounded-md"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="p-3 bg-green-600 text-white border-none rounded-md "
|
|
||||||
type="submit"
|
type="submit"
|
||||||
|
className="p-2 bg-green-600 text-white rounded-md cursor-pointer"
|
||||||
>
|
>
|
||||||
Ajouter utilisateur
|
Add User
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{/* Tableau des utilisateurs */}
|
{/* Tableau des utilisateurs */}
|
||||||
<table className="w-full">
|
<table className="w-full border-collapse mb-6">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className={`${th}`}>Nom</th>
|
<th className={th}>Nom</th>
|
||||||
<th className={`${th}`}>Email</th>
|
<th className={th}>Email</th>
|
||||||
<th className={`${th}`}>Niveau d'accès</th>
|
<th className={th}>Niveau d'accès</th>
|
||||||
<th className={`${th}`}>Points</th>
|
<th className={th}>Points</th>
|
||||||
<th className={`${th}`}>Actions</th>
|
<th className={th}>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{users.map((user) => (
|
{users.map((user) => (
|
||||||
<tr key={user.id}>
|
<tr key={user.id}>
|
||||||
<td className={`${thTd}`}>{user.name}</td>
|
<td className={thTd}>{user.name}</td>
|
||||||
<td className={`${thTd}`}>{user.email}</td>
|
<td className={thTd}>{user.email}</td>
|
||||||
<td className={`${thTd}`}>
|
<td className={thTd}>
|
||||||
<select
|
<select
|
||||||
value={user.role}
|
value={user.accessLevel}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleChangeAccessLevel(user.id, e.target.value)
|
handleChangeAccessLevel(user.id, e.target.value)
|
||||||
}
|
}
|
||||||
className="p-2 rounded-md"
|
className="p-1 rounded-md border border-gray-300"
|
||||||
>
|
>
|
||||||
<option value="admin">Admin</option>
|
<option value="admin">Admin</option>
|
||||||
<option value="user">User</option>
|
<option value="user">User</option>
|
||||||
<option value="complexe">Complexe</option>
|
<option value="complexe">Complexe</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td className={`${thTd}`}>
|
<td className={thTd}>
|
||||||
<input
|
<input
|
||||||
className="border ml-4 w-16"
|
className="border ml-4 w-16"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
value={pointsInput[user.id] || user.points}
|
value={pointsInput[user.id] ?? user.points}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setPointsInput({
|
setPointsInput({
|
||||||
...pointsInput,
|
...pointsInput,
|
||||||
[user.id]: e.target.value,
|
[user.id]: e.target.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
className="w-16 ml-2 p-1 border border-gray-300 rounded-md"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="p-2 bg-green-600 text-white border-none rounded-md"
|
|
||||||
onClick={() => handleAdjustPoints(user.id)}
|
onClick={() => handleAdjustPoints(user.id)}
|
||||||
|
className="p-2 bg-green-600 text-white rounded-md ml-2"
|
||||||
>
|
>
|
||||||
Changer
|
Changer
|
||||||
|
Changer
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td className={`${thTd}`}>
|
<td className={thTd}>
|
||||||
<button
|
<button
|
||||||
className="p-2 bg-red-600 text-white border-none rounded-md"
|
|
||||||
onClick={() => handleDeleteUser(user.id)}
|
onClick={() => handleDeleteUser(user.id)}
|
||||||
|
className="p-2 bg-red-600 text-white rounded-md"
|
||||||
>
|
>
|
||||||
Supprimer
|
Supprimer
|
||||||
|
Supprimer
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -237,28 +260,34 @@ function User() {
|
|||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
{/* Tableau des logs */}
|
{/* Tableau des logs */}
|
||||||
<section className="user-logs mt-10">
|
<section className="mt-10">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-5">
|
<h2 className="text-2xl font-bold mb-4">
|
||||||
Historique des connexions et journal des logs{" "}
|
Historique des connexions et journal des logs
|
||||||
</h2>
|
</h2>
|
||||||
<table className="w-full">
|
<table className="w-full border-collapse mb-4">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className={`${th}`}>Nom</th>
|
<th className={th}>Nom</th>
|
||||||
<th className={`${th}`}>Action</th>
|
<th className={th}>Action</th>
|
||||||
<th className={`${th}`}>Timestamp</th>
|
<th className={th}>Timestamp</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{logs.map((log) => (
|
{logs.map((log) => (
|
||||||
<tr key={log.id}>
|
<tr key={log.id}>
|
||||||
<td className={`${thTd}`}>{log.name}</td>
|
<td className={thTd}>{log.name}</td>
|
||||||
<td className={`${thTd}`}>{log.action}</td>
|
<td className={thTd}>{log.action}</td>
|
||||||
<td className={`${thTd}`}>{log.timestamp}</td>
|
<td className={thTd}>{log.timestamp}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<button
|
||||||
|
onClick={downloadLogs}
|
||||||
|
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md"
|
||||||
|
>
|
||||||
|
Télécharger les logs
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,9 +1,27 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Menu, X } from "lucide-react";
|
||||||
|
|
||||||
function Sidebar() {
|
function Sidebar({ isOpen, toggleSidebar }) {
|
||||||
return (
|
return (
|
||||||
<aside className="w-[250px] bg-gray-800 text-white p-5 h-screen box-border">
|
<aside
|
||||||
<h2 className="text-xl font-bold mb-4">Admin Panel</h2>
|
className={`
|
||||||
|
bg-gray-800 text-white p-5 h-screen w-64 fixed top-0 left-0 z-40
|
||||||
|
transform transition-transform duration-200 ease-in-out
|
||||||
|
${isOpen ? "translate-x-0" : "-translate-x-full"}
|
||||||
|
md:static md:translate-x-0
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{/* En-tête visible sur mobile avec bouton de fermeture */}
|
||||||
|
<div className="flex justify-between items-center md:hidden mb-4">
|
||||||
|
<h2 className="text-xl font-bold">Admin Panel</h2>
|
||||||
|
<button onClick={toggleSidebar} className="focus:outline-none">
|
||||||
|
<X size={24} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* En-tête pour les écrans md+ */}
|
||||||
|
<div className="hidden md:block mb-4">
|
||||||
|
<h2 className="text-xl font-bold">Admin Panel</h2>
|
||||||
|
</div>
|
||||||
<nav>
|
<nav>
|
||||||
<ul className="list-none p-0">
|
<ul className="list-none p-0">
|
||||||
<li className="mb-3">
|
<li className="mb-3">
|
||||||
@ -43,13 +61,4 @@ function Sidebar() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
export default Sidebar;
|
||||||
return (
|
|
||||||
<div className="flex h-screen m-0 p-0">
|
|
||||||
<Sidebar />
|
|
||||||
<main className="flex-1 ">{/* Ajoutez ici le contenu principal */}</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { Calendar, Settings, LayoutDashboard } from "lucide-react";
|
|
||||||
|
|
||||||
function AdminDashboard() {
|
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState('dashboard');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
||||||
{/* SideMenu */}
|
|
||||||
<div className="w-64 bg-white rounded-lg shadow-sm p-4">
|
|
||||||
<h2 className="text-lg font-semibold text-gray-800 mb-4">
|
|
||||||
Administration
|
|
||||||
</h2>
|
|
||||||
<nav className="flex flex-col space-y-2">
|
|
||||||
<button
|
|
||||||
onClick={() => setActiveTab('events')}
|
|
||||||
className={`w-full flex items-center gap-2 px-4 py-2 rounded-lg ${
|
|
||||||
activeTab === 'events'
|
|
||||||
? 'bg-red-50 text-red-600'
|
|
||||||
: 'text-gray-600 hover:bg-gray-50'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Calendar size={20} />
|
|
||||||
Events
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button onClick="" className="">
|
|
||||||
<LayoutDashboard size="15" />
|
|
||||||
Layout
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button onClick="">
|
|
||||||
<Settings size="15" />
|
|
||||||
Settings
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
<h2>Test</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AdminDashboard;
|
|
||||||
Loading…
Reference in New Issue
Block a user