252 lines
9.6 KiB
PHP
252 lines
9.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Absensi;
|
|
use App\Models\Teknisi;
|
|
use Illuminate\Http\Request;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class AbsensiController extends Controller
|
|
{
|
|
/**
|
|
* Menampilkan daftar data absensi dengan fitur filter.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$teknisis = Teknisi::orderBy('nama', 'asc')->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.'
|
|
]);
|
|
}
|
|
|
|
} |