chore: stop tracking compiled blade cache
This commit is contained in:
@@ -10,10 +10,9 @@
|
||||
@endphp
|
||||
<title>{{ $appName }}</title>
|
||||
<link rel="icon" href="{{ $favicon ? Storage::url($favicon) : asset('favicon.ico') }}">
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=inter:400,500,600,700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/fontawesome/css/all.min.css') }}">
|
||||
@vite(['resources/css/app.css','resources/js/app.js'])
|
||||
<script src="{{ asset('vendor/ilycats/compat.js') }}" defer></script>
|
||||
@stack('styles')
|
||||
<style>[x-cloak]{display:none!important}</style>
|
||||
</head>
|
||||
@@ -25,21 +24,22 @@
|
||||
<main class="flex-1 overflow-y-auto">
|
||||
<div class="mx-auto w-full max-w-[1500px] p-4 md:p-6 lg:p-8">
|
||||
@if($errors->any())
|
||||
<div class="mb-5 border border-rose-200 bg-rose-50 p-4 text-sm text-rose-700"><p class="font-semibold">Periksa kembali data yang diisi.</p><ul class="mt-1 list-disc pl-5">@foreach($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul></div>
|
||||
<div class="mb-5 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-700"><p class="font-semibold">Periksa kembali data yang diisi.</p><ul class="mt-1 list-disc pl-5">@foreach($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul></div>
|
||||
@endif
|
||||
@if(session('success'))<div class="mb-5 border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-700">{{ session('success') }}</div>@endif
|
||||
@if(session('error'))<div class="mb-5 border border-rose-200 bg-rose-50 p-4 text-sm text-rose-700">{{ session('error') }}</div>@endif
|
||||
@if(session('warning'))<div class="mb-5 border border-amber-200 bg-amber-50 p-4 text-sm text-amber-700">{{ session('warning') }}</div>@endif
|
||||
@if(session('success'))<div class="mb-5 rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-700">{{ session('success') }}</div>@endif
|
||||
@if(session('error'))<div class="mb-5 rounded-xl border border-rose-200 bg-rose-50 p-4 text-sm text-rose-700">{{ session('error') }}</div>@endif
|
||||
@if(session('warning'))<div class="mb-5 rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-700">{{ session('warning') }}</div>@endif
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
window.confirmDelete=function(target,itemName='data ini'){
|
||||
Swal.fire({title:'Hapus data?',text:`${itemName} akan dihapus.`,icon:'warning',showCancelButton:true,confirmButtonText:'Hapus',cancelButtonText:'Batal',confirmButtonColor:'#e11d48'}).then(r=>{if(r.isConfirmed){if(typeof target==='string')document.getElementById(target)?.submit();else target.closest('form')?.submit();}})
|
||||
}
|
||||
window.confirmDelete = function(target, itemName='data ini') {
|
||||
if (!window.confirm(`Hapus ${itemName}? Tindakan ini tidak dapat dibatalkan.`)) return;
|
||||
if (typeof target === 'string') document.getElementById(target)?.submit();
|
||||
else target.closest('form')?.submit();
|
||||
};
|
||||
</script>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ $title ?? config('app.name', 'Laravel') }}</title>
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600,900&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/fontawesome/css/all.min.css') }}">
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
<script src="{{ asset('vendor/ilycats/compat.js') }}" defer></script>
|
||||
</head>
|
||||
<body class="font-sans text-gray-900 antialiased bg-white">
|
||||
{{--
|
||||
|
||||
@@ -119,7 +119,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
function confirmDeleteRole(id, name) {
|
||||
Swal.fire({
|
||||
|
||||
@@ -118,6 +118,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Notifikasi WhatsApp Operasional --}}
|
||||
<div class="rounded-[2rem] border border-slate-200 bg-white p-6 md:p-8">
|
||||
<div class="mb-6 flex items-start gap-4">
|
||||
<div class="flex h-11 w-11 items-center justify-center rounded-2xl bg-emerald-50 text-emerald-600"><i class="fab fa-whatsapp text-xl"></i></div>
|
||||
<div><h3 class="font-bold text-slate-900">Notifikasi WhatsApp operasional</h3><p class="mt-1 text-sm text-slate-500">Pilih jenis pengingat dan role penerima. Pengiriman dilakukan oleh scheduler server.</p></div>
|
||||
</div>
|
||||
<label class="mb-5 flex items-center gap-3 rounded-xl border border-slate-200 bg-slate-50 p-4">
|
||||
<input type="checkbox" name="notif_wa_status" value="1" {{ ($settings['notif_wa_status'] ?? 'active')==='active' ? 'checked' : '' }} class="rounded border-slate-300 text-emerald-600">
|
||||
<span><span class="block text-sm font-bold text-slate-800">Aktifkan notifikasi WhatsApp</span><span class="text-xs text-slate-500">Master switch untuk semua pengingat di bawah.</span></span>
|
||||
</label>
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
@foreach([
|
||||
['notif_hpl_wa','notif_hpl_role_id','HPL kucing','H-7 sampai tanggal HPL, dikirim setiap hari.'],
|
||||
['notif_inventory_expiry_wa','notif_inventory_role_id','Kedaluwarsa inventori','Obat, makanan, dan perlengkapan mulai H-14.'],
|
||||
['notif_weight_stale_wa','notif_weight_role_id','Berat belum diperbarui','Kucing aktif yang tidak ditimbang lebih dari 7 hari.'],
|
||||
] as [$switchKey,$roleKey,$title,$desc])
|
||||
<div class="rounded-2xl border border-slate-200 p-4">
|
||||
<label class="flex items-start gap-3"><input type="checkbox" name="{{ $switchKey }}" value="1" {{ ($settings[$switchKey] ?? 'active')==='active' ? 'checked' : '' }} class="mt-1 rounded border-slate-300 text-indigo-600"><span><span class="block text-sm font-bold text-slate-800">{{ $title }}</span><span class="mt-1 block text-xs leading-relaxed text-slate-500">{{ $desc }}</span></span></label>
|
||||
<div class="mt-4"><label class="field-label">Role penerima</label><select name="{{ $roleKey }}" class="field-input rounded-xl"><option value="">Admin (default)</option>@foreach($roles as $role)<option value="{{ $role->id }}" @selected((string)($settings[$roleKey] ?? '') === (string)$role->id)>{{ $role->name }}</option>@endforeach</select></div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Card 3: Status Layanan --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{{-- WA Status --}}
|
||||
@@ -129,7 +153,7 @@
|
||||
<div>
|
||||
<h4 class="text-[10px] font-black text-slate-400 uppercase tracking-widest">WhatsApp Gateway</h4>
|
||||
<p class="text-xs font-black {{ $waStatus['active'] ? 'text-emerald-700' : 'text-rose-700' }}">
|
||||
{{ $waStatus['active'] ? 'Terhubung (Kuota: '.$waStatus['quota'].')' : 'Terputus' }}
|
||||
{{ $waStatus['message'] ?? ($waStatus['active'] ? 'Terkonfigurasi' : 'Belum dikonfigurasi') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -227,11 +227,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Status keberadaan di cattery --}}
|
||||
<div class="rounded-[2rem] border border-slate-200 bg-white p-6 shadow-sm">
|
||||
<div class="mb-4">
|
||||
<h4 class="text-xs font-black uppercase tracking-widest text-slate-700">Status keberadaan</h4>
|
||||
<p class="mt-1 text-xs text-slate-500">Kucing teradopsi dan induk eksternal tetap tersimpan untuk riwayat/pedigree, tetapi tidak dihitung sebagai kucing aktif di cattery.</p>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<label class="flex cursor-pointer items-start gap-3 rounded-2xl border border-emerald-100 bg-emerald-50/50 p-4">
|
||||
<input type="checkbox" id="is_adopted" name="is_adopted" value="1" {{ old('is_adopted') ? 'checked' : '' }} class="mt-1 rounded border-slate-300 text-emerald-600">
|
||||
<span><span class="block text-sm font-bold text-emerald-800">Kucing teradopsi</span><span class="mt-1 block text-xs text-emerald-700/80">Tandai bila kucing sudah keluar dari cattery karena adopsi.</span></span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-start gap-3 rounded-2xl border border-amber-100 bg-amber-50/50 p-4">
|
||||
<input type="checkbox" id="is_external_parent" name="is_external_parent" value="1" {{ old('is_external_parent') ? 'checked' : '' }} class="mt-1 rounded border-slate-300 text-amber-600">
|
||||
<span><span class="block text-sm font-bold text-amber-800">Induk eksternal / tidak tinggal di cattery</span><span class="mt-1 block text-xs text-amber-700/80">Gunakan untuk sire/dam yang hanya dibutuhkan pada silsilah dan pembiakan.</span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mt-4 grid gap-4 md:grid-cols-2">
|
||||
<div><label class="text-[10px] font-bold uppercase text-slate-400">Tanggal adopsi</label><input type="date" name="adopted_at" value="{{ old('adopted_at') }}" class="mt-1 w-full rounded-xl border-slate-200 bg-slate-50 text-sm"></div>
|
||||
<div><label class="text-[10px] font-bold uppercase text-slate-400">Catatan adopsi / keberadaan</label><input type="text" name="adoption_notes" value="{{ old('adoption_notes') }}" class="mt-1 w-full rounded-xl border-slate-200 bg-slate-50 text-sm" placeholder="Opsional"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Action Button --}}
|
||||
<div class="pt-4">
|
||||
<button type="submit" class="w-full py-6 bg-indigo-600 text-white text-xs font-black rounded-[2.5rem] shadow-2xl shadow-indigo-100 hover:bg-indigo-700 hover:-translate-y-1 transition-all active:scale-[0.98] flex items-center justify-center gap-4 group">
|
||||
<i class="fas fa-save text-xl group-hover:rotate-12 transition-transform"></i>
|
||||
<span class="tracking-[0.2em] uppercase">Simpan Record Baru Ilycats</span>
|
||||
<span class="tracking-[0.2em] uppercase">Simpan Data Kucing Baru</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -265,6 +287,16 @@
|
||||
handleSearchableDatalist('sire_search', 'sire_list', 'induk_jantan_id');
|
||||
handleSearchableDatalist('dam_search', 'dam_list', 'induk_betina_id');
|
||||
|
||||
// Status teradopsi dan induk eksternal bersifat saling eksklusif.
|
||||
const adoptedCheckbox = document.getElementById('is_adopted');
|
||||
const externalParentCheckbox = document.getElementById('is_external_parent');
|
||||
adoptedCheckbox?.addEventListener('change', () => {
|
||||
if (adoptedCheckbox.checked && externalParentCheckbox) externalParentCheckbox.checked = false;
|
||||
});
|
||||
externalParentCheckbox?.addEventListener('change', () => {
|
||||
if (externalParentCheckbox.checked && adoptedCheckbox) adoptedCheckbox.checked = false;
|
||||
});
|
||||
|
||||
// Preview Foto
|
||||
function previewImage(event) {
|
||||
const reader = new FileReader();
|
||||
|
||||
@@ -231,6 +231,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Status keberadaan di cattery --}}
|
||||
<div class="rounded-[2rem] border border-slate-200 bg-white p-6 shadow-sm">
|
||||
<div class="mb-4">
|
||||
<h4 class="text-xs font-black uppercase tracking-widest text-slate-700">Status keberadaan</h4>
|
||||
<p class="mt-1 text-xs text-slate-500">Kucing teradopsi dan induk eksternal tetap tersimpan untuk riwayat/pedigree, tetapi tidak dihitung sebagai kucing aktif di cattery.</p>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<label class="flex cursor-pointer items-start gap-3 rounded-2xl border border-emerald-100 bg-emerald-50/50 p-4">
|
||||
<input type="checkbox" id="is_adopted" name="is_adopted" value="1" {{ old('is_adopted', ($cat->cattery_status ?? 'active') === 'adopted') ? 'checked' : '' }} class="mt-1 rounded border-slate-300 text-emerald-600">
|
||||
<span><span class="block text-sm font-bold text-emerald-800">Kucing teradopsi</span><span class="mt-1 block text-xs text-emerald-700/80">Tandai bila kucing sudah keluar dari cattery karena adopsi.</span></span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-start gap-3 rounded-2xl border border-amber-100 bg-amber-50/50 p-4">
|
||||
<input type="checkbox" id="is_external_parent" name="is_external_parent" value="1" {{ old('is_external_parent', ($cat->cattery_status ?? 'active') === 'external_parent') ? 'checked' : '' }} class="mt-1 rounded border-slate-300 text-amber-600">
|
||||
<span><span class="block text-sm font-bold text-amber-800">Induk eksternal / tidak tinggal di cattery</span><span class="mt-1 block text-xs text-amber-700/80">Gunakan untuk sire/dam yang hanya dibutuhkan pada silsilah dan pembiakan.</span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mt-4 grid gap-4 md:grid-cols-2">
|
||||
<div><label class="text-[10px] font-bold uppercase text-slate-400">Tanggal adopsi</label><input type="date" name="adopted_at" value="{{ old('adopted_at', $cat->adopted_at?->format('Y-m-d')) }}" class="mt-1 w-full rounded-xl border-slate-200 bg-slate-50 text-sm"></div>
|
||||
<div><label class="text-[10px] font-bold uppercase text-slate-400">Catatan adopsi / keberadaan</label><input type="text" name="adoption_notes" value="{{ old('adoption_notes', $cat->adoption_notes) }}" class="mt-1 w-full rounded-xl border-slate-200 bg-slate-50 text-sm" placeholder="Opsional"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Action Button --}}
|
||||
<button type="submit" class="w-full py-6 bg-indigo-600 text-white text-xs font-black rounded-[2.5rem] shadow-2xl hover:bg-indigo-700 hover:shadow-indigo-200 transition-all active:scale-[0.98] flex items-center justify-center gap-4 group">
|
||||
<i class="fas fa-save text-lg group-hover:rotate-12 transition-transform"></i>
|
||||
@@ -263,6 +285,16 @@
|
||||
handleSearchableDatalist('sire_search', 'sire_list', 'induk_jantan_id');
|
||||
handleSearchableDatalist('dam_search', 'dam_list', 'induk_betina_id');
|
||||
|
||||
// Status teradopsi dan induk eksternal bersifat saling eksklusif.
|
||||
const adoptedCheckbox = document.getElementById('is_adopted');
|
||||
const externalParentCheckbox = document.getElementById('is_external_parent');
|
||||
adoptedCheckbox?.addEventListener('change', () => {
|
||||
if (adoptedCheckbox.checked && externalParentCheckbox) externalParentCheckbox.checked = false;
|
||||
});
|
||||
externalParentCheckbox?.addEventListener('change', () => {
|
||||
if (externalParentCheckbox.checked && adoptedCheckbox) adoptedCheckbox.checked = false;
|
||||
});
|
||||
|
||||
// Preview Foto saat dipilih
|
||||
function previewImage(event) {
|
||||
const reader = new FileReader();
|
||||
|
||||
@@ -3,44 +3,80 @@
|
||||
$currentSort=request('sort','created_at'); $currentDir=request('direction','desc'); $nextDir=$currentDir==='asc'?'desc':'asc';
|
||||
$sortIcon=function($field)use($currentSort,$currentDir){return $currentSort!==$field?'fa-sort text-slate-300':($currentDir==='asc'?'fa-sort-up text-indigo-600':'fa-sort-down text-indigo-600');};
|
||||
@endphp
|
||||
|
||||
<div class="mb-6 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div><h1 class="text-2xl font-bold text-slate-900">Data kucing</h1><p class="mt-1 text-sm text-slate-500">Profil, silsilah, pedigree, berat badan, dan jadwal perawatan.</p></div>
|
||||
<div class="flex flex-wrap gap-2">@can('check-access',['cats','update'])<a href="{{ route('cats.bulk-weight') }}" class="border border-slate-300 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 hover:bg-slate-50"><i class="fas fa-weight-scale mr-2"></i>Input berat massal</a>@endcan @can('check-access',['cats','create'])<a href="{{ route('cats.create') }}" class="bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700"><i class="fas fa-plus mr-2"></i>Tambah kucing</a>@endcan</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-slate-900">Data kucing</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Kucing aktif, kucing teradopsi, induk eksternal, silsilah, pedigree, dan pemantauan kesehatan.</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@can('check-access',['cats','update'])
|
||||
<a href="{{ route('cats.bulk-weight') }}" class="rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 hover:bg-slate-50"><i class="fas fa-weight-scale mr-2"></i>Input berat massal</a>
|
||||
@endcan
|
||||
@can('check-access',['cats','create'])
|
||||
<a href="{{ route('cats.create') }}" class="rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700"><i class="fas fa-plus mr-2"></i>Tambah kucing / induk eksternal</a>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 grid gap-3 sm:grid-cols-3">
|
||||
<div class="border border-slate-200 bg-white p-4"><p class="text-xs text-slate-500">Total kucing</p><p class="mt-1 text-2xl font-bold text-slate-900">{{ $totalCats }}</p></div>
|
||||
<div class="border border-slate-200 bg-white p-4"><p class="text-xs text-slate-500">Sehat</p><p class="mt-1 text-2xl font-bold text-emerald-700">{{ max(0,$totalCats-$sickCats) }}</p></div>
|
||||
<div class="border border-slate-200 bg-white p-4"><p class="text-xs text-slate-500">Dalam perawatan</p><p class="mt-1 text-2xl font-bold text-rose-600">{{ $sickCats }}</p></div>
|
||||
<div class="mb-5 grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<a href="{{ route('cats.index',['list_scope'=>'active']) }}" class="rounded-2xl border {{ $listScope==='active'?'border-indigo-300 bg-indigo-50/60':'border-slate-200 bg-white' }} p-4 hover:border-indigo-300">
|
||||
<p class="text-xs font-semibold text-slate-500">Kucing aktif di cattery</p><p class="mt-1 text-2xl font-bold text-slate-900">{{ $activeCats }}</p>
|
||||
</a>
|
||||
<a href="{{ route('cats.index',['list_scope'=>'adopted']) }}" class="rounded-2xl border {{ $listScope==='adopted'?'border-emerald-300 bg-emerald-50/60':'border-slate-200 bg-white' }} p-4 hover:border-emerald-300">
|
||||
<p class="text-xs font-semibold text-slate-500">Kucing teradopsi</p><p class="mt-1 text-2xl font-bold text-emerald-700">{{ $adoptedCats }}</p>
|
||||
</a>
|
||||
<a href="{{ route('cats.index',['list_scope'=>'external_parent']) }}" class="rounded-2xl border {{ $listScope==='external_parent'?'border-amber-300 bg-amber-50/60':'border-slate-200 bg-white' }} p-4 hover:border-amber-300">
|
||||
<p class="text-xs font-semibold text-slate-500">Induk eksternal</p><p class="mt-1 text-2xl font-bold text-amber-700">{{ $externalParentCats }}</p>
|
||||
</a>
|
||||
<a href="{{ route('cats.index',['list_scope'=>'active','filter_health'=>'treatment']) }}" class="rounded-2xl border border-slate-200 bg-white p-4 hover:border-rose-300">
|
||||
<p class="text-xs font-semibold text-slate-500">Dalam perawatan</p><p class="mt-1 text-2xl font-bold text-rose-600">{{ $sickCats }}</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<section class="border border-slate-200 bg-white">
|
||||
<div class="flex flex-col gap-3 border-b border-slate-200 p-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<form method="GET" action="{{ route('cats.index') }}" class="flex w-full max-w-lg gap-2">
|
||||
<input type="hidden" name="status" value="{{ request('status') }}"><input type="hidden" name="sort" value="{{ request('sort') }}"><input type="hidden" name="direction" value="{{ request('direction') }}">
|
||||
<div class="relative flex-1"><i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-sm text-slate-400"></i><input name="search" value="{{ request('search') }}" placeholder="Cari nama, EMS, microchip, ras, kandang..." class="field-input pl-9"></div><button class="border border-slate-300 px-4 text-sm font-semibold text-slate-700 hover:bg-slate-50">Cari</button>
|
||||
<section class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
<div class="flex flex-col gap-3 border-b border-slate-200 p-4 xl:flex-row xl:items-center xl:justify-between">
|
||||
<form method="GET" action="{{ route('cats.index') }}" class="flex w-full max-w-xl gap-2">
|
||||
<input type="hidden" name="list_scope" value="{{ $listScope }}">
|
||||
<input type="hidden" name="status" value="{{ request('status') }}">
|
||||
<input type="hidden" name="sort" value="{{ request('sort') }}">
|
||||
<input type="hidden" name="direction" value="{{ request('direction') }}">
|
||||
<div class="relative flex-1"><i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-sm text-slate-400"></i><input name="search" value="{{ request('search') }}" placeholder="Cari nama, EMS, microchip, ras, kandang..." class="field-input rounded-xl pl-9"></div>
|
||||
<button class="rounded-xl border border-slate-300 px-4 text-sm font-semibold text-slate-700 hover:bg-slate-50">Cari</button>
|
||||
</form>
|
||||
<div class="flex flex-wrap gap-1 text-sm">@foreach([''=>'Semua','Dewasa'=>'Dewasa','Junior'=>'Junior','Kitten'=>'Kitten'] as $value=>$label)<a href="{{ route('cats.index',array_merge(request()->except('page','status'),$value?['status'=>$value]:[])) }}" class="px-3 py-2 {{ request('status','')===$value?'bg-slate-900 text-white':'text-slate-600 hover:bg-slate-100' }}">{{ $label }}</a>@endforeach</div>
|
||||
<div class="flex flex-wrap gap-1 text-sm">
|
||||
@foreach([''=>'Semua usia','Dewasa'=>'Dewasa','Junior'=>'Junior','Kitten'=>'Kitten'] as $value=>$label)
|
||||
<a href="{{ route('cats.index',array_merge(request()->except('page','status'),$value?['status'=>$value]:[])) }}" class="rounded-lg px-3 py-2 {{ request('status','')===$value?'bg-slate-900 text-white':'text-slate-600 hover:bg-slate-100' }}">{{ $label }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold text-slate-500"><tr>
|
||||
<th class="px-4 py-3"><a class="flex items-center gap-2" href="{{ request()->fullUrlWithQuery(['sort'=>'nama_kucing','direction'=>$nextDir]) }}">Kucing <i class="fas {{ $sortIcon('nama_kucing') }}"></i></a></th>
|
||||
<th class="px-4 py-3">Identitas & pedigree</th><th class="px-4 py-3">Silsilah</th>
|
||||
<th class="px-4 py-3">Status cattery</th>
|
||||
<th class="px-4 py-3">Identitas & pedigree</th>
|
||||
<th class="px-4 py-3">Silsilah</th>
|
||||
<th class="px-4 py-3"><a class="flex items-center gap-2" href="{{ request()->fullUrlWithQuery(['sort'=>'berat_badan','direction'=>$nextDir]) }}">Berat <i class="fas {{ $sortIcon('berat_badan') }}"></i></a></th>
|
||||
<th class="px-4 py-3"><a class="flex items-center gap-2" href="{{ request()->fullUrlWithQuery(['sort'=>'lokasi_kandang','direction'=>$nextDir]) }}">Kandang <i class="fas {{ $sortIcon('lokasi_kandang') }}"></i></a></th>
|
||||
<th class="px-4 py-3 text-right">Aksi</th>
|
||||
</tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
@forelse($cats as $cat)
|
||||
@php
|
||||
$statusStyle = match($cat->cattery_status ?: 'active') {
|
||||
'adopted' => 'bg-emerald-50 text-emerald-700 border-emerald-100',
|
||||
'external_parent' => 'bg-amber-50 text-amber-700 border-amber-100',
|
||||
default => 'bg-indigo-50 text-indigo-700 border-indigo-100',
|
||||
};
|
||||
@endphp
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-4 py-3"><div class="flex min-w-52 items-center gap-3"><img src="{{ $cat->foto_url }}" alt="{{ $cat->nama_kucing }}" class="h-11 w-11 rounded-lg border border-slate-200 object-cover"><div><a href="{{ route('cats.show',$cat) }}" class="font-semibold text-slate-900 hover:text-indigo-600">{{ $cat->nama_kucing }}</a><p class="mt-0.5 text-xs text-slate-500">{{ $cat->jenis_kelamin }} · {{ $cat->ras }}</p></div></div></td>
|
||||
<td class="px-4 py-3"><div class="space-y-1"><p class="text-xs text-slate-600"><span class="text-slate-400">EMS:</span> {{ $cat->kode_ems ?: '-' }}</p><p class="text-xs text-slate-600"><span class="text-slate-400">Microchip:</span> {{ $cat->microchip ?: '-' }}</p><div class="flex flex-wrap gap-1">@forelse($cat->registrations as $reg)<span class="border border-slate-200 bg-slate-50 px-1.5 py-0.5 text-[11px] font-semibold text-slate-600">{{ $reg->association->code }}{{ $reg->is_primary?' · utama':'' }}</span>@empty<span class="text-xs text-slate-400">Belum ada registrasi</span>@endforelse</div></div></td>
|
||||
<td class="px-4 py-3"><p class="text-xs text-slate-600">{{ $cat->age }}</p><p class="mt-1 text-xs text-slate-500">Jantan: {{ $cat->sire->nama_kucing ?? '-' }}</p><p class="text-xs text-slate-500">Betina: {{ $cat->dam->nama_kucing ?? '-' }}</p></td>
|
||||
<td class="px-4 py-3"><p class="font-semibold text-slate-800">{{ number_format((float)($cat->berat_badan ?? $cat->berat_terakhir ?? 0),2) }} kg</p><p class="text-xs {{ $cat->treatment_status==1?'text-rose-600':'text-emerald-600' }}">{{ $cat->treatment_status==1?'Dalam perawatan':'Sehat' }}</p></td>
|
||||
<td class="px-4 py-3"><p class="text-slate-700">{{ $cat->lokasi_kandang ?: '-' }}</p><p class="text-xs text-slate-500">{{ $cat->pic->name ?? '-' }}</p></td>
|
||||
<td class="px-4 py-3"><div class="flex justify-end gap-1"><a href="{{ route('cats.show',$cat) }}" class="p-2 text-slate-500 hover:bg-slate-100" title="Lihat"><i class="fas fa-eye"></i></a><a href="{{ route('cats.pedigree',$cat) }}" class="p-2 text-slate-500 hover:bg-slate-100" title="Pedigree"><i class="fas fa-file-lines"></i></a>@can('check-access',['cats','update'])<a href="{{ route('cats.edit',$cat) }}" class="p-2 text-slate-500 hover:bg-slate-100" title="Ubah"><i class="fas fa-pen"></i></a>@endcan @can('check-access',['cats','delete'])<form id="delete-cat-{{ $cat->id }}" method="POST" action="{{ route('cats.destroy',$cat) }}">@csrf @method('DELETE')<button type="button" onclick="confirmDelete('delete-cat-{{ $cat->id }}','{{ addslashes($cat->nama_kucing) }}')" class="p-2 text-rose-500 hover:bg-rose-50" title="Hapus"><i class="fas fa-trash"></i></button></form>@endcan</div></td>
|
||||
<td class="px-4 py-3"><div class="flex min-w-52 items-center gap-3"><img src="{{ $cat->foto_url }}" alt="{{ $cat->nama_kucing }}" class="h-11 w-11 rounded-xl border border-slate-200 object-cover"><div><a href="{{ route('cats.show',$cat) }}" class="font-semibold text-slate-900 hover:text-indigo-600">{{ $cat->nama_kucing }}</a><p class="mt-0.5 text-xs text-slate-500">{{ $cat->jenis_kelamin }} · {{ $cat->ras }}</p></div></div></td>
|
||||
<td class="px-4 py-3"><span class="inline-flex rounded-full border px-2.5 py-1 text-xs font-semibold {{ $statusStyle }}">{{ $cat->cattery_status_label }}</span>@if($cat->adopted_at)<p class="mt-1 text-xs text-slate-400">Sejak {{ $cat->adopted_at->format('d-m-Y') }}</p>@endif</td>
|
||||
<td class="px-4 py-3"><div class="space-y-1"><p class="text-xs text-slate-600"><span class="text-slate-400">EMS:</span> {{ $cat->kode_ems ?: '-' }}</p><p class="text-xs text-slate-600"><span class="text-slate-400">Microchip:</span> {{ $cat->microchip ?: '-' }}</p><div class="flex flex-wrap gap-1">@forelse($cat->registrations as $reg)<span class="rounded border border-slate-200 bg-slate-50 px-1.5 py-0.5 text-[11px] font-semibold text-slate-600">{{ $reg->association->code }}{{ $reg->is_primary?' · utama':'' }}</span>@empty<span class="text-xs text-slate-400">Belum ada registrasi</span>@endforelse</div></div></td>
|
||||
<td class="px-4 py-3"><p class="text-xs text-slate-600">{{ $cat->age }}</p><p class="mt-1 text-xs text-slate-500">Sire: {{ $cat->sire->nama_kucing ?? '-' }}</p><p class="text-xs text-slate-500">Dam: {{ $cat->dam->nama_kucing ?? '-' }}</p></td>
|
||||
<td class="px-4 py-3"><p class="font-semibold text-slate-800">{{ number_format((float)($cat->berat_badan ?? $cat->berat_terakhir ?? 0),2) }} kg</p>@if(($cat->cattery_status ?: 'active')==='active')<p class="text-xs {{ $cat->treatment_status==1?'text-rose-600':'text-emerald-600' }}">{{ $cat->treatment_status==1?'Dalam perawatan':'Sehat' }}</p>@else<p class="text-xs text-slate-400">Tidak dihitung sebagai kucing aktif</p>@endif</td>
|
||||
<td class="px-4 py-3"><div class="flex justify-end gap-1"><a href="{{ route('cats.show',$cat) }}" class="rounded-lg p-2 text-slate-500 hover:bg-slate-100" title="Lihat"><i class="fas fa-eye"></i></a><a href="{{ route('cats.pedigree',$cat) }}" class="rounded-lg p-2 text-slate-500 hover:bg-slate-100" title="Pedigree"><i class="fas fa-file-lines"></i></a>@can('check-access',['cats','update'])<a href="{{ route('cats.edit',$cat) }}" class="rounded-lg p-2 text-slate-500 hover:bg-slate-100" title="Ubah"><i class="fas fa-pen"></i></a>@endcan @can('check-access',['cats','delete'])<form id="delete-cat-{{ $cat->id }}" method="POST" action="{{ route('cats.destroy',$cat) }}">@csrf @method('DELETE')<button type="button" onclick="confirmDelete('delete-cat-{{ $cat->id }}','{{ addslashes($cat->nama_kucing) }}')" class="rounded-lg p-2 text-rose-500 hover:bg-rose-50" title="Hapus"><i class="fas fa-trash"></i></button></form>@endcan</div></td>
|
||||
</tr>
|
||||
@empty<tr><td colspan="6" class="px-4 py-12 text-center text-sm text-slate-500">Data kucing tidak ditemukan.</td></tr>@endforelse
|
||||
</tbody>
|
||||
|
||||
@@ -1,51 +1,79 @@
|
||||
<x-app-layout>
|
||||
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div><a href="{{ route('cats.show',$cat) }}" class="mb-2 inline-flex items-center gap-2 text-sm text-slate-500 hover:text-slate-800"><i class="fas fa-arrow-left"></i>Kembali ke profil</a><h1 class="text-2xl font-bold text-slate-900">Registrasi & pedigree</h1><p class="mt-1 text-sm text-slate-500">{{ $cat->nama_kucing }} dapat memiliki registrasi dan sertifikat dari lebih dari satu asosiasi.</p></div>
|
||||
<div>
|
||||
<a href="{{ route('cats.show',$cat) }}" class="mb-2 inline-flex items-center gap-2 text-sm text-slate-500 hover:text-slate-800"><i class="fas fa-arrow-left"></i>Kembali ke profil</a>
|
||||
<h1 class="text-2xl font-bold text-slate-900">Registrasi & pedigree</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">{{ $cat->nama_kucing }} dapat memiliki registrasi dan sertifikat dari beberapa asosiasi.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 xl:grid-cols-[1fr_360px]">
|
||||
<div class="space-y-4">
|
||||
@forelse($cat->registrations as $registration)
|
||||
<section class="border border-slate-200 bg-white">
|
||||
<section class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3 border-b border-slate-200 px-5 py-4">
|
||||
<div class="flex items-center gap-3"><div class="flex h-10 min-w-10 items-center justify-center bg-slate-100 px-2 text-sm font-bold text-slate-700">{{ $registration->association->code }}</div><div><h2 class="font-semibold text-slate-900">{{ $registration->association->name }}</h2><p class="text-xs text-slate-500">{{ $registration->is_primary ? 'Pedigree utama' : 'Registrasi tambahan' }}</p></div></div>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex h-10 min-w-10 items-center justify-center rounded-xl bg-slate-100 px-2 text-sm font-bold text-slate-700">{{ $registration->association->code }}</div>
|
||||
<div><h2 class="font-semibold text-slate-900">{{ $registration->association->name }}</h2><p class="text-xs text-slate-500">{{ $registration->is_primary ? 'Pedigree utama' : 'Registrasi tambahan' }}</p></div>
|
||||
</div>
|
||||
@can('check-access',['cats','update'])
|
||||
<form action="{{ route('cats.registrations.destroy',[$cat,$registration]) }}" method="POST" onsubmit="return confirm('Hapus registrasi ini beserta dokumennya?')">@csrf @method('DELETE')<button class="text-sm font-medium text-rose-600 hover:text-rose-700">Hapus</button></form>
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
<form action="{{ route('cats.registrations.update',[$cat,$registration]) }}" method="POST" class="grid gap-4 p-5 sm:grid-cols-2 lg:grid-cols-3">@csrf @method('PUT')
|
||||
<div><label class="field-label">Nomor registrasi</label><input name="registration_number" value="{{ $registration->registration_number }}" class="field-input"></div>
|
||||
<div><label class="field-label">Nomor pedigree</label><input name="pedigree_number" value="{{ $registration->pedigree_number }}" class="field-input"></div>
|
||||
<div><label class="field-label">Nomor sertifikat</label><input name="certificate_number" value="{{ $registration->certificate_number }}" class="field-input"></div>
|
||||
<div><label class="field-label">Tanggal terbit</label><input type="date" name="issue_date" value="{{ $registration->issue_date?->format('Y-m-d') }}" class="field-input"></div>
|
||||
<div><label class="field-label">Gelar / title</label><input name="title" value="{{ $registration->title }}" class="field-input" placeholder="Contoh: CH, GCH"></div>
|
||||
<div><label class="field-label">Status kepemilikan</label><select name="ownership_status" class="field-input"><option value="">-</option>@foreach(['milik_sendiri'=>'Milik sendiri','co_own'=>'Co-own','transfer'=>'Transfer','lainnya'=>'Lainnya'] as $v=>$l)<option value="{{ $v }}" @selected($registration->ownership_status===$v)>{{ $l }}</option>@endforeach</select></div>
|
||||
<div class="sm:col-span-2 lg:col-span-3"><label class="field-label">Catatan</label><textarea name="notes" rows="2" class="field-input">{{ $registration->notes }}</textarea></div>
|
||||
<div><label class="field-label">Nomor registrasi</label><input name="registration_number" value="{{ $registration->registration_number }}" class="field-input rounded-xl"></div>
|
||||
<div><label class="field-label">Nomor pedigree</label><input name="pedigree_number" value="{{ $registration->pedigree_number }}" class="field-input rounded-xl"></div>
|
||||
<div><label class="field-label">Nomor sertifikat</label><input name="certificate_number" value="{{ $registration->certificate_number }}" class="field-input rounded-xl"></div>
|
||||
<div><label class="field-label">Tanggal terbit</label><input type="date" name="issue_date" value="{{ $registration->issue_date?->format('Y-m-d') }}" class="field-input rounded-xl"></div>
|
||||
<div><label class="field-label">Gelar / title</label><input name="title" value="{{ $registration->title }}" class="field-input rounded-xl" placeholder="Contoh: CH, GCH"></div>
|
||||
<div><label class="field-label">Status kepemilikan</label><select name="ownership_status" class="field-input rounded-xl"><option value="">-</option>@foreach(['milik_sendiri'=>'Milik sendiri','co_own'=>'Co-own','transfer'=>'Transfer','lainnya'=>'Lainnya'] as $v=>$l)<option value="{{ $v }}" @selected($registration->ownership_status===$v)>{{ $l }}</option>@endforeach</select></div>
|
||||
<div class="sm:col-span-2 lg:col-span-3"><label class="field-label">Catatan</label><textarea name="notes" rows="2" class="field-input rounded-xl">{{ $registration->notes }}</textarea></div>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700"><input type="hidden" name="is_primary" value="0"><input type="checkbox" name="is_primary" value="1" @checked($registration->is_primary) class="rounded border-slate-300 text-indigo-600">Jadikan pedigree utama</label>
|
||||
<div class="sm:col-span-2 flex justify-end"><button class="border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Simpan perubahan</button></div>
|
||||
@can('check-access',['cats','update'])<div class="sm:col-span-2 lg:col-span-3"><button class="rounded-xl bg-slate-800 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-900">Simpan perubahan registrasi</button></div>@endcan
|
||||
</form>
|
||||
<div class="border-t border-slate-200 p-5">
|
||||
<div class="mb-3 flex items-center justify-between"><h3 class="text-sm font-semibold text-slate-800">Dokumen</h3><span class="text-xs text-slate-500">PDF/JPG/PNG, maks. 10 MB</span></div>
|
||||
<div class="mb-4 grid gap-2">@forelse($registration->documents as $document)<div class="flex items-center justify-between gap-3 border border-slate-200 p-3"><div class="min-w-0"><a href="{{ Storage::url($document->file_path) }}" target="_blank" class="truncate text-sm font-medium text-indigo-600 hover:underline">{{ $document->label }}</a><p class="text-xs text-slate-500">{{ ucfirst($document->document_type) }}{{ $document->issued_at ? ' · '.$document->issued_at->format('d-m-Y') : '' }}</p></div><form action="{{ route('cats.registrations.documents.destroy',[$cat,$registration,$document]) }}" method="POST">@csrf @method('DELETE')<button class="text-xs font-medium text-rose-600">Hapus</button></form></div>@empty<p class="text-sm text-slate-500">Belum ada dokumen pada registrasi ini.</p>@endforelse</div>
|
||||
<form action="{{ route('cats.registrations.documents.store',[$cat,$registration]) }}" method="POST" enctype="multipart/form-data" class="grid gap-3 md:grid-cols-[1fr_180px_180px_auto]">@csrf<input type="file" name="document" required class="field-input py-2"><select name="document_type" class="field-input"><option value="pedigree">Pedigree</option><option value="registration">Registrasi</option><option value="ownership">Kepemilikan</option><option value="title">Gelar</option><option value="other">Lainnya</option></select><input name="label" class="field-input" placeholder="Nama dokumen"><button class="bg-slate-800 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-900">Tambah dokumen</button></form>
|
||||
|
||||
<div class="border-t border-slate-200 bg-slate-50/50 p-5">
|
||||
<div class="mb-3 flex items-center justify-between"><h3 class="text-sm font-semibold text-slate-800">Dokumen pedigree</h3><span class="text-xs text-slate-500">PDF/JPG/PNG, maks. 10 MB</span></div>
|
||||
<div class="mb-4 grid gap-2">
|
||||
@forelse($registration->documents as $document)
|
||||
<div class="flex flex-col gap-3 rounded-xl border border-slate-200 bg-white p-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="min-w-0"><p class="truncate text-sm font-semibold text-slate-800">{{ $document->label }}</p><p class="text-xs text-slate-500">{{ ucfirst($document->document_type) }}{{ $document->issued_at ? ' · '.$document->issued_at->format('d-m-Y') : '' }}</p></div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('cats.registrations.documents.show',[$cat,$registration,$document]) }}" target="_blank" rel="noopener" class="rounded-lg border border-indigo-200 bg-indigo-50 px-3 py-2 text-xs font-semibold text-indigo-700 hover:bg-indigo-100"><i class="fas fa-eye mr-1"></i>Lihat</a>
|
||||
<a href="{{ route('cats.registrations.documents.download',[$cat,$registration,$document]) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 hover:bg-slate-50"><i class="fas fa-download mr-1"></i>Unduh</a>
|
||||
@can('check-access',['cats','update'])<form action="{{ route('cats.registrations.documents.destroy',[$cat,$registration,$document]) }}" method="POST" onsubmit="return confirm('Hapus dokumen ini?')">@csrf @method('DELETE')<button class="rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-xs font-semibold text-rose-700 hover:bg-rose-100"><i class="fas fa-trash mr-1"></i>Hapus</button></form>@endcan
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="rounded-xl border border-dashed border-slate-300 bg-white p-5 text-center text-sm text-slate-500">Belum ada dokumen pada registrasi ini.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
@can('check-access',['cats','update'])
|
||||
<form action="{{ route('cats.registrations.documents.store',[$cat,$registration]) }}" method="POST" enctype="multipart/form-data" class="grid gap-3 md:grid-cols-[1fr_180px_180px_auto]">@csrf<input type="file" name="document" required class="field-input rounded-xl py-2"><select name="document_type" class="field-input rounded-xl"><option value="pedigree">Pedigree</option><option value="registration">Registrasi</option><option value="ownership">Kepemilikan</option><option value="title">Gelar</option><option value="other">Lainnya</option></select><input name="label" class="field-input rounded-xl" placeholder="Nama dokumen"><button class="rounded-xl bg-slate-800 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-900">Tambah dokumen</button></form>
|
||||
@endcan
|
||||
</div>
|
||||
</section>
|
||||
@empty
|
||||
<div class="border border-dashed border-slate-300 bg-white p-10 text-center"><i class="fas fa-file-lines mb-3 text-3xl text-slate-300"></i><p class="font-medium text-slate-700">Belum ada registrasi pedigree</p><p class="mt-1 text-sm text-slate-500">Tambahkan asosiasi dari formulir di sebelah kanan.</p></div>
|
||||
<div class="rounded-2xl border border-dashed border-slate-300 bg-white p-10 text-center"><i class="fas fa-file-lines mb-3 text-3xl text-slate-300"></i><p class="font-medium text-slate-700">Belum ada registrasi pedigree</p><p class="mt-1 text-sm text-slate-500">Tambahkan asosiasi dari formulir di sebelah kanan.</p></div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<aside class="h-fit border border-slate-200 bg-white p-5" x-data="{custom:false}">
|
||||
@can('check-access',['cats','update'])
|
||||
<aside class="h-fit rounded-2xl border border-slate-200 bg-white p-5" x-data="{custom:false}">
|
||||
<h2 class="font-semibold text-slate-900">Tambah registrasi</h2><p class="mt-1 text-sm text-slate-500">Satu asosiasi dapat ditambahkan satu kali untuk setiap kucing.</p>
|
||||
<form action="{{ route('cats.registrations.store',$cat) }}" method="POST" enctype="multipart/form-data" class="mt-5 space-y-4">@csrf
|
||||
<div><label class="field-label">Asosiasi</label><select name="association_id" required class="field-input" @change="custom=$event.target.value==='custom'"><option value="">Pilih asosiasi</option>@foreach($associations as $association)<option value="{{ $association->id }}">{{ $association->code }} - {{ $association->name }}</option>@endforeach<option value="custom">Asosiasi lain…</option></select></div>
|
||||
<template x-if="custom"><div class="space-y-3"><div><label class="field-label">Kode asosiasi</label><input name="association_code" class="field-input" placeholder="Contoh: TICA"></div><div><label class="field-label">Nama asosiasi</label><input name="association_name" class="field-input"></div></div></template>
|
||||
<div><label class="field-label">Nomor registrasi</label><input name="registration_number" class="field-input"></div>
|
||||
<div><label class="field-label">Nomor pedigree</label><input name="pedigree_number" class="field-input"></div>
|
||||
<div><label class="field-label">Nomor sertifikat</label><input name="certificate_number" class="field-input"></div>
|
||||
<div class="grid grid-cols-2 gap-3"><div><label class="field-label">Tanggal terbit</label><input type="date" name="issue_date" class="field-input"></div><div><label class="field-label">Gelar</label><input name="title" class="field-input"></div></div>
|
||||
<div><label class="field-label">File sertifikat</label><input type="file" name="certificate_file" accept=".pdf,.jpg,.jpeg,.png" class="field-input py-2"></div>
|
||||
<div><label class="field-label">Asosiasi</label><select name="association_id" required class="field-input rounded-xl" @change="custom=$event.target.value==='custom'"><option value="">Pilih asosiasi</option>@foreach($associations as $association)<option value="{{ $association->id }}">{{ $association->code }} - {{ $association->name }}</option>@endforeach<option value="custom">Asosiasi lain…</option></select></div>
|
||||
<template x-if="custom"><div class="space-y-3"><div><label class="field-label">Kode asosiasi</label><input name="association_code" class="field-input rounded-xl" placeholder="Contoh: TICA"></div><div><label class="field-label">Nama asosiasi</label><input name="association_name" class="field-input rounded-xl"></div></div></template>
|
||||
<div><label class="field-label">Nomor registrasi</label><input name="registration_number" class="field-input rounded-xl"></div>
|
||||
<div><label class="field-label">Nomor pedigree</label><input name="pedigree_number" class="field-input rounded-xl"></div>
|
||||
<div><label class="field-label">Nomor sertifikat</label><input name="certificate_number" class="field-input rounded-xl"></div>
|
||||
<div class="grid grid-cols-2 gap-3"><div><label class="field-label">Tanggal terbit</label><input type="date" name="issue_date" class="field-input rounded-xl"></div><div><label class="field-label">Gelar</label><input name="title" class="field-input rounded-xl"></div></div>
|
||||
<div><label class="field-label">File sertifikat</label><input type="file" name="certificate_file" accept=".pdf,.jpg,.jpeg,.png" class="field-input rounded-xl py-2"></div>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700"><input type="hidden" name="is_primary" value="0"><input type="checkbox" name="is_primary" value="1" class="rounded border-slate-300 text-indigo-600">Jadikan pedigree utama</label>
|
||||
<button class="w-full bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700">Simpan registrasi</button>
|
||||
<button class="w-full rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700">Simpan registrasi</button>
|
||||
</form>
|
||||
</aside>
|
||||
@endcan
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -367,7 +367,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const ctx = document.getElementById('weightChart');
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
@foreach([
|
||||
['Total kucing',$stats['total_cats'] ?? 0,'fa-cat','cats.index'],
|
||||
['Total kucing aktif',$stats['total_cats'] ?? 0,'fa-cat','cats.index'],
|
||||
['Dalam perawatan',$stats['sick_cats'] ?? 0,'fa-heart-pulse','cats.index'],
|
||||
['Pembiakan aktif',$stats['total_mating'] ?? 0,'fa-venus-mars','matting.index'],
|
||||
['Anakan tercatat',$stats['total_kittens'] ?? 0,'fa-paw','matting.index']
|
||||
['Masa kehamilan',$stats['total_mating'] ?? 0,'fa-venus-mars','matting.index'],
|
||||
['Total anakan',$stats['total_kittens'] ?? 0,'fa-paw','matting.index']
|
||||
] as $i=>$card)
|
||||
<a href="{{ $i===1 ? route($card[3],['filter_health'=>'treatment']) : route($card[3]) }}" class="border border-slate-200 bg-white p-5 hover:border-slate-300">
|
||||
<div class="flex items-start justify-between">
|
||||
@@ -301,6 +301,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-slate-100 px-5 pb-5 pt-5">
|
||||
<div class="mb-4 flex flex-col gap-1 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div><h3 class="text-base font-bold text-slate-900">Perubahan berat yang perlu diperhatikan</h3><p class="mt-0.5 text-xs text-slate-500">Kenaikan dan penurunan berdasarkan dua pencatatan terakhir dalam periode {{ $weightPeriodLabel ?? '-' }}.</p></div>
|
||||
</div>
|
||||
<div class="grid gap-4 xl:grid-cols-3">
|
||||
@foreach(['Kitten','Junior','Dewasa'] as $category)
|
||||
@php $movement = data_get($weightMovements ?? [], $category, ['gainers'=>collect(),'losers'=>collect()]); @endphp
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50/40 p-4">
|
||||
<h4 class="mb-3 font-bold text-slate-800">{{ $category }}</h4>
|
||||
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-1 2xl:grid-cols-2">
|
||||
<div class="rounded-xl border border-emerald-100 bg-emerald-50/70 p-3"><p class="mb-2 text-xs font-bold text-emerald-700"><i class="fas fa-arrow-trend-up mr-1"></i>Kenaikan</p><div class="space-y-2">@forelse($movement['gainers'] as $row)<div class="flex items-center justify-between gap-3 text-xs"><a href="{{ route('cats.show',$row['cat_id']) }}" class="truncate font-semibold text-slate-700 hover:text-indigo-600">{{ $row['name'] }}</a><span class="shrink-0 font-bold text-emerald-700">+{{ number_format($row['change'],2,',','.') }} kg</span></div>@empty<p class="text-xs text-slate-400">Tidak ada kenaikan tercatat.</p>@endforelse</div></div>
|
||||
<div class="rounded-xl border border-rose-100 bg-rose-50/70 p-3"><p class="mb-2 text-xs font-bold text-rose-700"><i class="fas fa-arrow-trend-down mr-1"></i>Penurunan</p><div class="space-y-2">@forelse($movement['losers'] as $row)<div class="flex items-center justify-between gap-3 text-xs"><a href="{{ route('cats.show',$row['cat_id']) }}" class="truncate font-semibold text-slate-700 hover:text-indigo-600">{{ $row['name'] }}</a><span class="shrink-0 font-bold text-rose-700">{{ number_format($row['change'],2,',','.') }} kg</span></div>@empty<p class="text-xs text-slate-400">Tidak ada penurunan tercatat.</p>@endforelse</div></div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="mt-6 grid gap-6 xl:grid-cols-3">
|
||||
@@ -378,171 +396,77 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const formatKg = (value) => `${Number(value).toFixed(2).replace('.', ',')} kg`;
|
||||
const formatMonth = (month) => {
|
||||
const d = new Date(`${month}-01T00:00:00`);
|
||||
return Number.isNaN(d.getTime()) ? month : d.toLocaleDateString('id-ID', { month: 'short', year: '2-digit' });
|
||||
};
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const formatKg = (value) => `${Number(value).toFixed(2).replace('.', ',')} kg`;
|
||||
const formatMonth = (month) => {
|
||||
const d = new Date(`${month}-01T00:00:00`);
|
||||
return Number.isNaN(d.getTime()) ? month : d.toLocaleDateString('id-ID', { month: 'short', year: '2-digit' });
|
||||
};
|
||||
|
||||
if (typeof Chart !== 'undefined') {
|
||||
Chart.defaults.font.family = "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
||||
Chart.defaults.color = '#64748b';
|
||||
function renderLineChart(canvas, labels, datasets) {
|
||||
if (!canvas || !labels.length || !datasets.length) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const parent = canvas.parentElement;
|
||||
const cssWidth = Math.max(320, parent.clientWidth);
|
||||
const cssHeight = Math.max(250, parent.clientHeight);
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = cssWidth * dpr; canvas.height = cssHeight * dpr;
|
||||
canvas.style.width = cssWidth + 'px'; canvas.style.height = cssHeight + 'px';
|
||||
ctx.setTransform(dpr,0,0,dpr,0,0);
|
||||
ctx.clearRect(0,0,cssWidth,cssHeight);
|
||||
|
||||
const categoryColors = {
|
||||
Kitten: '#2563eb',
|
||||
Junior: '#db2777',
|
||||
Dewasa: '#059669'
|
||||
};
|
||||
const catPalette = ['#d97706', '#7c3aed', '#0891b2', '#2563eb', '#059669', '#be123c'];
|
||||
const pad = {left:48,right:16,top:12,bottom:58};
|
||||
const w = cssWidth-pad.left-pad.right, h = cssHeight-pad.top-pad.bottom;
|
||||
const values = datasets.flatMap(d => d.data.filter(v => v !== null && v !== undefined).map(Number));
|
||||
if (!values.length) return;
|
||||
const maxY = Math.max(1, Math.ceil(Math.max(...values)*1.15*2)/2);
|
||||
const minY = 0;
|
||||
const xAt = i => pad.left + (labels.length<=1 ? w/2 : (i/(labels.length-1))*w);
|
||||
const yAt = v => pad.top + h - ((Number(v)-minY)/(maxY-minY))*h;
|
||||
|
||||
const avgData = @json($categoryAverageTrend ?? ['labels'=>[], 'series'=>[]]);
|
||||
const avgCanvas = document.getElementById('weightCategoryAverageChart');
|
||||
if (avgCanvas && avgData.labels && avgData.labels.length) {
|
||||
new Chart(avgCanvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: avgData.labels.map(formatMonth),
|
||||
datasets: (avgData.series || []).map((series) => ({
|
||||
label: series.name,
|
||||
data: series.data,
|
||||
borderColor: categoryColors[series.name] || '#64748b',
|
||||
backgroundColor: categoryColors[series.name] || '#64748b',
|
||||
borderWidth: 2.5,
|
||||
pointRadius: 3,
|
||||
pointHoverRadius: 5,
|
||||
pointBorderWidth: 0,
|
||||
tension: 0.28,
|
||||
spanGaps: true
|
||||
}))
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 18, font: { size: 11, weight: '600' } }
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: '#0f172a',
|
||||
padding: 10,
|
||||
cornerRadius: 10,
|
||||
callbacks: { label: (ctx) => `${ctx.dataset.label}: ${formatKg(ctx.parsed.y)}` }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: { grid: { display: false }, border: { display: false }, ticks: { maxRotation: 0, autoSkip: true, maxTicksLimit: 8 } },
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: { color: '#e2e8f0' },
|
||||
border: { display: false },
|
||||
title: { display: true, text: 'Berat (kg)', color: '#64748b', font: { size: 11, weight: '600' } }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const trendLabels = @json($weightTrendLabels ?? []);
|
||||
const trendSeries = @json($weightTrendSeries ?? []);
|
||||
const trendCanvas = document.getElementById('weightSelectedCategoryChart');
|
||||
if (trendCanvas && trendLabels.length && trendSeries.length) {
|
||||
new Chart(trendCanvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: trendLabels.map(formatMonth),
|
||||
datasets: trendSeries.map((series, index) => ({
|
||||
label: series.name,
|
||||
data: series.data,
|
||||
borderColor: catPalette[index % catPalette.length],
|
||||
backgroundColor: catPalette[index % catPalette.length],
|
||||
borderWidth: 2.2,
|
||||
pointRadius: 3,
|
||||
pointHoverRadius: 5,
|
||||
pointBorderWidth: 0,
|
||||
tension: 0.28,
|
||||
spanGaps: true
|
||||
}))
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'nearest', intersect: false },
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 15, font: { size: 10, weight: '600' } }
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: '#0f172a',
|
||||
padding: 10,
|
||||
cornerRadius: 10,
|
||||
callbacks: { label: (ctx) => `${ctx.dataset.label}: ${formatKg(ctx.parsed.y)}` }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: { grid: { display: false }, border: { display: false }, ticks: { maxRotation: 0, autoSkip: true, maxTicksLimit: 8 } },
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: { color: '#e2e8f0' },
|
||||
border: { display: false },
|
||||
title: { display: true, text: 'Berat (kg)', color: '#64748b', font: { size: 11, weight: '600' } }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ctx.font='11px system-ui, -apple-system, Segoe UI, sans-serif'; ctx.textBaseline='middle';
|
||||
for(let i=0;i<=4;i++){
|
||||
const v=maxY*(i/4), y=yAt(v);
|
||||
ctx.strokeStyle='#e2e8f0'; ctx.lineWidth=1; ctx.beginPath(); ctx.moveTo(pad.left,y); ctx.lineTo(pad.left+w,y); ctx.stroke();
|
||||
ctx.fillStyle='#64748b'; ctx.textAlign='right'; ctx.fillText(v.toFixed(v<1?1:1),pad.left-8,y);
|
||||
}
|
||||
const step=Math.max(1,Math.ceil(labels.length/6));
|
||||
labels.forEach((lab,i)=>{ if(i%step!==0 && i!==labels.length-1) return; ctx.fillStyle='#64748b'; ctx.textAlign='center'; ctx.fillText(formatMonth(lab),xAt(i),pad.top+h+20); });
|
||||
|
||||
// Pencarian + pagination tabel berat terbaru (client-side, tanpa request tambahan).
|
||||
const searchInput = document.getElementById('weightTableSearch');
|
||||
const rowsPerPageSelect = document.getElementById('weightRowsPerPage');
|
||||
const tableRows = Array.from(document.querySelectorAll('[data-weight-row]'));
|
||||
const prevButton = document.getElementById('weightPrevPage');
|
||||
const nextButton = document.getElementById('weightNextPage');
|
||||
const pageLabel = document.getElementById('weightPageLabel');
|
||||
const tableInfo = document.getElementById('weightTableInfo');
|
||||
let currentPage = 1;
|
||||
datasets.forEach(ds=>{
|
||||
ctx.strokeStyle=ds.color; ctx.fillStyle=ds.color; ctx.lineWidth=2.4; ctx.lineJoin='round'; ctx.lineCap='round';
|
||||
ctx.beginPath(); let started=false;
|
||||
ds.data.forEach((v,i)=>{ if(v===null||v===undefined){started=false;return;} const x=xAt(i),y=yAt(v); if(!started){ctx.moveTo(x,y);started=true;} else ctx.lineTo(x,y); });
|
||||
ctx.stroke();
|
||||
ds.data.forEach((v,i)=>{ if(v===null||v===undefined)return; const x=xAt(i),y=yAt(v); ctx.beginPath();ctx.arc(x,y,3,0,Math.PI*2);ctx.fill(); });
|
||||
});
|
||||
|
||||
const renderWeightTable = () => {
|
||||
if (!tableRows.length) {
|
||||
if (tableInfo) tableInfo.textContent = '0 data';
|
||||
if (pageLabel) pageLabel.textContent = '';
|
||||
if (prevButton) prevButton.disabled = true;
|
||||
if (nextButton) nextButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
let lx=pad.left, ly=cssHeight-16;
|
||||
datasets.forEach(ds=>{ const text=ds.label; const tw=ctx.measureText(text).width+28; if(lx+tw>cssWidth-pad.right){ly-=18;lx=pad.left;} ctx.fillStyle=ds.color;ctx.beginPath();ctx.arc(lx+4,ly,4,0,Math.PI*2);ctx.fill();ctx.fillStyle='#475569';ctx.textAlign='left';ctx.fillText(text,lx+14,ly);lx+=tw; });
|
||||
}
|
||||
|
||||
const search = (searchInput?.value || '').trim().toLowerCase();
|
||||
const pageSize = Number(rowsPerPageSelect?.value || 10);
|
||||
const filtered = tableRows.filter((row) => (row.dataset.name || '').includes(search));
|
||||
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
|
||||
currentPage = Math.min(currentPage, totalPages);
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const visible = new Set(filtered.slice(start, start + pageSize));
|
||||
const categoryColors={Kitten:'#2563eb',Junior:'#db2777',Dewasa:'#059669'};
|
||||
const catPalette=['#d97706','#7c3aed','#0891b2','#2563eb','#059669','#be123c'];
|
||||
const avgData=@json($categoryAverageTrend ?? ['labels'=>[], 'series'=>[]]);
|
||||
const trendLabels=@json($weightTrendLabels ?? []);
|
||||
const trendSeries=@json($weightTrendSeries ?? []);
|
||||
const avgCanvas=document.getElementById('weightCategoryAverageChart');
|
||||
const trendCanvas=document.getElementById('weightSelectedCategoryChart');
|
||||
const draw=()=>{
|
||||
renderLineChart(avgCanvas, avgData.labels||[], (avgData.series||[]).map(s=>({label:s.name,data:s.data,color:categoryColors[s.name]||'#64748b'})));
|
||||
renderLineChart(trendCanvas, trendLabels||[], (trendSeries||[]).map((s,i)=>({label:s.name,data:s.data,color:catPalette[i%catPalette.length]})));
|
||||
};
|
||||
draw(); let resizeTimer; window.addEventListener('resize',()=>{clearTimeout(resizeTimer);resizeTimer=setTimeout(draw,120);});
|
||||
|
||||
tableRows.forEach((row) => row.classList.toggle('hidden', !visible.has(row)));
|
||||
|
||||
if (tableInfo) {
|
||||
const from = filtered.length ? start + 1 : 0;
|
||||
const to = Math.min(start + pageSize, filtered.length);
|
||||
tableInfo.textContent = `Menampilkan ${from}-${to} dari ${filtered.length} data`;
|
||||
}
|
||||
if (pageLabel) pageLabel.textContent = `Hal. ${currentPage} / ${totalPages}`;
|
||||
if (prevButton) prevButton.disabled = currentPage <= 1;
|
||||
if (nextButton) nextButton.disabled = currentPage >= totalPages;
|
||||
};
|
||||
|
||||
searchInput?.addEventListener('input', () => { currentPage = 1; renderWeightTable(); });
|
||||
rowsPerPageSelect?.addEventListener('change', () => { currentPage = 1; renderWeightTable(); });
|
||||
prevButton?.addEventListener('click', () => { if (currentPage > 1) { currentPage--; renderWeightTable(); } });
|
||||
nextButton?.addEventListener('click', () => { currentPage++; renderWeightTable(); });
|
||||
renderWeightTable();
|
||||
});
|
||||
const searchInput=document.getElementById('weightTableSearch');
|
||||
const rowsPerPageSelect=document.getElementById('weightRowsPerPage');
|
||||
const tableRows=Array.from(document.querySelectorAll('[data-weight-row]'));
|
||||
const prevButton=document.getElementById('weightPrevPage'); const nextButton=document.getElementById('weightNextPage');
|
||||
const pageLabel=document.getElementById('weightPageLabel'); const tableInfo=document.getElementById('weightTableInfo'); let currentPage=1;
|
||||
const renderTable=()=>{ const search=(searchInput?.value||'').trim().toLowerCase(); const size=Number(rowsPerPageSelect?.value||10); const filtered=tableRows.filter(r=>(r.dataset.name||'').includes(search)); const pages=Math.max(1,Math.ceil(filtered.length/size)); currentPage=Math.min(currentPage,pages); const start=(currentPage-1)*size; const visible=new Set(filtered.slice(start,start+size)); tableRows.forEach(r=>r.classList.toggle('hidden',!visible.has(r))); if(tableInfo)tableInfo.textContent=`Menampilkan ${filtered.length?start+1:0}-${Math.min(start+size,filtered.length)} dari ${filtered.length} data`; if(pageLabel)pageLabel.textContent=`Hal. ${currentPage} / ${pages}`; if(prevButton)prevButton.disabled=currentPage<=1; if(nextButton)nextButton.disabled=currentPage>=pages; };
|
||||
searchInput?.addEventListener('input',()=>{currentPage=1;renderTable();}); rowsPerPageSelect?.addEventListener('change',()=>{currentPage=1;renderTable();}); prevButton?.addEventListener('click',()=>{if(currentPage>1){currentPage--;renderTable();}}); nextButton?.addEventListener('click',()=>{currentPage++;renderTable();}); renderTable();
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const ctx = document.getElementById('medicalTrendChart');
|
||||
|
||||
@@ -193,7 +193,6 @@
|
||||
|
||||
{{-- SCRIPTS SECTION --}}
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
function toggleCreateForm() {
|
||||
const form = document.getElementById('create-form');
|
||||
|
||||
@@ -196,7 +196,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
function confirmDeleteObat(id, name) {
|
||||
Swal.fire({
|
||||
|
||||
@@ -228,7 +228,6 @@
|
||||
|
||||
{{-- SCRIPTS SECTION --}}
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<!-- <script>
|
||||
function updateFileName(input) {
|
||||
|
||||
@@ -107,7 +107,8 @@
|
||||
<div>
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-slate-400">Status Pembiakan</label>
|
||||
<select name="status" class="w-full mt-2 bg-white/10 border-white/10 rounded-xl text-xs font-bold focus:ring-indigo-500 text-white" required>
|
||||
<option value="proses" class="text-slate-800">Dalam proses</option>
|
||||
<option value="proses" class="text-slate-800">Menunggu konfirmasi</option>
|
||||
<option value="hamil" class="text-slate-800">Hamil / menunggu lahir</option>
|
||||
<option value="berhasil" class="text-slate-800">Berhasil (Lahir)</option>
|
||||
<option value="gagal" class="text-slate-800">Gagal</option>
|
||||
</select>
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
<div>
|
||||
<label class="text-[9px] font-black uppercase text-slate-400 mb-1 block">Status</label>
|
||||
<select name="status" class="w-full bg-white/10 border-white/10 rounded-xl py-3 text-xs font-black text-white">
|
||||
<option value="proses" class="text-slate-800" {{ $matting->status == 'proses' ? 'selected' : '' }}>DALAM PROSES</option>
|
||||
<option value="proses" class="text-slate-800" {{ $matting->status == 'proses' ? 'selected' : '' }}>MENUNGGU KONFIRMASI</option>
|
||||
<option value="hamil" class="text-slate-800" {{ $matting->status == 'hamil' ? 'selected' : '' }}>HAMIL / MENUNGGU LAHIR</option>
|
||||
<option value="berhasil" class="text-slate-800" {{ $matting->status == 'berhasil' ? 'selected' : '' }}>BERHASIL</option>
|
||||
<option value="gagal" class="text-slate-800" {{ $matting->status == 'gagal' ? 'selected' : '' }}>GAGAL</option>
|
||||
</select>
|
||||
@@ -264,12 +265,12 @@
|
||||
jenis_kelamin: 'Jantan',
|
||||
tgl_lahir: defaultDate,
|
||||
kode_ems: '',
|
||||
registry: [], // Inisialisasi array kosong untuk record baru
|
||||
registry: [], // Inisialisasi array kosong untuk data baru
|
||||
is_pedigree: false
|
||||
});
|
||||
},
|
||||
removeKitten(index) {
|
||||
if(confirm('Hapus record anak ini? Profil di Manajemen Pasien juga akan terhapus setelah disimpan.')) {
|
||||
if(confirm('Hapus data anak ini? Profil di Manajemen Pasien juga akan terhapus setelah disimpan.')) {
|
||||
this.kittens.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,48 @@
|
||||
<x-app-layout>
|
||||
<div class="mb-6 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between"><div><h1 class="text-2xl font-bold text-slate-900">Perkawinan & pembiakan</h1><p class="mt-1 text-sm text-slate-500">Riwayat pasangan induk, periode kawin, perkiraan kelahiran, dan data anakan.</p></div>@can('check-access',['matting','create'])<a href="{{ route('matting.create') }}" class="bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700"><i class="fas fa-plus mr-2"></i>Catat perkawinan</a>@endcan</div>
|
||||
<div class="mb-4 border border-slate-200 bg-white p-4"><p class="text-xs text-slate-500">Total riwayat pembiakan</p><p class="mt-1 text-2xl font-bold text-slate-900">{{ $mattings->total() }}</p></div>
|
||||
<section class="border border-slate-200 bg-white"><div class="overflow-x-auto"><table class="min-w-full divide-y divide-slate-200 text-sm"><thead class="bg-slate-50 text-left text-xs font-semibold text-slate-500"><tr><th class="px-4 py-3">Pasangan induk</th><th class="px-4 py-3">Periode kawin</th><th class="px-4 py-3">Perkiraan lahir</th><th class="px-4 py-3">Status & anakan</th><th class="px-4 py-3 text-right">Aksi</th></tr></thead><tbody class="divide-y divide-slate-100">
|
||||
<div class="mb-6 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div><h1 class="text-2xl font-bold text-slate-900">Perkawinan & pembiakan</h1><p class="mt-1 text-sm text-slate-500">Alur: catat kawin → konfirmasi hamil → pantau HPL → catat kelahiran/anakan.</p></div>
|
||||
@can('check-access',['matting','create'])<a href="{{ route('matting.create') }}" class="rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700"><i class="fas fa-plus mr-2"></i>Catat perkawinan</a>@endcan
|
||||
</div>
|
||||
|
||||
<div class="mb-5 grid gap-3 sm:grid-cols-4">
|
||||
@php
|
||||
$all=$mattings->getCollection();
|
||||
$countProses=$all->where('status','proses')->count();
|
||||
$countHamil=$all->where('status','hamil')->count();
|
||||
$countLahir=$all->where('status','berhasil')->count();
|
||||
$countGagal=$all->where('status','gagal')->count();
|
||||
@endphp
|
||||
<div class="rounded-2xl border border-blue-100 bg-blue-50/60 p-4"><p class="text-xs font-semibold text-blue-700">Menunggu konfirmasi</p><p class="mt-1 text-2xl font-bold text-blue-900">{{ $countProses }}</p></div>
|
||||
<div class="rounded-2xl border border-amber-100 bg-amber-50/60 p-4"><p class="text-xs font-semibold text-amber-700">Hamil / menunggu lahir</p><p class="mt-1 text-2xl font-bold text-amber-900">{{ $countHamil }}</p></div>
|
||||
<div class="rounded-2xl border border-emerald-100 bg-emerald-50/60 p-4"><p class="text-xs font-semibold text-emerald-700">Sudah lahir</p><p class="mt-1 text-2xl font-bold text-emerald-900">{{ $countLahir }}</p></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-4"><p class="text-xs font-semibold text-slate-500">Tidak berhasil</p><p class="mt-1 text-2xl font-bold text-slate-800">{{ $countGagal }}</p></div>
|
||||
</div>
|
||||
|
||||
<section class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
<div class="overflow-x-auto"><table class="min-w-full divide-y divide-slate-200 text-sm"><thead class="bg-slate-50 text-left text-xs font-semibold text-slate-500"><tr><th class="px-4 py-3">Pasangan induk</th><th class="px-4 py-3">Periode kawin</th><th class="px-4 py-3">HPL</th><th class="px-4 py-3">Tahap</th><th class="px-4 py-3">Anakan</th><th class="px-4 py-3 text-right">Aksi</th></tr></thead><tbody class="divide-y divide-slate-100">
|
||||
@forelse($mattings as $m)
|
||||
@php $edd=$m->tgl_kawin_awal?->copy()->addDays(63); $days=$edd ? (int)now()->startOfDay()->diffInDays($edd->copy()->startOfDay(),false) : null; @endphp
|
||||
<tr class="hover:bg-slate-50"><td class="px-4 py-4"><div class="flex items-center gap-3"><div class="flex -space-x-2">@foreach([$m->sire,$m->dam] as $parent)<img src="{{ $parent?->foto_url ?? asset('images/default-cat.png') }}" class="h-10 w-10 rounded-full border-2 border-white object-cover" alt="">@endforeach</div><div><p class="font-semibold text-slate-900">{{ $m->sire->nama_kucing ?? '-' }} × {{ $m->dam->nama_kucing ?? '-' }}</p><p class="mt-1 text-xs text-slate-500">EMS {{ $m->sire->kode_ems ?? '-' }} × {{ $m->dam->kode_ems ?? '-' }}</p></div></div></td>
|
||||
<td class="px-4 py-4"><p class="text-slate-700">{{ $m->tgl_kawin_awal?->format('d-m-Y') ?? '-' }}{{ $m->tgl_kawin_akhir ? ' s.d. '.$m->tgl_kawin_akhir->format('d-m-Y') : '' }}</p>@if($m->tgl_kawin_awal && $m->tgl_kawin_akhir)<p class="mt-1 text-xs text-slate-500">{{ $m->tgl_kawin_awal->diffInDays($m->tgl_kawin_akhir)+1 }} hari</p>@endif</td>
|
||||
<td class="px-4 py-4">@if($edd)<p class="font-medium text-slate-800">{{ $edd->format('d-m-Y') }}</p><p class="mt-1 text-xs {{ $days!==null && $days<=7 && $days>=0?'text-rose-600':'text-slate-500' }}">{{ $days<0?'Lewat '.abs($days).' hari':($days===0?'Perkiraan hari ini':$days.' hari lagi') }}</p>@else<span class="text-slate-400">-</span>@endif</td>
|
||||
<td class="px-4 py-4"><p class="font-medium {{ $m->status==='berhasil'?'text-emerald-700':($m->status==='gagal'?'text-rose-600':'text-indigo-700') }}">{{ $m->status==='proses'?'Dalam proses':ucfirst($m->status) }}</p><p class="mt-1 text-xs text-slate-500">{{ $m->kittens_count }} anakan tercatat</p></td>
|
||||
<td class="px-4 py-4"><div class="flex justify-end gap-1">@can('check-access',['matting','update'])<a href="{{ route('matting.edit',$m) }}" class="p-2 text-slate-500 hover:bg-slate-100" title="Ubah"><i class="fas fa-pen"></i></a>@endcan @can('check-access',['matting','delete'])<form method="POST" action="{{ route('matting.destroy',$m) }}" id="delete-matting-{{ $m->id }}">@csrf @method('DELETE')<button type="button" onclick="confirmDelete('delete-matting-{{ $m->id }}','{{ addslashes(($m->sire->nama_kucing ?? '').' × '.($m->dam->nama_kucing ?? '')) }}')" class="p-2 text-rose-500 hover:bg-rose-50"><i class="fas fa-trash"></i></button></form>@endcan</div></td></tr>
|
||||
@empty<tr><td colspan="5" class="px-4 py-12 text-center text-sm text-slate-500">Belum ada riwayat pembiakan.</td></tr>@endforelse
|
||||
</tbody></table></div><div class="border-t border-slate-200 p-4">{{ $mattings->links() }}</div></section>
|
||||
@php
|
||||
$edd=$m->tgl_kawin_awal?->copy()->addDays(63); $days=$edd ? (int)now()->startOfDay()->diffInDays($edd->copy()->startOfDay(),false) : null;
|
||||
$flowLabel=match($m->status){'hamil'=>'Hamil / menunggu lahir','berhasil'=>'Sudah lahir','gagal'=>'Tidak berhasil',default=>'Menunggu konfirmasi'};
|
||||
$flowStyle=match($m->status){'hamil'=>'bg-amber-50 text-amber-700 border-amber-100','berhasil'=>'bg-emerald-50 text-emerald-700 border-emerald-100','gagal'=>'bg-rose-50 text-rose-700 border-rose-100',default=>'bg-blue-50 text-blue-700 border-blue-100'};
|
||||
@endphp
|
||||
<tr class="align-top hover:bg-slate-50">
|
||||
<td class="px-4 py-4"><div class="flex items-center gap-3"><div class="flex -space-x-2">@foreach([$m->sire,$m->dam] as $parent)<img src="{{ $parent?->foto_url ?? asset('images/default-cat.png') }}" class="h-10 w-10 rounded-full border-2 border-white object-cover" alt="">@endforeach</div><div><p class="font-semibold text-slate-900">{{ $m->sire->nama_kucing ?? '-' }} × {{ $m->dam->nama_kucing ?? '-' }}</p><p class="mt-1 text-xs text-slate-500">EMS {{ $m->sire->kode_ems ?? '-' }} × {{ $m->dam->kode_ems ?? '-' }}</p></div></div></td>
|
||||
<td class="px-4 py-4"><p class="text-slate-700">{{ $m->tgl_kawin_awal?->format('d-m-Y') ?? '-' }}{{ $m->tgl_kawin_akhir ? ' s.d. '.$m->tgl_kawin_akhir->format('d-m-Y') : '' }}</p>@if($m->tgl_kawin_awal && $m->tgl_kawin_akhir)<p class="mt-1 text-xs text-slate-500">{{ $m->tgl_kawin_awal->diffInDays($m->tgl_kawin_akhir)+1 }} hari</p>@endif</td>
|
||||
<td class="px-4 py-4">@if($edd)<p class="font-semibold text-slate-800">{{ $edd->format('d-m-Y') }}</p>@if(in_array($m->status,['proses','hamil']))<p class="mt-1 text-xs {{ $days!==null && $days<=7 && $days>=0?'font-semibold text-rose-600':'text-slate-500' }}">{{ $days<0?'Lewat '.abs($days).' hari':($days===0?'HPL hari ini':$days.' hari lagi') }}</p>@endif @else<span class="text-slate-400">-</span>@endif</td>
|
||||
<td class="px-4 py-4"><span class="inline-flex rounded-full border px-2.5 py-1 text-xs font-semibold {{ $flowStyle }}">{{ $flowLabel }}</span>
|
||||
@can('check-access',['matting','update'])
|
||||
<div class="mt-2 flex flex-wrap gap-1">
|
||||
@if($m->status==='proses')<form method="POST" action="{{ route('matting.status',$m) }}">@csrf @method('PATCH')<input type="hidden" name="status" value="hamil"><button class="rounded-lg bg-amber-100 px-2 py-1 text-[11px] font-semibold text-amber-800">Konfirmasi hamil</button></form>@endif
|
||||
@if(in_array($m->status,['proses','hamil']))<form method="POST" action="{{ route('matting.status',$m) }}">@csrf @method('PATCH')<input type="hidden" name="status" value="berhasil"><button class="rounded-lg bg-emerald-100 px-2 py-1 text-[11px] font-semibold text-emerald-800">Tandai lahir</button></form><form method="POST" action="{{ route('matting.status',$m) }}">@csrf @method('PATCH')<input type="hidden" name="status" value="gagal"><button class="rounded-lg bg-rose-100 px-2 py-1 text-[11px] font-semibold text-rose-800">Tidak berhasil</button></form>@endif
|
||||
</div>
|
||||
@endcan
|
||||
</td>
|
||||
<td class="px-4 py-4"><p class="font-semibold text-slate-800">{{ $m->kittens_count }}</p><p class="text-xs text-slate-500">anakan tercatat</p></td>
|
||||
<td class="px-4 py-4"><div class="flex justify-end gap-1">@can('check-access',['matting','update'])<a href="{{ route('matting.edit',$m) }}" class="rounded-lg p-2 text-slate-500 hover:bg-slate-100" title="Ubah / catat anakan"><i class="fas fa-pen"></i></a>@endcan @can('check-access',['matting','delete'])<form method="POST" action="{{ route('matting.destroy',$m) }}" id="delete-matting-{{ $m->id }}">@csrf @method('DELETE')<button type="button" onclick="confirmDelete('delete-matting-{{ $m->id }}','riwayat pembiakan ini')" class="rounded-lg p-2 text-rose-500 hover:bg-rose-50"><i class="fas fa-trash"></i></button></form>@endcan</div></td>
|
||||
</tr>
|
||||
@empty<tr><td colspan="6" class="px-4 py-12 text-center text-sm text-slate-500">Belum ada riwayat pembiakan.</td></tr>@endforelse
|
||||
</tbody></table></div>
|
||||
<div class="border-t border-slate-200 p-4">{{ $mattings->links() }}</div>
|
||||
</section>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -170,7 +170,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('treatmentForm', () => ({
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Lampiran Foto Bukti</label>
|
||||
<div class="relative group">
|
||||
<div class="w-full h-48 rounded-3xl bg-slate-50 border-2 border-dashed border-slate-200 overflow-hidden shadow-inner flex items-center justify-center">
|
||||
<img id="preview" src="{{ $treatment->foto_bukti ? asset('storage/' . $treatment->foto_bukti) : 'https://ui-avatars.com/api/?name=No+Photo&background=f1f5f9&color=94a3b8' }}" class="w-full h-full object-cover">
|
||||
<img id="preview" src="{{ $treatment->foto_bukti ? asset('storage/' . $treatment->foto_bukti) : asset('images/default-cat.svg') }}" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<input type="file" name="foto_bukti" onchange="previewImage(event)" class="mt-3 w-full text-[10px] font-black text-slate-400 file:mr-3 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-[9px] file:font-black file:bg-indigo-50 file:text-indigo-600 hover:file:bg-indigo-100 cursor-pointer">
|
||||
</div>
|
||||
@@ -161,7 +161,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||||
|
||||
@php
|
||||
// [FIX ERROR SYNTAX BLADE]
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-xl bg-slate-100 overflow-hidden ring-2 ring-white shadow-sm shrink-0">
|
||||
<img src="{{ $treatment->cat->foto ? asset('storage/'.$treatment->cat->foto) : 'https://ui-avatars.com/api/?name='.urlencode($treatment->cat->nama_kucing).'&background=f1f5f9&color=4f46e5' }}" class="w-full h-full object-cover">
|
||||
<img src="{{ $treatment->cat->foto ? asset('storage/'.$treatment->cat->foto) : asset('images/default-cat.svg') }}" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] font-black text-indigo-600 uppercase leading-none mb-1">{{ \Carbon\Carbon::parse($treatment->tanggal)->format('d M Y') }}</p>
|
||||
@@ -189,7 +189,6 @@
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
function confirmDelete(button, name) {
|
||||
Swal.fire({
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
{{-- Info Kucing --}}
|
||||
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-6 shadow-sm text-center">
|
||||
<div class="w-24 h-24 rounded-3xl bg-slate-100 mx-auto overflow-hidden ring-4 ring-indigo-50 shadow-md">
|
||||
<img src="{{ $treatment->cat->foto ? asset('storage/'.$treatment->cat->foto) : 'https://ui-avatars.com/api/?name='.urlencode($treatment->cat->nama_kucing) }}" class="w-full h-full object-cover">
|
||||
<img src="{{ $treatment->cat->foto ? asset('storage/'.$treatment->cat->foto) : asset('images/default-cat.svg') }}" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<h3 class="mt-4 text-xl font-black text-slate-800 uppercase tracking-tight">{{ $treatment->cat->nama_kucing }}</h3>
|
||||
<p class="text-[10px] font-bold text-slate-400 uppercase">{{ $treatment->cat->ras }} • {{ \Carbon\Carbon::parse($treatment->tanggal)->format('d F Y') }}</p>
|
||||
|
||||
@@ -70,5 +70,15 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form action="{{ route('admin.users.password', $user) }}" method="POST" class="rounded-[2rem] border border-slate-200 bg-white p-6 shadow-sm">
|
||||
@csrf @method('PATCH')
|
||||
<div class="mb-4"><h3 class="text-sm font-bold text-slate-900">Ganti kata sandi langsung</h3><p class="mt-1 text-xs text-slate-500">Administrator dapat menetapkan kata sandi baru tanpa mengetahui kata sandi lama.</p></div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div><label class="field-label">Kata sandi baru</label><input type="password" name="password" required class="field-input rounded-xl" autocomplete="new-password"></div>
|
||||
<div><label class="field-label">Konfirmasi kata sandi</label><input type="password" name="password_confirmation" required class="field-input rounded-xl" autocomplete="new-password"></div>
|
||||
</div>
|
||||
<button class="mt-4 rounded-xl bg-slate-800 px-4 py-2.5 text-sm font-semibold text-white hover:bg-slate-900"><i class="fas fa-key mr-2"></i>Ganti kata sandi</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -1,85 +1,38 @@
|
||||
<x-app-layout>
|
||||
<div class="space-y-6 pb-8">
|
||||
|
||||
{{-- SECTION 1: HEADER & TAMBAH USER --}}
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Manajemen User</h2>
|
||||
<p class="text-sm text-slate-500 font-medium italic">Kelola otoritas akses staf cattery.</p>
|
||||
</div>
|
||||
|
||||
{{-- PROTEKSI CREATE: Mengikuti matriks izin modul users --}}
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div><h2 class="text-2xl font-bold text-slate-900">Manajemen pengguna</h2><p class="mt-1 text-sm text-slate-500">Kelola akses, status akun, WhatsApp, dan kata sandi pengguna.</p></div>
|
||||
@can('check-access', ['users', 'create'])
|
||||
<a href="{{ route('admin.users.create') }}" class="inline-flex items-center px-5 py-3 bg-indigo-600 text-white text-[10px] font-black rounded-xl shadow-lg shadow-indigo-100 hover:bg-indigo-700 transition-all active:scale-95">
|
||||
<i class="fas fa-plus mr-2"></i> TAMBAH USER
|
||||
</a>
|
||||
<a href="{{ route('admin.users.create') }}" class="rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-700"><i class="fas fa-user-plus mr-2"></i>Tambah pengguna</a>
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
{{-- SECTION 2: TABEL USER --}}
|
||||
<div class="bg-white border border-slate-200 rounded-[2.5rem] overflow-hidden shadow-sm">
|
||||
<table class="w-full text-left">
|
||||
<thead class="bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b">
|
||||
<tr>
|
||||
<th class="px-6 py-4">Nama & Email</th>
|
||||
<th class="px-6 py-4">WhatsApp</th>
|
||||
<th class="px-6 py-4 text-center">Role</th>
|
||||
<th class="px-6 py-4 text-right">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100 font-medium text-slate-700">
|
||||
@foreach($users as $user)
|
||||
<tr class="hover:bg-slate-50/50 transition-all">
|
||||
{{-- Identitas --}}
|
||||
<td class="px-6 py-4">
|
||||
<div class="font-black text-slate-800">{{ $user->name }}</div>
|
||||
<div class="text-[10px] text-slate-400 font-bold uppercase">{{ $user->email }}</div>
|
||||
</td>
|
||||
|
||||
{{-- Kontak --}}
|
||||
<td class="px-6 py-4">
|
||||
<span class="text-xs font-bold text-slate-500">{{ $user->phone ?? '-' }}</span>
|
||||
</td>
|
||||
|
||||
{{-- Badge Role --}}
|
||||
<td class="px-6 py-4 text-center">
|
||||
<span class="px-3 py-1 rounded-xl text-[10px] font-black uppercase {{ $user->role == 'admin' ? 'bg-indigo-50 text-indigo-600 border border-indigo-100' : 'bg-slate-50 text-slate-500 border border-slate-200' }}">
|
||||
{{ $user->roleModel?->name ?? $user->role ?? '-' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{{-- Aksi Berbasis Izin --}}
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
|
||||
{{-- PROTEKSI EDIT: Mengikuti matriks izin modul users --}}
|
||||
@can('check-access', ['users', 'update'])
|
||||
<a href="{{ route('admin.users.edit', $user->id) }}" class="p-2 text-slate-400 hover:text-amber-600 hover:bg-amber-50 rounded-xl transition-all" title="Edit Profile">
|
||||
<i class="fas fa-edit text-xs"></i>
|
||||
</a>
|
||||
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold text-slate-500"><tr><th class="px-5 py-3">Pengguna</th><th class="px-5 py-3">WhatsApp</th><th class="px-5 py-3">Role</th><th class="px-5 py-3">Status</th><th class="px-5 py-3 text-right">Aksi</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
@forelse($users as $user)
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-5 py-4"><p class="font-semibold text-slate-900">{{ $user->name }}</p><p class="mt-0.5 text-xs text-slate-500">{{ $user->email }}</p></td>
|
||||
<td class="px-5 py-4 text-slate-600">{{ $user->phone ?: '-' }}</td>
|
||||
<td class="px-5 py-4"><span class="rounded-full border border-slate-200 bg-slate-50 px-2.5 py-1 text-xs font-semibold text-slate-600">{{ $user->roleModel?->name ?? $user->role ?? '-' }}</span></td>
|
||||
<td class="px-5 py-4">@if($user->is_active)<span class="rounded-full border border-emerald-100 bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700">Aktif</span>@else<span class="rounded-full border border-rose-100 bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700">Nonaktif</span>@endif</td>
|
||||
<td class="px-5 py-4"><div class="flex justify-end gap-1">
|
||||
@can('check-access',['users','update'])
|
||||
<a href="{{ route('admin.users.edit',$user) }}" class="rounded-lg p-2 text-slate-500 hover:bg-slate-100" title="Ubah"><i class="fas fa-pen"></i></a>
|
||||
<form action="{{ route('admin.users.status',$user) }}" method="POST">@csrf @method('PATCH')<button class="rounded-lg p-2 {{ $user->is_active?'text-rose-500 hover:bg-rose-50':'text-emerald-600 hover:bg-emerald-50' }}" title="{{ $user->is_active?'Nonaktifkan':'Aktifkan' }}" {{ $user->id===auth()->id() && $user->is_active ? 'disabled' : '' }}><i class="fas {{ $user->is_active?'fa-user-slash':'fa-user-check' }}"></i></button></form>
|
||||
@endcan
|
||||
|
||||
{{-- PROTEKSI DELETE: Pengecekan manual 'admin' dihapus agar permission matriks berlaku sepenuhnya --}}
|
||||
@can('check-access', ['users', 'delete'])
|
||||
<form action="{{ route('admin.users.destroy', $user->id) }}" method="POST" onsubmit="return confirm('Hapus user ini? Staf ini tidak akan bisa login lagi.')" class="inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="p-2 text-slate-400 hover:text-rose-600 hover:bg-rose-50 rounded-xl transition-all" title="Hapus User">
|
||||
<i class="fas fa-trash-alt text-xs"></i>
|
||||
</button>
|
||||
</form>
|
||||
@can('check-access',['users','delete'])
|
||||
<form action="{{ route('admin.users.destroy',$user) }}" method="POST" onsubmit="return confirm('Hapus user ini?')">@csrf @method('DELETE')<button class="rounded-lg p-2 text-rose-500 hover:bg-rose-50" title="Hapus"><i class="fas fa-trash"></i></button></form>
|
||||
@endcan
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- PAGINATION --}}
|
||||
<div class="mt-4">
|
||||
{{ $users->links() }}
|
||||
</div></td>
|
||||
</tr>
|
||||
@empty<tr><td colspan="5" class="px-5 py-10 text-center text-slate-500">Belum ada pengguna.</td></tr>@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="border-t border-slate-200 p-4">{{ $users->links() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
</x-app-layout>
|
||||
|
||||
Reference in New Issue
Block a user