Initial commit

This commit is contained in:
Porawit Dongwang
2026-09-16 23:20:08 +07:00
commit 0041668dbb
32577 changed files with 3687927 additions and 0 deletions
@@ -0,0 +1,413 @@
<?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'; ?>
@@ -0,0 +1,408 @@
<?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;
// Prepare options for javascript
$incomeOptions = '<option value="">-- เลือกรายการรับ --</option>';
foreach ($masterIncomes as $mi) {
$incomeOptions .= '<option value="' . $mi['id'] . '">' . htmlspecialchars($mi['code'] . ' - ' . $mi['name']) . '</option>';
}
$deductionOptions = '<option value="">-- เลือกรายการจ่าย --</option>';
foreach ($masterDeductions as $md) {
$deductionOptions .= '<option value="' . $md['id'] . '">' . htmlspecialchars($md['code'] . ' - ' . $md['name']) . '</option>';
}
?>
<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>
<a href="<?= BASE_URL ?>/payroll/details/<?= $period['id'] ?>" 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">แก้ไขข้อมูลพนักงาน</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 ?></p>
</div>
<a href="<?= BASE_URL ?>/payroll/details/<?= $period['id'] ?>" class="inline-flex items-center px-4 py-2 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl text-sm font-medium text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors shadow-sm">
ย้อนกลับ
</a>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6 relative">
<!-- Profile Card (Left) -->
<div class="lg:col-span-1 space-y-6">
<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 flex-col items-center text-center">
<div class="w-24 h-24 mb-4 relative">
<img class="w-24 h-24 rounded-full object-cover bg-sky-50 border-4 border-white dark:border-slate-800 shadow-sm" src="<?= BASE_URL ?>/employee/image/<?= htmlspecialchars($employee['national_id']) ?>" alt="<?= htmlspecialchars($employee['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 class="absolute inset-0 rounded-full shadow-inner pointer-events-none"></div>
</div>
<h3 class="text-lg font-bold text-slate-800 dark:text-white"><?= htmlspecialchars($employee['first_name'] . ' ' . $employee['last_name']) ?></h3>
<p class="text-sm text-slate-500 dark:text-slate-400 mt-1"><?= htmlspecialchars($employee['position'] ?: 'ไม่ระบุตำแหน่ง') ?></p>
<div class="mt-4 w-full bg-slate-50 dark:bg-slate-700/50 rounded-xl p-3 text-left">
<div class="text-xs text-slate-500 dark:text-slate-400 mb-1">เลขบัตรประชาชน</div>
<div class="text-sm font-medium text-slate-800 dark:text-slate-200 font-mono"><?= htmlspecialchars($employee['national_id']) ?></div>
<div class="text-xs text-slate-500 dark:text-slate-400 mt-3 mb-1">แผนก</div>
<div class="text-sm font-medium text-slate-800 dark:text-slate-200"><?= htmlspecialchars($employee['department'] ?: '-') ?></div>
<div class="text-xs text-slate-500 dark:text-slate-400 mt-3 mb-1">ธนาคาร</div>
<input type="text" name="bank_name" form="editEmployeeForm" value="<?= htmlspecialchars($employee['bank_name'] ?? 'ธนาคารกรุงไทย จำกัด(มหาชน)') ?>" placeholder="ชื่อธนาคาร" class="w-full text-sm rounded-md border-0 py-1.5 px-2 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 focus:ring-2 focus:ring-inset focus:ring-sky-500 bg-white dark:bg-slate-800 mb-2">
<div class="text-xs text-slate-500 dark:text-slate-400 mb-1">เลขที่บัญชี</div>
<input type="text" name="bank_account" form="editEmployeeForm" value="<?= htmlspecialchars($employee['bank_account'] ?? '') ?>" placeholder="เลขที่บัญชี" class="w-full text-sm rounded-md border-0 py-1.5 px-2 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 focus:ring-2 focus:ring-inset focus:ring-sky-500 bg-white dark:bg-slate-800 font-mono">
</div>
</div>
</div>
<!-- Summary Card -->
<div class="bg-white dark:bg-slate-800 rounded-2xl p-6 shadow-sm border border-slate-100 dark:border-slate-700 sticky top-4">
<h4 class="text-sm font-semibold text-slate-800 dark:text-white uppercase tracking-wider mb-4">สรุปยอดเงิน</h4>
<div class="space-y-3 text-sm">
<div class="flex justify-between items-center text-emerald-600 dark:text-emerald-400">
<span>รายรับรวม</span>
<span class="font-medium" id="sumTotalIncome">+ 0.00</span>
</div>
<div class="flex justify-between items-center text-red-500 dark:text-red-400">
<span>รายจ่ายรวม</span>
<span class="font-medium" id="sumTotalDeduction">- 0.00</span>
</div>
<div class="pt-3 mt-3 border-t border-slate-100 dark:border-slate-700 flex justify-between items-center">
<span class="font-bold text-slate-800 dark:text-white">ยอดรับสุทธิ</span>
<span class="text-lg font-bold text-sky-600 dark:text-sky-400" id="sumNetSalary">0.00</span>
</div>
</div>
</div>
</div>
<!-- Form Data (Right) -->
<div class="lg:col-span-3">
<form action="<?= BASE_URL ?>/payroll/employee/update" method="POST" id="editEmployeeForm" class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-100 dark:border-slate-700">
<input type="hidden" name="employee_salary_id" value="<?= $employee['id'] ?>">
<input type="hidden" name="salary_month_id" value="<?= $employee['salary_month_id'] ?>">
<div class="p-6 border-b border-slate-100 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-900/20">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-emerald-600 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>
รายการรับ (Incomes)
</h3>
<button type="button" onclick="addIncomeRow()" class="text-sm text-emerald-600 hover:text-emerald-700 font-medium px-3 py-1.5 rounded-lg hover:bg-emerald-50 dark:hover:bg-emerald-900/30 transition-colors">+ เพิ่มรายการรับ</button>
</div>
<div class="space-y-3" id="incomeContainer">
<?php if (empty($incomes)): ?>
<div class="text-sm text-slate-400 italic text-center py-4 empty-msg">ไม่มีรายการรับ</div>
<?php else: ?>
<?php foreach ($incomes as $inc): ?>
<div class="flex items-center gap-3 bg-white dark:bg-slate-800 p-3 rounded-xl border border-slate-200 dark:border-slate-700 income-row">
<div class="flex-1">
<select name="income_id[]" class="searchable-select w-full" required>
<option value="">-- เลือกรายการรับ --</option>
<?php foreach ($masterIncomes as $mi): ?>
<option value="<?= $mi['id'] ?>" <?= $mi['id'] == $inc['income_master_id'] ? 'selected' : '' ?>><?= htmlspecialchars($mi['code'] . ' - ' . $mi['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="w-40">
<input type="text" name="income_amount[]" value="<?= number_format($inc['amount'], 2) ?>" oninput="calculateTotal()" class="w-full rounded-lg border-0 py-2 px-3 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm bg-slate-50 dark:bg-slate-900 font-mono text-right number-input" required>
</div>
<button type="button" onclick="this.closest('.income-row').remove(); calculateTotal(); checkEmpty('income');" class="p-2 text-slate-400 hover:text-red-500 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-5 h-5"><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>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<div class="p-6 border-b border-slate-100 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-900/20">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-red-500 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14" /></svg>
รายการจ่าย (Deductions)
</h3>
<button type="button" onclick="addDeductionRow()" class="text-sm text-red-500 hover:text-red-600 font-medium px-3 py-1.5 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/30 transition-colors">+ เพิ่มรายการจ่าย</button>
</div>
<div class="space-y-3" id="deductionContainer">
<?php if (empty($deductions)): ?>
<div class="text-sm text-slate-400 italic text-center py-4 empty-msg">ไม่มีรายการจ่าย</div>
<?php else: ?>
<?php foreach ($deductions as $ded): ?>
<div class="flex items-center gap-3 bg-white dark:bg-slate-800 p-3 rounded-xl border border-slate-200 dark:border-slate-700 deduction-row">
<div class="flex-1">
<select name="deduction_id[]" class="searchable-select w-full" required>
<option value="">-- เลือกรายการจ่าย --</option>
<?php foreach ($masterDeductions as $md): ?>
<option value="<?= $md['id'] ?>" <?= $md['id'] == $ded['deduction_master_id'] ? 'selected' : '' ?>><?= htmlspecialchars($md['code'] . ' - ' . $md['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="w-40">
<input type="text" name="deduction_amount[]" value="<?= number_format($ded['amount'], 2) ?>" oninput="calculateTotal()" class="w-full rounded-lg border-0 py-2 px-3 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm bg-slate-50 dark:bg-slate-900 font-mono text-right number-input" required>
</div>
<button type="button" onclick="this.closest('.deduction-row').remove(); calculateTotal(); checkEmpty('deduction');" class="p-2 text-slate-400 hover:text-red-500 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-5 h-5"><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>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<div class="p-6 flex items-center justify-end gap-3 bg-slate-50 dark:bg-slate-800 rounded-b-2xl">
<a href="<?= BASE_URL ?>/payroll/details/<?= $period['id'] ?>" class="px-5 py-2.5 bg-white dark:bg-slate-700 border border-slate-300 dark:border-slate-600 rounded-xl text-sm font-medium text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-600 transition-colors shadow-sm">
ยกเลิก
</a>
<button type="submit" class="px-5 py-2.5 bg-sky-500 hover:bg-sky-600 text-white rounded-xl text-sm font-medium transition-colors shadow-sm flex items-center">
<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="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>
บันทึกข้อมูล
</button>
</div>
</form>
</div>
</div>
<script>
// Master data options for JS
const incomeOptions = `<?= $incomeOptions ?>`;
const deductionOptions = `<?= $deductionOptions ?>`;
function addIncomeRow() {
const container = document.getElementById('incomeContainer');
const emptyMsg = container.querySelector('.empty-msg');
if (emptyMsg) emptyMsg.remove();
const row = document.createElement('div');
row.className = 'flex items-center gap-3 bg-white dark:bg-slate-800 p-3 rounded-xl border border-slate-200 dark:border-slate-700 income-row';
row.innerHTML = `
<div class="flex-1">
<select name="income_id[]" class="searchable-select w-full" required>
${incomeOptions}
</select>
</div>
<div class="w-40">
<input type="text" name="income_amount[]" value="0.00" oninput="calculateTotal()" class="w-full rounded-lg border-0 py-2 px-3 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm bg-slate-50 dark:bg-slate-900 font-mono text-right number-input" required>
</div>
<button type="button" onclick="this.closest('.income-row').remove(); calculateTotal(); checkEmpty('income');" class="p-2 text-slate-400 hover:text-red-500 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-5 h-5"><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>
`;
container.appendChild(row);
initNumberFormat(row.querySelector('.number-input'));
// Initialize Select2 on the new row
$(row).find('.searchable-select').select2({
width: '100%',
matcher: function(params, data) {
if ($.trim(params.term) === '') return data;
if (typeof data.text === 'undefined') return null;
var q = params.term.toLowerCase();
if (data.text.toLowerCase().indexOf(q) > -1 || (data.id && data.id.toLowerCase().indexOf(q) > -1)) {
return $.extend({}, data, true);
}
return null;
}
});
}
function addDeductionRow() {
const container = document.getElementById('deductionContainer');
const emptyMsg = container.querySelector('.empty-msg');
if (emptyMsg) emptyMsg.remove();
const row = document.createElement('div');
row.className = 'flex items-center gap-3 bg-white dark:bg-slate-800 p-3 rounded-xl border border-slate-200 dark:border-slate-700 deduction-row';
row.innerHTML = `
<div class="flex-1">
<select name="deduction_id[]" class="searchable-select w-full" required>
${deductionOptions}
</select>
</div>
<div class="w-40">
<input type="text" name="deduction_amount[]" value="0.00" oninput="calculateTotal()" class="w-full rounded-lg border-0 py-2 px-3 text-slate-900 dark:text-white shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-600 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm bg-slate-50 dark:bg-slate-900 font-mono text-right number-input" required>
</div>
<button type="button" onclick="this.closest('.deduction-row').remove(); calculateTotal(); checkEmpty('deduction');" class="p-2 text-slate-400 hover:text-red-500 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-5 h-5"><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>
`;
container.appendChild(row);
initNumberFormat(row.querySelector('.number-input'));
// Initialize Select2 on the new row
$(row).find('.searchable-select').select2({
width: '100%',
matcher: function(params, data) {
if ($.trim(params.term) === '') return data;
if (typeof data.text === 'undefined') return null;
var q = params.term.toLowerCase();
if (data.text.toLowerCase().indexOf(q) > -1 || (data.id && data.id.toLowerCase().indexOf(q) > -1)) {
return $.extend({}, data, true);
}
return null;
}
});
}
function checkEmpty(type) {
const container = document.getElementById(type + 'Container');
const rows = container.querySelectorAll('.' + type + '-row');
if (rows.length === 0) {
container.innerHTML = `<div class="text-sm text-slate-400 italic text-center py-4 empty-msg">ไม่มีรายการ${type === 'income' ? 'รับ' : 'จ่าย'}</div>`;
}
}
function parseAmt(val) {
if (!val) return 0;
return parseFloat(val.toString().replace(/,/g, '')) || 0;
}
function formatNumber(num) {
return num.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
function calculateTotal() {
let totalIncome = 0;
document.querySelectorAll('input[name="income_amount[]"]').forEach(input => {
totalIncome += parseAmt(input.value);
});
let totalDeduction = 0;
document.querySelectorAll('input[name="deduction_amount[]"]').forEach(input => {
totalDeduction += parseAmt(input.value);
});
const netSalary = totalIncome - totalDeduction;
document.getElementById('sumTotalIncome').innerText = '+ ' + formatNumber(totalIncome);
document.getElementById('sumTotalDeduction').innerText = '- ' + formatNumber(totalDeduction);
document.getElementById('sumNetSalary').innerText = formatNumber(netSalary);
}
// Auto-format numbers on blur
function initNumberFormat(el) {
el.addEventListener('blur', function() {
const val = parseAmt(this.value);
this.value = formatNumber(val);
calculateTotal();
});
// Allow only numbers and dots
el.addEventListener('input', function() {
let val = this.value;
val = val.replace(/[^0-9.,]/g, '');
this.value = val;
});
}
document.querySelectorAll('.number-input').forEach(initNumberFormat);
// Initial calculation
calculateTotal();
</script>
<!-- Select2 for Searchable Dropdowns -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style type="text/tailwindcss">
/* Select2 Tailwind Override */
.select2-container--default .select2-selection--single {
@apply w-full rounded-lg py-2 px-3 text-slate-900 shadow-sm ring-1 ring-inset ring-slate-300 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm bg-slate-50 outline-none transition-all;
border: none !important;
border-radius: 0.5rem !important;
height: 36px !important;
}
.select2-container--default.select2-container--open .select2-selection--single {
@apply ring-2 ring-inset ring-sky-500;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 20px;
padding-left: 0;
@apply text-slate-900;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 34px;
right: 8px;
}
.select2-dropdown {
@apply border-0 shadow-lg rounded-lg overflow-hidden ring-1 ring-slate-200 mt-1;
}
.select2-search--dropdown {
@apply p-2 bg-slate-50 border-b border-slate-100;
}
.select2-search--dropdown .select2-search__field {
@apply w-full rounded-md border-0 py-1.5 px-3 text-slate-900 shadow-sm ring-1 ring-inset ring-slate-300 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm;
}
.select2-results__option {
@apply px-3 py-2 text-sm text-slate-700 cursor-pointer transition-colors;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
@apply bg-sky-50 text-sky-700;
}
.select2-container--default .select2-results__option[aria-selected=true] {
@apply bg-slate-100 text-slate-900 font-medium;
}
/* Dark Mode Support */
.dark .select2-container--default .select2-selection--single {
@apply bg-slate-900 ring-slate-600 text-white;
}
.dark .select2-container--default .select2-selection--single .select2-selection__rendered {
@apply text-white;
}
.dark .select2-dropdown {
@apply bg-slate-800 ring-slate-700;
}
.dark .select2-search--dropdown {
@apply bg-slate-800 border-slate-700;
}
.dark .select2-search--dropdown .select2-search__field {
@apply bg-slate-900 ring-slate-700 text-white focus:ring-sky-500;
}
.dark .select2-results__option {
@apply text-slate-300;
}
.dark .select2-container--default .select2-results__option--highlighted[aria-selected] {
@apply bg-slate-700 text-white;
}
.dark .select2-container--default .select2-results__option[aria-selected=true] {
@apply bg-slate-900 text-white;
}
</style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
// Initialize existing selects
$('.searchable-select').select2({
width: '100%',
matcher: function(params, data) {
// Custom matcher for better search (ignores spaces etc if needed)
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
var q = params.term.toLowerCase();
if (data.text.toLowerCase().indexOf(q) > -1 || (data.id && data.id.toLowerCase().indexOf(q) > -1)) {
return $.extend({}, data, true);
}
return null;
}
});
});
</script>
<?php require_once APP_ROOT . '/app/Views/layouts/footer.php'; ?>
@@ -0,0 +1,169 @@
<?php require_once APP_ROOT . '/app/Views/layouts/header.php'; ?>
<?php
$thaiMonths = [
1 => 'มกราคม', 2 => 'กุมภาพันธ์', 3 => 'มีนาคม', 4 => 'เมษายน',
5 => 'พฤษภาคม', 6 => 'มิถุนายน', 7 => 'กรกฎาคม', 8 => 'สิงหาคม',
9 => 'กันยายน', 10 => 'ตุลาคม', 11 => 'พฤศจิกายน', 12 => 'ธันวาคม'
];
function getStatusBadge($status) {
switch ($status) {
case 'Draft': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-slate-100 text-slate-700 dark:bg-slate-700 dark:text-slate-300">ฉบับร่าง</span>';
case 'Imported': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-sky-100 text-sky-700 dark:bg-sky-900/50 dark:text-sky-400">นำเข้าข้อมูลแล้ว</span>';
case 'Pending Review': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-400">รอตรวจสอบ</span>';
case 'HR Approved': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-400">HR อนุมัติ</span>';
case 'Finance Approved': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-900/50 dark:text-indigo-400">การเงินอนุมัติ</span>';
case 'Director Approved': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-400">ผอ.อนุมัติ</span>';
case 'Locked': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-zinc-800 text-zinc-100 dark:bg-zinc-900 dark:text-zinc-300">ล็อก</span>';
case 'Published': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-400">ประกาศใช้</span>';
case 'Cancelled': return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-400">ยกเลิก</span>';
default: return '<span class="px-2.5 py-1 text-xs font-semibold rounded-full bg-slate-100 text-slate-700 dark:bg-slate-700 dark:text-slate-300">' . htmlspecialchars($status) . '</span>';
}
}
?>
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6 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">สร้างและจัดการรอบบิลเงินเดือนของแต่ละเดือน</p>
</div>
<button onclick="document.getElementById('createPeriodModal').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 w-full md:w-auto">
<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>
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-100 dark:border-slate-700 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left text-sm text-slate-600 dark:text-slate-300">
<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">สถานะ</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($periods)): ?>
<tr>
<td colspan="4" class="px-6 py-8 text-center text-slate-500 dark:text-slate-400">
ยังไม่มีข้อมูลงวดเงินเดือน <button onclick="document.getElementById('createPeriodModal').classList.remove('hidden')" class="text-sky-500 hover:underline">สร้างงวดแรกที่นี่</button>
</td>
</tr>
<?php else: ?>
<?php foreach ($periods as $period): ?>
<tr class="hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors">
<td class="px-6 py-4 font-bold text-sky-600 dark:text-sky-400">
<?= $thaiMonths[$period['month_no']] ?> <?= $period['year_no'] > 2500 ? $period['year_no'] : $period['year_no'] + 543 ?>
</td>
<td class="px-6 py-4 text-xs">
<?= date('d/m/Y H:i', strtotime($period['updated_at'])) ?>
</td>
<td class="px-6 py-4">
<?= getStatusBadge($period['status']) ?>
</td>
<td class="px-6 py-4 text-right">
<?php if ($period['status'] !== 'Locked' && $period['status'] !== 'Published'): ?>
<button type="button" onclick="confirmClearData(<?= $period['id'] ?>, '<?= $thaiMonths[$period['month_no']] ?> <?= $period['year_no'] > 2500 ? $period['year_no'] : $period['year_no'] + 543 ?>')" class="inline-flex items-center px-3 py-1.5 border border-red-200 dark:border-red-900/50 bg-red-50 dark:bg-red-900/20 rounded-lg text-xs font-medium text-red-600 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/40 transition-colors mr-2">
<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 mr-1.5"><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 endif; ?>
<a href="<?= BASE_URL ?>/payroll/details/<?= $period['id'] ?>" class="inline-flex items-center px-3 py-1.5 border border-slate-300 dark:border-slate-600 rounded-lg text-xs font-medium text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700 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 mr-1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 0 1 1.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 0 1-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 0 1-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 0 1 .12-1.45l.773-.773a1.125 1.125 0 0 1 1.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894Z" /><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>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Tailwind Modal -->
<div id="createPeriodModal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<!-- Backdrop -->
<div class="fixed inset-0 bg-slate-900/50 backdrop-blur-sm transition-opacity" aria-hidden="true" onclick="document.getElementById('createPeriodModal').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/store" method="POST">
<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="M12 4.5v15m7.5-7.5h-15" />
</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>
<label for="year_no" class="block text-sm font-medium leading-6 text-slate-700 dark:text-slate-300 mb-2">ปี พ.ศ. (Year)</label>
<input type="number" id="year_no" name="year_no" value="<?= date('Y') + 543 ?>" required min="2500" max="2600" 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>
<label for="month_no" class="block text-sm font-medium leading-6 text-slate-700 dark:text-slate-300 mb-2">เดือน (Month)</label>
<select id="month_no" name="month_no" required 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 focus:ring-2 focus:ring-inset focus:ring-sky-500 sm:text-sm sm:leading-6 bg-slate-50 dark:bg-slate-900">
<option value="">-- เลือกเดือน --</option>
<?php foreach ($thaiMonths as $num => $name): ?>
<option value="<?= $num ?>" <?= (date('n') == $num) ? 'selected' : '' ?>><?= $name ?></option>
<?php endforeach; ?>
</select>
</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">งวดใหม่จะถูกสร้างในสถานะ <strong>ฉบับร่าง (Draft)</strong></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('createPeriodModal').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 Clearing Data -->
<form id="clearDataForm" action="<?= BASE_URL ?>/payroll/clear" method="POST" class="hidden">
<input type="hidden" name="salary_month_id" id="clearSalaryMonthId" value="">
</form>
<script>
function confirmClearData(id, periodName) {
Swal.fire({
title: 'ยืนยันการล้างข้อมูล?',
html: `คุณต้องการล้างข้อมูลที่นำเข้าทั้งหมดของงวด <b>${periodName}</b> ใช่หรือไม่?<br><span class="text-red-500 text-sm">คำเตือน: ข้อมูลเงินเดือนของพนักงานทุกคนในงวดนี้จะถูกลบทั้งหมด</span>`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#ef4444',
cancelButtonColor: '#64748b',
confirmButtonText: 'ใช่, ล้างข้อมูล',
cancelButtonText: 'ยกเลิก'
}).then((result) => {
if (result.isConfirmed) {
document.getElementById('clearSalaryMonthId').value = id;
document.getElementById('clearDataForm').submit();
}
});
}
</script>
<?php require_once APP_ROOT . '/app/Views/layouts/footer.php'; ?>