with(['pic', 'sire', 'dam']); // 1. Pencarian Global if ($request->filled('search')) { $search = $request->search; $query->where(function($q) use ($search) { $q->where('nama_kucing', 'like', "%{$search}%") ->orWhere('ras', 'like', "%{$search}%") ->orWhere('microchip', 'like', "%{$search}%") ->orWhere('pedigree', 'like', "%{$search}%") ->orWhere('kode_ems', 'like', "%{$search}%"); }); } // 2. Filter Spesifik if ($request->filled('status')) $query->where('status', $request->status); if ($request->filled('ras')) $query->where('ras', $request->ras); if ($request->filled('gender')) $query->where('jenis_kelamin', $request->gender); $cats = $query->latest()->paginate(15)->withQueryString(); // 3. Statistik Dashboard (Tetap Utuh) $totalCats = Cat::count(); $safeWords = ['sehat', 'normal', 'aktif', 'sembuh', '-', 'tidak ada', 'clean']; $sickCats = Cat::whereNotNull('riwayat_sakit_terakhir') ->where(function($q) use ($safeWords) { foreach ($safeWords as $word) { $q->where('riwayat_sakit_terakhir', 'NOT LIKE', "%{$word}%"); } })->count(); $breeds = Cat::distinct()->pluck('ras'); return view('pages.cats.index', compact('cats', 'totalCats', 'sickCats', 'breeds')); } public function create(): View { // Proteksi Akses: Create Gate::authorize('check-access', ['cats', 'create']); $sires = Cat::where('jenis_kelamin', 'Jantan')->get(['id', 'nama_kucing']); $dams = Cat::where('jenis_kelamin', 'Betina')->get(['id', 'nama_kucing']); $users = User::all(['id', 'name']); return view('pages.cats.create', compact('sires', 'dams', 'users')); } public function store(Request $request): RedirectResponse { Gate::authorize('check-access', ['cats', 'create']); $validated = $this->validateCat($request); return DB::transaction(function () use ($request, $validated) { if ($request->hasFile('foto')) { $validated['foto'] = $this->handleImageUpload($request->file('foto')); } if (isset($validated['berat_badan'])) { $validated['berat_terakhir'] = $validated['berat_badan']; } if (empty($validated['status'])) { $validated['status'] = ($request->induk_jantan_id || $request->induk_betina_id) ? 'Kitten' : 'Active'; } // Memastikan registry disimpan sebagai JSON jika model tidak melakukan casting if (isset($validated['registry'])) { $validated['registry'] = $validated['registry']; } $cat = Cat::create($validated); $this->logAction('CREATED', $cat, "Mendaftarkan pasien baru: {$cat->nama_kucing}", 'Cat'); return redirect()->route('cats.index')->with('success', "{$cat->nama_kucing} berhasil didaftarkan."); }); } public function show(Cat $cat): View { Gate::authorize('check-access', ['cats', 'read']); $cat->load([ 'treatments' => fn($q) => $q->latest(), 'sire', 'dam', 'pic', 'weightHistories' => fn($q) => $q->latest()->limit(12) ]); return view('pages.cats.show', compact('cat')); } public function edit(Cat $cat): View { // Proteksi: Akses Update Gate::authorize('check-access', ['cats', 'update']); $sires = Cat::where('jenis_kelamin', 'Jantan')->where('id', '!=', $cat->id)->get(['id', 'nama_kucing']); $dams = Cat::where('jenis_kelamin', 'Betina')->where('id', '!=', $cat->id)->get(['id', 'nama_kucing']); $users = User::all(['id', 'name']); return view('pages.cats.edit', compact('cat', 'sires', 'dams', 'users')); } public function update(Request $request, Cat $cat): RedirectResponse { Gate::authorize('check-access', ['cats', 'update']); $validated = $this->validateCat($request, $cat->id); return DB::transaction(function () use ($request, $validated, $cat) { if ($request->hasFile('foto')) { if ($cat->foto) Storage::disk('public')->delete($cat->foto); $validated['foto'] = $this->handleImageUpload($request->file('foto')); } if ($request->filled('berat_badan')) { $validated['berat_terakhir'] = $request->berat_badan; if ($cat->berat_badan != $request->berat_badan) { $cat->weightHistories()->create(['berat' => $request->berat_badan]); } } $cat->update($validated); $this->logAction('UPDATED', $cat, "Update data medis/profil: {$cat->nama_kucing}", 'Cat'); return redirect()->route('cats.index')->with('success', 'Data berhasil diperbarui.'); }); } public function destroy(Cat $cat): RedirectResponse { Gate::authorize('check-access', ['cats', 'delete']); $isParent = Matting::where('sire_id', $cat->id)->orWhere('dam_id', $cat->id)->exists(); if ($isParent) { return back()->with('error', "Gagal! {$cat->nama_kucing} tidak bisa dihapus karena sudah menjadi Induk."); } return DB::transaction(function () use ($cat) { $name = $cat->nama_kucing; if ($cat->foto) Storage::disk('public')->delete($cat->foto); $this->logAction('DELETED', $cat, "Menghapus data permanen: {$name}", 'Cat'); $cat->delete(); return redirect()->route('cats.index')->with('success', "Data {$name} telah dihapus."); }); } public function getCatData($id): JsonResponse { $cat = Cat::find($id); if (!$cat) return response()->json(['error' => 'Not found'], 404); return response()->json([ 'tgl_lahir' => $cat->tanggal_lahir ? $cat->tanggal_lahir->format('Y-m-d') : '', 'pedigree' => $cat->pedigree ?? '', 'microchip' => $cat->microchip ?? '', 'kode_ems' => $cat->kode_ems ?? '', ]); } /** * PDF ID Card / Medical Pass * PENYEMPURNAAN: Perbaikan Barcode (Base64 SVG) */ public function downloadPdf(Cat $cat) { Gate::authorize('check-access', ['cats', 'read']); $cat->load(['pic', 'sire', 'dam']); // FIX: Konversi SVG ke Base64 agar tampil di domPDF tanpa Imagick $qrCodeRaw = QrCode::format('svg') ->size(150) ->margin(1) ->generate(route('cats.show', $cat->id)); $qrCode = base64_encode($qrCodeRaw); $pdf = Pdf::loadView('pages.cats.pdf-card', [ 'cat' => $cat, 'qrCode' => $qrCode, // Dikirim sebagai Base64 'printedAt' => Carbon::now()->translatedFormat('d F Y H:i') ]); return $pdf->setPaper([0, 0, 300, 500], 'portrait') ->stream('MEDICAL_PASS_' . strtoupper($cat->nama_kucing) . '.pdf'); } /* -------------------------------------------------------------------------- */ /* HELPERS (TETAP UTUH) */ /* -------------------------------------------------------------------------- */ private function validateCat(Request $request, $id = null) { return $request->validate([ 'nama_kucing' => 'required|string|max:255', 'ras' => 'required|string', 'jenis_kelamin' => 'required|in:Jantan,Betina', 'tanggal_lahir' => 'nullable|date', 'foto' => 'nullable|image|mimes:jpg,jpeg,png|max:3072', 'registry' => 'nullable|array', 'registry.*' => 'string', 'pedigree' => 'nullable|string|max:255', 'microchip' => 'nullable|string|max:255', 'kode_ems' => 'nullable|string|max:255', 'status' => 'nullable|string', 'induk_jantan_id' => 'nullable|exists:cats,id', 'induk_betina_id' => 'nullable|exists:cats,id', 'lokasi_kandang' => 'nullable|string', 'pic_id' => 'nullable|exists:users,id', 'berat_badan' => 'nullable|numeric|min:0', 'riwayat_sakit_terakhir' => 'nullable|string', 'jadwal_vaksin' => 'nullable|date', 'jadwal_obat_cacing' => 'nullable|date', 'jadwal_obat_kutu' => 'nullable|date', 'jadwal_grooming' => 'nullable|date', ]); } private function handleImageUpload($file): string { $filename = 'cat_' . uniqid() . '.png'; $path = 'cats/' . $filename; $img = Image::read($file)->cover(500, 500, 'center')->toPng(); Storage::disk('public')->put($path, (string) $img); return $path; } }