231 lines
22 KiB
PHP
231 lines
22 KiB
PHP
<!-- TAB 2: ระยะเวลาตามเงื่อนไข (Condition) -->
|
|
<div id="tab-opd-condition" class="tab-content hidden space-y-4 md:space-y-6">
|
|
<form class="opd-condition-filter-form card-premium p-4 bg-gradient-to-r from-slate-50 to-white border-l-4 border-l-indigo-500 shadow-sm flex flex-col gap-3.5 mb-6">
|
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-3">
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">การนัดหมาย</label>
|
|
<select name="appoint" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="">ทั้งหมด</option>
|
|
<option value="1">มาตามนัด</option>
|
|
<option value="3">Walk in (ไม่นัด)</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">จากจุด (Step From)</label>
|
|
<select name="step_from" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="vsttime">1. ยื่นบัตร (Register)</option>
|
|
<option value="service4">2. ซักประวัติ (Screen)</option>
|
|
<option value="service11">3. รอพบแพทย์ (Wait Doctor)</option>
|
|
<option value="service5">4. แพทย์ตรวจ (Doctor Check)</option>
|
|
<option value="service12">5. รับยา (RX)</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">ถึงจุด (Step To)</label>
|
|
<select name="step_to" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="service6" selected>เสร็จสิ้นทั้งหมด (Service 6)</option>
|
|
<option value="service12">5. รับยา (RX)</option>
|
|
<option value="service5">4. แพทย์ตรวจ (Doctor Check)</option>
|
|
<option value="service11">3. รอพบแพทย์ (Wait Doctor)</option>
|
|
<option value="service4">2. ซักประวัติ (Screen)</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">คลินิก</label>
|
|
<select name="clinic_id" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="">ทั้งหมด</option>
|
|
<option value="000">ผู้ป่วยนอก (ทั่วไป)</option><option value="130">คลินิกอายุรกรรม</option>
|
|
<option value="102">คลินิกศัลยกรรม</option><option value="149">คลินิกระบบประสาท</option>
|
|
<option value="168">คลินิกกุมารเวชกรรม</option><option value="220">คลินิกสูติ-นรีเวช</option>
|
|
<option value="073">คลินิกตา</option><option value="072">คลินิกกระดูก</option>
|
|
<option value="111">คลินิกจิตเวช</option><option value="139">คลินิกเบาหวาน</option>
|
|
<option value="140">คลินิกความดัน</option><option value="154">คลินิกไขมัน</option>
|
|
<option value="137">คลินิกไต</option><option value="110">หอบหืด</option>
|
|
<option value="009">ฝากครรภ์</option><option value="227">ทางเดินอาหารและตับ</option>
|
|
<option value="024">คลินิกวัณโรค</option><option value="225">คลินิกอายุรกรรมปอด</option>
|
|
<option value="017">ทันตกรรม</option><option value="132">คลินิกหัวใจ</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">ช่วงเวลา</label>
|
|
<select name="time_period" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="">ทั้งหมด (08:00 - 16:00)</option>
|
|
<option value="morning">เช้า (08:00 - 12:00)</option>
|
|
<option value="afternoon">บ่าย (12:01 - 16:00)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-3 items-end">
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">สถานะ Lab</label>
|
|
<select name="lab_status" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="">ทั้งหมด</option>
|
|
<option value="1">มี Lab</option>
|
|
<option value="0">ไม่มี Lab</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">สถานะ X-ray</label>
|
|
<select name="xray_status" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none">
|
|
<option value="">ทั้งหมด</option>
|
|
<option value="1">มี X-ray</option>
|
|
<option value="0">ไม่มี X-ray</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">ตั้งแต่วันที่</label>
|
|
<input type="date" name="start_date" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none" value="<?= date('Y-m-01') ?>">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">ถึงวันที่</label>
|
|
<input type="date" name="end_date" class="w-full bg-white border border-slate-300 rounded-lg p-2 text-xs md:text-sm outline-none" value="<?= date('Y-m-d') ?>">
|
|
</div>
|
|
<button type="submit" class="w-full bg-indigo-600 hover:bg-indigo-700 active:scale-[0.98] text-white font-bold py-2 px-4 rounded-lg h-[38px] flex items-center justify-center transition-all shadow-md shadow-indigo-500/10">
|
|
<i class="fa-solid fa-magnifying-glass mr-2"></i> ค้นหาเงื่อนไข
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="card-premium p-4 md:p-8 bg-white relative overflow-visible mb-6">
|
|
<div class="absolute top-0 left-0 w-1 md:w-1.5 h-full bg-gradient-to-b from-indigo-500 to-purple-500 rounded-l-xl"></div>
|
|
<h3 class="font-bold text-slate-800 mb-4 md:mb-8 flex flex-col md:flex-row items-start md:items-center text-base md:text-lg">
|
|
<div class="flex items-center mb-2 md:mb-0"><div class="w-6 h-6 md:w-8 md:h-8 rounded bg-indigo-100 text-indigo-600 flex items-center justify-center mr-2 md:mr-3"><i class="fa-solid fa-filter"></i></div>ผลลัพธ์เส้นทางระยะเวลารอคอย (ตามเงื่อนไข)</div>
|
|
<span class="md:ml-auto bg-slate-100 text-slate-500 px-5 py-2 rounded-full text-xs md:text-sm font-extrabold shadow-md opd-total-time-display-cond">- นาที</span>
|
|
</h3>
|
|
<div class="flow-container flex flex-col md:flex-row justify-between items-center py-2 md:py-4 relative">
|
|
<div class="flow-line hidden md:block" style="background: linear-gradient(90deg, #6366f1, #a855f7);"></div>
|
|
<div class="flex md:flex-col items-center justify-between md:justify-start w-full md:w-auto mb-3 md:mb-0 bg-slate-50 md:bg-transparent p-3 md:p-0 rounded-lg border border-slate-100 md:border-transparent">
|
|
<div class="flex items-center md:flex-col"><div class="w-9 h-9 md:w-16 md:h-16 bg-white border-[2px] md:border-[3px] border-blue-200 text-blue-500 rounded-full shadow-md flex items-center justify-center md:mb-3 mr-3 md:mr-0 z-10"><i class="fa-solid fa-id-card text-xs md:text-2xl"></i></div><span class="text-xs md:text-sm font-extrabold text-slate-700 bg-white md:px-2 z-10">ยื่นบัตร</span></div>
|
|
<span class="t-step-1-cond text-xs font-bold text-blue-700 bg-blue-50 px-2 md:px-3 py-0.5 rounded-full md:mt-1 border border-blue-100 z-10">- นาที</span>
|
|
</div>
|
|
<div class="flex md:flex-col items-center justify-between md:justify-start w-full md:w-auto mb-3 md:mb-0 bg-slate-50 md:bg-transparent p-3 md:p-0 rounded-lg border border-slate-100 md:border-transparent">
|
|
<div class="flex items-center md:flex-col"><div class="w-9 h-9 md:w-16 md:h-16 bg-white border-[2px] md:border-[3px] border-indigo-200 text-indigo-500 rounded-full shadow-md flex items-center justify-center md:mb-3 mr-3 md:mr-0 z-10"><i class="fa-solid fa-clipboard-list text-xs md:text-2xl"></i></div><span class="text-xs md:text-sm font-extrabold text-slate-700 bg-white md:px-2 z-10">ซักประวัติ</span></div>
|
|
<span class="t-step-2-cond text-xs font-bold text-indigo-700 bg-indigo-50 px-2 md:px-3 py-0.5 rounded-full md:mt-1 border border-indigo-100 z-10">- นาที</span>
|
|
</div>
|
|
<div class="flex md:flex-col items-center justify-between md:justify-start w-full md:w-auto mb-3 md:mb-0 bg-slate-50 md:bg-transparent p-3 md:p-0 rounded-lg border border-slate-100 md:border-transparent">
|
|
<div class="flex items-center md:flex-col"><div class="w-10 h-10 md:w-20 md:h-20 bg-gradient-to-br from-orange-500 to-amber-400 text-white border-2 md:border-4 rounded-full shadow-md flex items-center justify-center md:mb-3 mr-3 md:mr-0 z-10"><i class="fa-solid fa-user-clock text-sm md:text-3xl"></i></div><span class="text-xs md:text-sm font-extrabold text-slate-700 bg-white md:px-2 z-10">รอพบแพทย์</span></div>
|
|
<span class="t-step-3-cond text-xs font-bold text-orange-700 bg-orange-50 px-2 md:px-3 py-0.5 rounded-full md:mt-1 border border-orange-100 z-10">- นาที</span>
|
|
</div>
|
|
<div class="flex md:flex-col items-center justify-between md:justify-start w-full md:w-auto mb-3 md:mb-0 bg-slate-50 md:bg-transparent p-3 md:p-0 rounded-lg border border-slate-100 md:border-transparent">
|
|
<div class="flex items-center md:flex-col"><div class="w-9 h-9 md:w-16 md:h-16 bg-white border-[2px] md:border-[3px] border-teal-200 text-teal-500 rounded-full shadow-md flex items-center justify-center md:mb-3 mr-3 md:mr-0 z-10"><i class="fa-solid fa-user-doctor text-xs md:text-2xl"></i></div><span class="text-xs md:text-sm font-extrabold text-slate-700 bg-white md:px-2 z-10">แพทย์ตรวจ</span></div>
|
|
<span class="t-step-4-cond text-xs font-bold text-teal-700 bg-teal-50 px-2 md:px-3 py-0.5 rounded-full md:mt-1 border border-teal-100 z-10">- นาที</span>
|
|
</div>
|
|
<div class="flex md:flex-col items-center justify-between md:justify-start w-full md:w-auto mb-3 md:mb-0 bg-slate-50 md:bg-transparent p-3 md:p-0 rounded-lg border border-slate-100 md:border-transparent">
|
|
<div class="flex items-center md:flex-col"><div class="w-9 h-9 md:w-16 md:h-16 bg-white border-[2px] md:border-[3px] border-emerald-200 text-emerald-500 rounded-full shadow-md flex items-center justify-center md:mb-3 mr-3 md:mr-0 z-10"><i class="fa-solid fa-pills text-xs md:text-2xl"></i></div><span class="text-xs md:text-sm font-extrabold text-slate-700 bg-white md:px-2 z-10">รับยากลับบ้าน</span></div>
|
|
<span class="t-step-5-cond text-xs font-bold text-emerald-700 bg-emerald-50 px-2 md:px-3 py-0.5 rounded-full md:mt-1 border border-emerald-100 z-10">- นาที</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="font-extrabold text-slate-800 text-sm md:text-base mt-6 mb-3 border-l-4 border-indigo-500 pl-3">ระยะเวลารอคอยแต่ละคลินิกเฉลี่ย</h3>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-3 md:gap-4 mb-6" id="clinic-wait-cards-opd-cond">
|
|
<div class="col-span-full text-center text-slate-400 py-8 bg-white rounded-xl border border-dashed border-slate-300 font-medium">รอการค้นหาข้อมูล...</div>
|
|
</div>
|
|
|
|
<div class="card-premium p-4 md:p-6 mb-6">
|
|
<h3 class="font-extrabold text-slate-800 text-sm md:text-base mb-4 md:mb-6 flex items-center justify-between">
|
|
<span class="flex items-center"><i class="fa-solid fa-chart-area text-indigo-500 mr-2"></i> แนวโน้มระยะเวลารอคอย OPD (ตามเงื่อนไข)</span>
|
|
</h3>
|
|
<div class="h-[300px] md:h-[400px] w-full flex items-center justify-center bg-slate-50 rounded-lg border border-dashed border-slate-200" id="opdWaitTimeChartTabCond_container">
|
|
<span class="text-slate-400 font-medium">รอการค้นหาข้อมูล...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function fetchOpdConditionRealData(formElement) {
|
|
let params = new URLSearchParams(new FormData(formElement));
|
|
params.append('ajax_opd_cond', '1');
|
|
|
|
var overlay = document.getElementById('data-processing-overlay');
|
|
if (overlay) { overlay.classList.remove('hidden'); overlay.classList.add('flex'); }
|
|
|
|
try {
|
|
const response = await fetch('?' + params.toString(), { method: 'GET' });
|
|
const text = await response.text();
|
|
let data = {};
|
|
if (text.includes('---AJAX_OPD_COND_START---')) { data = JSON.parse(text.split('---AJAX_OPD_COND_START---')[1].split('---AJAX_OPD_COND_END---')[0]); }
|
|
else { throw new Error("รูปแบบข้อมูลผิดพลาด"); }
|
|
|
|
// --- SQL Logs ---
|
|
if (data.sql_logs) {
|
|
let hasError = false; let errorDetails = [];
|
|
const logContent = document.getElementById('sql-log-content');
|
|
if (logContent) {
|
|
if (data.sql_logs.length > 0) {
|
|
logContent.innerHTML = data.sql_logs.map((log, i) => {
|
|
if (log.status === 'Error') {
|
|
hasError = true; errorDetails.push(log.name);
|
|
return `<div class="mb-3 p-3 border-l-4 border-red-500 bg-red-900/20 text-red-300 rounded-r"><div class="font-bold text-red-400 mb-1">[${i+1}] ❌ ${log.name}</div><div class="mb-2 text-red-200">Error: ${log.error}</div><div class="text-slate-500 text-[10px] break-all">${log.sql}</div></div>`;
|
|
} else {
|
|
return `<div class="mb-3 p-3 border-l-4 border-emerald-500 bg-emerald-900/20 text-emerald-300 rounded-r"><div class="font-bold text-emerald-400 mb-1">[${i+1}] ✅ ${log.name}</div><div class="text-slate-500 text-[10px] break-all">${log.sql}</div></div>`;
|
|
}
|
|
}).join('');
|
|
} else { logContent.innerHTML = '<div class="text-slate-500 text-center py-4">ไม่มี Log บันทึก</div>'; }
|
|
}
|
|
if (data.debug_enabled && hasError) alert("⚠️ พบข้อผิดพลาดในการดึงข้อมูล (SQL Error):\n- " + errorDetails.join("\n- "));
|
|
}
|
|
|
|
// Update DOM Elements
|
|
const safeSet = (selector, text) => { document.querySelectorAll(selector).forEach(el => el.innerText = text); };
|
|
|
|
safeSet('.opd-total-time-display-cond', (data.flowchart.custom_total > 0) ? `ผลรวมช่วงเวลาที่เลือก ${data.flowchart.custom_total.toFixed(2)} นาที` : '- นาที');
|
|
safeSet('.t-step-1-cond', (data.flowchart.step1 > 0) ? `${data.flowchart.step1.toFixed(2)} นาที` : '- นาที');
|
|
safeSet('.t-step-2-cond', (data.flowchart.step2 > 0) ? `${data.flowchart.step2.toFixed(2)} นาที` : '- นาที');
|
|
safeSet('.t-step-3-cond', (data.flowchart.step3 > 0) ? `${data.flowchart.step3.toFixed(2)} นาที` : '- นาที');
|
|
safeSet('.t-step-4-cond', (data.flowchart.step4 > 0) ? `${data.flowchart.step4.toFixed(2)} นาที` : '- นาที');
|
|
safeSet('.t-step-5-cond', (data.flowchart.step5 > 0) ? `${data.flowchart.step5.toFixed(2)} นาที` : '- นาที');
|
|
|
|
// Clinic Grid
|
|
const clinicIconsDb = { 'ผู้ป่วยนอก (ทั่วไป)': 'fa-hospital-user', 'อายุรกรรม': 'fa-stethoscope', 'ศัลยกรรม': 'fa-syringe', 'กุมารเวชกรรม': 'fa-child-reaching', 'สูติ-นรีเวช': 'fa-person-pregnant', 'ตา': 'fa-eye', 'กระดูก': 'fa-bone', 'จิตเวช': 'fa-brain', 'ทันตกรรม': 'fa-tooth' };
|
|
const el = document.getElementById('clinic-wait-cards-opd-cond');
|
|
if(el) {
|
|
if (data.clinics_wait && data.clinics_wait.length > 0) {
|
|
el.innerHTML = data.clinics_wait.map(c => {
|
|
let bgClass = c.wait >= 120 ? 'bg-gradient-to-br from-rose-500 via-red-500 to-pink-500' : 'bg-gradient-to-br from-indigo-500 via-purple-500 to-fuchsia-600';
|
|
let icon = clinicIconsDb[c.name] || 'fa-user-doctor';
|
|
return `<div class="rounded-xl p-3 md:p-4 shadow-md ${bgClass} transition-all duration-300 hover:scale-[1.03] hover:shadow-lg relative overflow-hidden group"><div class="absolute -right-4 -top-4 w-16 h-16 rounded-full bg-white/10 blur-xl group-hover:bg-white/20 transition-all"></div><div class="flex justify-between items-start mb-1 relative z-10"><p class="text-xs md:text-sm font-bold truncate pr-2 text-white">${c.name}</p><div class="w-6 h-6 md:w-8 md:h-8 rounded-full bg-white/25 text-white flex items-center justify-center shrink-0"><i class="fa-solid ${icon} text-[10px] md:text-xs"></i></div></div><h3 class="text-2xl md:text-3xl font-black mt-2.5 leading-none text-white drop-shadow-md relative z-10 flex items-end">${c.wait.toFixed(2)} <span class="text-[10px] md:text-xs font-semibold ml-1 mb-0.5 text-white/80">นาที</span></h3></div>`;
|
|
}).join('');
|
|
} else { el.innerHTML = '<div class="col-span-full text-center text-slate-500 py-8 bg-white rounded-xl border border-dashed border-slate-300 font-medium">ไม่พบข้อมูลคลินิกที่เข้าเงื่อนไข</div>'; }
|
|
}
|
|
|
|
// Trend Chart
|
|
const chartId = 'opdWaitTimeChartTabCond';
|
|
const container = document.getElementById(chartId + '_container');
|
|
if (container && data.trend_labels && data.trend_labels.length > 0) {
|
|
container.innerHTML = `<canvas id="${chartId}"></canvas>`;
|
|
const ctx = document.getElementById(chartId).getContext('2d');
|
|
let grad = ctx.createLinearGradient(0, 0, 0, 400);
|
|
grad.addColorStop(0, 'rgba(99, 102, 241, 0.45)'); grad.addColorStop(1, 'rgba(99, 102, 241, 0.0)');
|
|
if(window.appCharts && window.appCharts[chartId]) window.appCharts[chartId].destroy();
|
|
window.appCharts[chartId] = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: data.trend_labels,
|
|
datasets: [
|
|
{ label: 'เวลาเฉลี่ย (นาที)', data: data.trend_wait, borderColor: '#6366f1', backgroundColor: grad, borderWidth: 3, pointBackgroundColor: '#ffffff', pointBorderColor: '#6366f1', pointBorderWidth: 2, fill: true, tension: 0.4, datalabels: { anchor: 'end', align: 'top', color: '#4f46e5', font: { weight: 'bold' }, formatter: v => Number(v).toFixed(1) } },
|
|
{ label: 'จำนวนผู้ป่วย (ราย)', data: data.trend_pt, borderColor: 'transparent', backgroundColor: '#ec4899', pointBackgroundColor: '#ec4899', pointBorderColor: 'transparent', pointRadius: 0, hoverRadius: 0, pointStyle: 'circle', datalabels: { anchor: 'center', align: 'bottom', offset: 12, color: '#ec4899', font: { weight: 'bold', size: 9 }, formatter: v => v.toLocaleString() + ' ราย' } }
|
|
]
|
|
},
|
|
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'bottom', labels: { usePointStyle: true } } }, scales: { x: { grid: { display: false } }, y: { display: false, min: 0, suggestedMax: Math.max(...data.trend_wait, 0) * 1.5 } }, layout: { padding: { top: 20 } } }
|
|
});
|
|
} else if(container) { container.innerHTML = '<span class="text-slate-400 font-medium"><i class="fa-solid fa-box-open mr-2"></i>ไม่พบข้อมูลในช่วงเวลาที่เลือก</span>'; }
|
|
|
|
} catch (err) {
|
|
console.error("Fetch Condition Data Error:", err);
|
|
alert("เกิดข้อผิดพลาดในการดึงข้อมูลเงื่อนไข\nรายละเอียด: " + err.message);
|
|
} finally {
|
|
if (overlay) { setTimeout(() => { overlay.classList.remove('flex'); overlay.classList.add('hidden'); }, 500); }
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const form = document.querySelector('.opd-condition-filter-form');
|
|
if (form) {
|
|
form.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
fetchOpdConditionRealData(e.target);
|
|
});
|
|
}
|
|
});
|
|
</script> |