328 lines
18 KiB
PHP
328 lines
18 KiB
PHP
<?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.");
|
||
}
|
||
} |