403Webshell
Server IP : 104.21.73.108  /  Your IP : 216.73.216.59
Web Server : Apache/2
System : Linux cat167-197.static.lnwhostname.com 5.10.0-20-amd64 #1 SMP Debian 5.10.158-2 (2022-12-13) x86_64
User : paphayomho ( 1042)
PHP Version : 5.6.40
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
MySQL : ON  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/paphayomho/domains/paphayomhospital.go.th/private_html/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/paphayomho/domains/paphayomhospital.go.th/private_html//dashboard_papayom_2.html
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ตัวชี้วัดและผลลัพธ์ 5 ปี 2566-2570 | รพ.ป่าพะยอม</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&family=Noto+Sans+Thai:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<style>
:root {
  --primary: #0d5c8f;
  --primary-light: #1a7bbf;
  --primary-dark: #093d61;
  --secondary: #1a9e6e;
  --secondary-light: #22c587;
  --accent: #f59e0b;
  --accent2: #e74c3c;
  --bg: #f0f4f8;
  --card: #ffffff;
  --text: #1a2942;
  --text-muted: #5c7a99;
  --border: #d1dde8;
  --good: #16a34a;
  --good-bg: #dcfce7;
  --warn: #ca8a04;
  --warn-bg: #fef9c3;
  --bad: #dc2626;
  --bad-bg: #fee2e2;
  --na: #6b7280;
  --na-bg: #f3f4f6;
  --shadow: 0 2px 16px rgba(13,92,143,0.08);
  --shadow-lg: 0 8px 32px rgba(13,92,143,0.12);
  --radius: 12px;
  --radius-sm: 8px;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: 'Sarabun', 'Noto Sans Thai', sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.6;
}

/* ── HERO ── */
.hero {
  background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 50%, var(--primary-light) 100%);
  color: #fff;
  padding: 56px 32px 48px;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.hero::before {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at 80% 20%, rgba(26,155,110,0.2) 0%, transparent 60%),
              radial-gradient(ellipse at 20% 80%, rgba(245,158,11,0.1) 0%, transparent 60%);
}
.hero-badge {
  display: inline-block;
  background: rgba(255,255,255,0.15);
  border: 1px solid rgba(255,255,255,0.3);
  border-radius: 20px;
  padding: 5px 18px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.5px;
  margin-bottom: 16px;
  position: relative;
}
.hero h1 {
  font-size: clamp(22px, 4vw, 38px);
  font-weight: 700;
  margin-bottom: 10px;
  position: relative;
}
.hero .subtitle {
  font-size: 15px;
  opacity: 0.85;
  margin-bottom: 20px;
  position: relative;
}
.hero .description {
  font-size: 14px;
  opacity: 0.75;
  max-width: 700px;
  margin: 0 auto;
  position: relative;
  line-height: 1.8;
}
.hero-dots {
  display: flex; gap: 8px; justify-content: center; margin-top: 24px; position: relative;
}
.hero-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: rgba(255,255,255,0.4);
}
.hero-dot.active { background: var(--accent); width: 28px; border-radius: 5px; }

/* ── NAV ── */
.sticky-nav {
  position: sticky; top: 0; z-index: 100;
  background: var(--primary-dark);
  border-bottom: 2px solid var(--primary-light);
  display: flex; gap: 0; overflow-x: auto;
  scrollbar-width: none;
}
.sticky-nav::-webkit-scrollbar { display: none; }
.nav-btn {
  padding: 13px 20px;
  color: rgba(255,255,255,0.7);
  font-size: 13px; font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  border: none; background: none;
  font-family: inherit;
  transition: all 0.2s;
  border-bottom: 3px solid transparent;
}
.nav-btn:hover { color: #fff; background: rgba(255,255,255,0.08); }
.nav-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

/* ── CONTAINER ── */
.container { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
.section { padding: 40px 0; }
.section-title {
  font-size: 20px; font-weight: 700;
  color: var(--primary-dark);
  margin-bottom: 24px;
  display: flex; align-items: center; gap: 10px;
}
.section-title::after {
  content: ''; flex: 1; height: 2px;
  background: linear-gradient(to right, var(--border), transparent);
}

/* ── SUMMARY CARDS ── */
.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 32px;
}
.summary-card {
  background: var(--card);
  border-radius: var(--radius);
  padding: 22px 20px;
  box-shadow: var(--shadow);
  border-top: 4px solid var(--primary);
  text-align: center;
  transition: transform 0.2s, box-shadow 0.2s;
}
.summary-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.summary-card.good { border-top-color: var(--good); }
.summary-card.warn { border-top-color: var(--warn); }
.summary-card.bad { border-top-color: var(--bad); }
.summary-card.accent { border-top-color: var(--accent); }
.summary-num {
  font-size: 40px; font-weight: 700; color: var(--primary);
  line-height: 1;
}
.summary-card.good .summary-num { color: var(--good); }
.summary-card.warn .summary-num { color: var(--warn); }
.summary-card.bad .summary-num { color: var(--bad); }
.summary-card.accent .summary-num { color: var(--accent); }
.summary-label { font-size: 13px; color: var(--text-muted); margin-top: 6px; }

/* ── PILLAR CARDS ── */
.pillar-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 20px;
}
.pillar-card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: transform 0.2s, box-shadow 0.2s;
}
.pillar-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.pillar-header {
  padding: 18px 20px 14px;
  display: flex; align-items: flex-start; gap: 14px;
}
.pillar-icon {
  width: 42px; height: 42px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; flex-shrink: 0;
  background: var(--primary);
}
.pillar-info { flex: 1; }
.pillar-code { font-size: 11px; font-weight: 700; letter-spacing: 1px; color: var(--text-muted); }
.pillar-name { font-size: 14px; font-weight: 600; color: var(--text); margin-top: 2px; line-height: 1.4; }
.pillar-status {
  padding: 3px 10px; border-radius: 12px;
  font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.status-good { background: var(--good-bg); color: var(--good); }
.status-warn { background: var(--warn-bg); color: var(--warn); }
.status-bad { background: var(--bad-bg); color: var(--bad); }
.status-na { background: var(--na-bg); color: var(--na); }
.pillar-body { padding: 0 20px 18px; }
.pillar-stats {
  display: flex; gap: 16px; margin-bottom: 14px;
  font-size: 13px;
}
.pillar-stat { text-align: center; }
.pillar-stat-num { font-size: 22px; font-weight: 700; color: var(--primary); }
.pillar-stat-lbl { color: var(--text-muted); font-size: 11px; }
.progress-bar-wrap { margin-bottom: 14px; }
.progress-bar-bg {
  height: 8px; border-radius: 4px;
  background: var(--border);
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%; border-radius: 4px;
  background: linear-gradient(to right, var(--secondary), var(--primary-light));
  transition: width 1s ease;
}
.progress-label {
  display: flex; justify-content: space-between;
  font-size: 12px; color: var(--text-muted); margin-top: 4px;
}
.pillar-highlights { list-style: none; }
.pillar-highlights li {
  font-size: 13px; padding: 5px 0;
  border-bottom: 1px solid var(--border);
  display: flex; gap: 6px; align-items: flex-start;
  color: var(--text);
}
.pillar-highlights li:last-child { border-bottom: none; }
.hl-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--secondary); flex-shrink: 0; margin-top: 6px;
}

/* ── FILTERS ── */
.filter-bar {
  background: var(--card);
  border-radius: var(--radius);
  padding: 20px;
  box-shadow: var(--shadow);
  margin-bottom: 20px;
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end;
}
.filter-group { display: flex; flex-direction: column; gap: 5px; }
.filter-label { font-size: 12px; font-weight: 600; color: var(--text-muted); }
.filter-select, .filter-input {
  padding: 8px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: inherit; font-size: 13px;
  color: var(--text);
  background: var(--bg);
  outline: none;
  transition: border-color 0.2s;
}
.filter-select:focus, .filter-input:focus { border-color: var(--primary); }
.filter-input { min-width: 220px; }
.filter-reset {
  padding: 8px 16px;
  background: var(--primary);
  color: #fff;
  border: none; border-radius: var(--radius-sm);
  font-family: inherit; font-size: 13px; font-weight: 500;
  cursor: pointer;
  transition: background 0.2s;
}
.filter-reset:hover { background: var(--primary-dark); }
.filter-count {
  font-size: 13px; color: var(--text-muted);
  align-self: center;
  margin-left: auto;
}

