Files
2026-09-16 23:20:08 +07:00

170 lines
16 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 => 'ธันวาคม'
];
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'; ?>