diff --git a/DataSentinel/src/pages/ServiceMonitorings/ServiceMonitorings.jsx b/DataSentinel/src/pages/ServiceMonitorings/ServiceMonitorings.jsx
index 890a804..25030ae 100644
--- a/DataSentinel/src/pages/ServiceMonitorings/ServiceMonitorings.jsx
+++ b/DataSentinel/src/pages/ServiceMonitorings/ServiceMonitorings.jsx
@@ -7,6 +7,7 @@ import DataTable from '../../components/common/DataTable';
import LoadingSpinner from '../../components/common/LoadingSpinner';
import ErrorMessage from '../../components/common/ErrorMessage';
import Button from '../../components/common/Button';
+import { buildMonitoringColumns } from '../../utils/monitoringColumns';
import styles from './ServiceMonitorings.module.css';
export default function ServiceMonitorings({ serviceLabel, serviceName }) {
@@ -34,22 +35,7 @@ export default function ServiceMonitorings({ serviceLabel, serviceName }) {
if (loading) return
;
- const columns = [
- { key: 'nom', label: 'Monitoring' },
- { key: 'service', label: 'Service' },
- { key: 'categorie', label: 'Catégorie' },
- { key: 'bdd_source', label: 'Source' },
- { key: 'nb_erreurs', label: 'Erreurs', render: (val) => val ?? 0 },
- {
- key: 'actions',
- label: 'Actions',
- render: (val, row) => (
-
- )
- }
- ];
+ const columns = buildMonitoringColumns(navigate);
return (
diff --git a/DataSentinel/src/pages/Users/Users.jsx b/DataSentinel/src/pages/Users/Users.jsx
index b0e5b79..41e3c1c 100644
--- a/DataSentinel/src/pages/Users/Users.jsx
+++ b/DataSentinel/src/pages/Users/Users.jsx
@@ -43,9 +43,25 @@ export default function Users() {
}
}, []);
+ /* Chargement initial. Le drapeau `annule` évite d'écrire dans un composant
+ déjà démonté si l'utilisateur quitte la page pendant la requête. */
useEffect(() => {
- charger();
- }, [charger]);
+ let annule = false;
+
+ async function chargerAuMontage() {
+ try {
+ const data = await getUsers();
+ if (!annule) setUsers(Array.isArray(data) ? data : []);
+ } catch (e) {
+ if (!annule) setErreur(e.message || 'Erreur lors du chargement des utilisateurs.');
+ } finally {
+ if (!annule) setLoading(false);
+ }
+ }
+
+ chargerAuMontage();
+ return () => { annule = true; };
+ }, []);
/* Ouverture des modales */
function ouvrirCreation() {
diff --git a/DataSentinel/src/pages/VueConso/VueConso.jsx b/DataSentinel/src/pages/VueConso/VueConso.jsx
index da8253b..2e28186 100644
--- a/DataSentinel/src/pages/VueConso/VueConso.jsx
+++ b/DataSentinel/src/pages/VueConso/VueConso.jsx
@@ -9,15 +9,10 @@ import DataTable from '../../components/common/DataTable';
import LoadingSpinner from '../../components/common/LoadingSpinner';
import ErrorMessage from '../../components/common/ErrorMessage';
import Button from '../../components/common/Button';
+import { getStatutMonitoring } from '../../utils/statutMonitoring';
+import { buildMonitoringColumns } from '../../utils/monitoringColumns';
import styles from './VueConso.module.css';
-/* Détermine la couleur selon le statut */
-function getStatusColor(nbErreurs) {
- if (nbErreurs === 0) return 'var(--color-ok)';
- if (nbErreurs < 10) return 'var(--color-warning)';
- return 'var(--color-primary)';
-}
-
export default function VueConso() {
const navigate = useNavigate();
const [data, setData] = useState([]);
@@ -47,26 +42,10 @@ export default function VueConso() {
const chartData = data.map(item => ({
name: item.nom || item.nom_monito || item.libelle || 'Monitoring',
erreurs: item.nb_erreurs ?? item.count ?? 0,
- fill: getStatusColor(item.nb_erreurs ?? item.count ?? 0)
+ fill: getStatutMonitoring(item.nb_erreurs ?? item.count ?? 0).color
}));
- /* Colonnes du tableau */
- const columns = [
- { key: 'nom', label: 'Monitoring' },
- { key: 'service', label: 'Service' },
- { key: 'categorie', label: 'Catégorie' },
- { key: 'bdd_source', label: 'Source' },
- { key: 'nb_erreurs', label: 'Erreurs', render: (val) => val ?? 0 },
- {
- key: 'actions',
- label: 'Actions',
- render: (val, row) => (
-
- )
- }
- ];
+ const columns = buildMonitoringColumns(navigate);
return (
diff --git a/DataSentinel/src/services/api.js b/DataSentinel/src/services/api.js
index bbd1116..912a6ec 100644
--- a/DataSentinel/src/services/api.js
+++ b/DataSentinel/src/services/api.js
@@ -3,9 +3,11 @@
/* Utilise la variable d'environnement Vite si définie, sinon localhost par défaut */
const BASE_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:8000';
-/* Clés de stockage local pour le token JWT et l'utilisateur connecté */
+/* Clés de stockage local pour le token JWT et l'utilisateur connecté.
+ USER_KEY est exporté : AuthContext lit/écrit la même entrée, et une clé
+ dupliquée des deux côtés divergerait silencieusement au premier renommage. */
const TOKEN_KEY = 'ds_token';
-const USER_KEY = 'ds_user';
+export const USER_KEY = 'ds_user';
/* Helpers de gestion du token JWT dans le localStorage */
export function getToken() {
@@ -188,6 +190,58 @@ export async function getContacts(id_service) {
return fetchApi('/contacts', { id_service });
}
+/* ---------- Administration du référentiel (réservé Admin) ---------- */
+
+export async function createService(body) {
+ return sendJson('/services', 'POST', body);
+}
+
+export async function updateService(id_service, body) {
+ return sendJson(`/services/${id_service}`, 'PUT', body);
+}
+
+export async function deleteService(id_service) {
+ return sendJson(`/services/${id_service}`, 'DELETE');
+}
+
+export async function createCategorie(body) {
+ return sendJson('/categories', 'POST', body);
+}
+
+export async function updateCategorie(id_categorie, body) {
+ return sendJson(`/categories/${id_categorie}`, 'PUT', body);
+}
+
+export async function deleteCategorie(id_categorie) {
+ return sendJson(`/categories/${id_categorie}`, 'DELETE');
+}
+
+export async function createContact(body) {
+ return sendJson('/contacts', 'POST', body);
+}
+
+export async function updateContact(id_contact, body) {
+ return sendJson(`/contacts/${id_contact}`, 'PUT', body);
+}
+
+export async function deleteContact(id_contact) {
+ return sendJson(`/contacts/${id_contact}`, 'DELETE');
+}
+
+export async function createMonitoring(body) {
+ return sendJson('/monitorings', 'POST', body);
+}
+
+export async function updateMonitoring(id_monito, body) {
+ return sendJson(`/monitorings/${id_monito}`, 'PUT', body);
+}
+
+/* Désactivation (actif = 0) : l'historique référence le monitoring,
+ il n'est jamais supprimé physiquement. */
+export async function desactiverMonitoring(id_monito) {
+ return sendJson(`/monitorings/${id_monito}`, 'DELETE');
+}
+
/* Récupère la nomenclature (monitorings), filtrable par service et catégorie */
export async function getMonitorings(id_service, id_categorie) {
const data = await fetchApi('/monitorings', { id_service, id_categorie });
diff --git a/DataSentinel/src/utils/monitoringColumns.jsx b/DataSentinel/src/utils/monitoringColumns.jsx
new file mode 100644
index 0000000..82984bb
--- /dev/null
+++ b/DataSentinel/src/utils/monitoringColumns.jsx
@@ -0,0 +1,24 @@
+import Button from '../components/common/Button';
+
+/* Colonnes du tableau de monitorings partagées par VueConso et
+ ServiceMonitorings (Contrat/Fournisseur) : même jeu de données
+ (GET /dashboard), même tableau. Centralisé pour qu'une évolution des
+ colonnes (ajout, renommage) ne soit faite qu'à un seul endroit. */
+export function buildMonitoringColumns(navigate) {
+ return [
+ { key: 'nom', label: 'Monitoring' },
+ { key: 'service', label: 'Service' },
+ { key: 'categorie', label: 'Catégorie' },
+ { key: 'bdd_source', label: 'Source' },
+ { key: 'nb_erreurs', label: 'Erreurs', render: (val) => val ?? 0 },
+ {
+ key: 'actions',
+ label: 'Actions',
+ render: (val, row) => (
+
+ )
+ }
+ ];
+}
diff --git a/DataSentinel/src/utils/statutMonitoring.js b/DataSentinel/src/utils/statutMonitoring.js
new file mode 100644
index 0000000..9c4de4c
--- /dev/null
+++ b/DataSentinel/src/utils/statutMonitoring.js
@@ -0,0 +1,16 @@
+/* Règle de statut partagée : un monitoring est "OK" sans erreur, "Attention"
+ en-dessous du seuil, "Critique" au-delà. Centralisé ici pour que le seuil
+ ne soit corrigé qu'à un seul endroit (il était dupliqué dans MonitoringCard,
+ MonitoringDetail et VueConso, avec le risque que l'une des copies diverge). */
+
+export const SEUIL_ATTENTION = 10;
+
+export function getStatutMonitoring(nbErreurs) {
+ if (nbErreurs === 0) {
+ return { label: 'OK', variant: 'ok', color: 'var(--color-ok)' };
+ }
+ if (nbErreurs < SEUIL_ATTENTION) {
+ return { label: 'Attention', variant: 'warning', color: 'var(--color-warning)' };
+ }
+ return { label: 'Critique', variant: 'critical', color: 'var(--color-primary)' };
+}