MIF_E31230892/sim-pkpps/app/Http/Controllers/Admin/RiwayatKegiatanController.php

328 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// app/Http/Controllers/admin/RiwayatKegiatanController.php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\AbsensiKegiatan;
use App\Models\Kegiatan;
use App\Models\KategoriKegiatan;
use App\Models\Santri;
use App\Models\Kelas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class RiwayatKegiatanController extends Controller
{
/**
* Halaman utama riwayat — default: HARI INI
*
* Mode hari_ini → flat list kegiatan (paginated cards)
* Mode minggu_ini / custom → grouped by actual date, tiap tanggal = 1 tabel
*/
public function index(Request $request)
{
$hariMap = [
'Monday' => 'Senin', 'Tuesday' => 'Selasa', 'Wednesday' => 'Rabu',
'Thursday' => 'Kamis', 'Friday' => 'Jumat', 'Saturday' => 'Sabtu',
'Sunday' => 'Ahad',
];
// ── Tentukan mode & rentang tanggal ───────────────────────────────────
$mode = $request->get('mode', 'hari_ini');
if ($mode === 'minggu_ini') {
$dari = now()->startOfWeek(Carbon::MONDAY)->format('Y-m-d');
$sampai = now()->endOfWeek(Carbon::SUNDAY)->format('Y-m-d');
$tanggal = null;
} elseif ($mode === 'custom') {
$dari = $request->get('dari', now()->subDays(6)->format('Y-m-d'));
$sampai = $request->get('sampai', now()->format('Y-m-d'));
if ($dari > $sampai) { [$dari, $sampai] = [$sampai, $dari]; }
$tanggal = null;
} else {
$mode = 'hari_ini';
$tanggal = $request->get('tanggal', now()->format('Y-m-d'));
$dari = $tanggal;
$sampai = $tanggal;
}
$kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->orderBy('nama_kategori')->get();
$kategoriId = $request->get('kategori_id', '');
// ── Label periode ─────────────────────────────────────────────────────
if ($mode === 'hari_ini') {
$periodeLabel = Carbon::parse($dari)->locale('id')->isoFormat('dddd, D MMMM Y');
} elseif ($mode === 'minggu_ini') {
$periodeLabel = Carbon::parse($dari)->locale('id')->isoFormat('D MMM') . ' '
. Carbon::parse($sampai)->locale('id')->isoFormat('D MMM Y');
} else {
$periodeLabel = Carbon::parse($dari)->locale('id')->isoFormat('D MMM Y') . ' '
. Carbon::parse($sampai)->locale('id')->isoFormat('D MMM Y');
}
// ═══════════════════════════════════════════════════════════════════
// MODE HARI INI — flat list kegiatan (paginated)
// ═══════════════════════════════════════════════════════════════════
if ($mode === 'hari_ini') {
$hariDipilih = $hariMap[Carbon::parse($dari)->format('l')] ?? null;
$baseQuery = Kegiatan::with(['kategori', 'kelasKegiatan.kelompok'])
->when($hariDipilih, fn($q) => $q->where('hari', $hariDipilih))
->when($kategoriId, fn($q) => $q->where('kategori_id', $kategoriId))
->withCount([
'absensis as total_absensi' => fn($q) => $q->whereDate('tanggal', $dari),
'absensis as hadir' => fn($q) => $q->where('status', 'Hadir')->whereDate('tanggal', $dari),
'absensis as terlambat' => fn($q) => $q->where('status', 'Terlambat')->whereDate('tanggal', $dari),
'absensis as izin' => fn($q) => $q->where('status', 'Izin')->whereDate('tanggal', $dari),
'absensis as sakit' => fn($q) => $q->where('status', 'Sakit')->whereDate('tanggal', $dari),
'absensis as alpa' => fn($q) => $q->where('status', 'Alpa')->whereDate('tanggal', $dari),
'absensis as pulang' => fn($q) => $q->where('status', 'Pulang')->whereDate('tanggal', $dari),
])
->orderBy('waktu_mulai');
$kegiatans = (clone $baseQuery)->paginate(20)->appends($request->query());
$kegiatanPerTanggal = null;
$allItems = (clone $baseQuery)->get();
$summary = [
'hadir' => $allItems->sum('hadir') + $allItems->sum('terlambat'),
'terlambat' => $allItems->sum('terlambat'),
'izin' => $allItems->sum('izin'),
'sakit' => $allItems->sum('sakit'),
'alpa' => $allItems->sum('alpa'),
'total_absensi' => $allItems->sum('total_absensi'),
'jumlah_kegiatan' => $allItems->count(),
'jumlah_hari' => 1,
];
// ═══════════════════════════════════════════════════════════════════
// MODE MULTI-HARI — grouped by actual date
// ═══════════════════════════════════════════════════════════════════
} else {
$kegiatans = null;
// Ambil semua tanggal yang ada absensi dalam range (terbaru dulu)
$tanggalList = AbsensiKegiatan::selectRaw('DATE(tanggal) as tgl')
->whereDate('tanggal', '>=', $dari)
->whereDate('tanggal', '<=', $sampai)
->when($kategoriId, fn($q) => $q->whereHas('kegiatan', fn($qq) => $qq->where('kategori_id', $kategoriId)))
->groupBy('tgl')
->orderBy('tgl', 'desc')
->pluck('tgl');
// Untuk tiap tanggal: kegiatan + stats khusus tanggal itu saja
$kegiatanPerTanggal = collect();
foreach ($tanggalList as $tgl) {
$hariIndo = $hariMap[Carbon::parse($tgl)->format('l')] ?? null;
$items = Kegiatan::with(['kategori', 'kelasKegiatan.kelompok'])
->when($hariIndo, fn($q) => $q->where('hari', $hariIndo))
->when($kategoriId, fn($q) => $q->where('kategori_id', $kategoriId))
->whereHas('absensis', fn($q) => $q->whereDate('tanggal', $tgl))
->withCount([
'absensis as total_absensi' => fn($q) => $q->whereDate('tanggal', $tgl),
'absensis as hadir' => fn($q) => $q->where('status', 'Hadir')->whereDate('tanggal', $tgl),
'absensis as terlambat' => fn($q) => $q->where('status', 'Terlambat')->whereDate('tanggal', $tgl),
'absensis as izin' => fn($q) => $q->where('status', 'Izin')->whereDate('tanggal', $tgl),
'absensis as sakit' => fn($q) => $q->where('status', 'Sakit')->whereDate('tanggal', $tgl),
'absensis as alpa' => fn($q) => $q->where('status', 'Alpa')->whereDate('tanggal', $tgl),
'absensis as pulang' => fn($q) => $q->where('status', 'Pulang')->whereDate('tanggal', $tgl),
])
->orderBy('waktu_mulai')
->get();
if ($items->count() > 0) {
$kegiatanPerTanggal[$tgl] = $items;
}
}
$allKeg = $kegiatanPerTanggal->flatten();
$summary = [
'hadir' => $allKeg->sum('hadir') + $allKeg->sum('terlambat'),
'terlambat' => $allKeg->sum('terlambat'),
'izin' => $allKeg->sum('izin'),
'sakit' => $allKeg->sum('sakit'),
'alpa' => $allKeg->sum('alpa'),
'total_absensi' => $allKeg->sum('total_absensi'),
'jumlah_kegiatan' => $allKeg->count(),
'jumlah_hari' => $kegiatanPerTanggal->count(),
];
}
return view('admin.kegiatan.riwayat.index', compact(
'kegiatans', 'kegiatanPerTanggal', 'kategoris', 'summary', 'mode',
'dari', 'sampai', 'tanggal', 'periodeLabel', 'kategoriId'
));
}
/**
* Detail riwayat per kegiatan — santri per kelas + filter
*/
public function show($id, Request $request)
{
$kegiatan = Kegiatan::with(['kategori', 'kelasKegiatan.kelompok'])
->findOrFail($id);
// ── Ambil parameter periode dari index ────────────────────────────────
$mode = $request->get('mode', 'hari_ini');
$dari = $request->get('dari', now()->format('Y-m-d'));
$sampai = $request->get('sampai', now()->format('Y-m-d'));
$tanggal = $request->get('tanggal', now()->format('Y-m-d'));
if ($mode === 'hari_ini') {
$dari = $tanggal;
$sampai = $tanggal;
}
// ── Query absensi ─────────────────────────────────────────────────────
$query = AbsensiKegiatan::with(['santri.kelasSantri.kelas.kelompok'])
->where('kegiatan_id', $kegiatan->kegiatan_id)
->whereDate('tanggal', '>=', $dari)
->whereDate('tanggal', '<=', $sampai);
if ($request->filled('id_santri')) $query->where('id_santri', $request->id_santri);
if ($request->filled('id_kelas')) $query->whereHas('santri.kelasSantri', fn($q) => $q->where('id_kelas', $request->id_kelas));
if ($request->filled('status')) $query->where('status', $request->status);
if ($request->filled('tanggal_spesifik')) $query->whereDate('tanggal', $request->tanggal_spesifik);
$riwayats = $query->orderBy('tanggal', 'desc')->orderBy('waktu_absen')->paginate(50)->appends($request->query());
// ── Statistik ─────────────────────────────────────────────────────────
$statsQuery = AbsensiKegiatan::where('kegiatan_id', $kegiatan->kegiatan_id)
->whereDate('tanggal', '>=', $dari)->whereDate('tanggal', '<=', $sampai);
if ($request->filled('id_kelas')) {
$statsQuery->whereHas('santri.kelasSantri', fn($q) => $q->where('id_kelas', $request->id_kelas));
}
$stats = $statsQuery->select('status', DB::raw('count(*) as total'))
->groupBy('status')->pluck('total', 'status')->toArray();
// ── Total santri yang seharusnya hadir ────────────────────────────────
if ($kegiatan->kelasKegiatan->isEmpty()) {
$totalSantriEligible = Santri::where('status', 'Aktif')->count();
} else {
$kelasIds = $kegiatan->kelasKegiatan->pluck('id')->toArray();
$totalSantriEligible = Santri::where('status', 'Aktif')
->whereHas('kelasSantri', fn($q) => $q->whereIn('id_kelas', $kelasIds))
->count();
if ($totalSantriEligible === 0) $totalSantriEligible = Santri::where('status', 'Aktif')->count();
}
$totalRecorded = array_sum($stats);
$hadirCount = ($stats['Hadir'] ?? 0) + ($stats['Terlambat'] ?? 0);
$persenHadir = $totalSantriEligible > 0 ? round($hadirCount / $totalSantriEligible * 100, 1) : 0;
// ── Filter dropdown ───────────────────────────────────────────────────
$santris = Santri::where('status', 'Aktif')->select('id_santri', 'nama_lengkap')->orderBy('nama_lengkap')->get();
$kelasList = Kelas::active()->ordered()->with('kelompok')->get();
// ── Label periode ─────────────────────────────────────────────────────
if ($mode === 'hari_ini') {
$periodeLabel = Carbon::parse($dari)->locale('id')->isoFormat('dddd, D MMMM Y');
} else {
$periodeLabel = Carbon::parse($dari)->locale('id')->isoFormat('D MMM Y')
. ' '
. Carbon::parse($sampai)->locale('id')->isoFormat('D MMM Y');
}
return view('admin.kegiatan.riwayat.show', compact(
'kegiatan', 'riwayats', 'santris', 'kelasList', 'stats',
'totalSantriEligible', 'totalRecorded', 'persenHadir',
'mode', 'dari', 'sampai', 'tanggal', 'periodeLabel'
));
}
/**
* Riwayat kehadiran per santri — tabbed per kategori
*/
public function detailSantri($id_santri, Request $request)
{
$santri = Santri::where('id_santri', $id_santri)->firstOrFail();
// ── Statistik ringkasan (Terlambat = Hadir, bukan Alpa) ──────────────
$rawStats = AbsensiKegiatan::where('id_santri', $id_santri)
->select('status', DB::raw('count(*) as total'))
->groupBy('status')->pluck('total', 'status')->toArray();
// Hadir efektif = Hadir + Terlambat
$stats = $rawStats;
$stats['_hadir_efektif'] = ($rawStats['Hadir'] ?? 0) + ($rawStats['Terlambat'] ?? 0);
// ── Tren 30 hari — Hadir efektif (Hadir + Terlambat) ─────────────────
$riwayat30Hari = AbsensiKegiatan::where('id_santri', $id_santri)
->whereDate('tanggal', '>=', now()->subDays(29))
->select(
DB::raw('DATE(tanggal) as tanggal'),
DB::raw('SUM(CASE WHEN status IN ("Hadir","Terlambat") THEN 1 ELSE 0 END) as hadir'),
DB::raw('COUNT(*) as total')
)
->groupBy('tanggal')->orderBy('tanggal', 'asc')->get();
// ── Daftar semua kategori yang punya riwayat untuk santri ini ─────────
$kategoriList = AbsensiKegiatan::where('id_santri', $id_santri)
->join('kegiatans', 'absensi_kegiatans.kegiatan_id', '=', 'kegiatans.kegiatan_id')
->join('kategori_kegiatans', 'kegiatans.kategori_id', '=', 'kategori_kegiatans.kategori_id')
->select(
'kategori_kegiatans.kategori_id',
'kategori_kegiatans.nama_kategori',
DB::raw('COUNT(*) as total'),
DB::raw('SUM(CASE WHEN absensi_kegiatans.status IN ("Hadir","Terlambat") THEN 1 ELSE 0 END) as hadir_efektif')
)
->groupBy('kategori_kegiatans.kategori_id', 'kategori_kegiatans.nama_kategori')
->orderBy('kategori_kegiatans.nama_kategori')
->get();
// ── Tab aktif & per_page ───────────────────────────────────────────────
$activeKategori = $request->get('tab_kat', $kategoriList->first()?->kategori_id ?? '');
// per_page: 15 | 50 | 100 | 'all' (semua data, tanpa paginasi)
$perPageRaw = $request->get('per_page', 15);
$showAll = ($perPageRaw === 'all');
$perPage = $showAll ? 'all' : (in_array((int)$perPageRaw, [15, 50, 100]) ? (int)$perPageRaw : 15);
// ── Riwayat untuk tab aktif ────────────────────────────────────────────
$riwayatsQuery = AbsensiKegiatan::with('kegiatan.kategori')
->where('id_santri', $id_santri)
->whereHas('kegiatan', fn($q) => $q->where('kategori_id', $activeKategori))
->orderBy('tanggal', 'desc');
if ($showAll) {
// Wrap sebagai LengthAwarePaginator manual agar view tetap konsisten
$allItems = $riwayatsQuery->get();
$riwayats = new \Illuminate\Pagination\LengthAwarePaginator(
$allItems, $allItems->count(), max($allItems->count(), 1), 1,
['path' => $request->url(), 'query' => $request->query()]
);
} else {
$riwayats = $riwayatsQuery->paginate($perPage)->appends($request->query());
}
return view('admin.kegiatan.riwayat.detail-santri', compact(
'santri', 'stats', 'riwayat30Hari', 'riwayats',
'kategoriList', 'activeKategori', 'perPage', 'showAll'
));
}
public function edit(AbsensiKegiatan $riwayat)
{
$riwayat->load(['santri', 'kegiatan']);
return view('admin.kegiatan.riwayat.edit', compact('riwayat'));
}
public function update(Request $request, AbsensiKegiatan $riwayat)
{
$validated = $request->validate([
'status' => 'required|in:Hadir,Izin,Sakit,Alpa,Terlambat,Pulang',
'waktu_absen' => 'nullable|date_format:H:i',
]);
$riwayat->update($validated);
return redirect()->route('admin.riwayat-kegiatan.index')->with('success', 'Riwayat absensi berhasil diperbarui.');
}
public function destroy(AbsensiKegiatan $riwayat)
{
$nama = $riwayat->santri->nama_lengkap;
$riwayat->delete();
return redirect()->route('admin.riwayat-kegiatan.index')->with('success', "Riwayat absensi $nama berhasil dihapus.");
}
}