
<!-- saved from url=(0060)file:///C:/Users/HP/Downloads/dashboard_kpi_capa_pharma.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: var(--font-sans); }
.db { padding: 16px; max-width: 1100px; }
.header { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-tertiary); border-radius: var(--border-radius-lg); padding: 14px 20px; margin-bottom: 16px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; }
.header-title { font-size: 18px; font-weight: 500; color: var(--color-text-primary); }
.header-meta { display: flex; gap: 24px; flex-wrap: wrap; }
.meta-item { font-size: 12px; color: var(--color-text-secondary); }
.meta-item span { color: var(--color-text-primary); font-weight: 500; }
.filters { display: flex; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; align-items: center; }
.filters label { font-size: 12px; color: var(--color-text-secondary); }
.filters select { font-size: 13px; padding: 4px 8px; border-radius: var(--border-radius-md); border: 0.5px solid var(--color-border-tertiary); background: var(--color-background-primary); color: var(--color-text-primary); }
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi-card { background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 14px 16px; position: relative; }
.kpi-label { font-size: 11px; color: var(--color-text-secondary); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.04em; }
.kpi-value { font-size: 26px; font-weight: 500; line-height: 1.1; }
.kpi-target { font-size: 11px; color: var(--color-text-secondary); margin-top: 4px; }
.kpi-bar-wrap { height: 4px; background: var(--color-background-primary); border-radius: 2px; margin-top: 8px; overflow: hidden; }
.kpi-bar { height: 100%; border-radius: 2px; }
.status-badge { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 500; padding: 2px 8px; border-radius: 20px; margin-top: 6px; }
.ok { background: #EAF3DE; color: #3B6D11; }
.warn { background: #FAEEDA; color: #854F0B; }
.crit { background: #FCEBEB; color: #A32D2D; }
.charts-row { display: grid; grid-template-columns: 1fr; gap: 16px; margin-bottom: 16px; }
.chart-card { background: var(--color-background-primary); border: 0.5px solid var(--color-border-tertiary); border-radius: var(--border-radius-lg); padding: 16px 20px; }
.chart-title { font-size: 13px; font-weight: 500; color: var(--color-text-primary); margin-bottom: 12px; }
.charts-row2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; margin-bottom: 16px; }
.alert-card { background: var(--color-background-primary); border: 0.5px solid var(--color-border-tertiary); border-radius: var(--border-radius-lg); padding: 14px 20px; margin-bottom: 16px; }
.alert-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; border-bottom: 0.5px solid var(--color-border-tertiary); font-size: 13px; }
.alert-row:last-child { border-bottom: none; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; margin-top: 4px; }
.dot-warn { background: #EF9F27; }
.dot-ok { background: #639922; }
.dot-crit { background: #E24B4A; }
.alert-text { color: var(--color-text-primary); }
.alert-meta { font-size: 11px; color: var(--color-text-secondary); margin-top: 2px; }
.legend { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 10px; font-size: 12px; color: var(--color-text-secondary); }
.legend span { display: flex; align-items: center; gap: 5px; }
.legend-sq { width: 10px; height: 10px; border-radius: 2px; }
.section-label { font-size: 11px; font-weight: 500; color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px; }
</style>

</head><body><div class="db">

<div class="header">
  <div>
    <div class="header-title">Dashboard Qualite CAPA</div>
    <div style="font-size:12px;color:var(--color-text-secondary);margin-top:2px;">Industrie Pharmaceutique — Suivi OOS / CAPA</div>
  </div>
  <div class="header-meta">
    <div class="meta-item">Periode <span id="hdr-period">2025</span></div>
    <div class="meta-item">Mise a jour <span>25/03/2025 09:14</span></div>
    <div class="meta-item">Utilisateur <span>R. Berkani</span></div>
    <div class="meta-item">Acces <span>Direction Qualite</span></div>
  </div>
</div>

<div class="filters">
  <label>Periode</label>
  <select id="f-period" onchange="updateDash()">
    <option value="2025">2025</option>
    <option value="2024">2024</option>
    <option value="2023">2023</option>
  </select>
  <label>Produit</label>
  <select id="f-product" onchange="updateDash()">
    <option value="all">Tous les produits</option>
    <option value="cardiostat">Cardiostat 10mg</option>
    <option value="betax">Beta-X 50mg</option>
    <option value="injectable">Injectable 5mg/mL</option>
  </select>
  <label>Ligne</label>
  <select id="f-line" onchange="updateDash()">
    <option value="all">Toutes les lignes</option>
    <option value="L1">Ligne 1</option>
    <option value="L2">Ligne 2</option>
    <option value="L3">Ligne 3</option>
  </select>
</div>

<div class="kpi-grid">
  <div class="kpi-card">
    <div class="kpi-label">OOS resolus &lt; 30j</div>
    <div class="kpi-value" id="kpi1-val" style="color:#185FA5;">85%</div>
    <div class="kpi-bar-wrap"><div class="kpi-bar" id="kpi1-bar" style="width:85%;background:#378ADD;"></div></div>
    <div class="kpi-target">Cible ≥ 90%</div>
    <div class="status-badge warn" id="kpi1-status">En alerte</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">Taux recurrence CAPA</div>
    <div class="kpi-value" id="kpi2-val" style="color:#3B6D11;">4%</div>
    <div class="kpi-bar-wrap"><div class="kpi-bar" id="kpi2-bar" style="width:40%;background:#639922;"></div></div>
    <div class="kpi-target">Cible ≤ 5%</div>
    <div class="status-badge ok" id="kpi2-status">Conforme</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">Delai moyen investigation</div>
    <div class="kpi-value" id="kpi3-val" style="color:#3B6D11;">28j</div>
    <div class="kpi-bar-wrap"><div class="kpi-bar" id="kpi3-bar" style="width:93%;background:#639922;"></div></div>
    <div class="kpi-target">Cible ≤ 30j</div>
    <div class="status-badge ok" id="kpi3-status">Conforme</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">CAPA en retard</div>
    <div class="kpi-value" id="kpi4-val" style="color:#A32D2D;">8%</div>
    <div class="kpi-bar-wrap"><div class="kpi-bar" id="kpi4-bar" style="width:80%;background:#E24B4A;"></div></div>
    <div class="kpi-target">Cible ≤ 5%</div>
    <div class="status-badge crit" id="kpi4-status">Hors cible</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">Ppk moyen post-CAPA</div>
    <div class="kpi-value" id="kpi5-val" style="color:#3B6D11;">1.48</div>
    <div class="kpi-bar-wrap"><div class="kpi-bar" id="kpi5-bar" style="width:74%;background:#639922;"></div></div>
    <div class="kpi-target">Cible ≥ 1.33</div>
    <div class="status-badge ok" id="kpi5-status">Conforme</div>
  </div>
  <div class="kpi-card">
    <div class="kpi-label">CAPA effectiveness 100%</div>
    <div class="kpi-value" id="kpi6-val" style="color:#854F0B;">72%</div>
    <div class="kpi-bar-wrap"><div class="kpi-bar" id="kpi6-bar" style="width:72%;background:#EF9F27;"></div></div>
    <div class="kpi-target">Cible 100%</div>
    <div class="status-badge warn" id="kpi6-status">En alerte</div>
  </div>
</div>

<div class="charts-row">
  <div class="chart-card">
    <div class="chart-title">Tendance OOS par mois — 12 derniers mois</div>
    <div class="legend">
      <span><span class="legend-sq" style="background:#378ADD;"></span>OOS detectes</span>
      <span><span class="legend-sq" style="background:#639922;"></span>OOS resolus</span>
      <span><span class="legend-sq" style="border:1px dashed #E24B4A;background:transparent;"></span>Seuil alerte (20)</span>
    </div>
    <div style="position:relative;width:100%;height:240px;">
      <canvas id="trendChart" width="1285" height="300" style="display: block; box-sizing: border-box; height: 240px; width: 1028px;"></canvas>
    </div>
  </div>
</div>

<div class="charts-row2">
  <div class="chart-card">
    <div class="chart-title">Causes d'OOS — Pareto</div>
    <div style="position:relative;width:100%;height:260px;">
      <canvas id="paretoChart" width="381" height="325" style="display: block; box-sizing: border-box; height: 260px; width: 305px;"></canvas>
    </div>
  </div>
  <div class="chart-card">
    <div class="chart-title">CAPA par statut</div>
    <div class="legend">
      <span><span class="legend-sq" style="background:#E24B4A;"></span>Ouvert 15</span>
      <span><span class="legend-sq" style="background:#EF9F27;"></span>En cours 32</span>
      <span><span class="legend-sq" style="background:#378ADD;"></span>Complete 24</span>
      <span><span class="legend-sq" style="background:#97C459;"></span>Verifie 8</span>
      <span><span class="legend-sq" style="background:#639922;"></span>Clos 41</span>
    </div>
    <div style="position:relative;width:100%;height:220px;">
      <canvas id="statusChart" width="381" height="275" style="display: block; box-sizing: border-box; height: 220px; width: 305px;"></canvas>
    </div>
  </div>
  <div class="chart-card">
    <div class="chart-title">OOS par produit</div>
    <div style="position:relative;width:100%;height:220px;">
      <canvas id="productChart" width="381" height="275" style="display: block; box-sizing: border-box; height: 220px; width: 305px;"></canvas>
    </div>
  </div>
</div>

<div class="alert-card">
  <div class="section-label">Alertes et actions requises</div>
  <div class="alert-row">
    <div class="dot dot-crit"></div>
    <div>
      <div class="alert-text"><strong>CAPA-2025-038</strong> — En retard de 15 jours. Escalade QA requise.</div>
      <div class="alert-meta">Produit : Beta-X 50mg • Responsable : M. Dupont • Echeance : 10/03/2025</div>
    </div>
  </div>
  <div class="alert-row">
    <div class="dot dot-warn"></div>
    <div>
      <div class="alert-text"><strong>Produit Cardiostat 10mg</strong> — 5 OOS ce mois. Investigation tendance requise (carte I-MR).</div>
      <div class="alert-meta">Ligne 2 • Parametre : Dosage PA • Declencher APR anticipe</div>
    </div>
  </div>
  <div class="alert-row">
    <div class="dot dot-warn"></div>
    <div>
      <div class="alert-text"><strong>28 CAPA</strong> sans Effectiveness Check documente (J+30/J+90/J+180).</div>
      <div class="alert-meta">Action requise : planifier les verifications • Responsable QA global</div>
    </div>
  </div>
  <div class="alert-row">
    <div class="dot dot-ok"></div>
    <div>
      <div class="alert-text"><strong>CAPA-2025-042</strong> — Verification J+90 completee. Ppk = 1.54. Efficace.</div>
      <div class="alert-meta">Produit : Cardiostat 10mg • Colonne HPLC • Clos le 22/03/2025</div>
    </div>
  </div>
  <div class="alert-row" style="border:none;">
    <div class="dot dot-ok"></div>
    <div>
      <div class="alert-text"><strong>CAPA-2025-031</strong> — Effectiveness check J+180 valide. CAPA cloturee.</div>
      <div class="alert-meta">Taux OOS post-CAPA : 0 sur 6 mois • Conforme</div>
    </div>
  </div>
</div>

<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-bottom:16px;">
  <div class="chart-card">
    <div class="chart-title">Effectiveness Check CAPA — suivi calendaire</div>
    <div style="position:relative;width:100%;height:180px;">
      <canvas id="effChart" width="607" height="225" style="display: block; box-sizing: border-box; height: 180px; width: 486px;"></canvas>
    </div>
  </div>
  <div class="chart-card">
    <div class="chart-title">Ppk avant / apres CAPA par produit</div>
    <div style="position:relative;width:100%;height:180px;">
      <canvas id="ppkChart" width="607" height="225" style="display: block; box-sizing: border-box; height: 180px; width: 486px;"></canvas>
    </div>
  </div>
</div>

</div>

<script src="./dashboard_kpi_capa_pharma_files/chart.umd.js.téléchargement"></script>
<script>
const months = ['Jan','Fev','Mar','Avr','Mai','Jun','Jul','Aou','Sep','Oct','Nov','Dec'];

const datasets2025 = {
  oos:      [12, 18, 15, 22, 19, 14, 16, 21, 17, 25, 28, 24],
  resolved: [10, 15, 14, 19, 18, 13, 15, 18, 16, 20, 22, 20],
};
const datasets2024 = {
  oos:      [20, 22, 18, 25, 23, 20, 19, 26, 22, 28, 30, 27],
  resolved: [17, 19, 16, 21, 20, 17, 17, 22, 19, 24, 25, 23],
};

let trendChart, paretoChart, statusChart, productChart, effChart, ppkChart;

function buildCharts(period) {
  const d = period === '2025' ? datasets2025 : datasets2024;

  if (trendChart) trendChart.destroy();
  trendChart = new Chart(document.getElementById('trendChart'), {
    type: 'line',
    data: {
      labels: months,
      datasets: [
        { label: 'OOS detectes', data: d.oos, borderColor: '#378ADD', backgroundColor: 'rgba(55,138,221,0.08)', fill: true, tension: 0.3, pointRadius: 4, pointBackgroundColor: '#378ADD', borderWidth: 2 },
        { label: 'OOS resolus', data: d.resolved, borderColor: '#639922', backgroundColor: 'rgba(99,153,34,0.06)', fill: true, tension: 0.3, pointRadius: 4, pointBackgroundColor: '#639922', borderWidth: 2 },
        { label: 'Seuil alerte', data: Array(12).fill(20), borderColor: '#E24B4A', borderDash: [5,4], borderWidth: 1.5, pointRadius: 0, fill: false },
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: { legend: { display: false } },
      scales: {
        x: { grid: { color: 'rgba(128,128,128,0.1)' }, ticks: { font: { size: 11 }, autoSkip: false, maxRotation: 0 } },
        y: { grid: { color: 'rgba(128,128,128,0.1)' }, ticks: { font: { size: 11 } }, beginAtZero: true, suggestedMax: 35 }
      }
    }
  });

  if (paretoChart) paretoChart.destroy();
  paretoChart = new Chart(document.getElementById('paretoChart'), {
    type: 'bar',
    data: {
      labels: ['Colonne HPLC','Erreur analyste','Pompe HPLC','Reactif','Methode','Autre'],
      datasets: [
        { label: 'Nb OOS', data: [34, 26, 18, 12, 8, 6], backgroundColor: ['#185FA5','#378ADD','#85B7EB','#B5D4F4','#D3D1C7','#B4B2A9'], borderRadius: 3 },
        { label: '% cumule', data: [33, 58, 76, 87, 95, 100], type: 'line', borderColor: '#E24B4A', borderWidth: 1.5, pointRadius: 3, pointBackgroundColor: '#E24B4A', yAxisID: 'y2', fill: false, tension: 0 }
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: { legend: { display: false } },
      scales: {
        x: { grid: { display: false }, ticks: { font: { size: 10 }, maxRotation: 25 } },
        y: { grid: { color: 'rgba(128,128,128,0.1)' }, ticks: { font: { size: 11 } }, title: { display: true, text: 'Nb OOS', font: { size: 10 } } },
        y2: { position: 'right', min: 0, max: 100, grid: { display: false }, ticks: { font: { size: 10 }, callback: v => v + '%' } }
      }
    }
  });

  if (statusChart) statusChart.destroy();
  statusChart = new Chart(document.getElementById('statusChart'), {
    type: 'doughnut',
    data: {
      labels: ['Ouvert','En cours','Complete','Verifie','Clos'],
      datasets: [{ data: [15, 32, 24, 8, 41], backgroundColor: ['#E24B4A','#EF9F27','#378ADD','#97C459','#639922'], borderWidth: 0, hoverOffset: 6 }]
    },
    options: {
      responsive: true, maintainAspectRatio: false, cutout: '62%',
      plugins: {
        legend: { display: false },
        tooltip: { callbacks: { label: ctx => ' ' + ctx.label + ': ' + ctx.raw + ' (' + Math.round(ctx.raw/120*100) + '%)' } }
      }
    }
  });

  if (productChart) productChart.destroy();
  productChart = new Chart(document.getElementById('productChart'), {
    type: 'bar',
    data: {
      labels: ['Cardiostat 10mg','Beta-X 50mg','Injectable 5mg/mL','Autre A','Autre B'],
      datasets: [{ data: [28, 22, 18, 12, 8], backgroundColor: '#378ADD', borderRadius: 3 }]
    },
    options: {
      indexAxis: 'y', responsive: true, maintainAspectRatio: false,
      plugins: { legend: { display: false } },
      scales: {
        x: { grid: { color: 'rgba(128,128,128,0.1)' }, ticks: { font: { size: 11 } } },
        y: { grid: { display: false }, ticks: { font: { size: 11 } } }
      }
    }
  });

  if (effChart) effChart.destroy();
  effChart = new Chart(document.getElementById('effChart'), {
    type: 'bar',
    data: {
      labels: ['J+30','J+90','J+180'],
      datasets: [
        { label: 'Realise', data: [38, 28, 19], backgroundColor: '#639922', borderRadius: 3 },
        { label: 'En attente', data: [6, 14, 22], backgroundColor: '#EF9F27', borderRadius: 3 },
        { label: 'En retard', data: [2, 4, 3], backgroundColor: '#E24B4A', borderRadius: 3 },
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: {
        legend: { display: true, position: 'bottom', labels: { font: { size: 11 }, boxWidth: 10, padding: 10 } }
      },
      scales: {
        x: { stacked: true, grid: { display: false }, ticks: { font: { size: 12 } } },
        y: { stacked: true, grid: { color: 'rgba(128,128,128,0.1)' }, ticks: { font: { size: 11 } } }
      }
    }
  });

  if (ppkChart) ppkChart.destroy();
  ppkChart = new Chart(document.getElementById('ppkChart'), {
    type: 'bar',
    data: {
      labels: ['Cardiostat','Beta-X','Injectable','Autre A'],
      datasets: [
        { label: 'Ppk avant CAPA', data: [0.82, 0.91, 1.05, 0.78], backgroundColor: '#F09595', borderRadius: 3 },
        { label: 'Ppk apres CAPA', data: [1.54, 1.41, 1.62, 1.38], backgroundColor: '#639922', borderRadius: 3 },
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: { legend: { display: true, position: 'bottom', labels: { font: { size: 11 }, boxWidth: 10, padding: 10 } } },
      scales: {
        x: { grid: { display: false }, ticks: { font: { size: 11 } } },
        y: {
          grid: { color: 'rgba(128,128,128,0.1)' },
          ticks: { font: { size: 11 } },
          min: 0, max: 2,
          plugins: {},
        }
      },
      plugins: {
        annotation: {}
      }
    }
  });
}

function updateDash() {
  const period = document.getElementById('f-period').value;
  document.getElementById('hdr-period').textContent = period;
  const factor = period === '2024' ? 1.18 : 1;
  buildCharts(period);
}

buildCharts('2025');
</script>
</body></html>