432 lines
24 KiB
PHP
432 lines
24 KiB
PHP
<?php
|
|
session_start();
|
|
require_once '../includes/db.php';
|
|
|
|
if (!isset($_SESSION['admin_logged_in'])) {
|
|
header("Location: login.php");
|
|
exit;
|
|
}
|
|
|
|
$is_superadmin = ($_SESSION['admin_role'] ?? '') === 'superadmin';
|
|
|
|
$stmt = $pdo_bot->query("SELECT * FROM line_staff_register ORDER BY date DESC");
|
|
$staff_list = $stmt->fetchAll();
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="th">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Staff List - KSH-STAFF</title>
|
|
<link rel="icon" type="image/png" href="../assets/img/logo.png">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
|
|
<!-- DataTables & Buttons CSS -->
|
|
<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://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
|
|
|
<!-- JS for Excel Export -->
|
|
<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://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
<link rel="stylesheet" href="../assets/css/datatables_custom.css?v=1.3">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
fontFamily: {
|
|
sans: ['Sarabun', 'sans-serif'],
|
|
},
|
|
animation: {
|
|
shine: 'shine 3s ease-in-out infinite'
|
|
},
|
|
keyframes: {
|
|
shine: {
|
|
'0%': { left: '-150%' },
|
|
'20%': { left: '150%' },
|
|
'100%': { left: '150%' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style>
|
|
/* เว้นระยะขอบซ้ายให้คอลัมน์ชื่อ-สกุล (คอลัมน์ที่ 3) */
|
|
#staffTable th:nth-child(3),
|
|
#staffTable td:nth-child(3) {
|
|
text-align: left !important;
|
|
padding-left: 30px !important;
|
|
}
|
|
/* DataTables length/filter Tailwind overrides */
|
|
.dataTables_length select { appearance: auto; background-color: #fff; }
|
|
</style>
|
|
</head>
|
|
<?php
|
|
$assessed_cids = [];
|
|
if (isset($pdo_assess) && $pdo_assess) {
|
|
$current_thai_year = date('Y') + 543;
|
|
try {
|
|
$stmt_assess = $pdo_assess->prepare("SELECT hr_cid FROM it_assessments WHERE assess_year = ?");
|
|
$stmt_assess->execute([(string)$current_thai_year]);
|
|
while ($r = $stmt_assess->fetch()) {
|
|
$assessed_cids[$r['hr_cid']] = true;
|
|
}
|
|
} catch (\PDOException $e) {
|
|
}
|
|
}
|
|
|
|
$active_count = 0;
|
|
$inactive_count = 0;
|
|
$assessed_count = 0;
|
|
$not_assessed_count = 0;
|
|
|
|
foreach ($staff_list as $row) {
|
|
$is_active = (($row['ksh_user'] ?? 'Y') === 'Y');
|
|
|
|
if ($is_active) {
|
|
$active_count++;
|
|
} else {
|
|
$inactive_count++;
|
|
}
|
|
|
|
if (isset($assessed_cids[$row['cid']])) {
|
|
$assessed_count++;
|
|
} else {
|
|
// นับคนที่ยังไม่ประเมิน เฉพาะคนที่ยังมีสถานะ "ปฏิบัติงาน" เท่านั้น
|
|
if ($is_active) {
|
|
$not_assessed_count++;
|
|
}
|
|
}
|
|
}
|
|
$total_count = count($staff_list);
|
|
// Add $current_admin logic
|
|
$stmt_admin = $pdo->prepare("SELECT username, role FROM admin_users WHERE id = ?");
|
|
$stmt_admin->execute([$_SESSION['admin_id'] ?? 0]);
|
|
$current_admin = $stmt_admin->fetch();
|
|
?>
|
|
<body class="bg-gray-100 font-sans antialiased text-gray-800 flex min-h-screen">
|
|
<!-- Sidebar -->
|
|
<aside class="w-64 bg-gray-800 text-gray-300 flex flex-col fixed top-0 left-0 h-screen shadow-lg z-20">
|
|
<div class="px-6 py-5 bg-gray-900 border-b border-gray-700 text-center">
|
|
<div class="relative inline-block overflow-hidden rounded-full w-20 h-20 mx-auto mb-3 shadow-md bg-white p-1">
|
|
<img src="../assets/img/logo.png" alt="Logo" class="w-full h-full object-cover rounded-full relative z-10">
|
|
<div class="absolute inset-0 z-20 w-1/2 h-full bg-gradient-to-r from-transparent via-white/60 to-transparent -skew-x-12 animate-shine mix-blend-overlay"></div>
|
|
</div>
|
|
<h3 class="text-white text-lg font-bold tracking-wide mt-1">KSH-STAFF Admin</h3>
|
|
</div>
|
|
<nav class="flex-1 pt-6 pb-4 overflow-y-auto flex flex-col gap-1">
|
|
<a href="index.php" class="block px-6 py-3 hover:bg-gray-700 hover:text-white transition duration-200">
|
|
<i class="fas fa-home w-6 text-center mr-2"></i> แดชบอร์ด
|
|
</a>
|
|
<a href="staff_list.php" class="block px-6 py-3 bg-blue-600 text-white font-medium transition duration-200 shadow-sm">
|
|
<i class="fas fa-users w-6 text-center mr-2"></i> รายชื่อเจ้าหน้าที่
|
|
</a>
|
|
<?php if ($is_superadmin): ?>
|
|
<a href="manage_admins.php" class="block px-6 py-3 hover:bg-gray-700 hover:text-white transition duration-200">
|
|
<i class="fas fa-crown w-6 text-center mr-2"></i> จัดการแอดมิน
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<div class="mt-auto px-6 pt-4">
|
|
<a href="logout.php" class="flex items-center text-red-400 hover:text-red-300 hover:bg-red-400/10 py-2 px-3 rounded transition duration-200">
|
|
<i class="fas fa-sign-out-alt w-6 text-center mr-1"></i> ออกจากระบบ
|
|
</a>
|
|
</div>
|
|
</nav>
|
|
</aside>
|
|
|
|
<!-- Main Content -->
|
|
<main class="flex-1 ml-64 flex flex-col min-h-screen">
|
|
<header class="bg-white px-8 py-5 border-b border-gray-200 flex flex-wrap justify-between items-center shadow-sm z-10 sticky top-0 gap-4">
|
|
<div class="flex items-center gap-3">
|
|
<h4 class="text-xl font-bold text-gray-700"><i class="fas fa-users text-blue-500 mr-2"></i> รายชื่อเจ้าหน้าที่ลงทะเบียนแล้ว</h4>
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<span id="syncProgress" class="hidden font-bold text-blue-600 text-sm bg-blue-50 px-3 py-1.5 rounded-full"></span>
|
|
<button id="btnCheckDuplicate" onclick="checkDuplicateCID()" class="bg-amber-500 hover:bg-amber-600 text-white font-medium py-2 px-4 rounded-lg transition duration-200 shadow-sm flex items-center gap-2 text-sm">
|
|
<i class="fas fa-users-slash"></i> เช็ค CID ซ้ำ
|
|
</button>
|
|
<button id="btnBulkUpdate" onclick="startBulkUpdate()" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition duration-200 shadow-sm flex items-center gap-2 text-sm">
|
|
<i class="fas fa-sync-alt"></i> ตรวจสอบสถานะกับ HR
|
|
</button>
|
|
|
|
<div class="h-8 w-px bg-gray-300 mx-1 hidden md:block"></div>
|
|
|
|
<!-- User Profile -->
|
|
<div class="text-sm text-gray-500 font-medium px-4 py-1.5 bg-gray-100 rounded-full border border-gray-200 flex items-center gap-2">
|
|
ผู้ใช้งาน: <span class="text-blue-600 font-bold"><?= htmlspecialchars($current_admin['username'] ?? $_SESSION['admin_username'] ?? 'Admin') ?></span>
|
|
<span class="bg-gray-200 text-gray-600 px-2 py-0.5 rounded text-xs"><?= htmlspecialchars($current_admin['role'] ?? '') ?></span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="p-8 flex-1">
|
|
<!-- Summary Cards -->
|
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
|
|
<div onclick="$('#statusFilter').val('').trigger('change'); window.table.column(7).search('').draw();" class="bg-white rounded-xl shadow-sm border-l-4 border-blue-500 p-4 text-center cursor-pointer hover:-translate-y-1 hover:shadow-md transition duration-200">
|
|
<h3 class="text-2xl font-bold text-blue-600 mb-1"><?= $total_count ?></h3>
|
|
<span class="text-gray-500 text-xs font-medium">ทั้งหมด (คน)</span>
|
|
</div>
|
|
<div onclick="$('#statusFilter').val('ปฏิบัติงาน').trigger('change'); window.table.column(7).search('').draw();" class="bg-white rounded-xl shadow-sm border-l-4 border-emerald-500 p-4 text-center cursor-pointer hover:-translate-y-1 hover:shadow-md transition duration-200">
|
|
<h3 class="text-2xl font-bold text-emerald-600 mb-1"><?= $active_count ?></h3>
|
|
<span class="text-gray-500 text-xs font-medium">ปฏิบัติงาน</span>
|
|
</div>
|
|
<div onclick="$('#statusFilter').val('พ้นสภาพ').trigger('change'); window.table.column(7).search('').draw();" class="bg-white rounded-xl shadow-sm border-l-4 border-red-500 p-4 text-center cursor-pointer hover:-translate-y-1 hover:shadow-md transition duration-200">
|
|
<h3 class="text-2xl font-bold text-red-500 mb-1"><?= $inactive_count ?></h3>
|
|
<span class="text-gray-500 text-xs font-medium">พ้นสภาพ</span>
|
|
</div>
|
|
<div onclick="$('#statusFilter').val('').trigger('change'); window.table.column(7).search('ประเมินแล้ว').draw();" class="bg-white rounded-xl shadow-sm border-l-4 border-indigo-500 p-4 text-center cursor-pointer hover:-translate-y-1 hover:shadow-md transition duration-200">
|
|
<h3 class="text-2xl font-bold text-indigo-500 mb-1"><?= $assessed_count ?></h3>
|
|
<span class="text-gray-500 text-xs font-medium">ประเมินแล้ว</span>
|
|
</div>
|
|
<div onclick="$('#statusFilter').val('ปฏิบัติงาน').trigger('change'); window.table.column(7).search('ยังไม่ประเมิน').draw();" class="bg-white rounded-xl shadow-sm border-l-4 border-orange-400 p-4 text-center cursor-pointer hover:-translate-y-1 hover:shadow-md transition duration-200">
|
|
<h3 class="text-2xl font-bold text-orange-400 mb-1"><?= $not_assessed_count ?></h3>
|
|
<span class="text-gray-500 text-xs font-medium">ยังไม่ประเมิน</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
|
<!-- Custom Filter Dropdown (Moved via JS into DOM later) -->
|
|
<div id="customFilterContainer" style="display: none;">
|
|
<select id="statusFilter" class="border border-gray-300 rounded-lg px-3 py-1.5 ml-3 outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-sans text-sm text-gray-700 bg-white">
|
|
<option value="">-- กรองสถานะทั้งหมด --</option>
|
|
<option value="ปฏิบัติงาน">ปฏิบัติงาน</option>
|
|
<option value="พ้นสภาพ">พ้นสภาพ</option>
|
|
</select>
|
|
</div>
|
|
|
|
<table id="staffTable" class="display" style="width:100%">
|
|
<thead>
|
|
<tr>
|
|
<th width="12%">CID</th>
|
|
<th width="8%">HN</th>
|
|
<th width="15%">ชื่อ - นามสกุล</th>
|
|
<th width="10%">เบอร์โทร</th>
|
|
<th width="10%">Line UID</th>
|
|
<th width="15%">วันที่สมัคร</th>
|
|
<th width="15%">สถานะทำงาน</th>
|
|
<th width="15%">การประเมิน (<?= date('Y') + 543 ?>)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($staff_list as $row):
|
|
$date_formatted = '-';
|
|
if (!empty($row['date'])) {
|
|
$d = date_create($row['date']);
|
|
if ($d) {
|
|
$year_be = (int)date_format($d, 'Y') + 543;
|
|
$date_formatted = date_format($d, 'd/m/') . $year_be;
|
|
}
|
|
}
|
|
|
|
$phone = $row['phone'];
|
|
if (strpos($phone, '+66') === 0) {
|
|
$phone = '0' . substr($phone, 3);
|
|
}
|
|
|
|
// จัดรูปแบบ xx-xxxx-xxxx
|
|
$phone_clean = preg_replace('/[^0-9]/', '', $phone);
|
|
if (strlen($phone_clean) === 10) {
|
|
$phone = substr($phone_clean, 0, 2) . '-' . substr($phone_clean, 2, 4) . '-' . substr($phone_clean, 6);
|
|
} else if (strlen($phone_clean) === 9) {
|
|
$phone = substr($phone_clean, 0, 2) . '-' . substr($phone_clean, 2, 3) . '-' . substr($phone_clean, 5);
|
|
}
|
|
|
|
$is_assessed = isset($assessed_cids[$row['cid']]);
|
|
?>
|
|
<tr>
|
|
<td><?= htmlspecialchars($row['cid']) ?></td>
|
|
<td><?= htmlspecialchars($row['hn']) ?></td>
|
|
<td><?= htmlspecialchars($row['fname'] . ' ' . $row['lname']) ?></td>
|
|
<td><?= htmlspecialchars($phone) ?></td>
|
|
<td>
|
|
<?php if (!empty($row['user_id'])): ?>
|
|
<span style="color:#198754; font-weight: 500; cursor: help;" title="<?= htmlspecialchars($row['user_id']) ?>">
|
|
<i class="fas fa-check-circle"></i> ผูกแล้ว
|
|
</span>
|
|
<?php else: ?>
|
|
<span style="color:#dc3545; font-weight: 500;">
|
|
<i class="fas fa-times-circle"></i> ยังไม่ผูก
|
|
</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?= $date_formatted ?></td>
|
|
<td id="status-td-<?= htmlspecialchars($row['cid']) ?>">
|
|
<?php if (($row['ksh_user'] ?? 'Y') === 'Y'): ?>
|
|
<span style="color:#198754; font-weight: 500;"><i class="fas fa-briefcase"></i> ปฏิบัติงาน</span>
|
|
<?php else: ?>
|
|
<span style="color:#dc3545; font-weight: 500;"><i class="fas fa-user-times"></i> พ้นสภาพ</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<?php if ($is_assessed): ?>
|
|
<span class="bg-indigo-100 text-indigo-700 px-3 py-1 rounded-full text-xs font-bold border border-indigo-200">
|
|
<i class="fas fa-clipboard-check mr-1"></i> ประเมินแล้ว
|
|
</span>
|
|
<?php else: ?>
|
|
<span class="bg-orange-100 text-orange-700 px-3 py-1 rounded-full text-xs font-bold border border-orange-200">
|
|
<i class="fas fa-exclamation-circle mr-1"></i> ยังไม่ประเมิน
|
|
</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<script>
|
|
// DataTables Initialization
|
|
$(document).ready(function() {
|
|
window.table = $('#staffTable').DataTable({
|
|
"columnDefs": [
|
|
{ "className": "dt-center", "targets": "_all" },
|
|
{ "className": "dt-left", "targets": 2 }
|
|
],
|
|
"language": {
|
|
"url": "//cdn.datatables.net/plug-ins/1.13.6/i18n/th.json",
|
|
"search": "",
|
|
"searchPlaceholder": "ค้นหาด้วยชื่อ, CID, เบอร์โทร..."
|
|
},
|
|
"dom": '<"dt-top-row"<"dt-top-left"lB>f>rt<"dt-bottom-row"ip>',
|
|
"buttons": [
|
|
{
|
|
extend: 'excelHtml5',
|
|
text: '<i class="fas fa-file-excel"></i> ส่งออก Excel',
|
|
title: 'รายชื่อเจ้าหน้าที่ลงทะเบียน Line KSH-STAFF',
|
|
exportOptions: {
|
|
columns: [0, 1, 2, 3, 4, 5, 6, 7]
|
|
}
|
|
}
|
|
],
|
|
"pageLength": 10,
|
|
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "ทั้งหมด"]]
|
|
});
|
|
|
|
// Append custom filter dropdown next to export button
|
|
$('#customFilterContainer').contents().appendTo('.dt-top-left');
|
|
|
|
// Handle Filter Change
|
|
$('#statusFilter').on('change', function() {
|
|
var val = $(this).val();
|
|
window.table.column(6).search(val).draw();
|
|
});
|
|
});
|
|
|
|
// Duplicate CID Check Logic
|
|
function checkDuplicateCID() {
|
|
const btn = document.getElementById('btnCheckDuplicate');
|
|
const originalHtml = btn.innerHTML;
|
|
|
|
Swal.fire({
|
|
title: 'ยืนยันการเคลียร์ข้อมูล',
|
|
text: "ระบบจะทำการลบข้อมูล CID ที่ซ้ำกันออก โดยเก็บเฉพาะรายการที่ลงทะเบียนล่าสุดไว้เท่านั้น ต้องการดำเนินการหรือไม่?",
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
cancelButtonColor: '#6c757d',
|
|
confirmButtonText: 'ลบข้อมูลซ้ำ',
|
|
cancelButtonText: 'ยกเลิก'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> กำลังตรวจสอบ...';
|
|
btn.disabled = true;
|
|
|
|
$.post('ajax_remove_duplicate_cid.php', {}, function(res) {
|
|
btn.innerHTML = originalHtml;
|
|
btn.disabled = false;
|
|
try {
|
|
const data = JSON.parse(res);
|
|
if (data.success) {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'เรียบร้อย!',
|
|
text: data.message,
|
|
confirmButtonColor: '#007bff'
|
|
}).then(() => {
|
|
if(data.deleted_count > 0) location.reload();
|
|
});
|
|
} else {
|
|
Swal.fire('ผิดพลาด', data.message, 'error');
|
|
}
|
|
} catch(e) {
|
|
Swal.fire('ผิดพลาด', 'ระบบเซิร์ฟเวอร์ขัดข้อง', 'error');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
const staffCIDs = [
|
|
<?php foreach ($staff_list as $row): ?>
|
|
"<?= htmlspecialchars($row['cid']) ?>",
|
|
<?php endforeach; ?>
|
|
];
|
|
|
|
async function startBulkUpdate() {
|
|
if(!confirm("ระบบจะทำการดึงข้อมูลสถานะล่าสุดจากฐานข้อมูล HR มาอัปเดตเจ้าหน้าที่ทั้งหมด " + staffCIDs.length + " รายการ\n\nต้องการดำเนินการต่อหรือไม่?")) {
|
|
return;
|
|
}
|
|
|
|
$('#btnBulkUpdate').prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> กำลังตรวจสอบ...');
|
|
$('#syncProgress').show().text(`ตรวจสอบแล้ว 0 / ${staffCIDs.length}`);
|
|
|
|
let updatedCount = 0;
|
|
|
|
for (let cid of staffCIDs) {
|
|
// If row is visible on current page, show loading spinner
|
|
const td = document.getElementById('status-td-' + cid);
|
|
if(td) {
|
|
td.innerHTML = '<span style="color:#007bff; font-weight: 500;"><i class="fas fa-spinner fa-spin"></i> กำลังตรวจสอบ...</span>';
|
|
}
|
|
|
|
try {
|
|
const res = await $.post('ajax_update_hr_status.php', { cid: cid });
|
|
const data = JSON.parse(res);
|
|
if (data.success && td) {
|
|
if (data.new_status === 'Y') {
|
|
td.innerHTML = '<span style="color:#198754; font-weight: 500;"><i class="fas fa-briefcase"></i> ปฏิบัติงาน</span>';
|
|
} else {
|
|
td.innerHTML = '<span style="color:#dc3545; font-weight: 500;"><i class="fas fa-user-times"></i> พ้นสภาพ</span>';
|
|
}
|
|
} else if (td) {
|
|
td.innerHTML = '<span style="color:#dc3545; font-weight: 500;"><i class="fas fa-exclamation-triangle"></i> ขัดข้อง</span>';
|
|
}
|
|
} catch (e) {
|
|
if(td) td.innerHTML = '<span style="color:#dc3545; font-weight: 500;"><i class="fas fa-exclamation-triangle"></i> ขัดข้อง</span>';
|
|
}
|
|
|
|
updatedCount++;
|
|
$('#syncProgress').text(`ตรวจสอบแล้ว ${updatedCount} / ${staffCIDs.length}`);
|
|
}
|
|
|
|
$('#btnBulkUpdate').prop('disabled', false).html('<i class="fas fa-sync-alt"></i> ตรวจสอบสถานะกับ HR (ทั้งหมด)');
|
|
$('#syncProgress').text(`เสร็จสิ้น! อัปเดตแล้ว ${updatedCount} รายการ`).delay(3000).fadeOut();
|
|
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'เสร็จสิ้น!',
|
|
text: 'อัปเดตสถานะเจ้าหน้าที่ทั้งหมดเรียบร้อยแล้ว',
|
|
confirmButtonColor: '#007bff'
|
|
}).then(() => {
|
|
// Reload page to refresh DataTable internal cache and formatting
|
|
location.reload();
|
|
});
|
|
}
|
|
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|