Files
sam-manage/resources/views/approvals/partials/_leave-promotion-1st-form.blade.php
김보곤 617c89a33f fix: [approval] 연차사용촉진 통지서 Employee 모델 속성 수정
- departments->first() → department? (BelongsTo 단수 관계)
- $emp->name → $emp->display_name
- $emp->position → $emp->position_key
- $emp->id → $emp->user_id
- LeaveService에 department eager load 추가
2026-03-07 00:33:36 +09:00

254 lines
16 KiB
PHP

{{--
연차유급휴가 사용촉진 통지서 (1) 전용
Props:
$tenantInfo (array) - 테넌트(회사) 정보
$employees (Collection) - 직원 목록
--}}
@php
$tenantInfo = $tenantInfo ?? [];
$employees = $employees ?? collect();
@endphp
<div id="leave-promotion-1st-form-container" style="display: none;" class="mb-4">
<input type="hidden" id="lp1-company-name" value="{{ $tenantInfo['company_name'] ?? '' }}">
<input type="hidden" id="lp1-ceo-name" value="{{ $tenantInfo['ceo_name'] ?? '' }}">
<div class="space-y-4">
{{-- 1. 수신자 정보 --}}
<div class="border border-gray-200 rounded-lg overflow-hidden">
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
<h3 class="text-sm font-semibold text-gray-700">1. 수신자 정보</h3>
</div>
<div class="p-4 space-y-3">
<div class="flex gap-4 flex-wrap">
<div style="flex: 1 1 300px; max-width: 400px;">
<label class="block text-xs font-medium text-gray-500 mb-1">대상 직원 <span class="text-red-500">*</span></label>
<select id="lp1-user-id" onchange="loadLp1EmployeeInfo(this.value)"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">-- 직원 선택 --</option>
@foreach($employees as $emp)
<option value="{{ $emp->user_id }}"
data-department="{{ $emp->department?->name ?? '' }}"
data-position="{{ $emp->position_key ?? '' }}">
{{ $emp->display_name }} {{ $emp->department?->name ? '('.$emp->department->name.')' : '' }}
</option>
@endforeach
</select>
</div>
</div>
<div class="flex gap-4 flex-wrap">
<div style="flex: 1 1 200px; max-width: 250px;">
<label class="block text-xs font-medium text-gray-500 mb-1">부서</label>
<input type="text" id="lp1-department" readonly
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-700">
</div>
<div style="flex: 1 1 150px; max-width: 200px;">
<label class="block text-xs font-medium text-gray-500 mb-1">직급</label>
<input type="text" id="lp1-position" readonly
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-700">
</div>
</div>
</div>
</div>
{{-- 2. 연차 현황 --}}
<div class="border border-gray-200 rounded-lg overflow-hidden">
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
<h3 class="text-sm font-semibold text-gray-700">2. 연차 현황</h3>
</div>
<div class="p-4">
<div class="flex gap-4 flex-wrap">
<div style="flex: 0 0 120px;">
<label class="block text-xs font-medium text-gray-500 mb-1">발생연차 <span class="text-red-500">*</span></label>
<input type="number" id="lp1-total-days" min="0" step="0.5" value="15"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 text-center">
</div>
<div style="flex: 0 0 120px;">
<label class="block text-xs font-medium text-gray-500 mb-1">사용연차</label>
<input type="number" id="lp1-used-days" min="0" step="0.5" value="5"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 text-center">
</div>
<div style="flex: 0 0 120px;">
<label class="block text-xs font-medium text-gray-500 mb-1">잔여연차</label>
<input type="number" id="lp1-remaining-days" readonly
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm bg-amber-50 text-amber-800 font-semibold text-center">
</div>
</div>
</div>
</div>
{{-- 3. 사용계획 제출기한 --}}
<div class="border border-gray-200 rounded-lg overflow-hidden">
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
<h3 class="text-sm font-semibold text-gray-700">3. 사용계획 제출기한</h3>
</div>
<div class="p-4">
<div style="max-width: 250px;">
<label class="block text-xs font-medium text-gray-500 mb-1">제출기한 <span class="text-red-500">*</span></label>
<input type="date" id="lp1-deadline"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<p class="text-xs text-gray-400 mt-2">* 기한 사용 시기를 제출하지 않을 경우 회사가 연차 사용 시기를 지정할 있습니다.</p>
</div>
</div>
{{-- 4. 통지 내용 (법적 문구) --}}
<div class="border border-blue-200 rounded-lg overflow-hidden">
<div class="bg-blue-50 px-4 py-2 border-b border-blue-200">
<h3 class="text-sm font-semibold text-blue-700">4. 법적 통지 문구</h3>
</div>
<div class="p-4">
<div class="text-xs text-gray-600 leading-relaxed space-y-2 bg-gray-50 p-3 rounded border border-gray-200">
<p>근로기준법 제61조에 따라 귀하의 미사용 연차유급휴가 사용을 촉진하고자 아래와 같이 통지합니다.</p>
<p> 잔여 연차휴가에 대하여 기한까지 사용 시기를 지정하여 제출하여 주시기 바랍니다.</p>
<p class="text-red-600 font-medium">기한 사용 시기를 제출하지 않을 경우 회사는 근로기준법 제61조에 따라 연차휴가 사용 시기를 지정할 있습니다.</p>
<p> 통지서는 연차 사용 촉진 절차에 따른 법적 통보 문서이며, 확인 수신 확인으로 간주됩니다.</p>
</div>
</div>
</div>
{{-- 미리보기 버튼 --}}
<div class="flex justify-end">
<button type="button" onclick="openLp1Preview()"
class="px-3 py-2 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 border border-indigo-200 rounded-lg text-sm font-medium transition inline-flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
미리보기
</button>
</div>
</div>
</div>
{{-- 1 통지서 미리보기 모달 --}}
<div id="lp1-preview-modal" style="display: none;" class="fixed inset-0 z-50">
<div class="absolute inset-0 bg-black/50" onclick="closeLp1Preview()"></div>
<div class="relative flex items-center justify-center min-h-full p-4">
<div class="bg-white rounded-xl shadow-2xl w-full overflow-hidden relative" style="max-width: 780px;">
<div class="flex items-center justify-between px-5 py-3 border-b border-gray-200 bg-gray-50">
<h3 class="text-base font-semibold text-gray-800">1 통지서 미리보기</h3>
<div class="flex items-center gap-2">
<button type="button" onclick="printLp1Preview()"
class="px-3 py-1.5 bg-white border border-gray-300 hover:bg-gray-50 rounded-lg text-xs font-medium transition inline-flex items-center gap-1">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/>
</svg>
인쇄
</button>
<button type="button" onclick="closeLp1Preview()"
class="p-1 text-gray-400 hover:text-gray-600 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
<div class="overflow-y-auto" style="max-height: 80vh;">
<div id="lp1-preview-content" style="padding: 40px 48px; font-family: 'Pretendard', 'Malgun Gothic', sans-serif;"></div>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
function loadLp1EmployeeInfo(userId) {
const sel = document.getElementById('lp1-user-id');
const opt = sel.options[sel.selectedIndex];
document.getElementById('lp1-department').value = opt?.dataset?.department || '';
document.getElementById('lp1-position').value = opt?.dataset?.position || '';
}
// 잔여연차 자동 계산
['lp1-total-days', 'lp1-used-days'].forEach(id => {
document.getElementById(id)?.addEventListener('input', function() {
const total = parseFloat(document.getElementById('lp1-total-days').value) || 0;
const used = parseFloat(document.getElementById('lp1-used-days').value) || 0;
document.getElementById('lp1-remaining-days').value = Math.max(0, total - used);
});
});
// 초기 계산
document.addEventListener('DOMContentLoaded', () => {
const total = parseFloat(document.getElementById('lp1-total-days')?.value) || 0;
const used = parseFloat(document.getElementById('lp1-used-days')?.value) || 0;
const rem = document.getElementById('lp1-remaining-days');
if (rem) rem.value = Math.max(0, total - used);
});
function buildLp1PreviewHtml(data) {
return '<div style="text-align:center;margin-bottom:32px;">' +
'<h1 style="font-size:20px;font-weight:800;margin:0;letter-spacing:4px;">연차유급휴가 사용촉진 통지서 (1차)</h1>' +
'</div>' +
'<div style="border-bottom:2px solid #333;padding-bottom:12px;margin-bottom:24px;font-size:13px;line-height:2;">' +
'<div><span style="display:inline-block;width:60px;font-weight:600;">수신</span> : ' + (data.employee_name || '') + '</div>' +
'<div><span style="display:inline-block;width:60px;font-weight:600;">부서</span> : ' + (data.department || '') + '</div>' +
'<div><span style="display:inline-block;width:60px;font-weight:600;">직급</span> : ' + (data.position || '') + '</div>' +
'</div>' +
'<div style="font-size:13px;line-height:1.8;margin-bottom:24px;">' +
'<p>근로기준법 제61조에 따라 귀하의 미사용 연차유급휴가 사용을 촉진하고자 아래와 같이 통지합니다.</p>' +
'</div>' +
'<div style="margin:24px 0;padding:16px 20px;border:1px solid #ddd;border-radius:4px;">' +
'<div style="font-size:13px;font-weight:700;margin-bottom:8px;">■ 연차 현황</div>' +
'<table style="width:100%;border-collapse:collapse;font-size:13px;">' +
'<tr><td style="padding:6px 12px;border:1px solid #ddd;background:#f8f9fa;font-weight:600;width:100px;">발생연차</td>' +
'<td style="padding:6px 12px;border:1px solid #ddd;text-align:center;">' + (data.total_days || 0) + '일</td>' +
'<td style="padding:6px 12px;border:1px solid #ddd;background:#f8f9fa;font-weight:600;width:100px;">사용연차</td>' +
'<td style="padding:6px 12px;border:1px solid #ddd;text-align:center;">' + (data.used_days || 0) + '일</td>' +
'<td style="padding:6px 12px;border:1px solid #ddd;background:#f8f9fa;font-weight:600;width:100px;">잔여연차</td>' +
'<td style="padding:6px 12px;border:1px solid #ddd;text-align:center;color:#dc2626;font-weight:700;">' + (data.remaining_days || 0) + '일</td></tr>' +
'</table>' +
'</div>' +
'<div style="font-size:13px;line-height:1.8;margin-bottom:16px;">' +
'<p>위 잔여 연차휴가에 대하여 아래 기한까지 사용 시기를 지정하여 제출하여 주시기 바랍니다.</p>' +
'</div>' +
'<div style="margin:16px 0;padding:12px 20px;border:1px solid #ddd;border-radius:4px;background:#fffbeb;">' +
'<div style="font-size:13px;font-weight:700;">■ 사용계획 제출기한 : ' + (data.deadline || '') + '</div>' +
'</div>' +
'<div style="font-size:12px;line-height:1.8;margin:24px 0;padding:12px 16px;background:#f8f9fa;border-radius:4px;color:#666;">' +
'<p>기한 내 사용 시기를 제출하지 않을 경우 회사는 근로기준법 제61조에 따라 연차휴가 사용 시기를 지정할 수 있습니다.</p>' +
'<p>본 통지서는 연차 사용 촉진 절차에 따른 법적 통보 문서이며, 확인 시 수신 확인으로 간주됩니다.</p>' +
'</div>' +
'<div style="text-align:center;margin:40px 0 16px;font-size:14px;">' +
'<span>' + (data.company_name || '') + '</span>&nbsp;&nbsp;&nbsp;&nbsp;' +
'<span>대표이사&nbsp;&nbsp;&nbsp;' + (data.ceo_name || '') + '</span>&nbsp;&nbsp;' +
'<span style="color:#999;">[직인날인]</span>' +
'</div>' +
'<div style="border-top:2px solid #333;padding-top:12px;margin-top:24px;">' +
'<div style="font-size:12px;color:#666;text-align:center;">□ 본인은 위 내용을 확인하였으며 연차 사용 시기를 제출하겠습니다.</div>' +
'<div style="text-align:right;margin-top:12px;font-size:12px;color:#999;">서명: ________________________&nbsp;&nbsp;&nbsp;일자: ____년 ____월 ____일</div>' +
'</div>';
}
function getLp1Data() {
const sel = document.getElementById('lp1-user-id');
const opt = sel.options[sel.selectedIndex];
return {
employee_name: opt?.text?.replace(/\s*\(.*\)/, '') || '',
employee_id: sel.value,
department: document.getElementById('lp1-department').value,
position: document.getElementById('lp1-position').value,
total_days: parseFloat(document.getElementById('lp1-total-days').value) || 0,
used_days: parseFloat(document.getElementById('lp1-used-days').value) || 0,
remaining_days: parseFloat(document.getElementById('lp1-remaining-days').value) || 0,
deadline: document.getElementById('lp1-deadline').value,
company_name: document.getElementById('lp1-company-name').value,
ceo_name: document.getElementById('lp1-ceo-name').value,
};
}
function openLp1Preview() {
document.getElementById('lp1-preview-content').innerHTML = buildLp1PreviewHtml(getLp1Data());
document.getElementById('lp1-preview-modal').style.display = '';
}
function closeLp1Preview() { document.getElementById('lp1-preview-modal').style.display = 'none'; }
function printLp1Preview() {
const content = document.getElementById('lp1-preview-content').innerHTML;
const win = window.open('', '_blank');
win.document.write('<html><head><title>연차사용촉진 1차 통지서</title><style>body{font-family:"Pretendard","Malgun Gothic",sans-serif;padding:40px 48px;}@media print{body{padding:20px 30px;}}</style></head><body>' + content + '</body></html>');
win.document.close();
win.print();
}
</script>
@endpush