initial commit

This commit is contained in:
2026-09-23 12:45:02 +07:00
parent d16f454083
commit 507fc6ccdf
235 changed files with 19587 additions and 2835 deletions
+419 -138
View File
@@ -2,17 +2,24 @@
namespace App\Http\Controllers;
use App\Models\{Cat, User, Matting};
use App\Models\{Cat, User, Matting,Medicine};
use App\Traits\LogsActivity;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\View\View;
use Illuminate\Support\Facades\{Storage, DB, Gate}; // Tambahkan Gate
use Illuminate\Support\Facades\{Storage, DB, Gate,Cache}; // Tambahkan Gate
use Illuminate\Support\Carbon;
use Barryvdh\DomPDF\Facade\Pdf;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
use Intervention\Image\Laravel\Facades\Image;
use Illuminate\Validation\Rule;
use Illuminate\Support\Str;
use App\Imports\CatWeightImport;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\CatWeightTemplateExport;
use Maatwebsite\Excel\Validators\ValidationException;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class CatController extends Controller
{
@@ -21,47 +28,106 @@ class CatController extends Controller
/**
* Menampilkan daftar kucing dengan filter cerdas.
*/
public function index(Request $request): View
{
// Proteksi Akses: Read
Gate::authorize('check-access', ['cats', 'read']);
public function index(Request $request): View
{
// Proteksi Akses: Read
Gate::authorize('check-access', ['cats', 'read']);
$query = Cat::query()->with(['pic', 'sire', 'dam']);
$now = now();
$query = Cat::query()->with(['pic:id,name', 'sire', 'dam', 'registrations.association']);
// 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'));
// 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}%")
->orWhere('lokasi_kandang', 'like', "%{$search}%")
->orWhereHas('pic', function($sub) use ($search) {
$sub->where('name', 'like', "%{$search}%");
});
});
}
// 2. FILTER OVERDUE (Link dari Dashboard)
if ($request->filled('filter_overdue')) {
$filter = $request->get('filter_overdue');
$query->where(function($q) use ($filter, $now) {
if ($filter === 'vaccine') {
$q->whereNotNull('jadwal_vaksin')->where('jadwal_vaksin', '<', $now);
} elseif ($filter === 'flea' || $filter === 'obat_kutu') {
$q->whereNotNull('jadwal_obat_kutu')->where('jadwal_obat_kutu', '<', $now);
} elseif ($filter === 'deworming' || $filter === 'obat_cacing') {
$q->whereNotNull('jadwal_obat_cacing')->where('jadwal_obat_cacing', '<', $now);
} elseif ($filter === 'grooming') {
$q->whereNotNull('jadwal_grooming')->where('jadwal_grooming', '<', $now);
}
});
}
// --- TAMBAHAN: 2.1 FILTER KESEHATAN (Link dari Dashboard Under Treatment) ---
if ($request->get('filter_health') === 'treatment') {
// Menggunakan kolom treatment_status: 1 (Treatment), 2 (Healthy)
$query->where('treatment_status', 1);
}
// 3. FILTER STATUS & LOGIKA USIA DINAMIS
if ($request->filled('status')) {
$status = $request->status;
if (in_array(strtolower($status), ['adult', 'dewasa'])) {
// Dewasa: > 12 bulan
$query->where('tanggal_lahir', '<=', $now->copy()->subMonths(12));
} elseif ($status === 'Junior') {
// Junior: 7 bulan - 12 bulan
$query->whereBetween('tanggal_lahir', [
$now->copy()->subMonths(12)->addDay(),
$now->copy()->subMonths(7)
]);
} elseif ($status === 'Kitten') {
// Kitten: < 7 bulan
$query->where('tanggal_lahir', '>', $now->copy()->subMonths(7));
} else {
$query->where('status', $status);
}
}
// 4. FILTER STANDAR (Ras & Gender)
if ($request->filled('ras')) $query->where('ras', $request->ras);
if ($request->filled('gender')) $query->where('jenis_kelamin', $request->gender);
// 5. VALIDASI SORTING
$allowedSorts = ['nama_kucing', 'lokasi_kandang', 'berat_badan', 'created_at', 'pic_name'];
$sort = in_array($request->get('sort'), $allowedSorts) ? $request->get('sort') : 'created_at';
$direction = strtolower($request->get('direction')) === 'asc' ? 'asc' : 'desc';
if ($sort === 'pic_name') {
$query->leftJoin('users', 'cats.pic_id', '=', 'users.id')
->select('cats.*')
->orderBy('users.name', $direction);
} else {
$query->orderBy("cats.$sort", $direction);
}
// 6. EKSEKUSI DENGAN PAGINASI
$cats = $query->paginate(15)->withQueryString();
// 7. STATISTIK (DIPERBAIKI: Menggunakan treatment_status)
$totalCats = Cat::count();
// Perbaikan: Tidak lagi menggunakan string search yang tidak akurat (15 vs 4)
// Sekarang langsung merujuk pada kolom treatment_status bernilai 1
$sickCats = Cat::where('treatment_status', 1)->count();
$breeds = Cat::distinct()->whereNotNull('ras')->pluck('ras');
return view('pages.cats.index', compact('cats', 'totalCats', 'sickCats', 'breeds'));
}
public function create(): View
{
// Proteksi Akses: Create
@@ -75,47 +141,93 @@ class CatController extends Controller
}
public function store(Request $request): RedirectResponse
{
Gate::authorize('check-access', ['cats', 'create']);
{
Gate::authorize('check-access', ['cats', 'create']);
$validated = $this->validateCat($request);
// 1. Validasi
$validated = $this->validateCat($request);
return DB::transaction(function () use ($request, $validated) {
if ($request->hasFile('foto')) {
$validated['foto'] = $this->handleImageUpload($request->file('foto'));
}
return DB::transaction(function () use ($request, $validated) {
// 2. Handling Upload Foto (Fix: Encoding Intervention v3)
if ($request->hasFile('foto')) {
$validated['foto'] = $this->handleImageUpload($request->file('foto'));
}
if (isset($validated['berat_badan'])) {
$validated['berat_terakhir'] = $validated['berat_badan'];
}
// 3. Handling Berat Badan (Sederhanakan)
// Cukup isi berat_badan, Model Cat akan otomatis mengisi berat_terakhir (via static::saving)
if ($request->filled('berat_badan')) {
$validated['berat_badan'] = $request->berat_badan;
}
if (empty($validated['status'])) {
$validated['status'] = ($request->induk_jantan_id || $request->induk_betina_id) ? 'Kitten' : 'Active';
}
// 4. Handling Status Otomatis
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'];
}
// 5. Simpan Data
// Pastikan kolom-kolom induk juga masuk ke validated jika ada di request
$validated['induk_jantan_id'] = $request->induk_jantan_id;
$validated['induk_betina_id'] = $request->induk_betina_id;
$validated['pic_id'] = $request->pic_id ?? auth()->id();
$cat = Cat::create($validated);
$this->logAction('CREATED', $cat, "Mendaftarkan pasien baru: {$cat->nama_kucing}", 'Cat');
$cat = Cat::create($validated);
return redirect()->route('cats.index')->with('success', "{$cat->nama_kucing} berhasil didaftarkan.");
});
}
// 6. Logging (Pastikan Trait LogsActivity & Tabel Log sudah punya kolom 'model')
try {
$this->logAction('CREATED', $cat, "Mendaftarkan kucing baru: {$cat->nama_kucing}", 'Cat');
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::warning("Log gagal dicatat: " . $e->getMessage());
}
return redirect()->route('cats.index')->with('success', "{$cat->nama_kucing} berhasil didaftarkan.");
});
}
/**
* Fix: Perbaikan cara simpan gambar pada Intervention Image v3
*/
private function handleImageUpload($file): string
{
$filename = 'cat_' . uniqid() . '.png';
$path = 'cats/' . $filename;
// Intervention v3: Gunakan encode() atau toPng() lalu simpan sebagai string
$img = Image::read($file)->cover(500, 500, 'center');
// Simpan ke disk public
Storage::disk('public')->put($path, $img->toPng()->toString());
return $path;
}
public function show(Cat $cat): View
{
Gate::authorize('check-access', ['cats', 'read']);
{
// 1. Otorisasi (TETAP ADA)
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'));
}
// 2. Eager Loading (DITAMBAHKAN medicalRecords & performer)
// Kita memuat medicalRecords agar tidak terjadi query berulang di dalam Loop Blade (N+1 Problem)
$cat->load([
'treatments' => fn($q) => $q->latest(),
'medicalRecords' => fn($q) => $q->latest(), // Baru: Riwayat Vaksin/Obat Cacing
'medicalRecords.performer', // Baru: Siapa yang melakukan tindakan
'sire',
'dam',
'pic',
'weightHistories' => fn($q) => $q->latest()->limit(12),
'registrations.association',
'registrations.documents'
]);
// 3. Ambil Data Inventori (Baru: Untuk Datalist di Modal Input)
// Menampilkan semua obat yang stoknya masih tersedia
$availableMedicines = Medicine::where('qty', '>', 0)
->orderBy('nama_obat', 'asc')
->get(['nama_obat', 'qty', 'unit']);
// 4. Return View dengan Compact (DITAMBAHKAN availableMedicines)
return view('pages.cats.show', compact('cat', 'availableMedicines'));
}
public function edit(Cat $cat): View
{
@@ -130,30 +242,46 @@ class CatController extends Controller
}
public function update(Request $request, Cat $cat): RedirectResponse
{
Gate::authorize('check-access', ['cats', 'update']);
{
// 1. Otorisasi Akses
Gate::authorize('check-access', ['cats', 'update']);
$validated = $this->validateCat($request, $cat->id);
// 2. Validasi (Mengirimkan $cat->id agar Rule:unique mengabaikan record ini sendiri)
$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'));
return DB::transaction(function () use ($request, $validated, $cat) {
// 3. Handling Foto
if ($request->hasFile('foto')) {
// Hapus foto lama jika ada
if ($cat->foto && Storage::disk('public')->exists($cat->foto)) {
Storage::disk('public')->delete($cat->foto);
}
// Upload foto baru menggunakan helper yang sudah diperbaiki
$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]);
}
}
/**
* 4. PENYEMPURNAAN REGISTRY (FIX UPDATE)
* Jika di form semua checkbox tidak dicentang, $request->registry akan kosong.
* Kita paksa nilainya menjadi ['NON-PED'] agar data lama di DB terhapus/terganti.
*/
$validated['registry'] = $request->input('registry', ['NON-PED']);
$cat->update($validated);
$this->logAction('UPDATED', $cat, "Update data medis/profil: {$cat->nama_kucing}", 'Cat');
// 5. Update Data Utama
// Logika berat_badan sudah otomatis dihandle oleh Model (static::saving/updated)
$cat->update($validated);
return redirect()->route('cats.index')->with('success', 'Data berhasil diperbarui.');
});
}
// 6. Activity Log (Dibalut Try-Catch agar proses update tidak gagal hanya karena log)
try {
$this->logAction('UPDATED', $cat, "Memperbarui profil medis & identitas: {$cat->nama_kucing}", 'Cat');
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::warning("Activity Log gagal dicatat: " . $e->getMessage());
}
return redirect()->route('cats.index')->with('success', "Data {$cat->nama_kucing} berhasil diperbarui.");
});
}
public function destroy(Cat $cat): RedirectResponse
{
@@ -189,71 +317,224 @@ class CatController extends Controller
]);
}
/**
* PDF ID Card / Medical Pass
* PENYEMPURNAAN: Perbaikan Barcode (Base64 SVG)
*/
public function downloadPdf(Cat $cat)
//cetak download health ledger pdf
public function downloadReport(Cat $cat)
{
Gate::authorize('check-access', ['cats', 'read']);
$cat->load([
'medicalRecords' => fn($q) => $q->orderBy('date_given', 'desc'),
'medicalRecords.performer',
'treatments' => fn($q) => $q->orderBy('tanggal', 'desc'), // <--- Pastikan ini ada
'sire',
'dam',
'weightHistories' => fn($q) => $q->orderBy('created_at', 'desc')
]);
$groupedHistory = $cat->medicalRecords->groupBy('type');
$pdf = Pdf::loadView('pages.reports.medical_history', compact('cat', 'groupedHistory'))
->setPaper('a4', 'portrait');
$filename = "Medical_Report_" . Str::slug($cat->nama_kucing) . "_" . date('Ymd') . ".pdf";
return $pdf->download($filename);
}
public function bulkWeightPage(): View
{
Gate::authorize('check-access', ['cats', 'read']);
$cat->load(['pic', 'sire', 'dam']);
Gate::authorize('check-access', ['cats', 'update']);
// 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);
return view('pages.cats.bulk-weight');
}
$pdf = Pdf::loadView('pages.cats.pdf-card', [
'cat' => $cat,
'qrCode' => $qrCode, // Dikirim sebagai Base64
'printedAt' => Carbon::now()->translatedFormat('d F Y H:i')
public function downloadWeightTemplate(): BinaryFileResponse
{
Gate::authorize('check-access', ['cats', 'update']);
// Menambahkan Jam & Menit agar nama file lebih spesifik
$fileName = "Template_Bulk_BB_" . date('Ymd_Hi') . ".xlsx";
return Excel::download(new CatWeightTemplateExport, $fileName);
}
public function uploadWeight(Request $request): RedirectResponse
{
Gate::authorize('check-access', ['cats', 'update']);
$request->validate([
// Tambahkan rule 'file' untuk memastikan inputan benar-benar file
'file_excel' => 'required|file|mimes:xlsx,xls,csv|max:5120',
], [
'file_excel.required' => 'File Excel wajib diunggah.',
'file_excel.mimes' => 'Format file harus berupa Excel (.xlsx, .xls) atau CSV.',
'file_excel.max' => 'Ukuran file maksimal adalah 5MB.',
]);
return $pdf->setPaper([0, 0, 300, 500], 'portrait')
->stream('MEDICAL_PASS_' . strtoupper($cat->nama_kucing) . '.pdf');
try {
// 1. Masukkan class import ke dalam variabel
$importHelper = new CatWeightImport();
// 2. Jalankan proses import menggunakan variabel tersebut
Excel::import($importHelper, $request->file('file_excel'));
// 3. Ambil jumlah data yang berhasil diproses
$totalBerhasil = $importHelper->getImportedCount();
// 4. Sisipkan jumlahnya ke dalam pesan sukses
return redirect()->route('cats.index')
->with('success', "Data berat badan massal berhasil diproses! Sebanyak {$totalBerhasil} record berhasil disinkronisasi ke dalam database.");
} catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
return back()->with('error', 'Tipe file tidak dikenali. Pastikan file tidak rusak (corrupt).');
} catch (\Exception $e) {
// MENANGKAP ERROR SISTEM / DATABASE
return back()->with('error', 'Gagal memproses file Excel. Pastikan format kolom sesuai template. Error: ' . $e->getMessage());
}
}
/**
* Simpan/Update Sertifikat Pedigree
*/
public function uploadPedigree(Request $request, Cat $cat)
{
Gate::authorize('check-access', ['cats', 'update']);
$request->validate(['sertifikat_ped_file' => 'required|file|mimes:pdf,jpg,jpeg,png|max:10240']);
$registration = $cat->registrations()->where('is_primary', true)->first() ?: $cat->registrations()->first();
if (!$registration) {
return redirect()->route('cats.pedigree', $cat)->with('error', 'Tambahkan registrasi asosiasi terlebih dahulu sebelum mengunggah sertifikat.');
}
$path = $request->file('sertifikat_ped_file')->store('cat-registrations/' . $cat->id, 'public');
$registration->documents()->create([
'label' => 'Sertifikat Pedigree',
'document_type' => 'pedigree',
'file_path' => $path,
]);
return redirect()->route('cats.pedigree', $cat)->with('success', 'Sertifikat pedigree berhasil ditambahkan.');
}
/* -------------------------------------------------------------------------- */
/* HELPERS (TETAP UTUH) */
/* -------------------------------------------------------------------------- */
private function validateCat(Request $request, $id = null)
private function validateCat(Request $request, $id = null)
{
return $request->validate([
// Identitas Dasar
'nama_kucing' => 'required|string|max:255',
'ras' => 'required|string|max:100',
'jenis_kelamin' => 'required|in:Jantan,Betina',
'tanggal_lahir' => 'nullable|date|before_or_equal:today',
// Media
'foto' => 'nullable|image|mimes:jpg,jpeg,png|max:3072',
// Registry & Legalitas (Ditambahkan NON-PED & Unik)
'registry' => 'nullable|array',
'registry.*' => ['string', \Illuminate\Validation\Rule::in(['ICA', 'CFA', 'WCF', 'FFF', 'NON-PED'])],
'pedigree' => [
'nullable', 'string', 'max:255',
Rule::unique('cats', 'pedigree')->ignore($id)
],
'microchip' => [
'nullable', 'string', 'max:255',
Rule::unique('cats', 'microchip')->ignore($id)
],
'kode_ems' => 'nullable|string|max:50',
// Status & Lokasi
'status' => 'nullable|string|max:50',
'lokasi_kandang'=> 'nullable|string|max:255',
'pic_id' => 'nullable|exists:users,id',
// Silsilah (Validasi Induk harus sesuai Gender)
'induk_jantan_id' => [
'nullable',
Rule::exists('cats', 'id')->where(function ($query) {
$query->where('jenis_kelamin', 'Jantan');
}),
],
'induk_betina_id' => [
'nullable',
Rule::exists('cats', 'id')->where(function ($query) {
$query->where('jenis_kelamin', 'Betina');
}),
],
// Medis & Monitoring
'berat_badan' => 'nullable|numeric|min:0|max:20',
'riwayat_sakit_terakhir' => 'nullable|string',
'jadwal_vaksin' => 'nullable|date',
'jadwal_obat_cacing' => 'nullable|date',
'jadwal_obat_kutu' => 'nullable|date',
'jadwal_grooming' => 'nullable|date',
], [
// Custom Message (Opsional agar user paham kenapa gagal)
'induk_jantan_id.exists' => 'Induk jantan harus kucing dengan jenis kelamin Jantan.',
'induk_betina_id.exists' => 'Induk betina harus kucing dengan jenis kelamin Betina.',
'microchip.unique' => 'Nomor Microchip sudah terdaftar di sistem.',
'tanggal_lahir.before_or_equal' => 'Tanggal lahir tidak boleh melebihi hari ini.',
]);
}
public function completeTreatment(Request $request, Cat $cat)
{
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',
Gate::authorize('check-access', ['cats', 'update']);
$request->validate([
'treatment_id' => 'required|exists:treatments,id',
'remark_pasca_sakit' => 'required|string|max:500',
]);
// ========================================================
// 1. EKSEKUSI PAKSA TABEL TREATMENTS (JALUR KERAS)
// DB::table akan menembus langsung ke database MySQL Anda.
// ========================================================
DB::table('treatments')
->where('id', $request->treatment_id)
->update([
'status_perawatan' => 2,
'remark_pasca_sakit' => $request->remark_pasca_sakit,
'updated_at' => now(),
]);
// Ambil nama keluhan untuk dimunculkan di notifikasi
$namaKeluhan = DB::table('treatments')->where('id', $request->treatment_id)->value('keluhan');
// ========================================================
// 2. CEK SISA PENYAKIT (Adakah penyakit lain di kucing ini yang masih = 1?)
// ========================================================
$sisaPenyakitAktif = DB::table('treatments')
->where('cat_id', $cat->id)
->where('status_perawatan', 1)
->exists();
// ========================================================
// 3. EKSEKUSI TABEL CATS BERDASARKAN HASIL CEK
// ========================================================
if (!$sisaPenyakitAktif) {
// JIKA SUDAH TIDAK ADA PENYAKIT LAIN -> Kucing Full Sehat (2)
$cat->treatment_status = 2;
$cat->save();
return redirect()->back()->with('success', "Penyakit '{$namaKeluhan}' disembuhkan. Kucing sekarang berstatus SEHAT sepenuhnya!");
}
// JIKA MASIH ADA PENYAKIT LAIN -> Pastikan kucing TETAP berstatus Treatment (1)
$cat->treatment_status = 1;
$cat->save();
return redirect()->back()->with('warning', "Penyakit '{$namaKeluhan}' disembuhkan, namun kucing MASIH DALAM PERAWATAN untuk keluhan lainnya.");
}
private function handleImageUpload($file): string
public function pedigree(Cat $cat)
{
$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;
return view('pages.cats.pedigree', compact('cat'));
}
}