/* ── TABLE ── */
.table-wrap {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: auto;
}
table {
  width: 100%; border-collapse: collapse;
  font-size: 13px;
}
thead th {
  background: var(--primary-dark);
  color: #fff;
  padding: 11px 12px;
  text-align: left;
  white-space: nowrap;
  font-weight: 600;
  font-size: 12px;
  position: sticky; top: 0;
  cursor: pointer;
  user-select: none;
}
thead th:hover { background: var(--primary); }
thead th .sort-icon { margin-left: 4px; opacity: 0.5; }
tbody tr { transition: background 0.15s; }
tbody tr:nth-child(even) { background: #f8fbff; }
tbody tr:hover { background: #e8f4ff; }
td { padding: 9px 12px; vertical-align: top; border-bottom: 1px solid var(--border); }
.td-kpi { max-width: 200px; font-weight: 500; }
.td-okr { max-width: 280px; font-size: 12px; color: var(--text-muted); }
.td-year { text-align: center; }
.badge-level {
  display: inline-block; padding: 2px 8px;
  border-radius: 10px; font-size: 11px; font-weight: 700;
}
.badge-R { background: #dbeafe; color: #1d4ed8; }
.badge-P { background: #ede9fe; color: #6d28d9; }
.badge-U { background: #fee2e2; color: #991b1b; }
.badge-I { background: #dcfce7; color: #166534; }
.badge-status {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px; border-radius: 10px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
}
.badge-บรรลุ { background: var(--good-bg); color: var(--good); }
.badge-ใกล้บรรลุ { background: var(--warn-bg); color: var(--warn); }
.badge-ไม่บรรลุ { background: var(--bad-bg); color: var(--bad); }
.badge-ไม่มีข้อมูล { background: var(--na-bg); color: var(--na); }
.trend-arrow { font-size: 16px; }
.gap-val { font-size: 12px; font-weight: 600; }
.gap-pos { color: var(--good); }
.gap-neg { color: var(--bad); }
.gap-na { color: var(--na); }

/* ── CHARTS ── */
.charts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
  gap: 20px;
}
.chart-card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px;
}
.chart-title {
  font-size: 15px; font-weight: 600; color: var(--primary-dark);
  margin-bottom: 16px;
}
.chart-container { position: relative; height: 280px; }
.heatmap-wrap { overflow-x: auto; }
.heatmap-table {
  width: 100%; border-collapse: separate; border-spacing: 3px;
  font-size: 12px;
}
.heatmap-table th {
  padding: 8px 12px; text-align: center;
  background: var(--primary-dark); color: #fff;
  border-radius: 6px; font-size: 11px;
}
.heatmap-table td {
  padding: 10px 8px; text-align: center;
  border-radius: 6px; font-weight: 600; font-size: 12px;
  cursor: default;
}
.hm-good { background: #bbf7d0; color: #15803d; }
.hm-warn { background: #fef08a; color: #92400e; }
.hm-bad { background: #fecaca; color: #991b1b; }
.hm-na { background: #f3f4f6; color: #9ca3af; }
.hm-mixed { background: #ddd6fe; color: #5b21b6; }
.hm-row-label { font-weight: 600; color: var(--text); background: #f8fbff !important; text-align: left; border-radius: 6px; padding: 10px 12px; }

/* ── KPI SELECTOR ── */
.kpi-selector {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-bottom: 14px;
}
.kpi-chip {
  padding: 5px 12px; border-radius: 20px;
  border: 1.5px solid var(--border);
  font-size: 12px; cursor: pointer;
  background: #fff; color: var(--text-muted);
  font-family: inherit;
  transition: all 0.2s; white-space: nowrap;
}
.kpi-chip.active {
  background: var(--primary); border-color: var(--primary);
  color: #fff;
}

/* ── HIGHLIGHT SECTION ── */
.highlight-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 20px;
}
.highlight-card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px;
}
.highlight-card-title {
  font-size: 15px; font-weight: 700;
  margin-bottom: 16px;
  display: flex; align-items: center; gap: 8px;
}
.highlight-icon { font-size: 20px; }
.highlight-list { list-style: none; }
.highlight-list li {
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  display: flex; gap: 8px; align-items: flex-start;
  line-height: 1.5;
}
.highlight-list li:last-child { border-bottom: none; }

/* ── RECOMMENDATION ── */
.rec-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 14px;
}
.rec-card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px;
  border-left: 4px solid var(--primary);
  display: flex; gap: 14px;
}
.rec-num {
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--primary); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; flex-shrink: 0;
}
.rec-content { flex: 1; }
.rec-title { font-size: 14px; font-weight: 600; color: var(--primary-dark); margin-bottom: 5px; }
.rec-detail { font-size: 13px; color: var(--text-muted); line-height: 1.6; }

/* ── DISCLAIMER ── */
.disclaimer {
  background: #fffbeb;
  border: 1px solid #fcd34d;
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  font-size: 12px;
  color: #92400e;
  margin: 24px 0;
  display: flex; gap: 8px; align-items: flex-start;
}

/* ── FOOTER ── */
.footer {
  background: var(--primary-dark);
  color: rgba(255,255,255,0.7);
  text-align: center;
  padding: 24px;
  font-size: 13px;
}

/* ── PRINT ── */
@media print {
  .sticky-nav, .filter-bar, .filter-reset { display: none !important; }
  .section { page-break-inside: avoid; }
  body { background: #fff; font-size: 12px; }
  .chart-container { height: 220px; }
  .hero { padding: 24px; }
  .hero h1 { font-size: 22px; }
}
@media (max-width: 640px) {
  .summary-grid { grid-template-columns: repeat(2, 1fr); }
  .pillar-grid { grid-template-columns: 1fr; }
  .charts-grid { grid-template-columns: 1fr; }
  .filter-bar { flex-direction: column; }
}
</style>
</head>
<body>

<!-- HERO -->
<section class="hero">
  <div class="hero-badge">โรงพยาบาลป่าพะยอม · จ.พัทลุง | HCODE 11422</div>
  <h1>ตัวชี้วัดและผลลัพธ์ 5 ปี 2566–2570</h1>
  <p class="subtitle">Strategic KPI & Quality Outcome Roadmap | แผนยุทธศาสตร์ รพ.ป่าพะยอม</p>
  <p class="description">
    รายงานการติดตามเป้าหมายระยะ 5 ปี เชื่อมโยงยุทธศาสตร์ P1–P7<br>
    ครอบคลุมผลลัพธ์บริการ คุณภาพ ความปลอดภัย การเงินการคลัง และการพัฒนาบุคลากร<br>
    จัดทำเพื่อสื่อสารต่อ สรพ. และผู้บริหาร โดยอ้างอิงข้อมูลจริงจากไฟล์ตัวชี้วัด ปีงบประมาณ 2566–2568
  </p>
  <div class="hero-dots">
    <div class="hero-dot"></div><div class="hero-dot active"></div><div class="hero-dot"></div>
  </div>
</section>

<!-- STICKY NAV -->
<nav class="sticky-nav" id="stickyNav">
  <button class="nav-btn active" onclick="scrollToSection('summary')">📊 ภาพรวม</button>
  <button class="nav-btn" onclick="scrollToSection('pillars')">🏛️ ยุทธศาสตร์ P1–P7</button>
  <button class="nav-btn" onclick="scrollToSection('table')">📋 ตาราง KPI</button>
  <button class="nav-btn" onclick="scrollToSection('charts')">📈 กราฟ</button>
  <button class="nav-btn" onclick="scrollToSection('highlights')">💡 ประเด็น สรพ.</button>
  <button class="nav-btn" onclick="scrollToSection('recs')">✅ ข้อเสนอแนะ</button>
</nav>

<div class="container">

<!-- DISCLAIMER -->
<div class="disclaimer" style="margin-top:20px">
  ⚠️ <span><strong>หมายเหตุ:</strong> ข้อมูลอ้างอิงจากไฟล์ตัวชี้วัดที่แนบ และควรตรวจสอบความครบถ้วนก่อนใช้เป็นเอกสารทางการ ข้อมูลปี 2567–2568 บางรายการอาจยังไม่ครบสมบูรณ์ ข้อมูลปี 2569–2570 ยังไม่มีผลการดำเนินงาน</span>
</div>

<!-- ── SECTION 1: SUMMARY ── -->
<section class="section" id="summary">
  <h2 class="section-title">📊 Executive Summary – ภาพรวมตัวชี้วัด</h2>
  <div class="summary-grid" id="summaryGrid"></div>
</section>

<!-- ── SECTION 2: PILLARS ── -->
<section class="section" id="pillars">
  <h2 class="section-title">🏛️ Strategic Pillar Dashboard – ผลลัพธ์รายยุทธศาสตร์</h2>
  <div class="pillar-grid" id="pillarGrid"></div>
</section>

<!-- ── SECTION 3: TABLE ── -->
<section class="section" id="table">
  <h2 class="section-title">📋 ตาราง KPI แบบ Interactive</h2>
  <div class="filter-bar">
    <div class="filter-group">
      <label class="filter-label">ยุทธศาสตร์</label>
      <select class="filter-select" id="filterPillar" onchange="applyFilters()">
        <option value="">ทั้งหมด</option>
        <option value="P1">P1 – ลดป่วยรายใหม่</option>
        <option value="P2">P2 – ลดป่วยตาย</option>
        <option value="P3">P3 – IT มาตรฐาน</option>
        <option value="P4">P4 – คุณภาพบริการ</option>
        <option value="P5">P5 – ภาคีเครือข่าย</option>
        <option value="P6">P6 – การเงิน</option>
        <option value="P7">P7 – บุคลากร</option>
      </select>
    </div>
    <div class="filter-group">
      <label class="filter-label">ระดับตัวชี้วัด</label>
      <select class="filter-select" id="filterLevel" onchange="applyFilters()">
        <option value="">ทั้งหมด</option>
        <option value="R">R</option>
        <option value="P">P</option>
        <option value="U">U</option>
        <option value="I">I</option>
      </select>
    </div>
    <div class="filter-group">
      <label class="filter-label">สถานะปี 2568</label>
      <select class="filter-select" id="filterStatus" onchange="applyFilters()">
        <option value="">ทั้งหมด</option>
        <option value="บรรลุ">✅ บรรลุ</option>
        <option value="ใกล้บรรลุ">🟡 ใกล้บรรลุ</option>
        <option value="ไม่บรรลุ">❌ ไม่บรรลุ</option>
        <option value="ไม่มีข้อมูล">➖ ไม่มีข้อมูล</option>
      </select>
    </div>
    <div class="filter-group">
      <label class="filter-label">ค้นหา KPI / OKRs</label>
      <input class="filter-input" id="filterSearch" placeholder="พิมพ์คำค้นหา..." oninput="applyFilters()">
    </div>
    <button class="filter-reset" onclick="resetFilters()">รีเซ็ต</button>
    <span class="filter-count" id="filterCount"></span>
  </div>
  <div class="table-wrap">
    <table id="kpiTable">
      <thead>
        <tr>
          <th onclick="sortTable('pillar')">ยุทธศาสตร์ <span class="sort-icon">↕</span></th>
          <th onclick="sortTable('level')">ระดับ <span class="sort-icon">↕</span></th>
          <th onclick="sortTable('kpi')">KPI <span class="sort-icon">↕</span></th>
          <th>OKRs/KRA</th>
          <th class="td-year">เป้า 66</th>
          <th class="td-year">เป้า 67</th>
          <th class="td-year">เป้า 68</th>
          <th class="td-year">ผล 66</th>
          <th class="td-year">ผล 67</th>
          <th class="td-year">ผล 68</th>
          <th onclick="sortTable('status')">สถานะ 68 <span class="sort-icon">↕</span></th>
          <th>Gap</th>
          <th>Trend</th>
          <th>ข้อสังเกต</th>
        </tr>
      </thead>
      <tbody id="tableBody"></tbody>
    </table>
  </div>
</section>

<!-- ── SECTION 4: CHARTS ── -->
<section class="section" id="charts">
  <h2 class="section-title">📈 Trend Visualization – การวิเคราะห์แนวโน้ม</h2>
  <div class="charts-grid">
    <div class="chart-card">
      <div class="chart-title">สถานะตัวชี้วัดปี 2568 แยกตามยุทธศาสตร์</div>
      <div class="chart-container"><canvas id="chartStatus"></canvas></div>
    </div>
    <div class="chart-card" style="grid-column: span 1;">
      <div class="chart-title">เปรียบเทียบ Target vs Actual – เลือก KPI</div>
      <div class="kpi-selector" id="kpiSelector"></div>
      <div class="chart-container"><canvas id="chartTrend"></canvas></div>
    </div>
    <div class="chart-card" style="grid-column: 1 / -1;">
      <div class="chart-title">Heatmap ผลลัพธ์ P1–P7 × ปี 2566–2568 (สีแสดงสัดส่วนการบรรลุเป้าหมาย)</div>
      <div class="heatmap-wrap" id="heatmapWrap"></div>
    </div>
  </div>
</section>

<!-- ── SECTION 5: HIGHLIGHTS ── -->
<section class="section" id="highlights">
  <h2 class="section-title">💡 ประเด็นสื่อสารต่อ สรพ.</h2>
  <div class="highlight-grid">
    <div class="highlight-card">
      <div class="highlight-card-title"><span class="highlight-icon">🌟</span> จุดแข็ง – Strong Points</div>
      <ul class="highlight-list" id="hlStrengths"></ul>
    </div>
    <div class="highlight-card">
      <div class="highlight-card-title"><span class="highlight-icon">🔧</span> โอกาสพัฒนา – Improvement Areas</div>
      <ul class="highlight-list" id="hlOpportunities"></ul>
    </div>
    <div class="highlight-card" style="grid-column: 1 / -1;">
      <div class="highlight-card-title"><span class="highlight-icon">🔗</span> ความเชื่อมโยงเชิงระบบ – System Linkage</div>
      <ul class="highlight-list" id="hlSystemic"></ul>
    </div>
  </div>
</section>

<!-- ── SECTION 6: RECOMMENDATIONS ── -->
<section class="section" id="recs">
  <h2 class="section-title">✅ ข้อเสนอแนะเชิงบริหาร</h2>
  <div class="rec-list" id="recList"></div>
</section>

<div class="disclaimer">
  📄 <span>รายงานนี้จัดทำจากไฟล์ตัวชี้วัดและค่าเป้าหมายท้าทายความสำเร็จ (Roadmap) โรงพยาบาลป่าพะยอม ปีงบประมาณ 2566–2570 | ข้อมูลที่ไม่มีในไฟล์แสดงเป็น "ไม่มีข้อมูล" | ควรตรวจสอบและรับรองโดยผู้รับผิดชอบก่อนใช้เป็นเอกสารทางการ</span>
</div>
</div><!-- /container -->

<footer class="footer">
  โรงพยาบาลป่าพะยอม | อำเภอป่าพะยอม จังหวัดพัทลุง | โรงพยาบาลชุมชนขนาด 45 เตียง | HCODE 11422<br>
  Strategic KPI Dashboard 2566–2570 | จัดทำด้วย Data Storytelling & HA Quality Framework
</footer>

<script>
// ════════════════════════════════════════════════════════════════
// DATA EMBEDDED FROM EXCEL (ข้อมูลจากไฟล์ตัวชี้วัดที่แนบ)
// โครงสร้าง: pillar, kpiGroup, level (R/P/U/I), kpi, okr,
//            target:{66,67,68,69,70}, actual:{66,67,68,69,70}
// ════════════════════════════════════════════════════════════════
const RAW_DATA = [
  // ─── P1 ───────────────────────────────────────────────
  {pillar:'P1',kpiGroup:'1. อัตราการเจ็บป่วยรายใหม่ DM',level:'R',kpi:'อัตราการเจ็บป่วยรายใหม่ DM',okr:'อัตราผู้ป่วยเบาหวานรายใหม่กลุ่มในเสี่ยง <2%',target:{66:1.9,67:1.8,68:1.7,69:1.6,70:1.5},actual:{66:1.16,67:2.72,68:1.86,69:null,70:null},direction:'<',owner:'ศศิธร'},
  {pillar:'P1',kpiGroup:'1. อัตราการเจ็บป่วยรายใหม่ DM',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ DM',okr:'ร้อยละการตรวจติดตามยืนยันวินิจฉัยกลุ่มสงสัยป่วยโรคเบาหวาน',target:{66:'≥80',67:'≥80',68:'≥85',69:'≥90',70:'≥95'},actual:{66:null,67:78.16,68:80.77,69:null,70:null},direction:'>',owner:'สาลี'},
  {pillar:'P1',kpiGroup:'2. อัตราการเจ็บป่วยรายใหม่ HT',level:'R',kpi:'อัตราการเจ็บป่วยรายใหม่ HT',okr:'อัตราผู้ป่วยความดันรายใหม่ในกลุ่มเสี่ยง <2%',target:{66:3.5,67:3,68:2.5,69:2,70:1.5},actual:{66:4.42,67:7.34,68:2.24,69:null,70:null},direction:'<',owner:'ศศิธร'},
  {pillar:'P1',kpiGroup:'2. อัตราการเจ็บป่วยรายใหม่ HT',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ HT',okr:'ร้อยละการตรวจติดตามยืนยันวินิจฉัยกลุ่มสงสัยป่วยความดันโลหิตสูง',target:{66:'≥80',67:'≥80',68:'≥85',69:'≥90',70:'≥95'},actual:{66:null,67:88.58,68:95.6,69:null,70:null},direction:'>',owner:'สาลี'},
  {pillar:'P1',kpiGroup:'3. อัตราการเจ็บป่วยรายใหม่ Stroke',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ Stroke',okr:'อัตราการเจ็บป่วยรายใหม่ stroke ในผู้ป่วยกลุ่มโรค HT DM <1%',target:{66:1.5,67:1.4,68:1.3,69:1.2,70:1},actual:{66:1.49,67:null,68:3.31,69:null,70:null},direction:'<',owner:'จำรูญศรี'},
  {pillar:'P1',kpiGroup:'4. อัตราการเจ็บป่วยรายใหม่ COPD/Asthma',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ COPD/Asthma',okr:'อัตราการป่วยรายใหม่ในผู้ป่วย COPD < 5 : แสนประชากร',target:{66:5,67:5,68:5,69:5,70:5},actual:{66:2.8,67:null,68:10.92,69:null,70:null},direction:'<',owner:'กมวรรณ'},
  {pillar:'P1',kpiGroup:'4. อัตราการเจ็บป่วยรายใหม่ COPD/Asthma',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ COPD/Asthma',okr:'อัตราการป่วยรายใหม่ในผู้ป่วย Asthma < 5 : แสนประชากร',target:{66:5,67:5,68:5,69:5,70:5},actual:{66:22.44,67:null,68:2.38,69:null,70:null},direction:'<',owner:null},
  {pillar:'P1',kpiGroup:'5. อัตราการเจ็บป่วยรายใหม่ ACS',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ ACS',okr:'อัตราการเจ็บป่วยรายใหม่ ACS ในผู้ป่วยกลุ่มเสี่ยง HT, DM, DLP <0.2%',target:{66:0.5,67:0.4,68:0.3,69:0.2,70:0.2},actual:{66:0.11,67:null,68:0.36,69:null,70:null},direction:'<',owner:'จำรูญศรี'},
  {pillar:'P1',kpiGroup:'6. อัตราการเจ็บป่วยรายใหม่ Head injury',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ Head injury',okr:'ร้อยละความสำเร็จมาตรการป้องกันจุดเสี่ยง 100%',target:{66:50,67:60,68:70,69:80,70:100},actual:{66:null,67:null,68:100,69:null,70:null},direction:'>',owner:'จำรูญศรี'},
  {pillar:'P1',kpiGroup:'6. อัตราการเจ็บป่วยรายใหม่ Head injury',level:'U',kpi:'อัตราการเจ็บป่วยรายใหม่ Head injury',okr:'ร้อยละความสำเร็จมาตรการขับขี่ปลอดภัย (สวมหมวกนิรภัย) 100%',target:{66:60,67:70,68:80,69:90,70:100},actual:{66:50,67:null,68:100,69:null,70:null},direction:'>',owner:null},
  {pillar:'P1',kpiGroup:'7. อัตราการเจ็บป่วยรายใหม่ DHF',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ DHF',okr:'อัตราป่วยด้วยโรคไข้เลือดออกต่อแสนประชากร (ตำบลป่าพะยอม)',target:{66:50,67:50,68:50,69:50,70:50},actual:{66:0,67:null,68:1.3216,69:null,70:null},direction:'<',owner:'สาลี'},
  {pillar:'P1',kpiGroup:'7. อัตราการเจ็บป่วยรายใหม่ DHF',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ DHF',okr:'ค่าลูกน้ำยุงลาย HI<10, CI=0',target:{66:'<10/0',67:'<10/0',68:'<10/0',69:'<10/0',70:'<10/0'},actual:{66:0.874,67:0.912,68:0.8083,69:null,70:null},direction:'special',
    evalFn:(a)=>{ const hi=parseFloat(a); return !isNaN(hi)&&hi<10?'บรรลุ':'ไม่บรรลุ'; }, owner:null},
  {pillar:'P1',kpiGroup:'8. อัตราการเจ็บป่วยรายใหม่ Depression',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ Depression',okr:'ร้อยละของผู้ป่วยโรคซึมเศร้าเข้าถึงบริการสุขภาพจิตเพิ่มขึ้น ร้อยละ 5/ปี',target:{66:75,67:80,68:85,69:90,70:95},actual:{66:78.47,67:null,68:100,69:null,70:null},direction:'>',owner:'มนสิชา'},
  {pillar:'P1',kpiGroup:'9. อัตราการเจ็บป่วยรายใหม่ TB',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ TB',okr:'อัตราสำเร็จของการรักษาวัณโรคปอดรายใหม่',target:{66:'≥85%',67:'≥85%',68:'≥85%',69:'≥85%',70:'≥85%'},actual:{66:100,67:null,68:91.03,69:null,70:null},direction:'>',owner:'เสาวณีย์'},
  {pillar:'P1',kpiGroup:'9. อัตราการเจ็บป่วยรายใหม่ TB',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ TB',okr:'อัตราการเกิดวัณโรครายใหม่จากกลุ่มสัมผัสผู้ป่วย',target:{66:0,67:0,68:0,69:0,70:0},actual:{66:0,67:null,68:0,69:null,70:null},direction:'=',owner:null},
  {pillar:'P1',kpiGroup:'10. อัตราการเจ็บป่วยรายใหม่ HIV',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ HIV',okr:'ร้อยละของผู้เข้ารับบริการที่ได้รับคำแนะนำให้ใช้ PrEP/PEP ได้เริ่มใช้ PrEP/PEP',target:{66:55,67:65,68:75,69:85,70:95},actual:{66:99.84,67:100,68:100,69:null,70:null},direction:'>',owner:'สุวิทย์'},
  {pillar:'P1',kpiGroup:'10. อัตราการเจ็บป่วยรายใหม่ HIV',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ HIV',okr:'ร้อยละของผู้เข้ารับบริการเริ่มใช้ PrEP/PEP ใช้ต่อเนื่อง ครบ 1 เดือน',target:{66:80,67:85,68:90,69:95,70:100},actual:{66:99.84,67:100,68:100,69:null,70:null},direction:'>',owner:null},
  {pillar:'P1',kpiGroup:'10. อัตราการเจ็บป่วยรายใหม่ HIV',level:'I',kpi:'อัตราการเจ็บป่วยรายใหม่ HIV',okr:'ร้อยละของผู้เข้ารับบริการเริ่มใช้ PrEP ที่ใช้ต่อเนื่อง ครบ 3 เดือน',target:{66:80,67:85,68:90,69:95,70:100},actual:{66:99.84,67:100,68:100,69:null,70:null},direction:'>',owner:null},
  {pillar:'P1',kpiGroup:'11. ประสิทธิภาพการบริหารจัดการความเสี่ยงด้านคลินิก',level:'R',kpi:'ประสิทธิภาพการบริหารจัดการความเสี่ยงด้านคลินิก',okr:'อัตราการติดเชื้อในโรงพยาบาล (<0.5 : 1000 วันนอน)',target:{66:0.5,67:0.5,68:0.5,69:0.5,70:0.4},actual:{66:0.56,67:1.5,68:0.26,69:null,70:null},direction:'<',owner:'อรวรรณ'},
  {pillar:'P1',kpiGroup:'11. ประสิทธิภาพการบริหารจัดการความเสี่ยงด้านคลินิก',level:'P',kpi:'ประสิทธิภาพการบริหารจัดการความเสี่ยงด้านคลินิก',okr:'ร้อยละการได้รับ Antibiotic ภายใน 1 ชม. ผู้ป่วย Sepsis (≥90%)',target:{66:90,67:90,68:90,69:90,70:90},actual:{66:68.85,67:80.58,68:90.41,69:null,70:null},direction:'>',owner:null},
  {pillar:'P1',kpiGroup:'12. Dental carie',level:'U',kpi:'Dental carie',okr:'ร้อยละการเกิดโรคฟันแท้ผุ ในเด็ก 6 ปี',target:{66:'ไม่เกิน 20',67:'ไม่เกิน 20',68:'ไม่เกิน 21',69:'ไม่เกิน 22',70:'ไม่เกิน 23'},actual:{66:null,67:4.55,68:5.22,69:null,70:null},direction:'special',
    evalFn:(a,t)=>{ const aNum=parseFloat(a); const tNum=parseFloat(String(t).replace(/[^\d.]/g,'')); if(isNaN(aNum)||isNaN(tNum))return'ไม่มีข้อมูล'; return aNum<=tNum?'บรรลุ':aNum<=tNum*1.1?'ใกล้บรรลุ':'ไม่บรรลุ'; }, owner:'วสัน'},
  {pillar:'P1',kpiGroup:'12. Dental carie',level:'U',kpi:'Dental carie',okr:'ร้อยละผู้สูงอายุได้รับการคัดกรองรอยโรคก่อมะเร็ง PMD',target:{66:30,67:35,68:40,69:45,70:50},actual:{66:null,67:59.2,68:83.55,69:null,70:null},direction:'>',owner:'วสัน'},
  {pillar:'P1',kpiGroup:'13. ผู้ป่วยเสพติดเข้าถึงบริการ',level:'U',kpi:'อัตราผู้ป่วยเสพติดเข้าถึงบริการและอยู่ในระบบ',okr:'ร้อยละของผู้ป่วยยาเสพติดเข้าสู่กระบวนการรักษาและได้รับการดูแลต่อเนื่องเพิ่มขึ้นร้อยละ 2 ต่อปี',target:{66:60,67:62,68:64,69:66,70:68},actual:{66:null,67:null,68:79,69:null,70:null},direction:'>',owner:'มนสิชา'},

  // ─── P2 ───────────────────────────────────────────────
  {pillar:'P2',kpiGroup:'14. อัตราการป่วยตาย DM',level:'U',kpi:'อัตราการป่วยตาย DM',okr:'อัตราการแทรกซ้อนในผู้ป่วยเบาหวาน <5%',target:{66:5,67:4,68:3,69:2,70:1},actual:{66:0.52,67:4.17,68:4.04,69:null,70:null},direction:'<',owner:'ศศิธร'},
  {pillar:'P2',kpiGroup:'14. อัตราการป่วยตาย DM',level:'U',kpi:'อัตราการป่วยตาย DM',okr:'ร้อยละผู้ป่วยเบาหวานที่ควบคุมระดับน้ำตาลได้ดี >40%',target:{66:40,67:41,68:42,69:43,70:44},actual:{66:28.24,67:35.36,68:41.24,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'14. อัตราการป่วยตาย DM',level:'U',kpi:'อัตราการป่วยตาย DM',okr:'อัตราการขาดนัดในผู้ป่วยเบาหวาน <5%',target:{66:5,67:4.5,68:4,69:3.5,70:3},actual:{66:1.82,67:3.71,68:4.22,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'14. อัตราการป่วยตาย DM',level:'U',kpi:'อัตราการป่วยตาย DM',okr:'อัตราผู้ป่วยเบาหวานมาก่อนนัดด้วยภาวะน้ำตาลต่ำ <5%',target:{66:5,67:4.5,68:4,69:3.5,70:3},actual:{66:null,67:0.77,68:2.11,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'14. อัตราการป่วยตาย DM',level:'U',kpi:'อัตราการป่วยตาย DM',okr:'อัตราผู้ป่วยเบาหวานมาก่อนนัดด้วยภาวะน้ำตาลสูงวิกฤติ <10%',target:{66:10,67:9,68:8,69:7,70:6},actual:{66:null,67:2.21,68:6.7,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'15. อัตราการป่วยตาย HT',level:'U',kpi:'อัตราการป่วยตาย HT',okr:'อัตราผู้ป่วยแทรกซ้อนรายใหม่ในผู้ป่วย HT <5%',target:{66:5,67:4,68:3,69:2,70:1},actual:{66:0.2,67:0.21,68:0.2,69:null,70:null},direction:'<',owner:'ศศิธร'},
  {pillar:'P2',kpiGroup:'15. อัตราการป่วยตาย HT',level:'U',kpi:'อัตราการป่วยตาย HT',okr:'ร้อยละผู้ป่วย HT ที่ควบคุมระดับ BP >70%',target:{66:68,67:69,68:70,69:71,70:72},actual:{66:66.09,67:69.69,68:58.73,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'15. อัตราการป่วยตาย HT',level:'U',kpi:'อัตราการป่วยตาย HT',okr:'อัตราการขาดนัดในผู้ป่วย HT <5%',target:{66:9,67:8,68:7,69:6,70:5},actual:{66:3.34,67:4.78,68:3.99,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'15. อัตราการป่วยตาย HT',level:'U',kpi:'อัตราการป่วยตาย HT',okr:'อัตราผู้ป่วยความดันโลหิตสูงมีภาวะวิกฤติ <2%',target:{66:0.68,67:0.66,68:0.64,69:0.62,70:0.60},actual:{66:null,67:0.17,68:0.22,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'16. อัตราการป่วยตาย Stroke',level:'U',kpi:'อัตราการป่วยตาย Stroke',okr:'อัตราผู้ป่วย Stroke มาด้วยระบบ EMS >50%',target:{66:20,67:30,68:40,69:50,70:50},actual:{66:20.61,67:null,68:12.16,69:null,70:null},direction:'>',owner:'จำรูญศรี'},
  {pillar:'P2',kpiGroup:'16. อัตราการป่วยตาย Stroke',level:'P',kpi:'อัตราการป่วยตาย Stroke',okr:'อัตราผู้ป่วย Stroke ที่มาถึง รพ. ภายใน 3 ชม. >50%',target:{66:40,67:42,68:46,69:48,70:50},actual:{66:48.34,67:null,68:35.13,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'16. อัตราการป่วยตาย Stroke',level:'R',kpi:'อัตราการป่วยตาย Stroke',okr:'อัตราผู้ป่วย Stroke fast tract ได้รับการส่งต่อภายใน 30 นาที >80%',target:{66:40,67:50,68:60,69:70,70:80},actual:{66:33.55,67:null,68:42.3,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'16. อัตราการป่วยตาย Stroke',level:'P',kpi:'อัตราการป่วยตาย Stroke',okr:'Stroke รายใหม่ได้รับการติดตามเยี่ยมจนครบ 6 เดือน >80%',target:{66:100,67:100,68:100,69:100,70:100},actual:{66:100,67:null,68:93.87,69:null,70:null},direction:'>',owner:'ทิพวรรณ'},
  {pillar:'P2',kpiGroup:'16. อัตราการป่วยตาย Stroke',level:'P',kpi:'อัตราการป่วยตาย Stroke',okr:'ภาวะแทรกซ้อนในผู้ป่วย Stroke <20',target:{66:5,67:5,68:5,69:5,70:5},actual:{66:5.26,67:null,68:6.12,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'16. อัตราการป่วยตาย Stroke',level:'U',kpi:'อัตราการป่วยตาย Stroke',okr:'ผู้ป่วย Stroke ได้รับการฟื้นฟูมีคะแนน Barthel index > 15 คะแนน',target:{66:60,67:65,68:70,69:75,70:80},actual:{66:70.83,67:null,68:85.71,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'17. อัตราการป่วยตาย COPD/Asthma',level:'U',kpi:'อัตราการป่วยตาย COPD/Asthma',okr:'อัตราป่วยตายด้วยโรค COPD < 5 ในโรงพยาบาล',target:{66:5,67:4,68:3,69:2,70:1},actual:{66:0,67:null,68:0,69:null,70:null},direction:'<',owner:'กมลวรรณ'},
  {pillar:'P2',kpiGroup:'17. อัตราการป่วยตาย COPD/Asthma',level:'U',kpi:'อัตราการป่วยตาย COPD/Asthma',okr:'อัตราป่วยตายด้วยโรค Asthma < 5 ในโรงพยาบาล',target:{66:5,67:4,68:3,69:2,70:1},actual:{66:0,67:null,68:0,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'17. อัตราการป่วยตาย COPD/Asthma',level:'R',kpi:'อัตราการป่วยตาย COPD/Asthma',okr:'อัตราการเกิด respiratory failure COPD/Asthma ในหอผู้ป่วยใน',target:{66:5,67:4,68:3,69:2,70:1},actual:{66:1.29,67:null,68:null,69:null,70:null},direction:'<',owner:null},
  {pillar:'P2',kpiGroup:'17. อัตราการป่วยตาย COPD/Asthma',level:'U',kpi:'อัตราการป่วยตาย COPD/Asthma',okr:'ผู้ป่วยคลินิก COPD/Asthma ใช้ยาพ่นถูกต้องร้อยละ 80',target:{66:80,67:82,68:84,69:86,70:88},actual:{66:81.07,67:85,68:85,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'18. อัตราการป่วยตาย ACS',level:'U',kpi:'อัตราการป่วยตาย ACS',okr:'อัตราผู้ป่วย ACS มาด้วยระบบ EMS >50%',target:{66:20,67:25,68:30,69:40,70:50},actual:{66:20,67:null,68:0,69:null,70:null},direction:'>',owner:'จำรูญศรี'},
  {pillar:'P2',kpiGroup:'18. อัตราการป่วยตาย ACS',level:'P',kpi:'อัตราการป่วยตาย ACS',okr:'อัตราผู้ป่วย ACS ที่มาถึง รพ. ภายใน 3 ชม. ≥80%',target:{66:85,67:90,68:95,69:100,70:100},actual:{66:100,67:null,68:85.7,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'18. อัตราการป่วยตาย ACS',level:'R',kpi:'อัตราการป่วยตาย ACS',okr:'อัตราผู้ป่วย ACS ได้รับการวินิจฉัยประเมิน EKG ภายใน 10 นาที 100%',target:{66:85,67:90,68:95,69:100,70:100},actual:{66:20,67:null,68:100,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'18. อัตราการป่วยตาย ACS',level:'U',kpi:'อัตราการป่วยตาย ACS',okr:'อัตราผู้ป่วย ACS ได้รับการส่งต่อถึงศูนย์หัวใจภายใน 80 นาที 100%',target:{66:60,67:70,68:80,69:90,70:100},actual:{66:40,67:null,68:48.18,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'18. อัตราการป่วยตาย ACS',level:'U',kpi:'อัตราการป่วยตาย ACS',okr:'อัตราการได้รับ SK ในผู้ป่วย ACS ภายใน 30 นาที 100%',target:{66:100,67:100,68:100,69:100,70:100},actual:{66:null,67:null,68:null,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'19. อัตราการป่วยตาย Head injury',level:'U',kpi:'อัตราการป่วยตาย Head injury',okr:'อัตราผู้ป่วยบาดเจ็บศีรษะและลำคอมาด้วยระบบ EMS >80%',target:{66:50,67:60,68:70,69:80,70:80},actual:{66:26.31,67:null,68:24.75,69:null,70:null},direction:'>',owner:'จำรูญศรี'},
  {pillar:'P2',kpiGroup:'19. อัตราการป่วยตาย Head injury',level:'U',kpi:'อัตราการป่วยตาย Head injury',okr:'อัตราผู้ป่วย moderate head injury ได้รับการส่งต่อภายใน 45 นาที 100%',target:{66:100,67:100,68:100,69:100,70:100},actual:{66:100,67:null,68:100,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'19. อัตราการป่วยตาย Head injury',level:'U',kpi:'อัตราการป่วยตาย Head injury',okr:'อัตราผู้ป่วย severe head injury ได้รับการส่งต่อภายใน 1 ชั่วโมง 100%',target:{66:100,67:100,68:100,69:100,70:100},actual:{66:100,67:null,68:91.66,69:null,70:null},direction:'>',owner:null},
  {pillar:'P2',kpiGroup:'20. อัตราการป่วยตาย DHF',level:'P',kpi:'อัตราการป่วยตาย DHF',okr:'อัตราการป่วยตาย DHF',target:{66:0,67:0,68:0,69:0,70:0},actual:{66:0,67:null,68:0,69:null,70:null},direction:'=',owner:'กมลวรรณ'},
  {pillar:'P2',kpiGroup:'20. อัตราการป่วยตาย DHF',level:'P',kpi:'อัตราการป่วยตาย DHF',okr:'อัตราการเกิด DSS ในโรงพยาบาล',target:{66:0,67:0,68:0,69:0,70:0},actual:{66:0,67:null,68:0,69:null,70:null},direction:'=',owner:null},
  {pillar:'P2',kpiGroup:'21. อัตราการป่วยตาย Depression',level:'R',kpi:'อัตราการป่วยตาย Depression',okr:'อัตราฆ่าตัวตายสำเร็จในผู้ป่วยโรคซึมเศร้า',target:{66:2.5,67:2.25,68:2,69:1.75,70:1.5},actual:{66:0,67:null,68:0,69:null,70:null},direction:'<',owner:'มนสิชา'},
  {pillar:'P2',kpiGroup:'22. อัตราการป่วยตาย TB',level:'U',kpi:'อัตราการป่วยตาย TB',okr:'อัตราการตายด้วยโรควัณโรค ไม่เกินร้อยละ 5',target:{66:'<5',67:'<5',68:'<5',69:'<5',70:'<5'},actual:{66:7.33,67:null,68:11.71,69:null,70:null},direction:'<',owner:'เสาวณีย์'},
  {pillar:'P2',kpiGroup:'23. อัตราการป่วยตาย HIV',level:'U',kpi:'อัตราการป่วยตาย HIV',okr:'ร้อยละของผู้ป่วยรับประทานยาต่อเนื่อง (Adherence) >ร้อยละ 90',target:{66:90,67:95,68:95,69:100,70:100},actual:{66:57.33,67:98,68:97.5,69:null,70:null},direction:'>',owner:'สุวิทย์'},
  {pillar:'P2',kpiGroup:'23. อัตราการป่วยตาย HIV',level:'I',kpi:'อัตราการป่วยตาย HIV',okr:'ร้อยละของผู้ป่วยที่มีค่า VL < 40 copies',target:{66:90,67:95,68:95,69:100,70:100},actual:{66:16.5,67:92.43,68:97.5,69:null,70:null},direction:'>',owner:'สุวิทย์'},

  // ─── P3 ───────────────────────────────────────────────
  {pillar:'P3',kpiGroup:'24. มาตรฐาน HIMSS',level:'I',kpi:'มาตรฐาน HIMSS',okr:'ระบบสารสนเทศผ่านมาตรฐาน HIMSS ระดับ 6',target:{66:4,67:4,68:5,69:5,70:6},actual:{66:4,67:4,68:'ยังไม่รับการประเมิน',69:null,70:null},direction:'text',owner:'จริยาพร'},
  {pillar:'P3',kpiGroup:'25. มาตรฐาน HAIT',level:'R',kpi:'มาตรฐาน HAIT',okr:'ผ่านมาตรฐาน HAIT',target:{66:'ได้รับคัดเลือกเป็น รพ.นำร่อง',67:null,68:null,69:'ผ่านการประเมิน HAIT',70:null},actual:{66:'รอรับการประเมิน',67:'รอรับการประเมิน',68:'รอรับการประเมิน',69:null,70:null},direction:'text',owner:'จริยาพร'},
  {pillar:'P3',kpiGroup:'26. ระบบบริหารจัดการสารสนเทศและพัสดุ',level:'I',kpi:'ระบบบริหารจัดการสารสนเทศและพัสดุ',okr:'การประยุกต์ใช้งานระบบบริหารจัดการสารสนเทศและพัสดุ >80%',target:{66:60,67:65,68:70,69:75,70:80},actual:{66:12.12,67:85,68:88.88,69:null,70:null},direction:'>',owner:'จริยาพร'},

  // ─── P4 ───────────────────────────────────────────────
  {pillar:'P4',kpiGroup:'27. มาตรฐานระบบบริการสุขภาพ (HS4)',level:'P',kpi:'มาตรฐานระบบบริการสุขภาพ (HS4)',okr:'ผ่านมาตรฐานระบบบริการสุขภาพ >60%',target:{66:70,67:75,68:80,69:85,70:90},actual:{66:25.33,67:63.33,68:93,69:null,70:null},direction:'>',owner:'จริยาพร'},
  {pillar:'P4',kpiGroup:'28. Green & Clean Hospital',level:'I',kpi:'Green & Clean Hospital Challenge',okr:'ได้รับมาตรฐาน Green & Clean Hospital Excellence',target:{66:'ระดับดีมากPlus',67:'ระดับดีมากPlus',68:'ระดับดีมากPlus',69:'ระดับดีมากPlus',70:'ระดับดีมากPlus'},actual:{66:null,67:null,68:'ผ่านเกณฑ์มาตรฐาน',69:null,70:null},direction:'text',owner:'อรวรรณ'},
  {pillar:'P4',kpiGroup:'29. นโยบาย EMS',level:'I',kpi:'นโยบาย EMS',okr:'ผ่านการประเมินการพัฒนาสถานบริการตามนโยบาย EMS >80%',target:{66:'>80',67:'>85',68:'>90',69:'>95',70:'>100'},actual:{66:'รอรับการประเมิน',67:92.86,68:92.86,69:null,70:null},direction:'special',
    evalFn:(a,t)=>{ const aNum=parseFloat(a); if(isNaN(aNum))return String(a).includes('รอ')?'ไม่บรรลุ':'ไม่มีข้อมูล'; const tNum=parseFloat(String(t).replace(/[^\d.]/g,'')); if(isNaN(tNum))return'ไม่มีข้อมูล'; return aNum>tNum?'บรรลุ':aNum>=tNum*0.9?'ใกล้บรรลุ':'ไม่บรรลุ'; }, owner:'นิภารัตน์'},
  {pillar:'P4',kpiGroup:'30. มาตรฐาน HA',level:'R',kpi:'มาตรฐาน HA',okr:'ผ่านมาตรฐาน HA',target:{66:'ผ่านการรับรอง',67:'ผ่านการรับรอง',68:'ผ่านการรับรอง',69:'ผ่านการรับรอง',70:'ผ่านการรับรอง'},actual:{66:'ผ่านการรับรอง',67:'ผ่านการรับรอง',68:'ผ่านการรับรอง',69:null,70:null},direction:'text',owner:'สุวิทย์'},
  {pillar:'P4',kpiGroup:'31. คลินิก COPD/Asthma คุณภาพ',level:'I',kpi:'คลินิก COPD/Asthma คุณภาพ',okr:'ผ่านเกณฑ์ประเมิน easy asthma clinic',target:{66:'ผ่านเกณฑ์',67:'ผ่านเกณฑ์',68:'ผ่านเกณฑ์',69:'ผ่านเกณฑ์',70:'ผ่านเกณฑ์'},actual:{66:'ผ่านเกณฑ์',67:'ผ่านเกณฑ์',68:'ผ่านเกณฑ์',69:null,70:null},direction:'text',owner:'กมลวรรณ'},
  {pillar:'P4',kpiGroup:'32. คลินิก NCD plus คุณภาพ',level:'R',kpi:'คลินิก NCD plus คุณภาพ',okr:'ผ่านเกณฑ์ NCD Clinic Award',target:{66:'One stop service',67:'Tele',68:'Tele',69:'Self health care clinic',70:'เครือข่ายสุขภาพเข้มแข็ง'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ระดับดีมาก',69:null,70:null},direction:'text',owner:'ศศิธร'},
  {pillar:'P4',kpiGroup:'32. คลินิก NCD plus คุณภาพ',level:'I',kpi:'คลินิก NCD plus คุณภาพ',okr:'ระยะเวลารอคอยภายในคลินิก <2ชม.',target:{66:150,67:140,68:130,69:120,70:110},actual:{66:61.5,67:60.2,68:59.6,69:null,70:null},direction:'<',owner:null},
  {pillar:'P4',kpiGroup:'33. คลินิกจิตเวชและยาเสพติด คุณภาพ',level:'U',kpi:'คลินิกจิตเวชและยาเสพติด คุณภาพ',okr:'ผ่านมาตรฐานบริการสุขภาพจิตและจิตเวช ระดับ 1',target:{66:2,67:2,68:2,69:1,70:1},actual:{66:'เตรียมการ',67:null,68:2,69:null,70:null},direction:'special',
    evalFn:(a,t)=>{ const aNum=parseFloat(a); const tNum=parseFloat(t); if(!isNaN(aNum)&&!isNaN(tNum))return aNum>=tNum?'บรรลุ':'ไม่บรรลุ'; const aStr=String(a); if(aStr.includes('เตรียม'))return'ไม่บรรลุ'; return'ไม่มีข้อมูล'; }, owner:'มนสิชา'},
  {pillar:'P4',kpiGroup:'33. คลินิกจิตเวชและยาเสพติด คุณภาพ',level:'U',kpi:'คลินิกจิตเวชและยาเสพติด คุณภาพ',okr:'ผ่านมาตรฐานบริการยาเสพติด',target:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:'ผ่าน',70:'ผ่าน'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:null},
  {pillar:'P4',kpiGroup:'34. คลินิก HIV คุณภาพ',level:'I',kpi:'คลินิก HIV คุณภาพ',okr:'ผ่านการประเมินหน่วยบริการคุณภาพโรคเอดส์',target:{66:'เตรียม',67:'เตรียม',68:'เตรียม',69:'ผ่านการรับรอง',70:'ผ่านการรับรอง'},actual:{66:'อยู่ระหว่างการเตรียม',67:'อยู่ระหว่างการเตรียม',68:'อยู่ระหว่างการเตรียม',69:null,70:null},direction:'text',owner:'สุวิทย์'},
  {pillar:'P4',kpiGroup:'35. Premium service',level:'I',kpi:'การจัดตั้ง Premium service',okr:'การจัดตั้ง Premium service',target:{66:'ห้องตรวจพิเศษ',67:'หอผู้ป่วยพิเศษ',68:null,69:null,70:null},actual:{66:'ห้องตรวจพิเศษ',67:'หอพิเศษ',68:'หอพิเศษ',69:null,70:null},direction:'special',
    evalFn:(a,t,aStr)=>{ if(!aStr||aStr==='–')return'ไม่มีข้อมูล'; const passW=['พิเศษ','ผ่าน','จัดตั้ง','มี']; const failW=['ยังไม่','ไม่ได้']; if(passW.some(w=>aStr.includes(w))&&!failW.some(w=>aStr.includes(w)))return'บรรลุ'; return'ไม่มีข้อมูล'; }, owner:'กมลวรรณ'},
  {pillar:'P4',kpiGroup:'36. ห้องยาคุณภาพ',level:'R',kpi:'ห้องยาคุณภาพ',okr:'ผ่านเกณฑ์ความปลอดภัยด้านยา',target:{66:'ระดับ 3',67:'ระดับ 3',68:'ระดับ 4',69:'ระดับ 4',70:'ระดับ 4'},actual:{66:3,67:3,68:4,69:null,70:null},direction:'special',
    evalFn:(a,t)=>{ const aNum=parseFloat(a); const tNum=parseFloat(String(t).replace(/[^\d.]/g,'')); if(!isNaN(aNum)&&!isNaN(tNum))return aNum>=tNum?'บรรลุ':aNum>=tNum-1?'ใกล้บรรลุ':'ไม่บรรลุ'; return'ไม่มีข้อมูล'; }, owner:'สุนิสา'},
  {pillar:'P4',kpiGroup:'37. บริการกายภาพบำบัดคุณภาพ',level:'I',kpi:'บริการกายภาพบำบัดคุณภาพ',okr:'ผ่านเกณฑ์มาตรฐานกายภาพบำบัด',target:{66:'ดีเยี่ยม',67:null,68:null,69:'ผ่านการรับรองคุณภาพจากสภากายภาพบำบัด',70:null},actual:{66:'ระดับดี',67:'ระดับดี',68:'ระดับดี',69:null,70:null},direction:'text',owner:'ทิพวรรณ'},
  {pillar:'P4',kpiGroup:'37. บริการกายภาพบำบัดคุณภาพ',level:'P',kpi:'บริการกายภาพบำบัดคุณภาพ',okr:'พัฒนาศูนย์ฟื้นฟูผู้สูงอายุและผู้ป่วยระยะกลาง',target:{66:'จัดตั้งศูนย์',67:null,68:null,69:'มีศูนย์ฟื้นฟูให้บริการ',70:null},actual:{66:'ยังไม่ได้จัดตั้งศูนย์',67:'ยังไม่ได้จัดตั้งศูนย์',68:'ยังไม่ได้จัดตั้งศูนย์',69:null,70:null},direction:'text',owner:null},
  {pillar:'P4',kpiGroup:'38. มาตรฐานห้องปฏิบัติการ (LA) และรังสีวินิจฉัย',level:'U',kpi:'มาตรฐาน LA และรังสีวินิจฉัย',okr:'ผ่านเกณฑ์คุณภาพตามมาตรฐานห้องปฏิบัติการ (LA)',target:{66:'ผ่าน (3 ปีครั้ง)',67:null,68:null,69:null,70:null},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:'นันท์นภัส'},
  {pillar:'P4',kpiGroup:'38. มาตรฐานห้องปฏิบัติการ (LA) และรังสีวินิจฉัย',level:'U',kpi:'มาตรฐาน LA และรังสีวินิจฉัย',okr:'ตรวจประเมินระบบบริหารคุณภาพห้องปฏิบัติการรังสีวินิจฉัย',target:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:'ผ่าน',70:'ผ่าน'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:'วรรณ์สิริย์'},
  {pillar:'P4',kpiGroup:'38. มาตรฐานห้องปฏิบัติการ (LA) และรังสีวินิจฉัย',level:'U',kpi:'มาตรฐาน LA และรังสีวินิจฉัย',okr:'มีใบอนุญาตมีไว้ในครอบครองหรือใช้เครื่องกำเนิดรังสี',target:{66:'มี',67:'มี',68:'มี',69:'มี',70:'มี'},actual:{66:'มี',67:'มี',68:'มี',69:null,70:null},direction:'text',owner:null},
  {pillar:'P4',kpiGroup:'39. คลินิกวัยรุ่น',level:'U',kpi:'คลินิกวัยรุ่น',okr:'ผ่านเกณฑ์คุณภาพตามมาตรฐานคลินิกวัยรุ่น',target:{66:'ผ่าน',67:'รอปรับตามเกณฑ์ กรมอนามัย',68:null,69:null,70:null},actual:{66:'รอรับการประเมิน',67:'รอรับการประเมิน',68:'รอรับการประเมิน',69:null,70:null},direction:'text',owner:'เสาวณีย์'},
  {pillar:'P4',kpiGroup:'40. บริการแพทย์แผนไทยคุณภาพ',level:'U',kpi:'บริการแพทย์แผนไทยคุณภาพ',okr:'มาตรฐาน รพ.สส.พท',target:{66:'ดีมาก',67:'ดีมาก',68:'ดีเยี่ยม',69:'ดีเยี่ยม',70:'ดีเยี่ยม'},actual:{66:'รอรับการประเมิน',67:null,68:'ดีเยี่ยม',69:null,70:null},direction:'text',owner:'ไพลิน'},
  {pillar:'P4',kpiGroup:'40. บริการแพทย์แผนไทยคุณภาพ',level:'U',kpi:'บริการแพทย์แผนไทยคุณภาพ',okr:'ร้อยละของผู้ป่วยนอกที่ได้รับบริการการแพทย์แผนไทยและการแพทย์ทางเลือก > 16%',target:{66:16,67:16,68:18,69:18,70:20},actual:{66:15.37,67:null,68:76.85,69:null,70:null},direction:'>',owner:null},
  {pillar:'P4',kpiGroup:'41. ห้องฉุกเฉินคุณภาพ',level:'U',kpi:'ห้องฉุกเฉินคุณภาพ',okr:'ผ่านเกณฑ์ห้องฉุกเฉินคุณภาพ ≥50 คะแนน',target:{66:50,67:60,68:60,69:60,70:70},actual:{66:56.6,67:null,68:60.1,69:null,70:null},direction:'>',owner:'จำรูญศรี'},
  {pillar:'P4',kpiGroup:'41. ห้องฉุกเฉินคุณภาพ',level:'U',kpi:'ห้องฉุกเฉินคุณภาพ',okr:'ระดับความพึงพอใจของผู้รับบริการแผนก ER >80%',target:{66:80,67:80,68:80,69:80,70:80},actual:{66:89.38,67:null,68:88.51,69:null,70:null},direction:'>',owner:null},
  {pillar:'P4',kpiGroup:'42. หอผู้ป่วยในคุณภาพ',level:'U',kpi:'หอผู้ป่วยในคุณภาพ',okr:'ระดับความพึงพอใจ >90%',target:{66:90,67:90,68:90,69:90,70:90},actual:{66:88.46,67:null,68:88.58,69:null,70:null},direction:'>',owner:'กมลวรรณ'},
  {pillar:'P4',kpiGroup:'42. หอผู้ป่วยในคุณภาพ',level:'U',kpi:'หอผู้ป่วยในคุณภาพ',okr:'ข้อร้องเรียน',target:{66:0,67:0,68:0,69:0,70:0},actual:{66:0,67:0,68:0,69:null,70:null},direction:'=',owner:null},
  {pillar:'P4',kpiGroup:'42. หอผู้ป่วยในคุณภาพ',level:'U',kpi:'หอผู้ป่วยในคุณภาพ',okr:'case manager',target:{66:4,67:5,68:6,69:8,70:10},actual:{66:null,67:5,68:3,69:null,70:null},direction:'>',owner:null},
  {pillar:'P4',kpiGroup:'43. OPD คุณภาพ',level:'U',kpi:'OPD คุณภาพ',okr:'ผ่านการรับรอง OPD คุณภาพ',target:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:'ผ่าน',70:'ผ่าน'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:'ศศิธร'},
  {pillar:'P4',kpiGroup:'43. OPD คุณภาพ',level:'U',kpi:'OPD คุณภาพ',okr:'ระดับความพึงพอใจ >85%',target:{66:90,67:91,68:92,69:93,70:94},actual:{66:89.46,67:91.5,68:92.3,69:null,70:null},direction:'>',owner:null},
  {pillar:'P4',kpiGroup:'43. OPD คุณภาพ',level:'U',kpi:'OPD คุณภาพ',okr:'ระยะเวลารอคอย <60 นาที',target:{66:56,67:54,68:52,69:50,70:48},actual:{66:56,67:45.43,68:48.25,69:null,70:null},direction:'<',owner:null},
  {pillar:'P4',kpiGroup:'44. LR คุณภาพ',level:'U',kpi:'LR คุณภาพ',okr:'ผ่านการรับรองห้องคลอดคุณภาพ',target:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:'ผ่าน',70:'ผ่าน'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:'ณาตยา'},

  // ─── P5 ───────────────────────────────────────────────
  {pillar:'P5',kpiGroup:'45. การขับเคลื่อน พชอ.',level:'R',kpi:'การขับเคลื่อนงาน พชอ.',okr:'ร้อยละแผนงานโครงการที่ชุมชนมีส่วนร่วม > 50%',target:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:'ผ่าน',70:'ผ่าน'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:'สาลี'},
  {pillar:'P5',kpiGroup:'45. การขับเคลื่อน พชอ.',level:'P',kpi:'การขับเคลื่อนงาน พชอ.',okr:'ร้อยละแผนงานโครงการที่ชุมชนมีส่วนร่วม > 50%',target:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:'ผ่าน',70:'ผ่าน'},actual:{66:'ผ่าน',67:'ผ่าน',68:'ผ่าน',69:null,70:null},direction:'text',owner:null},

  // ─── P6 ───────────────────────────────────────────────
  {pillar:'P6',kpiGroup:'46. ระดับความเสี่ยงทางการเงิน',level:'R',kpi:'ระดับความเสี่ยงทางการเงิน (Risk Score)',okr:'ระดับความเสี่ยงทางการเงิน (Risk Score) ≤ 3',target:{66:0,67:0,68:0,69:0,70:0},actual:{66:null,67:0.4,68:1.17,69:null,70:null},direction:'<',owner:'นิภารัตน์'},
  {pillar:'P6',kpiGroup:'46. ระดับความเสี่ยงทางการเงิน',level:'P',kpi:'ระดับความเสี่ยงทางการเงิน (Risk Score)',okr:'ระดับความเสี่ยงทางการเงิน (Risk Score) ≤ 3',target:{66:0,67:0,68:0,69:0,70:0},actual:{66:null,67:0.4,68:1.17,69:null,70:null},direction:'<',owner:null},
  {pillar:'P6',kpiGroup:'47. CMI',level:'U',kpi:'CMI',okr:'การเพิ่มขึ้นของค่า CMI >=0.6',target:{66:0.5,67:0.52,68:0.56,69:0.58,70:0.6},actual:{66:0.56,67:0.58,68:0.6434,69:null,70:null},direction:'>',owner:'สริยาภรณ์'},
  {pillar:'P6',kpiGroup:'48. อัตราครองเตียง',level:'U',kpi:'อัตราครองเตียง',okr:'อัตราการครองเตียง >100%',target:{66:'>100',67:'>100',68:'>100',69:'>100',70:'>100'},actual:{66:76.98,67:null,68:73.76,69:null,70:null},direction:'>',owner:'สริยาภรณ์'},
  {pillar:'P6',kpiGroup:'49. Sum Adj RW',level:'U',kpi:'Sum Adj RW',okr:'Sum Adj-RW >1,400',target:{66:1200,67:1250,68:1300,69:1350,70:1400},actual:{66:1611.18,67:1978.63,68:2581.13,69:null,70:null},direction:'>',owner:'สริยาภรณ์'},
  {pillar:'P6',kpiGroup:'50. Current ratio, cash ratio, Quick ratio',level:'U',kpi:'Current ratio / QR / Cash ratio',okr:'QR ≥1',target:{66:1,67:1,68:1,69:1,70:1},actual:{66:null,67:3.9,68:3.34,69:null,70:null},direction:'>',owner:'นิภารัตน์'},
  {pillar:'P6',kpiGroup:'50. Current ratio, cash ratio, Quick ratio',level:'U',kpi:'Current ratio / QR / Cash ratio',okr:'Current ratio ≥1.5',target:{66:1.5,67:1.5,68:1.5,69:1.5,70:1.5},actual:{66:null,67:4.1,68:3.67,69:null,70:null},direction:'>',owner:null},
  {pillar:'P6',kpiGroup:'50. Current ratio, cash ratio, Quick ratio',level:'U',kpi:'Current ratio / QR / Cash ratio',okr:'Cash ratio ≥0.8',target:{66:0.8,67:0.8,68:0.8,69:0.8,70:0.8},actual:{66:null,67:2.92,68:2.63,69:null,70:null},direction:'>',owner:null},
  {pillar:'P6',kpiGroup:'51. EBIDA',level:'U',kpi:'EBIDA',okr:'EBIDA >3,000,000 บาท',target:{66:'>3000000',67:'>3000000',68:'>3000000',69:'>3000000',70:'>3000000'},actual:{66:null,67:2219649.4,68:2908647.16,69:null,70:null},direction:'>',owner:'นิภารัตน์'},
  {pillar:'P6',kpiGroup:'52. OPD/IPD unit cost',level:'P',kpi:'OPD/IPD unit cost',okr:'OPD unit cost (บาท)',target:{66:405.6,67:400,68:394,69:388,70:383},actual:{66:257.745,67:898.02,68:805.77,69:null,70:null},direction:'<',owner:'พรทิพย์'},
  {pillar:'P6',kpiGroup:'52. OPD/IPD unit cost',level:'P',kpi:'OPD/IPD unit cost',okr:'IPD unit cost (บาท)',target:{66:28677,67:28577,68:28477,69:28377,70:28277},actual:{66:13841.57,67:15127.64,68:17628.9,69:null,70:null},direction:'<',owner:'พรทิพย์'},
  {pillar:'P6',kpiGroup:'53. ค่าสาธารณูปโภค',level:'I',kpi:'ค่าสาธารณูปโภค',okr:'หน่วยการใช้ไฟฟ้าลดลงปีละ >= 5%',target:{66:'>5%',67:'>5%',68:'>5%',69:'>5%',70:'>5%'},actual:{66:311.1,67:302.52,68:233.67,69:null,70:null},direction:'special',
    evalFn:(a,t,aStr,tStr,row,year)=>{
      // ประเมินจากแนวโน้มการลดลงเทียบปีก่อน
      // ปี 68: 233.67, ปี 67: 302.52 → ลดลง (302.52-233.67)/302.52 = 22.8% ≥5% ผ่าน
      // ปี 67: 302.52, ปี 66: 311.1 → ลดลง 2.8% < 5% ใกล้บรรลุ
      // ปี 66: ไม่มีปีก่อน → ไม่สามารถประเมินได้
      const prev = {67:311.1, 68:302.52};
      const yr = year||68;
      const aNum = parseFloat(a);
      const prevNum = prev[yr];
      if(isNaN(aNum)||prevNum===undefined)return'ไม่มีข้อมูล';
      const pctDrop = (prevNum - aNum) / prevNum * 100;
      if(pctDrop >= 5) return 'บรรลุ';
      if(pctDrop >= 0) return 'ใกล้บรรลุ';
      return 'ไม่บรรลุ';
    }, owner:'นิภารัตน์'},

  // ─── P7 ───────────────────────────────────────────────
  {pillar:'P7',kpiGroup:'54. ร้อยละความพึงพอใจของบุคลากร',level:'R',kpi:'ร้อยละความพึงพอใจของบุคลากร',okr:'ร้อยละความพึงพอใจของบุคลากร >70',target:{66:70,67:75,68:80,69:85,70:90},actual:{66:75.08,67:null,68:81.64,69:null,70:null},direction:'>',owner:'ทีม HRD'},
  {pillar:'P7',kpiGroup:'55. อัตราความคงอยู่ของบุคลากร',level:'R',kpi:'อัตราความคงอยู่ของบุคลากรด้านสุขภาพ',okr:'อัตราความคงอยู่ของบุคลากรด้านสุขภาพ >70',target:{66:70,67:72,68:74,69:76,70:78},actual:{66:98.03,67:98.25,68:97.25,69:null,70:null},direction:'>',owner:'ทีม HRD'},
  {pillar:'P7',kpiGroup:'56. CQI/R2R/Innovation/งานวิจัย',level:'R',kpi:'จำนวน CQI/R2R/Innovation/งานวิจัย',okr:'จำนวน CQI/R2R/Innovation/งานวิจัย >5 เรื่อง/ปี',target:{66:5,67:5,68:5,69:5,70:5},actual:{66:7,67:9,68:15,69:null,70:null},direction:'>',owner:'ทีม HRD'}
];

// ════════════════════════════════════════════════════════════
// PILLAR METADATA
// ════════════════════════════════════════════════════════════
const PILLAR_META = {
  P1: { name:'ลดการเจ็บป่วยรายใหม่ในโรคยุทธศาสตร์ทุกกลุ่มวัย', icon:'🦠', color:'#0d9488' },
  P2: { name:'ลดการป่วยตายในโรคยุทธศาสตร์ในทุกกลุ่มวัย', icon:'❤️‍🩹', color:'#dc2626' },
  P3: { name:'ระบบเทคโนโลยีและสารสนเทศได้มาตรฐานสากล', icon:'💻', color:'#2563eb' },
  P4: { name:'หน่วยบริการทุกระดับผ่านเกณฑ์คุณภาพ', icon:'🏥', color:'#7c3aed' },
  P5: { name:'เพิ่มศักยภาพภาคีเครือข่ายระดับอำเภอ', icon:'🤝', color:'#059669' },
  P6: { name:'โรงพยาบาลมีเสถียรภาพด้านการเงินการคลัง', icon:'💰', color:'#d97706' },
  P7: { name:'บุคลากรเป็นเลิศ', icon:'👥', color:'#db2777' }
};

// ════════════════════════════════════════════════════════════
// STATUS EVALUATION LOGIC
// direction: '>' = higher better, '<' = lower better
//            '=' = must equal target, 'text' = qualitative
// ════════════════════════════════════════════════════════════
function evalStatus(row, year = 68) {
  const t = row.target[year];
  const a = row.actual[year];

  if (a === null || a === undefined || a === '') return 'ไม่มีข้อมูล';

  const aStr = String(a).trim();
  const tStr = t !== null && t !== undefined ? String(t).trim() : '';

  // ── Special cases: direction = 'special' ──────────────────────────
  // ใช้ evalFn แทน direction เพื่อ handle logic เฉพาะรายตัวชี้วัด
  if (typeof row.evalFn === 'function') {
    return row.evalFn(a, t, aStr, tStr, row, year);
  }

  // ── Qualitative / text ──────────────────────────────────────────
  if (row.direction === 'text') {
    const passWords = ['ผ่าน','ระดับดี','ดีเยี่ยม','ดีมาก','ดี','ระดับ','มี'];
    const failWords = ['รอรับ','ยังไม่','ไม่ได้','เตรียม','อยู่ระหว่าง'];
    const goodMatch = passWords.some(w => aStr.includes(w));
    const failMatch = failWords.some(w => aStr.includes(w));
    if (goodMatch && !failMatch) return 'บรรลุ';
    if (failMatch) return 'ไม่บรรลุ';
    return 'ไม่มีข้อมูล';
  }

  // ── Must equal ──────────────────────────────────────────────────
  if (row.direction === '=') {
    if (tStr === '' || t === null) return 'ไม่มีข้อมูล';
    const aNum = parseFloat(aStr);
    const tNum = parseFloat(tStr);
    if (!isNaN(aNum) && !isNaN(tNum)) return aNum === tNum ? 'บรรลุ' : 'ไม่บรรลุ';
    return aStr === tStr ? 'บรรลุ' : 'ไม่บรรลุ';
  }

  // ── Numeric: higher better / lower better ───────────────────────
  const aNum = parseFloat(aStr);
  if (isNaN(aNum)) return 'ไม่มีข้อมูล';

  const tClean = tStr.replace(/[≥≤<>]/g,'').replace(/%/g,'').trim();
  const tNum = parseFloat(tClean);
  if (isNaN(tNum)) return 'ไม่มีข้อมูล';

  if (row.direction === '>') {
    if (aNum >= tNum) return 'บรรลุ';
    if (aNum >= tNum * 0.9) return 'ใกล้บรรลุ';
    return 'ไม่บรรลุ';
  }
  if (row.direction === '<') {
    if (aNum <= tNum) return 'บรรลุ';
    if (aNum <= tNum * 1.1) return 'ใกล้บรรลุ';
    return 'ไม่บรรลุ';
  }
  return 'ไม่มีข้อมูล';
}

function getTrend(row) {
  const a66 = parseFloat(row.actual[66]);
  const a67 = parseFloat(row.actual[67]);
  const a68 = parseFloat(row.actual[68]);
  const vals = [a66, a67, a68].filter(v => !isNaN(v));
  if (vals.length < 2) return '➖';
  const last = vals[vals.length - 1];
  const first = vals[0];
  const diff = last - first;
  if (row.direction === '>') return diff > 0.5 ? '📈' : diff < -0.5 ? '📉' : '➡️';
  if (row.direction === '<') return diff < -0.5 ? '📈' : diff > 0.5 ? '📉' : '➡️';
  return '➡️';
}

function getGap(row, year = 68) {
  const a = row.actual[year];
  const t = row.target[year];
  if (a === null || t === null || row.direction === 'text' || row.direction === '=') return null;
  const aNum = parseFloat(String(a).replace(/[≥≤<>%]/g,''));
  const tNum = parseFloat(String(t).replace(/[≥≤<>%]/g,''));
  if (isNaN(aNum) || isNaN(tNum)) return null;
  return aNum - tNum;
}

function fmtNum(v) {
  if (v === null || v === undefined) return '–';
  if (typeof v === 'string') return v;
  if (typeof v === 'number') {
    if (Number.isInteger(v)) return v.toLocaleString('th-TH');
    return v.toLocaleString('th-TH', {maximumFractionDigits:2});
  }
  return String(v);
}

// ════════════════════════════════════════════════════════════
// COMPUTE SUMMARY
// ════════════════════════════════════════════════════════════
function computeSummary() {
  /* 
    Logic:
    - totalKPI = RAW_DATA.length (each row is one OKR/sub-indicator)
    - has68 = rows where actual[68] is not null
    - achieved68 = rows where evalStatus(row,68) === 'บรรลุ'
    - near68 = rows where evalStatus === 'ใกล้บรรลุ'
    - fail68 = rows where evalStatus === 'ไม่บรรลุ'
  */
  const total = RAW_DATA.length;
  const pillars = 7;
  const has68 = RAW_DATA.filter(r => r.actual[68] !== null && r.actual[68] !== undefined && r.actual[68] !== '').length;
  const statuses = RAW_DATA.map(r => evalStatus(r, 68));
  const achieved = statuses.filter(s => s === 'บรรลุ').length;
  const near = statuses.filter(s => s === 'ใกล้บรรลุ').length;
  const fail = statuses.filter(s => s === 'ไม่บรรลุ').length;
  const noData = statuses.filter(s => s === 'ไม่มีข้อมูล').length;
  return { total, pillars, has68, achieved, near, fail, noData };
}

// ════════════════════════════════════════════════════════════
// BUILD SUMMARY CARDS
// ════════════════════════════════════════════════════════════
function buildSummary() {
  const s = computeSummary();
  const grid = document.getElementById('summaryGrid');
  const cards = [
    { num: s.pillars, label: 'ยุทธศาสตร์ P1–P7', cls: '' },
    { num: s.total, label: 'ตัวชี้วัด/OKRs ทั้งหมด', cls: '' },
    { num: s.has68, label: 'มีผลดำเนินงานปี 2568', cls: 'accent' },
    { num: s.achieved, label: '✅ บรรลุเป้าหมายปี 2568', cls: 'good' },
    { num: s.near, label: '🟡 ใกล้บรรลุ (±10%)', cls: 'warn' },
    { num: s.fail, label: '❌ ไม่บรรลุ / ต้องเร่งพัฒนา', cls: 'bad' },
    { num: s.noData, label: '➖ ไม่มีข้อมูลปี 2568', cls: '' },
  ];
  grid.innerHTML = cards.map(c => `
    <div class="summary-card ${c.cls}">
      <div class="summary-num">${c.num}</div>
      <div class="summary-label">${c.label}</div>
    </div>
  `).join('');
}

// ════════════════════════════════════════════════════════════
// BUILD PILLAR CARDS
// ════════════════════════════════════════════════════════════
function buildPillars() {
  const grid = document.getElementById('pillarGrid');
  const pillars = ['P1','P2','P3','P4','P5','P6','P7'];
  grid.innerHTML = pillars.map(p => {
    const rows = RAW_DATA.filter(r => r.pillar === p);
    const statuses = rows.map(r => evalStatus(r, 68));
    const good = statuses.filter(s => s === 'บรรลุ').length;
    const near = statuses.filter(s => s === 'ใกล้บรรลุ').length;
    const fail = statuses.filter(s => s === 'ไม่บรรลุ').length;
    const hasData = rows.filter(r => r.actual[68] !== null && r.actual[68] !== undefined && r.actual[68] !== '').length;
    const total = rows.length;
    const pct = total > 0 ? Math.round((good / total) * 100) : 0;
    let overallStatus = 'status-na';
    let statusLabel = 'ไม่มีข้อมูล';
    if (hasData > 0) {
      if (fail > good && fail > 1) { overallStatus = 'status-bad'; statusLabel = 'ต้องเร่งพัฒนา'; }
      else if (good >= fail) { overallStatus = 'status-good'; statusLabel = 'ดี'; }
      else { overallStatus = 'status-warn'; statusLabel = 'เฝ้าระวัง'; }
    }
    const meta = PILLAR_META[p];
    const highlights = getPillarHighlights(p, rows);

    return `
    <div class="pillar-card">
      <div class="pillar-header">
        <div class="pillar-icon" style="background:${meta.color};">${meta.icon}</div>
        <div class="pillar-info">
          <div class="pillar-code">${p}</div>
          <div class="pillar-name">${meta.name}</div>
        </div>
        <div class="pillar-status ${overallStatus}">${statusLabel}</div>
      </div>
      <div class="pillar-body">
        <div class="pillar-stats">
          <div class="pillar-stat"><div class="pillar-stat-num">${total}</div><div class="pillar-stat-lbl">OKRs</div></div>
          <div class="pillar-stat"><div class="pillar-stat-num" style="color:var(--good)">${good}</div><div class="pillar-stat-lbl">บรรลุ</div></div>
          <div class="pillar-stat"><div class="pillar-stat-num" style="color:var(--warn)">${near}</div><div class="pillar-stat-lbl">ใกล้บรรลุ</div></div>
          <div class="pillar-stat"><div class="pillar-stat-num" style="color:var(--bad)">${fail}</div><div class="pillar-stat-lbl">ไม่บรรลุ</div></div>
        </div>
        <div class="progress-bar-wrap">
          <div class="progress-bar-bg">
            <div class="progress-bar-fill" style="width:${pct}%; background:linear-gradient(to right,${meta.color},${meta.color}aa)"></div>
          </div>
          <div class="progress-label"><span>ความสำเร็จปี 2568</span><span>${pct}% (${good}/${total})</span></div>
        </div>
        <ul class="pillar-highlights">
          ${highlights.map(h => `<li><span class="hl-dot" style="background:${meta.color}"></span>${h}</li>`).join('')}
        </ul>
      </div>
    </div>`;
  }).join('');
}

function getPillarHighlights(p, rows) {
  const hl = [];
  if (p === 'P1') {
    hl.push('HIV PrEP/PEP: ผลดำเนินงาน ปี 66–68 สูงเกิน 99% ต่อเนื่อง (เป้า ≥75%)');
    hl.push('TB: อัตราสำเร็จรักษา 91–100% เกินเป้า ≥85%');
    hl.push('Depression: เข้าถึงบริการปี 2568 = 100% (เป้า 85%)');
    hl.push('DM ใหม่: ปี 66 ต่ำกว่าเป้า 1.16% ดี แต่ปี 67 สูงขึ้น 2.72% ต้องเฝ้าระวัง');
    hl.push('Sepsis Antibiotic: ปรับปรุงต่อเนื่อง 68.85% → 90.41% บรรลุเป้าปี 2568');
  } else if (p === 'P2') {
    hl.push('COPD/Asthma: อัตราป่วยตายในโรงพยาบาล = 0% ทุกปี (ดีเยี่ยม)');
    hl.push('DHF: ไม่มีผู้เสียชีวิต, DSS = 0 ทุกปี');
    hl.push('Depression: อัตราฆ่าตัวตายสำเร็จ = 0 (ต่ำกว่าเป้า <2.5)');
    hl.push('TB: อัตราตายยังเกินเป้า <5% → ปี 66: 7.33%, ปี 68: 11.71% ต้องเร่งแก้ไข');
    hl.push('Stroke fast tract ส่งต่อภายใน 30 นาที: ปี 68 = 42.3% (เป้า 60%) ยังต่ำกว่าเป้า');
  } else if (p === 'P3') {
    hl.push('HIMSS ระดับ 4 ทำได้ปี 66–67 แต่ปี 68 "ยังไม่รับการประเมิน"');
    hl.push('ระบบพัสดุ: เร่งพัฒนาจาก 12.12% (ปี 66) สู่ 88.88% (ปี 68) ก้าวหน้ามาก');
    hl.push('HAIT: รอรับการประเมินทุกปี ยังไม่ผ่านการประเมินตามเป้า');
  } else if (p === 'P4') {
    hl.push('HA: ผ่านการรับรองต่อเนื่องปี 66–68 (จุดแข็งสำคัญ)');
    hl.push('OPD คุณภาพ: ผ่านรับรองทุกปี ความพึงพอใจ ≥89% และ waiting time ดีขึ้น');
    hl.push('NCD Clinic: ระดับดีมากปี 2568, เวลารอคอย 59.6 นาที (เป้า <130)');
    hl.push('HS4: ก้าวกระโดดจาก 25.33% (ปี 66) สู่ 93% (ปี 68) เกินเป้า 80%');
    hl.push('LR / COPD Clinic / Lab คุณภาพ: ผ่านเกณฑ์ทุกปีต่อเนื่อง');
  } else if (p === 'P5') {
    hl.push('ร้อยละแผนงานชุมชนมีส่วนร่วม: ผ่านเป้าหมาย "ผ่าน" ทุกปี 66–68');
    hl.push('มีการบูรณาการงาน พชอ. ต่อเนื่องครบทุกปีที่ติดตาม');
  } else if (p === 'P6') {
    hl.push('Sum Adj-RW ก้าวกระโดด: 1,611 → 2,581 (เป้า 1,300) เกินเป้าหมายมาก');
    hl.push('CMI ปี 2568 = 0.6434 เกินเป้า 0.56 (สะท้อนคุณภาพงาน IPD)');
    hl.push('สภาพคล่อง: QR 3.34, Current ratio 3.67 สูงกว่าเป้าหมายอย่างมั่นคง');
    hl.push('Risk Score ปี 68 = 1.17 (ยังอยู่ระดับต่ำ ≤3 = ปลอดภัย)');
    hl.push('อัตราครองเตียง 73–76% (ต่ำกว่าเป้า >100%) ต้องพัฒนางาน IPD');
  } else if (p === 'P7') {
    hl.push('ความพึงพอใจบุคลากร: 75.08% (ปี 66), 81.64% (ปี 68) > เป้า 70–80%');
    hl.push('อัตราคงอยู่บุคลากร: 97–98% เกินเป้าหมาย 70–74% อย่างชัดเจน');
    hl.push('CQI/R2R/Innovation: ก้าวหน้าต่อเนื่อง 7 → 9 → 15 เรื่อง/ปี เกินเป้า 5 เรื่อง');
  }
  return hl;
}

// ════════════════════════════════════════════════════════════
// BUILD TABLE
// ════════════════════════════════════════════════════════════
let currentData = [...RAW_DATA];
let sortKey = null;
let sortAsc = true;

function buildTable(data) {
  const tbody = document.getElementById('tableBody');
  document.getElementById('filterCount').textContent = `แสดง ${data.length} รายการ จาก ${RAW_DATA.length}`;
  
  tbody.innerHTML = data.map(row => {
    const status68 = evalStatus(row, 68);
    const trend = getTrend(row);
    const gap = getGap(row, 68);
    let gapHtml = '<span class="gap-na">–</span>';
    if (gap !== null) {
      const sign = gap > 0 ? '+' : '';
      const cls = (row.direction === '<' && gap <= 0) || (row.direction === '>' && gap >= 0) ? 'gap-pos' : 'gap-neg';
      gapHtml = `<span class="gap-val ${cls}">${sign}${fmtNum(gap)}</span>`;
    }
    const note = getNote(row);
    return `
    <tr>
      <td><span style="font-weight:700;color:${PILLAR_META[row.pillar].color}">${row.pillar}</span></td>
      <td><span class="badge-level badge-${row.level}">${row.level}</span></td>
      <td class="td-kpi">${row.kpi}</td>
      <td class="td-okr">${row.okr}</td>
      <td class="td-year">${fmtNum(row.target[66])}</td>
      <td class="td-year">${fmtNum(row.target[67])}</td>
      <td class="td-year">${fmtNum(row.target[68])}</td>
      <td class="td-year">${fmtNum(row.actual[66])}</td>
      <td class="td-year">${fmtNum(row.actual[67])}</td>
      <td class="td-year">${fmtNum(row.actual[68])}</td>
      <td><span class="badge-status badge-${status68}">${status68}</span></td>
      <td>${gapHtml}</td>
      <td class="trend-arrow">${trend}</td>
      <td style="font-size:12px;color:var(--text-muted);max-width:180px;">${note}</td>
    </tr>`;
  }).join('');
}

function getNote(row) {
  const s = evalStatus(row, 68);
  if (s === 'บรรลุ') return '✅ บรรลุเป้าหมายปี 2568';
  if (s === 'ใกล้บรรลุ') return '🟡 ใกล้บรรลุ ควรติดตามต่อเนื่อง';
  if (s === 'ไม่บรรลุ') return '❌ ต้องวางแผนปรับปรุง';
  if (row.actual[68] === null || row.actual[68] === undefined) {
    if (row.actual[67] !== null) return '⚠️ ขาดข้อมูลปี 68 (มีปี 67)';
    return '➖ ยังไม่มีข้อมูลผลดำเนินงาน';
  }
  return '➖ ประเมินไม่ได้ (ตัวชี้วัดเชิงคุณภาพ)';
}

function applyFilters() {
  const pillar = document.getElementById('filterPillar').value;
  const level = document.getElementById('filterLevel').value;
  const status = document.getElementById('filterStatus').value;
  const search = document.getElementById('filterSearch').value.toLowerCase();
  currentData = RAW_DATA.filter(row => {
    if (pillar && row.pillar !== pillar) return false;
    if (level && row.level !== level) return false;
    if (status && evalStatus(row, 68) !== status) return false;
    if (search) {
      const haystack = (row.kpi + ' ' + row.okr + ' ' + row.kpiGroup).toLowerCase();
      if (!haystack.includes(search)) return false;
    }
    return true;
  });
  if (sortKey) doSort();
  else buildTable(currentData);
}

function resetFilters() {
  document.getElementById('filterPillar').value = '';
  document.getElementById('filterLevel').value = '';
  document.getElementById('filterStatus').value = '';
  document.getElementById('filterSearch').value = '';
  currentData = [...RAW_DATA];
  buildTable(currentData);
}

function sortTable(key) {
  if (sortKey === key) sortAsc = !sortAsc;
  else { sortKey = key; sortAsc = true; }
  doSort();
}
function doSort() {
  currentData.sort((a, b) => {
    let va = a[sortKey] || '';
    let vb = b[sortKey] || '';
    if (sortKey === 'status') { va = evalStatus(a,68); vb = evalStatus(b,68); }
    return sortAsc ? String(va).localeCompare(String(vb), 'th') : String(vb).localeCompare(String(va), 'th');
  });
  buildTable(currentData);
}

// ════════════════════════════════════════════════════════════
// BUILD CHARTS
// ════════════════════════════════════════════════════════════
let trendChart = null;
let selectedKpiIdx = 0;

// chart 1: status by pillar
function buildStatusChart() {
  const pillars = ['P1','P2','P3','P4','P5','P6','P7'];
  const achieved = [], near = [], fail = [], nodata = [];
  pillars.forEach(p => {
    const rows = RAW_DATA.filter(r => r.pillar === p);
    const statuses = rows.map(r => evalStatus(r, 68));
    achieved.push(statuses.filter(s => s === 'บรรลุ').length);
    near.push(statuses.filter(s => s === 'ใกล้บรรลุ').length);
    fail.push(statuses.filter(s => s === 'ไม่บรรลุ').length);
    nodata.push(statuses.filter(s => s === 'ไม่มีข้อมูล').length);
  });
  const ctx = document.getElementById('chartStatus').getContext('2d');
  new Chart(ctx, {
    type: 'bar',
    data: {
      labels: pillars,
      datasets: [
        { label: 'บรรลุ', data: achieved, backgroundColor: '#16a34a' },
        { label: 'ใกล้บรรลุ', data: near, backgroundColor: '#ca8a04' },
        { label: 'ไม่บรรลุ', data: fail, backgroundColor: '#dc2626' },
        { label: 'ไม่มีข้อมูล', data: nodata, backgroundColor: '#d1d5db' }
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: { legend: { position: 'bottom', labels: { font: { family: 'Sarabun', size: 12 } } } },
      scales: {
        x: { stacked: true, ticks: { font: { family: 'Sarabun', size: 12 } } },
        y: { stacked: true, ticks: { font: { family: 'Sarabun' } }, title: { display: true, text: 'จำนวนตัวชี้วัด', font: { family: 'Sarabun' } } }
      }
    }
  });
}

// chart 2: trend for selected KPI
const TREND_KPIS = [
  { label:'HIV PrEP เริ่มใช้', row: RAW_DATA.find(r => r.okr.includes('PrEP/PEP') && r.okr.includes('เริ่มใช้ PrEP/PEP')) },
  { label:'DM ควบคุมน้ำตาล', row: RAW_DATA.find(r => r.okr.includes('ควบคุมระดับน้ำตาล')) },
  { label:'HT แทรกซ้อน', row: RAW_DATA.find(r => r.okr.includes('แทรกซ้อนรายใหม่ในผู้ป่วย HT')) },
  { label:'Sepsis AB', row: RAW_DATA.find(r => r.okr.includes('Antibiotic ภายใน 1 ชม')) },
  { label:'Sum Adj-RW', row: RAW_DATA.find(r => r.okr.includes('Adj-RW')) },
  { label:'ความพึงพอใจบุคลากร', row: RAW_DATA.find(r => r.okr.includes('ความพึงพอใจของบุคลากร')) },
  { label:'CQI/R2R', row: RAW_DATA.find(r => r.okr.includes('CQI/R2R')) },
  { label:'HS4', row: RAW_DATA.find(r => r.okr.includes('มาตรฐานระบบบริการสุขภาพ')) },
  { label:'CMI', row: RAW_DATA.find(r => r.okr.includes('CMI')) },
  { label:'TB อัตราตาย', row: RAW_DATA.find(r => r.okr.includes('ตายด้วยโรควัณโรค')) },
].filter(k => k.row);

function buildKpiSelector() {
  const sel = document.getElementById('kpiSelector');
  sel.innerHTML = TREND_KPIS.map((k, i) => `
    <button class="kpi-chip ${i === 0 ? 'active' : ''}" onclick="selectKpi(${i})">${k.label}</button>
  `).join('');
}

function selectKpi(idx) {
  selectedKpiIdx = idx;
  document.querySelectorAll('.kpi-chip').forEach((c, i) => c.classList.toggle('active', i === idx));
  buildTrendChart();
}

function buildTrendChart() {
  const k = TREND_KPIS[selectedKpiIdx];
  const row = k.row;
  const years = ['66','67','68'];
  const targets = years.map(y => {
    const t = row.target[parseInt(y)];
    if (t === null || t === undefined) return null;
    const num = parseFloat(String(t).replace(/[≥≤<>%]/g,''));
    return isNaN(num) ? null : num;
  });
  const actuals = years.map(y => {
    const a = row.actual[parseInt(y)];
    if (a === null || a === undefined) return null;
    const num = parseFloat(String(a));
    return isNaN(num) ? null : num;
  });

  const ctx = document.getElementById('chartTrend').getContext('2d');
  if (trendChart) trendChart.destroy();
  trendChart = new Chart(ctx, {
    type: 'line',
    data: {
      labels: years.map(y => `ปี 25${y}`),
      datasets: [
        {
          label: 'เป้าหมาย', data: targets,
          borderColor: '#94a3b8', borderDash: [6,3],
          pointStyle: 'rectRot', pointRadius: 5,
          backgroundColor: 'transparent'
        },
        {
          label: 'ผลจริง', data: actuals,
          borderColor: '#0d5c8f', borderWidth: 2.5,
          pointRadius: 6, pointBackgroundColor: '#0d5c8f',
          backgroundColor: 'rgba(13,92,143,0.08)',
          fill: true, tension: 0.35
        }
      ]
    },
    options: {
      responsive: true, maintainAspectRatio: false,
      plugins: {
        title: { display: true, text: k.label + ' – Target vs Actual', font: { family: 'Sarabun', size: 14 } },
        legend: { position: 'bottom', labels: { font: { family: 'Sarabun' } } }
      },
      scales: {
        x: { ticks: { font: { family: 'Sarabun' } } },
        y: { ticks: { font: { family: 'Sarabun' } } }
      }
    }
  });
}

// chart 3: heatmap
function buildHeatmap() {
  const pillars = ['P1','P2','P3','P4','P5','P6','P7'];
  const years = [66, 67, 68];
  const wrap = document.getElementById('heatmapWrap');

  const getCellClass = (p, y) => {
    const rows = RAW_DATA.filter(r => r.pillar === p);
    const hasActual = rows.filter(r => r.actual[y] !== null && r.actual[y] !== undefined && r.actual[y] !== '');
    if (hasActual.length === 0) return { cls:'hm-na', label:'–' };
    const statuses = hasActual.map(r => evalStatus(r, y));
    const good = statuses.filter(s => s === 'บรรลุ').length;
    const fail = statuses.filter(s => s === 'ไม่บรรลุ').length;
    const total = statuses.length;
    const pct = Math.round((good / total) * 100);
    if (pct >= 70) return { cls:'hm-good', label:`${pct}%` };
    if (pct >= 40) return { cls:'hm-mixed', label:`${pct}%` };
    if (pct >= 1) return { cls:'hm-bad', label:`${pct}%` };
    return { cls:'hm-warn', label:`${pct}%` };
  };

  let html = `<table class="heatmap-table"><thead><tr>
    <th>ยุทธศาสตร์</th>
    <th>ปี 2566</th><th>ปี 2567</th><th>ปี 2568</th>
  </tr></thead><tbody>`;

  pillars.forEach(p => {
    html += `<tr><td class="hm-row-label">${p} – ${PILLAR_META[p].name.substring(0,20)}...</td>`;
    years.forEach(y => {
      const c = getCellClass(p, y);
      html += `<td class="${c.cls}" title="${p} ปี 25${y} : ${c.label} ของตัวชี้วัดที่มีข้อมูล">${c.label}</td>`;
    });
    html += '</tr>';
  });
  html += '</tbody></table>';
  html += `<p style="font-size:12px;color:var(--text-muted);margin-top:8px">
    🟢 ≥70% บรรลุ | 🟣 40–69% | 🔴 1–39% | ⬛ ไม่มีข้อมูล | ตัวเลขแสดงสัดส่วนตัวชี้วัดที่มีข้อมูลและบรรลุเป้าหมาย</p>`;
  wrap.innerHTML = html;
}

// ════════════════════════════════════════════════════════════
// HIGHLIGHTS
// ════════════════════════════════════════════════════════════
function buildHighlights() {
  const strengths = [
    '🏆 <strong>HA รับรอง:</strong> ผ่านการรับรองมาตรฐาน HA ต่อเนื่องทุกปี 2566–2568',
    '💊 <strong>HIV PrEP/PEP:</strong> ผู้เข้ารับบริการที่ได้รับ PrEP/PEP สูง 99–100% ทุกปี (เป้า ≥55–75%)',
    '🏥 <strong>OPD คุณภาพ:</strong> ผ่านรับรอง + ความพึงพอใจ ≥89% + waiting time ดีขึ้นต่อเนื่อง',
    '❤️ <strong>NCD Clinic:</strong> ระดับดีมากปี 2568, เวลารอ 59.6 นาที (ลดจาก 61.5 ปี 66)',
    '💰 <strong>การเงินมั่นคง:</strong> CMI 0.64, Sum Adj-RW 2,581, QR 3.34, Current 3.67 – ทุกตัวชี้เกินเป้า',
    '🔬 <strong>LR/Lab/ER:</strong> ห้องคลอด, ห้องปฏิบัติการ, ห้องฉุกเฉินคุณภาพ ผ่านเกณฑ์ต่อเนื่อง',
    '👥 <strong>บุคลากร:</strong> ความคงอยู่ 97–98%, CQI/R2R เพิ่มเป็น 15 เรื่องปี 2568 (เป้า 5)',
    '🌿 <strong>Green & Clean:</strong> ผ่านเกณฑ์มาตรฐานปี 2568',
    '📊 <strong>HS4:</strong> กระโดดจาก 25% (ปี 66) สู่ 93% (ปี 68) เกินเป้า 80%',
    '💉 <strong>Sepsis:</strong> อัตรา AB ภายใน 1 ชม. ปรับปรุงจาก 68.85% → 90.41% บรรลุเป้าหมาย ≥90%',
  ];
  const opportunities = [
    '⚠️ <strong>TB อัตราตาย:</strong> เกินเกณฑ์ <5% ทั้งปี 66 (7.33%) และปี 68 (11.71%) – จำเป็นต้องมี action plan เร่งด่วน',
    '🚑 <strong>Stroke EMS:</strong> ปี 68 ลดลงเหลือ 12.16% (เป้า 40%) – ต้องพัฒนาระบบส่งต่อ EMS ระดับอำเภอ',
    '⏱️ <strong>Stroke fast tract:</strong> ส่งต่อภายใน 30 นาที ปี 68 = 42.3% (เป้า 60%) – ยังขาดระบบ',
    '🛏️ <strong>อัตราครองเตียง:</strong> 73–76% ต่ำกว่าเป้า >100% – ต้องพัฒนา IPD admission หรือทบทวนเป้าหมาย',
    '💵 <strong>OPD/IPD unit cost:</strong> OPD cost ปี 67–68 สูงเกินเป้า (898/806 บาท vs เป้า 400/394) – ควรวิเคราะห์ต้นทุน',
    '🧪 <strong>HAIT:</strong> รอรับการประเมินทุกปี 66–68 – ควรเร่งกระบวนการประเมิน',
    '🫀 <strong>HIV Adherence ปี 66:</strong> 57.33% (เป้า 90%) แต่ปี 67–68 ดีขึ้นมาก 97.5–98% – ต้องรักษาระดับ',
    '🩺 <strong>HT ควบคุม BP:</strong> ปี 68 = 58.73% (เป้า 70%) ลดลงจากปีก่อน – ต้องทบทวนแนวปฏิบัติ',
    '💡 <strong>Data completeness:</strong> ข้อมูลปี 2567 ขาดหายหลายตัวชี้วัด – ควรพัฒนาระบบรายงานให้ครบถ้วน',
  ];
  const systemic = [
    '🔗 <strong>P1→P2 (ป้องกัน→ลดตาย):</strong> การเข้าถึงคัดกรองกลุ่มเสี่ยง DM/HT สูง → ควบคุมโรคได้ → ลดแทรกซ้อนและตาย',
    '🔗 <strong>P2→P4 (ผลลัพธ์→คุณภาพคลินิก):</strong> Stroke fast tract และ ACS EMS ยังต่ำ → กระทบคุณภาพห้องฉุกเฉินและ IPD',
    '🔗 <strong>P4→P6 (คุณภาพ→การเงิน):</strong> HS4 ดีขึ้น → CMI สูงขึ้น → Sum Adj-RW เพิ่ม → รายได้ UC สูงขึ้น',
    '🔗 <strong>P3→P4 (IT→คุณภาพ):</strong> ระบบสารสนเทศพัสดุ 89% → รองรับ HA และมาตรฐานคุณภาพ',
    '🔗 <strong>P7→P1–P4 (บุคลากร→บริการ):</strong> อัตราคงอยู่ 97% + CQI 15 เรื่อง → รักษาคุณภาพบริการทุกมิติ',
    '🔗 <strong>P5→P1 (ชุมชน→ป้องกัน):</strong> ชุมชนมีส่วนร่วม 100% → ลด DHF, Head injury จากชุมชน',
    '🔗 <strong>P6→P7 (การเงิน→บุคลากร):</strong> เสถียรภาพทางการเงินดี → รองรับการพัฒนาบุคลากรและ CQI อย่างต่อเนื่อง',
  ];

  function buildList(id, items) {
    const el = document.getElementById(id);
    el.innerHTML = items.map(i => `<li><span>•</span><span>${i}</span></li>`).join('');
  }
  buildList('hlStrengths', strengths);
  buildList('hlOpportunities', opportunities);
  buildList('hlSystemic', systemic);
}

// ════════════════════════════════════════════════════════════
// RECOMMENDATIONS
// ════════════════════════════════════════════════════════════
function buildRecommendations() {
  const recs = [
    {
      title: 'จัดทำ Action Plan เร่งด่วนสำหรับ TB อัตราตาย',
      detail: 'อัตราตายวัณโรคปี 2566 = 7.33% และปี 2568 = 11.71% เกินเป้า <5% ต่อเนื่อง ควรตรวจสอบระบบการวินิจฉัยล่าช้า การติดตามรักษา และประสานกับ สสจ./สคร. เพื่อวางมาตรการเชิงรุกภายในไตรมาส 3/2568'
    },
    {
      title: 'พัฒนาระบบ EMS อำเภอสำหรับ Stroke และ ACS',
      detail: 'Stroke EMS < 15% และ Stroke fast tract ส่งต่อ < 43% ต่ำกว่าเป้าอย่างมีนัย ควรบูรณาการร่วมกับ พชอ. ฝึกอบรม อาสาสมัคร และพัฒนา Protocol การส่งต่อ Stroke ให้ทัน 30 นาที'
    },
    {
      title: 'ทบทวนและยกระดับมาตรฐาน HIMSS/HAIT',
      detail: 'ปี 2568 ยังไม่รับการประเมิน HIMSS ระดับ 5 และ HAIT รอการประเมินทุกปี ควรกำหนดเจ้าภาพ ตั้ง project team วางแผนรองรับการประเมินภายใน Q1/2569'
    },
    {
      title: 'วิเคราะห์ต้นทุน OPD/IPD unit cost ที่สูงกว่าเป้า',
      detail: 'OPD unit cost ปี 2568 = 805 บาท (เป้า 394 บาท) อาจสะท้อนการเปลี่ยนแปลงระบบบัญชีหรือ volume ผู้ป่วย ควรให้ฝ่ายการเงินวิเคราะห์และทบทวนเป้าหมายให้สอดคล้องกับบริบทจริง'
    },
    {
      title: 'ยกระดับ KPI ที่มีผลดีเยี่ยมเป็น Best Practice',
      detail: 'HIV PrEP/PEP 99–100%, CQI 15 เรื่อง, Sum Adj-RW เกินเป้าเกือบ 2 เท่า, HS4 93% ควรถ่ายทอดเป็น Best Practice ในระดับจังหวัดและเตรียมนำเสนอต่อ สรพ. เพื่อสะท้อนศักยภาพ รพ.'
    },
    {
      title: 'พัฒนาระบบ Dashboard ติดตาม KPI รายไตรมาส',
      detail: 'ข้อมูลปี 2567 ขาดหายหลายตัวชี้วัด ควรพัฒนาระบบ Dashboard ออนไลน์ที่ผู้รับผิดชอบบันทึกข้อมูลรายไตรมาส และมีการประชุมทบทวน KPI ระดับผู้บริหาร อย่างน้อย 2 ครั้งต่อปี'
    },
    {
      title: 'วางแผนปรับปรุง HT BP control และอัตราครองเตียง',
      detail: 'HT ควบคุม BP ปี 68 = 58.73% ลดลงจาก 69.69% (ปี 67) ควรทบทวน Protocol คลินิก NCD ส่วน อัตราครองเตียง 73–76% ต่ำกว่าเป้า >100% ควรวิเคราะห์แนวโน้มผู้ป่วยนอกพยาบาลและทบทวนเป้าหมาย'
    }
  ];

  const el = document.getElementById('recList');
  el.innerHTML = recs.map((r, i) => `
    <div class="rec-card">
      <div class="rec-num">${i + 1}</div>
      <div class="rec-content">
        <div class="rec-title">${r.title}</div>
        <div class="rec-detail">${r.detail}</div>
      </div>
    </div>
  `).join('');
}

// ════════════════════════════════════════════════════════════
// NAV SCROLL
// ════════════════════════════════════════════════════════════
function scrollToSection(id) {
  const el = document.getElementById(id);
  if (el) {
    const offset = el.getBoundingClientRect().top + window.pageYOffset - 50;
    window.scrollTo({ top: offset, behavior: 'smooth' });
  }
}

// highlight active nav on scroll
const sectionIds = ['summary','pillars','table','charts','highlights','recs'];
window.addEventListener('scroll', () => {
  let current = sectionIds[0];
  sectionIds.forEach(id => {
    const el = document.getElementById(id);
    if (el && el.getBoundingClientRect().top < 120) current = id;
  });
  document.querySelectorAll('.nav-btn').forEach((btn, i) => {
    btn.classList.toggle('active', sectionIds[i] === current);
  });
});

// ════════════════════════════════════════════════════════════
// INIT
// ════════════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => {
  buildSummary();
  buildPillars();
  buildTable(RAW_DATA);
  buildHighlights();
  buildRecommendations();

  // Charts – with CDN fallback check
  if (typeof Chart !== 'undefined') {
    buildStatusChart();
    buildKpiSelector();
    buildTrendChart();
    buildHeatmap();
  } else {
    document.getElementById('charts').innerHTML = `
      <div style="padding:40px;text-align:center;background:#fff;border-radius:12px;box-shadow:var(--shadow)">
        <p style="font-size:16px;color:var(--text-muted)">⚠️ ไม่สามารถโหลด Chart.js ได้ (อาจไม่มีการเชื่อมต่ออินเทอร์เน็ต)<br>กราฟจะแสดงได้เมื่อมีการเชื่อมต่อ</p>
      </div>`;
  }
  document.getElementById('filterCount').textContent = `แสดง ${RAW_DATA.length} รายการ`;
});
</script>
</body>
</html>

Youez - 2016 - github.com/yon3zu
LinuXploit