902 lines
69 KiB
PHP
902 lines
69 KiB
PHP
<?php
|
|
require_once("../core/auth.php");
|
|
require_once("../config/db.php");
|
|
require_once("../core/utils.php");
|
|
require_once("../core/settings.php");
|
|
$current_page = 'ncd_registry.php';
|
|
|
|
// Initialize variables and clinic data
|
|
$t_diag = ['E119', 'I10', 'E785', 'N183'];
|
|
$curr_clinic = $_GET['clinic'] ?? 'ALL';
|
|
$ncd_clinics = [
|
|
'DM' => 'คลินิกเบาหวาน (DM)',
|
|
'HT' => 'คลินิกความดันฯ (HT)',
|
|
'DLP' => 'คลินิกไขมันฯ (DLP)',
|
|
'CKD' => 'คลินิกโรคไต (CKD)'
|
|
];
|
|
|
|
$app_settings = get_app_settings();
|
|
|
|
$clinic_codes = [
|
|
'DM' => $app_settings['ncd_clinic_dm_code'] ?? '001',
|
|
'HT' => $app_settings['ncd_clinic_ht_code'] ?? '002',
|
|
'DLP' => $app_settings['ncd_clinic_dlp_code'] ?? '039',
|
|
'CKD' => $app_settings['ncd_clinic_ckd_code'] ?? '051'
|
|
];
|
|
$clinic_code = $clinic_codes[$curr_clinic] ?? null;
|
|
|
|
// Date parameters for filtering
|
|
$ds1 = $_GET['ds1'] ?? date('Y-m-d', strtotime('-1 month'));
|
|
$ds2 = $_GET['ds2'] ?? date('Y-m-d');
|
|
|
|
// Create reverse mapping for clinic codes to names
|
|
$clinic_code_to_name = [];
|
|
foreach ($clinic_codes as $key => $code) {
|
|
$clinic_code_to_name[$code] = $ncd_clinics[$key];
|
|
}
|
|
|
|
$patientsData = [];
|
|
|
|
// Execute query only if ds1 and ds2 are submitted
|
|
$dashboardData = [];
|
|
if (isset($_GET['ds1']) && isset($_GET['ds2'])) {
|
|
if ($curr_clinic === 'ALL') {
|
|
// Run dashboard queries instead of fetching all patients
|
|
$dashboard_keys = [
|
|
'dashboard_dm_all', 'dashboard_dm_hba1c',
|
|
'dashboard_dm_1', 'dashboard_dm_2', 'dashboard_dm_3', 'dashboard_dm_4', 'dashboard_dm_5', 'dashboard_dm_eye',
|
|
'dashboard_ht_all', 'dashboard_ht_1', 'dashboard_ht_2',
|
|
'dashboard_dlp_all',
|
|
'dashboard_ckd_all', 'dashboard_ckd_1'
|
|
];
|
|
foreach ($dashboard_keys as $k) {
|
|
$dashboardData[$k] = ($k === 'dashboard_ckd_1') ? [] : 0;
|
|
if (!empty($app_settings[$k])) {
|
|
$sql = str_replace(['{ds1}', '{ds2}'], [$ds1, $ds2], $app_settings[$k]);
|
|
if ($res = $conn1->query($sql)) {
|
|
if ($k === 'dashboard_ckd_1') {
|
|
while ($r = $res->fetch_assoc()) {
|
|
$dashboardData[$k][$r['stage']] = $r['total_patient'];
|
|
}
|
|
} else {
|
|
if ($r = $res->fetch_assoc()) {
|
|
$dashboardData[$k] = $r['total_patient'] ?? 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$clinic_cond = "AND cl.clinic = '" . $conn1->real_escape_string($clinic_code) . "'";
|
|
$raw_sql = $app_settings['ncd_sql'] ?? '';
|
|
$sql = str_replace('{clinic_cond}', $clinic_cond, $raw_sql);
|
|
|
|
if ($stmt = $conn1->prepare($sql)) {
|
|
$stmt->bind_param("ss", $ds1, $ds2);
|
|
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
if ($result) {
|
|
while ($row = $result->fetch_assoc()) {
|
|
$patientsData[] = $row;
|
|
}
|
|
}
|
|
$stmt->close();
|
|
}
|
|
|
|
// Bulk fetch additional clinical data (Labs & Screenings)
|
|
if (!empty($patientsData)) {
|
|
$hn_list = array_column($patientsData, 'hn');
|
|
$hn_sql_in = "'" . implode("','", array_map(function($hn) use ($conn1) {
|
|
return $conn1->real_escape_string($hn);
|
|
}, array_unique($hn_list))) . "'";
|
|
|
|
$extraData = [];
|
|
foreach ($hn_list as $hn) {
|
|
$extraData[$hn] = [
|
|
'hba1c' => ['res' => '-', 'date' => '-'],
|
|
'urine_prot' => ['res' => '-', 'date' => '-'],
|
|
'cr' => ['res' => '-', 'date' => '-'],
|
|
'urine_alb' => ['res' => '-', 'date' => '-'],
|
|
'urine_cr' => ['res' => '-', 'date' => '-'],
|
|
'hbsag' => ['res' => '-', 'date' => '-'],
|
|
'anti_hcv' => ['res' => '-', 'date' => '-'],
|
|
'hpv' => ['res' => '-', 'date' => '-'],
|
|
'colon_cancer' => ['res' => '-', 'date' => '-'],
|
|
'ldl' => ['res' => '-', 'date' => '-'],
|
|
'eye_screen' => '-',
|
|
'foot_screen' => '-',
|
|
'oral_cancer' => '-'
|
|
];
|
|
}
|
|
|
|
// 1. LAB Data
|
|
$lab_sql = "SELECT a.hn, b.lab_items_code, b.lab_order_result, a.order_date
|
|
FROM lab_head a
|
|
JOIN lab_order b ON a.lab_order_number=b.lab_order_number
|
|
WHERE a.hn IN ($hn_sql_in)
|
|
AND b.lab_items_code IN ('288','223','224','802','78','226','227','641','656','659','2215','1896','1549','479')
|
|
AND (
|
|
(b.lab_items_code != '1549' AND a.order_date BETWEEN '$ds1' AND '$ds2') OR
|
|
(b.lab_items_code = '1549' AND a.order_date BETWEEN '2024-10-01' AND '2026-06-11')
|
|
)
|
|
ORDER BY a.order_date ASC";
|
|
if ($res_lab = $conn1->query($lab_sql)) {
|
|
while ($r = $res_lab->fetch_assoc()) {
|
|
$hn = $r['hn'];
|
|
$code = $r['lab_items_code'];
|
|
$res = trim($r['lab_order_result'] ?? '');
|
|
$date = (!empty($r['order_date']) && $r['order_date'] != '0000-00-00') ? thai_date2($r['order_date']) : '-';
|
|
|
|
if ($code == '288') $extraData[$hn]['hba1c'] = ['res' => $res, 'date' => $date];
|
|
else if (in_array($code, ['223','224'])) $extraData[$hn]['urine_prot'] = ['res' => $res, 'date' => $date];
|
|
else if (in_array($code, ['802','78','226','227'])) {
|
|
$extraData[$hn]['cr'] = ['res' => $res, 'date' => $date];
|
|
if ($code == '227') $extraData[$hn]['urine_cr'] = ['res' => $res, 'date' => $date];
|
|
}
|
|
else if ($code == '641') $extraData[$hn]['urine_alb'] = ['res' => $res, 'date' => $date];
|
|
else if ($code == '656') $extraData[$hn]['hbsag'] = ['res' => $res, 'date' => $date];
|
|
else if ($code == '659') $extraData[$hn]['anti_hcv'] = ['res' => $res, 'date' => $date];
|
|
else if (in_array($code, ['2215','1896'])) $extraData[$hn]['hpv'] = ['res' => $res, 'date' => $date];
|
|
else if ($code == '1549') $extraData[$hn]['colon_cancer'] = ['res' => $res, 'date' => $date];
|
|
else if ($code == '479') $extraData[$hn]['ldl'] = ['res' => $res, 'date' => $date];
|
|
}
|
|
}
|
|
|
|
// 2. Eye/Foot Screening
|
|
$screen_sql = "SELECT hn, screen_date, do_eye_screen, do_foot_screen
|
|
FROM clinicmember_cormobidity_screen
|
|
WHERE hn IN ($hn_sql_in) AND screen_date BETWEEN '$ds1' AND '$ds2'
|
|
ORDER BY screen_date ASC";
|
|
if ($res_scr = $conn1->query($screen_sql)) {
|
|
while ($r = $res_scr->fetch_assoc()) {
|
|
$hn = $r['hn'];
|
|
$date = (!empty($r['screen_date']) && $r['screen_date'] != '0000-00-00') ? thai_date2($r['screen_date']) : '-';
|
|
if ($r['do_eye_screen'] == 'Y') $extraData[$hn]['eye_screen'] = $date;
|
|
if ($r['do_foot_screen'] == 'Y') $extraData[$hn]['foot_screen'] = $date;
|
|
}
|
|
}
|
|
|
|
// 3. Oral Cancer Screening
|
|
$oral_sql = "SELECT hn, vstdate
|
|
FROM dtmain
|
|
WHERE hn IN ($hn_sql_in) AND tmcode IN ('001480') AND vstdate BETWEEN '$ds1' AND '$ds2'
|
|
ORDER BY vstdate ASC";
|
|
if ($res_oral = $conn1->query($oral_sql)) {
|
|
while ($r = $res_oral->fetch_assoc()) {
|
|
$date = (!empty($r['vstdate']) && $r['vstdate'] != '0000-00-00') ? thai_date2($r['vstdate']) : '-';
|
|
$extraData[$r['hn']]['oral_cancer'] = $date;
|
|
}
|
|
}
|
|
|
|
// Merge back into patientsData
|
|
foreach ($patientsData as &$p) {
|
|
$h = $p['hn'];
|
|
$p['extra'] = $extraData[$h] ?? [];
|
|
}
|
|
unset($p);
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="th">
|
|
<head>
|
|
<?php
|
|
$page_title = "ทะเบียนผู้ป่วย NCD";
|
|
require_once("../components/head.php");
|
|
?>
|
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.1/css/buttons.dataTables.min.css">
|
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
|
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/4.3.0/css/fixedColumns.dataTables.min.css">
|
|
<script src="https://cdn.datatables.net/fixedcolumns/4.3.0/js/dataTables.fixedColumns.min.js"></script>
|
|
<!-- SweetAlert2 -->
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<style>
|
|
body { font-family: 'Sarabun', sans-serif; }
|
|
.glass-card {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
|
|
border-radius: 1rem;
|
|
}
|
|
|
|
.tab-btn.active {
|
|
border-bottom: 2px solid #3b82f6;
|
|
color: #3b82f6;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.ncd-table th {
|
|
position: sticky;
|
|
top: 0;
|
|
background: #f8fafc;
|
|
z-index: 10;
|
|
border-bottom: 2px solid #cbd5e1;
|
|
white-space: nowrap;
|
|
text-align: center !important;
|
|
}
|
|
.ncd-table td {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Sticky first 3 columns */
|
|
.ncd-table th:nth-child(1), .ncd-table td:nth-child(1) { position: sticky; left: 0; z-index: 20; background: inherit; }
|
|
.ncd-table th:nth-child(2), .ncd-table td:nth-child(2) { position: sticky; left: 50px; z-index: 20; background: inherit; }
|
|
.ncd-table th:nth-child(3), .ncd-table td:nth-child(3) { position: sticky; left: 130px; z-index: 20; background: inherit; }
|
|
.ncd-table th:nth-child(1) { z-index: 30; }
|
|
.ncd-table th:nth-child(2) { z-index: 30; }
|
|
.ncd-table th:nth-child(3) { z-index: 30; }
|
|
|
|
/* Shadow for fixed columns */
|
|
.ncd-table td:nth-child(3)::after, .ncd-table th:nth-child(3)::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; right: 0; bottom: -1px; width: 30px;
|
|
transform: translateX(100%);
|
|
transition: box-shadow 0.3s;
|
|
pointer-events: none;
|
|
box-shadow: inset 10px 0 8px -8px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.dashboard-gradient-dm {
|
|
background: linear-gradient(135deg, #4f46e5 0%, #3b82f6 100%);
|
|
}
|
|
.dashboard-gradient-ht {
|
|
background: linear-gradient(135deg, #f97316 0%, #f59e0b 100%);
|
|
}
|
|
.dashboard-gradient-dlp {
|
|
background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
|
|
}
|
|
.dashboard-gradient-ckd {
|
|
background: linear-gradient(135deg, #0d9488 0%, #14b8a6 100%);
|
|
}
|
|
|
|
.progress-bar-bg { background-color: #e2e8f0; border-radius: 9999px; height: 6px; width: 100%; overflow: hidden; margin-top: 8px; }
|
|
.progress-bar-fill-dm { background-color: #4f46e5; height: 100%; border-radius: 9999px; }
|
|
.progress-bar-fill-ht { background-color: #f97316; height: 100%; border-radius: 9999px; }
|
|
.progress-bar-fill-dlp { background-color: #8b5cf6; height: 100%; border-radius: 9999px; }
|
|
.progress-bar-fill-ckd { background-color: #0d9488; height: 100%; border-radius: 9999px; }
|
|
|
|
/* Custom CSS to make DataTables look good with Tailwind */
|
|
.dataTables_wrapper .dataTables_length select, .dataTables_wrapper .dataTables_filter input {
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 0.5rem;
|
|
padding: 0.25rem 0.5rem;
|
|
outline: none;
|
|
}
|
|
.dataTables_wrapper .dataTables_length select:focus, .dataTables_wrapper .dataTables_filter input:focus {
|
|
border-color: #10b981;
|
|
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
|
|
}
|
|
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
|
border-radius: 0.5rem !important;
|
|
padding: 0.25rem 0.75rem !important;
|
|
margin: 0 0.125rem !important;
|
|
border: 1px solid transparent !important;
|
|
}
|
|
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
|
|
background: #10b981 !important;
|
|
color: white !important;
|
|
border: none !important;
|
|
}
|
|
.dataTables_wrapper .dataTables_paginate .paginate_button:hover:not(.current) {
|
|
background: #f1f5f9 !important;
|
|
color: #334155 !important;
|
|
}
|
|
table.dataTable.no-footer {
|
|
border-bottom: 1px solid #cbd5e1;
|
|
}
|
|
/* Fixed columns styling & Shadow */
|
|
.ncd-table th.dtfc-fixed-left {
|
|
background-color: #f1f5f9 !important;
|
|
z-index: 50 !important;
|
|
}
|
|
.ncd-table tr.bg-white td.dtfc-fixed-left {
|
|
background-color: #ffffff !important;
|
|
z-index: 40 !important;
|
|
}
|
|
.ncd-table tr.bg-slate-50 td.dtfc-fixed-left {
|
|
background-color: #f8fafc !important;
|
|
z-index: 40 !important;
|
|
}
|
|
.ncd-table tr:hover td.dtfc-fixed-left {
|
|
background-color: #eff6ff !important;
|
|
}
|
|
/* Add shadow to the rightmost fixed column to show elevation */
|
|
.ncd-table th:nth-child(3).dtfc-fixed-left,
|
|
.ncd-table td:nth-child(3).dtfc-fixed-left {
|
|
box-shadow: 4px 0 6px -2px rgba(0, 0, 0, 0.1) !important;
|
|
border-right: 1px solid #e2e8f0;
|
|
}
|
|
/* Custom Sort Icons using SVG Backgrounds */
|
|
table.dataTable thead th.sorting,
|
|
table.dataTable thead th.sorting_asc,
|
|
table.dataTable thead th.sorting_desc {
|
|
background-image: none !important;
|
|
position: relative;
|
|
}
|
|
table.dataTable thead th.sorting:before,
|
|
table.dataTable thead th.sorting:after,
|
|
table.dataTable thead th.sorting_asc:before,
|
|
table.dataTable thead th.sorting_asc:after,
|
|
table.dataTable thead th.sorting_desc:before,
|
|
table.dataTable thead th.sorting_desc:after {
|
|
display: none !important;
|
|
content: none !important;
|
|
}
|
|
table.dataTable thead th.sorting::after,
|
|
table.dataTable thead th.sorting_asc::after,
|
|
table.dataTable thead th.sorting_desc::after {
|
|
content: "" !important;
|
|
position: absolute;
|
|
right: 12px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 10px;
|
|
height: 14px;
|
|
background-size: contain;
|
|
background-repeat: no-repeat;
|
|
background-position: center;
|
|
display: block !important;
|
|
opacity: 1 !important;
|
|
}
|
|
table.dataTable thead th.sorting::after {
|
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="%23cbd5e1" d="M137.4 41.4c12.5-12.5 32.8-12.5 45.3 0l128 128c9.2 9.2 11.9 22.9 6.9 34.9s-16.6 19.8-29.6 19.8H32c-12.9 0-24.6-7.8-29.6-19.8s-2.2-25.7 6.9-34.9l128-128zm0 429.3l-128-128c-9.2-9.2-11.9-22.9-6.9-34.9s16.6-19.8 29.6-19.8h256c12.9 0 24.6 7.8 29.6 19.8s2.2 25.7-6.9 34.9l-128 128c-12.5 12.5-32.8 12.5-45.3 0z"/></svg>') !important;
|
|
}
|
|
table.dataTable thead th.sorting_asc::after {
|
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="%2310b981" d="M182.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8h256c12.9 0 24.6 7.8 29.6 19.8s-2.2-25.7-6.9-34.9l-128-128z"/></svg>') !important;
|
|
}
|
|
table.dataTable thead th.sorting_desc::after {
|
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="%2310b981" d="M182.6 470.6c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-9.2-9.2-11.9-22.9-6.9-34.9s16.6-19.8 29.6-19.8h256c12.9 0 24.6 7.8 29.6 19.8s-2.2 25.7-6.9 34.9l-128 128z"/></svg>') !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gradient-to-br from-emerald-50 to-emerald-100 text-slate-800 font-sans antialiased min-h-screen flex">
|
|
|
|
<?php require_once("../components/layout_sidebar.php"); ?>
|
|
|
|
<main class="flex-1 flex flex-col min-w-0 h-screen overflow-hidden">
|
|
|
|
<?php require_once("../components/layout_topbar.php"); ?>
|
|
|
|
<div class="flex-1 overflow-y-auto p-4 md:p-6 lg:p-8">
|
|
<div class="max-w-7xl mx-auto">
|
|
|
|
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 gap-4">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-slate-800 flex items-center gap-3">
|
|
<svg class="w-7 h-7 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path></svg>
|
|
ทะเบียนผู้ป่วย<?= ($curr_clinic !== 'ALL') ? " " . ($ncd_clinics[$curr_clinic] ?? $curr_clinic) : " NCD รวม" ?>
|
|
<?php
|
|
$show_dm = ($curr_clinic === 'ALL' || $curr_clinic === 'DM');
|
|
$show_ht = ($curr_clinic === 'ALL' || $curr_clinic === 'HT');
|
|
$show_dlp = ($curr_clinic === 'ALL' || $curr_clinic === 'DLP');
|
|
$show_ckd = ($curr_clinic === 'ALL' || $curr_clinic === 'CKD');
|
|
|
|
if ($curr_clinic !== 'ALL') {
|
|
echo " - " . ($ncd_clinics[$curr_clinic] ?? '');
|
|
}
|
|
?>
|
|
</h1>
|
|
<p class="text-slate-500 mt-1">ระบบลงทะเบียนและติดตามผู้ป่วยโรคเรื้อรัง</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CONDITIONAL VIEW: DASHBOARD OR TABLE -->
|
|
<?php if ($curr_clinic === 'ALL'): ?>
|
|
|
|
<!-- DASHBOARD VIEW -->
|
|
<div id="view-dash" class="space-y-6">
|
|
|
|
<div class="glass-card p-4 flex flex-col md:flex-row justify-between items-center gap-4 border-b border-slate-200">
|
|
<h3 class="font-bold text-slate-800 flex items-center gap-2">
|
|
ตัวชี้วัดสถิติ NCD
|
|
</h3>
|
|
<form method="GET" action="ncd_registry.php" class="flex flex-col sm:flex-row items-center gap-3" onsubmit="return showProcessingModal();">
|
|
<input type="hidden" name="clinic" value="<?= htmlspecialchars($curr_clinic) ?>">
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-sm font-semibold text-slate-600">ตั้งแต่:</label>
|
|
<input type="date" name="ds1" value="<?= htmlspecialchars($ds1) ?>" class="border border-slate-200 rounded px-3 py-1.5 text-sm focus:ring-emerald-500 focus:border-emerald-500 bg-white" required>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-sm font-semibold text-slate-600">ถึง:</label>
|
|
<input type="date" name="ds2" value="<?= htmlspecialchars($ds2) ?>" class="border border-slate-200 rounded px-3 py-1.5 text-sm focus:ring-emerald-500 focus:border-emerald-500 bg-white" required>
|
|
</div>
|
|
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 text-white px-4 py-1.5 rounded-md text-sm font-semibold transition-colors shadow-sm whitespace-nowrap">ประมวลผล (Dashboard)</button>
|
|
</form>
|
|
</div>
|
|
|
|
|
|
<!-- DM Section -->
|
|
<div>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
|
|
<div class="w-3 h-3 rounded-full bg-blue-600"></div>
|
|
คลินิกโรคเบาหวาน (Diabetes Mellitus)
|
|
</h3>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ผู้ป่วยเบาหวานทั้งหมด</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_all'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ได้รับการตรวจ HBA1C</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_hba1c'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">DM ไม่มีโรคร่วม (HbA1C < 7)</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_1'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">DM มีโรคร่วม (HbA1C < 8)</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_2'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ได้รับการตรวจ LDL</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_3'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ได้รับการตรวจไต</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_4'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ได้รับการตรวจเท้า</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_5'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ได้รับการตรวจตา</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dm_eye'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- HT Section -->
|
|
<div>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
|
|
<div class="w-3 h-3 rounded-full bg-emerald-500"></div>
|
|
คลินิกโรคความดันโลหิตสูง (Hypertension)
|
|
</h3>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ผู้ป่วยความดันโลหิตสูงทั้งหมด</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_ht_all'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ความดัน 2 ครั้งสุดท้าย < 139/89</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_ht_1'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ได้รับการตรวจไต</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_ht_2'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DLP Section -->
|
|
<div>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
|
|
<div class="w-3 h-3 rounded-full bg-purple-600"></div>
|
|
คลินิกโรคไขมันในเลือดสูง (Dyslipidemia)
|
|
</h3>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ผู้ป่วยไขมันในเลือดสูงทั้งหมด</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_dlp_all'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CKD Section -->
|
|
<div>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
|
|
<div class="w-3 h-3 rounded-full bg-teal-500"></div>
|
|
คลินิกโรคไตเรื้อรัง (CKD)
|
|
</h3>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-2">ผู้ป่วยโรคไตเรื้อรังทั้งหมด</div>
|
|
<div class="text-3xl font-bold text-slate-800"><?php echo number_format($dashboardData['dashboard_ckd_all'] ?? 0); ?> <span class="text-sm font-normal text-slate-400">คน</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white rounded-xl p-5 border border-slate-200 shadow-sm">
|
|
<div class="text-sm text-slate-500 mb-4 font-bold">จำนวนผู้ป่วยโรคไตแยกตาม Stage</div>
|
|
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3">
|
|
<?php
|
|
$stages = ['1' => 'Stage 1', '2' => 'Stage 2', '3a' => 'Stage 3a', '3b' => 'Stage 3b', '4' => 'Stage 4', '5' => 'Stage 5'];
|
|
foreach ($stages as $st => $st_name):
|
|
$val = isset($dashboardData['dashboard_ckd_1'][$st]) ? $dashboardData['dashboard_ckd_1'][$st] : 0;
|
|
?>
|
|
<div class="bg-slate-50 rounded-lg p-3 border border-slate-100 text-center">
|
|
<div class="text-xs text-slate-500 mb-1 font-semibold"><?php echo $st_name; ?></div>
|
|
<div class="text-xl font-bold text-slate-800"><?php echo number_format($val); ?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<?php else: ?>
|
|
|
|
<!-- TABLE VIEW -->
|
|
<div id="view-table" class="glass-card overflow-hidden">
|
|
<div class="p-4 border-b border-slate-200 flex flex-col xl:flex-row justify-between items-center gap-4 bg-white/50">
|
|
<h3 class="font-bold text-slate-800 flex items-center gap-2">
|
|
ทะเบียนผู้ป่วย<?= ($curr_clinic !== 'ALL') ? " " . ($ncd_clinics[$curr_clinic] ?? $curr_clinic) : " NCD รวม" ?>
|
|
</h3>
|
|
<form method="GET" action="ncd_registry.php" class="flex flex-col sm:flex-row items-center gap-3" onsubmit="return showProcessingModal();">
|
|
<input type="hidden" name="clinic" value="<?= htmlspecialchars($curr_clinic) ?>">
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-sm font-semibold text-slate-600">ตั้งแต่:</label>
|
|
<input type="date" name="ds1" value="<?= htmlspecialchars($ds1) ?>" class="border border-slate-200 rounded px-3 py-1.5 text-sm focus:ring-emerald-500 focus:border-emerald-500 bg-white" required>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-sm font-semibold text-slate-600">ถึง:</label>
|
|
<input type="date" name="ds2" value="<?= htmlspecialchars($ds2) ?>" class="border border-slate-200 rounded px-3 py-1.5 text-sm focus:ring-emerald-500 focus:border-emerald-500 bg-white" required>
|
|
</div>
|
|
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 text-white px-4 py-1.5 rounded-md text-sm font-semibold transition-colors shadow-sm whitespace-nowrap">ประมวลผล (ค้นหา)</button>
|
|
</form>
|
|
</div>
|
|
<div class="overflow-y-auto overflow-x-hidden w-full p-4" style="max-height: calc(100vh - 250px);">
|
|
<table class="ncd-table w-full text-left text-sm" id="ncdTable">
|
|
<thead class="bg-slate-100">
|
|
<tr>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[50px]">ลำดับ</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[80px]">HN</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[150px]">ชื่อ-สกุล</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[140px]">CID</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[100px]">วันเกิด</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[50px]">อายุ</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[60px]">เพศ</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[180px]">ที่อยู่</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[50px]">หมู่</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[100px]">ตำบล</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[100px]">ประเภทบ้าน</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[100px]">เบอร์โทร</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[120px]">รหัสทะเบียน</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[120px]">วันที่ขึ้นทะเบียน</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[120px]">สิทธิการรักษา</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[150px]">โรงพยาบาล</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[100px]">Person ID</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-slate-700 text-center whitespace-nowrap min-w-[80px]">Diag</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[80px] bg-indigo-50 border-l border-indigo-100">HbA1C</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[100px] bg-indigo-50">Urine Protein</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[80px] bg-indigo-50">Cr</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[100px] bg-indigo-50">Urine Albumin</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[100px] bg-indigo-50">Urine Cr</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[80px] bg-indigo-50">HBsAg</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[80px] bg-indigo-50">Anti HCV</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[80px] bg-indigo-50">HPV</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[100px] bg-indigo-50">มะเร็งลำไส้</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-indigo-700 text-center whitespace-nowrap min-w-[80px] bg-indigo-50">LDL</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-amber-700 text-center whitespace-nowrap min-w-[100px] bg-amber-50 border-l border-amber-100">ตรวจตา</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-amber-700 text-center whitespace-nowrap min-w-[100px] bg-amber-50">ตรวจเท้า</th>
|
|
<th class="py-3 pl-3 pr-8 font-semibold text-amber-700 text-center whitespace-nowrap min-w-[120px] bg-amber-50">มะเร็งช่องปาก</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php
|
|
// Helper functions for rendering cells and JSON mapping
|
|
$fmtLabObj = function($data) {
|
|
if (empty($data) || $data['res'] === '-') return '-';
|
|
return $data['res'] . ' (' . $data['date'] . ')';
|
|
};
|
|
|
|
if (!function_exists('fmtLabCell')) {
|
|
function fmtLabCell($data) {
|
|
if(empty($data) || $data['res'] === '-') return '<span class="text-slate-300">-</span>';
|
|
return '<span class="font-bold text-indigo-700">' . htmlspecialchars($data['res']) . '</span> <span class="text-[10px] text-slate-500 block leading-tight">(' . htmlspecialchars($data['date']) . ')</span>';
|
|
}
|
|
}
|
|
if (!function_exists('fmtScrCell')) {
|
|
function fmtScrCell($date) {
|
|
if(empty($date) || $date === '-') return '<span class="text-slate-300">-</span>';
|
|
return '<span class="font-medium text-amber-700">' . htmlspecialchars($date) . '</span>';
|
|
}
|
|
}
|
|
|
|
$no = 1;
|
|
foreach ($patientsData as $row):
|
|
$bg = $no % 2 == 0 ? 'bg-white' : 'bg-slate-50';
|
|
?>
|
|
<tr class="<?php echo $bg; ?> hover:bg-blue-50 border-b border-slate-100 table-row-item">
|
|
<td class="p-3 text-slate-500 text-center"><?php echo $no++; ?></td>
|
|
<td class="p-3 font-medium text-slate-700 hn-col"><?php echo htmlspecialchars($row['hn'] ?? ''); ?></td>
|
|
<td class="p-3 name-col">
|
|
<?php
|
|
$bday_formatted = '';
|
|
if (!empty($row['birthday']) && $row['birthday'] !== '0000-00-00') {
|
|
$time = strtotime($row['birthday']);
|
|
if ($time) {
|
|
$bday_formatted = date('d-m-', $time) . (date('Y', $time) + 543);
|
|
}
|
|
}
|
|
|
|
$ext = $row['extra'] ?? [];
|
|
|
|
$patient_json = htmlspecialchars(json_encode([
|
|
'hn' => $row['hn'] ?? '',
|
|
'name' => $row['pt_name'] ?? '',
|
|
'cid' => $row['cid'] ?? '',
|
|
'birthday' => $bday_formatted,
|
|
'age' => $row['age_y'] ?? '',
|
|
'sex' => $row['sex_name'] ?? '',
|
|
'address' => $row['addrpart'] ?? '',
|
|
'moo' => $row['moopart'] ?? '',
|
|
'tumbon' => $row['tmb_name'] ?? '',
|
|
'house_type' => $row['house_regist_type_id'] ?? '',
|
|
'phone' => $row['mobile_phone_number'] ?? '',
|
|
'member_id' => $row['clinicmember_id'] ?? '',
|
|
'clinic' => $row['clinic_name'] ?? $clinic_code_to_name[$row['clinic'] ?? ''] ?? $row['clinic'] ?? '-',
|
|
'regdate' => (!empty($row['regdate']) && $row['regdate'] != '0000-00-00') ? thai_date2($row['regdate']) : '-',
|
|
'dchdate' => (!empty($row['dchdate']) && $row['dchdate'] != '0000-00-00') ? thai_date2($row['dchdate']) : 'ยังไม่จำหน่าย',
|
|
'pttype' => $row['pttype_name'] ?? '',
|
|
'hospital' => $row['hosp_name'] ?? '',
|
|
'person_id' => $row['person_id'] ?? '',
|
|
'icd10' => $row['icd10'] ?? '',
|
|
'nextdate' => (!empty($row['nextdate']) && $row['nextdate'] != '0000-00-00') ? thai_date2($row['nextdate']) : 'ไม่มีนัด',
|
|
'nexttime' => (!empty($row['nexttime']) && $row['nexttime'] != '00:00:00') ? substr($row['nexttime'], 0, 5) . ' น.' : '-',
|
|
'labs' => [
|
|
'hba1c' => $fmtLabObj($ext['hba1c'] ?? null),
|
|
'urine_prot' => $fmtLabObj($ext['urine_prot'] ?? null),
|
|
'cr' => $fmtLabObj($ext['cr'] ?? null),
|
|
'urine_alb' => $fmtLabObj($ext['urine_alb'] ?? null),
|
|
'urine_cr' => $fmtLabObj($ext['urine_cr'] ?? null),
|
|
'hbsag' => $fmtLabObj($ext['hbsag'] ?? null),
|
|
'anti_hcv' => $fmtLabObj($ext['anti_hcv'] ?? null),
|
|
'hpv' => $fmtLabObj($ext['hpv'] ?? null),
|
|
'colon' => $fmtLabObj($ext['colon_cancer'] ?? null),
|
|
'ldl' => $fmtLabObj($ext['ldl'] ?? null)
|
|
],
|
|
'screen' => [
|
|
'eye' => $ext['eye_screen'] ?? '-',
|
|
'foot' => $ext['foot_screen'] ?? '-',
|
|
'oral' => $ext['oral_cancer'] ?? '-'
|
|
]
|
|
]), ENT_QUOTES, 'UTF-8');
|
|
?>
|
|
<button onclick="showPatientDetails(this)" data-patient="<?php echo $patient_json; ?>"
|
|
class="inline-flex items-center gap-3 group text-blue-600 hover:text-emerald-600 transition-colors cursor-pointer text-left focus:outline-none"
|
|
title="คลิกเพื่อดูรายละเอียดผู้ป่วย">
|
|
<div class="w-8 h-8 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center shrink-0 group-hover:bg-emerald-50 group-hover:text-emerald-500 group-hover:scale-110 transition-all duration-300 shadow-sm border border-blue-100 group-hover:border-emerald-200">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>
|
|
</div>
|
|
<span class="font-bold tracking-tight underline-offset-4 group-hover:underline">
|
|
<?php echo htmlspecialchars($row['pt_name'] ?? ''); ?>
|
|
</span>
|
|
</button>
|
|
</td>
|
|
<td class="p-3 text-slate-600 cid-col"><?php echo htmlspecialchars($row['cid'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500 text-center"><?php echo htmlspecialchars($bday_formatted); ?></td>
|
|
<td class="p-3 text-slate-700 text-center"><?php echo htmlspecialchars($row['age_y'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500 text-center"><?php echo htmlspecialchars($row['sex_name'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500 truncate max-w-[180px]" title="<?php echo htmlspecialchars($row['addrpart'] ?? ''); ?>"><?php echo htmlspecialchars($row['addrpart'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500 text-center"><?php echo htmlspecialchars($row['moopart'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-600"><?php echo htmlspecialchars($row['tmb_name'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500 text-center"><?php echo htmlspecialchars($row['house_regist_type_id'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-600"><?php echo htmlspecialchars($row['mobile_phone_number'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-600"><?php echo htmlspecialchars($row['clinicmember_id'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500 text-center"><?php echo htmlspecialchars($row['regdate'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-700"><?php echo htmlspecialchars($row['pttype_name'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-600"><?php echo htmlspecialchars($row['hosp_name'] ?? ''); ?></td>
|
|
<td class="p-3 text-slate-500"><?php echo htmlspecialchars($row['person_id'] ?? ''); ?></td>
|
|
<td class="p-3 font-bold text-slate-700 text-center"><?php echo htmlspecialchars($row['icd10'] ?? ''); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30 border-l border-indigo-100/50"><?php echo fmtLabCell($ext['hba1c'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['urine_prot'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['cr'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['urine_alb'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['urine_cr'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['hbsag'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['anti_hcv'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['hpv'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['colon_cancer'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-indigo-50/30"><?php echo fmtLabCell($ext['ldl'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-amber-50/30 border-l border-amber-100/50"><?php echo fmtScrCell($ext['eye_screen'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-amber-50/30"><?php echo fmtScrCell($ext['foot_screen'] ?? null); ?></td>
|
|
<td class="p-3 text-center whitespace-nowrap bg-amber-50/30"><?php echo fmtScrCell($ext['oral_cancer'] ?? null); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="p-3 border-t border-slate-200 bg-slate-50 text-xs text-slate-500 flex justify-between items-center">
|
|
<span>ดึงข้อมูลจาก HOSxP Database (<?php echo count($patientsData); ?> รายการ)</span>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<?php require_once("../components/layout_footer.php"); ?>
|
|
</main>
|
|
|
|
<script src="../assets/js/micro-interactions.js"></script>
|
|
<script>
|
|
|
|
$(document).ready(function() {
|
|
// Disabled prototype alert since we are querying real DB now
|
|
// Swal.fire({
|
|
// icon: 'info',
|
|
// ...
|
|
// });
|
|
|
|
$('#ncdTable').DataTable({
|
|
"pageLength": 10,
|
|
"language": {
|
|
"lengthMenu": "แสดง _MENU_ รายการต่อหน้า",
|
|
"zeroRecords": "ไม่พบข้อมูล",
|
|
"info": "แสดงหน้า _PAGE_ จาก _PAGES_",
|
|
"infoEmpty": "ไม่มีข้อมูล",
|
|
"infoFiltered": "(กรองจากทั้งหมด _MAX_ รายการ)",
|
|
"search": "ค้นหา:",
|
|
"paginate": {
|
|
"first": "หน้าแรก",
|
|
"last": "หน้าสุดท้าย",
|
|
"next": "ถัดไป",
|
|
"previous": "ก่อนหน้า"
|
|
}
|
|
},
|
|
"dom": '<"flex flex-col sm:flex-row justify-between items-center mb-4"<"flex items-center gap-4"l><"flex items-center gap-2 text-sm"fB>>rt<"flex flex-col sm:flex-row justify-between items-center mt-4"<"text-sm text-slate-500"i><"text-sm"p>>',
|
|
"buttons": {
|
|
dom: {
|
|
button: {
|
|
className: ''
|
|
}
|
|
},
|
|
buttons: [
|
|
{
|
|
extend: 'excelHtml5',
|
|
text: '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M15.8,20H14L12,16.6L10,20H8.2L11.1,15.5L8.2,11H10L12,14.4L14,11H15.8L12.9,15.5L15.8,20M13,9V3.5L18.5,9H13Z" /></svg>',
|
|
className: 'inline-flex items-center justify-center w-9 h-9 bg-gradient-to-r from-emerald-500 to-teal-600 rounded-lg text-white shadow-sm hover:shadow-md hover:from-emerald-600 hover:to-teal-700 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-1 transition-all duration-200 p-0 ml-2 border-0 cursor-pointer',
|
|
titleAttr: 'ส่งออก Excel',
|
|
title: 'NCD_Registry_Export'
|
|
}
|
|
]
|
|
},
|
|
"scrollX": true,
|
|
"fixedColumns": {
|
|
left: 3
|
|
},
|
|
"ordering": true,
|
|
"order": [[ 1, "asc" ]] // Order by HN (Column index 1)
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<!-- Processing Modal -->
|
|
<div id="processingModal" class="fixed inset-0 z-[100] hidden items-center justify-center bg-slate-900/50 backdrop-blur-sm transition-all duration-300 opacity-0">
|
|
<div class="bg-white rounded-2xl shadow-2xl p-8 max-w-sm w-full mx-4 transform scale-95 transition-all duration-300 flex flex-col items-center border border-emerald-100">
|
|
<!-- Spinner -->
|
|
<div class="relative w-20 h-20 mb-6">
|
|
<div class="absolute inset-0 border-4 border-slate-100 rounded-full"></div>
|
|
<div class="absolute inset-0 border-4 border-emerald-500 rounded-full border-t-transparent animate-spin"></div>
|
|
<div class="absolute inset-0 flex items-center justify-center text-emerald-600">
|
|
<svg class="w-8 h-8 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path></svg>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="text-xl font-bold text-slate-800 mb-2">กำลังประมวลผลข้อมูล...</h3>
|
|
<p class="text-sm text-slate-500 text-center mb-6">กรุณารอสักครู่ ระบบกำลังดึงและรวบรวมข้อมูลผู้ป่วยจากฐานข้อมูล HOSxP</p>
|
|
|
|
<!-- Timer -->
|
|
<div class="bg-emerald-50/50 border border-emerald-100 rounded-xl px-6 py-4 w-full flex justify-center items-center gap-3">
|
|
<svg class="w-5 h-5 text-emerald-600 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
|
<span id="processingTimer" class="font-mono text-2xl font-bold text-emerald-700 tracking-wider">00:00</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let timerInterval;
|
|
let secondsElapsed = 0;
|
|
|
|
function showProcessingModal() {
|
|
const modal = document.getElementById('processingModal');
|
|
const timerDisplay = document.getElementById('processingTimer');
|
|
|
|
secondsElapsed = 0;
|
|
timerDisplay.textContent = '00:00';
|
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('flex');
|
|
// Trigger reflow
|
|
void modal.offsetWidth;
|
|
|
|
modal.classList.remove('opacity-0');
|
|
modal.querySelector('div').classList.remove('scale-95');
|
|
modal.querySelector('div').classList.add('scale-100');
|
|
|
|
timerInterval = setInterval(() => {
|
|
secondsElapsed++;
|
|
const mins = String(Math.floor(secondsElapsed / 60)).padStart(2, '0');
|
|
const secs = String(secondsElapsed % 60).padStart(2, '0');
|
|
timerDisplay.textContent = `${mins}:${secs}`;
|
|
}, 1000);
|
|
return true;
|
|
}
|
|
|
|
function showPatientDetails(btn) {
|
|
const data = JSON.parse(btn.getAttribute('data-patient'));
|
|
|
|
Swal.fire({
|
|
title: '<div class="text-2xl font-bold text-slate-800 flex items-center gap-3 justify-center"><div class="w-10 h-10 rounded-full bg-emerald-100 text-emerald-600 flex items-center justify-center shrink-0"><svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg></div>รายละเอียดผู้ป่วย NCD</div>',
|
|
html: `
|
|
<div class="mt-4 text-left space-y-4">
|
|
<div class="bg-slate-50 rounded-xl p-5 border border-slate-100 shadow-inner grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-4">
|
|
<!-- Section: ข้อมูลบุคคล -->
|
|
<div class="md:col-span-2 border-b border-slate-200 pb-2 mb-2">
|
|
<h4 class="font-bold text-slate-800 flex items-center gap-2"><svg class="w-4 h-4 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg> ข้อมูลส่วนตัว</h4>
|
|
</div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">ชื่อ-สกุล</span><span class="text-sm font-bold text-emerald-700">${data.name}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">เลขบัตร ปชช.</span><span class="text-sm font-medium text-slate-700 tracking-wider">${data.cid}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">อายุ / เพศ</span><span class="text-sm font-medium text-slate-700">${data.age} ปี / ${data.sex} <span class="text-[11px] text-slate-400 font-normal ml-1 bg-white px-1.5 py-0.5 rounded border border-slate-200">เกิด ${data.birthday}</span></span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">เบอร์โทรติดต่อ</span><span class="text-sm font-medium text-slate-700">${data.phone || '-'}</span></div>
|
|
<div class="md:col-span-2 flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">ที่อยู่ปัจจุบัน</span><span class="text-sm font-medium text-slate-700">บ้านเลขที่ ${data.address} หมู่ ${data.moo} ต.${data.tumbon} <span class="text-[11px] bg-slate-200 text-slate-600 px-1.5 py-0.5 rounded">ประเภทบ้าน: ${data.house_type}</span></span></div>
|
|
|
|
<!-- Section: ข้อมูลคลินิกและการรักษา -->
|
|
<div class="md:col-span-2 border-b border-slate-200 pb-2 mt-2 mb-2">
|
|
<h4 class="font-bold text-slate-800 flex items-center gap-2"><svg class="w-4 h-4 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path></svg> ข้อมูลการรับบริการ</h4>
|
|
</div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">HN / Person ID</span><span class="text-sm font-bold text-blue-600 bg-blue-50 px-2 py-0.5 rounded border border-blue-100 w-max">${data.hn} / ${data.person_id}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">รหัสทะเบียน / Diag</span><span class="text-sm font-medium text-slate-700">${data.member_id} / <span class="font-bold text-slate-800">${data.icd10}</span></span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">คลินิกโรคเรื้อรัง</span><span class="text-sm font-bold text-slate-800">${data.clinic}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">สิทธิการรักษา / รพ.</span><span class="text-sm font-medium text-slate-700">${data.pttype} (${data.hospital})</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">วันที่ขึ้นทะเบียน</span><span class="text-sm font-medium text-slate-700">${data.regdate}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">สถานะจำหน่าย</span><span class="text-sm font-medium ${data.dchdate === 'ยังไม่จำหน่าย' ? 'text-emerald-600' : 'text-amber-600'}">${data.dchdate}</span></div>
|
|
|
|
<!-- Section: ผลทางห้องปฏิบัติการ (LAB) -->
|
|
<div class="md:col-span-2 border-b border-slate-200 pb-2 mt-4 mb-2">
|
|
<h4 class="font-bold text-slate-800 flex items-center gap-2"><svg class="w-4 h-4 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path></svg> ผลทางห้องปฏิบัติการ (LAB ล่าสุด)</h4>
|
|
</div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">HbA1C</span><span class="text-sm font-bold text-indigo-700">${data.labs.hba1c}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">Urine Protein</span><span class="text-sm font-bold text-indigo-700">${data.labs.urine_prot}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">Cr</span><span class="text-sm font-bold text-indigo-700">${data.labs.cr}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">Urine Albumin</span><span class="text-sm font-bold text-indigo-700">${data.labs.urine_alb}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">Urine Cr</span><span class="text-sm font-bold text-indigo-700">${data.labs.urine_cr}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">HBsAg</span><span class="text-sm font-bold text-indigo-700">${data.labs.hbsag}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">Anti HCV</span><span class="text-sm font-bold text-indigo-700">${data.labs.anti_hcv}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">HPV</span><span class="text-sm font-bold text-indigo-700">${data.labs.hpv}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">มะเร็งลำไส้</span><span class="text-sm font-bold text-indigo-700">${data.labs.colon}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">LDL</span><span class="text-sm font-bold text-indigo-700">${data.labs.ldl}</span></div>
|
|
|
|
<!-- Section: การตรวจคัดกรอง -->
|
|
<div class="md:col-span-2 border-b border-slate-200 pb-2 mt-2 mb-2">
|
|
<h4 class="font-bold text-slate-800 flex items-center gap-2"><svg class="w-4 h-4 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg> การตรวจคัดกรอง (ล่าสุด)</h4>
|
|
</div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">ตรวจตา</span><span class="text-sm font-bold text-amber-700">${data.screen.eye}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">ตรวจเท้า</span><span class="text-sm font-bold text-amber-700">${data.screen.foot}</span></div>
|
|
<div class="flex flex-col gap-1"><span class="text-xs font-semibold text-slate-500">มะเร็งช่องปาก</span><span class="text-sm font-bold text-amber-700">${data.screen.oral}</span></div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
showCloseButton: true,
|
|
showConfirmButton: true,
|
|
confirmButtonText: 'ปิดหน้าต่าง',
|
|
confirmButtonColor: '#10b981',
|
|
customClass: {
|
|
container: 'backdrop-blur-sm',
|
|
popup: 'rounded-3xl shadow-2xl border border-slate-100 p-2',
|
|
title: 'border-b border-slate-100 pb-4',
|
|
closeButton: 'focus:outline-none hover:text-red-500 transition-colors',
|
|
confirmButton: 'rounded-xl px-6 py-2.5 font-bold shadow-sm'
|
|
},
|
|
width: '42rem'
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|