@extends('layouts.app') @push('page-styles') @endpush @section('page-content')
| {{ __('Employee') }} | @for ($day = 1; $day <= $days_in_month; $day++) @php $currentMonth = request()->month ?? now()->month; $year = request()->year ?? now()->year; $date = \Carbon\Carbon::createFromDate($year, $currentMonth, $day); $dayName = $date->format('D'); // Mon, Tue, Wed, etc. @endphp{{ $day }} ({{ $dayName }}) |
@endfor
|---|---|
|
@php
$img = !empty($employee->avatar) ? asset('storage/users/'.$employee->avatar): asset('images/user.jpg');
$link = route('employees.show', ['employee' => Crypt::encrypt($employee->id)]);
$departmentName = $employee->employeeDetail && $employee->employeeDetail->department
? $employee->employeeDetail->department->name
: '';
@endphp
{!! \Spatie\Menu\Laravel\Html::userAvatar($employee->fullname, $img, $link) !!}
@if($departmentName)
{{ $departmentName }}
@endif
|
@for ($day = 1; $day <= $days_in_month; $day++)
@php
$currentMonth = request()->month ?? now()->month;
$year = request()->year ?? now()->year;
$currentDate = \Carbon\Carbon::createFromDate($year, $currentMonth, $day)->toDateString();
// Pre-bucket attendances by 09:40 AM shift logic using device punches
static $attendanceCache = [];
$employeeKey = $employee->id;
if (!isset($attendanceCache[$employeeKey])) {
$attendanceCache[$employeeKey] = [];
// 1. Process Device Punches
$punches = $employee->devicePunches;
// Note: Controller filters these roughly by month,
// but we should iterate all available to ensuring binding.
foreach ($punches as $punch) {
$time = \Carbon\Carbon::parse($punch->punch_time);
// Determine "Logical Date": If before 09:40 AM, it belongs to previous day
if ($time->format('H:i') < '09:40') {
$dateKey = $time->copy()->subDay()->toDateString();
} else {
$dateKey = $time->toDateString();
}
if (!isset($attendanceCache[$employeeKey][$dateKey])) {
$attendanceCache[$employeeKey][$dateKey] = [
'punches' => [],
'attendance_id' => null // will try to find matching attendance
];
}
$attendanceCache[$employeeKey][$dateKey]['punches'][] = $time;
}
// 2. Map existing Attendances to dates to get IDs for the popup link
foreach ($employee->attendances as $att) {
if ($att->attendance_date) {
$dKey = $att->attendance_date->toDateString();
if (!isset($attendanceCache[$employeeKey][$dKey])) {
$attendanceCache[$employeeKey][$dKey] = [
'punches' => [],
'attendance_id' => $att->id
];
} else {
$attendanceCache[$employeeKey][$dKey]['attendance_id'] = $att->id;
}
}
}
}
// Retrieve for current cell
$dayData = $attendanceCache[$employeeKey][$currentDate] ?? null;
$displayCheckIn = null;
$displayCheckOut = null;
$attendanceId = $dayData['attendance_id'] ?? null;
if ($dayData && !empty($dayData['punches'])) {
$sortedPunches = collect($dayData['punches'])->sort();
$displayCheckIn = $sortedPunches->first();
$displayCheckOut = $sortedPunches->count() > 1 ? $sortedPunches->last() : null;
// If only one punch, it's check-in. Check-out stays null.
} else {
// Fallback to strict attendance object dates if no punches found but attendance exists
// (Already handled by step 2 above, if punches array is empty)
// But if we want to trust the attendance object's checks:
if ($dayData && $attendanceId) {
// We have attendance ID but no punches found in that bucket.
// This might happen if punches are not synced or deleted.
// Ideally we should have loaded attendance times earlier if we wanted this fallback.
// For now, let's rely on punches as primary source of truth for 11AM logic.
}
}
@endphp
@if ($displayCheckIn)
{{-- We need an ID for the link. If we have derived formatted times but no DB attendance record yet, we can't show details --}}
{{-- However, usually an attendance record exists if punches exist. If not, link might be broken or we leave it. --}}
@if ($attendanceId)
@else
@endif
{{ $displayCheckIn->format('h:i A') }}
@php
// Lateness Calculation with Grace Period Color Coding
$startTimeStr = $employee->employeeDetail ? $employee->employeeDetail->start_time : null;
$lateText = '';
$lateColorClass = 'text-danger'; // Default red
$gracePeriodMinutes = 15;
if ($startTimeStr) {
// Parse start time (it's usually H:i:s from DB)
// We need to combine it with the current date of the punch to compare apples to apples
$scheduleStartTime = \Carbon\Carbon::parse($displayCheckIn->format('Y-m-d') . ' ' . $startTimeStr);
if ($displayCheckIn->gt($scheduleStartTime)) {
$diffInMinutes = $scheduleStartTime->diffInMinutes($displayCheckIn);
if ($diffInMinutes > 0) {
$hours = intdiv($diffInMinutes, 60);
$minutes = $diffInMinutes % 60;
// Set color based on grace period: Red only for 16 minutes or more
if ($diffInMinutes >= 16) {
$lateColorClass = 'text-danger'; // Red for 16+ minutes late
} else {
$lateColorClass = 'text-success'; // Green for 1-15 minutes late
}
if ($hours > 0) {
if ($minutes > 0) {
$lateText = sprintf('%d hr %d min late', $hours, $minutes);
} else {
$lateText = sprintf('%d hr late', $hours);
}
} else {
$lateText = sprintf('%d min late', $minutes);
}
}
}
}
@endphp
@if ($lateText)
@if ($displayCheckOut)
@php
$checkoutNextDay = $displayCheckOut->gt($displayCheckIn->copy()->endOfDay());
@endphp
{{ $lateText }}
@endif
{{ $displayCheckOut->format('h:i A') }}
@if ($checkoutNextDay)
next day
@endif
@endif
@else
-
@endif
|
@endfor