{"id":16028,"date":"2026-05-04T16:31:42","date_gmt":"2026-05-04T15:31:42","guid":{"rendered":"https:\/\/elearning.nasla.cm\/?page_id=16028"},"modified":"2026-05-06T08:18:58","modified_gmt":"2026-05-06T07:18:58","slug":"tableau-de-bord-df","status":"publish","type":"page","link":"https:\/\/elearning.nasla.cm\/index.php\/tableau-de-bord-df\/","title":{"rendered":"Tableau de Bord DF"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"16028\" class=\"elementor elementor-16028\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-93e0254 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"93e0254\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-be9ab90\" data-id=\"be9ab90\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-56b08da elementor-widget elementor-widget-html\" data-id=\"56b08da\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Tableau de bord Chef de cellule - NASLA<\/title>\n<style>\n:root {\n  --dark:#111827; --mid:#374151; --muted:#6B7280;\n  --border:#E5E7EB; --bg:#F3F4F6; --white:#FFFFFF;\n  --blue:#1D4ED8; --blue-l:#DBEAFE;\n  --green:#065F46; --green-l:#D1FAE5;\n  --amber:#92400E; --amber-l:#FEF3C7;\n  --red:#991B1B; --red-l:#FEE2E2;\n  --radius:10px; --shadow:0 1px 3px rgba(0,0,0,.08);\n}\n*{box-sizing:border-box;margin:0;padding:0;}\nbody{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(--dark);}\n.shell{display:grid;grid-template-columns:220px 1fr;min-height:100vh;}\n\n\/* SIDEBAR *\/\n.sidebar{background:var(--dark);color:#fff;position:sticky;top:0;height:100vh;overflow-y:auto;}\n.sidebar-logo{padding:20px 18px 16px;border-bottom:1px solid rgba(255,255,255,.1);}\n.sidebar-logo h1{font-size:14px;font-weight:700;}\n.sidebar-logo p{font-size:10px;opacity:.5;margin-top:2px;}\n.sidebar-nav{padding:12px 0;}\n.nav-label{padding:6px 18px 4px;font-size:9px;text-transform:uppercase;letter-spacing:.1em;opacity:.4;}\n.nav-item{display:flex;align-items:center;gap:8px;padding:9px 18px;font-size:12px;cursor:pointer;opacity:.7;transition:background .1s;}\n.nav-item:hover,.nav-item.active{background:rgba(255,255,255,.1);opacity:1;}\n.nav-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;}\n.nav-count{margin-left:auto;background:rgba(255,255,255,.15);font-size:10px;padding:1px 6px;border-radius:10px;}\n\n\/* MAIN *\/\n.main{padding:24px;overflow-y:auto;}\n.view{display:none;}\n.view.active{display:block;}\n\n\/* HEADER *\/\n.page-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:20px;flex-wrap:wrap;gap:10px;}\n.page-header-text h2{font-size:18px;font-weight:700;}\n.page-header-text p{font-size:12px;color:var(--muted);margin-top:2px;}\n.header-actions{display:flex;gap:8px;flex-wrap:wrap;}\n.btn{padding:8px 14px;border-radius:8px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer;border:none;transition:all .15s;}\n.btn-dark{background:var(--dark);color:#fff;}\n.btn-dark:hover{background:#1F2937;}\n.btn-outline{background:var(--white);color:var(--dark);border:1px solid var(--border);}\n.btn-outline:hover{background:var(--bg);}\n.btn-green{background:#059669;color:#fff;}\n.btn-green:hover{opacity:.9;}\nselect.btn-outline{-webkit-appearance:none;padding:8px 10px;}\n\n\/* KPI GRID *\/\n.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px;}\n.kpi-card{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:16px;box-shadow:var(--shadow);}\n.kpi-label{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-bottom:6px;}\n.kpi-value{font-size:30px;font-weight:700;line-height:1;}\n.kpi-sub{font-size:11px;color:var(--muted);margin-top:4px;}\n.kpi-bar{height:4px;background:var(--border);border-radius:2px;margin-top:10px;overflow:hidden;}\n.kpi-bar-fill{height:100%;border-radius:2px;transition:width .5s;}\n\n\/* SECTION *\/\n.section-title{font-size:13px;font-weight:700;margin-bottom:12px;display:flex;align-items:center;gap:8px;}\n.pill{font-size:10px;font-weight:600;padding:2px 8px;border-radius:10px;}\n\n\/* ALERT *\/\n.alert{padding:10px 14px;border-radius:8px;font-size:12px;margin-bottom:12px;border:1px solid;}\n.alert-info{background:var(--blue-l);border-color:#93C5FD;color:var(--blue);}\n.alert-warn{background:var(--amber-l);border-color:#FDE68A;color:var(--amber);}\n\n\/* JOURNAL CARDS *\/\n.journal-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;}\n.journal-card{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:14px;box-shadow:var(--shadow);cursor:pointer;transition:all .15s;}\n.journal-card:hover{border-color:#93C5FD;box-shadow:0 0 0 3px rgba(59,130,246,.1);}\n.journal-card.selected{border-color:#2563EB;box-shadow:0 0 0 3px rgba(37,99,235,.12);}\n.jc-top{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:6px;}\n.jc-agent{font-size:13px;font-weight:600;}\n.jc-date{font-size:11px;color:var(--muted);}\n.jc-cellule{display:inline-block;font-size:10px;font-weight:700;padding:2px 7px;border-radius:4px;margin-bottom:6px;}\n.jc-codes{display:flex;flex-wrap:wrap;gap:3px;margin-bottom:4px;}\n.jc-code{font-size:10px;padding:1px 6px;background:#EFF6FF;color:#1E40AF;border:1px solid #BFDBFE;border-radius:4px;}\n.jc-flag{font-size:10px;font-style:italic;margin-top:3px;}\n.badge{display:inline-block;font-size:10px;font-weight:600;padding:2px 8px;border-radius:4px;}\n.badge-green{background:var(--green-l);color:var(--green);}\n.badge-amber{background:var(--amber-l);color:var(--amber);}\n.badge-red{background:var(--red-l);color:var(--red);}\n.badge-blue{background:var(--blue-l);color:var(--blue);}\n.badge-gray{background:#F3F4F6;color:var(--muted);}\n\n\/* DETAIL PANEL *\/\n.detail-panel{background:var(--white);border:1px solid #BFDBFE;border-radius:var(--radius);padding:18px;box-shadow:var(--shadow);margin-bottom:20px;display:none;}\n.detail-panel.open{display:block;animation:slideIn .2s ease;}\n@keyframes slideIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}\n.dp-hdr{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px;}\n.dp-title{font-size:15px;font-weight:700;}\n.dp-meta{font-size:11px;color:var(--muted);margin-top:2px;}\n.dp-close{background:none;border:none;font-size:22px;color:var(--muted);cursor:pointer;line-height:1;}\n.dp-close:hover{color:var(--dark);}\n.dp-sec-title{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin:12px 0 6px;}\n.dp-task{padding:8px 10px;background:#F8FAFF;border:1px solid #DBEAFE;border-radius:8px;margin-bottom:6px;}\n.dp-task-code{font-size:9px;font-weight:700;font-family:monospace;color:var(--blue);margin-bottom:2px;}\n.dp-task-name{font-size:12px;font-weight:600;margin-bottom:4px;}\n.dp-subops{display:flex;flex-wrap:wrap;gap:4px;}\n.dp-subop{font-size:10px;padding:2px 7px;background:#EFF6FF;border:1px solid #BFDBFE;color:#1E40AF;border-radius:4px;}\n.dp-note{font-size:11px;color:var(--muted);font-style:italic;margin-top:4px;}\n.dp-hors-item{padding:6px 10px;background:var(--amber-l);border:1px solid #FDE68A;border-radius:6px;font-size:12px;color:var(--amber);margin-bottom:4px;}\n.dp-bilan{padding:10px 12px;border-radius:8px;font-size:12px;margin-bottom:6px;}\n.dp-bilan.green{background:var(--green-l);color:var(--green);}\n.dp-bilan.amber{background:var(--amber-l);color:var(--amber);}\n.dp-bilan.red{background:var(--red-l);color:var(--red);}\n.dp-field{font-size:12px;margin-bottom:5px;}\n.dp-field strong{color:var(--mid);}\n\n\/* TABLE *\/\n.data-table{width:100%;border-collapse:collapse;background:var(--white);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);margin-bottom:20px;}\n.data-table th{padding:10px 14px;text-align:left;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);background:#F9FAFB;border-bottom:1px solid var(--border);}\n.data-table td{padding:9px 14px;font-size:12px;border-bottom:1px solid #F3F4F6;vertical-align:top;}\n.data-table tr:last-child td{border-bottom:none;}\n.data-table tr:hover td{background:#FAFBFC;}\n\n\/* CHARTS *\/\n.chart-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;}\n.chart-card{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:16px;box-shadow:var(--shadow);}\n.chart-title{font-size:12px;font-weight:600;margin-bottom:12px;color:var(--dark);}\n.bar-row{display:flex;align-items:center;gap:8px;margin-bottom:7px;}\n.bar-label{font-size:11px;color:var(--mid);width:130px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}\n.bar-track{flex:1;height:8px;background:var(--border);border-radius:4px;overflow:hidden;}\n.bar-fill{height:100%;border-radius:4px;transition:width .5s ease;}\n.bar-count{font-size:10px;color:var(--muted);width:26px;text-align:right;flex-shrink:0;}\n\n\/* IMPORT ZONE *\/\n.import-zone{border:2px dashed var(--border);border-radius:var(--radius);padding:32px;text-align:center;background:var(--white);cursor:pointer;transition:all .15s;margin-bottom:20px;}\n.import-zone:hover,.import-zone.drag{border-color:#3B82F6;background:#EFF6FF;}\n.import-zone h3{font-size:14px;font-weight:600;margin-bottom:6px;}\n.import-zone p{font-size:12px;color:var(--muted);}\n\n\/* RAPPORT *\/\n.rapport-block{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:18px;margin-bottom:12px;}\n.rapport-preview{font-family:'Courier New',monospace;font-size:11px;line-height:1.7;white-space:pre-wrap;color:var(--mid);max-height:420px;overflow-y:auto;background:#F9FAFB;padding:14px;border-radius:8px;border:1px solid var(--border);}\n\n\/* EMPTY *\/\n.empty{text-align:center;padding:40px 24px;color:var(--muted);}\n.empty h3{font-size:14px;font-weight:600;margin-bottom:6px;color:var(--mid);}\n\n@media(max-width:900px){.shell{grid-template-columns:1fr}.sidebar{display:none}.kpi-grid{grid-template-columns:1fr 1fr}.journal-grid,.chart-grid{grid-template-columns:1fr}}\n@media(max-width:480px){.kpi-grid{grid-template-columns:1fr}}\n<\/style>\n<\/head>\n<body>\n<div class=\"shell\">\n\n<!-- SIDEBAR -->\n<div class=\"sidebar\">\n  <div class=\"sidebar-logo\">\n    <h1>NASLA - DF<\/h1>\n    <p>Tableau de bord chef de cellule 2026<\/p>\n    <div id=\"statut-sauvegarde\" style=\"font-size:10px;margin-top:6px;opacity:.7;color:#10B981\"><\/div>\n  <\/div>\n  <div class=\"sidebar-nav\">\n    <div class=\"nav-label\">Navigation<\/div>\n    <div class=\"nav-item active\" id=\"nav-jour\" onclick=\"showView('jour',this)\">\n      <span class=\"nav-dot\" style=\"background:#3B82F6\"><\/span>Vue journaliere\n    <\/div>\n    <div class=\"nav-item\" id=\"nav-agents\" onclick=\"showView('agents',this)\">\n      <span class=\"nav-dot\" style=\"background:#10B981\"><\/span>Par agent\n    <\/div>\n    <div class=\"nav-item\" id=\"nav-pta\" onclick=\"showView('pta',this)\">\n      <span class=\"nav-dot\" style=\"background:#8B5CF6\"><\/span>Alignement PTA\n    <\/div>\n    <div class=\"nav-item\" id=\"nav-rapport\" onclick=\"showView('rapport',this)\">\n      <span class=\"nav-dot\" style=\"background:#F59E0B\"><\/span>Rapport d'activites\n    <\/div>\n    <div class=\"nav-label\" style=\"margin-top:8px\">Donnees<\/div>\n    <div class=\"nav-item\" id=\"nav-import\" onclick=\"showView('import',this)\">\n      <span class=\"nav-dot\" style=\"background:#6B7280\"><\/span>Importer journaux\n      <span class=\"nav-count\" id=\"nav-count\">0<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- MAIN -->\n<div class=\"main\">\n\n  <!-- VUE JOURNALIERE -->\n  <div class=\"view active\" id=\"view-jour\">\n    <div class=\"page-header\">\n      <div class=\"page-header-text\">\n        <h2>Vue journaliere<\/h2>\n        <p id=\"date-display\"><\/p>\n      <\/div>\n      <div class=\"header-actions\">\n        <select class=\"btn btn-outline\" id=\"filter-date\" onchange=\"applyFilters()\">\n          <option value=\"\">Toutes les dates<\/option>\n        <\/select>\n        <select class=\"btn btn-outline\" id=\"filter-cellule\" onchange=\"applyFilters()\">\n          <option value=\"\">Toutes les cellules<\/option>\n          <option value=\"FI\">Formation Initiale<\/option>\n          <option value=\"FC\">Formation Continue<\/option>\n          <option value=\"GQ\">Gouvernance &amp; Qualite<\/option>\n          <option value=\"NUM\">E-Learning \/ Numerique<\/option>\n        <\/select>\n      <\/div>\n    <\/div>\n\n    <div class=\"kpi-grid\">\n      <div class=\"kpi-card\">\n        <div class=\"kpi-label\">Journaux recus<\/div>\n        <div class=\"kpi-value\" id=\"kpi-total\">0<\/div>\n        <div class=\"kpi-sub\">agents ayant soumis<\/div>\n      <\/div>\n      <div class=\"kpi-card\">\n        <div class=\"kpi-label\">Taches PTA<\/div>\n        <div class=\"kpi-value\" id=\"kpi-pta\" style=\"color:#1D4ED8\">0<\/div>\n        <div class=\"kpi-sub\">declarations inscrites au PTA<\/div>\n        <div class=\"kpi-bar\"><div class=\"kpi-bar-fill\" id=\"kpi-pta-bar\" style=\"background:#3B82F6;width:0%\"><\/div><\/div>\n      <\/div>\n      <div class=\"kpi-card\">\n        <div class=\"kpi-label\">Activites hors PTA<\/div>\n        <div class=\"kpi-value\" id=\"kpi-hors\" style=\"color:#B45309\">0<\/div>\n        <div class=\"kpi-sub\">activites non programmees<\/div>\n      <\/div>\n      <div class=\"kpi-card\">\n        <div class=\"kpi-label\">Alertes blocage<\/div>\n        <div class=\"kpi-value\" id=\"kpi-bloc\" style=\"color:#DC2626\">0<\/div>\n        <div class=\"kpi-sub\">journees avec difficultes<\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"section-title\">\n      Journaux du jour\n      <span class=\"pill badge-blue\" id=\"count-pill\">0 journal(aux)<\/span>\n    <\/div>\n    <div class=\"alert alert-info\" id=\"no-data-msg\">\n      Aucun journal charge. Rendez-vous dans \"Importer journaux\" pour charger les fichiers JSON de vos agents.\n    <\/div>\n    <div class=\"journal-grid\" id=\"journal-grid\"><\/div>\n\n    <div class=\"detail-panel\" id=\"detail-panel\">\n      <div class=\"dp-hdr\">\n        <div>\n          <div class=\"dp-title\" id=\"dp-title\"><\/div>\n          <div class=\"dp-meta\" id=\"dp-meta\"><\/div>\n        <\/div>\n        <button class=\"dp-close\" onclick=\"closeDetail()\">x<\/button>\n      <\/div>\n      <div id=\"dp-body\"><\/div>\n    <\/div>\n  <\/div>\n\n  <!-- VUE AGENTS -->\n  <div class=\"view\" id=\"view-agents\">\n    <div class=\"page-header\">\n      <div class=\"page-header-text\"><h2>Suivi par agent<\/h2><p>Activite cumulee sur la periode chargee<\/p><\/div>\n    <\/div>\n    <table class=\"data-table\">\n      <thead><tr>\n        <th>Agent<\/th><th>Cellule<\/th><th>Journaux<\/th>\n        <th>Taches PTA<\/th><th>Sous-operations<\/th><th>Hors PTA<\/th>\n        <th>Blocage<\/th><th>Dernier journal<\/th>\n      <\/tr><\/thead>\n      <tbody id=\"agents-tbody\"><\/tbody>\n    <\/table>\n    <div class=\"chart-grid\">\n      <div class=\"chart-card\">\n        <div class=\"chart-title\">Taches PTA par agent<\/div>\n        <div id=\"chart-agents\"><\/div>\n      <\/div>\n      <div class=\"chart-card\">\n        <div class=\"chart-title\">Repartition des journees<\/div>\n        <div id=\"chart-bilan\"><\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <!-- VUE PTA -->\n  <div class=\"view\" id=\"view-pta\">\n    <div class=\"page-header\">\n      <div class=\"page-header-text\"><h2>Alignement PTA<\/h2><p>Taches declarees vs hors PTA<\/p><\/div>\n    <\/div>\n    <div class=\"chart-grid\">\n      <div class=\"chart-card\">\n        <div class=\"chart-title\">Taches PTA les plus frequentes<\/div>\n        <div id=\"chart-top-tasks\"><\/div>\n      <\/div>\n      <div class=\"chart-card\">\n        <div class=\"chart-title\">Taux d'alignement PTA global<\/div>\n        <div id=\"chart-align\"><\/div>\n      <\/div>\n    <\/div>\n    <div class=\"section-title\">Detail par activite PTA declaree<\/div>\n    <table class=\"data-table\">\n      <thead><tr><th>Code<\/th><th>Tache<\/th><th>Declarations<\/th><th>Agents<\/th><th>Sous-operations frequentes<\/th><\/tr><\/thead>\n      <tbody id=\"pta-tbody\"><\/tbody>\n    <\/table>\n    <div class=\"section-title\">Activites hors PTA declarees<\/div>\n    <table class=\"data-table\">\n      <thead><tr><th>Description<\/th><th>Occurrences<\/th><th>Agents<\/th><\/tr><\/thead>\n      <tbody id=\"hors-tbody\"><\/tbody>\n    <\/table>\n  <\/div>\n\n  <!-- VUE RAPPORT -->\n  <div class=\"view\" id=\"view-rapport\">\n    <div class=\"page-header\">\n      <div class=\"page-header-text\"><h2>Rapport d'activites<\/h2><p>Generation automatique depuis les journaux<\/p><\/div>\n      <div class=\"header-actions\">\n        <select class=\"btn btn-outline\" id=\"rapport-periode\" onchange=\"genRapport()\" style=\"padding:8px 10px;font-size:12px;\">\n          <option value=\"all\">Toute la periode<\/option>\n        <\/select>\n        <button class=\"btn btn-green\" onclick=\"copyRapport()\">Copier le texte<\/button>\n      <\/div>\n    <\/div>\n    <div class=\"rapport-block\">\n      <div class=\"section-title\">Apercu<\/div>\n      <pre class=\"rapport-preview\" id=\"rapport-preview\">Aucune donnee. Importez des journaux pour generer un rapport.<\/pre>\n    <\/div>\n  <\/div>\n\n  <!-- VUE IMPORT -->\n  <div class=\"view\" id=\"view-import\">\n    <div class=\"page-header\">\n      <div class=\"page-header-text\"><h2>Importer les journaux<\/h2><p>Fichiers JSON envoyes par les agents<\/p><\/div>\n      <button class=\"btn btn-outline\" onclick=\"clearAll()\">Tout effacer<\/button>\n    <\/div>\n\n    <!-- BANDEAU PERSISTANCE -->\n    <div style=\"background:#fff;border:1px solid #E5E7EB;border-radius:10px;padding:16px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.06)\">\n      <div style=\"font-size:12px;font-weight:700;color:#111827;margin-bottom:12px;display:flex;align-items:center;gap:6px\">\n        <span style=\"width:8px;height:8px;background:#10B981;border-radius:50%;display:inline-block\"><\/span>\n        Persistance des donnees\n      <\/div>\n      <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:12px\">\n        <div style=\"background:#F0FDF4;border:1px solid #86EFAC;border-radius:8px;padding:12px\">\n          <div style=\"font-size:11px;font-weight:700;color:#166534;margin-bottom:4px\">Sauvegarde automatique<\/div>\n          <div style=\"font-size:11px;color:#15803D;line-height:1.4\">Chaque journal importe est automatiquement memorise dans ce navigateur. Les donnees survivent aux actualisations et fermetures de page.<\/div>\n        <\/div>\n        <div style=\"background:#EFF6FF;border:1px solid #93C5FD;border-radius:8px;padding:12px\">\n          <div style=\"font-size:11px;font-weight:700;color:#1D4ED8;margin-bottom:4px\">Fichier maitre hebdomadaire<\/div>\n          <div style=\"font-size:11px;color:#1E40AF;line-height:1.4\">Exportez un fichier maitre chaque semaine pour archiver et pouvoir restaurer sur un autre appareil.<\/div>\n        <\/div>\n      <\/div>\n      <div style=\"display:flex;gap:8px;flex-wrap:wrap\">\n        <button class=\"btn btn-green\" onclick=\"exporterFichierMaitre()\" style=\"font-size:12px\">\n          Exporter le fichier maitre (.json)\n        <\/button>\n        <label style=\"padding:8px 14px;border-radius:8px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer;background:#fff;color:#111827;border:1px solid #E5E7EB;transition:all .15s\" onmouseover=\"this.style.background='#F9FAFB'\" onmouseout=\"this.style.background='#fff'\">\n          Restaurer depuis un fichier maitre\n          <input type=\"file\" accept=\".json\" style=\"display:none\" onchange=\"importerFichierMaitre(this.files)\">\n        <\/label>\n      <\/div>\n    <\/div>\n\n    <!-- IMPORT JOURNAUX INDIVIDUELS -->\n    <div class=\"section-title\">Ajouter des journaux d'agents<\/div>\n    <div class=\"import-zone\" id=\"drop-zone\"\n         onclick=\"document.getElementById('file-in').click()\"\n         ondragover=\"event.preventDefault();this.classList.add('drag')\"\n         ondragleave=\"this.classList.remove('drag')\"\n         ondrop=\"handleDrop(event)\">\n      <input type=\"file\" id=\"file-in\" multiple accept=\".json\" style=\"display:none\" onchange=\"handleFiles(this.files)\">\n      <h3>Cliquer pour selectionner les fichiers JSON des agents<\/h3>\n      <p>ou glisser-deposer ici \u2014 plusieurs fichiers acceptes simultanement<\/p>\n    <\/div>\n    <div id=\"import-feedback\"><\/div>\n    <div class=\"section-title\">Journaux en memoire (<span id=\"loaded-count\">0<\/span>)<\/div>\n    <table class=\"data-table\">\n      <thead><tr><th>Date<\/th><th>Agent<\/th><th>Cellule<\/th><th>PTA<\/th><th>Hors PTA<\/th><th>Bilan<\/th><th><\/th><\/tr><\/thead>\n      <tbody id=\"loaded-tbody\"><\/tbody>\n    <\/table>\n  <\/div>\n\n<\/div>\n<\/div>\n\n<script>\n\/\/ \u2500\u2500 Couleurs cellules \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst CC = {\n  FI:  {bg:'#1A4A7A',badge:'#DBEAFE',bt:'#1D4ED8'},\n  FCS: {bg:'#7A3A00',badge:'#FED7AA',bt:'#C55A11'},\n  FC:  {bg:'#7A3A00',badge:'#FED7AA',bt:'#C55A11'},\n  GQ:  {bg:'#1A5C30',badge:'#BBF7D0',bt:'#166534'},\n  NUM: {bg:'#3A1A7A',badge:'#DDD6FE',bt:'#5B21B6'},\n  ADM: {bg:'#374151',badge:'#E5E7EB',bt:'#374151'},\n};\nfunction cc(code){return CC[code]||CC.ADM;}\n\n\/\/ \u2500\u2500 Cle localStorage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst LS_KEY = 'nasla_df_journaux_2026';\n\n\/\/ \u2500\u2500 Persistence localStorage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction sauvegarderLocal() {\n  try {\n    localStorage.setItem(LS_KEY, JSON.stringify(allJ));\n    afficherStatutSauvegarde('auto');\n  } catch(e) {\n    console.warn('localStorage plein ou indisponible:', e);\n    afficherStatutSauvegarde('erreur');\n  }\n}\n\nfunction restaurerLocal() {\n  try {\n    const raw = localStorage.getItem(LS_KEY);\n    if (!raw) return false;\n    const data = JSON.parse(raw);\n    if (Array.isArray(data) && data.length > 0) {\n      allJ = data;\n      return true;\n    }\n  } catch(e) { console.warn('Erreur restauration localStorage:', e); }\n  return false;\n}\n\nfunction afficherStatutSauvegarde(type) {\n  const el = document.getElementById('statut-sauvegarde');\n  if (!el) return;\n  const now = new Date().toLocaleTimeString('fr-FR', {hour:'2-digit',minute:'2-digit'});\n  if (type === 'auto') {\n    el.textContent = 'Sauvegarde automatique : ' + now;\n    el.style.color = '#10B981';\n  } else if (type === 'erreur') {\n    el.textContent = 'Attention : sauvegarde automatique impossible (memoire pleine)';\n    el.style.color = '#EF4444';\n  } else if (type === 'export') {\n    el.textContent = 'Fichier maitre exporte : ' + now;\n    el.style.color = '#3B82F6';\n  }\n}\n\n\/\/ \u2500\u2500 Etat \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet allJ = [];      \/\/ tous les journaux\nlet filtered = [];  \/\/ journaux filtres\nlet openIdx = -1;   \/\/ journal ouvert dans le detail\n\n\/\/ \u2500\u2500 Navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction showView(id, el) {\n  document.querySelectorAll('.view').forEach(v=>v.classList.remove('active'));\n  document.querySelectorAll('.nav-item').forEach(n=>n.classList.remove('active'));\n  document.getElementById('view-'+id).classList.add('active');\n  if(el) el.classList.add('active');\n  if(id==='agents') renderAgents();\n  if(id==='pta') renderPTA();\n  if(id==='rapport') genRapport();\n  if(id==='import') renderLoaded();\n}\n\n\/\/ \u2500\u2500 Import \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction handleDrop(e) {\n  e.preventDefault();\n  document.getElementById('drop-zone').classList.remove('drag');\n  handleFiles(e.dataTransfer.files);\n}\nfunction handleFiles(files) {\n  let done=0, added=0;\n  const total = files.length;\n  if(!total) return;\n  Array.from(files).forEach(file=>{\n    const reader = new FileReader();\n    reader.onload = ev => {\n      try {\n        const d = JSON.parse(ev.target.result);\n        if(d.meta && d.meta.date) {\n          const key = d.meta.date+'_'+d.meta.agent+'_'+d.meta.celluleCode;\n          if(!allJ.find(j=>j.meta.date+'_'+j.meta.agent+'_'+j.meta.celluleCode===key)){\n            allJ.push(d); added++;\n          }\n        }\n      } catch(e){}\n      done++;\n      if(done===total) {\n        refresh();\n        sauvegarderLocal();\n        const msg = added > 0\n          ? added+' nouveau(x) journal(aux) importe(s) et sauvegardes automatiquement. Total : '+allJ.length\n          : 'Aucun nouveau journal (doublons ignores). Total : '+allJ.length;\n        document.getElementById('import-feedback').innerHTML =\n          '<div class=\"alert alert-info\">'+msg+'<\/div>';\n      }\n    };\n    reader.readAsText(file);\n  });\n}\nfunction clearAll(){\n  if(!confirm('Supprimer tous les journaux charges ?\\n\\nAttention : cette action efface aussi la sauvegarde automatique du navigateur.')) return;\n  allJ=[];\n  try { localStorage.removeItem(LS_KEY); } catch(e){}\n  refresh();\n  document.getElementById('import-feedback').innerHTML =\n    '<div class=\"alert alert-warn\">Tous les journaux ont ete effaces (memoire locale et affichage).<\/div>';\n}\nfunction removeJ(i){\n  allJ.splice(i,1);\n  sauvegarderLocal();\n  refresh();\n}\n\n\/\/ \u2500\u2500 Refresh global \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction refresh() {\n  \/\/ Mise a jour selecteurs de dates\n  const selDate = document.getElementById('filter-date');\n  const curDate = selDate.value;\n  const dates = [...new Set(allJ.map(j=>j.meta.date))].sort().reverse();\n  selDate.innerHTML = '<option value=\"\">Toutes les dates<\/option>' +\n    dates.map(d=>`<option value=\"${d}\"${d===curDate?' selected':''}>${fmtDate(d)}<\/option>`).join('');\n  \/\/ Periode rapport\n  const rp = document.getElementById('rapport-periode');\n  const months = [...new Set(allJ.map(j=>j.meta.date.substring(0,7)))].sort().reverse();\n  rp.innerHTML = '<option value=\"all\">Toute la periode<\/option>' +\n    months.map(m=>`<option value=\"${m}\">${m}<\/option>`).join('');\n  document.getElementById('nav-count').textContent = allJ.length;\n  document.getElementById('loaded-count').textContent = allJ.length;\n  applyFilters();\n  renderLoaded();\n}\nfunction applyFilters(){\n  const date = document.getElementById('filter-date').value;\n  const cell = document.getElementById('filter-cellule').value;\n  filtered = allJ.filter(j=>{\n    if(date && j.meta.date!==date) return false;\n    if(cell && j.meta.celluleCode!==cell) return false;\n    return true;\n  });\n  renderDashboard();\n}\n\n\/\/ \u2500\u2500 Date helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction fmtDate(d){\n  if(!d) return '';\n  const p=d.split('-'); return p[2]+'\/'+p[1]+'\/'+p[0];\n}\n\n\/\/ \u2500\u2500 Dashboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction renderDashboard(){\n  const data = filtered.length ? filtered : allJ;\n  const hasDates = data.length;\n  document.getElementById('date-display').textContent = hasDates\n    ? data.length+' journal(aux) charge(s)'\n    : 'Aucun journal charge';\n  const totalPTA = data.reduce((s,j)=>s+(j.tachesPTA||[]).reduce((ss,t)=>ss+(t.sousOps||[]).length,0),0);\n  const totalHors = data.reduce((s,j)=>s+(j.horsePTA||[]).length,0);\n  const totalBloc = data.filter(j=>j.bilan&&j.bilan.blocages&&j.bilan.blocages.trim()).length;\n  document.getElementById('kpi-total').textContent = data.length;\n  document.getElementById('kpi-pta').textContent = totalPTA;\n  document.getElementById('kpi-hors').textContent = totalHors;\n  document.getElementById('kpi-bloc').textContent = totalBloc;\n  const pct = (totalPTA+totalHors)>0 ? Math.round(totalPTA\/(totalPTA+totalHors)*100) : 0;\n  document.getElementById('kpi-pta-bar').style.width = pct+'%';\n  document.getElementById('count-pill').textContent = data.length+' journal(aux)';\n  document.getElementById('no-data-msg').style.display = allJ.length ? 'none' : 'block';\n  renderCards(data);\n}\n\nfunction renderCards(data){\n  const grid = document.getElementById('journal-grid');\n  if(!data.length){grid.innerHTML='';return;}\n  grid.innerHTML = data.map((j,i)=>{\n    const col = cc(j.meta.celluleCode);\n    const codes = [...new Set((j.tachesPTA||[]).map(t=>t.codeAct))];\n    const bCls = bilanClass(j.bilan&&j.bilan.statut);\n    return `<div class=\"journal-card\" id=\"jc-${i}\" onclick=\"openDetail(${i})\">\n      <div class=\"jc-top\">\n        <div class=\"jc-agent\">${j.meta.agent||'\u2014'}<\/div>\n        <div class=\"jc-date\">${fmtDate(j.meta.date)}<\/div>\n      <\/div>\n      <div class=\"jc-cellule\" style=\"background:${col.badge};color:${col.bt}\">${j.meta.cellule||j.meta.celluleCode||'\u2014'}<\/div>\n      <div class=\"jc-codes\">\n        ${codes.length ? codes.map(c=>`<span class=\"jc-code\">${c}<\/span>`).join('') : '<span class=\"jc-code\" style=\"background:#F3F4F6;color:#6B7280;border-color:#E5E7EB\">Aucune tache PTA<\/span>'}\n      <\/div>\n      ${(j.horsePTA||[]).length ? `<div class=\"jc-flag\" style=\"color:#92400E\">${(j.horsePTA||[]).length} activite(s) hors PTA<\/div>`:''}\n      ${j.bilan&&j.bilan.blocages&&j.bilan.blocages.trim() ? '<div class=\"jc-flag\" style=\"color:#DC2626\">Blocage signale<\/div>':''}\n      <div style=\"margin-top:6px\"><span class=\"badge ${bCls}\">${(j.bilan&&j.bilan.statut)||'\u2014'}<\/span><\/div>\n    <\/div>`;\n  }).join('');\n}\nfunction bilanClass(s){\n  if(!s) return 'badge-gray';\n  if(s.includes('roductive')||s.includes('Productive')) return 'badge-green';\n  if(s.includes('artielle')||s.includes('artiel')) return 'badge-amber';\n  return 'badge-red';\n}\n\nfunction openDetail(i){\n  document.querySelectorAll('.journal-card').forEach(c=>c.classList.remove('selected'));\n  const card = document.getElementById('jc-'+i);\n  if(card) card.classList.add('selected');\n  const data = filtered.length ? filtered : allJ;\n  const j = data[i];\n  if(!j) return;\n  const col = cc(j.meta.celluleCode);\n  document.getElementById('dp-title').textContent = (j.meta.agent||'\u2014')+' \u2014 '+fmtDate(j.meta.date);\n  document.getElementById('dp-meta').innerHTML =\n    `<span style=\"background:${col.badge};color:${col.bt};padding:2px 7px;border-radius:4px;font-size:10px;font-weight:700\">${j.meta.cellule||j.meta.celluleCode||'\u2014'}<\/span>`+\n    (j.meta.grade ? ' \u00b7 '+j.meta.grade : '');\n  let html='';\n  \/\/ Taches PTA\n  const pts = j.tachesPTA||[];\n  if(pts.length){\n    html += `<div class=\"dp-sec-title\">Taches PTA realises (${pts.length})<\/div>`;\n    pts.forEach(t=>{\n      html += `<div class=\"dp-task\">\n        <div class=\"dp-task-code\">${t.codeAct} \u00b7 ${t.codeTache}<\/div>\n        <div class=\"dp-task-name\">${t.libelleTache}<\/div>\n        ${(t.sousOps&&t.sousOps.length) ? '<div class=\"dp-subops\">'+t.sousOps.map(s=>`<span class=\"dp-subop\">${s.lib}<\/span>`).join('')+'<\/div>' : ''}\n        ${t.note ? `<div class=\"dp-note\">${t.note}<\/div>` : ''}\n      <\/div>`;\n    });\n  }\n  \/\/ Hors PTA\n  const hp = j.horsePTA||[];\n  if(hp.length){\n    html += `<div class=\"dp-sec-title\">Activites hors PTA (${hp.length})<\/div>`;\n    hp.forEach(h=>{html+=`<div class=\"dp-hors-item\">${h.lib}<\/div>`;});\n  }\n  \/\/ Bilan\n  const b = j.bilan||{};\n  const bCls = b.statut&&b.statut.includes('roductive') ? 'green' : b.statut&&b.statut.includes('artiel') ? 'amber' : 'red';\n  html += `<div class=\"dp-sec-title\">Bilan de la journee<\/div>\n    <div class=\"dp-bilan ${bCls}\">${b.statut||'\u2014'}<\/div>\n    ${b.blocages&&b.blocages.trim() ? `<div class=\"dp-field\"><strong>Blocages :<\/strong> ${b.blocages}<\/div>`:''}\n    ${b.previsions&&b.previsions.trim() ? `<div class=\"dp-field\"><strong>Previsions demain :<\/strong> ${b.previsions}<\/div>`:''}\n    ${b.observations&&b.observations.trim() ? `<div class=\"dp-field\"><strong>A remonter :<\/strong> ${b.observations}<\/div>`:''}`;\n  document.getElementById('dp-body').innerHTML = html;\n  const panel = document.getElementById('detail-panel');\n  panel.classList.add('open');\n  panel.scrollIntoView({behavior:'smooth',block:'nearest'});\n}\nfunction closeDetail(){\n  document.getElementById('detail-panel').classList.remove('open');\n  document.querySelectorAll('.journal-card').forEach(c=>c.classList.remove('selected'));\n}\n\n\/\/ \u2500\u2500 Vue agents \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction renderAgents(){\n  const agMap={};\n  allJ.forEach(j=>{\n    const k=(j.meta.agent||'?')+'__'+(j.meta.celluleCode||'?');\n    if(!agMap[k]) agMap[k]={name:j.meta.agent,cell:j.meta.celluleCode,cellLib:j.meta.cellule,js:[],last:''};\n    agMap[k].js.push(j);\n    if(j.meta.date>agMap[k].last) agMap[k].last=j.meta.date;\n  });\n  const agents=Object.values(agMap);\n  const tbody=document.getElementById('agents-tbody');\n  if(!agents.length){tbody.innerHTML='<tr><td colspan=\"8\" style=\"text-align:center;padding:24px;color:#6B7280\">Aucune donnee<\/td><\/tr>';return;}\n  tbody.innerHTML=agents.map(a=>{\n    const col=cc(a.cell);\n    const nPTA=a.js.reduce((s,j)=>s+(j.tachesPTA||[]).length,0);\n    const nSub=a.js.reduce((s,j)=>s+(j.tachesPTA||[]).reduce((ss,t)=>ss+(t.sousOps||[]).length,0),0);\n    const nHors=a.js.reduce((s,j)=>s+(j.horsePTA||[]).length,0);\n    const blk=a.js.some(j=>j.bilan&&j.bilan.blocages&&j.bilan.blocages.trim());\n    return `<tr>\n      <td><strong>${a.name||'\u2014'}<\/strong><\/td>\n      <td><span class=\"badge\" style=\"background:${col.badge};color:${col.bt}\">${a.cellLib||a.cell}<\/span><\/td>\n      <td>${a.js.length}<\/td><td><strong>${nPTA}<\/strong><\/td><td>${nSub}<\/td><td>${nHors}<\/td>\n      <td>${blk?'<span class=\"badge badge-red\">Oui<\/span>':'<span class=\"badge badge-green\">RAS<\/span>'}<\/td>\n      <td>${fmtDate(a.last)}<\/td>\n    <\/tr>`;\n  }).join('');\n\n  \/\/ Chart agents PTA\n  const maxPTA=Math.max(...agents.map(a=>a.js.reduce((s,j)=>s+(j.tachesPTA||[]).length,0)),1);\n  document.getElementById('chart-agents').innerHTML=agents.map(a=>{\n    const n=a.js.reduce((s,j)=>s+(j.tachesPTA||[]).length,0);\n    return `<div class=\"bar-row\">\n      <div class=\"bar-label\" title=\"${a.name}\">${(a.name||'').split(' ')[0]}<\/div>\n      <div class=\"bar-track\"><div class=\"bar-fill\" style=\"width:${Math.round(n\/maxPTA*100)}%;background:#3B82F6\"><\/div><\/div>\n      <div class=\"bar-count\">${n}<\/div>\n    <\/div>`;\n  }).join('');\n\n  \/\/ Chart bilan\n  const prod=allJ.filter(j=>j.bilan&&j.bilan.statut&&j.bilan.statut.includes('roductive')).length;\n  const part=allJ.filter(j=>j.bilan&&j.bilan.statut&&j.bilan.statut.includes('artiel')).length;\n  const blk2=allJ.filter(j=>j.bilan&&j.bilan.statut&&(j.bilan.statut.includes('loqu')||j.bilan.statut.includes('loqu'))).length;\n  const tot=allJ.length||1;\n  document.getElementById('chart-bilan').innerHTML=[\n    {l:'Journees productives',n:prod,c:'#10B981'},\n    {l:'Partiellement realisees',n:part,c:'#F59E0B'},\n    {l:'Bloquees',n:blk2,c:'#EF4444'},\n  ].map(({l,n,c})=>`<div class=\"bar-row\">\n    <div class=\"bar-label\">${l}<\/div>\n    <div class=\"bar-track\"><div class=\"bar-fill\" style=\"width:${Math.round(n\/tot*100)}%;background:${c}\"><\/div><\/div>\n    <div class=\"bar-count\">${n}<\/div>\n  <\/div>`).join('');\n}\n\n\/\/ \u2500\u2500 Vue PTA \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction renderPTA(){\n  const taskMap={},agPerTask={},subMap={};\n  allJ.forEach(j=>{\n    (j.tachesPTA||[]).forEach(t=>{\n      const k=t.codeAct+'::'+t.codeTache+'::'+t.libelleTache;\n      taskMap[k]=(taskMap[k]||0)+1;\n      if(!agPerTask[k]) agPerTask[k]=new Set();\n      agPerTask[k].add(j.meta.agent);\n      (t.sousOps||[]).forEach(s=>{\n        const sk=k+'::'+s.lib;\n        subMap[sk]=(subMap[sk]||0)+1;\n      });\n    });\n  });\n  const sorted=Object.entries(taskMap).sort((a,b)=>b[1]-a[1]);\n  const tbody=document.getElementById('pta-tbody');\n  if(!sorted.length){tbody.innerHTML='<tr><td colspan=\"5\" style=\"text-align:center;padding:24px;color:#6B7280\">Aucune donnee<\/td><\/tr>';}\n  else tbody.innerHTML=sorted.map(([k,n])=>{\n    const parts=k.split('::');\n    const codeAct=parts[0],codeTache=parts[1],lib=parts[2];\n    const ags=[...(agPerTask[k]||new Set())].join(', ');\n    const topSub=Object.entries(subMap).filter(([sk])=>sk.startsWith(k+'::')).sort((a,b)=>b[1]-a[1]).slice(0,3).map(([sk,sn])=>sk.split('::').pop()+' ('+sn+'x)').join(' \u00b7 ')||'\u2014';\n    return `<tr>\n      <td><code style=\"font-size:10px;background:#F0F7FF;padding:2px 6px;border-radius:4px\">${codeAct}<\/code><\/td>\n      <td><strong>${lib}<\/strong><div style=\"font-size:10px;color:#6B7280\">${codeTache}<\/div><\/td>\n      <td><strong>${n}<\/strong><\/td>\n      <td style=\"font-size:11px\">${ags}<\/td>\n      <td style=\"font-size:10px;color:#6B7280\">${topSub}<\/td>\n    <\/tr>`;\n  }).join('');\n\n  \/\/ Top tasks chart\n  const top8=sorted.slice(0,8);\n  const maxN=top8.length?top8[0][1]:1;\n  document.getElementById('chart-top-tasks').innerHTML=top8.map(([k,n])=>{\n    const lib=k.split('::')[2];\n    const s=lib.length>28?lib.substring(0,28)+'...':lib;\n    return `<div class=\"bar-row\">\n      <div class=\"bar-label\" title=\"${lib}\">${s}<\/div>\n      <div class=\"bar-track\"><div class=\"bar-fill\" style=\"width:${Math.round(n\/maxN*100)}%;background:#8B5CF6\"><\/div><\/div>\n      <div class=\"bar-count\">${n}<\/div>\n    <\/div>`;\n  }).join('');\n\n  \/\/ Align chart\n  const totPTA=allJ.reduce((s,j)=>s+(j.tachesPTA||[]).length,0);\n  const totHors=allJ.reduce((s,j)=>s+(j.horsePTA||[]).length,0);\n  const tot=totPTA+totHors||1;\n  const pct=Math.round(totPTA\/tot*100);\n  document.getElementById('chart-align').innerHTML=`\n    <div style=\"text-align:center;padding:10px 0\">\n      <div style=\"font-size:46px;font-weight:700;color:#2563EB\">${pct}%<\/div>\n      <div style=\"font-size:12px;color:#6B7280;margin-bottom:14px\">des taches inscrites au PTA<\/div>\n    <\/div>\n    <div class=\"bar-row\"><div class=\"bar-label\">Taches PTA<\/div><div class=\"bar-track\"><div class=\"bar-fill\" style=\"width:${pct}%;background:#3B82F6\"><\/div><\/div><div class=\"bar-count\">${totPTA}<\/div><\/div>\n    <div class=\"bar-row\"><div class=\"bar-label\">Hors PTA<\/div><div class=\"bar-track\"><div class=\"bar-fill\" style=\"width:${100-pct}%;background:#F59E0B\"><\/div><\/div><div class=\"bar-count\">${totHors}<\/div><\/div>`;\n\n  \/\/ Hors PTA table\n  const horsMap={},horsAg={};\n  allJ.forEach(j=>(j.horsePTA||[]).forEach(h=>{\n    horsMap[h.lib]=(horsMap[h.lib]||0)+1;\n    if(!horsAg[h.lib]) horsAg[h.lib]=new Set();\n    horsAg[h.lib].add(j.meta.agent);\n  }));\n  const hSorted=Object.entries(horsMap).sort((a,b)=>b[1]-a[1]);\n  document.getElementById('hors-tbody').innerHTML=hSorted.length\n    ? hSorted.map(([lib,n])=>`<tr><td>${lib}<\/td><td><strong>${n}<\/strong><\/td><td style=\"font-size:11px\">${[...(horsAg[lib]||new Set())].join(', ')}<\/td><\/tr>`).join('')\n    : '<tr><td colspan=\"3\" style=\"text-align:center;padding:16px;color:#6B7280\">Aucune activite hors PTA<\/td><\/tr>';\n}\n\n\/\/ \u2500\u2500 Rapport \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction genRapport(){\n  const periode=document.getElementById('rapport-periode').value;\n  const data=periode==='all' ? allJ : allJ.filter(j=>j.meta.date.startsWith(periode));\n  if(!data.length){document.getElementById('rapport-preview').textContent='Aucune donnee pour cette periode.';return;}\n  const dates=[...new Set(data.map(j=>j.meta.date))].sort();\n  const agents=[...new Set(data.map(j=>j.meta.agent))];\n  const taskMap={};\n  data.forEach(j=>(j.tachesPTA||[]).forEach(t=>{\n    const k=t.codeAct+' - '+t.libelleTache;\n    if(!taskMap[k]) taskMap[k]={n:0,subs:{}};\n    taskMap[k].n++;\n    (t.sousOps||[]).forEach(s=>{taskMap[k].subs[s.lib]=(taskMap[k].subs[s.lib]||0)+1;});\n  }));\n  const horsAll=[];\n  data.forEach(j=>(j.horsePTA||[]).forEach(h=>{if(!horsAll.includes(h.lib))horsAll.push(h.lib);}));\n  const blkLines=data.filter(j=>j.bilan&&j.bilan.blocages&&j.bilan.blocages.trim())\n    .map(j=>'  - '+j.meta.agent+' ('+fmtDate(j.meta.date)+') : '+j.bilan.blocages);\n  const prevLines=[...new Set(data.filter(j=>j.bilan&&j.bilan.previsions&&j.bilan.previsions.trim()).map(j=>j.bilan.previsions))];\n\n  let r='RAPPORT D\\'ACTIVITES - DIVISION DES FORMATIONS NASLA\\n';\n  r+='='.repeat(52)+'\\n';\n  r+='Periode  : '+fmtDate(dates[0])+' -> '+fmtDate(dates[dates.length-1])+'\\n';\n  r+='Genere le: '+new Date().toLocaleDateString('fr-FR')+'\\n';\n  r+='Agents   : '+agents.join(', ')+'\\n';\n  r+='Journaux : '+data.length+'\\n\\n';\n  r+='I. ACTIVITES REALISEES AU TITRE DU PTA\\n'+'-'.repeat(52);\n  Object.entries(taskMap).sort((a,b)=>b[1].n-a[1].n).forEach(([k,v])=>{\n    r+='\\n\\n'+k+' ('+v.n+' declaration'+(v.n>1?'s':'')+')';\n    const top=Object.entries(v.subs).sort((a,b)=>b[1]-a[1]).slice(0,5);\n    if(top.length){r+='\\n  Sous-operations :';top.forEach(([lib,n])=>{r+='\\n    - '+lib+' ('+n+'x)';});}\n  });\n  if(horsAll.length){\n    r+='\\n\\nII. ACTIVITES HORS PTA\\n'+'-'.repeat(52);\n    horsAll.forEach(h=>{r+='\\n  - '+h;});\n  }\n  if(blkLines.length){\n    r+='\\n\\nIII. BLOCAGES ET DIFFICULTES\\n'+'-'.repeat(52)+'\\n';\n    r+=blkLines.join('\\n');\n  }\n  if(prevLines.length){\n    r+='\\n\\nIV. PREVISIONS\\n'+'-'.repeat(52);\n    prevLines.forEach(p=>{r+='\\n  - '+p;});\n  }\n  r+='\\n\\n'+'='.repeat(52)+'\\nFin du rapport - genere automatiquement depuis le journal de bord NASLA';\n  document.getElementById('rapport-preview').textContent=r;\n}\nfunction copyRapport(){\n  const t=document.getElementById('rapport-preview').textContent;\n  navigator.clipboard.writeText(t).then(()=>{\n    const b=event.target; b.textContent='Copie !';\n    setTimeout(()=>{b.textContent='Copier le texte';},2000);\n  });\n}\n\n\/\/ \u2500\u2500 Table import \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction renderLoaded(){\n  const tbody=document.getElementById('loaded-tbody');\n  document.getElementById('loaded-count').textContent=allJ.length;\n  if(!allJ.length){tbody.innerHTML='<tr><td colspan=\"7\" style=\"text-align:center;padding:24px;color:#6B7280\">Aucun journal charge<\/td><\/tr>';return;}\n  const sorted=[...allJ].sort((a,b)=>b.meta.date.localeCompare(a.meta.date));\n  tbody.innerHTML=sorted.map(j=>{\n    const col=cc(j.meta.celluleCode);\n    const bCls=bilanClass(j.bilan&&j.bilan.statut);\n    const realIdx=allJ.indexOf(j);\n    return `<tr>\n      <td>${fmtDate(j.meta.date)}<\/td>\n      <td><strong>${j.meta.agent||'\u2014'}<\/strong><\/td>\n      <td><span class=\"badge\" style=\"background:${col.badge};color:${col.bt}\">${j.meta.cellule||j.meta.celluleCode||'\u2014'}<\/span><\/td>\n      <td>${(j.tachesPTA||[]).length}<\/td>\n      <td>${(j.horsePTA||[]).length}<\/td>\n      <td><span class=\"badge ${bCls}\">${(j.bilan&&j.bilan.statut)||'\u2014'}<\/span><\/td>\n      <td><button class=\"btn btn-outline\" style=\"padding:3px 8px;font-size:10px\" onclick=\"removeJ(${realIdx})\">Retirer<\/button><\/td>\n    <\/tr>`;\n  }).join('');\n}\n\/\/ removeJ est defini dans la section clearAll ci-dessus\n\n\/\/ \u2500\u2500 Export JSON maitre (sauvegarde hebdomadaire) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction exporterFichierMaitre(){\n  if(!allJ.length){alert('Aucun journal a exporter.');return;}\n  const payload={type:'nasla_df_archive',version:'2.0',exporteLe:new Date().toISOString(),nbJournaux:allJ.length,journaux:allJ};\n  const blob=new Blob([JSON.stringify(payload,null,2)],{type:'application\/json'});\n  const url=URL.createObjectURL(blob);\n  const a=document.createElement('a');\n  const d=new Date();\n  const sem=Math.ceil(((d-new Date(d.getFullYear(),0,1))\/86400000+new Date(d.getFullYear(),0,1).getDay()+1)\/7);\n  const semStr=d.getFullYear()+'_S'+String(sem).padStart(2,'0');\n  a.href=url; a.download='NASLA_DF_Archive_'+semStr+'.json'; a.click();\n  URL.revokeObjectURL(url);\n  afficherStatutSauvegarde('export');\n  document.getElementById('import-feedback').innerHTML=\n    '<div class=\"alert alert-info\">Fichier maitre exporte : NASLA_DF_Archive_'+semStr+'.json ('+allJ.length+' journaux). Conservez ce fichier en lieu sur.<\/div>';\n}\n\n\/\/ \u2500\u2500 Import JSON maitre (restauration depuis archive) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction importerFichierMaitre(files){\n  if(!files||!files.length) return;\n  const reader=new FileReader();\n  reader.onload=ev=>{\n    try{\n      const data=JSON.parse(ev.target.result);\n      let journaux=[];\n      if(data.type==='nasla_df_archive'&&Array.isArray(data.journaux)) journaux=data.journaux;\n      else if(Array.isArray(data)) journaux=data;\n      else{alert('Format non reconnu. Verifiez qu\\'il s\\'agit bien d\\'un fichier maitre NASLA.');return;}\n      let ajouts=0;\n      journaux.forEach(j=>{\n        if(j.meta&&j.meta.date){\n          const key=j.meta.date+'_'+j.meta.agent+'_'+j.meta.celluleCode;\n          if(!allJ.find(x=>x.meta.date+'_'+x.meta.agent+'_'+x.meta.celluleCode===key)){allJ.push(j);ajouts++;}\n        }\n      });\n      refresh(); sauvegarderLocal();\n      document.getElementById('import-feedback').innerHTML=\n        '<div class=\"alert alert-info\">Archive restauree : '+ajouts+' journaux ajoutes. Total : '+allJ.length+'<\/div>';\n    }catch(e){alert('Erreur de lecture : '+e.message);}\n  };\n  reader.readAsText(files[0]);\n}\n\n\/\/ \u2500\u2500 Init avec restauration automatique localStorage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ndocument.getElementById('date-display').textContent=\n  'Aujourd\\'hui : '+new Date().toLocaleDateString('fr-FR',{weekday:'long',year:'numeric',month:'long',day:'numeric'});\n\n(function init(){\n  try{\n    const raw=localStorage.getItem(LS_KEY);\n    if(raw){\n      const data=JSON.parse(raw);\n      if(Array.isArray(data)&&data.length>0){\n        allJ=data;\n        refresh();\n        document.getElementById('import-feedback').innerHTML=\n          '<div class=\"alert alert-info\">Memoire restauree automatiquement : '+allJ.length+' journal(aux) recupere(s) depuis votre derniere session.<\/div>';\n        return;\n      }\n    }\n  }catch(e){console.warn('Erreur restauration localStorage:',e);}\n  renderDashboard();\n})();\n<\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Tableau de bord Chef de cellule &#8211; NASLA NASLA &#8211; DF Tableau de bord chef de cellule 2026 Navigation Vue journaliere Par agent Alignement PTA Rapport d&rsquo;activites Donnees Importer journaux 0 Vue journaliere Toutes les dates Toutes les cellulesFormation InitialeFormation ContinueGouvernance &amp; QualiteE-Learning \/ Numerique Journaux recus 0 agents ayant soumis Taches PTA 0 declarations [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-16028","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/pages\/16028","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/comments?post=16028"}],"version-history":[{"count":7,"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/pages\/16028\/revisions"}],"predecessor-version":[{"id":16040,"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/pages\/16028\/revisions\/16040"}],"wp:attachment":[{"href":"https:\/\/elearning.nasla.cm\/index.php\/wp-json\/wp\/v2\/media?parent=16028"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}