216 lines
9.9 KiB
PHP
216 lines
9.9 KiB
PHP
<?php
|
|
// views/dashboard/executive.php
|
|
?>
|
|
<div class="mb-6 flex justify-between items-end flex-wrap gap-4">
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-slate-800 dark:text-white">การวิเคราะห์ระบบ (Executive Dashboard)</h2>
|
|
<p class="text-slate-500 dark:text-slate-400">ดูสถิติประสิทธิภาพและตัวชี้วัด (KPI) ของหน่วยงาน</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<select class="glass-input text-sm py-2 px-3" id="periodSelect" onchange="loadExecutiveData()">
|
|
<option value="today">วันนี้</option>
|
|
<option value="week">สัปดาห์นี้</option>
|
|
<option value="month">เดือนนี้</option>
|
|
</select>
|
|
<button class="btn-primary-glass py-2 px-4 text-sm font-medium flex items-center">
|
|
<i class="fa-solid fa-file-export me-2"></i> Export CSV
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
|
<div class="glass-card p-6 border-t-4 border-t-emerald-500">
|
|
<h6 class="text-sm font-medium text-slate-500 dark:text-slate-400 mb-1">SLA Compliance (< 15 นาที)</h6>
|
|
<h2 class="text-4xl font-bold text-slate-800 dark:text-white" id="slaPercent">0%</h2>
|
|
<div class="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2 mt-4 overflow-hidden">
|
|
<div class="bg-emerald-500 h-2 rounded-full" id="slaBar" style="width: 0%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="glass-card p-6 border-t-4 border-t-blue-500">
|
|
<h6 class="text-sm font-medium text-slate-500 dark:text-slate-400 mb-1">เวลารอคอยเฉลี่ย (Wait Time)</h6>
|
|
<h2 class="text-4xl font-bold text-slate-800 dark:text-white"><span id="avgWaitTime">0</span> <span class="text-xl text-slate-500">นาที</span></h2>
|
|
<p class="text-xs text-red-500 mt-2"><i class="fa-solid fa-arrow-trend-up"></i> ช้าลงจากเมื่อวาน 2 นาที</p>
|
|
</div>
|
|
<div class="glass-card p-6 border-t-4 border-t-purple-500">
|
|
<h6 class="text-sm font-medium text-slate-500 dark:text-slate-400 mb-1">เวลาให้บริการเฉลี่ย (Service Time)</h6>
|
|
<h2 class="text-4xl font-bold text-slate-800 dark:text-white"><span id="avgServiceTime">0</span> <span class="text-xl text-slate-500">นาที</span></h2>
|
|
<p class="text-xs text-green-500 mt-2"><i class="fa-solid fa-arrow-trend-down"></i> เร็วขึ้นจากเมื่อวาน 1 นาที</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<div class="glass-card p-6">
|
|
<h5 class="font-bold text-slate-800 dark:text-white mb-4">แนวโน้มงาน 7 วันย้อนหลัง (Trend)</h5>
|
|
<div class="relative h-72">
|
|
<canvas id="trendChart"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="glass-card p-6">
|
|
<h5 class="font-bold text-slate-800 dark:text-white mb-4">ภาระงานแยกตามแผนก (Workload by Dept)</h5>
|
|
<div class="relative h-72">
|
|
<canvas id="deptChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="glass-card overflow-hidden mb-6">
|
|
<div class="p-6 border-b border-slate-200 dark:border-slate-700">
|
|
<h5 class="font-bold text-slate-800 dark:text-white">สถิติรายบุคคล (Staff Performance)</h5>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-left border-collapse">
|
|
<thead>
|
|
<tr class="bg-slate-100/50 dark:bg-slate-800/50 text-slate-500 dark:text-slate-400 text-xs uppercase tracking-wider">
|
|
<th class="p-4 font-medium border-b border-slate-200 dark:border-slate-700">พนักงาน</th>
|
|
<th class="p-4 font-medium border-b border-slate-200 dark:border-slate-700 text-center">รับงานทั้งหมด</th>
|
|
<th class="p-4 font-medium border-b border-slate-200 dark:border-slate-700 text-center">สำเร็จ</th>
|
|
<th class="p-4 font-medium border-b border-slate-200 dark:border-slate-700 text-center">ยกเลิก/ปฏิเสธ</th>
|
|
<th class="p-4 font-medium border-b border-slate-200 dark:border-slate-700 text-center">เวลาบริการเฉลี่ย</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100 dark:divide-slate-800 text-sm" id="staffPerformanceTable">
|
|
<tr>
|
|
<td colspan="5" class="p-8 text-center text-slate-500"><i class="fa-solid fa-spinner fa-spin me-2"></i>กำลังโหลด...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let trendChartInstance = null;
|
|
let deptChartInstance = null;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadExecutiveData();
|
|
});
|
|
|
|
function loadExecutiveData() {
|
|
const period = document.getElementById('periodSelect').value;
|
|
|
|
fetch(`api/executive.php?period=${period}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Update KPI Cards
|
|
const kpi = data.kpi;
|
|
document.getElementById('slaPercent').textContent = kpi.sla_percent + '%';
|
|
document.getElementById('slaBar').style.width = kpi.sla_percent + '%';
|
|
document.getElementById('avgWaitTime').textContent = kpi.avg_wait_time;
|
|
document.getElementById('avgServiceTime').textContent = kpi.avg_service_time;
|
|
|
|
// Update Charts
|
|
initCharts(data.charts);
|
|
|
|
// Update Staff Performance Table
|
|
renderStaffPerformance(data.staff_performance);
|
|
}
|
|
})
|
|
.catch(error => console.error('Error fetching dashboard data:', error));
|
|
}
|
|
|
|
function renderStaffPerformance(staffData) {
|
|
const tbody = document.getElementById('staffPerformanceTable');
|
|
tbody.innerHTML = '';
|
|
|
|
if (!staffData || staffData.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" class="p-8 text-center text-slate-500">ไม่มีข้อมูลในช่วงเวลานี้</td></tr>';
|
|
return;
|
|
}
|
|
|
|
staffData.forEach(staff => {
|
|
const total = staff.total_jobs || 0;
|
|
const completed = staff.completed_jobs || 0;
|
|
const cancelled = staff.cancelled_jobs || 0;
|
|
const avgTime = staff.avg_service_time ? Math.round(staff.avg_service_time) : 0;
|
|
|
|
tbody.innerHTML += `
|
|
<tr class="hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors">
|
|
<td class="p-4 font-medium text-slate-800 dark:text-slate-200">
|
|
<div class="flex items-center gap-3">
|
|
<img src="api/avatar.php?name=${encodeURIComponent(staff.full_name)}" class="w-8 h-8 rounded-full border border-slate-200 dark:border-slate-700">
|
|
${staff.full_name}
|
|
</div>
|
|
</td>
|
|
<td class="p-4 text-center font-bold text-blue-600 dark:text-blue-400">${total}</td>
|
|
<td class="p-4 text-center font-bold text-emerald-600 dark:text-emerald-400">${completed}</td>
|
|
<td class="p-4 text-center font-bold text-red-500 dark:text-red-400">${cancelled}</td>
|
|
<td class="p-4 text-center font-bold text-purple-600 dark:text-purple-400">${avgTime} นาที</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
}
|
|
|
|
function initCharts(chartData) {
|
|
// Determine text colors based on theme
|
|
const isDark = document.documentElement.classList.contains('dark');
|
|
const textColor = isDark ? '#cbd5e1' : '#475569';
|
|
const gridColor = isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)';
|
|
|
|
// Trend Chart
|
|
const ctxTrend = document.getElementById('trendChart').getContext('2d');
|
|
|
|
if (trendChartInstance) {
|
|
trendChartInstance.destroy();
|
|
}
|
|
|
|
let gradient = ctxTrend.createLinearGradient(0, 0, 0, 400);
|
|
gradient.addColorStop(0, 'rgba(16, 185, 129, 0.5)'); // emerald-500
|
|
gradient.addColorStop(1, 'rgba(16, 185, 129, 0)');
|
|
|
|
trendChartInstance = new Chart(ctxTrend, {
|
|
type: 'line',
|
|
data: {
|
|
labels: chartData.trend.labels,
|
|
datasets: [{
|
|
label: 'จำนวนงาน',
|
|
data: chartData.trend.data,
|
|
borderColor: '#10b981',
|
|
backgroundColor: gradient,
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: { beginAtZero: true, grid: { color: gridColor }, ticks: { color: textColor } },
|
|
x: { grid: { display: false }, ticks: { color: textColor } }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Department Chart
|
|
const ctxDept = document.getElementById('deptChart').getContext('2d');
|
|
|
|
if (deptChartInstance) {
|
|
deptChartInstance.destroy();
|
|
}
|
|
|
|
deptChartInstance = new Chart(ctxDept, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: chartData.departments.labels,
|
|
datasets: [{
|
|
label: 'จำนวนงาน (Job)',
|
|
data: chartData.departments.data,
|
|
backgroundColor: '#3b82f6',
|
|
borderRadius: 6
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: { beginAtZero: true, grid: { color: gridColor }, ticks: { color: textColor } },
|
|
x: { grid: { display: false }, ticks: { color: textColor } }
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</script>
|