616 lines
28 KiB
PHP
616 lines
28 KiB
PHP
<?php
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Kegiatan;
|
|
use App\Models\KategoriKegiatan;
|
|
use App\Models\KelompokKelas;
|
|
use App\Models\Kelas;
|
|
use App\Models\AbsensiKegiatan;
|
|
use App\Models\Santri;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Carbon\Carbon;
|
|
|
|
class KegiatanController extends Controller
|
|
{
|
|
/**
|
|
* Dashboard Kegiatan Hari Ini
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$selectedDate = $request->filled('tanggal')
|
|
? Carbon::parse($request->tanggal)
|
|
: Carbon::now();
|
|
|
|
$hariIndonesia = [
|
|
'Monday' => 'Senin',
|
|
'Tuesday' => 'Selasa',
|
|
'Wednesday' => 'Rabu',
|
|
'Thursday' => 'Kamis',
|
|
'Friday' => 'Jumat',
|
|
'Saturday' => 'Sabtu',
|
|
'Sunday' => 'Ahad',
|
|
];
|
|
|
|
$selectedHari = $hariIndonesia[$selectedDate->format('l')];
|
|
$selectedKelasId = $request->filled('kelas') ? $request->kelas : null;
|
|
|
|
$query = Kegiatan::with(['kategori', 'kelasKegiatan.kelompok', 'absensis' => function ($q) use ($selectedDate) {
|
|
$q->whereDate('tanggal', $selectedDate->format('Y-m-d'));
|
|
}])->where('hari', $selectedHari);
|
|
|
|
if ($selectedKelasId) {
|
|
if ($selectedKelasId === 'umum') {
|
|
$query->doesntHave('kelasKegiatan');
|
|
} else {
|
|
$query->whereHas('kelasKegiatan', function ($q) use ($selectedKelasId) {
|
|
$q->where('kelas.id', $selectedKelasId);
|
|
});
|
|
}
|
|
}
|
|
|
|
if ($request->filled('kategori_id')) {
|
|
$query->where('kategori_id', $request->kategori_id);
|
|
}
|
|
|
|
$kegiatanHariIni = $query->orderBy('waktu_mulai')->get();
|
|
|
|
// ── Total santri aktif (dipakai sebagai denominator kegiatan umum) ──────
|
|
$totalSantriAktif = Santri::where('status', 'Aktif')->count();
|
|
|
|
// ── Pre-load semua santri aktif beserta kelas mereka (1 query) ────────────
|
|
// Ini dipakai untuk menghitung "berapa santri yang seharusnya hadir" per kegiatan
|
|
$allSantriAktif = Santri::where('status', 'Aktif')
|
|
->with('kelasSantri')
|
|
->get();
|
|
|
|
$kegiatanHariIni->each(function ($kegiatan) use ($totalSantriAktif, $selectedDate, $allSantriAktif) {
|
|
$absensis = $kegiatan->absensis; // sudah di-eager-load
|
|
$totalAbsensi = $absensis->count();
|
|
|
|
// ── FIX 1: Terlambat = hadir (masuk), bukan alpha ──────────────────
|
|
$hadirEfektif = $absensis->whereIn('status', ['Hadir', 'Terlambat'])->count();
|
|
$terlambat = $absensis->where('status', 'Terlambat')->count();
|
|
$izin = $absensis->where('status', 'Izin')->count();
|
|
$sakit = $absensis->where('status', 'Sakit')->count();
|
|
$alpa = $absensis->where('status', 'Alpa')->count();
|
|
|
|
// ── FIX 2: Denominator = jumlah santri yg seharusnya ikut kegiatan ini ──
|
|
if ($kegiatan->kelasKegiatan->isEmpty()) {
|
|
// Kegiatan Umum → semua santri aktif
|
|
$totalSantriKegiatan = $totalSantriAktif;
|
|
} else {
|
|
// Kegiatan Khusus → hitung santri yang terdaftar di kelas kegiatan
|
|
$kelasIds = $kegiatan->kelasKegiatan->pluck('id')->toArray();
|
|
$totalSantriKegiatan = $allSantriAktif->filter(function ($s) use ($kelasIds) {
|
|
return $s->kelasSantri->whereIn('id_kelas', $kelasIds)->count() > 0;
|
|
})->count();
|
|
// Fallback jika tidak ada santri terdaftar di kelas
|
|
if ($totalSantriKegiatan === 0) $totalSantriKegiatan = $totalSantriAktif;
|
|
}
|
|
|
|
// ── FIX 3: Persentase berdasarkan total santri kegiatan, bukan yg sudah absen ──
|
|
$persenKehadiran = $totalSantriKegiatan > 0
|
|
? round(($hadirEfektif / $totalSantriKegiatan) * 100)
|
|
: 0;
|
|
|
|
// ── FIX 4: Info per kelas — berapa kelas sudah/belum input ───────────
|
|
$infoPerKelas = collect();
|
|
if (!$kegiatan->kelasKegiatan->isEmpty()) {
|
|
$absensiByKelas = $absensis->groupBy(function ($ab) {
|
|
// group berdasarkan kelas santri yg hadir (ambil kelas pertama yg sesuai)
|
|
return $ab->santri->kelas_name ?? 'Tanpa Kelas';
|
|
});
|
|
|
|
foreach ($kegiatan->kelasKegiatan as $kls) {
|
|
$kelasId = $kls->id;
|
|
$santriDiKelas = $allSantriAktif->filter(function ($s) use ($kelasId) {
|
|
return $s->kelasSantri->where('id_kelas', $kelasId)->count() > 0;
|
|
})->count();
|
|
|
|
// Berapa dari kelas ini yang sudah diinput absensi hari ini
|
|
// Cek dari absensis: santri yg ada di kelas ini
|
|
$sudahInputKelas = 0; // akan diisi di blade via data yg dikirim
|
|
|
|
$infoPerKelas->push([
|
|
'nama' => $kls->nama_kelas,
|
|
'total_santri' => $santriDiKelas,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ── Status kegiatan (belum / berlangsung / selesai) ─────────────────
|
|
$now = Carbon::now();
|
|
$waktuMulaiStr = is_string($kegiatan->waktu_mulai) ? $kegiatan->waktu_mulai : $kegiatan->waktu_mulai->format('H:i');
|
|
$waktuSelesaiStr = is_string($kegiatan->waktu_selesai) ? $kegiatan->waktu_selesai : $kegiatan->waktu_selesai->format('H:i');
|
|
$waktuMulai = Carbon::parse($selectedDate->format('Y-m-d') . ' ' . $waktuMulaiStr);
|
|
$waktuSelesai = Carbon::parse($selectedDate->format('Y-m-d') . ' ' . $waktuSelesaiStr);
|
|
|
|
if ($selectedDate->isToday()) {
|
|
if ($now->lt($waktuMulai)) $status = 'belum';
|
|
elseif ($now->between($waktuMulai, $waktuSelesai)) $status = 'berlangsung';
|
|
else $status = 'selesai';
|
|
} elseif ($selectedDate->isFuture()) {
|
|
$status = 'belum';
|
|
} else {
|
|
$status = 'selesai';
|
|
}
|
|
|
|
// Set semua property ke object kegiatan
|
|
$kegiatan->total_hadir = $hadirEfektif; // hadir + terlambat
|
|
$kegiatan->total_hadir_murni = $absensis->where('status', 'Hadir')->count();
|
|
$kegiatan->total_terlambat = $terlambat;
|
|
$kegiatan->total_izin = $izin;
|
|
$kegiatan->total_sakit = $sakit;
|
|
$kegiatan->total_alpa = $alpa;
|
|
$kegiatan->total_absensi = $totalAbsensi; // sudah diinput
|
|
$kegiatan->total_santri_kegiatan = $totalSantriKegiatan; // seharusnya hadir
|
|
$kegiatan->belum_absen = max(0, $totalSantriKegiatan - $totalAbsensi);
|
|
$kegiatan->persen_kehadiran = $persenKehadiran;
|
|
$kegiatan->status_kegiatan = $status;
|
|
$kegiatan->info_per_kelas = $infoPerKelas;
|
|
});
|
|
|
|
$totalKegiatanHariIni = $kegiatanHariIni->count();
|
|
$kegiatanSelesai = $kegiatanHariIni->where('status_kegiatan', 'selesai')->count();
|
|
$kegiatanBerlangsung = $kegiatanHariIni->where('status_kegiatan', 'berlangsung')->count();
|
|
$avgKehadiran = $kegiatanHariIni->count() > 0 ? round($kegiatanHariIni->avg('persen_kehadiran')) : 0;
|
|
|
|
$lastWeekDate = $selectedDate->copy()->subWeek();
|
|
$lastWeekHari = $hariIndonesia[$lastWeekDate->format('l')];
|
|
$kegiatanLastWeekCount = Kegiatan::where('hari', $lastWeekHari)->count();
|
|
$comparisonTotal = $totalKegiatanHariIni - $kegiatanLastWeekCount;
|
|
|
|
$avgKehadiranLastWeek = Cache::remember('avg_kehadiran_' . $lastWeekDate->format('Y-m-d'), 600, function () use ($lastWeekDate, $lastWeekHari) {
|
|
$list = Kegiatan::where('hari', $lastWeekHari)->get();
|
|
$totalPersen = 0;
|
|
$count = 0;
|
|
foreach ($list as $kg) {
|
|
$abs = AbsensiKegiatan::where('kegiatan_id', $kg->kegiatan_id)
|
|
->whereDate('tanggal', $lastWeekDate->format('Y-m-d'))->get();
|
|
if ($abs->count() > 0) {
|
|
// FIX: hitung hadir + terlambat, bukan hadir saja
|
|
$hadirCount = $abs->whereIn('status', ['Hadir', 'Terlambat'])->count();
|
|
$totalPersen += ($hadirCount / $abs->count()) * 100;
|
|
$count++;
|
|
}
|
|
}
|
|
return $count > 0 ? round($totalPersen / $count) : 0;
|
|
});
|
|
|
|
$comparisonAvg = $avgKehadiran - $avgKehadiranLastWeek;
|
|
$kelasList = Kelas::with('kelompok')->active()->ordered()->get();
|
|
$kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get();
|
|
$insights = $this->generateInsights($kegiatanHariIni, $totalSantriAktif, $selectedDate);
|
|
|
|
$heatmapData = Cache::remember('heatmap_30days_' . now()->format('Y-m-d'), 600, function () {
|
|
return $this->generateHeatmapData();
|
|
});
|
|
|
|
$hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad'];
|
|
|
|
return view('admin.kegiatan.data.dashboard', compact(
|
|
'kegiatanHariIni', 'totalKegiatanHariIni', 'kegiatanSelesai',
|
|
'kegiatanBerlangsung', 'avgKehadiran', 'totalSantriAktif',
|
|
'selectedDate', 'selectedHari', 'hariList', 'kelasList',
|
|
'selectedKelasId', 'comparisonTotal', 'comparisonAvg',
|
|
'insights', 'heatmapData', 'kategoris'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Generate Quick Insights (Rule-Based AI)
|
|
*/
|
|
private function generateInsights($kegiatanHariIni, $totalSantriAktif, $selectedDate)
|
|
{
|
|
$insights = [];
|
|
|
|
foreach ($kegiatanHariIni as $kegiatan) {
|
|
if ($kegiatan->total_absensi > 0 && $kegiatan->persen_kehadiran < 70) {
|
|
$insights[] = [
|
|
'type' => 'warning',
|
|
'icon' => 'exclamation-triangle',
|
|
'message' => "Kegiatan {$kegiatan->nama_kegiatan} kehadiran rendah ({$kegiatan->persen_kehadiran}%)",
|
|
'detail' => "{$kegiatan->total_hadir} hadir dari {$kegiatan->total_santri_kegiatan} santri",
|
|
'action_url' => route('admin.absensi-kegiatan.input', $kegiatan->kegiatan_id) . '?tanggal=' . $selectedDate->format('Y-m-d'),
|
|
'action_text' => 'Input Absensi',
|
|
];
|
|
}
|
|
}
|
|
|
|
foreach ($kegiatanHariIni as $kegiatan) {
|
|
if ($kegiatan->persen_kehadiran == 100 && $kegiatan->total_absensi > 0) {
|
|
$insights[] = [
|
|
'type' => 'success',
|
|
'icon' => 'check-circle',
|
|
'message' => "Perfect! {$kegiatan->nama_kegiatan} kehadiran 100%",
|
|
'detail' => "Semua {$kegiatan->total_santri_kegiatan} santri hadir",
|
|
'action_url' => null,
|
|
'action_text' => null,
|
|
];
|
|
}
|
|
}
|
|
|
|
$kegiatanLive = $kegiatanHariIni->where('status_kegiatan', 'berlangsung')->first();
|
|
if ($kegiatanLive) {
|
|
$insights[] = [
|
|
'type' => 'info',
|
|
'icon' => 'clock',
|
|
'message' => "Kegiatan {$kegiatanLive->nama_kegiatan} sedang berlangsung",
|
|
'detail' => "Progress absensi: {$kegiatanLive->total_absensi}/{$kegiatanLive->total_santri_kegiatan} santri ({$kegiatanLive->persen_kehadiran}%)",
|
|
'action_url' => route('admin.absensi-kegiatan.input', $kegiatanLive->kegiatan_id) . '?tanggal=' . $selectedDate->format('Y-m-d'),
|
|
'action_text' => 'Input Absensi Sekarang',
|
|
];
|
|
}
|
|
|
|
foreach ($kegiatanHariIni as $kegiatan) {
|
|
if ($kegiatan->status_kegiatan == 'selesai' && $kegiatan->total_absensi == 0) {
|
|
$waktuSelesai = is_string($kegiatan->waktu_selesai) ? $kegiatan->waktu_selesai : $kegiatan->waktu_selesai->format('H:i');
|
|
$insights[] = [
|
|
'type' => 'danger',
|
|
'icon' => 'exclamation-circle',
|
|
'message' => "Kegiatan {$kegiatan->nama_kegiatan} belum input absensi",
|
|
'detail' => "Sudah selesai pukul {$waktuSelesai}, {$kegiatan->total_santri_kegiatan} santri belum diinput",
|
|
'action_url' => route('admin.absensi-kegiatan.input', $kegiatan->kegiatan_id) . '?tanggal=' . $selectedDate->format('Y-m-d'),
|
|
'action_text' => 'Input Sekarang',
|
|
];
|
|
}
|
|
}
|
|
|
|
return collect($insights)->take(5)->toArray();
|
|
}
|
|
|
|
/**
|
|
* Generate Heatmap Data (30 hari terakhir)
|
|
* FIX: hitung hadir + terlambat, bukan hadir saja
|
|
*/
|
|
private function generateHeatmapData()
|
|
{
|
|
$heatmapData = [];
|
|
$startDate = Carbon::now()->subDays(29);
|
|
|
|
for ($i = 0; $i < 30; $i++) {
|
|
$date = $startDate->copy()->addDays($i);
|
|
$dateStr = $date->format('Y-m-d');
|
|
$absensi = AbsensiKegiatan::whereDate('tanggal', $dateStr)->get();
|
|
|
|
$percentage = $absensi->count() > 0
|
|
? round(($absensi->whereIn('status', ['Hadir', 'Terlambat'])->count() / $absensi->count()) * 100, 1)
|
|
: 0;
|
|
|
|
$heatmapData[] = [
|
|
'date' => $dateStr,
|
|
'day_name' => $date->locale('id')->isoFormat('ddd'),
|
|
'percentage' => $percentage,
|
|
'level' => $this->getHeatmapLevel($percentage),
|
|
'is_today' => $date->isToday(),
|
|
];
|
|
}
|
|
|
|
return $heatmapData;
|
|
}
|
|
|
|
private function getHeatmapLevel($percentage)
|
|
{
|
|
if ($percentage >= 90) return 4;
|
|
if ($percentage >= 80) return 3;
|
|
if ($percentage >= 70) return 2;
|
|
if ($percentage > 0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* AJAX: Get Detail Kegiatan untuk Modal
|
|
* FIX: persen_hadir juga ikut hitung Terlambat
|
|
*/
|
|
public function getDetailModal($kegiatan_id, Request $request)
|
|
{
|
|
$tanggal = $request->get('tanggal', now()->format('Y-m-d'));
|
|
$kegiatan = Kegiatan::with(['kategori', 'kelasKegiatan.kelompok'])
|
|
->where('kegiatan_id', $kegiatan_id)->firstOrFail();
|
|
|
|
$absensis = AbsensiKegiatan::with(['santri.kelasSantri.kelas'])
|
|
->where('kegiatan_id', $kegiatan_id)
|
|
->whereDate('tanggal', $tanggal)
|
|
->orderBy('waktu_absen', 'desc')->get();
|
|
|
|
$isUmum = $kegiatan->isForAllClasses();
|
|
|
|
// Grup absensi per kelas
|
|
if ($isUmum) {
|
|
$absensiPerKelas = $absensis->groupBy(fn($item) => $item->santri->kelas_name ?? 'Belum Ada Kelas')->sortKeys();
|
|
} else {
|
|
$absensiPerKelas = collect();
|
|
foreach ($kegiatan->kelasKegiatan as $kelas) {
|
|
$filtered = $absensis->filter(fn($item) => $item->santri->kelasSantri->contains('id_kelas', $kelas->id));
|
|
if ($filtered->count() > 0) $absensiPerKelas[$kelas->nama_kelas] = $filtered;
|
|
}
|
|
$placedIds = $absensiPerKelas->flatten()->pluck('id')->toArray();
|
|
$lainnya = $absensis->filter(fn($item) => !in_array($item->id, $placedIds));
|
|
if ($lainnya->count() > 0) $absensiPerKelas['Kelas Lain'] = $lainnya;
|
|
}
|
|
|
|
// FIX: hadir efektif = Hadir + Terlambat
|
|
$hadirEfektif = $absensis->whereIn('status', ['Hadir', 'Terlambat'])->count();
|
|
|
|
// Total santri yang seharusnya hadir
|
|
if ($isUmum) {
|
|
$totalSantri = Santri::where('status', 'Aktif')->count();
|
|
} else {
|
|
$kelasIds = $kegiatan->kelasKegiatan->pluck('id')->toArray();
|
|
$totalSantri = Santri::where('status', 'Aktif')
|
|
->whereHas('kelasSantri', fn($q) => $q->whereIn('id_kelas', $kelasIds))
|
|
->count();
|
|
if ($totalSantri === 0) $totalSantri = Santri::where('status', 'Aktif')->count();
|
|
}
|
|
|
|
$stats = [
|
|
'hadir' => $absensis->where('status', 'Hadir')->count(),
|
|
'terlambat' => $absensis->where('status', 'Terlambat')->count(),
|
|
'hadir_efektif'=> $hadirEfektif, // hadir + terlambat
|
|
'izin' => $absensis->where('status', 'Izin')->count(),
|
|
'sakit' => $absensis->where('status', 'Sakit')->count(),
|
|
'alpa' => $absensis->where('status', 'Alpa')->count(),
|
|
];
|
|
|
|
$stats['belum_absen'] = max(0, $totalSantri - $absensis->count());
|
|
$stats['sudah_absen'] = $absensis->count();
|
|
$stats['total'] = $totalSantri;
|
|
// FIX: persen hadir pakai hadir_efektif bukan hadir saja
|
|
$stats['persen_hadir'] = $totalSantri > 0
|
|
? round(($hadirEfektif / $totalSantri) * 100, 1)
|
|
: 0;
|
|
|
|
// ── Info per kelas untuk modal: sudah/belum input ───────────────────────
|
|
$infoKelasModal = collect();
|
|
if (!$isUmum) {
|
|
$allSantriAktif = Santri::where('status', 'Aktif')->with('kelasSantri')->get();
|
|
foreach ($kegiatan->kelasKegiatan as $kls) {
|
|
$kelasId = $kls->id;
|
|
$santriDiKelas = $allSantriAktif->filter(fn($s) => $s->kelasSantri->where('id_kelas', $kelasId)->count() > 0)->count();
|
|
$sudahInputKelas = $absensis->filter(fn($ab) =>
|
|
$ab->santri->kelasSantri->contains('id_kelas', $kelasId)
|
|
)->count();
|
|
$infoKelasModal->push([
|
|
'nama' => $kls->nama_kelas,
|
|
'total_santri' => $santriDiKelas,
|
|
'sudah_input' => $sudahInputKelas,
|
|
'belum_input' => max(0, $santriDiKelas - $sudahInputKelas),
|
|
'sudah_semua' => $sudahInputKelas >= $santriDiKelas && $santriDiKelas > 0,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Daftar santri belum absen
|
|
$idSantriSudahAbsen = $absensis->pluck('id_santri')->toArray();
|
|
$belumQuery = Santri::where('status', 'Aktif')
|
|
->whereNotIn('id_santri', $idSantriSudahAbsen)
|
|
->with(['kelasSantri.kelas', 'kelasPrimary.kelas'])
|
|
->orderBy('nama_lengkap');
|
|
|
|
if (!$isUmum) {
|
|
$kelasIds = $kegiatan->kelasKegiatan->pluck('id')->toArray();
|
|
$belumQuery->whereHas('kelasSantri', fn($q) => $q->whereIn('id_kelas', $kelasIds));
|
|
}
|
|
|
|
$allBelumAbsen = $belumQuery->get();
|
|
|
|
if ($isUmum) {
|
|
$santriBelumAbsenPerKelas = $allBelumAbsen->groupBy(function($s) {
|
|
return optional(optional($s->kelasPrimary)->kelas)->nama_kelas ?? 'Tanpa Kelas';
|
|
})->sortKeys();
|
|
} else {
|
|
$santriBelumAbsenPerKelas = collect();
|
|
$placedBelumIds = [];
|
|
foreach ($kegiatan->kelasKegiatan as $kelas) {
|
|
$inKelas = $allBelumAbsen->filter(function($s) use ($kelas, &$placedBelumIds) {
|
|
if (in_array($s->id_santri, $placedBelumIds)) return false;
|
|
return $s->kelasSantri->contains('id_kelas', $kelas->id);
|
|
});
|
|
foreach ($inKelas as $s) $placedBelumIds[] = $s->id_santri;
|
|
if ($inKelas->count() > 0) $santriBelumAbsenPerKelas[$kelas->nama_kelas] = $inKelas;
|
|
}
|
|
$lainnyaBelum = $allBelumAbsen->whereNotIn('id_santri', $placedBelumIds);
|
|
if ($lainnyaBelum->count() > 0) $santriBelumAbsenPerKelas['Kelas Lain'] = $lainnyaBelum;
|
|
}
|
|
|
|
$santriBelumAbsen = $allBelumAbsen;
|
|
|
|
return view('admin.kegiatan.data.partials.detail-modal', compact(
|
|
'kegiatan', 'absensis', 'absensiPerKelas', 'stats', 'tanggal',
|
|
'santriBelumAbsen', 'santriBelumAbsenPerKelas', 'infoKelasModal'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Jadwal Kegiatan Lengkap
|
|
*/
|
|
public function jadwal(Request $request)
|
|
{
|
|
$query = Kegiatan::with(['kategori', 'kelasKegiatan.kelompok']);
|
|
|
|
if ($request->filled('hari')) $query->where('hari', $request->hari);
|
|
if ($request->filled('kategori_id')) $query->where('kategori_id', $request->kategori_id);
|
|
if ($request->filled('kelas_id')) {
|
|
if ($request->kelas_id === 'umum') {
|
|
$query->doesntHave('kelasKegiatan');
|
|
} else {
|
|
$query->whereHas('kelasKegiatan', fn($q) => $q->where('kelas.id', $request->kelas_id));
|
|
}
|
|
}
|
|
if ($request->filled('search')) $query->search($request->search);
|
|
|
|
$kegiatans = $query->select('id', 'kegiatan_id', 'kategori_id', 'nama_kegiatan', 'hari', 'waktu_mulai', 'waktu_selesai', 'materi')
|
|
->orderBy('hari')->orderBy('waktu_mulai')
|
|
->paginate(15)->appends(request()->query());
|
|
|
|
$kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get();
|
|
$hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad'];
|
|
$kelasList = Kelas::with('kelompok')->active()->ordered()->get();
|
|
|
|
return view('admin.kegiatan.data.index', compact('kegiatans', 'kategoris', 'hariList', 'kelasList'));
|
|
}
|
|
|
|
/**
|
|
* Form tambah kegiatan
|
|
*/
|
|
public function create()
|
|
{
|
|
$nextId = Cache::remember('next_kegiatan_id', 60, function () {
|
|
$last = Kegiatan::select('kegiatan_id')->orderBy('id', 'desc')->first();
|
|
$num = $last ? intval(substr($last->kegiatan_id, 2)) + 1 : 1;
|
|
return 'KG' . str_pad($num, 3, '0', STR_PAD_LEFT);
|
|
});
|
|
|
|
$kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get();
|
|
$hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad'];
|
|
$kelompokKelas = KelompokKelas::with(['kelas' => fn($q) => $q->where('is_active', true)->orderBy('urutan')])
|
|
->active()->ordered()->get();
|
|
|
|
return view('admin.kegiatan.data.create', compact('nextId', 'kategoris', 'hariList', 'kelompokKelas'));
|
|
}
|
|
|
|
/**
|
|
* Simpan kegiatan baru
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'kategori_id' => 'required|exists:kategori_kegiatans,kategori_id',
|
|
'nama_kegiatan' => 'required|string|max:150',
|
|
'hari' => 'required|array|min:1',
|
|
'hari.*' => 'in:Senin,Selasa,Rabu,Kamis,Jumat,Sabtu,Ahad',
|
|
'waktu_mulai' => 'required|date_format:H:i',
|
|
'waktu_selesai' => 'required|date_format:H:i|after:waktu_mulai',
|
|
'materi' => 'nullable|string|max:200',
|
|
'keterangan' => 'nullable|string',
|
|
'kelas_ids' => 'nullable|array',
|
|
'kelas_ids.*' => 'exists:kelas,id',
|
|
], [
|
|
'kategori_id.required' => 'Kategori wajib dipilih.',
|
|
'nama_kegiatan.required' => 'Nama kegiatan wajib diisi.',
|
|
'hari.required' => 'Minimal pilih satu hari.',
|
|
'hari.min' => 'Minimal pilih satu hari.',
|
|
'waktu_mulai.required' => 'Waktu mulai wajib diisi.',
|
|
'waktu_selesai.required' => 'Waktu selesai wajib diisi.',
|
|
'waktu_selesai.after' => 'Waktu selesai harus lebih dari waktu mulai.',
|
|
]);
|
|
|
|
$hariList = $validated['hari'];
|
|
unset($validated['hari']);
|
|
$createdCount = 0;
|
|
|
|
foreach ($hariList as $hari) {
|
|
$kg = Kegiatan::create(array_merge($validated, ['hari' => $hari]));
|
|
if ($request->has('kelas_ids') && !empty($request->kelas_ids)) {
|
|
$kg->assignKelas($request->kelas_ids);
|
|
}
|
|
$createdCount++;
|
|
}
|
|
|
|
Cache::forget('next_kegiatan_id');
|
|
|
|
$message = $createdCount > 1
|
|
? "Berhasil menambahkan kegiatan untuk {$createdCount} hari."
|
|
: 'Kegiatan berhasil ditambahkan.';
|
|
|
|
return redirect()->route('admin.kegiatan.jadwal')->with('success', $message);
|
|
}
|
|
|
|
/**
|
|
* Tampilkan detail kegiatan
|
|
*/
|
|
public function show(Kegiatan $kegiatan)
|
|
{
|
|
$kegiatan->load(['kategori', 'kelasKegiatan.kelompok']);
|
|
return view('admin.kegiatan.data.show', compact('kegiatan'));
|
|
}
|
|
|
|
/**
|
|
* Form edit kegiatan
|
|
*/
|
|
public function edit(Kegiatan $kegiatan)
|
|
{
|
|
$kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get();
|
|
$hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad'];
|
|
$kelompokKelas = KelompokKelas::with(['kelas' => fn($q) => $q->where('is_active', true)->orderBy('urutan')])
|
|
->active()->ordered()->get();
|
|
|
|
$kegiatan->load('kelasKegiatan');
|
|
|
|
return view('admin.kegiatan.data.edit', compact('kegiatan', 'kategoris', 'hariList', 'kelompokKelas'));
|
|
}
|
|
|
|
/**
|
|
* Update kegiatan — smart multi-hari
|
|
*/
|
|
public function update(Request $request, Kegiatan $kegiatan)
|
|
{
|
|
$validated = $request->validate([
|
|
'kategori_id' => 'required|exists:kategori_kegiatans,kategori_id',
|
|
'nama_kegiatan' => 'required|string|max:150',
|
|
'hari' => 'required|array|min:1',
|
|
'hari.*' => 'in:Senin,Selasa,Rabu,Kamis,Jumat,Sabtu,Ahad',
|
|
'waktu_mulai' => 'required|date_format:H:i',
|
|
'waktu_selesai' => 'required|date_format:H:i|after:waktu_mulai',
|
|
'materi' => 'nullable|string|max:200',
|
|
'keterangan' => 'nullable|string',
|
|
'kelas_ids' => 'nullable|array',
|
|
'kelas_ids.*' => 'exists:kelas,id',
|
|
], [
|
|
'kategori_id.required' => 'Kategori wajib dipilih.',
|
|
'nama_kegiatan.required' => 'Nama kegiatan wajib diisi.',
|
|
'hari.required' => 'Minimal pilih satu hari.',
|
|
'hari.min' => 'Minimal pilih satu hari.',
|
|
'waktu_selesai.after' => 'Waktu selesai harus lebih dari waktu mulai.',
|
|
]);
|
|
|
|
$hariDipilih = $validated['hari'];
|
|
$kelasIds = $request->input('kelas_ids', []);
|
|
$baseData = collect($validated)->except(['hari', 'kelas_ids'])->toArray();
|
|
|
|
$saudara = Kegiatan::where('nama_kegiatan', $kegiatan->nama_kegiatan)
|
|
->where('kategori_id', $kegiatan->kategori_id)
|
|
->get()
|
|
->keyBy('hari');
|
|
|
|
$updatedCount = 0;
|
|
$createdCount = 0;
|
|
|
|
foreach ($hariDipilih as $hari) {
|
|
if ($saudara->has($hari)) {
|
|
$target = $saudara->get($hari);
|
|
$target->update(array_merge($baseData, ['hari' => $hari]));
|
|
$target->assignKelas($kelasIds);
|
|
$updatedCount++;
|
|
} else {
|
|
$newKg = Kegiatan::create(array_merge($baseData, ['hari' => $hari]));
|
|
$newKg->assignKelas($kelasIds);
|
|
$createdCount++;
|
|
}
|
|
}
|
|
|
|
Cache::forget('next_kegiatan_id');
|
|
|
|
$parts = [];
|
|
if ($updatedCount > 0) $parts[] = "{$updatedCount} kegiatan diperbarui";
|
|
if ($createdCount > 0) $parts[] = "{$createdCount} kegiatan baru dibuat";
|
|
|
|
return redirect()->route('admin.kegiatan.jadwal')
|
|
->with('success', 'Berhasil: ' . implode(', ', $parts) . '.');
|
|
}
|
|
|
|
/**
|
|
* Hapus kegiatan
|
|
*/
|
|
public function destroy(Kegiatan $kegiatan)
|
|
{
|
|
$nama = $kegiatan->nama_kegiatan;
|
|
$kegiatan->delete();
|
|
Cache::forget('next_kegiatan_id');
|
|
|
|
return redirect()->route('admin.kegiatan.jadwal')
|
|
->with('success', "Kegiatan \"{$nama}\" berhasil dihapus.");
|
|
}
|
|
} |