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."); } }