414 lines
32 KiB
PHP
414 lines
32 KiB
PHP
<?php require_once APP_ROOT . '/app/Views/layouts/header.php'; ?>
|
|
|
|
<?php
|
|
$thaiMonths = [
|
|
1 => 'มกราคม', 2 => 'กุมภาพันธ์', 3 => 'มีนาคม', 4 => 'เมษายน',
|
|
5 => 'พฤษภาคม', 6 => 'มิถุนายน', 7 => 'กรกฎาคม', 8 => 'สิงหาคม',
|
|
9 => 'กันยายน', 10 => 'ตุลาคม', 11 => 'พฤศจิกายน', 12 => 'ธันวาคม'
|
|
];
|
|
$yearBe = $period['year_no'] > 2500 ? $period['year_no'] : $period['year_no'] + 543;
|
|
$periodName = $thaiMonths[$period['month_no']] . ' ' . $yearBe;
|
|
$isLocked = ($period['status'] === 'Locked' || $period['status'] === 'Published');
|
|
?>
|
|
|
|
<div class="mb-6">
|
|
<div class="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 mb-2">
|
|
<a href="<?= BASE_URL ?>/payroll" class="hover:text-sky-500 transition-colors">จัดการงวดเงินเดือน</a>
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-3 h-3"><path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>
|
|
<span class="text-slate-800 dark:text-slate-200 font-medium">รายละเอียดงวด <?= $periodName ?></span>
|
|
</div>
|
|
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
<div>
|
|
<h2 class="text-2xl font-bold text-slate-800 dark:text-white">รายละเอียดงวดเงินเดือน</h2>
|
|
<p class="text-slate-500 dark:text-slate-400 text-sm mt-1">ประจำเดือน <?= $periodName ?> (สถานะ: <?= $period['status'] ?>)</p>
|
|
</div>
|
|
|
|
<?php if (!$isLocked): ?>
|
|
<div class="flex flex-wrap gap-2">
|
|
<form action="<?= BASE_URL ?>/payroll/updateStatus" method="POST" class="inline" onsubmit="return confirm('ยืนยันการล็อกและประกาศใช้ข้อมูล? (เมื่อทำแล้วจะไม่สามารถแก้ไขได้อีกและจะแสดงผลในตู้ Kiosk ทันที)');">
|
|
<input type="hidden" name="id" value="<?= $period['id'] ?>">
|
|
<input type="hidden" name="status" value="Published">
|
|
<button type="submit" class="inline-flex items-center justify-center px-4 py-2.5 bg-emerald-500 hover:bg-emerald-600 text-white text-sm font-medium rounded-xl transition-colors shadow-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" /></svg>
|
|
ประกาศใช้ (Lock)
|
|
</button>
|
|
</form>
|
|
<button type="button" onclick="confirmCopyPreviousMonth()" class="inline-flex items-center justify-center px-4 py-2.5 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-600 text-sm font-medium rounded-xl transition-colors shadow-sm">
|
|
<svg class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
|
|
</svg>
|
|
คัดลอกจากเดือนก่อน
|
|
</button>
|
|
<button onclick="document.getElementById('addEmployeeModal').classList.remove('hidden')" class="inline-flex items-center justify-center px-4 py-2.5 bg-sky-500 hover:bg-sky-600 text-white text-sm font-medium rounded-xl transition-colors shadow-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>
|
|
เพิ่มเจ้าหน้าที่
|
|
</button>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="flex flex-wrap gap-2">
|
|
<form action="<?= BASE_URL ?>/payroll/updateStatus" method="POST" class="inline" onsubmit="return confirm('ยืนยันการปลดล็อกข้อมูล? (เมื่อปลดล็อกแล้วข้อมูลจะไม่แสดงที่ตู้ Kiosk จนกว่าจะประกาศใช้อีกครั้ง)');">
|
|
<input type="hidden" name="id" value="<?= $period['id'] ?>">
|
|
<input type="hidden" name="status" value="Draft">
|
|
<button type="submit" class="inline-flex items-center justify-center px-4 py-2.5 bg-amber-500 hover:bg-amber-600 text-white text-sm font-medium rounded-xl transition-colors shadow-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 10.5V6.75a4.5 4.5 0 119 0v3.75M3.75 21.75h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H3.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" /></svg>
|
|
ปลดล็อกเพื่อแก้ไข
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
|
<div class="bg-white dark:bg-slate-800 rounded-2xl p-6 shadow-sm border border-slate-100 dark:border-slate-700">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">จำนวนพนักงาน</p>
|
|
<p class="text-2xl font-bold text-slate-800 dark:text-white mt-2"><?= number_format($stats['total_emp'] ?? 0) ?> <span class="text-sm font-normal text-slate-500">คน</span></p>
|
|
</div>
|
|
<div class="p-3 bg-sky-50 dark:bg-sky-900/30 rounded-xl">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" /></svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-slate-800 rounded-2xl p-6 shadow-sm border border-slate-100 dark:border-slate-700">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-sm font-medium text-slate-500 dark:text-slate-400">ยอดเงินสุทธิรวม</p>
|
|
<p class="text-2xl font-bold text-slate-800 dark:text-white mt-2"><?= number_format($stats['total_net'] ?? 0, 2) ?> <span class="text-sm font-normal text-slate-500">บาท</span></p>
|
|
</div>
|
|
<div class="p-3 bg-emerald-50 dark:bg-emerald-900/30 rounded-xl">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-emerald-500"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m-3-2.818.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DataTables CSS for Tailwind -->
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/2.0.8/css/dataTables.tailwindcss.css">
|
|
<style>
|
|
/* DataTables Dark Mode Overrides */
|
|
.dark .dt-container { color: #cbd5e1; }
|
|
.dark .dt-length select, .dark .dt-search input {
|
|
background-color: #1e293b; border-color: #334155; color: #f8fafc; border-radius: 0.5rem; padding: 0.25rem 0.5rem;
|
|
}
|
|
.dark .dt-info, .dark .dt-paging-button { color: #94a3b8 !important; }
|
|
.dark .dt-paging-button.current { background: #38bdf8 !important; color: #0f172a !important; border-color: #38bdf8 !important; }
|
|
.dark .dt-paging-button:hover { background: #334155 !important; color: #f8fafc !important; }
|
|
table.dataTable.no-footer { border-bottom: 1px solid #334155 !important; }
|
|
</style>
|
|
|
|
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-100 dark:border-slate-700 overflow-hidden p-6">
|
|
<div class="flex justify-end mb-4">
|
|
<label class="inline-flex items-center cursor-pointer">
|
|
<input type="checkbox" id="togglePrefix" class="w-4 h-4 text-indigo-600 bg-slate-100 border-slate-300 rounded focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600">
|
|
<span class="ml-2 text-sm font-medium text-slate-700 dark:text-slate-300">แสดงคำนำหน้าชื่อ</span>
|
|
</label>
|
|
</div>
|
|
<div class="overflow-x-hidden">
|
|
<table id="employeeTable" class="w-full text-left text-sm text-slate-600 dark:text-slate-300 display">
|
|
<thead class="bg-slate-50 dark:bg-slate-900/50 text-slate-500 dark:text-slate-400 text-xs uppercase font-semibold">
|
|
<tr>
|
|
<th class="px-6 py-4">ชื่อ - นามสกุล</th>
|
|
<th class="px-6 py-4">ตำแหน่ง / แผนก</th>
|
|
<th class="px-6 py-4 text-right text-emerald-600">รายรับรวม</th>
|
|
<th class="px-6 py-4 text-right text-red-600">รายจ่ายรวม</th>
|
|
<th class="px-6 py-4 text-right text-sky-600">ยอดสุทธิ</th>
|
|
<th class="px-6 py-4 text-right">จัดการ</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100 dark:divide-slate-700">
|
|
<?php if (empty($employees)): ?>
|
|
<tr>
|
|
<td colspan="6" class="px-6 py-8 text-center text-slate-500 dark:text-slate-400">
|
|
ยังไม่มีพนักงานในงวดนี้ <?php if (!$isLocked): ?><button onclick="document.getElementById('addEmployeeModal').classList.remove('hidden')" class="text-sky-500 hover:underline">เพิ่มเลย</button><?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($employees as $emp): ?>
|
|
<tr class="hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors">
|
|
<td class="px-6 py-4" data-sort="<?= htmlspecialchars(preg_replace('/^(นาย|นางสาว|นาง|ว่าที่ร้อยตรี|น\.ส\.|นพ\.|พญ\.|ดร\.)\s*/u', '', trim($emp['first_name']))) ?>">
|
|
<div class="flex items-center gap-3">
|
|
<div class="h-10 w-10 flex-shrink-0">
|
|
<img class="h-10 w-10 rounded-full object-cover bg-slate-100 border border-slate-200 dark:border-slate-600" src="<?= BASE_URL ?>/employee/image/<?= htmlspecialchars($emp['national_id']) ?>" alt="<?= htmlspecialchars($emp['first_name']) ?>" onerror="this.src='data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'%2394a3b8\'><path fill-rule=\'evenodd\' d=\'M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z\' clip-rule=\'evenodd\'/></svg>'">
|
|
</div>
|
|
<div>
|
|
<?php
|
|
$rawName = trim($emp['first_name']);
|
|
$prefix = '';
|
|
$bodyName = $rawName;
|
|
if (preg_match('/^(นาย|นางสาว|นาง|ว่าที่ร้อยตรี|น\.ส\.|นพ\.|พญ\.|ดร\.)\s*(.*)$/u', $rawName, $matches)) {
|
|
$prefix = $matches[1];
|
|
$bodyName = $matches[2];
|
|
}
|
|
?>
|
|
<div class="font-medium text-slate-900 dark:text-white"><span class="name-prefix hidden"><?= htmlspecialchars($prefix) ?></span><?= htmlspecialchars($bodyName . ' ' . $emp['last_name']) ?></div>
|
|
<div class="text-xs text-slate-500 mt-0.5">ID: <?= htmlspecialchars($emp['national_id']) ?></div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<div class="text-slate-800 dark:text-slate-300"><?= htmlspecialchars($emp['position'] ?: '-') ?></div>
|
|
<div class="text-xs text-slate-500 mt-1"><?= htmlspecialchars($emp['department'] ?: '-') ?></div>
|
|
</td>
|
|
<td class="px-6 py-4 text-right text-emerald-600 font-medium">
|
|
<?= number_format($emp['total_income'], 2) ?>
|
|
</td>
|
|
<td class="px-6 py-4 text-right text-red-500 font-medium">
|
|
<?= number_format($emp['total_deduction'], 2) ?>
|
|
</td>
|
|
<td class="px-6 py-4 text-right text-sky-600 font-bold">
|
|
<?= number_format($emp['net_salary'], 2) ?>
|
|
</td>
|
|
<td class="px-6 py-4 text-right whitespace-nowrap">
|
|
<a href="<?= BASE_URL ?>/reports/slip/<?= htmlspecialchars($emp['national_id']) ?>/<?= sprintf('%02d', $period['month_no']) ?>/<?= $period['year_no'] ?>" target="_blank" class="inline-flex items-center justify-center p-1.5 text-slate-400 hover:text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 rounded-lg transition-colors mr-1" title="ดูสลิปเงินเดือน">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>
|
|
</a>
|
|
<?php if (!$isLocked): ?>
|
|
<a href="<?= BASE_URL ?>/payroll/employee/edit/<?= $emp['id'] ?>" class="inline-flex items-center justify-center p-1.5 text-slate-400 hover:text-sky-500 hover:bg-sky-50 dark:hover:bg-sky-900/30 rounded-lg transition-colors mr-1">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" /></svg>
|
|
</a>
|
|
<button type="button" onclick="confirmDeleteEmployee(<?= $emp['id'] ?>, '<?= htmlspecialchars($emp['first_name'] . ' ' . $emp['last_name']) ?>')" class="inline-flex items-center justify-center p-1.5 text-slate-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/30 rounded-lg transition-colors">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" /></svg>
|
|
</button>
|
|
<?php else: ?>
|
|
<span class="text-xs text-slate-400">ล็อกแล้ว</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!$isLocked): ?>
|
|
<!-- Add Employee Modal -->
|
|
<div id="addEmployeeModal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
|
<div class="fixed inset-0 bg-slate-900/50 backdrop-blur-sm transition-opacity" aria-hidden="true" onclick="document.getElementById('addEmployeeModal').classList.add('hidden')"></div>
|
|
<div class="flex min-h-screen items-center justify-center p-4 text-center sm:p-0">
|
|
<div class="relative transform overflow-hidden rounded-2xl bg-white dark:bg-slate-800 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg border border-slate-100 dark:border-slate-700">
|
|
<form action="<?= BASE_URL ?>/payroll/employee/add" method="POST">
|
|
<input type="hidden" name="salary_month_id" value="<?= $period['id'] ?>">
|
|
<div class="bg-white dark:bg-slate-800 px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
|
|
<div class="sm:flex sm:items-start">
|
|
<div class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-sky-100 dark:bg-sky-900/50 sm:mx-0 sm:h-10 sm:w-10">
|
|
<svg class="h-6 w-6 text-sky-600 dark:text-sky-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M18 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0ZM3 19.235v-.11a6.375 6.375 0 0 1 12.75 0v.109A12.318 12.318 0 0 1 9.374 21c-2.331 0-4.512-.647-6.374-1.766Z" /></svg>
|
|
</div>
|
|
<div class="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left w-full">
|
|
<h3 class="text-lg font-semibold leading-6 text-slate-900 dark:text-white" id="modal-title">เพิ่มพนักงานในงวดนี้</h3>
|
|
|
|
<div class="mt-6 space-y-4">
|
|
<div class="mb-4 relative">
|
|
<label class="block text-sm font-medium leading-6 text-slate-700 dark:text-slate-300 mb-2">ค้นหาเจ้าหน้าที่จากระบบ HR</label>
|
|
<div class="relative">
|
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<svg class="h-5 w-5 text-slate-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
<input type="text" id="empSearch" placeholder="พิมพ์ชื่อ, นามสกุล เพื่อค้นหา..."
|
|
class="block w-full rounded-xl border-0 py-2.5 pl-10 px-3 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 placeholder:text-slate-400 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm sm:leading-6 bg-slate-50 dark:bg-slate-900">
|
|
</div>
|
|
<ul id="empSearchResults" class="absolute z-10 w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl mt-1 max-h-60 overflow-y-auto hidden shadow-lg overflow-hidden"></ul>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="national_id" class="block text-sm font-medium leading-6 text-slate-700 dark:text-slate-300 mb-2">เลขบัตรประชาชน 13 หลัก</label>
|
|
<input type="text" id="national_id" name="national_id" required pattern="[0-9]{13}" maxlength="13" placeholder="ระบุเลขบัตรประชาชน หรือเลือกจากการค้นหาด้านบน" class="block w-full rounded-xl border-0 py-2.5 px-3 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 placeholder:text-slate-400 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm sm:leading-6 bg-slate-50 dark:bg-slate-900">
|
|
</div>
|
|
|
|
<div class="rounded-xl bg-sky-50 dark:bg-sky-900/30 p-4 mt-6">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<svg class="h-5 w-5 text-sky-400" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0ZM8.25 9.75A.75.75 0 0 1 9 9h.253a1.75 1.75 0 0 1 1.709 2.13l-.46 2.066a.25.25 0 0 0 .245.304H11a.75.75 0 0 1 0 1.5h-.253a1.75 1.75 0 0 1-1.709-2.13l.46-2.066a.25.25 0 0 0-.245-.304H9a.75.75 0 0 1-.75-.75ZM10 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" /></svg>
|
|
</div>
|
|
<div class="ml-3">
|
|
<p class="text-sm text-sky-700 dark:text-sky-300 m-0">ระบบจะดึงชื่อ-นามสกุลจากระบบ HR (hosoffice_2566) อัตโนมัติ</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-slate-50 dark:bg-slate-700/50 px-4 py-4 sm:flex sm:flex-row-reverse sm:px-6">
|
|
<button type="submit" class="inline-flex w-full justify-center rounded-xl bg-sky-500 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 sm:ml-3 sm:w-auto transition-colors">ดึงข้อมูลและเพิ่ม</button>
|
|
<button type="button" onclick="document.getElementById('addEmployeeModal').classList.add('hidden')" class="mt-3 inline-flex w-full justify-center rounded-xl bg-white dark:bg-slate-800 px-4 py-2.5 text-sm font-semibold text-slate-900 dark:text-slate-300 shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700 sm:mt-0 sm:w-auto transition-colors">ยกเลิก</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form for Delete Employee -->
|
|
<form id="deleteEmployeeForm" action="<?= BASE_URL ?>/payroll/employee/delete" method="POST" class="hidden">
|
|
<input type="hidden" name="id" id="deleteEmpId" value="">
|
|
<input type="hidden" name="salary_month_id" value="<?= $period['id'] ?>">
|
|
</form>
|
|
|
|
<!-- Form for Copy Previous Month -->
|
|
<form id="copyPreviousMonthForm" action="<?= BASE_URL ?>/payroll/copy-previous" method="POST" class="hidden">
|
|
<input type="hidden" name="salary_month_id" value="<?= $period['id'] ?>">
|
|
</form>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const searchInput = document.getElementById('empSearch');
|
|
const searchResults = document.getElementById('empSearchResults');
|
|
const nationalIdInput = document.getElementById('national_id');
|
|
|
|
let debounceTimer;
|
|
|
|
searchInput.addEventListener('input', function() {
|
|
const keyword = this.value.trim();
|
|
clearTimeout(debounceTimer);
|
|
|
|
if (keyword.length < 2) {
|
|
searchResults.classList.add('hidden');
|
|
searchResults.innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
debounceTimer = setTimeout(function() {
|
|
fetch('<?= BASE_URL ?>/settings/system/search-officer?q=' + encodeURIComponent(keyword))
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Network error ' + response.status);
|
|
return response.text();
|
|
})
|
|
.then(text => {
|
|
let res;
|
|
try {
|
|
res = JSON.parse(text);
|
|
} catch (e) {
|
|
console.error('Invalid JSON from server:', text);
|
|
return;
|
|
}
|
|
|
|
searchResults.innerHTML = '';
|
|
if (res.status === 'success' && res.data && res.data.length > 0) {
|
|
res.data.forEach(item => {
|
|
const li = document.createElement('li');
|
|
li.className = 'px-4 py-3 hover:bg-sky-50 dark:hover:bg-slate-700 cursor-pointer text-sm text-slate-700 dark:text-slate-300 border-b border-slate-100 dark:border-slate-700 last:border-0';
|
|
li.innerHTML = `
|
|
<div class="font-medium">${item.name}</div>
|
|
<div class="text-xs text-slate-500 mt-1">${item.position}</div>
|
|
`;
|
|
li.addEventListener('click', function() {
|
|
nationalIdInput.value = item.id;
|
|
searchInput.value = item.name;
|
|
searchResults.classList.add('hidden');
|
|
// Highlight the input briefly to show it was populated
|
|
nationalIdInput.classList.add('ring-sky-500', 'ring-2');
|
|
setTimeout(() => {
|
|
nationalIdInput.classList.remove('ring-sky-500', 'ring-2');
|
|
}, 500);
|
|
});
|
|
searchResults.appendChild(li);
|
|
});
|
|
searchResults.classList.remove('hidden');
|
|
} else {
|
|
const li = document.createElement('li');
|
|
li.className = 'px-4 py-3 text-sm text-slate-500 dark:text-slate-400 italic text-center';
|
|
li.textContent = res.message || 'ไม่พบข้อมูล';
|
|
searchResults.appendChild(li);
|
|
searchResults.classList.remove('hidden');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error searching:', error);
|
|
});
|
|
}, 400);
|
|
});
|
|
|
|
// Hide results when clicking outside
|
|
document.addEventListener('click', function(e) {
|
|
if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
|
|
searchResults.classList.add('hidden');
|
|
}
|
|
});
|
|
});
|
|
|
|
function confirmDeleteEmployee(id, name) {
|
|
Swal.fire({
|
|
title: 'ยืนยันการลบ?',
|
|
html: `คุณต้องการลบ <b>${name}</b> ออกจากงวดเงินเดือนนี้ใช่หรือไม่?<br><span class="text-sm text-red-500">ยอดเงินและรายการรับจ่ายทั้งหมดของคนนี้จะถูกลบไปด้วย</span>`,
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#ef4444',
|
|
cancelButtonColor: '#64748b',
|
|
confirmButtonText: 'ใช่, ลบเลย',
|
|
cancelButtonText: 'ยกเลิก'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
document.getElementById('deleteEmpId').value = id;
|
|
document.getElementById('deleteEmployeeForm').submit();
|
|
}
|
|
});
|
|
}
|
|
|
|
function confirmCopyPreviousMonth() {
|
|
Swal.fire({
|
|
title: 'คัดลอกข้อมูลจากเดือนก่อน?',
|
|
html: `ระบบจะคัดลอกรายชื่อพนักงาน พร้อมรายการรายได้และรายจ่ายทั้งหมด จากงวดเงินเดือนเดือนที่แล้ว มายังงวดปัจจุบัน<br><br><span class="text-sm text-sky-600">พนักงานที่มีชื่ออยู่ในงวดนี้แล้วจะถูกข้ามไป ไม่คัดลอกซ้ำ</span>`,
|
|
icon: 'info',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#4f46e5',
|
|
cancelButtonColor: '#64748b',
|
|
confirmButtonText: 'ใช่, เริ่มคัดลอก',
|
|
cancelButtonText: 'ยกเลิก'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
document.getElementById('copyPreviousMonthForm').submit();
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
<?php endif; ?>
|
|
|
|
<!-- jQuery and DataTables JS -->
|
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
<script src="https://cdn.datatables.net/2.0.8/js/dataTables.js"></script>
|
|
<script src="https://cdn.datatables.net/2.0.8/js/dataTables.tailwindcss.js"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
$('#employeeTable').DataTable({
|
|
"language": {
|
|
"sProcessing": "กำลังดำเนินการ...",
|
|
"sLengthMenu": "แสดง _MENU_ เรคคอร์ด",
|
|
"sZeroRecords": "ไม่พบข้อมูล",
|
|
"sInfo": "แสดง _START_ ถึง _END_ จาก _TOTAL_ เรคคอร์ด",
|
|
"sInfoEmpty": "แสดง 0 ถึง 0 จาก 0 เรคคอร์ด",
|
|
"sInfoFiltered": "(กรองข้อมูล _MAX_ ทุกเรคคอร์ด)",
|
|
"sSearch": "ค้นหา:",
|
|
"oPaginate": {
|
|
"sFirst": "แรกสุด",
|
|
"sPrevious": "ก่อนหน้า",
|
|
"sNext": "ถัดไป",
|
|
"sLast": "สุดท้าย"
|
|
}
|
|
},
|
|
"pageLength": 25,
|
|
"columnDefs": [
|
|
{ "orderable": false, "targets": 5 }
|
|
],
|
|
"order": [[ 0, "asc" ]]
|
|
});
|
|
|
|
// Checkbox toggle prefix
|
|
$('#togglePrefix').on('change', function() {
|
|
if ($(this).is(':checked')) {
|
|
$('.name-prefix').removeClass('hidden');
|
|
} else {
|
|
$('.name-prefix').addClass('hidden');
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<?php require_once APP_ROOT . '/app/Views/layouts/footer.php'; ?>
|