refonte: style HF, ticker alertes, sidebar Claude, cards KPI
- Typographie Inter, fond #f9fafb, couleur primaire #2563eb - Sidebar toggle ⊞ style Claude.ai, état persisté localStorage - Ticker alertes critiques CSS pur, vitesse dynamique - Cards KPI icône à gauche, grid 4 colonnes responsive - Badges pill #fee2e2/#fef3c7/#dcfce7 sur toutes les tables - Search bar full-width pour Marchés et Pipeline AO - Hover tables #f1f5f9, padding 12×16px, shadows subtiles Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
18ec359cf2
commit
ba77ef8240
622
index.html
622
index.html
|
|
@ -5,188 +5,255 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Marchés RLA - Zone Sud | Tunisie Telecom</title>
|
||||
<link rel="icon" type="image/svg+xml" href="logo-RLA.svg">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="config.js"></script>
|
||||
<style>
|
||||
/* ── VARIABLES ── */
|
||||
:root {
|
||||
--primary: #0b2a55; --primary-light: #1e40af; --accent: #00d4ff;
|
||||
--success: #10b981; --warning: #f59e0b; --danger: #ef4444;
|
||||
--bg-dark: #0f172a; --bg-card: rgba(255,255,255,0.06);
|
||||
--text: #f1f5f9; --text-muted: #94a3b8;
|
||||
--bg-body: linear-gradient(135deg, #0f172a 0%, #1a1a2e 50%, #16213e 100%);
|
||||
--table-header: rgba(0,0,0,0.3); --border-color: rgba(255,255,255,0.1);
|
||||
--primary:#2563eb; --primary-light:#1d4ed8; --accent:#2563eb;
|
||||
--success:#16a34a; --warning:#d97706; --danger:#dc2626;
|
||||
--bg-body:#f9fafb; --bg-card:#ffffff; --bg-dark:#f9fafb;
|
||||
--text:#111827; --text-muted:#6b7280;
|
||||
--border-color:#e5e7eb; --table-header:#f9fafb;
|
||||
--header-bg:#ffffff; --header-text:#111827; --header-border:#e5e7eb;
|
||||
--sidebar-bg:#ffffff; --sidebar-border:#e5e7eb;
|
||||
--shadow-card:0 1px 3px rgba(0,0,0,0.08);
|
||||
--radius-card:12px; --transition:180ms ease;
|
||||
--sidebar-w:240px; --header-h:64px; --topbar-h:var(--header-h);
|
||||
}
|
||||
[data-theme="light"] {
|
||||
--primary: #0b2a55; --primary-light: #1e40af; --accent: #0284c7;
|
||||
--bg-dark: #f8fafc; --bg-card: rgba(255,255,255,0.9);
|
||||
--text: #1e293b; --text-muted: #64748b;
|
||||
--bg-body: linear-gradient(135deg, #e2e8f0 0%, #f1f5f9 50%, #ffffff 100%);
|
||||
--table-header: rgba(11,42,85,0.08); --border-color: rgba(0,0,0,0.1);
|
||||
[data-theme=""] , [data-theme="dark"] {
|
||||
--primary:#1e40af; --primary-light:#3b82f6; --accent:#38bdf8;
|
||||
--bg-body:linear-gradient(135deg,#0f172a 0%,#1a1a2e 50%,#16213e 100%);
|
||||
--bg-card:rgba(255,255,255,0.06); --bg-dark:#0f172a;
|
||||
--text:#f1f5f9; --text-muted:#94a3b8;
|
||||
--border-color:rgba(255,255,255,0.1); --table-header:rgba(0,0,0,0.3);
|
||||
--header-bg:linear-gradient(90deg,#0b2a55,#1e40af); --header-text:#ffffff; --header-border:rgba(255,255,255,0.1);
|
||||
--sidebar-bg:#1e293b; --sidebar-border:rgba(255,255,255,0.08);
|
||||
--shadow-card:0 1px 3px rgba(0,0,0,0.3);
|
||||
}
|
||||
[data-theme="professional"] {
|
||||
--primary:#1f2937; --primary-light:#374151; --accent:#2563eb;
|
||||
--bg-dark: #ffffff; --bg-card: #f9fafb;
|
||||
--bg-body:#f3f4f6; --bg-card:#ffffff; --bg-dark:#ffffff;
|
||||
--text:#111827; --text-muted:#6b7280;
|
||||
--bg-body: #f3f4f6; --table-header: #e5e7eb; --border-color: #d1d5db;
|
||||
--border-color:#d1d5db; --table-header:#f9fafb;
|
||||
--header-bg:#1f2937; --header-text:#ffffff; --header-border:#374151;
|
||||
--sidebar-bg:#ffffff; --sidebar-border:#d1d5db;
|
||||
}
|
||||
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box;}
|
||||
body{font-family:'Segoe UI',system-ui,-apple-system,sans-serif;background:var(--bg-body);min-height:100vh;color:var(--text);overflow-x:hidden;transition:all 0.3s ease;}
|
||||
body.has-ticker { --topbar-h:calc(var(--header-h) + 36px); }
|
||||
|
||||
/* ── RESET ── */
|
||||
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box;}
|
||||
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg-body);min-height:100vh;color:var(--text);overflow-x:hidden;transition:background var(--transition),color var(--transition);font-size:14px;line-height:1.5;}
|
||||
|
||||
<style>
|
||||
/* ── 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{background:var(--bg-card);padding:40px;border-radius:20px;box-shadow:0 8px 32px rgba(0,0,0,0.12);border:1px solid var(--border-color);width:100%;max-width:400px;text-align:center;}
|
||||
.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;}
|
||||
.login-box h2{color:var(--primary);margin-bottom:8px;font-size:1.5em;font-weight:700;}
|
||||
[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;}
|
||||
.login-box input{width:100%;padding:13px 16px;margin-bottom:14px;border:2px solid var(--border-color);border-radius:10px;font-size:1em;background:rgba(255,255,255,0.08);color:var(--text);transition:all 0.3s;}
|
||||
[data-theme="light"] .login-box input,[data-theme="professional"] .login-box input{background:white;}
|
||||
.login-box input:focus{border-color:var(--accent);outline:none;background:rgba(255,255,255,0.12);}
|
||||
.login-box button{width:100%;padding:14px;background:linear-gradient(135deg,var(--primary),var(--primary-light));color:white;border:none;border-radius:10px;font-size:1.05em;cursor:pointer;transition:all 0.3s;font-weight:600;}
|
||||
.login-box button:hover{transform:translateY(-2px);box-shadow:0 5px 20px rgba(0,0,0,0.3);}
|
||||
.login-error{color:var(--danger);margin-bottom:14px;font-size:0.88em;display:none;background:rgba(239,68,68,0.1);padding:8px 12px;border-radius:8px;}
|
||||
.login-box input{width:100%;padding:11px 14px;margin-bottom:12px;border:1.5px solid var(--border-color);border-radius:8px;font-size:0.95em;background:var(--bg-card);color:var(--text);transition:border-color var(--transition);font-family:inherit;}
|
||||
.login-box input:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px rgba(37,99,235,0.1);}
|
||||
.login-box button{width:100%;padding:12px;background:var(--primary);color:white;border:none;border-radius:8px;font-size:1em;cursor:pointer;transition:background var(--transition);font-weight:600;font-family:inherit;}
|
||||
.login-box button:hover{background:var(--primary-light);}
|
||||
.login-error{color:var(--danger);margin-bottom:14px;font-size:0.88em;display:none;background:#fef2f2;padding:8px 12px;border-radius:8px;border:1px solid #fecaca;}
|
||||
.login-error.visible{display:block;}
|
||||
[data-theme=""] .login-error,[data-theme="dark"] .login-error{background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3);}
|
||||
|
||||
/* ── APP CONTENT ── */
|
||||
.app-content{display:none;}
|
||||
.app-content.active{display:block;}
|
||||
|
||||
/* ── HEADER ── */
|
||||
.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;}
|
||||
.theme-selector{display:flex;gap:4px;background:rgba(255,255,255,0.1);padding:4px;border-radius:24px;}
|
||||
.theme-btn{padding:6px 11px;border:none;border-radius:18px;cursor:pointer;font-size:0.82em;background:transparent;color:white;transition:all 0.3s;}
|
||||
.theme-btn.active{background:rgba(255,255,255,0.2);}
|
||||
.theme-btn:hover{background:rgba(255,255,255,0.15);}
|
||||
.user-info{display:flex;align-items:center;gap:10px;color:white;font-size:0.88em;}
|
||||
.user-badge{background:rgba(255,255,255,0.2);padding:4px 10px;border-radius:20px;font-size:0.8em;}
|
||||
.user-badge.superadmin{background:linear-gradient(135deg,#f59e0b,#d97706);}
|
||||
.header-btn{padding:7px 14px;border:none;border-radius:20px;color:white;cursor:pointer;font-size:0.82em;transition:all 0.3s;display:inline-flex;align-items:center;gap:6px;}
|
||||
.header-btn:hover{filter:brightness(1.15);transform:translateY(-1px);}
|
||||
.btn-admin{background:rgba(37,99,235,0.8);}
|
||||
.btn-logout{background:rgba(239,68,68,0.8);}
|
||||
.header-info{text-align:right;font-size:0.82em;color:rgba(255,255,255,0.75);}
|
||||
.header{background:var(--header-bg);color:var(--header-text);padding:0 20px;height:var(--header-h);display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--header-border);box-shadow:var(--shadow-card);position:sticky;top:0;z-index:100;gap:12px;}
|
||||
.header-left{display:flex;align-items:center;gap:10px;}
|
||||
.sidebar-toggle{width:36px;height:36px;border:none;border-radius:8px;background:transparent;cursor:pointer;color:inherit;display:flex;align-items:center;justify-content:center;font-size:1.25em;transition:background var(--transition);flex-shrink:0;opacity:0.8;}
|
||||
.sidebar-toggle:hover{background:rgba(128,128,128,0.15);opacity:1;}
|
||||
[data-theme=""] .sidebar-toggle,[data-theme="dark"] .sidebar-toggle{color:white;}
|
||||
.logo-section{display:flex;align-items:center;gap:12px;}
|
||||
.logo-section img{height:40px;object-fit:contain;}
|
||||
.logo-section h1{font-size:1.05em;font-weight:700;color:inherit;line-height:1.2;}
|
||||
.logo-section .sub{font-size:0.72em;color:var(--text-muted);margin-top:1px;}
|
||||
[data-theme=""] .logo-section .sub,[data-theme="dark"] .logo-section .sub{color:rgba(255,255,255,0.6);}
|
||||
[data-theme="light"] .logo-section h1{color:#111827;}
|
||||
.header-controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
|
||||
.theme-selector{display:flex;gap:3px;background:rgba(128,128,128,0.1);padding:3px;border-radius:20px;}
|
||||
.theme-btn{padding:5px 10px;border:none;border-radius:16px;cursor:pointer;font-size:0.78em;background:transparent;color:inherit;transition:background var(--transition);opacity:0.7;}
|
||||
.theme-btn.active{background:rgba(128,128,128,0.2);opacity:1;}
|
||||
.theme-btn:hover{background:rgba(128,128,128,0.15);opacity:1;}
|
||||
.user-info{display:flex;align-items:center;gap:8px;font-size:0.85em;color:inherit;}
|
||||
[data-theme=""] .user-info,[data-theme="dark"] .user-info{color:white;}
|
||||
.user-badge{background:rgba(128,128,128,0.15);padding:3px 9px;border-radius:16px;font-size:0.78em;font-weight:600;}
|
||||
.user-badge.superadmin{background:linear-gradient(135deg,#f59e0b,#d97706);color:white;}
|
||||
.header-btn{padding:6px 12px;border:none;border-radius:16px;color:white;cursor:pointer;font-size:0.78em;transition:all var(--transition);display:inline-flex;align-items:center;gap:5px;font-family:inherit;font-weight:500;}
|
||||
.header-btn:hover{filter:brightness(1.1);transform:translateY(-1px);}
|
||||
.btn-admin{background:#2563eb;}
|
||||
.btn-logout{background:#dc2626;}
|
||||
.header-info{text-align:right;font-size:0.78em;color:var(--text-muted);white-space:nowrap;}
|
||||
[data-theme=""] .header-info,[data-theme="dark"] .header-info{color:rgba(255,255,255,0.6);}
|
||||
.header-info .date{color:var(--accent);font-weight:700;}
|
||||
|
||||
/* ── SLIDE NAV ── */
|
||||
.slide-nav{display:flex;justify-content:center;gap:6px;padding:12px 16px;background:rgba(0,0,0,0.2);flex-wrap:wrap;}
|
||||
[data-theme="light"] .slide-nav,[data-theme="professional"] .slide-nav{background:rgba(0,0,0,0.05);}
|
||||
.slide-nav button{padding:9px 16px;border:none;border-radius:24px;background:var(--bg-card);color:var(--text);cursor:pointer;transition:all 0.3s ease;font-size:0.83em;display:inline-flex;align-items:center;gap:7px;border:1px solid var(--border-color);}
|
||||
.slide-nav button:hover{background:var(--primary-light);color:white;transform:translateY(-2px);}
|
||||
.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;}
|
||||
/* ── ALERT TICKER ── */
|
||||
.alert-ticker{position:sticky;top:var(--header-h);z-index:95;background:#dc2626;color:white;height:36px;overflow:hidden;display:none;align-items:center;}
|
||||
.alert-ticker.active{display:flex;}
|
||||
.ticker-track{width:100%;overflow:hidden;height:36px;display:flex;align-items:center;}
|
||||
.ticker-content{display:inline-block;white-space:nowrap;font-size:0.82em;font-weight:500;animation:marquee 20s linear infinite;padding-right:60px;}
|
||||
@keyframes marquee{0%{transform:translateX(100vw)}100%{transform:translateX(-100%)}}
|
||||
|
||||
/* ── APP LAYOUT ── */
|
||||
.app-layout{display:flex;min-height:calc(100vh - var(--header-h));}
|
||||
|
||||
/* ── SIDEBAR ── */
|
||||
.sidebar{width:var(--sidebar-w);flex-shrink:0;background:var(--sidebar-bg);border-right:1px solid var(--sidebar-border);position:sticky;top:var(--topbar-h);height:calc(100vh - var(--topbar-h));align-self:flex-start;overflow-y:auto;transition:margin-left var(--transition),width var(--transition);display:flex;flex-direction:column;}
|
||||
.sidebar.hidden{margin-left:calc(-1 * var(--sidebar-w));width:0;overflow:hidden;}
|
||||
.sidebar-nav{padding:12px 8px;flex:1;display:flex;flex-direction:column;gap:2px;}
|
||||
.sidebar-section-label{font-size:0.68em;font-weight:700;text-transform:uppercase;letter-spacing:0.8px;color:var(--text-muted);padding:8px 10px 4px;margin-top:8px;}
|
||||
.sidebar-section-label:first-child{margin-top:0;}
|
||||
.sidebar-btn{width:100%;padding:9px 12px;border:none;border-radius:8px;background:transparent;color:var(--text);cursor:pointer;transition:all var(--transition);font-size:0.84em;display:flex;align-items:center;gap:9px;text-align:left;font-family:inherit;font-weight:500;}
|
||||
.sidebar-btn:hover{background:rgba(37,99,235,0.08);color:var(--primary);}
|
||||
.sidebar-btn.active{background:rgba(37,99,235,0.1);color:var(--primary);font-weight:600;}
|
||||
[data-theme=""] .sidebar-btn:hover,[data-theme="dark"] .sidebar-btn:hover{background:rgba(255,255,255,0.08);color:var(--accent);}
|
||||
[data-theme=""] .sidebar-btn.active,[data-theme="dark"] .sidebar-btn.active{background:rgba(56,189,248,0.12);color:var(--accent);}
|
||||
.sidebar-btn i{width:16px;text-align:center;opacity:0.7;font-size:0.9em;}
|
||||
.sidebar-btn.active i{opacity:1;}
|
||||
.sidebar-divider{height:1px;background:var(--border-color);margin:10px 8px;}
|
||||
.sidebar-export{padding:8px;display:flex;flex-direction:column;gap:4px;border-top:1px solid var(--border-color);}
|
||||
.sidebar-export-label{font-size:0.68em;font-weight:700;text-transform:uppercase;letter-spacing:0.8px;color:var(--text-muted);padding:4px 10px 6px;}
|
||||
.sidebar-export-row{display:flex;gap:4px;flex-wrap:wrap;padding:0 2px;}
|
||||
.export-btn-sm{flex:1;padding:6px 8px;border:none;border-radius:6px;color:white;cursor:pointer;font-size:0.72em;font-weight:600;display:inline-flex;align-items:center;justify-content:center;gap:4px;transition:opacity var(--transition);font-family:inherit;min-width:50px;}
|
||||
.export-btn-sm:hover{opacity:0.88;transform:translateY(-1px);}
|
||||
.export-btn-sm.pdf-btn{background:#dc2626;}
|
||||
.export-btn-sm.pptx-btn{background:#C65D21;}
|
||||
.export-btn-sm.xlsx-btn{background:#16a34a;}
|
||||
.export-btn-sm.docx-btn{background:#1d4ed8;}
|
||||
.export-btn-sm.refresh-btn-sm{background:var(--accent);}
|
||||
.nav-hidden{display:none !important;}
|
||||
|
||||
/* ── MAIN WRAPPER ── */
|
||||
.main-wrapper{flex:1;min-width:0;transition:all var(--transition);}
|
||||
|
||||
/* ── SLIDES ── */
|
||||
.slides-container{position:relative;max-width:1400px;margin:0 auto;padding:24px;min-height:75vh;}
|
||||
.slide{display:none;animation:slideIn 0.35s ease-out;}
|
||||
.slides-container{max-width:1400px;margin:0 auto;padding:24px;min-height:75vh;}
|
||||
.slide{display:none;animation:slideIn 0.3s ease-out;}
|
||||
.slide.active{display:block;}
|
||||
@keyframes slideIn{from{opacity:0;transform:translateX(15px)}to{opacity:1;transform:translateX(0)}}
|
||||
@keyframes slideIn{from{opacity:0;transform:translateX(12px)}to{opacity:1;transform:translateX(0)}}
|
||||
|
||||
/* ── SECTION TITLE ── */
|
||||
.section-title{font-size:1.35em;margin-bottom:18px;padding-left:14px;border-left:4px solid var(--accent);display:flex;align-items:center;gap:11px;color:var(--text);}
|
||||
.section-title{font-size:1.25em;font-weight:700;margin-bottom:20px;padding-left:14px;border-left:4px solid var(--accent);display:flex;align-items:center;gap:10px;color:var(--text);}
|
||||
|
||||
/* ── KPI GRID ── */
|
||||
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:18px;margin-bottom:22px;}
|
||||
.kpi-card{background:var(--bg-card);border-radius:16px;padding:20px;border:1px solid var(--border-color);transition:all 0.3s ease;position:relative;overflow:hidden;backdrop-filter:blur(10px);}
|
||||
.kpi-card::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:var(--accent);}
|
||||
.kpi-card.capex::before{background:var(--success);}
|
||||
.kpi-card.opex::before{background:var(--warning);}
|
||||
.kpi-card.alertes::before{background:var(--danger);}
|
||||
.kpi-card.clotures::before{background:#6b7280;}
|
||||
.kpi-card.proactif-normal::before{background:#059669;}
|
||||
.kpi-card.proactif-sous::before{background:#DC2626;}
|
||||
.kpi-card.proactif-depasse::before{background:#D97706;}
|
||||
.kpi-card.proactif-none::before{background:#64748B;}
|
||||
.kpi-card:hover{transform:translateY(-3px);box-shadow:0 8px 30px rgba(0,0,0,0.2);}
|
||||
.kpi-card .icon{font-size:2em;margin-bottom:10px;opacity:0.85;}
|
||||
.kpi-card .value{font-size:2em;font-weight:800;color:var(--accent);}
|
||||
.kpi-card .label{font-size:0.88em;color:var(--text-muted);margin-top:4px;}
|
||||
.kpi-card .sub{font-size:0.78em;color:var(--text-muted);margin-top:8px;padding-top:8px;border-top:1px solid var(--border-color);}
|
||||
.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px;}
|
||||
@media(max-width:1024px){.kpi-grid{grid-template-columns:repeat(2,1fr);}}
|
||||
@media(max-width:640px){.kpi-grid{grid-template-columns:1fr;}}
|
||||
.kpi-card{background:var(--bg-card);border-radius:var(--radius-card);padding:20px;border:1px solid var(--border-color);box-shadow:var(--shadow-card);transition:all var(--transition);display:flex;align-items:center;gap:16px;}
|
||||
.kpi-card:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.12);}
|
||||
.kpi-icon{width:52px;height:52px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:1.4em;flex-shrink:0;}
|
||||
.kpi-icon.blue{background:#eff6ff;color:#2563eb;}
|
||||
.kpi-icon.green{background:#f0fdf4;color:#16a34a;}
|
||||
.kpi-icon.red{background:#fef2f2;color:#dc2626;}
|
||||
.kpi-icon.gray{background:#f9fafb;color:#6b7280;}
|
||||
[data-theme=""] .kpi-icon.blue,[data-theme="dark"] .kpi-icon.blue{background:rgba(37,99,235,0.15);}
|
||||
[data-theme=""] .kpi-icon.green,[data-theme="dark"] .kpi-icon.green{background:rgba(22,163,74,0.15);}
|
||||
[data-theme=""] .kpi-icon.red,[data-theme="dark"] .kpi-icon.red{background:rgba(220,38,38,0.15);}
|
||||
[data-theme=""] .kpi-icon.gray,[data-theme="dark"] .kpi-icon.gray{background:rgba(107,114,128,0.15);}
|
||||
.kpi-body{flex:1;min-width:0;}
|
||||
.kpi-card .value{font-size:2rem;font-weight:700;line-height:1;color:var(--text);}
|
||||
.kpi-card .label{font-size:0.82em;color:#6b7280;margin-top:4px;}
|
||||
.kpi-card .sub{font-size:0.75em;color:var(--text-muted);margin-top:6px;padding-top:6px;border-top:1px solid var(--border-color);}
|
||||
/* legacy compat — proactif cards keep ::before accent */
|
||||
.kpi-card.proactif-normal,.kpi-card.proactif-sous,.kpi-card.proactif-depasse,.kpi-card.proactif-none{position:relative;overflow:hidden;}
|
||||
.kpi-card.proactif-normal::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:#059669;}
|
||||
.kpi-card.proactif-sous::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:#DC2626;}
|
||||
.kpi-card.proactif-depasse::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:#D97706;}
|
||||
.kpi-card.proactif-none::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:#64748B;}
|
||||
|
||||
/* ── CHARTS ── */
|
||||
.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{background:var(--bg-card);border-radius:var(--radius-card);padding:18px;border:1px solid var(--border-color);box-shadow:var(--shadow-card);}
|
||||
.chart-card-title{font-size:0.88em;font-weight:600;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;}
|
||||
|
||||
/* ── SEARCH BAR FULL WIDTH ── */
|
||||
.search-bar-full{position:relative;margin-bottom:12px;}
|
||||
.search-bar-full i{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:1em;}
|
||||
.search-input-full{width:100%;padding:11px 14px 11px 42px;border:1.5px solid var(--border-color);border-radius:10px;background:var(--bg-card);color:var(--text);font-size:0.9em;font-family:inherit;transition:border-color var(--transition),box-shadow var(--transition);box-shadow:var(--shadow-card);}
|
||||
.search-input-full:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(37,99,235,0.1);}
|
||||
.search-input-full::placeholder{color:var(--text-muted);}
|
||||
|
||||
/* ── FILTERS ── */
|
||||
.filters-bar{display:flex;gap:12px;margin-bottom:18px;flex-wrap:wrap;align-items:center;}
|
||||
.filter-group{display:flex;align-items:center;gap:7px;}
|
||||
.filter-group label{font-size:0.83em;color:var(--text-muted);font-weight:600;}
|
||||
.filter-group select,.filter-select{padding:8px 12px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.83em;min-width:150px;cursor:pointer;}
|
||||
.filters-bar{display:flex;gap:10px;margin-bottom:18px;flex-wrap:wrap;align-items:center;}
|
||||
.filter-group{display:flex;align-items:center;gap:6px;}
|
||||
.filter-group label{font-size:0.8em;color:var(--text-muted);font-weight:600;white-space:nowrap;}
|
||||
.filter-group select,.filter-select{padding:8px 12px;border:1.5px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.82em;min-width:140px;cursor:pointer;font-family:inherit;transition:border-color var(--transition);}
|
||||
.filter-group select:focus,.filter-select:focus{outline:none;border-color:var(--accent);}
|
||||
.search-wrapper{position:relative;}
|
||||
.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{padding:8px 12px 8px 30px;border:1.5px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.82em;width:200px;font-family:inherit;}
|
||||
.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{padding:8px 12px;border:1.5px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.82em;font-family:inherit;}
|
||||
.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);}
|
||||
.table-header{background:linear-gradient(90deg,var(--primary),var(--primary-light));padding:13px 18px;display:flex;justify-content:space-between;align-items:center;color:white;}
|
||||
.table-header h3{font-size:0.95em;display:flex;align-items:center;gap:9px;}
|
||||
.table-header .badge{background:rgba(255,255,255,0.2);padding:3px 10px;border-radius:20px;font-size:0.82em;}
|
||||
.table-container{background:var(--bg-card);border-radius:var(--radius-card);overflow:hidden;border:1px solid var(--border-color);box-shadow:var(--shadow-card);margin-bottom:20px;}
|
||||
.table-header{background:var(--primary);padding:12px 18px;display:flex;justify-content:space-between;align-items:center;color:white;}
|
||||
.table-header h3{font-size:0.9em;font-weight:600;display:flex;align-items:center;gap:8px;}
|
||||
.table-header .badge{background:rgba(255,255,255,0.2);padding:2px 9px;border-radius:16px;font-size:0.78em;font-weight:600;}
|
||||
.table-wrapper{overflow-x:auto;}
|
||||
table{width:100%;border-collapse:collapse;min-width:600px;}
|
||||
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,td{padding:12px 16px;text-align:left;border-bottom:1px solid var(--border-color);vertical-align:middle;}
|
||||
th{background:var(--table-header);font-weight:600;font-size:0.72em;text-transform:uppercase;letter-spacing:0.6px;color:var(--text-muted);cursor:pointer;user-select:none;white-space:nowrap;}
|
||||
th:hover{color:var(--accent);}
|
||||
th .sort-icon{margin-left:4px;opacity:0.4;font-size:0.85em;}
|
||||
th .sort-icon{margin-left:4px;opacity:0.35;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;}
|
||||
tbody tr:hover{background:#f1f5f9;}
|
||||
[data-theme=""] tbody tr:hover,[data-theme="dark"] tbody tr:hover{background:rgba(255,255,255,0.04);}
|
||||
td{font-size:0.84em;}
|
||||
.table-toolbar{padding:12px 16px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;border-bottom:1px solid var(--border-color);}
|
||||
.table-toolbar-title{font-size:0.88em;font-weight:600;color:var(--text);flex:1;min-width:100px;}
|
||||
.table-pagination{padding:10px 16px;display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--border-color);font-size:0.78em;color:var(--text-muted);flex-wrap:wrap;gap:8px;}
|
||||
.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{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 var(--transition);}
|
||||
.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;}
|
||||
.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.78em;cursor:pointer;font-family:inherit;}
|
||||
|
||||
/* ── PROGRESS BAR ── */
|
||||
.progress-bar{display:flex;align-items:center;gap:8px;}
|
||||
.progress-track{flex:1;height:7px;background:var(--border-color);border-radius:4px;overflow:hidden;min-width:60px;}
|
||||
.progress-fill{height:100%;border-radius:4px;transition:width 0.6s ease-out;}
|
||||
.progress-fill.green{background:linear-gradient(90deg,#10b981,#34d399);}
|
||||
.progress-fill.orange{background:linear-gradient(90deg,#f59e0b,#fbbf24);}
|
||||
.progress-fill.red{background:linear-gradient(90deg,#ef4444,#f87171);}
|
||||
.progress-value{font-weight:700;min-width:38px;text-align:right;font-size:0.88em;}
|
||||
.progress-track{flex:1;height:6px;background:var(--border-color);border-radius:4px;overflow:hidden;min-width:60px;}
|
||||
.progress-fill{height:100%;border-radius:4px;transition:width 0.5s ease-out;}
|
||||
.progress-fill.green{background:#16a34a;}
|
||||
.progress-fill.orange{background:#d97706;}
|
||||
.progress-fill.red{background:#dc2626;}
|
||||
.progress-value{font-weight:700;min-width:36px;text-align:right;font-size:0.85em;}
|
||||
|
||||
/* ── BADGES ── */
|
||||
.status-badge{padding:3px 9px;border-radius:14px;font-size:0.73em;font-weight:700;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;}
|
||||
.status-badge.critique{background:rgba(239,68,68,0.18);color:#ef4444;}
|
||||
.status-badge.attention{background:rgba(245,158,11,0.18);color:#f59e0b;}
|
||||
.status-badge.ok{background:rgba(16,185,129,0.18);color:#10b981;}
|
||||
.status-badge.info{background:rgba(59,130,246,0.15);color:#3b82f6;}
|
||||
.status-badge.muted{background:rgba(107,114,128,0.18);color:#6b7280;}
|
||||
.status-badge.superadmin{background:rgba(245,158,11,0.18);color:#f59e0b;}
|
||||
.status-badge.admin{background:rgba(59,130,246,0.15);color:#3b82f6;}
|
||||
.status-badge.user{background:rgba(16,185,129,0.18);color:#10b981;}
|
||||
.status-badge{padding:3px 10px;border-radius:20px;font-size:0.72em;font-weight:600;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;}
|
||||
.status-badge.critique{background:#fee2e2;color:#dc2626;}
|
||||
.status-badge.attention{background:#fef3c7;color:#d97706;}
|
||||
.status-badge.ok{background:#dcfce7;color:#16a34a;}
|
||||
.status-badge.info{background:#eff6ff;color:#2563eb;}
|
||||
.status-badge.muted{background:#f3f4f6;color:#6b7280;}
|
||||
.status-badge.superadmin{background:#fef3c7;color:#d97706;}
|
||||
.status-badge.admin{background:#eff6ff;color:#2563eb;}
|
||||
.status-badge.user{background:#dcfce7;color:#16a34a;}
|
||||
[data-theme=""] .status-badge.critique,[data-theme="dark"] .status-badge.critique{background:rgba(220,38,38,0.2);}
|
||||
[data-theme=""] .status-badge.attention,[data-theme="dark"] .status-badge.attention{background:rgba(217,119,6,0.2);}
|
||||
[data-theme=""] .status-badge.ok,[data-theme="dark"] .status-badge.ok{background:rgba(22,163,74,0.2);}
|
||||
[data-theme=""] .status-badge.info,[data-theme="dark"] .status-badge.info{background:rgba(37,99,235,0.2);}
|
||||
[data-theme=""] .status-badge.muted,[data-theme="dark"] .status-badge.muted{background:rgba(107,114,128,0.2);}
|
||||
|
||||
/* ── SITUATION PHRASE ── */
|
||||
.situation-phrase{background:var(--bg-card);border:1px solid var(--border-color);border-left:4px solid var(--accent);border-radius:10px;padding:14px 20px;margin-bottom:20px;font-size:0.97em;color:var(--text);backdrop-filter:blur(10px);line-height:1.6;}
|
||||
.situation-phrase{background:var(--bg-card);border:1px solid var(--border-color);border-left:4px solid var(--accent);border-radius:var(--radius-card);padding:14px 20px;margin-bottom:20px;font-size:0.93em;color:var(--text);line-height:1.6;box-shadow:var(--shadow-card);}
|
||||
.situation-phrase strong{color:var(--accent);}
|
||||
.situation-phrase .situ-danger{color:var(--danger);font-weight:700;}
|
||||
.situation-phrase .situ-warn{color:var(--warning);font-weight:700;}
|
||||
|
|
@ -196,10 +263,10 @@
|
|||
.synthese-row{display:grid;grid-template-columns:220px 1fr;gap:16px;margin-bottom:22px;align-items:start;}
|
||||
@media(max-width:900px){.synthese-row{grid-template-columns:1fr;}}
|
||||
.statut-blocs{display:flex;flex-direction:column;gap:10px;}
|
||||
.statut-bloc{border-radius:14px;padding:16px 18px;text-align:center;border:1px solid var(--border-color);backdrop-filter:blur(10px);}
|
||||
.statut-bloc.critique{background:rgba(239,68,68,0.12);border-color:rgba(239,68,68,0.35);}
|
||||
.statut-bloc.attention{background:rgba(245,158,11,0.12);border-color:rgba(245,158,11,0.35);}
|
||||
.statut-bloc.ok{background:rgba(16,185,129,0.10);border-color:rgba(16,185,129,0.35);}
|
||||
.statut-bloc{border-radius:var(--radius-card);padding:16px 18px;text-align:center;border:1px solid var(--border-color);box-shadow:var(--shadow-card);background:var(--bg-card);}
|
||||
.statut-bloc.critique{border-left:4px solid var(--danger);}
|
||||
.statut-bloc.attention{border-left:4px solid var(--warning);}
|
||||
.statut-bloc.ok{border-left:4px solid var(--success);}
|
||||
.statut-bloc-value{font-size:2.2em;font-weight:800;line-height:1;}
|
||||
.statut-bloc.critique .statut-bloc-value{color:var(--danger);}
|
||||
.statut-bloc.attention .statut-bloc-value{color:var(--warning);}
|
||||
|
|
@ -209,10 +276,10 @@
|
|||
/* ── REGION JAUGES ── */
|
||||
.region-jauge-row{display:flex;align-items:center;gap:10px;margin-bottom:9px;}
|
||||
.region-jauge-name{width:72px;font-size:0.8em;font-weight:600;color:var(--text);text-align:right;flex-shrink:0;}
|
||||
.region-jauge-track{flex:1;height:16px;background:var(--border-color);border-radius:8px;overflow:hidden;position:relative;}
|
||||
.region-jauge-fill{height:100%;border-radius:8px;transition:width 0.7s ease-out;display:flex;align-items:center;justify-content:flex-end;padding-right:6px;}
|
||||
.region-jauge-fill span{font-size:0.72em;font-weight:700;color:white;white-space:nowrap;}
|
||||
.region-jauge-meta{font-size:0.73em;color:var(--text-muted);width:56px;flex-shrink:0;}
|
||||
.region-jauge-track{flex:1;height:14px;background:var(--border-color);border-radius:8px;overflow:hidden;}
|
||||
.region-jauge-fill{height:100%;border-radius:8px;transition:width 0.6s ease-out;display:flex;align-items:center;justify-content:flex-end;padding-right:6px;}
|
||||
.region-jauge-fill span{font-size:0.7em;font-weight:700;color:white;white-space:nowrap;}
|
||||
.region-jauge-meta{font-size:0.72em;color:var(--text-muted);width:56px;flex-shrink:0;}
|
||||
|
||||
/* ── PRIORITE BADGE ── */
|
||||
.prio-badge{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;font-size:0.75em;font-weight:800;}
|
||||
|
|
@ -221,7 +288,7 @@
|
|||
|
||||
/* ── ALERT CARDS ── */
|
||||
.alert-list{display:flex;flex-direction:column;gap:10px;}
|
||||
.alert-card{background:var(--bg-card);border:1px solid var(--border-color);border-radius:12px;padding:14px 16px;display:flex;align-items:center;gap:14px;backdrop-filter:blur(10px);}
|
||||
.alert-card{background:var(--bg-card);border:1px solid var(--border-color);border-radius:var(--radius-card);padding:14px 16px;display:flex;align-items:center;gap:14px;box-shadow:var(--shadow-card);}
|
||||
.alert-card.critique{border-left:4px solid var(--danger);}
|
||||
.alert-card.attention{border-left:4px solid var(--warning);}
|
||||
.alert-days{min-width:56px;text-align:center;font-size:1.5em;font-weight:800;line-height:1;}
|
||||
|
|
@ -234,20 +301,20 @@
|
|||
|
||||
/* ── REGION CARDS ── */
|
||||
.regions-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;}
|
||||
.region-card{background:var(--bg-card);border-radius:14px;padding:16px;border:2px solid #0066CC;transition:all 0.3s ease;backdrop-filter:blur(10px);}
|
||||
.region-card:hover{transform:scale(1.01);box-shadow:0 6px 25px rgba(0,102,204,0.2);border-color:#0088FF;}
|
||||
.region-card{background:var(--bg-card);border-radius:var(--radius-card);padding:16px;border:2px solid #0066CC;box-shadow:var(--shadow-card);transition:all var(--transition);}
|
||||
.region-card:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(0,102,204,0.15);border-color:#0088FF;}
|
||||
.region-header{display:flex;align-items:center;gap:11px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px solid var(--border-color);}
|
||||
.region-dot{width:11px;height:11px;border-radius:50%;}
|
||||
.region-dot{width:10px;height:10px;border-radius:50%;}
|
||||
.region-stats{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;}
|
||||
.region-stat{background:var(--table-header);padding:9px;border-radius:8px;text-align:center;}
|
||||
.region-stat .value{font-size:1.25em;font-weight:800;color:var(--accent);}
|
||||
.region-stat .value{font-size:1.2em;font-weight:800;color:var(--accent);}
|
||||
.region-stat .label{font-size:0.68em;color:var(--text-muted);margin-top:2px;}
|
||||
|
||||
/* ── ADMIN PANEL ── */
|
||||
.admin-form-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px;padding:14px 16px;background:var(--table-header);border-bottom:1px solid var(--border-color);}
|
||||
.admin-form-row input,.admin-form-row select{padding:8px 11px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-dark);color:var(--text);font-size:0.83em;}
|
||||
.admin-form-row input,.admin-form-row select{padding:8px 11px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.83em;font-family:inherit;}
|
||||
.admin-form-row input:focus,.admin-form-row select:focus{outline:none;border-color:var(--accent);}
|
||||
.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-action{padding:6px 12px;border:none;border-radius:7px;cursor:pointer;font-size:0.78em;transition:all var(--transition);display:inline-flex;align-items:center;gap:5px;font-family:inherit;font-weight:500;}
|
||||
.btn-primary{background:var(--primary);color:white;}
|
||||
.btn-primary:hover{background:var(--primary-light);}
|
||||
.btn-warning{background:#f59e0b;color:white;}
|
||||
|
|
@ -260,52 +327,50 @@
|
|||
.log-failure{color:var(--danger);font-weight:600;}
|
||||
|
||||
/* ── 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{position:fixed;inset:0;background:rgba(0,0,0,0.5);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-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.2);}
|
||||
.modal-title{font-size:1.05em;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-field label{display:block;font-size:0.8em;color:var(--text-muted);margin-bottom:5px;font-weight:600;}
|
||||
.modal-field input,.modal-field select{width:100%;padding:9px 12px;border:1.5px solid var(--border-color);border-radius:8px;background:var(--bg-card);color:var(--text);font-size:0.88em;font-family:inherit;}
|
||||
.modal-field input:focus,.modal-field select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(37,99,235,0.1);}
|
||||
.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 ── */
|
||||
.toast{position:fixed;bottom:28px;left:50%;transform:translateX(-50%);padding:12px 24px;border-radius:10px;z-index:9999;font-size:0.9em;box-shadow:0 4px 16px rgba(0,0,0,0.2);display:none;max-width:92vw;text-align:center;transition:opacity var(--transition);}
|
||||
.toast.active{display:block;}
|
||||
.toast.error{background:#dc2626;color:white;}
|
||||
.toast.success{background:#059669;color:white;}
|
||||
.toast.warning{background:#f59e0b;color:white;}
|
||||
.toast.success{background:#16a34a;color:white;}
|
||||
.toast.warning{background:#d97706;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{position:fixed;inset:0;background:rgba(0,0,0,0.6);display:none;justify-content:center;align-items:center;flex-direction:column;gap:16px;z-index:9998;backdrop-filter:blur(3px);}
|
||||
.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;}
|
||||
.spinner{width:44px;height:44px;border:3px solid rgba(255,255,255,0.2);border-top-color:#2563eb;border-radius:50%;animation:spin 0.8s linear infinite;}
|
||||
@keyframes spin{to{transform:rotate(360deg)}}
|
||||
|
||||
/* ── PIPELINE AO ── */
|
||||
.phase-badge{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:12px;font-size:0.73em;font-weight:700;color:white;}
|
||||
.pipeline-total{padding:11px 16px;text-align:right;border-top:1px solid var(--border-color);font-size:0.85em;color:var(--text-muted);}
|
||||
.pipeline-total strong{color:var(--accent);font-size:1.05em;}
|
||||
.region-tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:0.72em;font-weight:700;color:white;margin:1px;}
|
||||
.phase-badge{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:10px;font-size:0.72em;font-weight:700;color:white;}
|
||||
.pipeline-total{padding:10px 16px;text-align:right;border-top:1px solid var(--border-color);font-size:0.84em;color:var(--text-muted);}
|
||||
.pipeline-total strong{color:var(--accent);font-size:1em;font-weight:700;}
|
||||
.region-tag{display:inline-flex;align-items:center;padding:2px 7px;border-radius:8px;font-size:0.7em;font-weight:700;color:white;margin:1px;}
|
||||
|
||||
/* ── REGION CARD — AO SUIVANT ── */
|
||||
.region-suivant{margin-top:10px;padding-top:10px;border-top:1px dashed var(--border-color);}
|
||||
.region-suivant-title{font-size:0.72em;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;display:flex;align-items:center;gap:5px;}
|
||||
.region-suivant-title{font-size:0.7em;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted);margin-bottom:6px;display:flex;align-items:center;gap:5px;}
|
||||
.region-suivant-item{font-size:0.78em;color:var(--text);padding:5px 8px;background:var(--table-header);border-radius:6px;margin-bottom:4px;display:flex;justify-content:space-between;align-items:center;gap:8px;}
|
||||
|
||||
/* ── MODERNISATION SUCCESSION ── */
|
||||
.moderni-region-block{background:var(--bg-card);border-radius:14px;border:1px solid var(--border-color);margin-bottom:18px;overflow:hidden;backdrop-filter:blur(10px);}
|
||||
.moderni-region-header{background:linear-gradient(90deg,var(--primary),var(--primary-light));padding:11px 16px;display:flex;align-items:center;gap:10px;color:white;font-weight:700;}
|
||||
.moderni-region-block{background:var(--bg-card);border-radius:var(--radius-card);border:1px solid var(--border-color);box-shadow:var(--shadow-card);margin-bottom:18px;overflow:hidden;}
|
||||
.moderni-region-header{background:var(--primary);padding:11px 16px;display:flex;align-items:center;gap:10px;color:white;font-weight:600;}
|
||||
.moderni-region-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0;}
|
||||
.moderni-body{display:grid;grid-template-columns:1fr 1fr;gap:0;}
|
||||
@media(max-width:900px){.moderni-body{grid-template-columns:1fr;}}
|
||||
.moderni-col{padding:14px 16px;}
|
||||
.moderni-col.actuel{border-right:1px solid var(--border-color);}
|
||||
@media(max-width:900px){.moderni-col.actuel{border-right:none;border-bottom:1px solid var(--border-color);}}
|
||||
.moderni-col-title{font-size:0.75em;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:10px;display:flex;align-items:center;gap:6px;}
|
||||
.moderni-col-title{font-size:0.72em;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:10px;display:flex;align-items:center;gap:6px;}
|
||||
.moderni-col-title.actuel{color:var(--success);}
|
||||
.moderni-col-title.suivant{color:#8b5cf6;}
|
||||
.moderni-card{background:var(--table-header);border-radius:8px;padding:10px 12px;margin-bottom:8px;font-size:0.83em;}
|
||||
|
|
@ -318,17 +383,18 @@
|
|||
@media(max-width:900px){.moderni-arrow{display:none;}}
|
||||
|
||||
/* ── 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-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;}
|
||||
.footer{text-align:center;padding:22px;color:var(--text-muted);font-size:0.82em;border-top:1px solid var(--border-color);margin-top:28px;}
|
||||
.footer-avatar{width:40px;height:40px;border-radius:50%;border:2px solid var(--accent);margin-bottom:7px;display:inline-flex;align-items:center;justify-content:center;background:var(--primary);color:white;font-size:1em;font-weight:700;}
|
||||
|
||||
/* ── RESPONSIVE ── */
|
||||
@media(max-width:768px){
|
||||
.header{flex-direction:column;padding:11px;}
|
||||
.header-controls{width:100%;justify-content:center;}
|
||||
.slide-nav{padding:9px;}
|
||||
.slide-nav button{padding:7px 12px;font-size:0.77em;}
|
||||
.header{padding:0 12px;height:56px;}
|
||||
.header-info{display:none;}
|
||||
.logo-section h1{font-size:0.95em;}
|
||||
.slides-container{padding:12px;}
|
||||
.kpi-grid{grid-template-columns:1fr 1fr;}
|
||||
.kpi-card .value{font-size:1.6em;}
|
||||
.sidebar{position:fixed;z-index:80;height:calc(100vh - 56px);}
|
||||
.main-wrapper{margin-left:0 !important;}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
@ -383,6 +449,8 @@
|
|||
|
||||
<!-- HEADER -->
|
||||
<header class="header">
|
||||
<div class="header-left">
|
||||
<button class="sidebar-toggle" id="sidebarToggle" onclick="toggleSidebar()" title="Menu">⊞</button>
|
||||
<div class="logo-section">
|
||||
<img src="logo-RLA.svg" alt="RLA Zone Sud" onerror="this.style.display='none'">
|
||||
<div>
|
||||
|
|
@ -390,14 +458,15 @@
|
|||
<div class="sub">Zone Sud — Tableau de Bord</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
<div class="theme-selector">
|
||||
<button class="theme-btn" data-theme="" onclick="setTheme('')" title="Sombre"><i class="fas fa-moon"></i></button>
|
||||
<button class="theme-btn" data-theme="light" onclick="setTheme('light')" title="Clair"><i class="fas fa-sun"></i></button>
|
||||
<button class="theme-btn" data-theme="professional" onclick="setTheme('professional')" title="Professionnel"><i class="fas fa-briefcase"></i></button>
|
||||
<button class="theme-btn" data-theme="professional" onclick="setTheme('professional')" title="Pro"><i class="fas fa-briefcase"></i></button>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<i class="fas fa-user-circle" style="font-size:1.4em;"></i>
|
||||
<i class="fas fa-user-circle" style="font-size:1.3em;"></i>
|
||||
<span id="currentUser">—</span>
|
||||
<span class="user-badge" id="userRole">—</span>
|
||||
<button class="header-btn btn-admin" id="adminBtn" onclick="showSlide(7)" style="display:none"><i class="fas fa-users-cog"></i> Utilisateurs</button>
|
||||
|
|
@ -405,32 +474,49 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="header-info">
|
||||
<div>Dernière mise à jour</div>
|
||||
<div>Dernière MAJ</div>
|
||||
<div class="date" id="lastUpdate">—</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- SLIDE NAVIGATION -->
|
||||
<nav class="slide-nav">
|
||||
<button class="active" id="btn-slide-0" onclick="showSlide(0)"><i class="fas fa-chart-pie"></i> Vue Générale</button>
|
||||
<button id="btn-slide-1" onclick="showSlide(1)"><i class="fas fa-exclamation-triangle"></i> Alertes <span id="badge-alertes" style="background:var(--danger);color:white;border-radius:10px;padding:1px 7px;font-size:0.8em;display:none">0</span></button>
|
||||
<button id="btn-slide-2" onclick="showSlide(2)"><i class="fas fa-check-circle"></i> En Service</button>
|
||||
<button id="btn-slide-3" onclick="showSlide(3)"><i class="fas fa-rocket"></i> Pilotage Proactif</button>
|
||||
<button id="btn-slide-4" onclick="showSlide(4)"><i class="fas fa-map-marker-alt"></i> Par Région</button>
|
||||
<button id="btn-slide-5" onclick="showSlide(5)"><i class="fas fa-list-alt"></i> Marchés</button>
|
||||
<button id="btn-slide-6" class="nav-hidden" onclick="showSlide(6)"><i class="fas fa-stream"></i> Pipeline AO</button>
|
||||
<button id="btn-slide-9" class="nav-hidden" onclick="showSlide(9)"><i class="fas fa-link"></i> Modernisation</button>
|
||||
<button id="btn-slide-7" class="nav-hidden" onclick="showSlide(7)"><i class="fas fa-users-cog"></i> Utilisateurs</button>
|
||||
<button id="btn-slide-8" class="nav-hidden" onclick="showSlide(8)"><i class="fas fa-history"></i> Logs</button>
|
||||
<span class="nav-separator"></span>
|
||||
<button class="export-btn" onclick="downloadPDF()" title="Export PDF"><i class="fas fa-file-pdf"></i> PDF</button>
|
||||
<button class="export-pptx-btn nav-hidden" id="btnExportPPTX" onclick="exportPPTX()" title="Export PPTX"><i class="fas fa-file-powerpoint"></i> PPTX</button>
|
||||
<button class="export-xlsx-btn nav-hidden" id="btnExportXLSX" onclick="exportXLSX()" title="Export XLSX"><i class="fas fa-file-excel"></i> XLSX</button>
|
||||
<button class="export-docx-btn nav-hidden" id="btnExportDOCX" onclick="exportDOCX()" title="Export DOCX"><i class="fas fa-file-word"></i> DOCX</button>
|
||||
<span class="nav-separator"></span>
|
||||
<button class="refresh-btn" onclick="loadData()" title="Actualiser"><i class="fas fa-sync-alt"></i></button>
|
||||
</nav>
|
||||
<!-- ALERT TICKER -->
|
||||
<div class="alert-ticker" id="alertTicker">
|
||||
<div class="ticker-track">
|
||||
<div class="ticker-content" id="tickerContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- APP LAYOUT: sidebar + main -->
|
||||
<div class="app-layout">
|
||||
<aside class="sidebar" id="appSidebar">
|
||||
<nav class="sidebar-nav">
|
||||
<div class="sidebar-section-label">Tableaux de bord</div>
|
||||
<button class="sidebar-btn active" id="btn-slide-0" onclick="showSlide(0)"><i class="fas fa-chart-pie"></i> Vue Générale</button>
|
||||
<button class="sidebar-btn" id="btn-slide-1" onclick="showSlide(1)"><i class="fas fa-exclamation-triangle"></i> Alertes <span id="badge-alertes" style="background:var(--danger);color:white;border-radius:10px;padding:1px 7px;font-size:0.78em;margin-left:auto;display:none">0</span></button>
|
||||
<button class="sidebar-btn" id="btn-slide-2" onclick="showSlide(2)"><i class="fas fa-check-circle"></i> En Service</button>
|
||||
<button class="sidebar-btn" id="btn-slide-3" onclick="showSlide(3)"><i class="fas fa-rocket"></i> Pilotage Proactif</button>
|
||||
<button class="sidebar-btn" id="btn-slide-4" onclick="showSlide(4)"><i class="fas fa-map-marker-alt"></i> Par Région</button>
|
||||
<button class="sidebar-btn" id="btn-slide-5" onclick="showSlide(5)"><i class="fas fa-list-alt"></i> Marchés</button>
|
||||
<div class="sidebar-section-label">Administration</div>
|
||||
<button class="sidebar-btn nav-hidden" id="btn-slide-6" onclick="showSlide(6)"><i class="fas fa-stream"></i> Pipeline AO</button>
|
||||
<button class="sidebar-btn nav-hidden" id="btn-slide-9" onclick="showSlide(9)"><i class="fas fa-link"></i> Modernisation</button>
|
||||
<div class="sidebar-section-label">Super Admin</div>
|
||||
<button class="sidebar-btn nav-hidden" id="btn-slide-7" onclick="showSlide(7)"><i class="fas fa-users-cog"></i> Utilisateurs</button>
|
||||
<button class="sidebar-btn nav-hidden" id="btn-slide-8" onclick="showSlide(8)"><i class="fas fa-history"></i> Logs</button>
|
||||
</nav>
|
||||
<div class="sidebar-export">
|
||||
<div class="sidebar-export-label">Export & Actions</div>
|
||||
<div class="sidebar-export-row">
|
||||
<button class="export-btn-sm pdf-btn" onclick="downloadPDF()" title="Export PDF"><i class="fas fa-file-pdf"></i> PDF</button>
|
||||
<button class="export-btn-sm pptx-btn nav-hidden" id="btnExportPPTX" onclick="exportPPTX()" title="Export PPTX"><i class="fas fa-file-powerpoint"></i> PPTX</button>
|
||||
<button class="export-btn-sm xlsx-btn nav-hidden" id="btnExportXLSX" onclick="exportXLSX()" title="Export XLSX"><i class="fas fa-file-excel"></i> XLSX</button>
|
||||
<button class="export-btn-sm docx-btn nav-hidden" id="btnExportDOCX" onclick="exportDOCX()" title="Export DOCX"><i class="fas fa-file-word"></i> DOCX</button>
|
||||
<button class="export-btn-sm refresh-btn-sm" onclick="loadData()" title="Actualiser"><i class="fas fa-sync-alt"></i> Sync</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="main-wrapper" id="mainWrapper">
|
||||
<!-- SLIDES CONTAINER -->
|
||||
<main class="slides-container">
|
||||
|
||||
|
|
@ -445,10 +531,22 @@
|
|||
|
||||
<!-- KPIs -->
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi-card"><div class="icon" style="color:var(--accent);"><i class="fas fa-folder-open"></i></div><div class="value" id="kpiTotal">—</div><div class="label">Total Marchés</div></div>
|
||||
<div class="kpi-card capex"><div class="icon" style="color:var(--success);"><i class="fas fa-play-circle"></i></div><div class="value" id="kpiActifs">—</div><div class="label">Marchés Actifs</div><div class="sub" id="kpiAvt">Avancement moy. : —</div></div>
|
||||
<div class="kpi-card alertes"><div class="icon" style="color:var(--danger);"><i class="fas fa-exclamation-triangle"></i></div><div class="value" id="kpiAlertes">—</div><div class="label">Alertes Délais</div><div class="sub" id="kpiCritiques">Critiques (≤45j) : —</div></div>
|
||||
<div class="kpi-card clotures"><div class="icon" style="color:#6b7280;"><i class="fas fa-archive"></i></div><div class="value" id="kpiClotures">—</div><div class="label">Clôturés</div></div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-icon blue"><i class="fas fa-folder-open"></i></div>
|
||||
<div class="kpi-body"><div class="value" id="kpiTotal">—</div><div class="label">Total Marchés</div></div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-icon green"><i class="fas fa-play-circle"></i></div>
|
||||
<div class="kpi-body"><div class="value" id="kpiActifs">—</div><div class="label">Marchés Actifs</div><div class="sub" id="kpiAvt">Avancement moy. : —</div></div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-icon red"><i class="fas fa-exclamation-triangle"></i></div>
|
||||
<div class="kpi-body"><div class="value" id="kpiAlertes">—</div><div class="label">Alertes Délais</div><div class="sub" id="kpiCritiques">Critiques (≤45j) : —</div></div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-icon gray"><i class="fas fa-archive"></i></div>
|
||||
<div class="kpi-body"><div class="value" id="kpiClotures">—</div><div class="label">Clôturés</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Blocs statut + Jauges région -->
|
||||
|
|
@ -598,16 +696,12 @@
|
|||
<!-- ── SLIDE 5 : MARCHÉS ── -->
|
||||
<section class="slide" id="slide-5">
|
||||
<h2 class="section-title"><i class="fas fa-list-alt" style="color:var(--accent)"></i> Liste des Marchés</h2>
|
||||
<div class="table-container">
|
||||
<div class="table-toolbar">
|
||||
<div class="table-toolbar-title">Marchés <span id="marchesCount" style="color:var(--text-muted);font-weight:400"></span></div>
|
||||
<div class="search-wrapper">
|
||||
<div class="search-bar-full">
|
||||
<i class="fas fa-search"></i>
|
||||
<input class="search-input" type="text" id="searchMarches" placeholder="Rechercher..." oninput="filterMarches()">
|
||||
<input class="search-input-full" type="text" id="searchMarches" placeholder="Rechercher par référence, entrepreneur, projet, région..." oninput="filterMarches()">
|
||||
</div>
|
||||
<select class="filter-select" id="filterRegion" onchange="filterMarches()">
|
||||
<option value="">Toutes régions</option>
|
||||
</select>
|
||||
<div class="filters-bar">
|
||||
<select class="filter-select" id="filterRegion" onchange="filterMarches()"><option value="">Toutes régions</option></select>
|
||||
<select class="filter-select" id="filterEntrepreneur" onchange="filterMarches()"><option value="">Tous entrepreneurs</option></select>
|
||||
<select class="filter-select" id="filterStatut" onchange="filterMarches()"><option value="">Tous statuts</option></select>
|
||||
<div class="filter-group">
|
||||
|
|
@ -620,6 +714,10 @@
|
|||
</div>
|
||||
<button class="btn-action btn-secondary" onclick="resetFilters()" title="Réinitialiser filtres"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<div class="table-toolbar">
|
||||
<div class="table-toolbar-title">Marchés <span id="marchesCount" style="color:var(--text-muted);font-weight:400"></span></div>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
|
|
@ -644,6 +742,10 @@
|
|||
<!-- ── SLIDE 6 : PIPELINE AO ── -->
|
||||
<section class="slide" id="slide-6">
|
||||
<h2 class="section-title"><i class="fas fa-stream" style="color:#6366F1"></i> Pipeline Appels d'Offres</h2>
|
||||
<div class="search-bar-full">
|
||||
<i class="fas fa-search"></i>
|
||||
<input class="search-input-full" type="text" id="searchPipeline" placeholder="Rechercher par N° AO, description, région..." oninput="filterPipeline()">
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<div class="table-header" style="background:linear-gradient(90deg,#4F46E5,#6366F1);">
|
||||
<h3><i class="fas fa-rocket"></i> AO en cours de lancement</h3>
|
||||
|
|
@ -721,7 +823,9 @@
|
|||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div><!-- /main-wrapper -->
|
||||
</div><!-- /app-layout -->
|
||||
</div><!-- /app-content -->
|
||||
|
||||
<script>
|
||||
/* ── CONFIG HELPERS ── */
|
||||
|
|
@ -931,7 +1035,8 @@ async function loadData() {
|
|||
|
||||
function renderAll() {
|
||||
renderKPIs(); renderSynthese(); renderService(); renderProactif();
|
||||
renderRegions(); renderMarches(); renderPipeline(); renderModernisation(); updateBadges();
|
||||
renderRegions(); renderMarches(); renderPipeline(); renderModernisation();
|
||||
updateBadges(); updateTicker();
|
||||
}
|
||||
|
||||
/* ── KPIs ── */
|
||||
|
|
@ -1281,43 +1386,12 @@ function changePageSize(n) { pageSize = parseInt(n,10); currentPage = 1; renderM
|
|||
/* ── PIPELINE ── */
|
||||
function renderPipeline() {
|
||||
const total = pipelineData._total_estimation ?? 0;
|
||||
document.getElementById('pipeline-count').textContent = `${pipelineData.length} projets`;
|
||||
document.getElementById('pipelineTotalEstimation').textContent =
|
||||
total > 0 ? parseNum(total).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g,' ') + ' DT' : '—';
|
||||
|
||||
document.getElementById('pipelineBody').innerHTML = pipelineData.length
|
||||
? pipelineData.map(r => {
|
||||
const phase = r._phase || {};
|
||||
const jours = r._jours_limite;
|
||||
const regs = (r._regions || []);
|
||||
const est = r._estimation || parseFloat(r.Estimation || 0) || 0;
|
||||
const numAO = r['num-ao'] || '—';
|
||||
const desc = r['Description du projet'] || r.description || '—';
|
||||
const dur = r['Duree'] || r.duree || '—';
|
||||
const dateLim = r['date-limite'] ? formatDateFR(r['date-limite']) : '—';
|
||||
|
||||
const joursHtml = jours === null ? '—'
|
||||
: jours < 0 ? `<span class="status-badge muted">Passé</span>`
|
||||
: jours <= 7 ? `<strong style="color:var(--danger)">${jours}j</strong>`
|
||||
: jours <= 30? `<strong style="color:var(--warning)">${jours}j</strong>`
|
||||
: `<span style="color:var(--success)">${jours}j</span>`;
|
||||
|
||||
const regHtml = regs.map(rg =>
|
||||
`<span class="region-tag" style="background:${rg.color}">${escapeHtml(rg.name)}</span>`
|
||||
).join('');
|
||||
|
||||
return `<tr>
|
||||
<td><strong>${escapeHtml(numAO)}</strong></td>
|
||||
<td>${escapeHtml(desc)}</td>
|
||||
<td><span class="phase-badge" style="background:${phase.color||'#888'}">${escapeHtml(phase.label||'—')}</span></td>
|
||||
<td>${regHtml || '—'}</td>
|
||||
<td>${est > 0 ? est.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g,' ') + ' DT' : '—'}</td>
|
||||
<td style="font-size:0.82em">${escapeHtml(dur)}</td>
|
||||
<td style="white-space:nowrap">${dateLim}</td>
|
||||
<td>${joursHtml}</td>
|
||||
</tr>`;
|
||||
}).join('')
|
||||
: '<tr><td colspan="8" style="text-align:center;color:var(--text-muted);padding:28px;">Pipeline vide.</td></tr>';
|
||||
_pipelineFiltered = [...pipelineData];
|
||||
const sq = document.getElementById('searchPipeline');
|
||||
if (sq) sq.value = '';
|
||||
renderPipelineTable(_pipelineFiltered);
|
||||
}
|
||||
|
||||
/* ── MODERNISATION ── */
|
||||
|
|
@ -1559,6 +1633,89 @@ function exportPPTX() { triggerExport('pptx'); }
|
|||
function exportXLSX() { triggerExport('xlsx'); }
|
||||
function exportDOCX() { triggerExport('docx'); }
|
||||
|
||||
/* ── SIDEBAR ── */
|
||||
let _sidebarVisible = localStorage.getItem('rla_sidebar_state') !== 'hidden';
|
||||
function toggleSidebar() {
|
||||
_sidebarVisible = !_sidebarVisible;
|
||||
localStorage.setItem('rla_sidebar_state', _sidebarVisible ? 'visible' : 'hidden');
|
||||
applySidebarState();
|
||||
}
|
||||
function applySidebarState() {
|
||||
const sb = document.getElementById('appSidebar');
|
||||
if (sb) sb.classList.toggle('hidden', !_sidebarVisible);
|
||||
}
|
||||
|
||||
/* ── ALERT TICKER ── */
|
||||
function updateTicker() {
|
||||
const critiques = allData
|
||||
.filter(r => !isCloture(r))
|
||||
.map(r => ({ r, delai: getDelaiRestant(r) }))
|
||||
.filter(x => x.delai !== null && x.delai <= 45)
|
||||
.sort((a, b) => a.delai - b.delai);
|
||||
const ticker = document.getElementById('alertTicker');
|
||||
const content = document.getElementById('tickerContent');
|
||||
if (!ticker || !content) return;
|
||||
if (!critiques.length) {
|
||||
ticker.classList.remove('active');
|
||||
document.body.classList.remove('has-ticker');
|
||||
return;
|
||||
}
|
||||
const text = critiques.map(x =>
|
||||
`⚠ ${x.r.region} — ${x.r.id_marche} : ${x.delai} jours restants`
|
||||
).join(' • ');
|
||||
content.textContent = text;
|
||||
const duration = Math.min(45, Math.max(15, Math.ceil(text.length / 80)));
|
||||
content.style.animationDuration = `${duration}s`;
|
||||
ticker.classList.add('active');
|
||||
document.body.classList.add('has-ticker');
|
||||
}
|
||||
|
||||
/* ── PIPELINE SEARCH ── */
|
||||
let _pipelineFiltered = [];
|
||||
function filterPipeline() {
|
||||
const q = (document.getElementById('searchPipeline')?.value || '').toLowerCase();
|
||||
_pipelineFiltered = q
|
||||
? pipelineData.filter(r => {
|
||||
const text = `${r['num-ao']||''} ${r['Description du projet']||''} ${(r._regions||[]).map(rg=>rg.name).join(' ')}`.toLowerCase();
|
||||
return text.includes(q);
|
||||
})
|
||||
: [...pipelineData];
|
||||
renderPipelineTable(_pipelineFiltered);
|
||||
}
|
||||
function renderPipelineTable(data) {
|
||||
document.getElementById('pipeline-count').textContent = `${data.length} projets`;
|
||||
document.getElementById('pipelineBody').innerHTML = data.length
|
||||
? data.map(r => {
|
||||
const phase = r._phase || {};
|
||||
const jours = r._jours_limite;
|
||||
const regs = (r._regions || []);
|
||||
const est = r._estimation || parseFloat(r.Estimation || 0) || 0;
|
||||
const numAO = r['num-ao'] || '—';
|
||||
const desc = r['Description du projet'] || r.description || '—';
|
||||
const dur = r['Duree'] || r.duree || '—';
|
||||
const dateLim = r['date-limite'] ? formatDateFR(r['date-limite']) : '—';
|
||||
const joursHtml = jours === null ? '—'
|
||||
: jours < 0 ? `<span class="status-badge muted">Passé</span>`
|
||||
: jours <= 7 ? `<strong style="color:var(--danger)">${jours}j</strong>`
|
||||
: jours <= 30? `<strong style="color:var(--warning)">${jours}j</strong>`
|
||||
: `<span style="color:var(--success)">${jours}j</span>`;
|
||||
const regHtml = regs.map(rg =>
|
||||
`<span class="region-tag" style="background:${rg.color}">${escapeHtml(rg.name)}</span>`
|
||||
).join('');
|
||||
return `<tr>
|
||||
<td><strong>${escapeHtml(numAO)}</strong></td>
|
||||
<td>${escapeHtml(desc)}</td>
|
||||
<td><span class="phase-badge" style="background:${phase.color||'#888'}">${escapeHtml(phase.label||'—')}</span></td>
|
||||
<td>${regHtml || '—'}</td>
|
||||
<td>${est > 0 ? est.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g,' ') + ' DT' : '—'}</td>
|
||||
<td style="font-size:0.82em">${escapeHtml(dur)}</td>
|
||||
<td style="white-space:nowrap">${dateLim}</td>
|
||||
<td>${joursHtml}</td>
|
||||
</tr>`;
|
||||
}).join('')
|
||||
: '<tr><td colspan="8" style="text-align:center;color:var(--text-muted);padding:28px;">Aucun résultat.</td></tr>';
|
||||
}
|
||||
|
||||
/* ── AUTO-REFRESH ── */
|
||||
setInterval(() => { if (jwtToken) loadData(); }, (CFG.REFRESH_INTERVAL || 60) * 60 * 1000);
|
||||
|
||||
|
|
@ -1575,6 +1732,7 @@ document.getElementById('editUserModal').addEventListener('click', e => {
|
|||
|
||||
/* ── INIT ── */
|
||||
loadTheme();
|
||||
applySidebarState();
|
||||
checkSession();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Reference in New Issue