Files
gravity/Dashboard ลดแออัด-ลดรอคอย/views/tab_opd_2_condition2.php
T
2026-09-16 23:20:08 +07:00

282 lines
21 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="2">ไม่มาตามนัด</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)</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>ยื่นบัตร</option>
<option>ซักประวัติ</option>
<option>รอพบแพทย์</option>
<option>แพทย์ตรวจ</option>
<option>รับยากลับบ้าน</option>
</select></div>
<div><label class="block text-[10px] md:text-xs font-bold text-slate-600 mb-1">ถึงจุด (Step)</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>รับยากลับบ้าน</option>
<option>แพทย์ตรวจ</option>
<option>รอพบแพทย์</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="130">อายุรกรรม</option>
<option value="102">ศัลยกรรม</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="">ทั้งหมด</option>
<option value="morning">เช้า</option>
<option value="afternoon">บ่าย</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("รูปแบบข้อมูลผิดพลาด");
}
// Update DOM Elements
const safeSet = (selector, text) => {
document.querySelectorAll(selector).forEach(el => el.innerText = text);
};
safeSet('.opd-total-time-display-cond', (data.flowchart.total > 0) ? `เฉลี่ยรวม ${data.flowchart.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-stethoscope',
'ศัลยกรรม': 'fa-syringe',
'กุมารเวชกรรม': 'fa-child-reaching',
'สูติ-นรีเวช': 'fa-person-pregnant',
'ตา': 'fa-eye',
'กระดูก': 'fa-bone'
};
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>