diff --git a/app/Http/Controllers/Admin/ViolationReportController.php b/app/Http/Controllers/Admin/ViolationReportController.php index e095fbd..e71b2c9 100644 --- a/app/Http/Controllers/Admin/ViolationReportController.php +++ b/app/Http/Controllers/Admin/ViolationReportController.php @@ -7,6 +7,7 @@ use App\Models\Classes; use App\Models\Setting; use App\Models\Violation; use Barryvdh\DomPDF\Facade\Pdf; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\View\View; @@ -15,36 +16,31 @@ use Illuminate\View\View; */ class ViolationReportController extends Controller { - public function index(): View + public function index(Request $request): View { $classes = Classes::orderBy('name')->get(); + $filters = $this->filters($request); - $query = Violation::query() - ->when(auth()->user()->isScopedWaliKelas(), fn ($q) => $q->whereHas('student', fn ($qq) => $qq->whereIn('class_id', auth()->user()->homeroomClassIds()))); + $query = $this->baseQuery($filters); + $stats = $this->stats($query); - $totalKasus = (clone $query)->count(); - $totalPoin = (clone $query)->sum('points'); - $siswaTerlibat = (clone $query)->distinct('student_id')->count('student_id'); + $recent = (clone $query) + ->with(['student.class', 'violationType']) + ->orderByDesc('violation_date') + ->limit(20) + ->get(); - return view('reports.violations', compact('classes', 'totalKasus', 'totalPoin', 'siswaTerlibat')); + return view('reports.violations', compact('classes', 'filters', 'stats', 'recent')); } public function pdf(Request $request) { - $data = $request->validate([ - 'date_from' => ['nullable', 'date'], - 'date_to' => ['nullable', 'date', 'after_or_equal:date_from'], - 'class_id' => ['nullable', 'exists:classes,id'], - ]); + $filters = $this->filters($request, validate: true); - $query = Violation::with(['student.class', 'violationType.category', 'handlings']) - ->when($data['date_from'] ?? null, fn ($q) => $q->whereDate('violation_date', '>=', $data['date_from'])) - ->when($data['date_to'] ?? null, fn ($q) => $q->whereDate('violation_date', '<=', $data['date_to'])) - ->when($data['class_id'] ?? null, fn ($q) => $q->whereHas('student', fn ($qq) => $qq->where('class_id', $data['class_id']))) - // Wali kelas murni: hanya kelas yang diwalikan - ->when(auth()->user()->isScopedWaliKelas(), fn ($q) => $q->whereHas('student', fn ($qq) => $qq->whereIn('class_id', auth()->user()->homeroomClassIds()))); - - $violations = $query->orderBy('violation_date')->get(); + $violations = $this->baseQuery($filters) + ->with(['student.class', 'violationType.category', 'handlings']) + ->orderBy('violation_date') + ->get(); $summary = [ 'total_kasus' => $violations->count(), @@ -80,17 +76,84 @@ class ViolationReportController extends Controller 'kepala_sekolah_nip' => Setting::getValue('kepala_sekolah_nip', ''), ]; - $periode = ($data['date_from'] ?? 'Awal').' s/d '.($data['date_to'] ?? 'Sekarang'); - $filterKelas = $data['class_id'] ?? null - ? Classes::find($data['class_id'])?->name - : 'Semua Kelas'; + $periode = ($filters['date_from'] ?? 'Awal').' s/d '.($filters['date_to'] ?? 'Sekarang'); + $filterKelas = $filters['class_id'] ? (Classes::find($filters['class_id'])?->name ?? '-') : 'Semua Kelas'; $pdf = Pdf::loadView('reports.violation-report-pdf', compact( 'violations', 'summary', 'perJenis', 'perKelas', 'school', 'periode', 'filterKelas' ))->setPaper('a4', 'landscape'); - $filename = 'rekap-pelanggaran-'.now()->format('Ymd-His').'.pdf'; + return $pdf->download('rekap-pelanggaran-'.now()->format('Ymd-His').'.pdf'); + } - return $pdf->download($filename); + /** + * Ambil & validasi filter dari request. + * + * @return array{date_from: ?string, date_to: ?string, class_id: ?int} + */ + protected function filters(Request $request, bool $validate = false): array + { + $rules = [ + 'date_from' => ['nullable', 'date'], + 'date_to' => ['nullable', 'date', 'after_or_equal:date_from'], + 'class_id' => ['nullable', 'integer', 'exists:classes,id'], + ]; + + $data = $validate ? $request->validate($rules) : $request->validate($rules); + + return [ + 'date_from' => $data['date_from'] ?? null, + 'date_to' => $data['date_to'] ?? null, + 'class_id' => isset($data['class_id']) ? (int) $data['class_id'] : null, + ]; + } + + /** + * Query dasar pelanggaran: scoping wali kelas + filter periode & kelas. + */ + protected function baseQuery(array $filters): Builder + { + return Violation::query() + ->when($filters['date_from'], fn ($q) => $q->whereDate('violation_date', '>=', $filters['date_from'])) + ->when($filters['date_to'], fn ($q) => $q->whereDate('violation_date', '<=', $filters['date_to'])) + ->when($filters['class_id'], fn ($q) => $q->whereHas('student', fn ($qq) => $qq->where('class_id', $filters['class_id']))) + ->when(auth()->user()->isScopedWaliKelas(), fn ($q) => $q->whereHas('student', fn ($qq) => $qq->whereIn('class_id', auth()->user()->homeroomClassIds()))); + } + + /** + * Statistik ringkas + rekap preview untuk halaman. + */ + protected function stats(Builder $query): array + { + $totalKasus = (clone $query)->count(); + $totalPoin = (clone $query)->sum('points'); + $siswaTerlibat = (clone $query)->distinct('student_id')->count('student_id'); + + $rows = (clone $query) + ->with(['violationType.category', 'student.class']) + ->get(['id', 'student_id', 'violation_type_id', 'points', 'violation_date']); + + $perJenis = $rows->groupBy(fn ($v) => $v->violationType?->name ?? 'Tanpa Jenis') + ->map(fn ($group) => [ + 'jenis' => $group->first()->violationType?->name ?? 'Tanpa Jenis', + 'kategori' => $group->first()->violationType?->category?->name ?? '-', + 'jumlah' => $group->count(), + 'poin' => $group->sum('points'), + ]) + ->sortByDesc('jumlah') + ->values(); + + $perKelas = $rows->groupBy(fn ($v) => $v->student?->class?->name ?? 'Tanpa Kelas') + ->map(fn ($group) => [ + 'kelas' => $group->first()->student?->class?->name ?? 'Tanpa Kelas', + 'jumlah' => $group->count(), + 'poin' => $group->sum('points'), + ]) + ->sortByDesc('jumlah') + ->values(); + + $jenisTerbanyak = $perJenis->first(); + + return compact('totalKasus', 'totalPoin', 'siswaTerlibat', 'perJenis', 'perKelas', 'jenisTerbanyak'); } } diff --git a/resources/views/reports/violations.blade.php b/resources/views/reports/violations.blade.php index 7c130f6..1b34eaa 100644 --- a/resources/views/reports/violations.blade.php +++ b/resources/views/reports/violations.blade.php @@ -3,68 +3,321 @@ @section('title', 'Laporan Pelanggaran') @section('content') -
Cetak rekap pelanggaran per periode untuk BK & Kepala Sekolah
+@php $selectedClass = $classes->firstWhere('id', $filters['class_id']); @endphp ++ Rekap pelanggaran per periode untuk BK & Kepala Sekolah — cetak langsung atau unduh PDF. +
+