409 lines
27 KiB
PHP
409 lines
27 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;
|
|
|
|
// 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'; ?>
|