From f5a8e9a14bd779ed670cc60bb381e2147354baa2 Mon Sep 17 00:00:00 2001 From: Nabil Derouiche Date: Sat, 18 Apr 2026 16:52:53 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20v4=20=E2=80=94=20am=C3=A9liorations=20c?= =?UTF-8?q?ompl=C3=A8tes=20UX/bugs/architecture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugs corrigés : - Remplace logo-TT.png et photo absents par logo-RLA.svg + avatar initiales - Thème par défaut synchronisé avec CONFIG.DEFAULT_THEME - showSlide() ne cible plus que les btn-slide-N (fix index des boutons export) - Filtre entrepreneur "En Service" se réinitialise au changement de région - Suppression variable `const now` inutilisée Améliorations : - normalizeMarche() : préprocesseur unifié des champs API multi-noms - ALL_REGIONS centralisé dans config.js, buildRegionOptions() partout - 2 bar charts région dans Vue Générale (avancement moyen + nb marchés) - Taille de page configurable 10/25/50/100 - Icônes de tri actives (fa-sort-up/down + classe CSS) - Filtres période (date) sur liste Marchés + bouton reset - Filtres région + état dans Pilotage Proactif - Modal édition utilisateur (rôle, région, mot de passe) - Toast confirmation suppression utilisateur (remplace confirm natif) - Gestion session expirée : 401 → toast + déconnexion auto - Titre onglet dynamique avec badge alertes - Toast unifié error/success/warning - Touche Escape ferme le modal Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 39 ++++ config.js | 17 +- index.html | 636 ++++++++++++++++++++++++++++++++++++--------------- logo-RLA.svg | 24 ++ 4 files changed, 523 insertions(+), 193 deletions(-) create mode 100644 logo-RLA.svg diff --git a/CLAUDE.md b/CLAUDE.md index f1658b8..b0c99bc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,3 +13,42 @@ ## MCP disponibles - baserow : http://192.168.100.33 (données marchés RLA) - portainer : http://192.168.100.33:9000 (déploiement Docker) + +## Architecture frontend (index.html) +- Fichier unique SPA — HTML + CSS + JS inline +- API REST : `/api/auth/login`, `/api/marches`, `/api/stats`, `/api/pilotage-proactif`, `/api/pipeline`, `/api/users`, `/api/logs`, `/api/export/:format` +- JWT stocké en localStorage (`rla_jwt`) +- Thème persisté en localStorage (`rla_theme`) +- Rôles : `user` (lecture, régions restreintes) · `admin` (pipeline) · `superadmin` (users, logs, exports) + +## Champs API normalisés via `normalizeMarche(r)` +Appeler `normalizeMarche(r)` à la réception — ensuite utiliser uniquement : +- `r.id_marche`, `r.region`, `r.taux_phy` (number), `r.tot_marche` (number), `r.date_debut`, `r.date_fin`, `r.observation` (string) + +## Régions Zone Sud — source unique +Défini dans `CONFIG.ALL_REGIONS` (config.js). Ne pas dupliquer dans le HTML. +Accès via la variable globale `ALL_REGIONS` (initialisée depuis CONFIG). + +## Changelog v4 — 2026-04-18 +### Bugs corrigés +- `logo-TT.png` / `Nabil.Derouiche.jpg` absents → remplacés par `logo-RLA.svg` + avatar initiales +- Thème par défaut : `loadTheme()` utilise désormais `CONFIG.DEFAULT_THEME` (plus d'incohérence avec config.js) +- `showSlide()` : active uniquement les `btn-slide-N`, pas les boutons export (ancien bug d'index) +- Filtre entrepreneur "En Service" : se réinitialise quand la région change (`onServiceRegionChange`) +- Variable `const now` inutilisée supprimée + +### Améliorations +- **normalizeMarche()** : préprocesseur centralisé des champs API multi-noms +- **ALL_REGIONS centralisé** dans config.js, injecté partout via `buildRegionOptions()` +- **2 graphiques région** dans Vue Générale : avancement moyen + marchés actifs par région (bar charts) +- **Taille de page configurable** : sélecteur 10/25/50/100 dans la pagination +- **Icônes de tri actives** : fa-sort-up / fa-sort-down + classe CSS sur colonne active +- **Filtres période** sur liste Marchés (date début / date fin) + bouton reset +- **Filtre région + état** dans slide Pilotage Proactif +- **Modal édition utilisateur** : modifier rôle, région, mot de passe (PATCH /api/users/:id) +- **Toast de confirmation** pour suppression utilisateur (remplace `confirm()` natif) +- **Gestion session expirée** : 401 → toast warning + déconnexion automatique après 1,5s +- **Titre onglet dynamique** : `⚠️ N alertes — Marchés RLA` si alertes actives +- **Toast unifié** (error / success / warning) remplace errorToast mono-usage +- **Footer** : avatar initiales SVG remplace image absente +- **Touche Escape** ferme le modal édition diff --git a/config.js b/config.js index 43e767e..5fc77cc 100644 --- a/config.js +++ b/config.js @@ -15,14 +15,17 @@ const CONFIG = { // Thème par défaut DEFAULT_THEME: 'light', + // Régions Zone Sud (source unique) + ALL_REGIONS: ['Gabes','Gafsa','Kebili','Medenine','Sfax','Tataouine','Tozeur'], + // Couleurs régions REGION_COLORS: { - 'Gabes': '#17A2B8', - 'Gafsa': '#22C55E', - 'Kebili': '#9333EA', - 'Medenine': '#0EA5E9', - 'Sfax': '#002855', + 'Gabes': '#17A2B8', + 'Gafsa': '#22C55E', + 'Kebili': '#9333EA', + 'Medenine': '#0EA5E9', + 'Sfax': '#002855', 'Tataouine': '#14B8A6', - 'Tozeur': '#818CF8' + 'Tozeur': '#818CF8' } -}; \ No newline at end of file +}; diff --git a/index.html b/index.html index 3691fda..dfd24ef 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ Marchés RLA - Zone Sud | Tunisie Telecom + @@ -35,7 +36,9 @@ /* ── LOGIN ── */ .login-container{position:fixed;inset:0;background:var(--bg-body);display:flex;justify-content:center;align-items:center;z-index:10000;} .login-box{background:var(--bg-card);padding:40px;border-radius:20px;box-shadow:0 20px 60px rgba(0,0,0,0.4);border:1px solid var(--border-color);width:100%;max-width:400px;text-align:center;backdrop-filter:blur(20px);} - .login-box .login-logo{height:70px;margin-bottom:20px;object-fit:contain;} + .login-logo-wrap{height:70px;margin-bottom:20px;display:flex;align-items:center;justify-content:center;} + .login-logo-wrap img{height:70px;object-fit:contain;} + .login-logo-wrap .logo-svg-fallback{width:70px;height:70px;} .login-box h2{color:var(--primary);margin-bottom:8px;font-size:1.5em;} [data-theme=""] .login-box h2,[data-theme="dark"] .login-box h2{color:var(--accent);} .login-box p{color:var(--text-muted);margin-bottom:25px;font-size:0.9em;} @@ -55,6 +58,7 @@ .header{background:linear-gradient(90deg,var(--primary),var(--primary-light));padding:14px 24px;display:flex;justify-content:space-between;align-items:center;box-shadow:0 4px 20px rgba(0,0,0,0.3);position:sticky;top:0;z-index:100;flex-wrap:wrap;gap:12px;} .logo-section{display:flex;align-items:center;gap:14px;} .logo-section img{height:46px;object-fit:contain;} + .logo-section .logo-svg-fallback{width:46px;height:46px;} .logo-section h1{font-size:1.2em;font-weight:700;color:white;line-height:1.2;} .logo-section .sub{font-size:0.78em;color:rgba(255,255,255,0.7);margin-top:2px;} .header-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;} @@ -80,12 +84,12 @@ .slide-nav button.active{background:linear-gradient(135deg,var(--accent),var(--primary-light));color:white;box-shadow:0 4px 15px rgba(0,212,255,0.25);} [data-theme="light"] .slide-nav button.active,[data-theme="professional"] .slide-nav button.active{box-shadow:0 4px 15px rgba(2,132,199,0.3);} .nav-separator{width:2px;height:28px;background:var(--border-color);margin:0 4px;align-self:center;} - .export-btn{background:linear-gradient(135deg,#dc2626,#b91c1c)!important;color:white!important;border-color:transparent!important;} - .export-pptx-btn{background:linear-gradient(135deg,#C65D21,#E07832)!important;color:white!important;border-color:transparent!important;} - .export-xlsx-btn{background:linear-gradient(135deg,#16a34a,#15803d)!important;color:white!important;border-color:transparent!important;} - .export-docx-btn{background:linear-gradient(135deg,#1d4ed8,#1e40af)!important;color:white!important;border-color:transparent!important;} - .refresh-btn{background:linear-gradient(135deg,#0891b2,#0e7490)!important;color:white!important;border-color:transparent!important;} - .nav-hidden{display:none!important;} + .export-btn{background:linear-gradient(135deg,#dc2626,#b91c1c) !important;color:white !important;border-color:transparent !important;} + .export-pptx-btn{background:linear-gradient(135deg,#C65D21,#E07832) !important;color:white !important;border-color:transparent !important;} + .export-xlsx-btn{background:linear-gradient(135deg,#16a34a,#15803d) !important;color:white !important;border-color:transparent !important;} + .export-docx-btn{background:linear-gradient(135deg,#1d4ed8,#1e40af) !important;color:white !important;border-color:transparent !important;} + .refresh-btn{background:linear-gradient(135deg,#0891b2,#0e7490) !important;color:white !important;border-color:transparent !important;} + .nav-hidden{display:none !important;} /* ── SLIDES ── */ .slides-container{position:relative;max-width:1400px;margin:0 auto;padding:24px;min-height:75vh;} @@ -115,12 +119,15 @@ .kpi-card .sub{font-size:0.78em;color:var(--text-muted);margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color);} /* ── CHARTS ── */ - .charts-row{display:grid;grid-template-columns:300px 1fr;gap:16px;margin-bottom:22px;} + .charts-row{display:grid;grid-template-columns:280px 1fr;gap:16px;margin-bottom:22px;} @media(max-width:900px){.charts-row{grid-template-columns:1fr;}} + .charts-row-3{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:22px;} + @media(max-width:900px){.charts-row-3{grid-template-columns:1fr;}} .chart-card{background:var(--bg-card);border-radius:14px;padding:18px;border:1px solid var(--border-color);backdrop-filter:blur(10px);} .chart-card-title{font-size:0.9em;font-weight:700;color:var(--text);margin-bottom:14px;display:flex;align-items:center;gap:8px;} .chart-card-title i{color:var(--accent);} .chart-container{position:relative;height:220px;} + .chart-container.tall{height:260px;} /* ── FILTERS ── */ .filters-bar{display:flex;gap:12px;margin-bottom:18px;flex-wrap:wrap;align-items:center;} @@ -132,6 +139,8 @@ .search-wrapper i{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:0.82em;} .search-input{padding:8px 12px 8px 32px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.83em;width:200px;} .search-input:focus{outline:none;border-color:var(--accent);} + .date-input{padding:8px 12px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.83em;} + .date-input:focus{outline:none;border-color:var(--accent);} /* ── TABLES ── */ .table-container{background:var(--bg-card);border-radius:14px;overflow:hidden;border:1px solid var(--border-color);margin-bottom:18px;backdrop-filter:blur(10px);} @@ -143,16 +152,19 @@ th,td{padding:11px 13px;text-align:left;border-bottom:1px solid var(--border-color);vertical-align:top;} th{background:var(--table-header);font-weight:700;font-size:0.74em;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted);cursor:pointer;user-select:none;} th:hover{color:var(--accent);} + th .sort-icon{margin-left:4px;opacity:0.4;font-size:0.85em;} + th.sort-asc .sort-icon, th.sort-desc .sort-icon{opacity:1;color:var(--accent);} tr:hover{background:rgba(0,212,255,0.04);} [data-theme="light"] tr:hover,[data-theme="professional"] tr:hover{background:rgba(2,132,199,0.04);} td{font-size:0.85em;} .table-toolbar{padding:13px 16px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;border-bottom:1px solid var(--border-color);} .table-toolbar-title{font-size:0.9em;font-weight:700;color:var(--text);flex:1;min-width:100px;} .table-pagination{padding:11px 16px;display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--border-color);font-size:0.8em;color:var(--text-muted);flex-wrap:wrap;gap:8px;} - .pagination-btns{display:flex;gap:4px;} + .pagination-btns{display:flex;gap:4px;align-items:center;} .page-btn{width:28px;height:28px;border:1px solid var(--border-color);border-radius:6px;background:var(--bg-card);color:var(--text);font-size:0.8em;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:all 0.15s;} .page-btn:hover{border-color:var(--accent);color:var(--accent);} .page-btn.active{background:var(--accent);color:white;border-color:var(--accent);} + .page-size-select{padding:4px 8px;border:1px solid var(--border-color);border-radius:6px;background:var(--bg-card);color:var(--text);font-size:0.8em;cursor:pointer;} /* ── PROGRESS BAR ── */ .progress-bar{display:flex;align-items:center;gap:8px;} @@ -205,6 +217,8 @@ .btn-action{padding:6px 12px;border:none;border-radius:7px;cursor:pointer;font-size:0.78em;transition:all 0.2s;display:inline-flex;align-items:center;gap:5px;} .btn-primary{background:var(--primary);color:white;} .btn-primary:hover{background:var(--primary-light);} + .btn-warning{background:#f59e0b;color:white;} + .btn-warning:hover{opacity:0.88;} .btn-danger{background:#ef4444;color:white;} .btn-danger:hover{opacity:0.88;} .btn-secondary{background:var(--bg-card);color:var(--text);border:1px solid var(--border-color);} @@ -212,17 +226,35 @@ .log-success{color:var(--success);font-weight:600;} .log-failure{color:var(--danger);font-weight:600;} - /* ── LOADING & TOAST ── */ + /* ── MODAL ── */ + .modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.6);display:none;justify-content:center;align-items:center;z-index:9000;backdrop-filter:blur(4px);} + .modal-overlay.active{display:flex;} + .modal-box{background:var(--bg-card);border:1px solid var(--border-color);border-radius:16px;padding:28px;width:100%;max-width:420px;box-shadow:0 20px 60px rgba(0,0,0,0.4);} + .modal-title{font-size:1.1em;font-weight:700;margin-bottom:18px;display:flex;align-items:center;gap:9px;color:var(--text);} + .modal-field{margin-bottom:13px;} + .modal-field label{display:block;font-size:0.82em;color:var(--text-muted);margin-bottom:5px;font-weight:600;} + .modal-field input,.modal-field select{width:100%;padding:9px 12px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-dark);color:var(--text);font-size:0.88em;} + [data-theme="light"] .modal-field input,[data-theme="professional"] .modal-field input, + [data-theme="light"] .modal-field select,[data-theme="professional"] .modal-field select{background:white;} + .modal-field input:focus,.modal-field select:focus{outline:none;border-color:var(--accent);} + .modal-actions{display:flex;gap:9px;justify-content:flex-end;margin-top:20px;} + + /* ── TOAST (succès / confirm) ── */ + .toast{position:fixed;bottom:28px;left:50%;transform:translateX(-50%);padding:13px 28px;border-radius:10px;z-index:9999;font-size:0.93em;box-shadow:0 5px 20px rgba(0,0,0,0.35);display:none;max-width:92vw;text-align:center;transition:opacity 0.3s;} + .toast.active{display:block;} + .toast.error{background:#dc2626;color:white;} + .toast.success{background:#059669;color:white;} + .toast.warning{background:#f59e0b;color:white;} + + /* ── LOADING ── */ .loading-overlay{position:fixed;inset:0;background:rgba(15,23,42,0.85);display:none;justify-content:center;align-items:center;flex-direction:column;gap:16px;z-index:9998;} .loading-overlay.active{display:flex;} .spinner{width:46px;height:46px;border:4px solid var(--border-color);border-top-color:var(--accent);border-radius:50%;animation:spin 0.9s linear infinite;} @keyframes spin{to{transform:rotate(360deg)}} - .error-toast{position:fixed;bottom:28px;left:50%;transform:translateX(-50%);background:#dc2626;color:white;padding:13px 28px;border-radius:10px;z-index:9999;font-size:0.93em;box-shadow:0 5px 20px rgba(0,0,0,0.35);display:none;max-width:92vw;text-align:center;} - .error-toast.active{display:block;} /* ── FOOTER ── */ .footer{text-align:center;padding:22px;color:var(--text-muted);font-size:0.83em;border-top:1px solid var(--border-color);margin-top:28px;} - .footer img{width:42px;height:42px;border-radius:50%;border:2px solid var(--accent);margin-bottom:7px;object-fit:cover;} + .footer-avatar{width:42px;height:42px;border-radius:50%;border:2px solid var(--accent);margin-bottom:7px;display:inline-flex;align-items:center;justify-content:center;background:var(--primary);color:var(--accent);font-size:1.1em;font-weight:700;} @media(max-width:768px){ .header{flex-direction:column;padding:11px;} @@ -240,7 +272,9 @@
- - - - - - - - + + + + + + + + @@ -456,7 +553,10 @@
Référence Région Entrepreneur Projet Statut Avt. Phy. Période Montant Référence Région Entrepreneur Projet Statut Avt. Phy. Période Montant
Chargement...
- + + + +
Description du projetRégionsEstimation (DT)Durée (mois)Date prévisionnelle DCA
Description du projetRégionsEstimation (DT)Durée (mois)Date prévisionnelle DCA
Chargement...
@@ -475,7 +575,7 @@ - + @@ -501,10 +601,10 @@ - +
- Nabil Derouiche +
Nabil Derouiche
Responsable Achats Zone Sud — Tunisie Telecom
@@ -513,25 +613,31 @@
- +