get(); return view('settings.categories', compact('categories')); } public function storeCategory(Request $request): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'color' => ['required', 'string', 'max:7'], 'description' => ['nullable', 'string', 'max:500'], 'sort_order' => ['nullable', 'integer', 'min:0'], ]); $validated['slug'] = Str::slug($validated['name']); ViolationCategory::create($validated); return back()->with('success', 'Kategori pelanggaran berhasil ditambahkan.'); } public function updateCategory(Request $request, ViolationCategory $category): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'color' => ['required', 'string', 'max:7'], 'description' => ['nullable', 'string', 'max:500'], 'sort_order' => ['nullable', 'integer', 'min:0'], 'is_active' => ['boolean'], ]); $validated['slug'] = Str::slug($validated['name']); $validated['is_active'] = $request->boolean('is_active'); $category->update($validated); return back()->with('success', 'Kategori berhasil diperbarui.'); } public function destroyCategory(ViolationCategory $category): RedirectResponse { if ($category->violationTypes()->count() > 0) { return back()->with('error', 'Kategori masih memiliki jenis pelanggaran. Hapus jenis pelanggaran terlebih dahulu.'); } $category->delete(); return back()->with('success', 'Kategori berhasil dihapus.'); } // === Violation Types === public function types(Request $request): View { $query = ViolationType::with('category'); if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('default_sanction', 'like', "%{$search}%"); }); } if ($request->filled('category_id')) { $query->where('category_id', $request->category_id); } $types = $query->orderBy('category_id')->orderBy('name')->paginate(20); $categories = ViolationCategory::orderBy('sort_order')->get(); return view('settings.violation-types', compact('types', 'categories')); } public function storeType(Request $request): RedirectResponse { $validated = $request->validate([ 'category_id' => ['required', 'exists:violation_categories,id'], 'name' => ['required', 'string', 'max:255'], 'points' => ['required', 'integer', 'min:0', 'max:500'], 'default_sanction' => ['nullable', 'string', 'max:500'], 'description' => ['nullable', 'string', 'max:1000'], ]); $validated['slug'] = Str::slug($validated['name']); $validated['is_active'] = true; ViolationType::create($validated); return back()->with('success', 'Jenis pelanggaran berhasil ditambahkan.'); } public function updateType(Request $request, ViolationType $type): RedirectResponse { $validated = $request->validate([ 'category_id' => ['required', 'exists:violation_categories,id'], 'name' => ['required', 'string', 'max:255'], 'points' => ['required', 'integer', 'min:0', 'max:500'], 'default_sanction' => ['nullable', 'string', 'max:500'], 'description' => ['nullable', 'string', 'max:1000'], 'is_active' => ['boolean'], ]); // System types: slug tetap, nama bisa diubah tapi slug gak ikut berubah if (!$type->is_system) { $validated['slug'] = Str::slug($validated['name']); } $validated['is_active'] = $request->boolean('is_active'); $type->update($validated); return back()->with('success', 'Jenis pelanggaran berhasil diperbarui.'); } public function destroyType(ViolationType $type): RedirectResponse { if ($type->is_system) { return back()->with('error', 'Jenis pelanggaran sistem tidak bisa dihapus. Nonaktifkan saja jika tidak diperlukan.'); } if ($type->violations()->count() > 0) { return back()->with('error', 'Jenis pelanggaran ini sudah tercatat pada pelanggaran siswa. Nonaktifkan saja.'); } $type->delete(); return back()->with('success', 'Jenis pelanggaran berhasil dihapus.'); } // === Handling Types (Jenis Penanganan) === public function handlingTypes(): View { $types = HandlingType::orderBy('sort_order')->orderBy('name')->get(); $roles = HandlingType::roles(); return view('settings.handling-types', compact('types', 'roles')); } public function storeHandlingType(Request $request): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255', 'unique:handling_types,name'], 'icon' => ['required', 'string', 'max:100'], 'color' => ['required', 'string', 'max:7'], 'sort_order' => ['nullable', 'integer', 'min:0', 'max:999'], ]); $validated['sort_order'] = $validated['sort_order'] ?? 0; $validated['is_active'] = true; HandlingType::create($validated); return back()->with('success', 'Jenis penanganan berhasil ditambahkan.'); } public function updateHandlingType(Request $request, HandlingType $type): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255', 'unique:handling_types,name,' . $type->id], 'icon' => ['required', 'string', 'max:100'], 'color' => ['required', 'string', 'max:7'], 'sort_order' => ['nullable', 'integer', 'min:0', 'max:999'], 'is_active' => ['boolean'], ]); $validated['sort_order'] = $validated['sort_order'] ?? $type->sort_order; $validated['is_active'] = $request->boolean('is_active'); $type->update($validated); return back()->with('success', 'Jenis penanganan berhasil diperbarui.'); } public function reorderHandlingTypes(Request $request): JsonResponse { $validated = $request->validate([ 'order' => ['required', 'array'], 'order.*' => ['integer', 'exists:handling_types,id'], ]); foreach ($validated['order'] as $i => $id) { HandlingType::where('id', $id)->update(['sort_order' => $i + 1]); } return response()->json(['ok' => true]); } public function destroyHandlingType(HandlingType $type): RedirectResponse { if ($type->is_system) { return back()->with('error', 'Jenis penanganan sistem tidak bisa dihapus. Nonaktifkan saja jika tidak diperlukan.'); } $type->delete(); return back()->with('success', 'Jenis penanganan berhasil dihapus.'); } public function storeHandlingRole(Request $request): RedirectResponse { $validated = $request->validate([ 'role' => ['required', 'string', 'max:100'], ]); $roles = HandlingType::roles(); $role = trim($validated['role']); if (in_array($role, $roles)) { return back()->with('error', 'Peran "' . $role . '" sudah ada.'); } $roles[] = $role; HandlingType::saveRoles($roles); return back()->with('success', 'Peran "' . $role . '" berhasil ditambahkan.'); } public function destroyHandlingRole(string $role): RedirectResponse { $roles = HandlingType::roles(); $roles = array_values(array_filter($roles, fn($r) => $r !== $role)); HandlingType::saveRoles($roles); return back()->with('success', 'Peran "' . $role . '" berhasil dihapus.'); } public function reorderHandlingRoles(Request $request): JsonResponse { $validated = $request->validate([ 'order' => ['required', 'array'], 'order.*' => ['required', 'string', 'max:100'], ]); HandlingType::saveRoles($validated['order']); return response()->json(['ok' => true]); } // === Homeroom Teachers (Wali Kelas) === public function homeroomTeachers(): View { $classes = Classes::with('homeroomTeacher') ->where('is_active', true) ->orderBy('level') ->orderBy('name') ->get(); $teachers = \App\Models\User::where(function ($q) { $q->whereJsonContains('roles', 'wali_kelas')->orWhere('role', 'wali_kelas'); }) ->where('is_active', true) ->orderBy('name') ->get(); return view('settings.homeroom', compact('classes', 'teachers')); } public function updateHomeroomTeacher(Request $request): RedirectResponse { $validated = $request->validate([ 'class_id' => ['required', 'exists:classes,id'], 'teacher_id' => ['nullable', 'exists:users,id'], ]); $class = Classes::findOrFail($validated['class_id']); $class->homeroom_teacher_id = $validated['teacher_id'] ?? null; $class->save(); return back()->with('success', 'Wali kelas untuk ' . $class->name . ' berhasil diperbarui.'); } // === SP Thresholds === public function thresholds(): View { $thresholds = SpThreshold::orderBy('min_points')->get(); return view('settings.thresholds', compact('thresholds')); } public function updateThresholds(Request $request): RedirectResponse { $thresholds = $request->validate([ 'thresholds' => ['required', 'array'], 'thresholds.*.id' => ['required', 'exists:sp_thresholds,id'], 'thresholds.*.min_points' => ['required', 'integer', 'min:0'], 'thresholds.*.max_points' => ['nullable', 'integer', 'min:0'], 'thresholds.*.name' => ['required', 'string', 'max:255'], 'thresholds.*.default_description' => ['nullable', 'string', 'max:500'], ]); $inputs = $request->input('thresholds', []); $idx = 0; foreach ($thresholds['thresholds'] as $data) { $update = [ 'name' => $data['name'], 'min_points' => $data['min_points'], 'max_points' => $data['max_points'], 'default_description' => $data['default_description'], 'is_active' => isset($inputs[$idx]['is_active']), ]; SpThreshold::where('id', $data['id'])->update($update); $idx++; } return back()->with('success', 'Ambang batas SP berhasil diperbarui.'); } public function storeThreshold(Request $request): RedirectResponse { $data = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'min_points' => ['required', 'integer', 'min:0'], 'max_points' => ['nullable', 'integer', 'min:0'], 'default_description' => ['nullable', 'string', 'max:500'], 'color' => ['nullable', 'string', 'max:20'], ]); $slug = str($data['name'])->slug('-'); SpThreshold::create([ 'name' => $data['name'], 'slug' => $slug, 'min_points' => $data['min_points'], 'max_points' => $data['max_points'], 'default_description' => $data['default_description'], 'color' => $data['color'] ?? '#6b7280', 'is_active' => true, ]); return redirect()->route('settings.thresholds')->with('success', 'Threshold ' . $data['name'] . ' berhasil ditambahkan.'); } public function destroyThreshold(SpThreshold $threshold): RedirectResponse { $name = $threshold->name; $threshold->delete(); return redirect()->route('settings.thresholds')->with('success', 'Threshold ' . $name . ' berhasil dihapus.'); } }