get(); // Query dasar untuk data utama $query = Absensi::with('teknisi'); // Filter Rentang Tanggal if ($request->filled('start_date') && $request->filled('end_date')) { $query->whereBetween('tanggal', [$request->start_date, $request->end_date]); } elseif ($request->filled('start_date')) { $query->whereDate('tanggal', '>=', $request->start_date); } elseif ($request->filled('end_date')) { $query->whereDate('tanggal', '<=', $request->end_date); } // Filter Teknisi if ($request->filled('teknisi')) { $query->where('id_teknisi', $request->teknisi); } // Hitung statistik berdasarkan filter di atas (sebelum filter status) $counts = [ 'total' => (clone $query)->count(), 'hadir' => (clone $query)->where('status', 'hadir')->count(), 'izin' => (clone $query)->whereIn('status', ['izin', 'sakit'])->count(), ]; // Apply filter status untuk tabel if ($request->query('status') === 'izin') { $query->whereIn('status', ['izin', 'sakit']); } else { $query->filterByStatus($request->query('status')); } $absensis = $query->orderBy('tanggal', 'desc') ->orderBy('jam_masuk', 'desc') ->paginate(15); return view('Admin.KelolaTeknisi.Absensi', compact('absensis', 'teknisis', 'counts')); } /** * Menampilkan detail data absensi spesifik. */ public function show($id) { $absensi = Absensi::with('teknisi')->where('id_absensi', $id)->first(); if (!$absensi) { return response()->json([ 'error' => 'Data absensi tidak ditemukan.' ], 404); } // Debug log \Log::info('=== DEBUG ABSENSI SHOW ==='); \Log::info('Absensi ID: ' . $absensi->id_absensi); \Log::info('Foto Masuk Path: ' . ($absensi->foto_absen_masuk ?? 'NULL')); \Log::info('Foto Keluar Path: ' . ($absensi->foto_absen_keluar ?? 'NULL')); // Cek apakah file benar-benar ada if ($absensi->foto_absen_masuk) { $exists = Storage::disk('public')->exists($absensi->foto_absen_masuk); \Log::info('Foto Masuk Exists: ' . ($exists ? 'YES' : 'NO')); \Log::info('Full Path: ' . Storage::disk('public')->path($absensi->foto_absen_masuk)); } if ($absensi->foto_absen_keluar) { $exists = Storage::disk('public')->exists($absensi->foto_absen_keluar); \Log::info('Foto Keluar Exists: ' . ($exists ? 'YES' : 'NO')); \Log::info('Full Path: ' . Storage::disk('public')->path($absensi->foto_absen_keluar)); } // Generate URL yang benar $fotoMasukUrl = null; $fotoKeluarUrl = null; if ($absensi->foto_absen_masuk && Storage::disk('public')->exists($absensi->foto_absen_masuk)) { $fotoMasukUrl = Storage::url($absensi->foto_absen_masuk); \Log::info('Generated Foto Masuk URL: ' . $fotoMasukUrl); } if ($absensi->foto_absen_keluar && Storage::disk('public')->exists($absensi->foto_absen_keluar)) { $fotoKeluarUrl = Storage::url($absensi->foto_absen_keluar); \Log::info('Generated Foto Keluar URL: ' . $fotoKeluarUrl); } $data = [ 'id' => $absensi->id_absensi, 'tanggal' => $absensi->tanggal instanceof \Carbon\Carbon ? $absensi->tanggal->format('d/m/Y') : Carbon::parse($absensi->tanggal)->format('d/m/Y'), 'tanggal_full' => $absensi->tanggal instanceof \Carbon\Carbon ? $absensi->tanggal->isoFormat('dddd, D MMMM YYYY') : Carbon::parse($absensi->tanggal)->isoFormat('dddd, D MMMM YYYY'), 'jam_masuk' => $absensi->jam_masuk ? Carbon::parse($absensi->jam_masuk)->format('H:i') : '-', 'jam_keluar' => $absensi->jam_keluar ? Carbon::parse($absensi->jam_keluar)->format('H:i') : '-', 'durasi_kerja' => $absensi->durasi_kerja_formatted ?? '00:00', 'status' => $absensi->status, 'status_formatted' => ucfirst($absensi->status), 'status_badge_class' => $absensi->status_badge_class ?? 'badge-secondary', 'is_terlambat' => $absensi->is_terlambat ?? false, 'keterangan' => $absensi->keterangan ?? '-', 'teknisi' => [ 'id' => $absensi->teknisi ? $absensi->teknisi->id_teknisi : null, 'nama' => $absensi->teknisi ? $absensi->teknisi->nama : 'Teknisi tidak ditemukan', 'email' => ($absensi->teknisi && $absensi->teknisi->email) ? $absensi->teknisi->email : 'Email tidak tersedia', ], 'foto_masuk_url' => $fotoMasukUrl, 'foto_keluar_url' => $fotoKeluarUrl, 'kategori_kerja' => $absensi->kategori_kerja, 'latitude' => $absensi->latitude ?? '0', 'longitude' => $absensi->longitude ?? '0', ]; \Log::info('Response Data: ' . json_encode($data)); \Log::info('=== END DEBUG ==='); return response()->json([ 'success' => true, 'data' => $data ]); } /** * Mendapatkan statistik absensi */ public function getStatistik(Request $request) { $id_teknisi = $request->id_teknisi; $start = $request->start_date; $end = $request->end_date; // Ambil data mentah pakai Query Builder agar lebih pasti $data = \DB::table('absensis') ->whereBetween('tanggal', [$start . ' 00:00:00', $end . ' 23:59:59']) ->get(); $stats = ['hadir' => 0, 'terlambat' => 0, 'izin' => 0, 'sakit' => 0]; $trendGroups = []; $rankGroups = []; $weekly = ['Sen'=>0, 'Sel'=>0, 'Rab'=>0, 'Kam'=>0, 'Jum'=>0]; $dayMap = [1=>'Min', 2=>'Sen', 3=>'Sel', 4=>'Rab', 5=>'Kam', 6=>'Jum', 7=>'Sab']; foreach ($data as $d) { $status = strtolower($d->status); $tgl = \Carbon\Carbon::parse($d->tanggal); // 1. Filter Teknisi untuk Statistik Utama if (!$id_teknisi || $d->id_teknisi == $id_teknisi) { if (isset($stats[$status])) $stats[$status]++; $jam = $d->jam_masuk ? \Carbon\Carbon::parse($d->jam_masuk)->format('H:i') : ''; if ($jam && ($jam < '07:00' || $jam > '18:00')) $stats['terlambat']++; } // 2. Trend Bulanan (Gunakan semua data biar grafiknya penuh) $week = 'Minggu ' . ceil($tgl->day / 7); if (!isset($trendGroups[$week])) { $trendGroups[$week] = ['label' => $week, 'hadir' => 0, 'urgent' => 0]; } if ($status == 'hadir') $trendGroups[$week]['hadir']++; // 3. Ranking Data (Group by Teknisi) if (!isset($rankGroups[$d->id_teknisi])) { $tek = \DB::table('teknisis')->where('id_teknisi', $d->id_teknisi)->first(); $rankGroups[$d->id_teknisi] = [ 'name' => $tek ? $tek->nama : 'Unknown', 'init' => $tek ? strtoupper(substr($tek->nama, 0, 2)) : '??', 'total' => 0, 'hadir' => 0 ]; } $rankGroups[$d->id_teknisi]['total']++; if ($status == 'hadir') $rankGroups[$d->id_teknisi]['hadir']++; // 4. Weekly Data $dayName = $dayMap[$tgl->dayOfWeek + 1] ?? ''; if (isset($weekly[$dayName]) && $status == 'hadir') $weekly[$dayName]++; } // Finalisasi Ranking $rankings = collect($rankGroups)->map(function($r) { $r['pct'] = $r['total'] > 0 ? round(($r['hadir'] / $r['total']) * 100) : 0; return $r; })->sortByDesc('pct')->values()->take(5); // Finalisasi Trend (Urutkan Minggu 1, 2, dst) ksort($trendGroups); $trend = array_values($trendGroups); return response()->json([ 'success' => true, 'data' => array_merge($stats, [ 'trend' => $trend, 'rankings' => $rankings, 'weekly' => $weekly ]) ]); } /** * Memperbarui data absensi (digunakan oleh Admin). */ public function update(Request $request, $id) { $request->validate([ 'jam_masuk' => 'nullable', 'jam_keluar' => 'nullable', 'status' => 'required|in:hadir,izin,sakit', 'keterangan' => 'nullable|string' ]); $absensi = Absensi::findOrFail($id); $tgl = \Carbon\Carbon::parse($absensi->tanggal)->format('Y-m-d'); // Update fields $absensi->jam_masuk = $request->jam_masuk ? $tgl . ' ' . $request->jam_masuk : null; $absensi->jam_keluar = $request->jam_keluar ? $tgl . ' ' . $request->jam_keluar : null; $absensi->status = $request->status; $absensi->keterangan = $request->keterangan; $absensi->save(); return response()->json([ 'success' => true, 'message' => 'Data absensi berhasil diperbarui.' ]); } }