chore: stop tracking compiled blade cache

This commit is contained in:
2026-09-23 13:56:51 +07:00
parent 507fc6ccdf
commit edd2f626be
154 changed files with 1149 additions and 12046 deletions
@@ -157,6 +157,28 @@ class MattingController extends Controller
});
}
public function updateStatus(Request $request, Matting $matting): RedirectResponse
{
Gate::authorize('check-access', ['matting', 'update']);
$validated = $request->validate([
'status' => 'required|in:proses,hamil,berhasil,gagal',
'actual_birth_date' => 'nullable|date',
'breeding_notes' => 'nullable|string|max:2000',
]);
if ($validated['status'] === 'hamil' && !$matting->pregnancy_confirmed_at) {
$validated['pregnancy_confirmed_at'] = now()->toDateString();
}
if ($validated['status'] === 'berhasil' && empty($validated['actual_birth_date'])) {
$validated['actual_birth_date'] = $matting->kittens()->min('tgl_lahir') ?: now()->toDateString();
}
$matting->update($validated);
$this->logAction('MATTING_STATUS_UPDATED', $matting, 'Memperbarui tahap pembiakan menjadi ' . $validated['status']);
return back()->with('success', 'Tahap pembiakan berhasil diperbarui.');
}
/**
* SINKRONISASI DATA INDUK (EMS Fix)
*/
@@ -267,7 +289,10 @@ class MattingController extends Controller
'sire_id' => 'required|exists:cats,id',
'dam_id' => 'required|exists:cats,id',
'tgl_kawin_awal' => 'required|date',
'status' => 'required|in:proses,berhasil,gagal',
'status' => 'required|in:proses,hamil,berhasil,gagal',
'pregnancy_confirmed_at' => 'nullable|date',
'actual_birth_date' => 'nullable|date',
'breeding_notes' => 'nullable|string|max:2000',
'ems_bapak' => 'nullable|string',
'ems_ibu' => 'nullable|string',
'no_pedigree_bapak' => 'nullable|string',
+46 -39
View File
@@ -30,13 +30,24 @@ class CatController extends Controller
*/
public function index(Request $request): View
{
// Proteksi Akses: Read
Gate::authorize('check-access', ['cats', 'read']);
$now = now();
$listScope = $request->get('list_scope', 'active');
if (!in_array($listScope, ['active', 'adopted', 'external_parent', 'all'], true)) {
$listScope = 'active';
}
$query = Cat::query()->with(['pic:id,name', 'sire', 'dam', 'registrations.association']);
// 1. PENCARIAN GLOBAL
if ($listScope === 'active') {
$query->activeInCattery();
} elseif ($listScope === 'adopted') {
$query->adopted();
} elseif ($listScope === 'external_parent') {
$query->externalParents();
}
if ($request->filled('search')) {
$search = $request->search;
$query->where(function($q) use ($search) {
@@ -46,22 +57,18 @@ class CatController extends Controller
->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}%");
});
->orWhereHas('pic', fn($sub) => $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') {
} elseif (in_array($filter, ['flea', 'obat_kutu'], true)) {
$q->whereNotNull('jadwal_obat_kutu')->where('jadwal_obat_kutu', '<', $now);
} elseif ($filter === 'deworming' || $filter === 'obat_cacing') {
} elseif (in_array($filter, ['deworming', 'obat_cacing'], true)) {
$q->whereNotNull('jadwal_obat_cacing')->where('jadwal_obat_cacing', '<', $now);
} elseif ($filter === 'grooming') {
$q->whereNotNull('jadwal_grooming')->where('jadwal_grooming', '<', $now);
@@ -69,63 +76,47 @@ class CatController extends Controller
});
}
// --- 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
if (in_array(strtolower($status), ['adult', 'dewasa'], true)) {
$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)
]);
$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('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';
$sort = in_array($request->get('sort'), $allowedSorts, true) ? $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);
$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();
$activeCats = Cat::activeInCattery()->count();
$adoptedCats = Cat::adopted()->count();
$externalParentCats = Cat::externalParents()->count();
$sickCats = Cat::activeInCattery()->where('treatment_status', 1)->count();
$breeds = Cat::activeInCattery()->distinct()->whereNotNull('ras')->pluck('ras');
$breeds = Cat::distinct()->whereNotNull('ras')->pluck('ras');
return view('pages.cats.index', compact('cats', 'totalCats', 'sickCats', 'breeds'));
return view('pages.cats.index', compact(
'cats', 'activeCats', 'adoptedCats', 'externalParentCats', 'sickCats', 'breeds', 'listScope'
));
}
public function create(): View
@@ -169,6 +160,13 @@ class CatController extends Controller
$validated['induk_jantan_id'] = $request->induk_jantan_id;
$validated['induk_betina_id'] = $request->induk_betina_id;
$validated['pic_id'] = $request->pic_id ?? auth()->id();
$validated['cattery_status'] = $request->boolean('is_external_parent')
? 'external_parent'
: ($request->boolean('is_adopted') ? 'adopted' : 'active');
$validated['adopted_at'] = $validated['cattery_status'] === 'adopted'
? ($request->input('adopted_at') ?: now()->toDateString())
: null;
$validated['adoption_notes'] = $request->input('adoption_notes');
$cat = Cat::create($validated);
@@ -267,6 +265,13 @@ private function handleImageUpload($file): string
* Kita paksa nilainya menjadi ['NON-PED'] agar data lama di DB terhapus/terganti.
*/
$validated['registry'] = $request->input('registry', ['NON-PED']);
$validated['cattery_status'] = $request->boolean('is_external_parent')
? 'external_parent'
: ($request->boolean('is_adopted') ? 'adopted' : 'active');
$validated['adopted_at'] = $validated['cattery_status'] === 'adopted'
? ($request->input('adopted_at') ?: ($cat->adopted_at?->format('Y-m-d') ?? now()->toDateString()))
: null;
$validated['adoption_notes'] = $request->input('adoption_notes');
// 5. Update Data Utama
// Logika berat_badan sudah otomatis dihandle oleh Model (static::saving/updated)
@@ -449,7 +454,9 @@ private function validateCat(Request $request, $id = null)
'kode_ems' => 'nullable|string|max:50',
// Status & Lokasi
'status' => 'nullable|string|max:50',
'status' => 'nullable|string|max:50',
'adopted_at' => 'nullable|date',
'adoption_notes'=> 'nullable|string|max:1000',
'lokasi_kandang'=> 'nullable|string|max:255',
'pic_id' => 'nullable|exists:users,id',
@@ -86,6 +86,29 @@ class CatRegistrationController extends Controller
return back()->with('success', 'Dokumen berhasil ditambahkan.');
}
public function showDocument(Cat $cat, CatRegistration $registration, CatRegistrationDocument $document)
{
Gate::authorize('check-access', ['cats', 'read']);
abort_unless($registration->cat_id === $cat->id && $document->cat_registration_id === $registration->id, 404);
abort_unless(Storage::disk('public')->exists($document->file_path), 404);
return response()->file(Storage::disk('public')->path($document->file_path), [
'Content-Disposition' => 'inline; filename="' . basename($document->file_path) . '"',
]);
}
public function downloadDocument(Cat $cat, CatRegistration $registration, CatRegistrationDocument $document)
{
Gate::authorize('check-access', ['cats', 'read']);
abort_unless($registration->cat_id === $cat->id && $document->cat_registration_id === $registration->id, 404);
abort_unless(Storage::disk('public')->exists($document->file_path), 404);
$extension = pathinfo($document->file_path, PATHINFO_EXTENSION);
$filename = str($document->label ?: 'pedigree')->slug('_') . ($extension ? '.' . $extension : '');
return Storage::disk('public')->download($document->file_path, $filename);
}
public function destroyDocument(Cat $cat, CatRegistration $registration, CatRegistrationDocument $document)
{
Gate::authorize('check-access', ['cats', 'update']);
+22 -8
View File
@@ -20,14 +20,14 @@ class DashboardController extends Controller
$selectedStatus = $request->get('status');
$stats = [
'total_cats' => Cat::count(),
'total_cats' => Cat::activeInCattery()->count(),
'total_kittens' => Kitten::count(),
'sick_cats' => Cat::where('treatment_status', 1)->count(),
'total_mating' => Matting::where('status', 'proses')->count(),
'sick_cats' => Cat::activeInCattery()->where('treatment_status', 1)->count(),
'total_mating' => Matting::whereIn('status', ['proses', 'hamil'])->count(),
];
$upcomingBirths = Matting::with(['dam', 'sire'])
->where('status', 'proses')
->whereIn('status', ['proses', 'hamil'])
->whereNotNull('tgl_kawin_awal')
->get()
->map(function ($mating) use ($now) {
@@ -40,19 +40,19 @@ class DashboardController extends Controller
$overdueAlerts = [
'vaccine' => [
'count' => Cat::whereNotNull('jadwal_vaksin')->where('jadwal_vaksin', '<', $now)->count(),
'count' => Cat::activeInCattery()->whereNotNull('jadwal_vaksin')->where('jadwal_vaksin', '<', $now)->count(),
'url' => route('cats.index', ['filter_overdue' => 'vaccine']),
],
'flea' => [
'count' => Cat::whereNotNull('jadwal_obat_kutu')->where('jadwal_obat_kutu', '<', $now)->count(),
'count' => Cat::activeInCattery()->whereNotNull('jadwal_obat_kutu')->where('jadwal_obat_kutu', '<', $now)->count(),
'url' => route('cats.index', ['filter_overdue' => 'flea']),
],
'deworming' => [
'count' => Cat::whereNotNull('jadwal_obat_cacing')->where('jadwal_obat_cacing', '<', $now)->count(),
'count' => Cat::activeInCattery()->whereNotNull('jadwal_obat_cacing')->where('jadwal_obat_cacing', '<', $now)->count(),
'url' => route('cats.index', ['filter_overdue' => 'deworming']),
],
'grooming' => [
'count' => Cat::whereNotNull('jadwal_grooming')->where('jadwal_grooming', '<', $now)->count(),
'count' => Cat::activeInCattery()->whereNotNull('jadwal_grooming')->where('jadwal_grooming', '<', $now)->count(),
'url' => route('cats.index', ['filter_overdue' => 'grooming']),
],
];
@@ -72,6 +72,7 @@ class DashboardController extends Controller
'weight_histories.berat',
'weight_histories.tanggal_timbang'
)
->where(function ($q) { $q->whereNull('cats.cattery_status')->orWhere('cats.cattery_status', 'active'); })
->whereIn('weight_histories.id', function ($query) use ($selectedMonth, $selectedYear) {
$query->select(DB::raw('MAX(id)'))
->from('weight_histories')
@@ -148,6 +149,7 @@ class DashboardController extends Controller
'weight_histories.tanggal_timbang'
)
->whereNull('cats.deleted_at')
->where(function ($q) { $q->whereNull('cats.cattery_status')->orWhere('cats.cattery_status', 'active'); })
->whereDate('weight_histories.tanggal_timbang', '>=', $trendStart->toDateString())
->orderBy('weight_histories.tanggal_timbang')
->orderBy('cats.nama_kucing')
@@ -303,6 +305,16 @@ class DashboardController extends Controller
->sortByDesc('date')
->values();
// Daftar kenaikan/penurunan berat berdasarkan kategori pada periode yang dipilih.
$weightMovements = collect(['Kitten', 'Junior', 'Dewasa'])->mapWithKeys(function ($category) use ($weightLatestTable) {
$rows = $weightLatestTable->where('category', $category)->whereNotNull('change');
return [$category => [
'gainers' => $rows->where('change', '>', 0)->sortByDesc('change')->take(5)->values(),
'losers' => $rows->where('change', '<', 0)->sortBy('change')->take(5)->values(),
]];
});
$weightPeriodLabel = $trendStart->translatedFormat('M Y') . ' – ' . $now->translatedFormat('M Y');
// Inventory
$allExpiringItems = collect();
$lowStock = collect();
@@ -392,6 +404,8 @@ class DashboardController extends Controller
'weightTableCategory',
'weightTrendCategory',
'weightTrendMonths',
'weightMovements',
'weightPeriodLabel',
'lowStock',
'totalLowStock',
'expiringItems',
+29 -18
View File
@@ -4,6 +4,8 @@ namespace App\Http\Controllers;
use App\Models\Setting;
use App\Models\ActivityLog;
use App\Models\Role;
use App\Helpers\WhatsappHelper;
use App\Traits\LogsActivity;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
@@ -24,7 +26,10 @@ class SettingController extends Controller
'app_slogan',
'app_version', // Tambahan baru
'app_address',
'wa_number'
'wa_number',
'notif_hpl_role_id',
'notif_inventory_role_id',
'notif_weight_role_id'
];
public function index()
@@ -46,7 +51,8 @@ class SettingController extends Controller
->take(5)
->get();
return view('pages.admin.settings', compact('settings', 'waStatus', 'emailStatus', 'logs'));
$roles = Role::orderBy('name')->get(['id','name']);
return view('pages.admin.settings', compact('settings', 'waStatus', 'emailStatus', 'logs', 'roles'));
}
public function update(Request $request)
@@ -76,7 +82,21 @@ class SettingController extends Controller
}
}
$this->logAction('UPDATED_SETTINGS', null, "Memperbarui identitas sistem ke versi " . $request->app_version);
foreach (['notif_hpl_wa','notif_inventory_expiry_wa','notif_weight_stale_wa','notif_wa_status'] as $notificationKey) {
Setting::updateOrCreate(
['key' => $notificationKey],
['value' => $request->boolean($notificationKey) ? 'active' : 'inactive', 'group' => 'notification']
);
}
foreach (['notif_hpl_role_id','notif_inventory_role_id','notif_weight_role_id'] as $roleKey) {
if ($request->filled($roleKey)) {
$request->validate([$roleKey => 'exists:roles,id']);
Setting::updateOrCreate(['key' => $roleKey], ['value' => $request->input($roleKey), 'group' => 'notification']);
}
}
$this->logAction('UPDATED_SETTINGS', null, "Memperbarui identitas dan notifikasi sistem ke versi " . $request->app_version);
return back()->with('success', 'Konfigurasi ' . $request->app_name . ' berhasil diperbarui!');
}
@@ -104,21 +124,12 @@ class SettingController extends Controller
private function checkWhatsAppService()
{
// Gunakan cache atau timeout pendek agar page load tidak berat
try {
$response = Http::timeout(3)->withHeaders([
'Authorization' => config('services.fonnte.token') ?? env('FONNTE_TOKEN'),
])->post('https://api.fonnte.com/device');
$data = $response->json();
return [
'active' => ($data['status'] ?? false) == true,
'quota' => $data['quota'] ?? 0,
'message' => ($data['status'] ?? false) ? 'API Terhubung' : ($data['reason'] ?? 'Perangkat tidak aktif')
];
} catch (\Exception $e) {
return ['active' => false, 'quota' => 0, 'message' => 'Layanan WA Offline'];
}
$provider = strtoupper((string) config('services.whatsapp.provider', 'fonnte'));
return [
'active' => WhatsappHelper::configured(),
'quota' => null,
'message' => WhatsappHelper::configured() ? "{$provider} dikonfigurasi" : "{$provider} belum dikonfigurasi",
];
}
private function checkEmailService()
+38
View File
@@ -149,6 +149,44 @@ class UserController extends Controller
return redirect()->route('admin.users.index')->with('success', "User {$userName} telah dihapus.");
}
public function toggleStatus(User $user): RedirectResponse
{
Gate::authorize('check-access', ['users', 'update']);
if ($user->id === auth()->id() && $user->is_active) {
return back()->with('error', 'Akun yang sedang digunakan tidak dapat dinonaktifkan.');
}
$newStatus = !$user->is_active;
$user->forceFill([
'is_active' => $newStatus,
'deactivated_at' => $newStatus ? null : now(),
])->save();
$this->logAction(
$newStatus ? 'USER_ACTIVATED' : 'USER_DEACTIVATED',
$user,
($newStatus ? 'Mengaktifkan' : 'Menonaktifkan') . " akun user: {$user->name}",
'User'
);
return back()->with('success', "Status akun {$user->name} berhasil diperbarui.");
}
public function changePassword(Request $request, User $user): RedirectResponse
{
Gate::authorize('check-access', ['users', 'update']);
$validated = $request->validate([
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user->forceFill(['password' => Hash::make($validated['password'])])->save();
$this->logAction('USER_PASSWORD_CHANGED', $user, "Mengganti kata sandi user: {$user->name}", 'User');
return back()->with('success', "Kata sandi {$user->name} berhasil diganti.");
}
/* -------------------------------------------------------------------------- */
/* AUDIT TRAIL / LOGS SECTION */
/* -------------------------------------------------------------------------- */