Files
sam-manage/resources/views/hr/attendances/partials/requests.blade.php
김보곤 adc587292f feat: [attendance] 근태관리 2차 고도화 8개 기능 구현
- 월간 캘린더 뷰 (사원별 필터, 날짜 클릭 등록, HTMX 월 이동)
- 일괄 등록 (다수 사원 체크박스 선택 후 일괄 등록, upsert 처리)
- 사원별 월간 요약 (상태별 카운트 + 총 근무시간 집계 테이블)
- 초과근무 알림 (주 48h 경고 / 52h 위험 배너)
- 근태 승인 워크플로우 (신청→승인→근태 레코드 자동 생성)
- 자동 결근 처리 (매일 23:50 스케줄러, 주말 제외)
- 연차 관리 연동 (휴가 등록 시 leave_balances 자동 차감)
- GPS 출퇴근 UI (테이블 GPS 아이콘 + 상세 모달)
- 탭 네비게이션 (목록/캘린더/요약/승인) HTMX 기반 전환
2026-02-26 21:29:25 +09:00

111 lines
5.9 KiB
PHP

{{-- 근태 신청/승인 목록 (HTMX로 로드) --}}
@php
use App\Models\HR\AttendanceRequest;
@endphp
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<h3 class="text-lg font-semibold text-gray-800">근태 신청/승인</h3>
<button type="button" onclick="openRequestModal()"
class="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors">
<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="M12 4v16m8-8H4"/>
</svg>
신청
</button>
</div>
@if($requests->isEmpty())
<div class="px-6 py-12 text-center">
<svg class="w-12 h-12 text-gray-300 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg>
<p class="text-gray-500">근태 신청 내역이 없습니다.</p>
</div>
@else
<x-table-swipe>
<table class="min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-6 py-3 text-left text-sm font-semibold text-gray-600">신청자</th>
<th class="px-4 py-3 text-center text-sm font-semibold text-gray-600">유형</th>
<th class="px-4 py-3 text-center text-sm font-semibold text-gray-600">기간</th>
<th class="px-6 py-3 text-left text-sm font-semibold text-gray-600">사유</th>
<th class="px-4 py-3 text-center text-sm font-semibold text-gray-600">상태</th>
<th class="px-4 py-3 text-left text-sm font-semibold text-gray-600">처리자</th>
<th class="px-4 py-3 text-center text-sm font-semibold text-gray-600">작업</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
@foreach($requests as $req)
@php
$profile = $req->user?->tenantProfiles?->first();
$displayName = $profile?->display_name ?? $req->user?->name ?? '-';
$statusColor = AttendanceRequest::STATUS_COLORS[$req->status] ?? 'gray';
$typeLabel = AttendanceRequest::TYPE_MAP[$req->request_type] ?? $req->request_type;
$statusLabel = AttendanceRequest::STATUS_MAP[$req->status] ?? $req->status;
$dateRange = $req->start_date->format('m/d') . ($req->start_date->ne($req->end_date) ? ' ~ ' . $req->end_date->format('m/d') : '');
@endphp
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-3 whitespace-nowrap">
<div class="flex items-center gap-2">
<div class="shrink-0 w-7 h-7 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-xs font-medium">
{{ mb_substr($displayName, 0, 1) }}
</div>
<span class="text-sm font-medium text-gray-900">{{ $displayName }}</span>
</div>
</td>
<td class="px-4 py-3 text-center">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-700">
{{ $typeLabel }}
</span>
</td>
<td class="px-4 py-3 text-center text-sm text-gray-700 whitespace-nowrap">{{ $dateRange }}</td>
<td class="px-6 py-3 text-sm text-gray-500" style="max-width: 200px;">
<span class="truncate block">{{ $req->reason ?? '-' }}</span>
</td>
<td class="px-4 py-3 text-center">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-{{ $statusColor }}-100 text-{{ $statusColor }}-700">
{{ $statusLabel }}
</span>
</td>
<td class="px-4 py-3 text-sm text-gray-500 whitespace-nowrap">
@if($req->approved_by)
{{ $req->approver?->name ?? '-' }}
<div class="text-xs text-gray-400">{{ $req->approved_at?->format('m/d H:i') }}</div>
@else
-
@endif
</td>
<td class="px-4 py-3 text-center whitespace-nowrap">
@if($req->status === 'pending')
<div class="flex items-center justify-center gap-1">
<button type="button" onclick="approveRequest({{ $req->id }})"
class="px-3 py-1 text-xs bg-emerald-600 hover:bg-emerald-700 text-white rounded transition-colors">
승인
</button>
<button type="button" onclick="rejectRequest({{ $req->id }})"
class="px-3 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded transition-colors">
반려
</button>
</div>
@elseif($req->status === 'rejected' && $req->reject_reason)
<span class="text-xs text-red-500" title="{{ $req->reject_reason }}">
사유: {{ mb_substr($req->reject_reason, 0, 20) }}{{ mb_strlen($req->reject_reason) > 20 ? '...' : '' }}
</span>
@else
<span class="text-xs text-gray-400">-</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</x-table-swipe>
@if($requests->hasPages())
<div class="px-6 py-4 border-t border-gray-200 bg-gray-50">
{{ $requests->links() }}
</div>
@endif
@endif