Initial commit: Struktur aplikasi ilycats

This commit is contained in:
Dwi F Sukmana
2026-02-08 15:16:35 +07:00
commit 0921901aed
235 changed files with 29016 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+7
View File
@@ -0,0 +1,7 @@
import './bootstrap';
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
+4
View File
@@ -0,0 +1,4 @@
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
@@ -0,0 +1,27 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600">
{{ __('This is a secure area of the application. Please confirm your password before continuing.') }}
</div>
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<!-- Password -->
<div>
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="current-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<div class="flex justify-end mt-4">
<x-primary-button>
{{ __('Confirm') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
@@ -0,0 +1,25 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600">
{{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }}
</div>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
<form method="POST" action="{{ route('password.email') }}">
@csrf
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<x-primary-button>
{{ __('Email Password Reset Link') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
+110
View File
@@ -0,0 +1,110 @@
<x-guest-layout>
{{-- Title Browser Dinamis --}}
<x-slot name="title">Masuk | {{ $setting->app_name ?? config('app.name') }}</x-slot>
<div class="min-h-screen flex bg-white overflow-hidden">
{{-- SISI KIRI: BRANDING (Optimasi Proporsi) --}}
<div class="hidden lg:flex lg:w-[35%] relative bg-slate-900 justify-center items-center p-8">
<div class="absolute inset-0 bg-cover bg-center opacity-30" style="background-image: url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop');"></div>
<div class="absolute inset-0 bg-gradient-to-br from-slate-900 via-indigo-950 to-slate-900 opacity-95"></div>
<div class="relative z-10 text-center w-full max-w-xs">
{{-- Logo: Dibuat lebih proporsional --}}
<div class="inline-flex p-4 mb-6 bg-white/5 backdrop-blur-xl rounded-[2rem] border border-white/10 shadow-2xl">
@if(isset($setting) && isset($setting->app_logo))
<img src="{{ asset('storage/' . $setting->app_logo) }}"
alt="Logo"
class="h-20 w-auto max-w-[150px] object-contain filter drop-shadow-2xl">
@else
<div class="w-20 h-20 bg-indigo-600 rounded-2xl flex items-center justify-center text-3xl font-black text-white italic">I</div>
@endif
</div>
{{-- Nama Aplikasi: Tidak oversized --}}
<h2 class="text-2xl xl:text-3xl font-black text-white tracking-tight mb-2 break-words">
{{ $setting->app_name ?? 'Ily Cattery' }}
</h2>
{{-- Slogan: Lebih ramping --}}
<p class="text-indigo-300 text-[10px] font-bold uppercase tracking-[0.2em] leading-relaxed italic opacity-80">
{{ $setting->app_slogan ?? 'Professional Cattery Management' }}
</p>
</div>
</div>
{{-- SISI KANAN: FORM LOGIN (Dibuat Lebih Center & Proporsional) --}}
<div class="flex-1 flex items-center justify-center p-6 sm:p-12 lg:p-16 bg-slate-50/50">
{{-- max-w-md: Lebar standar form agar tidak terlihat meluber --}}
<div class="w-full max-w-md space-y-8">
<div class="space-y-2 text-center lg:text-left">
{{-- Logo Mobile: Center di layar kecil --}}
<div class="lg:hidden flex justify-center">
@if(isset($setting) && isset($setting->app_logo))
<img src="{{ asset('storage/' . $setting->app_logo) }}" class="h-14 w-auto mb-6">
@endif
</div>
<h3 class="text-2xl lg:text-3xl font-black text-slate-900 tracking-tight">Selamat Datang.</h3>
<p class="text-slate-500 font-medium text-sm lg:text-base">Silakan masuk untuk mengelola cattery Anda.</p>
</div>
<x-auth-session-status class="mb-4" :status="session('status')" />
<form method="POST" action="{{ route('login') }}" class="space-y-6">
@csrf
{{-- Email Input --}}
<div class="group">
<label for="email" class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 block ml-1">Email / ID Pengguna</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-5 flex items-center pointer-events-none">
<i class="fas fa-envelope text-slate-300 group-focus-within:text-indigo-600 transition-colors"></i>
</div>
<input id="email"
class="block w-full pl-12 pr-4 py-4 bg-white border border-slate-100 rounded-2xl text-slate-900 text-sm font-semibold shadow-sm focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/20 transition-all placeholder:text-slate-300"
type="email" name="email" :value="old('email')" required autofocus placeholder="nama@cattery.com" />
</div>
<x-input-error :messages="$errors->get('email')" class="mt-1" />
</div>
{{-- Password Input --}}
<div class="group">
<div class="flex items-center justify-between mb-2 px-1">
<label for="password" class="text-[10px] font-black text-slate-400 uppercase tracking-widest">Kata Sandi</label>
@if (Route::has('password.request'))
<a class="text-[10px] font-bold text-indigo-600 hover:text-indigo-800 italic transition-colors" href="{{ route('password.request') }}">Lupa?</a>
@endif
</div>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-5 flex items-center pointer-events-none">
<i class="fas fa-lock text-slate-300 group-focus-within:text-indigo-600 transition-colors"></i>
</div>
<input id="password"
class="block w-full pl-12 pr-4 py-4 bg-white border border-slate-100 rounded-2xl text-slate-900 text-sm font-semibold shadow-sm focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/20 transition-all placeholder:text-slate-300"
type="password" name="password" required placeholder="••••••••" />
</div>
<x-input-error :messages="$errors->get('password')" class="mt-1" />
</div>
<div class="flex items-center justify-between px-1">
<label for="remember_me" class="inline-flex items-center cursor-pointer group">
<input id="remember_me" type="checkbox" class="w-5 h-5 rounded border-slate-200 text-indigo-600 shadow-sm focus:ring-indigo-500/20 cursor-pointer" name="remember">
<span class="ml-3 text-xs font-bold text-slate-500 group-hover:text-slate-800 transition-colors">Ingat sesi</span>
</label>
</div>
<button type="submit" class="w-full flex justify-center py-4 px-4 rounded-2xl shadow-xl shadow-indigo-600/20 text-sm font-black text-white bg-slate-900 hover:bg-indigo-700 focus:outline-none focus:ring-4 focus:ring-indigo-500/20 transition-all duration-300 hover:translate-y-[-1px] uppercase tracking-widest">
Masuk Sistem
</button>
</form>
<div class="pt-6 text-center">
<p class="text-[10px] text-slate-400 font-bold uppercase tracking-widest italic opacity-50">
© {{ date('Y') }} {{ $setting->app_name ?? 'ILY CATTERY' }} • SECURE ACCESS
</p>
</div>
</div>
</div>
</div>
</x-guest-layout>
+52
View File
@@ -0,0 +1,52 @@
<x-guest-layout>
<form method="POST" action="{{ route('register') }}">
@csrf
<!-- Name -->
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
<x-input-error :messages="$errors->get('name')" class="mt-2" />
</div>
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('login') }}">
{{ __('Already registered?') }}
</a>
<x-primary-button class="ms-4">
{{ __('Register') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
@@ -0,0 +1,39 @@
<x-guest-layout>
<form method="POST" action="{{ route('password.store') }}">
@csrf
<!-- Password Reset Token -->
<input type="hidden" name="token" value="{{ $request->route('token') }}">
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required autofocus autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<x-primary-button>
{{ __('Reset Password') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
@@ -0,0 +1,31 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600">
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
</div>
@if (session('status') == 'verification-link-sent')
<div class="mb-4 font-medium text-sm text-green-600">
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
</div>
@endif
<div class="mt-4 flex items-center justify-between">
<form method="POST" action="{{ route('verification.send') }}">
@csrf
<div>
<x-primary-button>
{{ __('Resend Verification Email') }}
</x-primary-button>
</div>
</form>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
{{ __('Log Out') }}
</button>
</form>
</div>
</x-guest-layout>
@@ -0,0 +1,3 @@
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,7 @@
@props(['status'])
@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
{{ $status }}
</div>
@endif
@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>
@@ -0,0 +1 @@
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
@@ -0,0 +1,35 @@
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white'])
@php
$alignmentClasses = match ($align) {
'left' => 'ltr:origin-top-left rtl:origin-top-right start-0',
'top' => 'origin-top',
default => 'ltr:origin-top-right rtl:origin-top-left end-0',
};
$width = match ($width) {
'48' => 'w-48',
default => $width,
};
@endphp
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
<div @click="open = ! open">
{{ $trigger }}
</div>
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
style="display: none;"
@click="open = false">
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
{{ $content }}
</div>
</div>
</div>
@@ -0,0 +1,19 @@
<footer class="mt-auto py-8 border-t border-slate-100 bg-white/50">
<div class="max-w-7xl mx-auto px-6 text-center">
@php
// Mengambil data dari database dengan fallback jika data kosong
$appName = \App\Models\Setting::where('key', 'app_name')->value('value') ?? 'Ily Cattery';
$appSlogan = \App\Models\Setting::where('key', 'app_slogan')->value('value') ?? 'Premium Breeding Management';
@endphp
<div class="flex flex-col items-center gap-2">
<p class="text-[10px] font-black text-slate-400 uppercase tracking-[0.4em] leading-none">
&copy; {{ date('Y') }} {{ $appName }}
</p>
<div class="h-px w-8 bg-slate-200 my-1"></div>
<p class="text-[9px] font-bold text-slate-300 uppercase tracking-widest italic">
{{ $appSlogan }}
</p>
</div>
</div>
</footer>
@@ -0,0 +1,9 @@
@props(['messages'])
@if ($messages)
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
@foreach ((array) $messages as $message)
<li>{{ $message }}</li>
@endforeach
</ul>
@endif
@@ -0,0 +1,5 @@
@props(['value'])
<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}>
{{ $value ?? $slot }}
</label>
@@ -0,0 +1,78 @@
@props([
'name',
'show' => false,
'maxWidth' => '2xl'
])
@php
$maxWidth = [
'sm' => 'sm:max-w-sm',
'md' => 'sm:max-w-md',
'lg' => 'sm:max-w-lg',
'xl' => 'sm:max-w-xl',
'2xl' => 'sm:max-w-2xl',
][$maxWidth];
@endphp
<div
x-data="{
show: @js($show),
focusables() {
// All focusable element types...
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
return [...$el.querySelectorAll(selector)]
// All non-disabled elements...
.filter(el => ! el.hasAttribute('disabled'))
},
firstFocusable() { return this.focusables()[0] },
lastFocusable() { return this.focusables().slice(-1)[0] },
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
}"
x-init="$watch('show', value => {
if (value) {
document.body.classList.add('overflow-y-hidden');
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
} else {
document.body.classList.remove('overflow-y-hidden');
}
})"
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null"
x-on:close.stop="show = false"
x-on:keydown.escape.window="show = false"
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
x-show="show"
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
style="display: {{ $show ? 'block' : 'none' }};"
>
<div
x-show="show"
class="fixed inset-0 transform transition-all"
x-on:click="show = false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
>
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<div
x-show="show"
class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
{{ $slot }}
</div>
</div>
@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>
@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>
@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>
@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>
@@ -0,0 +1,26 @@
@props(['active', 'icon'])
@php
// Logika untuk menentukan class CSS jika link sedang aktif atau tidak
$classes = ($active ?? false)
? 'flex items-center px-4 py-2.5 text-sm font-bold text-indigo-700 bg-indigo-50 rounded-xl border border-indigo-100 transition-all'
: 'flex items-center px-4 py-2.5 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-indigo-600 rounded-xl transition-all';
$iconClasses = ($active ?? false)
? 'text-indigo-600'
: 'text-slate-400 group-hover:text-indigo-500';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
@if($icon)
<i class="fas {{ $icon }} mr-3 w-5 text-center {{ $iconClasses }}"></i>
@endif
<span class="truncate">
{{ $slot }}
</span>
@if($active ?? false)
<span class="ml-auto w-1.5 h-1.5 rounded-full bg-indigo-600"></span>
@endif
</a>
@@ -0,0 +1,30 @@
@props(['title', 'value', 'icon', 'color'])
@php
// Map warna untuk icon background dan text
$colors = [
'indigo' => 'bg-indigo-100 text-indigo-600 shadow-indigo-100',
'emerald' => 'bg-emerald-100 text-emerald-600 shadow-emerald-100',
'rose' => 'bg-rose-100 text-rose-600 shadow-rose-100',
'amber' => 'bg-amber-100 text-amber-600 shadow-amber-100',
'blue' => 'bg-blue-100 text-blue-600 shadow-blue-100',
][$color ?? 'indigo'];
@endphp
<div class="bg-white p-6 rounded-3xl border border-slate-200 shadow-sm hover:shadow-md transition-all duration-300">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 {{ $colors }} rounded-2xl flex items-center justify-center shadow-sm">
<i class="fas {{ $icon }} text-xl"></i>
</div>
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Statistik</span>
</div>
<div class="space-y-1">
<h3 class="text-3xl font-black text-slate-800 leading-none">
{{ $value }}
</h3>
<p class="text-xs font-bold text-slate-500 uppercase tracking-tighter">
{{ $title }}
</p>
</div>
</div>
@@ -0,0 +1,17 @@
@props(['status'])
@php
$cleanStatus = strtolower(trim($status ?? ''));
$safeWords = ['sehat', 'normal', 'aktif', 'ok', 'good', 'sembuh', '-', ''];
$isHealthy = in_array($cleanStatus, $safeWords);
$classes = $isHealthy
? 'bg-green-100 text-green-700 border-green-200'
: 'bg-red-100 text-red-700 border-red-200';
@endphp
<span {{ $attributes->merge(['class' => "px-3 py-1 rounded-full text-xs font-bold border $classes"]) }}>
<i class="fas {{ $isHealthy ? 'fa-check-circle' : 'fa-pills' }} mr-1"></i>
{{ $isHealthy ? 'Sehat / Normal' : $status }}
</span>
@@ -0,0 +1,3 @@
@props(['disabled' => false])
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
+17
View File
@@ -0,0 +1,17 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Dashboard') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
{{ __("You're logged in!") }}
</div>
</div>
</div>
</div>
</x-app-layout>
+182
View File
@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="h-full">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
@php
// Optimasi: Gunakan try-catch agar jika tabel settings belum dimigrasi, app tidak crash
try {
$settings = \App\Models\Setting::whereIn('key', ['app_name', 'app_slogan', 'app_favicon', 'app_version'])
->pluck('value', 'key');
} catch (\Exception $e) {
$settings = collect();
}
$appName = $settings['app_name'] ?? config('app.name', 'Ily Cattery');
$appSlogan = $settings['app_slogan'] ?? 'Dedikasi untuk Kesehatan dan Kebahagiaan Anabul';
$appVersion = $settings['app_version'] ?? 'v1.0.0';
$favicon = $settings['app_favicon'] ?? null;
@endphp
<link rel="icon" type="image/x-icon" href="{{ $favicon ? Storage::url($favicon) : asset('favicon.ico') }}">
<title>{{ $appName }}</title>
{{-- Fonts & Icons --}}
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600,800,900&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
{{-- Scripts & Styles --}}
@vite(['resources/css/app.css', 'resources/js/app.js'])
@stack('styles')
<style>
.custom-scrollbar::-webkit-scrollbar { width: 6px; }
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 10px; }
.custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #cbd5e1; }
[x-cloak] { display: none !important; }
/* Animasi Fade In untuk Main Content */
.page-fade-in { animation: fadeIn 0.4s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
</style>
</head>
<body class="font-sans antialiased text-slate-900 bg-slate-50 h-full overflow-hidden">
<div class="flex h-screen overflow-hidden">
{{-- Sidebar Navigation --}}
@include('layouts.navigation-sidebar')
<div class="flex-1 flex flex-col min-w-0 bg-slate-50 relative">
{{-- Top Navigation --}}
@include('layouts.navigation')
{{-- Main Content Area --}}
<main class="flex-1 overflow-y-auto custom-scrollbar flex flex-col">
<div class="max-w-7xl mx-auto w-full p-4 md:p-8 flex-grow page-fade-in">
{{-- Notifikasi Error/Validasi (Hanya muncul jika bukan AJAX) --}}
@if ($errors->any())
<div x-data="{ show: true }" x-show="show" x-cloak
class="mb-6 p-4 bg-rose-50 border-2 border-rose-100 text-rose-700 rounded-3xl flex items-start shadow-sm transition-all">
<i class="fas fa-exclamation-circle mt-0.5 mr-3 text-lg"></i>
<div>
<h4 class="text-[11px] font-black uppercase tracking-widest mb-1">Terjadi Kesalahan Input:</h4>
<ul class="list-disc list-inside text-[10px] font-bold opacity-80">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
<button @click="show = false" class="ml-auto text-rose-300 hover:text-rose-500"><i class="fas fa-times"></i></button>
</div>
@endif
{{-- Main Slot (Halaman Konten Anda) --}}
{{ $slot }}
</div>
{{-- Footer --}}
<footer class="w-full py-8 border-t border-slate-100 mt-auto bg-white/50 backdrop-blur-md">
<div class="max-w-7xl mx-auto px-8 text-center">
<p class="text-[10px] font-black text-slate-400 uppercase tracking-[0.3em] mb-1">
{{ $appName }}
</p>
<p class="text-[9px] text-slate-300 font-bold tracking-widest italic uppercase">
{{ $appSlogan }} — {{ $appVersion }}
</p>
</div>
</footer>
</main>
</div>
</div>
{{-- SWEETALERT 2 INTEGRATION --}}
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 1. KONFIGURASI TOAST
const IlyToast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3500,
timerProgressBar: true,
didOpen: (toast) => {
toast.addEventListener('mouseenter', Swal.stopTimer)
toast.addEventListener('mouseleave', Swal.resumeTimer)
},
customClass: {
popup: 'rounded-2xl shadow-2xl border border-slate-50 p-4',
title: 'text-[11px] font-black uppercase tracking-widest ml-2'
}
});
// 2. FUNGSI GLOBAL TOAST (Bisa dipanggil di file JS manapun)
window.showToast = function(message, icon = 'success') {
IlyToast.fire({
icon: icon,
title: message,
iconColor: icon === 'success' ? '#4f46e5' : '#e11d48',
});
}
// 3. AUTO-TRIGGER FLASH MESSAGE
@if(session('success'))
showToast("{{ session('success') }}", 'success');
@endif
@if(session('error'))
showToast("{{ session('error') }}", 'error');
@endif
// 4. FUNGSI GLOBAL KONFIRMASI DELETE (Universal)
// Mendukung pemanggilan via ID Form (confirmDelete('formId', 'Nama'))
// Atau pemanggilan via Element (confirmDelete(this, 'Nama'))
window.confirmDelete = function(target, itemName = 'Data ini') {
const finalName = itemName || 'Data terpilih';
Swal.fire({
title: 'HAPUS DATA?',
html: "<div class='text-sm text-slate-500 font-medium'>Anda akan menghapus:<br><b class='text-slate-800 uppercase tracking-tighter'>" + finalName + "</b><br><br><span class='text-rose-500 font-bold italic'>Peringatan: Data yang dihapus tidak dapat dipulihkan!</span></div>",
icon: 'warning',
iconColor: '#e11d48',
showCancelButton: true,
confirmButtonColor: '#e11d48',
cancelButtonColor: '#f1f5f9',
confirmButtonText: 'YA, HAPUS PERMANEN',
cancelButtonText: 'BATAL',
reverseButtons: true,
customClass: {
popup: 'rounded-[2.5rem] p-10 border-none shadow-2xl',
confirmButton: 'rounded-2xl font-black uppercase text-[10px] px-8 py-4 ml-3 tracking-widest shadow-lg shadow-rose-100',
cancelButton: 'rounded-2xl font-black uppercase text-[10px] px-8 py-4 text-slate-500 tracking-widest'
}
}).then((result) => {
if (result.isConfirmed) {
// Logic: Jika target adalah string, cari by ID. Jika object, cari closest form.
if (typeof target === 'string') {
const form = document.getElementById(target);
if(form) form.submit();
} else {
const form = target.closest('form');
if(form) form.submit();
}
}
});
}
});
</script>
@stack('scripts')
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<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">
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="font-sans text-gray-900 antialiased bg-white">
{{--
Wrapper Utama:
Sekarang kita hilangkan 'max-w-md' dan 'shadow' bawaan Breeze
agar halaman login bisa mengatur lebarnya sendiri secara fleksibel.
--}}
<div class="min-h-screen flex flex-col">
{{ $slot }}
</div>
</body>
</html>
@@ -0,0 +1,165 @@
<aside class="w-72 bg-white border-r border-slate-200 hidden lg:flex flex-col sticky top-0 h-screen transition-all shadow-sm">
@php
$appSettings = \Illuminate\Support\Facades\Cache::remember('app_sidebar_settings', 3600, function() {
return \App\Models\Setting::whereIn('key', ['app_logo', 'app_name'])->pluck('value', 'key');
});
$appName = $appSettings['app_name'] ?? 'Ily Cattery';
// Hitung stok kritis untuk badge (TETAP UTUH)
$lowMedicinesCount = \App\Models\Medicine::where('qty', '<', 5)->count();
$lowFoodsCount = \App\Models\Food::where('qty', '<', 5)->count();
@endphp
{{-- Brand Header --}}
<div class="p-8 flex items-center gap-4">
<div class="w-12 h-12 bg-indigo-600 rounded-[1.25rem] flex items-center justify-center shadow-xl shadow-indigo-100">
<i class="fas fa-cat text-white text-xl"></i>
</div>
<div class="min-w-0">
<h1 class="text-sm font-black text-slate-800 uppercase tracking-tighter truncate leading-none mb-1">{{ $appName }}</h1>
<p class="text-[9px] font-black text-slate-400 uppercase tracking-[0.2em] leading-none">Management System</p>
</div>
</div>
{{-- Navigation Links --}}
<nav class="flex-1 px-4 space-y-1 overflow-y-auto custom-scrollbar">
{{-- Dashboard biasanya dapat diakses oleh semua user terautentikasi --}}
<x-sidebar-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" icon="fa-th-large">
Dashboard
</x-sidebar-link>
{{-- OPERASIONAL --}}
@if(Gate::allows('check-access', ['cats', 'read']) || Gate::allows('check-access', ['matting', 'read']))
<div class="pt-6 pb-2 px-5 text-[9px] font-black text-slate-400 uppercase tracking-[0.3em] flex items-center gap-3">
<span>Operasional</span>
<div class="h-px bg-slate-100 flex-1"></div>
</div>
@can('check-access', ['cats', 'read'])
<x-sidebar-link :href="route('cats.index')" :active="request()->routeIs('cats.*')" icon="fa-paw">
Data Kucing
</x-sidebar-link>
@endcan
@can('check-access', ['matting', 'read'])
<x-sidebar-link :href="route('matting.index')" :active="request()->routeIs('matting.*')" icon="fa-venus-mars">
<span class="flex-1">Matting Cat</span>
<span class="text-[8px] bg-emerald-100 text-emerald-600 px-1.5 py-0.5 rounded font-black uppercase italic">Breeding</span>
</x-sidebar-link>
@endcan
@endif
{{-- KESEHATAN --}}
@if(Gate::allows('check-access', ['medical_insights', 'read']) || Gate::allows('check-access', ['treatments', 'read']))
<div class="pt-6 pb-2 px-5 text-[9px] font-black text-slate-400 uppercase tracking-[0.3em] flex items-center gap-3">
<span>Kesehatan</span>
<div class="h-px bg-slate-100 flex-1"></div>
</div>
@can('check-access', ['medical_insights', 'read'])
<x-sidebar-link :href="route('medical.dashboard')" :active="request()->routeIs('medical.dashboard')" icon="fa-chart-pie">
Medical Insight
</x-sidebar-link>
@endcan
@can('check-access', ['treatments', 'read'])
<x-sidebar-link :href="route('treatments.index')" :active="request()->routeIs('treatments.*')" icon="fa-notes-medical">
Rekam Medis
</x-sidebar-link>
@endcan
@endif
{{-- LOGISTIK --}}
@if(Gate::allows('check-access', ['medicines', 'read']) || Gate::allows('check-access', ['foods', 'read']) || Gate::allows('check-access', ['supplies', 'read']) || Gate::allows('check-access', ['categories', 'read']))
<div class="pt-6 pb-2 px-5 text-[9px] font-black text-slate-400 uppercase tracking-[0.3em] flex items-center gap-3">
<span>Logistik</span>
<div class="h-px bg-slate-100 flex-1"></div>
</div>
@can('check-access', ['medicines', 'read'])
<x-sidebar-link :href="route('inventory.medicines.index')" :active="request()->routeIs('inventory.medicines.*')" icon="fa-pills">
<span class="flex-1">Obat & Vitamin</span>
@if($lowMedicinesCount > 0)
<span class="bg-rose-500 text-white text-[9px] font-black px-2 py-0.5 rounded-lg animate-pulse">{{ $lowMedicinesCount }}</span>
@endif
</x-sidebar-link>
@endcan
@can('check-access', ['foods', 'read'])
<x-sidebar-link :href="route('inventory.foods.index')" :active="request()->routeIs('inventory.foods.*')" icon="fa-bowl-food">
<span class="flex-1">Stok Makanan</span>
@if($lowFoodsCount > 0)
<span class="bg-rose-500 text-white text-[9px] font-black px-2 py-0.5 rounded-lg">{{ $lowFoodsCount }}</span>
@endif
</x-sidebar-link>
@endcan
@can('check-access', ['supplies', 'read'])
<x-sidebar-link :href="route('inventory.supplies.index')" :active="request()->routeIs('inventory.supplies.*')" icon="fa-box-archive">
Perlengkapan
</x-sidebar-link>
@endcan
@can('check-access', ['categories', 'read'])
<x-sidebar-link :href="route('categories.index')" :active="request()->routeIs('categories.*')" icon="fa-tags">
Master Kategori
</x-sidebar-link>
@endcan
@endif
{{-- INTELLIGENCE --}}
@if(Gate::allows('check-access', ['reports', 'read']) || Gate::allows('check-access', ['matting_reports', 'read']))
<div class="pt-6 pb-2 px-5 text-[9px] font-black text-indigo-500 uppercase tracking-[0.3em] flex items-center gap-3">
<span>Intelligence</span>
<div class="h-px bg-indigo-50 flex-1"></div>
</div>
@can('check-access', ['reports', 'read'])
<x-sidebar-link :href="route('admin.reports.index')" :active="request()->routeIs('admin.reports.index')" icon="fa-chart-line">
Pusat Laporan
</x-sidebar-link>
@endcan
@can('check-access', ['matting_reports', 'read'])
<x-sidebar-link :href="route('admin.reports.matting.index')" :active="request()->routeIs('admin.reports.matting')" icon="fa-file-medical">
<span class="flex-1">Laporan Breeding</span>
<span class="text-[8px] bg-rose-100 text-rose-600 px-1.5 py-0.5 rounded font-black uppercase shadow-sm">Special</span>
</x-sidebar-link>
@endcan
@endif
{{-- KONFIGURASI (Hanya tampil untuk yang memiliki akses ke modul-modul ini) --}}
@if(Gate::allows('check-access', ['users', 'read']) || Gate::allows('check-access', ['roles', 'read']) || Gate::allows('check-access', ['logs', 'read']) || Gate::allows('check-access', ['settings', 'read']))
<div class="pt-8 pb-2 px-5 text-[9px] font-black text-rose-500 uppercase tracking-[0.3em] flex items-center gap-3">
<span>Konfigurasi</span>
<div class="h-px bg-rose-50 flex-1"></div>
</div>
@can('check-access', ['users', 'read'])
<x-sidebar-link :href="route('admin.users.index')" :active="request()->routeIs('admin.users.*')" icon="fa-users-cog">
Manajemen User
</x-sidebar-link>
@endcan
@can('check-access', ['roles', 'read'])
<x-sidebar-link :href="route('admin.roles.index')" :active="request()->routeIs('admin.roles.*')" icon="fa-user-shield">
Role Management
</x-sidebar-link>
@endcan
@can('check-access', ['logs', 'read'])
<x-sidebar-link :href="route('admin.logs.index')" :active="request()->routeIs('admin.logs.*')" icon="fa-history">
Log Audit
</x-sidebar-link>
@endcan
@can('check-access', ['settings', 'read'])
<x-sidebar-link :href="route('admin.settings.index')" :active="request()->routeIs('admin.settings.*')" icon="fa-sliders-h">
Pengaturan Sistem
</x-sidebar-link>
@endcan
@endif
</nav>
</aside>
@@ -0,0 +1,128 @@
<nav x-data="{ open: false }" class="bg-white/80 backdrop-blur-md border-b border-slate-100 sticky top-0 z-50 shadow-sm">
@php
// Mengambil data branding dari database
$appLogo = \App\Models\Setting::where('key', 'app_logo')->value('value');
$appName = \App\Models\Setting::where('key', 'app_name')->value('value') ?? 'ILY Aregau';
@endphp
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-20"> <div class="flex">
<div class="shrink-0 flex items-center">
<a href="{{ route('dashboard') }}" class="flex items-center gap-4 group">
<div class="flex items-center justify-center transition-transform duration-300 group-hover:scale-105">
@if($appLogo)
<img src="{{ Storage::url($appLogo) }}"
alt="Logo {{ $appName }}"
class="h-16 w-auto max-w-[200px] object-contain object-left">
@else
<div class="w-12 h-12 bg-indigo-600 rounded-xl flex items-center justify-center text-white font-black text-sm shadow-lg">
{{ strtoupper(substr($appName, 0, 2)) }}
</div>
@endif
</div>
<div class="hidden md:flex flex-col border-l-2 border-slate-100 pl-4">
<span class="text-lg font-black text-slate-800 uppercase tracking-tighter leading-none">{{ $appName }}</span>
<span class="text-[10px] font-bold text-indigo-600 uppercase tracking-[0.2em] mt-1 italic">Cattery Management</span>
</div>
</a>
</div>
<div class="hidden space-x-2 sm:-my-px sm:ms-10 sm:flex items-center">
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" class="text-[10px] font-black uppercase tracking-widest px-4 py-2 rounded-xl transition-all duration-200">
<i class="fas fa-th-large mr-2 opacity-50 group-hover:opacity-100"></i> {{ __('Dashboard') }}
</x-nav-link>
@if(Auth::user()->role === 'admin')
<x-nav-link :href="route('admin.settings.index')" :active="request()->routeIs('admin.settings.*')" class="text-[10px] font-black uppercase tracking-widest px-4 py-2 rounded-xl text-rose-500 hover:text-rose-600 transition-all duration-200">
<i class="fas fa-cog mr-2 opacity-50"></i> {{ __('Settings') }}
</x-nav-link>
@endif
</div>
</div>
<div class="hidden sm:flex sm:items-center sm:ms-6">
<x-dropdown align="right" width="56">
<x-slot name="trigger">
<button class="inline-flex items-center gap-3 px-4 py-2 border-2 border-slate-50 text-xs leading-4 font-bold rounded-2xl text-slate-500 bg-slate-50/50 hover:bg-white hover:border-indigo-100 hover:text-indigo-600 focus:outline-none transition-all duration-200 shadow-sm active:scale-95">
<div class="w-7 h-7 bg-indigo-100 text-indigo-600 rounded-lg flex items-center justify-center font-black ring-2 ring-white">
{{ strtoupper(substr(Auth::user()->name, 0, 1)) }}
</div>
<div class="uppercase tracking-tighter">{{ Auth::user()->name }}</div>
<i class="fas fa-chevron-down text-[10px] ml-1 opacity-30"></i>
</button>
</x-slot>
<x-slot name="content">
<div class="px-4 py-3 border-b border-slate-50">
<p class="text-[8px] font-black text-slate-400 uppercase tracking-widest">Signed in as</p>
<p class="text-xs font-bold text-slate-800 truncate">{{ Auth::user()->email }}</p>
</div>
<x-dropdown-link :href="route('profile.edit')" class="text-xs font-bold py-3 hover:bg-indigo-50 transition-colors">
<i class="fas fa-user-circle mr-2 opacity-40"></i> {{ __('My Profile') }}
</x-dropdown-link>
<div class="h-px bg-slate-50 my-1"></div>
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-dropdown-link :href="route('logout')" class="text-xs font-bold py-3 text-rose-500 hover:bg-rose-50 transition-colors" onclick="event.preventDefault(); this.closest('form').submit();">
<i class="fas fa-sign-out-alt mr-2 opacity-40"></i> {{ __('Log Out') }}
</x-dropdown-link>
</form>
</x-slot>
</x-dropdown>
</div>
<div class="-me-2 flex items-center sm:hidden">
<button @click="open = ! open" class="inline-flex items-center justify-center p-2.5 rounded-xl text-slate-400 hover:text-indigo-600 hover:bg-indigo-50 transition-all duration-200 active:scale-90">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-4"
x-transition:enter-end="opacity-100 translate-y-0"
class="hidden sm:hidden bg-white border-t border-slate-100">
<div class="pt-2 pb-3 space-y-1 px-4">
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" class="rounded-xl font-black text-[10px] uppercase tracking-widest py-3">
<i class="fas fa-th-large mr-3"></i> {{ __('Dashboard') }}
</x-responsive-nav-link>
@if(Auth::user()->role === 'admin')
<x-responsive-nav-link :href="route('admin.settings.index')" :active="request()->routeIs('admin.settings.*')" class="rounded-xl font-black text-[10px] uppercase tracking-widest text-rose-500 py-3">
<i class="fas fa-cog mr-3"></i> {{ __('System Settings') }}
</x-responsive-nav-link>
@endif
</div>
<div class="pt-4 pb-4 border-t border-slate-100 bg-slate-50/50">
<div class="flex items-center px-6 gap-4">
<div class="w-12 h-12 bg-indigo-600 text-white rounded-2xl flex items-center justify-center font-black shadow-lg shadow-indigo-100 ring-4 ring-white">
{{ strtoupper(substr(Auth::user()->name, 0, 1)) }}
</div>
<div class="overflow-hidden">
<div class="font-black text-sm text-slate-800 uppercase tracking-tighter">{{ Auth::user()->name }}</div>
<div class="font-bold text-[10px] text-slate-400 truncate">{{ Auth::user()->email }}</div>
</div>
</div>
<div class="mt-4 space-y-1 px-4">
<x-responsive-nav-link :href="route('profile.edit')" class="rounded-xl font-bold text-xs uppercase tracking-widest py-3">
<i class="fas fa-user-circle mr-3"></i> {{ __('Profile Settings') }}
</x-responsive-nav-link>
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-responsive-nav-link :href="route('logout')" class="rounded-xl font-black text-xs uppercase tracking-widest text-rose-500 py-3" onclick="event.preventDefault(); this.closest('form').submit();">
<i class="fas fa-sign-out-alt mr-3"></i> {{ __('Sign Out') }}
</x-responsive-nav-link>
</form>
</div>
</div>
</div>
</nav>
+153
View File
@@ -0,0 +1,153 @@
<x-app-layout>
<div class="space-y-6 pb-12">
{{-- SECTION 1: Header --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-3xl font-black text-slate-800 tracking-tight leading-none mb-1 text-uppercase">Audit Trail</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">Rekaman Aktivitas Staf & Keamanan Sistem</p>
</div>
<div class="flex flex-wrap items-center gap-3">
{{-- Statistik --}}
<span class="px-4 py-2 bg-white border border-slate-200 rounded-xl text-[10px] font-black text-slate-400">
TOTAL: {{ $logs->total() }} LOGS
</span>
{{-- Export: Font Putih (text-white) --}}
<a href="{{ route('admin.logs.export') }}" class="inline-flex items-center px-6 py-4 bg-emerald-50 text-emerald-900 border-2 border-emerald-200 text-[10px] font-black rounded-2xl shadow-sm hover:bg-emerald-600 hover:text-white transition-all uppercase tracking-widest">
<i class="fas fa-file-excel mr-2 text-sm text-emerald-600 group-hover:text-white"></i> Export XLSX
</a>
{{-- Reset --}}
<form id="clear-logs-form" action="{{ route('admin.logs.clear') }}" method="POST" class="inline">
@csrf @method('DELETE')
<button type="button" onclick="confirmClearLogs()" class="inline-flex items-center px-5 py-3 bg-rose-50 text-rose-600 border border-rose-100 text-[10px] font-black rounded-2xl hover:bg-rose-100 transition-all uppercase tracking-widest">
<i class="fas fa-trash-alt mr-2 text-sm"></i> Clear Data
</button>
</form>
</div>
</div>
{{-- SECTION 2: Filter --}}
<div class="bg-white p-5 border border-slate-200 rounded-[2rem] shadow-sm">
<form action="{{ route('admin.logs.index') }}" method="GET" class="flex flex-col md:flex-row gap-4">
<div class="flex-1 relative">
<i class="fas fa-search absolute left-4 top-1/2 -translate-y-1/2 text-slate-400"></i>
<input type="text" name="search" value="{{ request('search') }}" placeholder="Cari deskripsi atau nama staf..."
class="w-full pl-11 pr-4 py-3.5 bg-slate-50 border border-slate-100 rounded-xl text-xs font-bold text-slate-700 focus:ring-2 focus:ring-indigo-100 transition-all">
</div>
{{-- Opsi Filter --}}
<select name="action" class="bg-slate-50 border border-slate-100 rounded-xl text-xs font-bold text-slate-700 px-5 py-3.5 focus:ring-2 focus:ring-indigo-100 transition-all">
<option value="">SEMUA AKSI</option>
<option value="CREATE" {{ request('action') == 'CREATE' ? 'selected' : '' }}>CREATE (Tambah)</option>
<option value="UPDATE" {{ request('action') == 'UPDATE' ? 'selected' : '' }}>UPDATE (Ubah)</option>
<option value="DELETE" {{ request('action') == 'DELETE' ? 'selected' : '' }}>DELETE (Hapus)</option>
<option value="LOGIN" {{ request('action') == 'LOGIN' ? 'selected' : '' }}>LOGIN</option>
</select>
<button type="submit" class="px-8 py-3.5 bg-slate-900 text-white text-[10px] font-black rounded-xl uppercase hover:bg-black transition-all">Filter</button>
@if(request()->anyFilled(['search', 'action']))
<a href="{{ route('admin.logs') }}" class="px-4 py-3.5 text-slate-400 hover:text-rose-600 text-[10px] font-black flex items-center transition-all uppercase">Reset</a>
@endif
</form>
</div>
{{-- SECTION 3: Tabel --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] overflow-hidden shadow-sm">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead class="bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100">
<tr>
<th class="px-6 py-4">Waktu</th>
<th class="px-6 py-4">Staf Pelaksana</th>
<th class="px-6 py-4 text-center">Aksi</th>
<th class="px-6 py-4">Deskripsi Aktivitas</th>
<th class="px-6 py-4 text-right">Teknis</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 font-medium text-slate-700">
@forelse($logs as $log)
<tr class="hover:bg-slate-50/50 transition-all group">
<td class="px-6 py-4">
<p class="text-xs font-black text-slate-800">{{ $log->created_at->format('d/m/Y') }}</p>
<p class="text-[10px] text-slate-400 font-bold tracking-tight">{{ $log->created_at->format('H:i:s') }}</p>
</td>
{{-- Inisial: Font Gelap (text-slate-900) di Box Putih --}}
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-white border border-slate-200 text-slate-900 flex items-center justify-center font-black text-sm shadow-sm uppercase">
{{ substr($log->user->name ?? 'S', 0, 1) }}
</div>
<div>
<p class="text-xs font-black text-slate-700">{{ $log->user->name ?? 'System' }}</p>
<p class="text-[9px] text-slate-400 font-bold uppercase tracking-widest">{{ $log->user->role ?? '-' }}</p>
</div>
</div>
</td>
<td class="px-6 py-4 text-center">
@php
$action = strtoupper($log->action);
$style = 'text-slate-500 bg-slate-50 border-slate-100';
if(str_contains($action, 'CREATE')) $style = 'text-emerald-600 bg-emerald-50 border-emerald-100';
if(str_contains($action, 'UPDATE')) $style = 'text-blue-600 bg-blue-50 border-blue-100';
if(str_contains($action, 'DELETE')) $style = 'text-rose-600 bg-rose-50 border-rose-100';
if(str_contains($action, 'LOGIN')) $style = 'text-amber-600 bg-amber-50 border-amber-100';
@endphp
<span class="px-3 py-1 rounded-lg text-[9px] font-black border uppercase {{ $style }}">
{{ $log->action }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex flex-col">
<span class="text-xs font-bold text-slate-600 leading-relaxed">{{ $log->description }}</span>
<span class="text-[9px] text-slate-400 italic">Target: {{ class_basename($log->model) }} (ID: {{ $log->model_id }})</span>
</div>
</td>
<td class="px-6 py-4 text-right">
<span class="text-[10px] font-mono font-bold text-slate-500 bg-slate-50 px-2 py-0.5 rounded border border-slate-100">
{{ $log->ip_address }}
</span>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-6 py-20 text-center text-slate-400 uppercase text-[10px] font-black tracking-widest">
Tidak ada log ditemukan
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="p-6 border-t border-slate-100 bg-slate-50/30">
{{ $logs->appends(request()->query())->links() }}
</div>
</div>
</div>
{{-- Script SweetAlert (Tanpa perubahan layout) --}}
@push('scripts')
<script>
function confirmClearLogs() {
Swal.fire({
title: 'RESET DATA LOG?',
html: "Anda akan menghapus <b class='text-rose-600'>SEMUA</b> riwayat aktivitas. Data tidak bisa dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#e11d48',
confirmButtonText: 'YA, BERSIHKAN',
cancelButtonText: 'BATAL',
reverseButtons: true,
customClass: { popup: 'rounded-[2.5rem] p-10' }
}).then((result) => {
if (result.isConfirmed) document.getElementById('clear-logs-form').submit();
});
}
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,55 @@
<x-app-layout>
<div class="py-12">
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white shadow-xl rounded-[2rem] overflow-hidden border border-slate-100">
{{-- Header --}}
<div class="p-8 border-b border-slate-100 flex justify-between items-center bg-slate-50/50">
<div>
<h2 class="text-2xl font-black text-slate-900 tracking-tight">Tambah Role Baru</h2>
<p class="text-slate-500 font-medium">Definisikan tingkat otoritas pengguna baru.</p>
</div>
<a href="{{ route('admin.roles.index') }}" class="text-xs font-bold text-slate-400 hover:text-slate-600 transition-colors uppercase tracking-widest italic">Batal</a>
</div>
<form action="{{ route('admin.roles.store') }}" method="POST" class="p-8 space-y-6">
@csrf
{{-- Nama Role --}}
<div class="space-y-2">
<label for="name" class="text-[10px] font-black text-slate-400 uppercase tracking-[0.2em] ml-1">Nama Role</label>
<input type="text" name="name" id="name" required placeholder="Contoh: Manager Operasional"
class="block w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-slate-900 font-semibold focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/20 transition-all">
<x-input-error :messages="$errors->get('name')" class="mt-1" />
</div>
{{-- Slug Role --}}
<div class="space-y-2">
<label for="slug" class="text-[10px] font-black text-slate-400 uppercase tracking-[0.2em] ml-1">Slug (ID Sistem)</label>
<input type="text" name="slug" id="slug" required placeholder="contoh: manager-ops"
class="block w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-slate-400 font-mono text-sm focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/20 transition-all">
<p class="text-[10px] text-slate-400 italic ml-1">*Slug digunakan sistem untuk identifikasi rute dan middleware.</p>
<x-input-error :messages="$errors->get('slug')" class="mt-1" />
</div>
<div class="pt-4">
<button type="submit" class="w-full py-4 bg-slate-900 text-white rounded-2xl font-black text-xs uppercase tracking-[0.3em] hover:bg-indigo-700 shadow-2xl shadow-indigo-600/20 transition-all active:scale-[0.98]">
<i class="fas fa-plus-circle mr-2"></i> Buat & Lanjut Atur Izin
</button>
</div>
</form>
</div>
</div>
</div>
{{-- Script Sederhana untuk Auto-Slug --}}
<script>
const nameInput = document.getElementById('name');
const slugInput = document.getElementById('slug');
nameInput.addEventListener('keyup', function() {
let text = this.value;
slugInput.value = text.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
});
</script>
</x-app-layout>
@@ -0,0 +1,71 @@
<x-app-layout>
<div class="py-12">
{{-- Lebar kontainer ditingkatkan ke 6xl agar tabel memiliki ruang lebih --}}
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white shadow-xl rounded-[2rem] overflow-hidden border border-slate-100">
{{-- Header Card --}}
<div class="p-6 md:p-8 border-b border-slate-100 flex flex-col md:flex-row justify-between items-start md:items-center bg-slate-50/50 gap-4">
<div>
<h2 class="text-2xl font-black text-slate-900 tracking-tight">Konfigurasi Hak Akses</h2>
<p class="text-slate-500 font-medium text-sm">
Role: <span class="text-indigo-600 font-bold uppercase tracking-widest">{{ $role->name }}</span>
</p>
</div>
<a href="{{ route('admin.roles.index') }}" class="text-xs font-bold text-slate-400 hover:text-slate-600 transition-colors uppercase tracking-widest italic flex items-center">
<i class="fas fa-arrow-left mr-2"></i> Kembali
</a>
</div>
<form action="{{ route('admin.roles.update', $role->id) }}" method="POST" class="p-6 md:p-8">
@csrf
{{-- Wrapper untuk Horizontal Scroll agar tidak terpotong di Mobile --}}
<div class="overflow-x-auto rounded-2xl border border-slate-200 shadow-sm mb-8">
<table class="w-full text-sm text-left border-collapse min-w-[600px]">
<thead class="bg-slate-900 text-white uppercase text-xs font-black tracking-widest">
<tr>
{{-- Padding disesuaikan (py-4 px-6) agar seimbang --}}
<th class="py-4 px-6 whitespace-nowrap">Modul Sistem</th>
<th class="py-4 px-6 text-center whitespace-nowrap">Read</th>
<th class="py-4 px-6 text-center whitespace-nowrap">Create</th>
<th class="py-4 px-6 text-center whitespace-nowrap">Update</th>
<th class="py-4 px-6 text-center whitespace-nowrap">Delete</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@foreach($modules as $slug => $label)
<tr class="group hover:bg-indigo-50/40 transition-all">
<td class="py-4 px-6">
<span class="font-bold text-slate-700 block">{{ $label }}</span>
<span class="text-[10px] text-slate-400 font-mono uppercase">{{ $slug }}</span>
</td>
@foreach(['read', 'create', 'update', 'delete'] as $action)
<td class="py-4 px-6 text-center">
<div class="flex justify-center">
<input type="checkbox"
name="perms[{{ $slug }}][{{ $action }}]"
value="1"
class="w-5 h-5 rounded-lg border-slate-300 text-indigo-600 shadow-sm focus:ring-4 focus:ring-indigo-500/10 cursor-pointer transition-all"
{{ $role->hasPermission($slug, 'can_'.$action) ? 'checked' : '' }}>
</div>
</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- Tombol Simpan --}}
<div class="flex justify-end pt-4 border-t border-slate-100">
<button type="submit" class="w-full sm:w-auto px-12 py-4 bg-slate-900 text-white rounded-2xl font-black text-xs uppercase tracking-[0.2em] hover:bg-indigo-700 shadow-xl shadow-indigo-600/20 transition-all active:scale-95 flex items-center justify-center">
<i class="fas fa-save mr-3"></i> Simpan Perubahan Izin
</button>
</div>
</form>
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,159 @@
<x-app-layout>
{{-- Container utama: Menggunakan max-w-6xl agar lebih lega dan mx-auto untuk center --}}
<div class="max-w-6xl mx-auto px-6 py-10 space-y-8">
{{-- 1. HEADER SECTION: Diberi jarak mt-4 agar tidak menempel ke atas --}}
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6 px-2 mt-4">
<div class="space-y-2">
<div class="inline-flex items-center gap-2 px-3 py-1 bg-indigo-50 text-indigo-600 rounded-full border border-indigo-100">
<i class="fas fa-shield-alt text-[10px]"></i>
<span class="text-[9px] font-black uppercase tracking-widest">Security Module</span>
</div>
<h2 class="text-3xl font-black text-slate-800 tracking-tighter uppercase leading-none">Role Management</h2>
<p class="text-sm text-slate-400 font-medium italic">Konfigurasi matriks perizinan dan otoritas sistem ilycats.</p>
</div>
{{-- Tombol Tambah --}}
@can('check-access', ['roles', 'create'])
<div class="shrink-0">
<a href="{{ route('admin.roles.create') }}" class="inline-flex items-center justify-center px-6 py-3.5 bg-indigo-600 hover:bg-indigo-700 text-white text-[10px] font-black rounded-2xl shadow-xl shadow-indigo-100 transition-all active:scale-95 tracking-widest uppercase">
<i class="fas fa-plus-circle mr-2 text-base"></i> TAMBAH ROLE BARU
</a>
</div>
@endcan
</div>
{{-- 2. TABEL ROLE: Menggunakan Card dengan shadow yang lebih soft --}}
<div class="bg-white border border-slate-200 rounded-[3rem] shadow-sm overflow-hidden">
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full text-left border-collapse min-w-[800px]">
<thead class="bg-slate-50 border-b border-slate-100">
<tr>
<th class="px-10 py-6 text-[10px] font-black text-slate-400 uppercase tracking-widest">Identitas Role</th>
<th class="px-10 py-6 text-[10px] font-black text-slate-400 uppercase tracking-widest">Slug Identitas</th>
<th class="px-10 py-6 text-[10px] font-black text-slate-400 uppercase tracking-widest text-right">Otoritas Matriks</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@foreach($roles as $role)
<tr class="hover:bg-slate-50/50 transition-all group">
{{-- Nama Role --}}
<td class="px-10 py-6">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-slate-100 flex items-center justify-center text-slate-400 group-hover:bg-indigo-600 group-hover:text-white border border-slate-200 group-hover:border-indigo-500 transition-all duration-300 shadow-sm">
<i class="fas fa-user-tag text-sm"></i>
</div>
<div>
<span class="text-sm font-black text-slate-800 uppercase tracking-tight block">{{ $role->name }}</span>
<span class="text-[9px] text-slate-400 font-bold uppercase tracking-tighter">System Authority Level</span>
</div>
</div>
</td>
{{-- Slug --}}
<td class="px-10 py-6">
<span class="px-3 py-1.5 bg-slate-100 text-slate-500 text-[9px] font-black rounded-lg border border-slate-200/60 tracking-wider font-mono">
{{ $role->slug }}
</span>
</td>
{{-- Aksi --}}
<td class="px-10 py-6">
<div class="flex justify-end items-center gap-3">
{{-- Configuration --}}
@can('check-access', ['roles', 'update'])
<a href="{{ route('admin.roles.edit', $role->id) }}"
class="inline-flex items-center gap-2 px-5 py-2.5 bg-white text-indigo-600 border-2 border-indigo-50 hover:bg-indigo-600 hover:text-white hover:border-indigo-600 rounded-xl text-[9px] font-black uppercase tracking-widest transition-all shadow-sm active:scale-95 group/btn">
<i class="fas fa-cog group-hover/btn:rotate-90 transition-transform duration-500"></i> Configuration
</a>
@endcan
{{-- Delete --}}
@can('check-access', ['roles', 'delete'])
@if($role->slug !== 'admin')
<form id="delete-form-{{ $role->id }}" action="{{ route('admin.roles.destroy', $role->id) }}" method="POST" class="inline">
@csrf @method('DELETE')
<button type="button"
onclick="confirmDeleteRole({{ $role->id }}, '{{ addslashes($role->name) }}')"
class="w-10 h-10 flex items-center justify-center text-slate-300 hover:text-rose-600 hover:bg-rose-50 rounded-xl transition-all"
title="Hapus Role">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</form>
@endif
@endcan
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
{{-- 3. FOOTER INFO: Memberikan kesan seimbang di bawah --}}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="p-6 bg-indigo-900 rounded-[2.5rem] text-white shadow-xl shadow-indigo-100 flex items-center gap-5">
<div class="w-12 h-12 bg-indigo-800 rounded-2xl flex items-center justify-center text-xl shrink-0">
<i class="fas fa-info-circle"></i>
</div>
<div>
<p class="text-[10px] font-black uppercase tracking-widest opacity-60 mb-1">Pemberitahuan</p>
<p class="text-xs font-bold leading-relaxed">Perubahan pada matriks izin akan langsung berdampak pada seluruh staf yang terkait.</p>
</div>
</div>
<div class="p-6 bg-slate-50 border border-slate-200 rounded-[2.5rem] flex items-center gap-5 italic">
<div class="w-12 h-12 bg-white border border-slate-200 rounded-2xl flex items-center justify-center text-slate-400 text-xl shrink-0">
<i class="fas fa-user-lock"></i>
</div>
<div>
<p class="text-[10px] font-black uppercase tracking-widest text-slate-400 mb-1">Keamanan Data</p>
<p class="text-xs font-bold text-slate-500">Pastikan role Administrator tidak kehilangan akses Read pada modul Hak Akses.</p>
</div>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
function confirmDeleteRole(id, name) {
Swal.fire({
title: 'Hapus Role?',
html: `Konfirmasi penghapusan permanen untuk:<br><b class="text-rose-600 text-lg">${name}</b>`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#f43f5e',
cancelButtonColor: '#64748b',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: {
popup: 'rounded-[2.5rem]',
title: 'font-black text-slate-800 uppercase tracking-tighter'
}
}).then((result) => {
if (result.isConfirmed) {
document.getElementById(`delete-form-${id}`).submit();
}
});
}
@if(session('success'))
Swal.fire({
icon: 'success',
title: 'BERHASIL',
text: "{{ session('success') }}",
showConfirmButton: false,
timer: 2500,
toast: true,
position: 'top-end',
customClass: { popup: 'rounded-2xl shadow-xl' }
});
@endif
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,196 @@
<x-app-layout>
<div class="max-w-5xl mx-auto space-y-8 pb-12">
{{-- Header --}}
<div class="px-2 flex justify-between items-end">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1">Pengaturan Identitas</h2>
<p class="text-sm text-slate-500 font-medium italic">Personalisasi branding & konfigurasi sistem.</p>
</div>
<div class="hidden md:block">
<span class="px-4 py-2 bg-white border border-slate-100 rounded-2xl text-[10px] font-black text-slate-400 uppercase tracking-widest shadow-sm">
Server Time: {{ now()->format('H:i') }} WIB
</span>
</div>
</div>
<form action="{{ route('admin.settings.update') }}" method="POST" enctype="multipart/form-data" class="space-y-6">
@csrf
{{-- Card 1: Visual Branding --}}
<div class="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm p-8 md:p-10">
<div class="flex items-center gap-4 mb-10">
<div class="w-12 h-12 bg-indigo-50 text-indigo-600 rounded-2xl flex items-center justify-center shadow-sm">
<i class="fas fa-paint-brush text-xl"></i>
</div>
<div>
<h3 class="text-sm font-black text-slate-800 uppercase tracking-wider">Visual Branding</h3>
<p class="text-[10px] text-slate-400 font-bold italic">Logo utama dan ikon browser (Favicon).</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
{{-- Logo --}}
<div class="space-y-4" x-data="{ photoName: null, photoPreview: null }">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest ms-4">Logo Utama (512x512)</label>
<div class="flex items-center gap-6 bg-slate-50 p-4 rounded-[2rem] border border-slate-100 transition-all hover:border-indigo-100">
<div class="w-20 h-20 bg-white rounded-2xl shadow-sm flex items-center justify-center overflow-hidden shrink-0 border border-slate-100">
<template x-if="!photoPreview">
@if(isset($settings['app_logo']))
<img src="{{ Storage::url($settings['app_logo']) }}" class="w-full h-full object-cover">
@else
<i class="fas fa-image text-slate-200 text-2xl"></i>
@endif
</template>
<template x-if="photoPreview">
<img :src="photoPreview" class="w-full h-full object-cover">
</template>
</div>
<div class="flex-grow">
<input type="file" name="app_logo" class="hidden" x-ref="logoInput"
@change="
photoName = $refs.logoInput.files[0].name;
const reader = new FileReader();
reader.onload = (e) => { photoPreview = e.target.result; };
reader.readAsDataURL($refs.logoInput.files[0]);
">
<button type="button" @click="$refs.logoInput.click()" class="text-[10px] font-black bg-white border border-slate-200 text-slate-600 px-4 py-2 rounded-full hover:bg-slate-100 transition-all">
GANTI LOGO
</button>
@error('app_logo') <p class="text-[9px] text-rose-500 mt-2 font-bold uppercase tracking-tighter">{{ $message }}</p> @enderror
</div>
</div>
</div>
{{-- Favicon --}}
<div class="space-y-4" x-data="{ favName: null, favPreview: null }">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest ms-4">Favicon (128x128)</label>
<div class="flex items-center gap-6 bg-slate-50 p-4 rounded-[2rem] border border-slate-100 transition-all hover:border-indigo-100">
<div class="w-20 h-20 bg-white rounded-2xl shadow-sm flex items-center justify-center overflow-hidden shrink-0 border border-slate-100">
<template x-if="!favPreview">
@if(isset($settings['app_favicon']))
<img src="{{ Storage::url($settings['app_favicon']) }}" class="w-full h-full object-cover">
@else
<i class="fas fa-cat text-slate-200 text-2xl"></i>
@endif
</template>
<template x-if="favPreview">
<img :src="favPreview" class="w-full h-full object-cover">
</template>
</div>
<div class="flex-grow">
<input type="file" name="app_favicon" class="hidden" x-ref="favInput"
@change="
favName = $refs.favInput.files[0].name;
const reader = new FileReader();
reader.onload = (e) => { favPreview = e.target.result; };
reader.readAsDataURL($refs.favInput.files[0]);
">
<button type="button" @click="$refs.favInput.click()" class="text-[10px] font-black bg-white border border-slate-200 text-slate-600 px-4 py-2 rounded-full hover:bg-slate-100 transition-all">
GANTI FAVICON
</button>
@error('app_favicon') <p class="text-[9px] text-rose-500 mt-2 font-bold uppercase tracking-tighter">{{ $message }}</p> @enderror
</div>
</div>
</div>
</div>
</div>
{{-- Card 2: Informasi Aplikasi --}}
<div class="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm p-8 md:p-10">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="md:col-span-1">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 ms-4">Nama Brand</label>
<input type="text" name="app_name" value="{{ $settings['app_name'] ?? 'Ily Cattery' }}"
class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-sm font-bold text-slate-700 focus:ring-2 focus:ring-indigo-100 shadow-inner">
@error('app_name') <p class="text-[9px] text-rose-500 mt-1 ms-4 font-bold uppercase">{{ $message }}</p> @enderror
</div>
<div class="md:col-span-1">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 ms-4">Versi Sistem</label>
<input type="text" name="app_version" value="{{ $settings['app_version'] ?? '1.0.0' }}"
class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-sm font-bold text-slate-700 focus:ring-2 focus:ring-indigo-100 shadow-inner">
@error('app_version') <p class="text-[9px] text-rose-500 mt-1 ms-4 font-bold uppercase">{{ $message }}</p> @enderror
</div>
<div class="md:col-span-1">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 ms-4">Slogan</label>
<input type="text" name="app_slogan" value="{{ $settings['app_slogan'] ?? '' }}"
class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-sm font-bold text-slate-700 focus:ring-2 focus:ring-indigo-100 shadow-inner" placeholder="E.g: Premium Quality">
</div>
</div>
</div>
{{-- Card 3: Status Layanan --}}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{{-- WA Status --}}
<div class="bg-white rounded-[2rem] border border-slate-100 shadow-sm p-6 flex items-center justify-between group hover:border-emerald-100 transition-all">
<div class="flex items-center gap-4">
<div class="w-12 h-12 {{ $waStatus['active'] ? 'bg-emerald-50 text-emerald-600' : 'bg-rose-50 text-rose-600' }} rounded-2xl flex items-center justify-center transition-transform group-hover:scale-110">
<i class="fab fa-whatsapp text-xl"></i>
</div>
<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' }}
</p>
</div>
</div>
@if(!$waStatus['active']) <i class="fas fa-exclamation-circle text-rose-300 animate-pulse"></i> @endif
</div>
{{-- Email Status --}}
<div class="bg-white rounded-[2rem] border border-slate-100 shadow-sm p-6 flex items-center justify-between group hover:border-indigo-100 transition-all">
<div class="flex items-center gap-4">
<div class="w-12 h-12 {{ $emailStatus['active'] ? 'bg-indigo-50 text-indigo-600' : 'bg-rose-50 text-rose-600' }} rounded-2xl flex items-center justify-center transition-transform group-hover:scale-110">
<i class="fas fa-envelope text-xl"></i>
</div>
<div>
<h4 class="text-[10px] font-black text-slate-400 uppercase tracking-widest">SMTP Email</h4>
<p class="text-xs font-black {{ $emailStatus['active'] ? 'text-indigo-700' : 'text-rose-700' }}">
{{ $emailStatus['active'] ? 'Service Online' : 'Konfigurasi Salah' }}
</p>
</div>
</div>
</div>
</div>
<div class="flex justify-end pt-4">
<button type="submit" class="bg-slate-900 hover:bg-indigo-600 text-white font-black py-5 px-14 rounded-full text-[11px] uppercase tracking-[0.3em] shadow-xl shadow-slate-200 transition-all active:scale-95 flex items-center gap-3">
<i class="fas fa-save text-sm"></i>
Simpan Perubahan
</button>
</div>
</form>
{{-- Log Aktivitas --}}
<div class="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm overflow-hidden">
<div class="px-10 py-6 border-b border-slate-50 bg-slate-50/50 flex items-center justify-between">
<div class="flex items-center gap-3">
<i class="fas fa-history text-indigo-400 text-xs"></i>
<h3 class="text-[10px] font-black text-slate-600 uppercase tracking-widest">Jejak Perubahan</h3>
</div>
<span class="text-[9px] font-bold text-slate-400 uppercase">Limit: 5 History</span>
</div>
<div class="divide-y divide-slate-50 px-6">
@forelse($logs as $log)
<div class="py-5 px-4 flex items-center justify-between hover:bg-slate-50/50 transition-all rounded-2xl">
<div class="flex items-center gap-5">
<div class="w-1.5 h-1.5 rounded-full bg-indigo-400 shadow-[0_0_8px_rgba(129,140,248,0.8)]"></div>
<div>
<p class="text-[11px] font-bold text-slate-600">
<span class="text-slate-900 uppercase tracking-tight">{{ $log->user->name }}</span>
<span class="mx-1 text-slate-400">—</span>
<span class="italic text-slate-500 font-medium">{{ $log->description }}</span>
</p>
</div>
</div>
<span class="text-[9px] font-black text-slate-300 uppercase tracking-tighter">{{ $log->created_at->diffForHumans() }}</span>
</div>
@empty
<div class="py-16 text-center">
<i class="fas fa-clipboard-list text-slate-100 text-4xl mb-4"></i>
<p class="text-[10px] font-black text-slate-300 uppercase tracking-widest">Belum ada catatan aktivitas</p>
</div>
@endforelse
</div>
</div>
</div>
</x-app-layout>
+250
View File
@@ -0,0 +1,250 @@
<x-app-layout>
<div class="max-w-4xl mx-auto space-y-6 pb-12">
{{-- Header & Back Button --}}
<div class="flex items-center gap-4">
<a href="{{ route('cats.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight uppercase">Tambah Kucing Baru</h2>
<p class="text-sm text-slate-500 font-medium italic tracking-tight">Registrasi identitas permanen & silsilah ke database Ilycats.</p>
</div>
</div>
{{-- Notifikasi Error --}}
@if ($errors->any())
<div class="p-5 bg-rose-50 border-2 border-rose-100 text-rose-600 rounded-[2rem] shadow-sm animate-fade-in">
<div class="flex items-center gap-3 mb-2">
<i class="fas fa-exclamation-circle text-lg"></i>
<h3 class="text-xs font-black uppercase tracking-widest">Terdapat Kesalahan Input:</h3>
</div>
<ul class="list-disc list-inside text-[11px] font-bold space-y-1 ml-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('cats.store') }}" method="POST" enctype="multipart/form-data" class="space-y-6">
@csrf
{{-- SECTION 1: IDENTITAS UTAMA --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-indigo-600 uppercase tracking-widest border-b border-slate-100 pb-4 mb-2 flex items-center gap-2 italic">
<i class="fas fa-id-card"></i> Identitas & Dokumen Resmi
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{{-- Nama Kucing --}}
<div class="space-y-2 md:col-span-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Lengkap Kucing (Sesuai Pedigree)</label>
<input type="text" name="nama_kucing" value="{{ old('nama_kucing') }}" required placeholder="Contoh: Earrin Cats Moonlight" class="w-full px-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm font-bold text-slate-800">
</div>
{{-- No. Pedigree, Microchip, & EMS (GRID 3 KOLOM) --}}
<div class="md:col-span-2 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">No. Pedigree</label>
<input type="text" name="pedigree" value="{{ old('pedigree') }}" placeholder="ICA.LO.xxxx" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-xs font-bold text-slate-800">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">No. Microchip</label>
<input type="text" name="microchip" value="{{ old('microchip') }}" placeholder="90011xxxx" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-xs font-bold text-slate-800">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Kode EMS</label>
<input type="text" name="kode_ems" value="{{ old('kode_ems') }}" placeholder="PER d 03" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:ring-2 focus:ring-indigo-500 transition-all text-xs font-bold text-slate-800">
</div>
</div>
{{-- Ras Kucing --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Ras / Breed</label>
<select name="ras" required class="w-full px-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm font-bold text-slate-800">
<option value="Persian" {{ old('ras') == 'Persian' ? 'selected' : '' }}>Persian</option>
<option value="British Shorthair" {{ old('ras') == 'British Shorthair' ? 'selected' : '' }}>British Shorthair</option>
<option value="Lainnya" {{ old('ras') == 'Lainnya' ? 'selected' : '' }}>Lainnya</option>
</select>
</div>
{{-- Tanggal Lahir --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Lahir</label>
<input type="date" name="tanggal_lahir" value="{{ old('tanggal_lahir') }}" class="w-full px-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-indigo-500 transition-all text-sm font-bold text-slate-800">
</div>
{{-- Registry --}}
<div class="md:col-span-2 space-y-3">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1 block">Federasi / Registry</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
@foreach(['ICA', 'CFA', 'WCF', 'FFF'] as $reg)
<label class="block cursor-pointer group">
<input type="checkbox" name="registry[]" value="{{ $reg }}"
{{ is_array(old('registry')) && in_array($reg, old('registry')) ? 'checked' : '' }}
class="sr-only peer">
<div class="py-4 text-center border-2 border-slate-100 rounded-2xl text-[10px] font-black text-slate-400
peer-checked:bg-indigo-600 peer-checked:text-white peer-checked:border-indigo-600
transition-all shadow-sm group-hover:border-slate-300 uppercase">
{{ $reg }}
</div>
</label>
@endforeach
</div>
</div>
{{-- Jenis Kelamin --}}
<div class="space-y-2 md:col-span-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jenis Kelamin</label>
<div class="flex gap-4">
<label class="flex-1 cursor-pointer">
<input type="radio" name="jenis_kelamin" value="Jantan" {{ old('jenis_kelamin', 'Jantan') == 'Jantan' ? 'checked' : '' }} class="hidden peer">
<div class="p-4 text-center border-2 border-slate-100 rounded-2xl text-xs font-black text-slate-400 peer-checked:bg-blue-50 peer-checked:border-blue-500 peer-checked:text-blue-600 transition-all shadow-sm">
<i class="fas fa-mars mr-2"></i> JANTAN (SIRE)
</div>
</label>
<label class="flex-1 cursor-pointer">
<input type="radio" name="jenis_kelamin" value="Betina" {{ old('jenis_kelamin') == 'Betina' ? 'checked' : '' }} class="hidden peer">
<div class="p-4 text-center border-2 border-slate-100 rounded-2xl text-xs font-black text-slate-400 peer-checked:bg-pink-50 peer-checked:border-pink-500 peer-checked:text-pink-600 transition-all shadow-sm">
<i class="fas fa-venus mr-2"></i> BETINA (DAM)
</div>
</label>
</div>
</div>
</div>
</div>
{{-- SECTION 2: GENETIK & LOKASI --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-indigo-600 uppercase tracking-widest border-b border-slate-100 pb-4 mb-2 flex items-center gap-2 italic">
<i class="fas fa-dna"></i> Penempatan & Silsilah Induk
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Induk Jantan</label>
<select name="induk_jantan_id" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-700">
<option value="">-- Tanpa Sire --</option>
@foreach($sires as $sire)
<option value="{{ $sire->id }}" {{ old('induk_jantan_id') == $sire->id ? 'selected' : '' }}>{{ $sire->nama_kucing }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Induk Betina</label>
<select name="induk_betina_id" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-700">
<option value="">-- Tanpa Dam --</option>
@foreach($dams as $dam)
<option value="{{ $dam->id }}" {{ old('induk_betina_id') == $dam->id ? 'selected' : '' }}>{{ $dam->nama_kucing }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Lokasi Kandang</label>
<select name="lokasi_kandang" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-black text-indigo-600 uppercase">
@foreach(['A1', 'A2', 'Lorong', 'E1', 'E2'] as $loc)
<option value="{{ $loc }}" {{ old('lokasi_kandang') == $loc ? 'selected' : '' }}>{{ $loc }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">PIC (Penanggung Jawab)</label>
<select name="pic_id" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-700">
@foreach($users as $user)
<option value="{{ $user->id }}" {{ old('pic_id', auth()->id()) == $user->id ? 'selected' : '' }}>{{ $user->name }}</option>
@endforeach
</select>
</div>
</div>
</div>
{{-- SECTION 3: JADWAL & PERTUMBUHAN --}}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{{-- Jadwal Medis --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-4">
<h4 class="text-xs font-black text-rose-600 uppercase tracking-widest border-b border-slate-100 pb-4 mb-2 flex items-center gap-2 italic">
<i class="fas fa-calendar-alt"></i> Jadwal Perawatan
</h4>
<div class="grid grid-cols-2 gap-4">
@foreach (['vaksin', 'obat_cacing', 'obat_kutu', 'grooming'] as $task)
<div class="space-y-1">
<label class="text-[9px] font-bold text-slate-400 uppercase ml-1">{{ str_replace('_', ' ', $task) }}</label>
<input type="date" name="jadwal_{{ $task }}" value="{{ old('jadwal_'.$task) }}" class="w-full p-3 bg-slate-50 border border-slate-200 rounded-xl text-[10px] font-black text-slate-700 focus:ring-rose-500">
</div>
@endforeach
</div>
</div>
{{-- Monitoring Berat --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-4">
<h4 class="text-xs font-black text-emerald-600 uppercase tracking-widest border-b border-slate-100 pb-4 mb-2 flex items-center gap-2 italic">
<i class="fas fa-weight-scale"></i> Vitalitas & Berat
</h4>
<div class="space-y-4">
<div>
<label class="text-[10px] font-black text-slate-400 uppercase ml-1">Berat Badan Awal</label>
<div class="relative mt-2">
<input type="number" step="0.01" name="berat_badan" value="{{ old('berat_badan') }}" placeholder="0.00" class="w-full pl-6 pr-14 py-5 bg-slate-50 border border-slate-200 rounded-3xl text-2xl font-black text-slate-800 focus:ring-emerald-500">
<span class="absolute right-6 top-1/2 -translate-y-1/2 text-xs font-black text-slate-400">KG</span>
</div>
</div>
</div>
</div>
</div>
{{-- SECTION 4: FOTO --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-4">
<h4 class="text-xs font-black text-slate-800 uppercase tracking-widest ml-1 mb-2">Unggah Foto Utama</h4>
<div class="relative group">
<input type="file" name="foto" id="foto" class="hidden" onchange="previewImage(event)">
<label for="foto" class="flex flex-col items-center justify-center border-4 border-dashed border-slate-100 rounded-[3rem] p-12 text-center cursor-pointer hover:border-indigo-200 hover:bg-indigo-50/20 transition-all duration-500 group">
<div id="preview-container" class="hidden mb-6">
<img id="image-preview" class="w-48 h-48 rounded-[2.5rem] object-cover shadow-2xl ring-8 ring-white">
</div>
<div id="upload-instruction" class="space-y-4">
<div class="w-20 h-20 bg-slate-50 rounded-3xl flex items-center justify-center mx-auto group-hover:scale-110 group-hover:bg-white transition-all duration-500 shadow-sm">
<i class="fas fa-camera-retro text-3xl text-slate-300 group-hover:text-indigo-500"></i>
</div>
<div>
<p class="text-xs text-slate-600 font-black tracking-widest uppercase">Pilih Foto Terbaik</p>
<p class="text-[10px] text-slate-400 mt-1 uppercase font-bold tracking-tighter italic">JPG, PNG, WEBP (Maks 2MB)</p>
</div>
</div>
</label>
</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"></i>
<span class="tracking-[0.2em] uppercase">Simpan Record Baru</span>
</button>
</div>
</form>
</div>
<style>
.animate-fade-in { animation: fadeIn 0.4s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
</style>
<script>
function previewImage(event) {
const reader = new FileReader();
reader.onload = function() {
const output = document.getElementById('image-preview');
const container = document.getElementById('preview-container');
const instruction = document.getElementById('upload-instruction');
output.src = reader.result;
container.classList.remove('hidden');
instruction.classList.add('hidden');
}
reader.readAsDataURL(event.target.files[0]);
}
</script>
</x-app-layout>
+245
View File
@@ -0,0 +1,245 @@
<x-app-layout>
<div class="max-w-4xl mx-auto space-y-6 pb-12">
{{-- Header & Back Button --}}
<div class="flex items-center gap-4">
<a href="{{ route('cats.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Profil: {{ $cat->nama_kucing }}</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">ID Pasien #{{ str_pad($cat->id, 4, '0', STR_PAD_LEFT) }} • Update Identitas</p>
</div>
</div>
{{-- Notifikasi Error --}}
@if ($errors->any())
<div class="p-5 bg-rose-50 border-2 border-rose-100 text-rose-600 rounded-[2rem] shadow-sm animate-fade-in">
<div class="flex items-center gap-3 mb-2">
<i class="fas fa-exclamation-circle text-lg"></i>
<h3 class="text-xs font-black uppercase tracking-widest">Gagal Memperbarui Data:</h3>
</div>
<ul class="list-disc list-inside text-[11px] font-bold space-y-1 ml-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('cats.update', $cat->id) }}" method="POST" enctype="multipart/form-data" class="space-y-6">
@csrf
@method('PATCH')
{{-- SECTION 1: IDENTITAS UTAMA & DOKUMEN --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-8">
<div class="flex items-center gap-4 mb-2 border-b border-slate-100 pb-4">
<div class="w-8 h-8 bg-indigo-600 text-white rounded-xl flex items-center justify-center text-xs shadow-lg">
<i class="fas fa-id-card"></i>
</div>
<h4 class="font-black text-slate-800 uppercase tracking-tighter text-lg">Identitas & Dokumen Resmi</h4>
</div>
<div class="flex flex-col md:flex-row gap-6 items-start">
{{-- FOTO SECTION (THUMBNAIL) --}}
<div class="relative group self-center md:self-start flex-shrink-0">
<div class="w-24 h-24 rounded-3xl bg-slate-50 border-2 border-slate-200 overflow-hidden ring-4 ring-indigo-50 shadow-md flex items-center justify-center">
<img id="image-preview" src="{{ $cat->foto_url }}" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110">
</div>
<label for="foto" class="absolute -bottom-2 -right-2 w-9 h-9 bg-indigo-600 text-white rounded-xl border-4 border-white cursor-pointer shadow-sm hover:scale-110 transition-transform active:scale-95 flex items-center justify-center">
<i class="fas fa-camera text-[10px]"></i>
<input type="file" name="foto" id="foto" class="hidden" accept="image/*" onchange="previewImage(event)">
</label>
</div>
{{-- Form Identitas Grid --}}
<div class="flex-1 grid grid-cols-1 md:grid-cols-2 gap-6 w-full">
<div class="space-y-2 md:col-span-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Lengkap Kucing</label>
<input type="text" name="nama_kucing" value="{{ old('nama_kucing', $cat->nama_kucing) }}" required class="w-full px-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
</div>
{{-- PERBAIKAN: GRID IDENTITAS (PEDIGREE, MICROCHIP, EMS) --}}
<div class="md:col-span-2 grid grid-cols-1 md:grid-cols-3 gap-4 border-y border-slate-50 py-6 my-2">
{{-- Input Pedigree --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">No. Pedigree</label>
<input type="text" name="pedigree" value="{{ old('pedigree', $cat->pedigree) }}" placeholder="ICA.LO.xxx"
class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:ring-2 focus:ring-indigo-500 text-xs font-bold text-slate-800">
</div>
{{-- Input Microchip --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">No. Microchip</label>
<input type="text" name="microchip" value="{{ old('microchip', $cat->microchip) }}" placeholder="9001xxxx"
class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:ring-2 focus:ring-indigo-500 text-xs font-bold text-slate-800">
</div>
{{-- Input Kode EMS (Highlight Indigo) --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-indigo-600 uppercase tracking-widest ml-1 flex items-center gap-2">
<i class="fas fa-dna text-[10px]"></i> Kode EMS
</label>
<input type="text" name="kode_ems" value="{{ old('kode_ems', $cat->kode_ems) }}" placeholder="PER d 03"
class="w-full px-4 py-3 bg-indigo-50/30 border border-indigo-100 rounded-xl focus:ring-2 focus:ring-indigo-500 text-xs font-black text-indigo-700 uppercase">
</div>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Ras / Breed</label>
<select name="ras" required class="w-full px-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500">
@foreach(['Persian', 'British Shorthair', 'Scottish Fold', 'Maine Coon', 'Munchkin', 'Lainnya'] as $ras)
<option value="{{ $ras }}" {{ old('ras', $cat->ras) == $ras ? 'selected' : '' }}>{{ $ras }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Lahir</label>
<input type="date" name="tanggal_lahir" value="{{ old('tanggal_lahir', $cat->tanggal_lahir?->format('Y-m-d')) }}" class="w-full px-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500">
</div>
{{-- Registry / Federasi --}}
<div class="space-y-3 md:col-span-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Registry / Federasi</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
@foreach(['ICA', 'CFA', 'WCF', 'FFF'] as $reg)
@php
$isChecked = (is_array(old('registry')) && in_array($reg, old('registry'))) ||
(is_array($cat->registry) && in_array($reg, $cat->registry));
@endphp
<label class="block cursor-pointer group">
<input type="checkbox" name="registry[]" value="{{ $reg }}" {{ $isChecked ? 'checked' : '' }} class="sr-only peer">
<div class="py-4 text-center border-2 border-slate-100 rounded-2xl text-[10px] font-black text-slate-400 peer-checked:bg-indigo-600 peer-checked:text-white peer-checked:border-indigo-600 transition-all shadow-sm group-hover:border-slate-300">
{{ $reg }}
</div>
</label>
@endforeach
</div>
</div>
{{-- Jenis Kelamin --}}
<div class="space-y-2 md:col-span-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jenis Kelamin</label>
<div class="flex gap-4">
<label class="flex-1 cursor-pointer group">
<input type="radio" name="jenis_kelamin" value="Jantan" {{ old('jenis_kelamin', $cat->jenis_kelamin) == 'Jantan' ? 'checked' : '' }} class="sr-only peer">
<div class="p-4 text-center border-2 border-slate-100 rounded-2xl text-xs font-black text-slate-400 peer-checked:bg-blue-50 peer-checked:border-blue-500 peer-checked:text-blue-600 transition-all shadow-sm group-hover:border-slate-200">
<i class="fas fa-mars mr-2"></i> JANTAN
</div>
</label>
<label class="flex-1 cursor-pointer group">
<input type="radio" name="jenis_kelamin" value="Betina" {{ old('jenis_kelamin', $cat->jenis_kelamin) == 'Betina' ? 'checked' : '' }} class="sr-only peer">
<div class="p-4 text-center border-2 border-slate-100 rounded-2xl text-xs font-black text-slate-400 peer-checked:bg-pink-50 peer-checked:border-pink-500 peer-checked:text-pink-600 transition-all shadow-sm group-hover:border-slate-200">
<i class="fas fa-venus mr-2"></i> BETINA
</div>
</label>
</div>
</div>
</div>
</div>
</div>
{{-- SECTION 2: GENETIK & LOKASI --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-indigo-600 uppercase tracking-widest border-b border-slate-100 pb-4 mb-2 flex items-center gap-2 italic">
<i class="fas fa-dna text-sm"></i> Penempatan & Silsilah Induk
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Sire (Bapak)</label>
<select name="induk_jantan_id" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-700 focus:ring-2 focus:ring-indigo-500">
<option value="">-- Tidak Diketahui --</option>
@foreach($sires as $sire)
<option value="{{ $sire->id }}" {{ old('induk_jantan_id', $cat->induk_jantan_id) == $sire->id ? 'selected' : '' }}>{{ $sire->nama_kucing }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Dam (Ibu)</label>
<select name="induk_betina_id" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-700 focus:ring-2 focus:ring-indigo-500">
<option value="">-- Tidak Diketahui --</option>
@foreach($dams as $dam)
<option value="{{ $dam->id }}" {{ old('induk_betina_id', $cat->induk_betina_id) == $dam->id ? 'selected' : '' }}>{{ $dam->nama_kucing }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Lokasi Kandang</label>
<select name="lokasi_kandang" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-black text-indigo-600 uppercase focus:ring-2 focus:ring-indigo-500">
@foreach(['A1', 'A2', 'Lorong', 'E1', 'E2', 'Karantina'] as $loc)
<option value="{{ $loc }}" {{ old('lokasi_kandang', $cat->lokasi_kandang) == $loc ? 'selected' : '' }}>{{ $loc }}</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">PIC Bertugas</label>
<select name="pic_id" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-[11px] font-bold text-slate-700 focus:ring-2 focus:ring-indigo-500">
@foreach($users as $user)
<option value="{{ $user->id }}" {{ old('pic_id', $cat->pic_id) == $user->id ? 'selected' : '' }}>{{ $user->name }}</option>
@endforeach
</select>
</div>
</div>
</div>
{{-- SECTION 3: JADWAL & DATA FISIK --}}
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2 bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm">
<div class="flex items-center gap-3 mb-6 border-b border-slate-100 pb-4">
<div class="w-7 h-7 bg-rose-50 text-rose-600 rounded-lg flex items-center justify-center text-[10px]">
<i class="fas fa-calendar-alt"></i>
</div>
<h4 class="text-xs font-black text-rose-600 uppercase tracking-widest italic">Jadwal Perawatan</h4>
</div>
<div class="grid grid-cols-2 gap-4">
@foreach(['jadwal_vaksin' => 'Vaksin', 'jadwal_obat_cacing' => 'Obat Cacing', 'jadwal_obat_kutu' => 'Obat Kutu', 'jadwal_grooming' => 'Grooming'] as $key => $label)
<div>
<label class="text-[9px] font-bold text-slate-400 uppercase ml-1">{{ $label }}</label>
<input type="date" name="{{ $key }}" value="{{ old($key, $cat->$key ? $cat->$key->format('Y-m-d') : '') }}" class="w-full mt-1 p-3 bg-slate-50 border border-slate-200 rounded-xl text-[10px] font-black text-slate-700 focus:ring-2 focus:ring-rose-200 transition-all">
</div>
@endforeach
</div>
</div>
<div class="space-y-6">
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm">
<h4 class="text-xs font-black text-emerald-600 uppercase tracking-widest border-b border-slate-100 pb-4 mb-6 italic flex items-center gap-2">
<i class="fas fa-weight-scale text-sm"></i> Vitalitas
</h4>
<label class="text-[10px] font-bold text-slate-400 uppercase ml-1">Berat Badan</label>
<div class="relative mt-2">
<input type="number" step="0.01" name="berat_badan" value="{{ old('berat_badan', $cat->berat_badan) }}" class="w-full pl-5 pr-14 py-4 bg-slate-50 border border-slate-200 rounded-[1.5rem] text-2xl font-black text-slate-800 focus:ring-2 focus:ring-emerald-500 transition-all">
<span class="absolute right-5 top-1/2 -translate-y-1/2 text-xs font-black text-slate-400">KG</span>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm border-l-4 border-l-rose-500">
<label class="text-[10px] font-black text-rose-600 uppercase tracking-widest ml-1">Kondisi Kesehatan</label>
<textarea name="riwayat_sakit_terakhir" class="w-full mt-2 p-4 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-700 h-24 focus:ring-2 focus:ring-rose-200 transition-all">{{ old('riwayat_sakit_terakhir', $cat->riwayat_sakit_terakhir) }}</textarea>
</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 transition-all active:scale-[0.98] flex items-center justify-center gap-4 group">
<i class="fas fa-cloud-upload-alt text-lg group-hover:animate-bounce transition-transform"></i>
<span class="tracking-[0.2em] uppercase">Simpan Perubahan Data Ilycats</span>
</button>
</form>
</div>
<script>
function previewImage(event) {
const reader = new FileReader();
const preview = document.getElementById('image-preview');
reader.onload = function() {
if(reader.readyState === 2) {
preview.src = reader.result;
}
}
reader.readAsDataURL(event.target.files[0]);
}
</script>
</x-app-layout>
+185
View File
@@ -0,0 +1,185 @@
<x-app-layout>
<div class="space-y-6 pb-8">
{{-- 1. STATS OVERVIEW --}}
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<x-stats-card title="Total Kucing" :value="$totalCats" icon="fa-cat" color="indigo" />
<x-stats-card title="Kucing Sehat" :value="$totalCats - $sickCats" icon="fa-heart" color="emerald" />
<x-stats-card title="Dalam Perawatan" :value="$sickCats" icon="fa-stethoscope" color="rose" />
</div>
{{-- 2. ACTION HEADER --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1 uppercase">Manajemen Kucing</h2>
<p class="text-[10px] text-slate-400 font-bold uppercase tracking-[0.2em]">Cattery Management System • Ilycats</p>
</div>
{{-- PROTEKSI TOMBOL CREATE --}}
@can('check-access', ['cats', 'create'])
<div class="flex items-center gap-3">
<a href="{{ route('cats.create') }}" class="inline-flex items-center px-6 py-3 bg-indigo-600 text-white text-xs font-black rounded-2xl shadow-lg shadow-indigo-100 hover:bg-indigo-700 transition-all active:scale-95">
<i class="fas fa-plus-circle mr-2"></i> REGISTRASI KUCING BARU
</a>
</div>
@endcan
</div>
{{-- 3. TABLE CONTAINER --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] overflow-hidden shadow-sm">
{{-- SEARCH & QUICK FILTER --}}
<div class="p-6 bg-slate-50/50 border-b border-slate-100 flex flex-col md:flex-row justify-between gap-4">
<form action="{{ route('cats.index') }}" method="GET" class="relative w-full md:w-1/3">
<i class="fas fa-search absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-xs"></i>
<input type="text" name="search" value="{{ request('search') }}" placeholder="Cari nama, ras, atau microchip..."
class="w-full pl-12 pr-4 py-3 bg-white border border-slate-200 rounded-xl text-xs font-bold focus:ring-2 focus:ring-indigo-500 transition-all shadow-sm">
</form>
<div class="flex items-center p-1 bg-slate-100 rounded-xl shrink-0">
<a href="{{ route('cats.index') }}" class="px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest {{ !request('status') ? 'bg-white text-indigo-600 shadow-sm' : 'text-slate-500 hover:text-slate-700' }}">Semua</a>
<a href="{{ route('cats.index', ['status' => 'Active']) }}" class="px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest {{ request('status') == 'Active' ? 'bg-white text-emerald-600 shadow-sm' : 'text-slate-500 hover:text-slate-700' }}">Dewasa</a>
<a href="{{ route('cats.index', ['status' => 'Kitten']) }}" class="px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest {{ request('status') == 'Kitten' ? 'bg-white text-amber-600 shadow-sm' : 'text-slate-500 hover:text-slate-700' }}">Kitten</a>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead class="bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100">
<tr>
<th class="px-6 py-5">Informasi Kucing</th>
<th class="px-6 py-5">Usia & Silsilah</th>
<th class="px-6 py-5 text-center">Lokasi Kandang</th>
<th class="px-6 py-5 text-center">Penanggung Jawab</th>
<th class="px-6 py-5 text-center">Monitoring Berat</th>
<th class="px-6 py-5 text-right">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 font-medium text-slate-700">
@forelse($cats as $cat)
<tr class="hover:bg-slate-50/80 transition-all group">
<td class="px-6 py-4">
<div class="flex items-center gap-4">
<div class="relative shrink-0">
<div class="w-14 h-14 rounded-2xl bg-slate-100 overflow-hidden ring-4 ring-white shadow-sm transition-transform group-hover:scale-105">
<img src="{{ $cat->foto_url }}" class="w-full h-full object-cover">
</div>
@if($cat->status === 'Kitten')
<div class="absolute -top-1 -right-1 w-4 h-4 bg-amber-500 border-2 border-white rounded-full shadow-sm animate-pulse" title="Kitten"></div>
@endif
</div>
<div>
<div class="flex items-center gap-2 mb-1">
<a href="{{ route('cats.show', $cat->id) }}" class="text-sm font-black text-slate-800 hover:text-indigo-600 transition-colors uppercase tracking-tight">{{ $cat->nama_kucing }}</a>
{!! $cat->gender_icon !!}
</div>
<div class="flex flex-col gap-1.5">
<div class="flex items-center gap-1.5">
<span class="px-2 py-0.5 bg-indigo-50 text-indigo-500 rounded text-[9px] font-black uppercase">{{ $cat->ras }}</span>
<span class="text-slate-300">•</span>
{!! $cat->registry_badges !!}
</div>
</div>
</div>
</div>
</td>
<td class="px-6 py-4">
<div class="flex flex-col">
<span class="text-[11px] font-black text-slate-700">{{ $cat->age }}</span>
<div class="flex items-center gap-1 mt-0.5">
<i class="fas fa-birthday-cake text-[8px] text-slate-300"></i>
<span class="text-[9px] text-slate-400 font-bold italic">{{ $cat->tanggal_lahir?->format('d M Y') ?? 'N/A' }}</span>
</div>
@if($cat->induk_betina_id)
<span class="text-[8px] text-rose-400 font-bold uppercase mt-1 tracking-tighter">Dam: {{ $cat->dam->nama_kucing }}</span>
@endif
</div>
</td>
<td class="px-6 py-4 text-center">
<div class="inline-flex items-center gap-1.5 px-3 py-1.5 bg-indigo-50/50 text-indigo-600 rounded-xl text-[10px] font-black border border-indigo-100 uppercase italic">
<i class="fas fa-door-closed text-[9px]"></i>
{{ $cat->lokasi_kandang ?? 'Global' }}
</div>
</td>
<td class="px-6 py-4 text-center">
<div class="flex flex-col items-center">
<span class="text-[10px] font-black text-slate-600 uppercase tracking-tighter">{{ $cat->pic->name ?? 'System' }}</span>
<span class="text-[8px] text-slate-400 font-bold uppercase tracking-widest">In Charge</span>
</div>
</td>
<td class="px-6 py-4 text-center">
@php $isOverdue = $cat->updated_at->diffInDays(now()) >= 3; @endphp
<div class="flex flex-col items-center">
<div class="flex items-baseline gap-0.5 {{ $isOverdue ? 'text-rose-600' : 'text-emerald-600' }}">
<span class="text-sm font-black">{{ number_format($cat->berat_badan, 2) }}</span>
<span class="text-[9px] font-bold">KG</span>
</div>
@if($isOverdue)
<div class="flex items-center gap-1 mt-1 text-rose-400 animate-pulse">
<i class="fas fa-exclamation-triangle text-[8px]"></i>
<span class="text-[7px] font-black uppercase tracking-widest">Update!</span>
</div>
@endif
</div>
</td>
<td class="px-6 py-4 text-right">
<div class="flex justify-end items-center gap-2">
{{-- PROTEKSI TOMBOL READ --}}
@can('check-access', ['cats', 'read'])
<a href="{{ route('cats.show', $cat->id) }}"
class="w-9 h-9 flex items-center justify-center bg-indigo-50 text-indigo-600 rounded-xl hover:bg-indigo-600 hover:text-white transition-all shadow-sm group-hover:shadow-indigo-100"
title="Detail Medis">
<i class="fas fa-heartbeat text-xs"></i>
</a>
@endcan
{{-- PROTEKSI TOMBOL EDIT/UPDATE --}}
@can('check-access', ['cats', 'update'])
<a href="{{ route('cats.edit', $cat->id) }}"
class="w-9 h-9 flex items-center justify-center bg-amber-50 text-amber-600 rounded-xl hover:bg-amber-600 hover:text-white transition-all shadow-sm"
title="Edit Profil">
<i class="fas fa-pencil-alt text-xs"></i>
</a>
@endcan
{{-- PROTEKSI TOMBOL DELETE --}}
@can('check-access', ['cats', 'delete'])
<button type="button"
onclick="confirmDelete('delete-form-{{ $cat->id }}', '{{ $cat->nama_kucing }}')"
class="w-9 h-9 flex items-center justify-center bg-rose-50 text-rose-600 rounded-xl hover:bg-rose-500 hover:text-white transition-all shadow-sm"
title="Hapus">
<i class="fas fa-trash text-xs"></i>
</button>
<form id="delete-form-{{ $cat->id }}" action="{{ route('cats.destroy', $cat->id) }}" method="POST" class="hidden">
@csrf @method('DELETE')
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-6 py-32 text-center bg-slate-50/30">
<div class="flex flex-col items-center">
<i class="fas fa-cat text-5xl text-slate-100 mb-4 animate-bounce"></i>
<h3 class="text-slate-400 font-black uppercase text-xs tracking-[0.4em]">Belum Ada Data Kucing</h3>
<p class="text-slate-300 text-[10px] font-bold mt-2 uppercase">Silakan klik tombol registrasi untuk memulai</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="p-8 border-t border-slate-100 bg-slate-50/30">
{{ $cats->links() }}
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,153 @@
<!DOCTYPE html>
<html>
<head>
<style>
@page { margin: 0; }
body {
font-family: 'Helvetica', 'Arial', sans-serif;
background-color: #f1f5f9;
margin: 0; padding: 0;
text-align: center;
}
.page-wrapper { padding-top: 30px; }
.card {
display: inline-block;
width: 260pt;
background: #ffffff;
border-radius: 30px;
padding: 0; /* Padding dipindah ke dalam agar aksen bar bisa full width */
text-align: left;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
overflow: hidden; /* Memastikan border-radius memotong aksen bar */
border: 1px solid #e2e8f0;
}
/* Akses Bar Warna di Atas */
.accent-bar {
height: 8px;
background: linear-gradient(to right, #4f46e5, #818cf8);
width: 100%;
}
.card-content { padding: 25px; }
.header {
text-align: center;
border-bottom: 2px solid #f8fafc;
padding-bottom: 15px;
margin-bottom: 20px;
}
.brand {
font-size: 11pt;
font-weight: 900;
color: #4f46e5;
text-transform: uppercase;
letter-spacing: 2px;
}
.cat-name {
font-size: 22pt;
font-weight: bold;
color: #0f172a;
margin-top: 4px;
letter-spacing: -1px;
}
.info-table { width: 100%; border-collapse: collapse; }
.label {
font-size: 7.5pt;
font-weight: 900;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 2px;
}
.value {
font-size: 11pt;
font-weight: bold;
color: #334155;
padding-bottom: 15px;
}
.qr-section {
text-align: center;
margin-top: 10px;
padding: 20px;
background-color: #f8fafc;
border-radius: 20px;
}
.qr-image { margin-bottom: 8px; }
.qr-text {
font-size: 8pt;
font-weight: 800;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.footer {
font-size: 7pt;
color: #94a3b8;
text-align: center;
margin-top: 20px;
font-style: italic;
}
</style>
</head>
<body>
<div class="page-wrapper">
<div class="card">
<div class="accent-bar"></div>
<div class="card-content">
<div class="header">
<div class="brand">ILY Aregau Cattery</div>
<div class="cat-name">{{ $cat->nama_kucing }}</div>
</div>
<table class="info-table">
<tr>
<td width="55%">
<div class="label">Ras / Breed</div>
<div class="value">{{ $cat->ras }}</div>
</td>
<td>
<div class="label">ID Kucing</div>
<div class="value">#{{ str_pad($cat->id, 4, '0', STR_PAD_LEFT) }}</div>
</td>
</tr>
<tr>
<td>
<div class="label">Jenis Kelamin</div>
<div class="value">{{ $cat->jenis_kelamin }}</div>
</td>
<td>
<div class="label">Usia</div>
<div class="value">{{ $cat->age }}</div>
</td>
</tr>
</table>
<div class="qr-section">
<div class="qr-image">
{{-- FIX: Base64 SVG yang sudah Anda buat di Controller --}}
<img src="data:image/svg+xml;base64,{{ $qrCode }}" width="90">
</div>
<div class="qr-text">Scan Medical History</div>
</div>
<div class="footer">
Generated by ILY Cattery Management System<br>
{{ now()->format('d M Y H:i') }}
</div>
</div>
</div>
</div>
</body>
</html>
+352
View File
@@ -0,0 +1,352 @@
<x-app-layout>
@push('styles')
<style>
canvas { max-width: 100% !important; }
.custom-scrollbar::-webkit-scrollbar { width: 4px; }
.custom-scrollbar::-webkit-scrollbar-track { background: #f1f5f9; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
</style>
@endpush
<div class="max-w-[1600px] mx-auto space-y-8 pb-12">
{{-- 1. Header Section --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div class="flex items-center gap-4">
<a href="{{ route('cats.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<div class="flex items-center gap-3">
<h2 class="text-3xl font-black text-slate-800 tracking-tight leading-none">{{ $cat->nama_kucing }}</h2>
{!! $cat->gender_icon !!}
</div>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-widest mt-1">Medical Record #FE-{{ str_pad($cat->id, 5, '0', STR_PAD_LEFT) }}</p>
</div>
</div>
<div class="flex items-center gap-3">
<a href="{{ route('cats.pdf', $cat->id) }}" target="_blank" class="inline-flex items-center px-6 py-3.5 bg-rose-600 text-white text-[10px] font-black rounded-2xl hover:bg-rose-700 transition-all shadow-lg shadow-rose-100 active:scale-95">
<i class="fas fa-file-pdf mr-2 text-sm"></i> CETAK MEDICAL PASS
</a>
{{-- PROTEKSI TOMBOL UPDATE: Hanya muncul jika memiliki izin Update ATAU Delete --}}
@if(Gate::allows('check-access', ['cats', 'update']) || Gate::allows('check-access', ['cats', 'delete']))
<a href="{{ route('cats.edit', $cat->id) }}" class="inline-flex items-center px-6 py-3.5 bg-white border border-slate-200 text-slate-600 text-[10px] font-black rounded-2xl hover:bg-slate-50 transition-all shadow-sm active:scale-95">
<i class="fas fa-edit mr-2 text-indigo-500 text-sm"></i> UPDATE DATA
</a>
@endif
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
{{-- Column 1: Profile & Core Identity --}}
<div class="space-y-6">
<div class="bg-white border border-slate-200 rounded-[3rem] p-8 shadow-sm text-center relative overflow-hidden group">
<div class="absolute top-0 left-0 p-6">
{!! $cat->status_badge !!}
</div>
<div class="w-48 h-48 mx-auto rounded-[3rem] bg-slate-50 overflow-hidden ring-8 ring-slate-50 shadow-xl mb-6 transition-transform group-hover:scale-105 duration-500">
<img src="{{ $cat->foto_url }}" class="w-full h-full object-cover">
</div>
<h3 class="text-2xl font-black text-slate-800 mb-6 tracking-tighter uppercase">{{ $cat->nama_kucing }}</h3>
<div class="grid grid-cols-2 gap-4 text-left border-t border-slate-100 pt-6">
<div class="bg-slate-50/50 p-4 rounded-2xl border border-slate-100">
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest mb-2">Registry</p>
<div class="flex flex-wrap gap-1">
{!! $cat->registry_badges !!}
</div>
</div>
<div class="bg-slate-50/50 p-4 rounded-2xl border border-slate-100 text-center">
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest mb-1">Umur Saat Ini</p>
<p class="text-xs font-black text-slate-700 tracking-tighter">{{ $cat->age }}</p>
</div>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[3rem] p-8 shadow-sm space-y-5">
<h4 class="text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-50 pb-3">Informasi Teknis</h4>
<div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-xs font-bold text-slate-400 uppercase">Microchip</span>
<span class="text-xs font-black text-slate-700 font-mono">{{ $cat->microchip ?? 'Unchipped' }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs font-bold text-slate-400 uppercase">Pedigree No.</span>
<span class="text-xs font-black text-slate-700">{{ $cat->pedigree ?? '-' }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs font-bold text-slate-400 uppercase">Lokasi Kandang</span>
<span class="px-2 py-0.5 bg-indigo-50 text-indigo-600 rounded text-[10px] font-black uppercase tracking-widest">{{ $cat->lokasi_kandang ?? 'Global' }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-xs font-bold text-slate-400 uppercase">PIC Bertugas</span>
<span class="text-xs font-black text-slate-700">{{ $cat->pic?->name ?? 'System' }}</span>
</div>
</div>
@php $isOverdue = $cat->updated_at->diffInDays(now()) >= 3; @endphp
<div class="mt-6 p-5 {{ $isOverdue ? 'bg-rose-50 border-rose-100' : 'bg-emerald-50 border-emerald-100' }} border rounded-3xl">
<div class="flex justify-between items-center mb-1">
<span class="text-[9px] font-black uppercase {{ $isOverdue ? 'text-rose-600' : 'text-emerald-600' }}">Monitoring Berat</span>
<span class="text-[9px] font-bold text-slate-400 uppercase italic">{{ $cat->updated_at->diffForHumans() }}</span>
</div>
<div class="flex items-baseline gap-1">
<span class="text-2xl font-black text-slate-800">{{ number_format($cat->berat_badan, 2) }}</span>
<span class="text-xs font-black text-slate-400 uppercase">Kg</span>
</div>
</div>
</div>
</div>
{{-- Column 2: Lineage & Health Routine --}}
<div class="space-y-8">
<div class="bg-white border border-slate-200 rounded-[3rem] p-8 shadow-sm">
<h4 class="text-xs font-black text-slate-800 uppercase tracking-widest border-b border-slate-100 pb-4 mb-6">Parental Intelligence</h4>
<div class="space-y-4">
{{-- SIRE --}}
<div class="p-4 bg-indigo-50/30 border border-indigo-100 rounded-3xl flex items-center gap-4 hover:bg-indigo-50 transition-colors cursor-default">
<div class="w-12 h-12 bg-white border border-indigo-100 text-indigo-600 rounded-2xl flex items-center justify-center shadow-sm">
<i class="fas fa-mars text-lg"></i>
</div>
<div class="flex-1">
<p class="text-[9px] font-black text-indigo-400 uppercase tracking-widest">Sire (Pejantan)</p>
<p class="text-sm font-black text-slate-700 uppercase leading-none mt-1">{{ $cat->sire->nama_kucing }}</p>
</div>
</div>
{{-- DAM --}}
<div class="p-4 bg-rose-50/30 border border-rose-100 rounded-3xl flex items-center gap-4 hover:bg-rose-50 transition-colors cursor-default">
<div class="w-12 h-12 bg-white border border-rose-100 text-rose-600 rounded-2xl flex items-center justify-center shadow-sm">
<i class="fas fa-venus text-lg"></i>
</div>
<div class="flex-1">
<p class="text-[9px] font-black text-rose-400 uppercase tracking-widest">Dam (Betina)</p>
<p class="text-sm font-black text-slate-700 uppercase leading-none mt-1">{{ $cat->dam->nama_kucing }}</p>
</div>
</div>
</div>
</div>
<div class="bg-slate-900 rounded-[3rem] p-8 text-white shadow-2xl relative overflow-hidden">
<h4 class="text-xs font-black uppercase tracking-widest border-b border-white/10 pb-4 mb-6 italic">Routine Medical Calendar</h4>
<div class="grid grid-cols-1 gap-4 relative z-10">
@foreach([
['icon' => 'fa-syringe', 'label' => 'Vaksin', 'field' => 'jadwal_vaksin'],
['icon' => 'fa-bug', 'label' => 'Obat Cacing', 'field' => 'jadwal_obat_cacing'],
['icon' => 'fa-shield-virus', 'label' => 'Obat Kutu', 'field' => 'jadwal_obat_kutu'],
['icon' => 'fa-bath', 'label' => 'Grooming', 'field' => 'jadwal_grooming']
] as $item)
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5 hover:bg-white/10 transition-all">
<div class="flex items-center gap-3">
<i class="fas {{ $item['icon'] }} text-xs text-slate-400"></i>
<span class="text-[10px] font-black uppercase tracking-widest text-slate-300">{{ $item['label'] }}</span>
</div>
<span class="text-[11px] font-black {{ $cat->{$item['field']} && $cat->{$item['field']}->isPast() ? 'text-rose-400' : 'text-white' }}">
{{ $cat->{$item['field']} ? $cat->{$item['field']}->format('d M Y') : 'NOT SET' }}
</span>
</div>
@endforeach
</div>
<i class="fas fa-paw absolute -bottom-10 -right-10 text-[12rem] text-white/5 -rotate-12"></i>
</div>
</div>
{{-- Column 3: Growth Analytics & Actions --}}
<div class="space-y-8">
<div class="bg-white border border-slate-200 rounded-[3rem] p-8 shadow-sm">
<div class="flex items-center justify-between mb-8">
<div>
<h4 class="font-black text-slate-800 uppercase tracking-tighter text-lg leading-none">Analisis Berat</h4>
<p class="text-[10px] text-slate-400 font-black uppercase mt-1">Growth Index (Kg)</p>
</div>
<div class="w-12 h-12 bg-emerald-50 text-emerald-500 rounded-2xl flex items-center justify-center">
<i class="fas fa-chart-line text-lg"></i>
</div>
</div>
<div class="relative h-[250px]">
<canvas id="weightChart"></canvas>
</div>
</div>
{{-- PROTEKSI QUICK ACTION: Hanya jika bisa Create Treatment --}}
@can('check-access', ['treatments', 'create'])
<div class="bg-indigo-600 rounded-[3rem] p-8 text-white shadow-xl shadow-indigo-100 flex flex-col justify-between h-[250px] group overflow-hidden relative">
<div class="relative z-10">
<h4 class="text-xs font-black uppercase tracking-widest mb-2 border-b border-white/20 pb-3">Quick Action</h4>
<p class="text-[10px] text-indigo-100/60 leading-relaxed italic uppercase font-bold tracking-tighter">Gunakan menu ini untuk mencatat tindakan medis segera.</p>
</div>
<a href="{{ route('treatments.create', ['cat_id' => $cat->id]) }}" class="relative z-10 flex items-center justify-center gap-3 p-5 bg-white text-indigo-600 rounded-3xl font-black uppercase text-xs shadow-xl hover:scale-105 transition-all active:scale-95">
<i class="fas fa-file-medical-alt text-lg"></i> Tambah Rekam Medis
</a>
<i class="fas fa-heartbeat absolute -bottom-10 -left-10 text-[10rem] text-white/5"></i>
</div>
@endcan
</div>
</div>
{{-- Log Section: Medical History Timeline --}}
<div class="bg-white border border-slate-200 rounded-[3rem] overflow-hidden shadow-sm">
<div class="p-8 border-b border-slate-100 bg-slate-50/50 flex justify-between items-center">
<div class="flex items-center gap-3">
<i class="fas fa-history text-slate-400"></i>
<h4 class="font-black text-slate-800 uppercase tracking-tighter text-lg">Historical Medical Log</h4>
</div>
<span class="text-[10px] font-black text-indigo-600 bg-white border border-indigo-100 px-5 py-2 rounded-full shadow-sm">{{ $cat->treatments->count() }} REKAMAN</span>
</div>
<div class="p-10 max-h-[800px] overflow-y-auto custom-scrollbar">
<div class="relative space-y-12">
<div class="absolute left-[11px] top-2 bottom-2 w-0.5 bg-slate-100"></div>
@forelse($cat->treatments->sortByDesc('tanggal') as $treatment)
<div class="relative flex items-start gap-8 group">
<div class="z-10 w-6 h-6 rounded-full bg-white border-4 border-indigo-500 ring-8 ring-indigo-50 shadow-md"></div>
<div class="flex-1 space-y-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<p class="text-xs font-black text-indigo-600 uppercase tracking-widest bg-indigo-50 px-3 py-1 rounded-lg border border-indigo-100">
{{ $treatment->tanggal->format('d F Y') }}
</p>
<span class="text-[10px] text-slate-400 font-bold italic">{{ $treatment->tanggal->diffForHumans() }}</span>
</div>
{{-- PROTEKSI EDIT LOG: Hanya jika bisa Update Treatment --}}
@can('check-access', ['treatments', 'update'])
<a href="{{ route('treatments.edit', $treatment->id) }}" class="opacity-0 group-hover:opacity-100 p-2.5 bg-slate-50 text-slate-400 hover:text-amber-600 hover:bg-amber-50 rounded-xl transition-all" title="Edit Log">
<i class="fas fa-pen text-xs"></i>
</a>
@endcan
</div>
<div class="bg-white rounded-[2.5rem] p-8 border border-slate-100 shadow-sm group-hover:border-indigo-200 transition-all duration-300">
<div class="flex flex-col md:flex-row gap-10">
<div class="flex-1">
<h5 class="text-lg font-black text-slate-800 mb-2 uppercase tracking-tight">{{ $treatment->keluhan }}</h5>
<p class="text-sm text-slate-500 font-medium leading-relaxed italic border-l-4 border-indigo-100 pl-4 mb-6">Diagnosa: {{ $treatment->diagnosa }}</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="flex items-center gap-4 p-4 bg-slate-50/50 rounded-2xl">
<div class="w-10 h-10 rounded-xl bg-rose-50 flex items-center justify-center text-rose-500">
<i class="fas fa-capsules"></i>
</div>
<div>
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest">Therapy</p>
<p class="text-xs font-black text-slate-800 uppercase">{{ $treatment->nama_obat }} ({{ $treatment->dosis }})</p>
</div>
</div>
<div class="flex items-center gap-4 p-4 bg-slate-50/50 rounded-2xl">
<div class="w-10 h-10 rounded-xl bg-indigo-50 flex items-center justify-center text-indigo-500">
<i class="fas fa-user-md"></i>
</div>
<div>
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest">Medical Staff</p>
<p class="text-xs font-black text-slate-800 uppercase">{{ $treatment->user?->name ?? 'System' }}</p>
</div>
</div>
</div>
</div>
@if($treatment->foto_bukti)
<div class="shrink-0">
<div class="w-44 h-44 rounded-[2.5rem] overflow-hidden ring-4 ring-slate-100 shadow-lg group/img relative">
<img src="{{ asset('storage/' . $treatment->foto_bukti) }}" class="w-full h-full object-cover">
<div class="absolute inset-0 bg-indigo-600/20 opacity-0 group-hover/img:opacity-100 transition-opacity flex items-center justify-center cursor-zoom-in">
<i class="fas fa-search-plus text-white text-2xl"></i>
</div>
</div>
</div>
@endif
</div>
@if($treatment->keterangan)
<div class="mt-8 p-5 bg-indigo-50/30 rounded-2xl border-l-4 border-indigo-200">
<p class="text-[11px] text-slate-600 font-bold italic">
<i class="fas fa-quote-left mr-3 text-indigo-200"></i> {{ $treatment->keterangan }}
</p>
</div>
@endif
</div>
</div>
</div>
@empty
<div class="text-center py-32 bg-slate-50/30 rounded-[4rem] border-4 border-dashed border-slate-100">
<i class="fas fa-notes-medical text-slate-200 text-5xl mb-4"></i>
<p class="text-slate-400 font-black uppercase text-xs tracking-[0.3em]">No Historical Medical Logs Recorded</p>
</div>
@endforelse
</div>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('weightChart');
{{-- Data diurutkan dari yang terlama ke terbaru untuk Chart --}}
const weightHistories = {!! json_encode($cat->weightHistories->sortBy('created_at')->values() ?? []) !!};
if(!weightHistories || weightHistories.length === 0) {
ctx.parentNode.innerHTML = '<div class="h-full flex flex-col items-center justify-center text-slate-300 italic text-xs gap-3"><i class="fas fa-weight-hanging text-4xl opacity-20"></i>No growth data available yet.</div>';
return;
}
const labels = weightHistories.map(d => {
const date = new Date(d.created_at);
return date.toLocaleDateString('id-ID', { day: '2-digit', month: 'short' });
});
const data = weightHistories.map(d => d.berat);
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Weight (Kg)',
data: data,
borderColor: '#10b981',
backgroundColor: (context) => {
const chart = context.chart;
const {ctx, chartArea} = chart;
if (!chartArea) return null;
const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradient.addColorStop(0, 'rgba(16, 185, 129, 0)');
gradient.addColorStop(1, 'rgba(16, 185, 129, 0.1)');
return gradient;
},
borderWidth: 5,
tension: 0.4,
fill: true,
pointBackgroundColor: '#fff',
pointBorderColor: '#10b981',
pointBorderWidth: 3,
pointRadius: 4,
pointHoverRadius: 8
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: false,
grid: { borderDash: [5, 5], color: '#f1f5f9' },
ticks: { font: { size: 10, weight: 'bold' }, color: '#94a3b8' }
},
x: {
grid: { display: false },
ticks: { font: { size: 10, weight: 'bold' }, color: '#94a3b8' }
}
}
}
});
});
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,141 @@
<x-app-layout>
<div class="max-w-[1600px] mx-auto p-4 sm:px-6 lg:px-8 py-8 space-y-8">
{{-- Header --}}
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6 bg-white p-8 rounded-[3rem] border border-slate-100 shadow-sm">
<div>
<h2 class="text-4xl font-black text-slate-800 tracking-tighter uppercase leading-none">
Intelligence <span class="text-indigo-600">Dashboard</span>
</h2>
<p class="text-slate-400 font-bold uppercase text-[10px] tracking-[0.3em] mt-2 italic">
<i class="fas fa-calendar-alt mr-2"></i> Operasional Earrin Cats: {{ date('d F Y') }}
</p>
</div>
<div class="flex gap-3">
<a href="{{ route('admin.reports.index') }}" class="px-6 py-3 bg-slate-100 text-slate-600 text-[10px] font-black uppercase rounded-2xl hover:bg-slate-200 transition-all tracking-widest">Laporan Lengkap</a>
<a href="{{ route('cats.create') }}" class="px-6 py-3 bg-indigo-600 text-white text-[10px] font-black uppercase rounded-2xl shadow-xl shadow-indigo-100 hover:bg-indigo-700 transition-all tracking-widest flex items-center">
<i class="fas fa-plus mr-2"></i> Tambah Kucing
</a>
</div>
</div>
{{-- Birth Watch Alert --}}
@if($upcomingBirths->count() > 0)
<div class="bg-white rounded-[3rem] border-2 border-rose-100 shadow-xl shadow-rose-50 overflow-hidden">
<div class="bg-rose-600 px-8 py-4 flex justify-between items-center">
<div class="flex items-center gap-3">
<i class="fas fa-baby-carriage text-white animate-bounce"></i>
<h3 class="text-[10px] font-black text-white uppercase tracking-[0.3em]">Birth Watch: Persalinan Mendatang</h3>
</div>
</div>
<div class="p-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
@foreach($upcomingBirths as $birth)
<div class="bg-rose-50/50 border border-rose-100 p-6 rounded-[2rem] relative group">
<div class="absolute -top-3 -right-3 bg-rose-600 text-white w-10 h-10 rounded-xl flex items-center justify-center font-black text-sm shadow-lg transform rotate-12">
{{ $birth->days_to_go }}d
</div>
<div class="text-xs font-black text-rose-700 uppercase mb-1">{{ $birth->dam->nama_kucing }}</div>
<div class="text-[9px] font-bold text-rose-400 uppercase tracking-tighter mb-3">Sire: {{ $birth->sire->nama_kucing }}</div>
<div class="text-[10px] font-bold text-slate-600">Estimasi: {{ $birth->edd->format('d M Y') }}</div>
</div>
@endforeach
</div>
</div>
@endif
{{-- Stats Cards --}}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
<x-stats-card title="Total Pasien" :value="$stats['total_cats']" icon="fa-cat" color="indigo" />
<x-stats-card title="Indukan Hamil" :value="$stats['total_mating']" icon="fa-venus" color="rose" />
<x-stats-card title="Sedang Sakit" :value="$stats['sick_cats']" icon="fa-stethoscope" color="amber" />
<x-stats-card title="Stok Kritis" :value="$totalLowStock" icon="fa-exclamation-triangle" color="rose" />
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
{{-- Left Content: Records & Stock --}}
<div class="lg:col-span-2 space-y-8">
<div class="bg-white rounded-[3rem] border border-slate-100 shadow-sm overflow-hidden">
<div class="p-8 border-b border-slate-50 flex justify-between items-center">
<h3 class="text-xs font-black text-slate-800 uppercase tracking-widest">Rekam Medis Terbaru</h3>
<a href="{{ route('treatments.index') }}" class="text-[10px] font-black text-indigo-600 uppercase hover:underline">Lihat Semua</a>
</div>
<table class="w-full text-left">
<tbody>
@foreach($recentTreatments as $t)
<tr class="hover:bg-slate-50/50">
<td class="px-8 py-5 flex items-center gap-4">
<div class="w-10 h-10 rounded-xl bg-indigo-50 flex items-center justify-center text-indigo-600 font-black text-xs">
{{ substr($t->cat->nama_kucing, 0, 1) }}
</div>
<div>
<div class="text-xs font-black text-slate-800 uppercase leading-none">{{ $t->cat->nama_kucing }}</div>
<div class="text-[9px] text-slate-400 font-bold mt-1">{{ $t->created_at->diffForHumans() }}</div>
</div>
</td>
<td class="px-8 py-5 text-xs italic text-slate-500">"{{ $t->keluhan }}"</td>
<td class="px-8 py-5 text-right font-black text-indigo-600 text-[10px] uppercase">{{ $t->nama_obat }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- Charts --}}
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="bg-white rounded-[3rem] border border-slate-100 p-8">
<h4 class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-6">Sebaran Ras</h4>
<canvas id="breedChart" class="max-h-[200px]"></canvas>
</div>
<div class="bg-white rounded-[3rem] border border-slate-100 p-8">
<h4 class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-6">Tren Berat Badan (kg)</h4>
<canvas id="weightChart" class="max-h-[200px]"></canvas>
</div>
</div>
</div>
{{-- Right Content: Reminders --}}
<div class="space-y-8">
<div class="bg-slate-900 rounded-[3rem] p-8 text-white relative overflow-hidden shadow-2xl">
<h3 class="text-[10px] font-black text-indigo-400 uppercase tracking-[0.3em] mb-6">Medical Reminders</h3>
<div class="space-y-6">
@foreach([['Vaksin', 'vaccine', 'syringe'], ['Kutu', 'flea', 'bug'], ['Cacing', 'deworming', 'bug-slash'], ['Grooming', 'grooming', 'soap']] as $item)
<div class="flex justify-between items-center">
<span class="text-[10px] font-bold text-slate-300 uppercase"><i class="fas fa-{{ $item[2] }} mr-2"></i> {{ $item[0] }}</span>
<span class="text-xs font-black {{ $medicalAlerts[$item[1]] > 0 ? 'text-rose-400 animate-pulse' : 'text-slate-500' }}">{{ $medicalAlerts[$item[1]] }}</span>
</div>
@endforeach
</div>
</div>
<div class="bg-indigo-600 rounded-[3rem] p-8 text-white">
<h4 class="font-black text-sm uppercase tracking-widest mb-2 italic">Breeder Tip</h4>
<p class="text-[10px] text-indigo-100 leading-relaxed font-medium">Jangan lupa berikan vitamin bulu khusus untuk kucing <strong>Persian</strong> Anda setiap pagi.</p>
</div>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Breed Chart
new Chart(document.getElementById('breedChart'), {
type: 'doughnut',
data: {
labels: {!! json_encode($chartData->pluck('ras')) !!},
datasets: [{ data: {!! json_encode($chartData->pluck('total')) !!}, backgroundColor: ['#6366f1', '#f43f5e', '#fbbf24', '#10b981'], borderWidth: 0, spacing: 10 }]
},
options: { cutout: '80%', plugins: { legend: { position: 'bottom', labels: { font: { size: 10, weight: 'bold' }, usePointStyle: true } } } }
});
// Weight Chart
new Chart(document.getElementById('weightChart'), {
type: 'line',
data: {
labels: {!! json_encode($weightTrends->pluck('month')) !!},
datasets: [{ label: 'Berat (kg)', data: {!! json_encode($weightTrends->pluck('avg_weight')) !!}, borderColor: '#6366f1', tension: 0.4, fill: true, backgroundColor: 'rgba(99, 102, 241, 0.1)' }]
},
options: { plugins: { legend: { display: false } }, scales: { y: { beginAtZero: false, grid: { display: false } }, x: { grid: { display: false } } } }
});
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,141 @@
<x-app-layout>
<div class="space-y-8 pb-12">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-3xl font-black text-slate-800 tracking-tight leading-none mb-1">Medical Insights</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">Analisis Kesehatan & Konsumsi Obat earrin cats</p>
</div>
<div class="flex gap-2">
<span class="px-4 py-2 bg-white border border-slate-200 rounded-xl text-[10px] font-black text-slate-400">
PERIODE: {{ now()->subMonths(6)->format('M Y') }} - {{ now()->format('M Y') }}
</span>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div class="lg:col-span-2 bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm">
<div class="flex items-center justify-between mb-8">
<div>
<h4 class="font-black text-slate-800 uppercase tracking-tighter text-lg">Tren Kunjungan Medis</h4>
<p class="text-[10px] text-slate-400 font-black">Volume pemeriksaan per bulan</p>
</div>
<i class="fas fa-wave-square text-indigo-500 text-xl"></i>
</div>
<div class="h-[300px]">
<canvas id="medicalTrendChart"></canvas>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm">
<h4 class="font-black text-slate-800 uppercase tracking-tighter text-lg mb-6 text-center">Kasus Terbanyak</h4>
<div class="space-y-4">
@foreach($topComplaints as $complaint)
<div class="flex items-center justify-between p-4 bg-slate-50 rounded-2xl border border-slate-100 group hover:border-rose-200 transition-all">
<div class="flex items-center gap-3">
<span class="w-8 h-8 rounded-lg bg-rose-500 text-white flex items-center justify-center text-[10px] font-black">
{{ $loop->iteration }}
</span>
<span class="text-xs font-black text-slate-700 uppercase tracking-tighter">{{ $complaint->keluhan }}</span>
</div>
<span class="text-xs font-black text-rose-500 bg-rose-50 px-2 py-1 rounded-lg">{{ $complaint->total }}x</span>
</div>
@endforeach
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="bg-slate-900 rounded-[2.5rem] p-8 text-white shadow-2xl">
<div class="flex items-center justify-between mb-8 border-b border-slate-800 pb-4">
<h4 class="font-black uppercase tracking-widest text-sm">Obat Paling Sering Digunakan</h4>
<i class="fas fa-pills text-emerald-400"></i>
</div>
<div class="space-y-6">
@foreach($topMedicines as $medicine)
<div class="relative">
<div class="flex justify-between text-[10px] font-black uppercase mb-2">
<span>{{ $medicine->nama_obat }}</span>
<span class="text-emerald-400">{{ $medicine->total_used }} Unit</span>
</div>
<div class="w-full bg-slate-800 rounded-full h-1.5">
@php
$maxUsed = $topMedicines->first()->total_used ?? 1;
$percentage = ($medicine->total_used / $maxUsed) * 100;
@endphp
<div class="bg-emerald-400 h-1.5 rounded-full" style="width: {{ $percentage }}%"></div>
</div>
</div>
@endforeach
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="bg-white border border-slate-200 rounded-[2rem] p-6 flex flex-col items-center justify-center text-center shadow-sm">
<div class="w-12 h-12 bg-indigo-50 text-indigo-500 rounded-2xl flex items-center justify-center mb-4">
<i class="fas fa-stethoscope text-xl"></i>
</div>
<p class="text-[10px] font-black text-slate-400 uppercase">Rata-rata Medis</p>
<p class="text-2xl font-black text-slate-800">
{{ number_format($monthlyTrends->avg('total'), 1) }}
</p>
<p class="text-[9px] text-slate-400 mt-1 italic">Kasus / Bulan</p>
</div>
<div class="bg-indigo-600 rounded-[2rem] p-6 flex flex-col items-center justify-center text-center shadow-xl shadow-indigo-100">
<div class="w-12 h-12 bg-white/20 text-white rounded-2xl flex items-center justify-center mb-4">
<i class="fas fa-calendar-check text-xl"></i>
</div>
<p class="text-[10px] font-black text-white/60 uppercase">Puncak Kasus</p>
<p class="text-xl font-black text-white">
{{ $monthlyTrends->sortByDesc('total')->first()->month ?? '-' }}
</p>
<p class="text-[9px] text-white/60 mt-1 italic">Bulan Terpadat</p>
</div>
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('medicalTrendChart');
new Chart(ctx, {
type: 'line',
data: {
labels: {!! json_encode($monthlyTrends->pluck('month')) !!},
datasets: [{
label: 'Jumlah Pemeriksaan',
data: {!! json_encode($monthlyTrends->pluck('total')) !!},
borderColor: '#6366f1',
backgroundColor: 'rgba(99, 102, 241, 0.1)',
borderWidth: 4,
tension: 0.4,
fill: true,
pointBackgroundColor: '#fff',
pointBorderColor: '#6366f1',
pointBorderWidth: 3,
pointRadius: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: true,
grid: { color: '#f1f5f9' },
ticks: { font: { size: 10, weight: 'bold' }, color: '#94a3b8' }
},
x: {
grid: { display: false },
ticks: { font: { size: 10, weight: 'bold' }, color: '#94a3b8' }
}
}
}
});
});
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,43 @@
<x-app-layout>
<div class="max-w-2xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4">
<a href="{{ route('categories.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Master Kategori</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">Penyesuaian Kata Kunci Smart Categorization</p>
</div>
</div>
<form action="{{ route('categories.update', $category->id) }}" method="POST" class="space-y-6">
@csrf
@method('PATCH')
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Kategori</label>
<input type="text" name="name" value="{{ old('name', $category->name) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Kata Kunci Deteksi (Koma Terpisah)</label>
<textarea name="keywords" rows="4" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none"
placeholder="contoh: amoxicillin, clavamox, doxycycline">{{ old('keywords', $category->keywords) }}</textarea>
<div class="bg-indigo-50 p-4 rounded-2xl border border-indigo-100 mt-2">
<p class="text-[10px] text-indigo-600 font-bold leading-relaxed">
<i class="fas fa-lightbulb mr-1"></i> Tips: Masukkan nama obat atau fragmen kata yang unik. Sistem akan otomatis menetapkan kategori ini jika nama obat mengandung salah satu kata kunci di atas.
</p>
</div>
</div>
</div>
<button type="submit" class="w-full py-5 bg-indigo-600 text-white text-sm font-black rounded-[2rem] shadow-xl hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-3">
<i class="fas fa-save text-lg"></i> PERBARUI KATEGORI & SINKRONISASI
</button>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,187 @@
<x-app-layout>
<div class="max-w-6xl mx-auto space-y-6 pb-12">
{{-- HEADER SECTION --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 px-2">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1">Kategori Manager</h2>
<p class="text-sm text-slate-500 font-medium italic">Master data Smart Categorization ilycats.</p>
</div>
<div class="flex items-center gap-3">
{{-- FITUR KHUSUS ADMIN: Sinkronisasi Massal --}}
@if(auth()->user()->role === 'admin')
<div class="flex items-center gap-2">
<button type="button" onclick="confirmRecategorize()" class="inline-flex items-center px-5 py-2.5 bg-emerald-50 text-emerald-600 border border-emerald-100 text-[10px] font-black rounded-xl hover:bg-emerald-600 hover:text-white transition-all shadow-sm active:scale-95">
<i class="fas fa-sync-alt mr-2"></i> RE-CATEGORIZE ALL
</button>
<form id="form-recategorize" action="{{ route('categories.recategorize') }}" method="POST" class="hidden">
@csrf
</form>
</div>
@endif
{{-- PROTEKSI CREATE: Mengikuti matriks izin modul categories --}}
@can('check-access', ['categories', 'create'])
<button onclick="toggleCreateForm()" class="inline-flex items-center px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white text-[10px] font-black rounded-xl transition-all shadow-lg active:scale-95">
<i class="fas fa-plus-circle mr-2"></i> TAMBAH KATEGORI
</button>
@endcan
</div>
</div>
{{-- FORM TAMBAH KATEGORI (Proteksi Ganda dengan @can) --}}
@can('check-access', ['categories', 'create'])
<div id="create-form" class="hidden bg-white border border-slate-200 rounded-[2rem] p-8 shadow-sm animate-fade-in-down mx-2">
<form action="{{ route('categories.store') }}" method="POST" class="grid grid-cols-1 md:grid-cols-7 gap-6">
@csrf
<div class="md:col-span-2 space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Kategori</label>
<input type="text" name="name" required placeholder="Contoh: Antibiotik" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="md:col-span-3 space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Kata Kunci (Pisah Koma)</label>
<input type="text" name="keywords" required placeholder="amoxicillin, doxy, antibiotic" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="md:col-span-2 flex items-end">
<button type="submit" class="w-full py-3.5 bg-indigo-600 text-white font-black rounded-xl text-[10px] uppercase tracking-widest shadow-lg shadow-indigo-100 transition-all active:scale-95">
SIMPAN MASTER DATA
</button>
</div>
</form>
</div>
@endcan
{{-- DATA TABLE SECTION --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] overflow-hidden shadow-sm mx-2">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse table-fixed">
<thead class="bg-slate-50/80 border-b border-slate-100">
<tr>
<th class="w-56 px-6 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">Informasi Kategori</th>
<th class="w-auto px-6 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">Kata Kunci Deteksi</th>
<th class="w-32 px-6 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@forelse($categories as $cat)
<tr class="hover:bg-slate-50/30 transition-all">
<td class="px-6 py-5 overflow-hidden">
<div class="flex items-center gap-4">
<div class="w-10 h-10 rounded-xl bg-indigo-50 flex items-center justify-center text-indigo-500 shrink-0 border border-indigo-100">
<i class="fas fa-tag text-xs"></i>
</div>
<div class="min-w-0">
<div class="text-sm font-black text-slate-800 truncate">{{ $cat->name }}</div>
</div>
</div>
</td>
<td class="px-6 py-5">
<div class="flex flex-wrap gap-1.5 max-w-full">
@foreach(explode(',', $cat->keywords) as $word)
<span class="px-2.5 py-1 bg-slate-50 text-slate-500 text-[9px] font-bold rounded-lg border border-slate-200/60 uppercase whitespace-nowrap">
{{ trim($word) }}
</span>
@endforeach
</div>
</td>
<td class="px-6 py-5">
<div class="flex justify-center gap-2">
{{-- PROTEKSI EDIT: Mengikuti matriks permission --}}
@can('check-access', ['categories', 'update'])
<a href="{{ route('categories.edit', $cat->id) }}"
class="w-8 h-8 flex items-center justify-center bg-amber-50 text-amber-600 hover:bg-amber-600 hover:text-white rounded-xl transition-all shadow-sm shadow-amber-100"
title="Edit Kategori">
<i class="fas fa-edit text-[10px]"></i>
</a>
@endcan
{{-- PROTEKSI DELETE: Syarat manual 'admin' dihapus agar mengikuti matriks sepenuhnya --}}
@can('check-access', ['categories', 'delete'])
<button type="button"
onclick="confirmDelete({{ $cat->id }}, '{{ addslashes($cat->name) }}')"
class="w-8 h-8 flex items-center justify-center bg-rose-50 text-rose-600 hover:bg-rose-600 hover:text-white rounded-xl transition-all shadow-sm shadow-rose-100"
title="Hapus Kategori">
<i class="fas fa-trash-alt text-[10px]"></i>
</button>
<form id="delete-form-{{ $cat->id }}" action="{{ route('categories.destroy', $cat->id) }}" method="POST" class="hidden">
@csrf @method('DELETE')
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="px-8 py-20 text-center text-slate-400 italic text-xs font-bold uppercase tracking-widest">
Belum ada kategori yang dikonfigurasi.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
{{-- SCRIPTS SECTION --}}
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
function toggleCreateForm() {
const form = document.getElementById('create-form');
if (form) form.classList.toggle('hidden');
}
function confirmRecategorize() {
Swal.fire({
title: 'Recategorize All?',
text: "Sistem akan memindai seluruh obat berdasarkan kata kunci terbaru.",
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#10b981',
confirmButtonText: 'Ya, Sinkronkan!',
cancelButtonText: 'Batal',
customClass: { popup: 'rounded-[1.5rem]' }
}).then((result) => {
if (result.isConfirmed) {
const form = document.getElementById('form-recategorize');
if (form) form.submit();
}
});
}
function confirmDelete(id, name) {
Swal.fire({
title: 'Hapus Kategori?',
text: `Kategori "${name}" akan dihapus permanen. Data obat terkait akan kembali ke status "Umum".`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#f43f5e',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: { popup: 'rounded-[1.5rem]' }
}).then((result) => {
if (result.isConfirmed) {
const form = document.getElementById(`delete-form-${id}`);
if (form) form.submit();
}
});
}
{{-- Toast Notifikasi Sukses --}}
@if(session('success'))
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: "{{ session('success') }}",
showConfirmButton: false,
timer: 2500,
customClass: { popup: 'rounded-[1.5rem]' },
iconColor: '#4f46e5'
});
@endif
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,74 @@
<x-app-layout>
<div class="py-12 bg-slate-50">
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="mb-6">
<a href="{{ route('inventory.foods') }}" class="inline-flex items-center text-sm font-medium text-slate-500 hover:text-slate-700 transition">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path></svg>
Kembali ke Daftar
</a>
</div>
<div class="bg-white overflow-hidden shadow-sm rounded-[2rem] p-8 md:p-12">
<form action="{{ route('foods.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-10">
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-4">FOTO PRODUK / KEMASAN</label>
<div class="flex flex-col md:flex-row items-center gap-6">
<div class="w-32 h-32 flex-shrink-0 bg-slate-50 border-2 border-dashed border-slate-200 rounded-[1.5rem] flex items-center justify-center">
<svg class="w-10 h-10 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
</div>
<div class="flex-grow w-full">
<input type="file" name="foto_produk" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-6 file:rounded-full file:border-0 file:text-xs file:font-bold file:bg-slate-100 file:text-slate-700 hover:file:bg-slate-200 border border-slate-100 rounded-full bg-slate-50 p-1 focus:outline-none">
<p class="mt-3 text-[11px] text-slate-400 flex items-center">
<svg class="w-3 h-3 mr-2 text-indigo-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
Rekomendasi: Gunakan foto kemasan yang jelas agar staf medis tidak salah mengambil pakan.
</p>
</div>
</div>
</div>
<div class="mb-8">
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">NAMA PAKAN / MAKANAN</label>
<input type="text" name="nama_makanan" placeholder="Contoh: Royal Canin Kitten" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 placeholder-slate-300 focus:ring-2 focus:ring-indigo-100 transition shadow-sm">
<p class="mt-2 text-[11px] text-indigo-400 italic flex items-center">
<svg class="w-3 h-3 mr-2" fill="currentColor" viewBox="0 0 20 20"><path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"></path></svg>
Sistem akan menentukan kategori secara otomatis berdasarkan nama.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">JUMLAH STOK AWAL</label>
<input type="number" name="qty" placeholder="0" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 placeholder-slate-300 focus:ring-2 focus:ring-indigo-100 shadow-sm">
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">SATUAN (UNIT)</label>
<input type="text" name="unit" placeholder="Pcs / Kg" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 placeholder-slate-300 focus:ring-2 focus:ring-indigo-100 shadow-sm">
</div>
</div>
<div class="mb-10">
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">TANGGAL KADALUARSA (EXPIRED)</label>
<input type="date" name="expired_date" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 focus:ring-2 focus:ring-indigo-100 shadow-sm">
</div>
<div class="flex justify-end items-center gap-4 pt-6 mt-8 border-t border-slate-100">
<button type="reset" class="text-[11px] font-bold text-slate-400 hover:text-slate-600 uppercase tracking-widest transition-colors">
Reset Form
</button>
<button type="submit" class="inline-flex items-center justify-center whitespace-nowrap bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2.5 px-10 rounded-full text-[11px] uppercase tracking-widest shadow-md shadow-indigo-100 transition-all transform active:scale-95 flex-shrink-0 w-auto">
<svg class="w-3.5 h-3.5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7"></path>
</svg>
<span>Simpan Data</span>
</button>
</div>
</form>
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,68 @@
<x-app-layout>
<div class="max-w-3xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4 px-2">
<a href="{{ route('inventory.foods.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Stok Makanan</h2>
<p class="text-sm text-slate-500 font-medium italic">Pembaruan nutrisi untuk {{ $food->nama_makanan }}.</p>
</div>
</div>
<form action="{{ route('inventory.foods.update', $food->id) }}" method="POST" enctype="multipart/form-data" class="space-y-6 mx-2">
@csrf
@method('PATCH')
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="md:col-span-2 space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Produk Makanan</label>
<input type="text" name="nama_makanan" value="{{ old('nama_makanan', $food->nama_makanan) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jumlah Stok</label>
<input type="number" step="any" name="qty" value="{{ old('qty', (float)$food->qty) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Satuan (Unit)</label>
<input type="text" name="unit" value="{{ old('unit', $food->unit) }}" required placeholder="kg, pouch, can"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Kadaluarsa</label>
<input type="date" name="expired_date" min="{{ date('Y-m-d') }}" value="{{ old('expired_date', $food->expired_date ? $food->expired_date->format('Y-m-d') : '') }}"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Ganti Foto Produk</label>
<input type="file" name="foto_produk"
class="w-full px-5 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-xl file:border-0 file:text-[10px] file:font-black file:bg-indigo-50 file:text-indigo-600 hover:file:bg-indigo-100">
</div>
</div>
@if($food->foto_produk)
<div class="pt-4 border-t border-slate-100 flex items-center gap-4">
<div>
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3">Foto Saat Ini</p>
<div class="w-24 h-24 rounded-2xl border-4 border-white shadow-md overflow-hidden bg-slate-50">
<img src="{{ asset('storage/' . $food->foto_produk) }}" class="w-full h-full object-cover">
</div>
</div>
<div class="text-[10px] text-slate-400 italic">Foto lama akan dihapus otomatis jika Anda mengunggah foto baru.</div>
</div>
@endif
</div>
<button type="submit" class="w-full py-5 bg-indigo-600 text-white text-sm font-black rounded-[2rem] shadow-xl shadow-indigo-100 hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-3">
<i class="fas fa-save text-lg"></i> SIMPAN PERUBAHAN STOK
</button>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,166 @@
<x-app-layout>
<div class="max-w-7xl mx-auto space-y-6 pb-12">
{{-- SECTION 1: Header --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 px-2">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1">Stok Makanan</h2>
<p class="text-sm text-slate-500 font-medium italic">Manajemen nutrisi Ily Cattery.</p>
</div>
{{-- PROTEKSI CREATE: Mengikuti matriks izin modul foods --}}
@can('check-access', ['foods', 'create'])
<div class="flex items-center gap-3">
<a href="{{ route('inventory.foods.create') }}" class="inline-flex items-center px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white text-[10px] font-black rounded-xl transition-all shadow-lg active:scale-95">
<i class="fas fa-plus-circle mr-2"></i> TAMBAH MAKANAN
</a>
</div>
@endcan
</div>
{{-- SECTION 2: Batch Import (HANYA MUNCUL UNTUK ROLE ADMIN) --}}
@if(auth()->user()->role === 'admin')
<div class="bg-indigo-50/50 border border-indigo-100 rounded-[2rem] p-6 shadow-sm mx-2">
<div class="flex flex-col lg:flex-row items-center justify-between gap-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-white rounded-xl flex items-center justify-center text-indigo-600 shadow-sm">
<i class="fas fa-file-excel text-lg"></i>
</div>
<div>
<h4 class="text-xs font-black text-indigo-900 uppercase tracking-widest">Batch Import Makanan</h4>
<p class="text-[9px] text-indigo-400 font-bold italic">Update stok massal via file .xlsx.</p>
</div>
</div>
<div class="flex flex-wrap items-center gap-3">
<a href="{{ route('inventory.foods.template') }}" class="px-3 py-2 bg-white border border-indigo-200 text-indigo-600 text-[9px] font-black rounded-lg hover:bg-indigo-100 transition-all shadow-sm">
<i class="fas fa-download mr-1"></i> TEMPLATE EXCEL
</a>
<form action="{{ route('inventory.foods.batch') }}" method="POST" enctype="multipart/form-data" class="flex items-center gap-2">
@csrf
<div class="relative">
<input type="file" name="file_excel" required class="text-[9px] w-48 file:mr-2 file:py-1 file:px-2 file:rounded-md file:border-0 file:text-[9px] file:font-black file:bg-indigo-600 file:text-white hover:file:bg-indigo-700 cursor-pointer">
</div>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white text-[9px] font-black rounded-lg uppercase shadow-md hover:bg-indigo-700 active:scale-95 transition-all">
UPLOAD SEKARANG
</button>
</form>
</div>
</div>
</div>
@endif
{{-- SECTION 3: Tabel Utama --}}
<div class="bg-white border border-slate-200 rounded-[2rem] overflow-hidden shadow-sm mx-2">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse table-fixed">
<thead class="bg-slate-50/80 border-b border-slate-100">
<tr>
<th class="w-auto px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">Informasi Produk</th>
<th class="w-32 px-4 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Stok</th>
<th class="w-28 px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@forelse($foods as $f)
<tr class="hover:bg-slate-50/40 transition-all group">
<td class="px-6 py-4">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-slate-50 border border-slate-100 overflow-hidden shrink-0 flex items-center justify-center">
@if($f->foto_produk)
<img src="{{ asset('storage/' . $f->foto_produk) }}" class="w-full h-full object-cover">
@else
<i class="fas fa-bowl-food text-slate-200 text-lg"></i>
@endif
</div>
<div class="min-w-0">
<div class="text-sm font-black text-slate-800 truncate">{{ $f->nama_makanan }}</div>
<span class="text-[8px] font-black text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded border border-indigo-100 uppercase italic">
{{ $f->kategori }}
</span>
</div>
</div>
</td>
<td class="px-4 py-4 text-center">
<span class="inline-flex px-3 py-1.5 rounded-xl text-[10px] font-black border {{ $f->qty < 5 ? 'bg-rose-50 text-rose-600 border-rose-100' : 'bg-emerald-50 text-emerald-600 border-emerald-100' }}">
{{ (float)$f->qty }} {{ $f->unit }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex justify-center gap-2">
{{-- PROTEKSI EDIT: Mengikuti matriks permission --}}
@can('check-access', ['foods', 'update'])
<a href="{{ route('inventory.foods.edit', $f->id) }}" class="w-8 h-8 flex items-center justify-center bg-amber-50 text-amber-600 hover:bg-amber-600 hover:text-white rounded-xl transition-all shadow-sm shadow-amber-100">
<i class="fas fa-edit text-[10px]"></i>
</a>
@endcan
{{-- PROTEKSI DELETE: Syarat role admin dihapus agar mengikuti matriks permission sepenuhnya --}}
@can('check-access', ['foods', 'delete'])
<button onclick="confirmDeleteFood({{ $f->id }}, '{{ addslashes($f->nama_makanan) }}')" class="w-8 h-8 flex items-center justify-center bg-rose-50 text-rose-600 hover:bg-rose-600 hover:text-white rounded-xl transition-all shadow-sm shadow-rose-100">
<i class="fas fa-trash-alt text-[10px]"></i>
</button>
<form id="delete-food-{{ $f->id }}" action="{{ route('inventory.foods.destroy', $f->id) }}" method="POST" class="hidden">
@csrf @method('DELETE')
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr><td colspan="3" class="px-6 py-16 text-center text-slate-400 text-xs italic">Data makanan kosong.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
{{-- SECTION 4: Scripts --}}
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// Dialog Konfirmasi Hapus (TETAP UTUH)
function confirmDeleteFood(id, name) {
Swal.fire({
title: 'Hapus Stok Makanan?',
text: `Yakin ingin menghapus "${name}"? Tindakan ini tidak dapat dibatalkan.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#f43f5e',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: {
popup: 'rounded-[1.5rem]'
}
}).then((result) => {
if (result.isConfirmed) document.getElementById(`delete-food-${id}`).submit();
});
}
// Notifikasi Sukses
@if(session('success'))
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: "{{ session('success') }}",
timer: 2500,
showConfirmButton: false,
customClass: { popup: 'rounded-[1.5rem]' },
iconColor: '#4f46e5'
});
@endif
// Notifikasi Error
@if(session('error'))
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: "{{ session('error') }}",
confirmButtonColor: '#4f46e5',
customClass: { popup: 'rounded-[1.5rem]' }
});
@endif
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,87 @@
<x-app-layout>
<div class="max-w-2xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4">
<a href="{{ route('inventory.medicines.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Tambah Stok Obat</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">Pendaftaran Inventori Medis Baru</p>
</div>
</div>
<form action="{{ route('inventory.medicines.store') }}" method="POST" enctype="multipart/form-data" class="space-y-6">
@csrf
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-8">
<div class="space-y-4">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Foto Produk / Kemasan</label>
<div class="flex flex-col md:flex-row items-center gap-6">
<div id="image-preview" class="w-32 h-32 rounded-[2rem] bg-slate-50 border-2 border-dashed border-slate-200 flex items-center justify-center overflow-hidden shrink-0">
<i class="fas fa-camera text-slate-300 text-3xl"></i>
</div>
<div class="flex-1 w-full">
<input type="file" name="foto_produk" id="foto_produk" accept="image/*" onchange="previewImage(event)"
class="w-full text-xs text-slate-500 file:mr-4 file:py-2.5 file:px-6 file:rounded-xl file:border-0 file:text-[10px] file:font-black file:bg-indigo-50 file:text-indigo-600 hover:file:bg-indigo-100 transition-all cursor-pointer border border-slate-100 p-2 rounded-2xl">
<p class="text-[9px] text-slate-400 mt-3 italic leading-relaxed">
<i class="fas fa-info-circle mr-1"></i> Rekomendasi: Gunakan foto kemasan yang jelas agar staf medis tidak salah mengambil obat.
</p>
</div>
</div>
</div>
<hr class="border-slate-50">
<div class="space-y-6">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Obat / Vitamin</label>
<input type="text" name="nama_obat" value="{{ old('nama_obat') }}" required placeholder="Contoh: Amoxicillin"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
<p class="text-[9px] text-indigo-500 font-bold italic px-1 animate-pulse">
<i class="fas fa-magic mr-1"></i> Sistem akan menentukan kategori secara otomatis berdasarkan nama.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jumlah Stok Awal</label>
<input type="number" name="qty" value="{{ old('qty', 0) }}" step="0.01" min="0" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-black text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Satuan (Unit)</label>
<input type="text" name="unit" value="{{ old('unit', 'Pcs') }}" required placeholder="ML, Kapsul, dll"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Kadaluarsa (Expired)</label>
<input type="date" name="expired_date" value="{{ old('expired_date') }}"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
</div>
</div>
<button type="submit" class="w-full py-5 bg-indigo-600 text-white text-sm font-black rounded-[2rem] shadow-xl hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-3">
<i class="fas fa-check-circle text-lg"></i> DAFTARKAN OBAT KE INVENTORI
</button>
</form>
</div>
@push('scripts')
<script>
function previewImage(event) {
const reader = new FileReader();
reader.onload = function(){
const output = document.getElementById('image-preview');
output.innerHTML = `<img src="${reader.result}" class="w-full h-full object-cover">`;
output.classList.remove('border-dashed');
output.classList.add('border-solid', 'border-indigo-100');
};
reader.readAsDataURL(event.target.files[0]);
}
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,65 @@
<x-app-layout>
<div class="max-w-3xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4 px-2">
<a href="{{ route('inventory.medicines.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Data Obat</h2>
<p class="text-sm text-slate-500 font-medium italic">Pembaruan stok medis {{ $medicine->nama_obat }}.</p>
</div>
</div>
<form action="{{ route('inventory.medicines.update', $medicine->id) }}" method="POST" enctype="multipart/form-data" class="space-y-6 mx-2">
@csrf
@method('PATCH')
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="md:col-span-2 space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Produk Medis</label>
<input type="text" name="nama_obat" value="{{ old('nama_obat', $medicine->nama_obat) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jumlah Stok (QTY)</label>
<input type="number" step="any" name="qty" value="{{ old('qty', (float)$medicine->qty) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Satuan (Unit)</label>
<input type="text" name="unit" value="{{ old('unit', $medicine->unit) }}" required placeholder="Contoh: Tablet, ml, Bottle"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Kadaluarsa</label>
<input type="date" name="expired_date" value="{{ old('expired_date', $medicine->expired_date ? $medicine->expired_date->format('Y-m-d') : '') }}"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Foto Produk (Opsional)</label>
<input type="file" name="foto_produk"
class="w-full px-5 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-xl file:border-0 file:text-[10px] file:font-black file:bg-indigo-50 file:text-indigo-600 hover:file:bg-indigo-100">
</div>
</div>
@if($medicine->foto_produk)
<div class="pt-4 border-t border-slate-100">
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3">Foto Saat Ini</p>
<div class="w-32 h-32 rounded-2xl border-4 border-white shadow-md overflow-hidden bg-slate-50">
<img src="{{ asset('storage/' . $medicine->foto_produk) }}" class="w-full h-full object-cover">
</div>
</div>
@endif
</div>
<button type="submit" class="w-full py-5 bg-indigo-600 text-white text-sm font-black rounded-[2rem] shadow-xl shadow-indigo-100 hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-3">
<i class="fas fa-save text-lg"></i> SIMPAN PERUBAHAN OBAT
</button>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,190 @@
<x-app-layout>
<div class="space-y-6 pb-12">
{{-- SECTION 1: Header --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1">Inventori Obat & Vitamin</h2>
<p class="text-sm text-slate-500 font-medium italic">Manajemen stok medis Ily Cattery.</p>
</div>
{{-- PROTEKSI CREATE: Mengikuti matriks izin modul medicines --}}
@can('check-access', ['medicines', 'create'])
<a href="{{ route('inventory.medicines.create') }}" class="inline-flex items-center px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white text-[10px] font-black rounded-xl shadow-lg transition-all active:scale-95">
<i class="fas fa-plus-circle mr-2"></i> TAMBAH OBAT /VITAMIN
</a>
@endcan
</div>
{{-- SECTION 2: Batch Import (HANYA MUNCUL UNTUK ROLE ADMIN) --}}
@if(auth()->user()->role === 'admin')
<div class="bg-indigo-50/50 border border-indigo-100 rounded-[2rem] p-6 shadow-sm">
<div class="flex flex-col lg:flex-row items-center justify-between gap-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-white rounded-xl flex items-center justify-center text-indigo-600 shadow-sm">
<i class="fas fa-file-excel text-lg"></i>
</div>
<div>
<h4 class="text-xs font-black text-indigo-900 uppercase">Batch Import</h4>
<p class="text-[9px] text-indigo-400 font-bold italic">Update stok via .xlsx.</p>
</div>
</div>
<div class="flex items-center gap-3">
<a href="{{ route('inventory.medicines.template') }}" class="px-3 py-2 bg-white border border-indigo-200 text-indigo-600 text-[9px] font-black rounded-lg hover:bg-indigo-100 transition-all">
TEMPLATE
</a>
<form action="{{ route('inventory.medicines.batch') }}" method="POST" enctype="multipart/form-data" class="flex items-center gap-2">
@csrf
<input type="file" name="file_excel" class="text-[9px] w-40">
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white text-[9px] font-black rounded-lg uppercase">Upload</button>
</form>
</div>
</div>
</div>
@endif
{{-- SECTION 3: Tabel Utama --}}
<div class="bg-white border border-slate-200 rounded-[2rem] overflow-hidden shadow-sm">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse table-fixed">
<thead class="bg-slate-50/80 border-b border-slate-100">
<tr>
<th class="w-auto px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">Informasi Produk</th>
<th class="w-32 px-4 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Stok</th>
<th class="w-40 px-4 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Kadaluarsa</th>
<th class="w-28 px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@forelse($medicines as $m)
<tr class="hover:bg-slate-50/40 transition-all group">
{{-- Informasi Produk --}}
<td class="px-6 py-4 overflow-hidden">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-slate-50 border border-slate-100 overflow-hidden shrink-0 flex items-center justify-center">
@if($m->foto_produk)
<img src="{{ asset('storage/' . $m->foto_produk) }}" class="w-full h-full object-cover">
@else
<i class="fas fa-pills text-slate-200 text-lg"></i>
@endif
</div>
<div class="min-w-0">
<div class="text-sm font-black text-slate-800 truncate">{{ $m->nama_obat }}</div>
<div class="flex items-center gap-2 mt-1">
<span class="text-[8px] font-black text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded border border-indigo-100 uppercase italic">
{{ $m->kategori ?? 'Umum' }}
</span>
<span class="text-[8px] font-bold text-slate-400 uppercase">/ {{ $m->unit }}</span>
</div>
</div>
</div>
</td>
{{-- Stok --}}
<td class="px-4 py-4 text-center">
<span class="inline-flex px-3 py-1.5 rounded-xl text-[10px] font-black border {{ $m->qty < 5 ? 'bg-rose-50 text-rose-600 border-rose-100' : 'bg-emerald-50 text-emerald-600 border-emerald-100' }}">
{{ (float)$m->qty }}
</span>
</td>
{{-- Kadaluarsa --}}
<td class="px-4 py-4 text-center">
@if($m->expired_date)
<div class="flex flex-col items-center">
<span class="text-[10px] font-black {{ $m->expired_date->isPast() ? 'text-rose-600' : 'text-slate-600' }}">
{{ $m->expired_date->format('d/m/y') }}
</span>
@if($m->expired_date->isPast())
<span class="text-[7px] font-black text-rose-400 uppercase">Expired!</span>
@endif
</div>
@else
<span class="text-slate-300 text-[9px] font-bold italic">-</span>
@endif
</td>
{{-- AKSI: Proteksi Penuh Berdasarkan Matriks Role Management --}}
<td class="px-6 py-4">
<div class="flex justify-center gap-2">
{{-- Edit --}}
@can('check-access', ['medicines', 'update'])
<a href="{{ route('inventory.medicines.edit', $m->id) }}"
class="w-8 h-8 flex items-center justify-center bg-amber-50 text-amber-600 hover:bg-amber-600 hover:text-white rounded-xl transition-all shadow-sm shadow-amber-100"
title="Edit Obat">
<i class="fas fa-edit text-[10px]"></i>
</a>
@endcan
{{-- Hapus: Pengecekan 'admin' dihapus agar permission matriks bekerja --}}
@can('check-access', ['medicines', 'delete'])
<button type="button"
onclick="confirmDeleteMedicine({{ $m->id }}, '{{ addslashes($m->nama_obat) }}')"
class="w-8 h-8 flex items-center justify-center bg-rose-50 text-rose-600 hover:bg-rose-600 hover:text-white rounded-xl transition-all shadow-sm shadow-rose-100"
title="Hapus Obat">
<i class="fas fa-trash-alt text-[10px]"></i>
</button>
<form id="delete-medicine-{{ $m->id }}" action="{{ route('inventory.medicines.destroy', $m->id) }}" method="POST" class="hidden">
@csrf @method('DELETE')
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-16 text-center text-slate-400 text-xs italic font-bold">Belum ada data obat.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
{{-- SECTION 4: Scripts --}}
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
function confirmDeleteMedicine(id, name) {
Swal.fire({
title: 'Hapus Obat?',
text: "Apakah Anda yakin ingin menghapus \"" + name + "\"? Data yang dihapus tidak bisa dikembalikan.",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#f43f5e',
cancelButtonColor: '#64748b',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: {
popup: 'rounded-[1.5rem]'
}
}).then((result) => {
if (result.isConfirmed) {
document.getElementById('delete-medicine-' + id).submit();
}
});
}
{{-- Toast Notifikasi --}}
@if(session('success'))
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: "{{ session('success') }}",
showConfirmButton: false,
timer: 2000,
customClass: { popup: 'rounded-[1.5rem]' }
});
@endif
@if(session('error'))
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: "{{ session('error') }}",
confirmButtonColor: '#4f46e5',
customClass: { popup: 'rounded-[1.5rem]' }
});
@endif
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,52 @@
<x-app-layout>
<div class="py-12 bg-slate-50">
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm rounded-[2rem] p-8 md:p-12">
<form action="{{ route('inventory.supplies.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-10">
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-4">FOTO PERLENGKAPAN</label>
<div class="flex flex-col md:flex-row items-center gap-6">
<div class="w-32 h-32 flex-shrink-0 bg-slate-50 border-2 border-dashed border-slate-200 rounded-[1.5rem] flex items-center justify-center">
<svg class="w-10 h-10 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
</div>
<div class="flex-grow w-full">
<input type="file" name="foto_produk" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-6 file:rounded-full file:border-0 file:text-xs file:font-bold file:bg-slate-100 file:text-slate-700 hover:file:bg-slate-200 border border-slate-100 rounded-full bg-slate-50 p-1 focus:outline-none">
</div>
</div>
</div>
<div class="mb-8">
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">NAMA PERLENGKAPAN</label>
<input type="text" name="nama_perlengkapan" placeholder="Contoh: Pasir Kucing 10kg" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 placeholder-slate-300 focus:ring-2 focus:ring-indigo-100 shadow-sm">
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-10">
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">JUMLAH STOK AWAL</label>
<input type="number" name="qty" placeholder="0" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 placeholder-slate-300 focus:ring-2 focus:ring-indigo-100 shadow-sm">
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2">SATUAN (UNIT)</label>
<input type="text" name="unit" placeholder="Pcs" class="w-full bg-slate-50 border-none rounded-full px-8 py-4 text-slate-700 placeholder-slate-300 focus:ring-2 focus:ring-indigo-100 shadow-sm">
</div>
</div>
<div class="flex justify-end items-center gap-4 pt-6 mt-8 border-t border-slate-100">
<button type="reset" class="text-[11px] font-bold text-slate-400 hover:text-slate-600 uppercase tracking-widest transition-colors">
Reset Form
</button>
<button type="submit" class="inline-flex items-center bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2.5 px-8 rounded-full text-[11px] uppercase tracking-widest shadow-md shadow-indigo-100 transition-all transform active:scale-95">
<svg class="w-3.5 h-3.5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7"></path>
</svg>
Simpan Data
</button>
</div>
</form>
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,62 @@
<x-app-layout>
<div class="max-w-3xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4 px-2">
<a href="{{ route('inventory.supplies.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Perlengkapan</h2>
<p class="text-sm text-slate-500 font-medium italic">Pembaruan data inventori untuk {{ $supply->nama_perlengkapan }}.</p>
</div>
</div>
<form action="{{ route('inventory.supplies.update', $supply->id) }}" method="POST" enctype="multipart/form-data" class="space-y-6 mx-2">
@csrf
@method('PATCH')
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="md:col-span-2 space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Perlengkapan</label>
<input type="text" name="nama_perlengkapan" value="{{ old('nama_perlengkapan', $supply->nama_perlengkapan) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jumlah Stok</label>
<input type="number" step="any" name="qty" value="{{ old('qty', (float)$supply->qty) }}" required
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Satuan (Unit)</label>
<input type="text" name="unit" value="{{ old('unit', $supply->unit) }}" required placeholder="Contoh: sak, set, pcs"
class="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all outline-none">
</div>
<div class="md:col-span-2 space-y-1">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Ganti Foto Item (Opsional)</label>
<input type="file" name="foto_produk"
class="w-full px-5 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-xl file:border-0 file:text-[10px] file:font-black file:bg-indigo-50 file:text-indigo-600 hover:file:bg-indigo-100">
</div>
</div>
@if($supply->foto_produk)
<div class="pt-4 border-t border-slate-100 flex items-center gap-4">
<div>
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3">Pratinjau Foto</p>
<div class="w-24 h-24 rounded-2xl border-4 border-white shadow-md overflow-hidden bg-slate-50">
<img src="{{ asset('storage/' . $supply->foto_produk) }}" class="w-full h-full object-cover">
</div>
</div>
<div class="text-[10px] text-slate-400 italic">Foto lama akan dihapus otomatis jika Anda mengunggah foto baru.</div>
</div>
@endif
</div>
<button type="submit" class="w-full py-5 bg-indigo-600 text-white text-sm font-black rounded-[2rem] shadow-xl shadow-indigo-100 hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-3">
<i class="fas fa-save text-lg"></i> SIMPAN PERUBAHAN ITEM
</button>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,176 @@
<x-app-layout>
<div class="max-w-7xl mx-auto space-y-6 pb-12">
{{-- HEADER SECTION --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 px-2">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1">Stok Perlengkapan</h2>
<p class="text-sm text-slate-500 font-medium italic">Manajemen inventori operasional Ily Cattery.</p>
</div>
{{-- PROTEKSI CREATE: Mengikuti matriks izin modul supplies --}}
@can('check-access', ['supplies', 'create'])
<div class="flex items-center gap-3">
<a href="{{ route('inventory.supplies.create') }}" class="inline-flex items-center px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white text-[10px] font-black rounded-xl transition-all shadow-lg active:scale-95 uppercase tracking-widest">
<i class="fas fa-plus-circle mr-2"></i> TAMBAH ITEM
</a>
</div>
@endcan
</div>
{{-- BATCH IMPORT SECTION: KHUSUS ROLE ADMIN --}}
@if(auth()->user()->role === 'admin')
<div class="bg-indigo-50/50 border border-indigo-100 rounded-[2rem] p-6 shadow-sm mx-2">
<div class="flex flex-col lg:flex-row items-center justify-between gap-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-white rounded-xl flex items-center justify-center text-indigo-600 shadow-sm">
<i class="fas fa-boxes-stacked text-lg"></i>
</div>
<div>
<h4 class="text-xs font-black text-indigo-900 uppercase tracking-widest">Batch Import Supplies</h4>
<p class="text-[9px] text-indigo-400 font-bold italic">Update stok massal via file .xlsx.</p>
</div>
</div>
<div class="flex flex-wrap items-center gap-3">
<a href="{{ route('inventory.supplies.template') }}" class="px-3 py-2 bg-white border border-indigo-200 text-indigo-600 text-[9px] font-black rounded-lg hover:bg-indigo-100 transition-all shadow-sm">
<i class="fas fa-download mr-1"></i> TEMPLATE EXCEL
</a>
<form action="{{ route('inventory.supplies.batch') }}" method="POST" enctype="multipart/form-data" class="flex items-center gap-2">
@csrf
<div class="relative">
<input type="file" name="file_excel" required class="text-[9px] w-48 file:mr-2 file:py-1 file:px-2 file:rounded-md file:border-0 file:text-[9px] file:font-black file:bg-indigo-600 file:text-white hover:file:bg-indigo-700 cursor-pointer">
</div>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white text-[9px] font-black rounded-lg uppercase shadow-md hover:bg-indigo-700 active:scale-95 transition-all">
UPLOAD
</button>
</form>
</div>
</div>
</div>
@endif
{{-- DATA TABLE SECTION --}}
<div class="bg-white border border-slate-200 rounded-[2rem] overflow-hidden shadow-sm mx-2">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse table-fixed">
<thead class="bg-slate-50/80 border-b border-slate-100">
<tr>
<th class="w-auto px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">Informasi Perlengkapan</th>
<th class="w-32 px-4 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Stok</th>
<th class="w-32 px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-center">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@forelse($supplies as $s)
<tr class="hover:bg-slate-50/40 transition-all group">
<td class="px-6 py-4">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-slate-50 border border-slate-100 overflow-hidden shrink-0 flex items-center justify-center shadow-inner">
@if($s->foto_produk)
<img src="{{ \Storage::url($s->foto_produk) }}" class="w-full h-full object-cover">
@else
<i class="fas fa-box-open text-slate-200 text-lg"></i>
@endif
</div>
<div class="min-w-0">
<div class="text-sm font-black text-slate-800 truncate">{{ $s->nama_perlengkapan }}</div>
<span class="text-[8px] font-black text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded border border-indigo-100 uppercase italic">
{{ $s->category->name ?? $s->kategori }}
</span>
</div>
</div>
</td>
<td class="px-4 py-4 text-center">
<span class="inline-flex px-3 py-1.5 rounded-xl text-[10px] font-black border {{ $s->qty < 5 ? 'bg-rose-50 text-rose-600 border-rose-100' : 'bg-emerald-50 text-emerald-600 border-emerald-100' }}">
{{ (float)$s->qty }} {{ $s->unit }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex justify-center gap-2">
{{-- PROTEKSI EDIT: Berdasarkan Matriks Izin --}}
@can('check-access', ['supplies', 'update'])
<a href="{{ route('inventory.supplies.edit', $s->id) }}" class="w-8 h-8 flex items-center justify-center bg-amber-50 text-amber-600 hover:bg-amber-600 hover:text-white rounded-xl transition-all shadow-sm shadow-amber-100">
<i class="fas fa-edit text-[10px]"></i>
</a>
@endcan
{{-- PROTEKSI DELETE: Pengecekan manual 'admin' dihapus agar mengikuti matriks izin --}}
@can('check-access', ['supplies', 'delete'])
<button onclick="confirmDeleteSupply({{ $s->id }}, '{{ addslashes($s->nama_perlengkapan) }}')" class="w-8 h-8 flex items-center justify-center bg-rose-50 text-rose-600 hover:bg-rose-600 hover:text-white rounded-xl transition-all shadow-sm shadow-rose-100">
<i class="fas fa-trash-alt text-[10px]"></i>
</button>
<form id="delete-supply-{{ $s->id }}" action="{{ route('inventory.supplies.destroy', $s->id) }}" method="POST" class="hidden">
@csrf @method('DELETE')
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="px-6 py-20 text-center">
<div class="flex flex-col items-center justify-center">
<i class="fas fa-folder-open text-slate-200 text-4xl mb-3"></i>
<p class="text-slate-400 text-xs italic font-bold">Data perlengkapan belum tersedia.</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if(method_exists($supplies, 'links'))
<div class="px-6 py-4 bg-slate-50/50 border-t border-slate-100">
{{ $supplies->links() }}
</div>
@endif
</div>
</div>
{{-- SCRIPTS SECTION --}}
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// Dialog Konfirmasi Hapus (TETAP UTUH)
function confirmDeleteSupply(id, name) {
Swal.fire({
title: 'Hapus Item?',
text: `Yakin ingin menghapus "${name}"? Tindakan ini tidak dapat dibatalkan.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#f43f5e',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
borderRadius: '1.5rem',
customClass: { title: 'font-black text-slate-800' }
}).then((result) => {
if (result.isConfirmed) document.getElementById(`delete-supply-${id}`).submit();
});
}
// Notifikasi Sukses & Error (TETAP UTUH)
@if(session('success'))
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: "{{ session('success') }}",
timer: 2500,
showConfirmButton: false,
borderRadius: '1.5rem',
iconColor: '#4f46e5'
});
@endif
@if(session('error'))
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: "{{ session('error') }}",
borderRadius: '1.5rem'
});
@endif
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,279 @@
<x-app-layout>
<div class="max-w-7xl mx-auto pb-12">
{{-- 1. DISPLAY VALIDATION ERRORS --}}
@if ($errors->any())
<div class="mb-8 p-6 bg-rose-50 border-2 border-rose-100 rounded-[2.5rem] animate-fade-in shadow-sm">
<div class="flex items-center gap-3 mb-3">
<i class="fas fa-exclamation-circle text-rose-500 text-xl"></i>
<h3 class="text-sm font-black text-rose-700 uppercase tracking-widest">Terdapat Kesalahan Input:</h3>
</div>
<ul class="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-1 border-t border-rose-100 pt-4">
@foreach ($errors->all() as $error)
<li class="text-[11px] text-rose-600 font-bold italic">• {{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('matting.store') }}" method="POST" enctype="multipart/form-data" x-data="kittenManager()">
@csrf
{{-- Header & Back Button --}}
<div class="flex items-center justify-between mb-8">
<div class="flex items-center gap-4">
<a href="{{ route('matting.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-3xl font-black text-slate-800 tracking-tighter leading-none mb-1 uppercase">Registrasi Breeding</h2>
<p class="text-[10px] text-slate-500 font-bold uppercase tracking-[0.2em] italic">Earrin Cats Breeding Program</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
{{-- SECTION: SIRE (Bapak) --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6 relative overflow-hidden group">
<div class="absolute top-0 right-0 p-6 opacity-10 group-hover:opacity-20 transition-opacity">
<i class="fas fa-mars text-6xl text-blue-600"></i>
</div>
<h4 class="text-xs font-black text-blue-600 uppercase tracking-widest border-b border-blue-50 pb-3 italic">Informasi Sire (Induk Jantan)</h4>
<div class="space-y-4">
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Pilih Sire Internal</label>
<select name="sire_id" id="sire_select" class="w-full mt-1 rounded-2xl border-slate-100 bg-slate-50 font-bold text-sm focus:ring-blue-500 @error('sire_id') border-rose-500 @enderror">
<option value="">-- Pilih Kucing Jantan --</option>
@foreach($sires as $sire)
<option value="{{ $sire->id }}" {{ old('sire_id') == $sire->id ? 'selected' : '' }}>{{ $sire->nama_kucing }} ({{ $sire->ras }})</option>
@endforeach
</select>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Tgl Lahir Sire</label>
<input type="date" name="tgl_lahir_sire" id="sire_birth" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">No. Pedigree Sire</label>
<input type="text" name="no_pedigree_bapak" id="sire_pedigree" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50" placeholder="Otomatis...">
</div>
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">No. Microchip Sire</label>
<input type="text" name="no_microchip_bapak" id="sire_microchip" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50" placeholder="9000xxxxxx">
</div>
</div>
</div>
{{-- SECTION: DAM (Ibu) --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6 relative overflow-hidden group">
<div class="absolute top-0 right-0 p-6 opacity-10 group-hover:opacity-20 transition-opacity">
<i class="fas fa-venus text-6xl text-pink-600"></i>
</div>
<h4 class="text-xs font-black text-pink-600 uppercase tracking-widest border-b border-pink-50 pb-3 italic">Informasi Dam (Induk Betina)</h4>
<div class="space-y-4">
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Pilih Dam Internal</label>
<select name="dam_id" id="dam_select" class="w-full mt-1 rounded-2xl border-slate-100 bg-slate-50 font-bold text-sm focus:ring-pink-500 @error('dam_id') border-rose-500 @enderror">
<option value="">-- Pilih Kucing Betina --</option>
@foreach($dams as $dam)
<option value="{{ $dam->id }}" {{ old('dam_id') == $dam->id ? 'selected' : '' }}>{{ $dam->nama_kucing }} ({{ $dam->ras }})</option>
@endforeach
</select>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Tgl Lahir Dam</label>
<input type="date" name="tgl_lahir_dam" id="dam_birth" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">No. Pedigree Dam</label>
<input type="text" name="no_pedigree_ibu" id="dam_pedigree" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50" placeholder="Otomatis...">
</div>
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">No. Microchip Dam</label>
<input type="text" name="no_microchip_ibu" id="dam_microchip" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50" placeholder="9000xxxxxx">
</div>
</div>
</div>
</div>
{{-- SECTION: PERIODE & STATUS --}}
<div class="bg-slate-900 rounded-[2.5rem] p-8 text-white shadow-2xl mb-12 relative overflow-hidden">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8 relative z-10">
<div>
<label class="text-[10px] font-black uppercase tracking-widest text-slate-400">Status Breeding</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">Proses (Monitoring)</option>
<option value="berhasil" class="text-slate-800">Berhasil (Lahir)</option>
<option value="gagal" class="text-slate-800">Gagal</option>
</select>
</div>
<div>
<label class="text-[10px] font-black uppercase tracking-widest text-slate-400">Tgl Kawin Awal</label>
<input type="date" name="tgl_kawin_awal" class="w-full mt-2 bg-white/10 border-white/10 rounded-xl text-xs font-bold focus:ring-indigo-500" required>
</div>
<div>
<label class="text-[10px] font-black uppercase tracking-widest text-slate-400">Tgl Kawin Akhir</label>
<input type="date" name="tgl_kawin_akhir" class="w-full mt-2 bg-white/10 border-white/10 rounded-xl text-xs font-bold focus:ring-indigo-500">
</div>
<div>
<label class="text-[10px] font-black uppercase tracking-widest text-slate-400">Anak Hidup</label>
<input type="number" name="jumlah_anak_hidup" value="0" class="w-full mt-2 bg-white/10 border-white/10 rounded-xl text-xs font-bold focus:ring-indigo-500" min="0">
</div>
</div>
</div>
{{-- SECTION: DYNAMIC KITTENS (Daftar Anak) --}}
<div class="space-y-6">
<div class="flex items-center justify-between border-b border-slate-100 pb-4">
<div>
<h3 class="text-2xl font-black text-slate-800 tracking-tighter uppercase leading-none">Daftar Anak (Kittens)</h3>
<p class="text-[10px] text-slate-400 font-bold uppercase tracking-widest mt-1 italic">Profil otomatis masuk ke Manajemen Pasien</p>
</div>
<button type="button" @click="addKitten" class="inline-flex items-center px-6 py-3 bg-indigo-600 text-white text-[10px] font-black rounded-2xl shadow-lg hover:bg-indigo-700 transition-all active:scale-95">
<i class="fas fa-plus-circle mr-2 text-sm"></i> TAMBAH ANAK
</button>
</div>
<template x-for="(kitten, index) in kittens" :key="index">
<div class="bg-white border-2 border-dashed border-slate-200 p-8 rounded-[3rem] relative space-y-8 animate-fade-in shadow-sm hover:border-indigo-200 transition-all">
<button type="button" @click="removeKitten(index)" class="absolute top-6 right-6 w-10 h-10 bg-rose-50 text-rose-500 rounded-full flex items-center justify-center hover:bg-rose-500 hover:text-white transition-all shadow-sm">
<i class="fas fa-times text-sm"></i>
</button>
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="md:col-span-2">
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Nama Anak</label>
<input type="text" :name="`kittens[${index}][nama]`" x-model="kitten.nama" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-bold" placeholder="Masukkan nama..." required>
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Jenis Kelamin</label>
<select :name="`kittens[${index}][jenis_kelamin]`" x-model="kitten.jenis_kelamin" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-bold">
<option value="Jantan">Jantan</option>
<option value="Betina">Betina</option>
</select>
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Tanggal Lahir</label>
<input type="date" :name="`kittens[${index}][tgl_lahir]`" x-model="kitten.tgl_lahir" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-bold" required>
</div>
</div>
{{-- PERUBAHAN DISINI: Grid untuk Registry (Multiple) --}}
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 items-start border-t border-slate-50 pt-6">
<div class="md:col-span-2 space-y-2">
<label class="text-[10px] font-black uppercase text-slate-400 ml-1">Registry / Federasi (Pilihan Ganda)</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<template x-for="regOption in ['ICA', 'CFA', 'WCF', 'FFF']" :key="regOption">
<label class="block cursor-pointer group">
<input type="checkbox"
:name="`kittens[${index}][registry][]`"
:value="regOption"
x-model="kitten.registry"
class="sr-only peer">
<div class="py-3 text-center border-2 border-slate-100 rounded-xl text-[9px] font-black text-slate-400
peer-checked:bg-indigo-600 peer-checked:text-white peer-checked:border-indigo-600
transition-all shadow-sm group-hover:border-slate-300 uppercase">
<span x-text="regOption"></span>
</div>
</label>
</template>
</div>
</div>
<div>
<label class="text-[10px] font-black uppercase text-slate-400 ml-2">Kode EMS</label>
<input type="text" :name="`kittens[${index}][kode_ems]`" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-bold" placeholder="PER d 62">
</div>
<div class="flex items-center h-12 gap-3 px-4 bg-slate-50 rounded-xl border border-slate-100 mt-6">
<input type="checkbox" :name="`kittens[${index}][is_pedigree]`" value="1" class="rounded text-indigo-600 focus:ring-indigo-500">
<label class="text-[10px] font-black uppercase text-slate-600">Sertifikat OK</label>
</div>
</div>
<div class="pt-6 border-t border-slate-100">
<label class="text-[10px] font-black uppercase text-slate-400 ml-2 mb-3 block italic">Dokumen Pedigree (Slot 1-4)</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<template x-for="n in 4" :key="n">
<div class="p-3 bg-slate-50/50 rounded-2xl border border-slate-100 text-center">
<input type="file" :name="`kittens[${index}][foto_ped_${n}]`" class="text-[9px] w-full file:mr-2 file:py-1 file:px-2 file:rounded-lg file:border-0 file:text-[9px] file:font-black file:bg-indigo-50 file:text-indigo-600">
</div>
</template>
</div>
</div>
</div>
</template>
<div x-show="kittens.length === 0" class="text-center py-20 bg-slate-50/50 rounded-[3rem] border-2 border-dashed border-slate-200">
<i class="fas fa-paw text-slate-200 text-6xl mb-4"></i>
<p class="text-slate-400 italic text-sm font-black uppercase tracking-widest">Belum ada data kelahiran ditambahkan</p>
</div>
</div>
<div class="mt-12 flex justify-end">
<button type="submit" class="w-full md:w-auto px-12 py-5 bg-indigo-600 text-white rounded-[2rem] font-black text-sm shadow-2xl hover:bg-indigo-700 transition-all active:scale-95 uppercase tracking-widest">
<i class="fas fa-save mr-2"></i> Finalisasi & Simpan Data
</button>
</div>
</form>
</div>
@push('scripts')
<script>
function kittenManager() {
return {
kittens: [],
addKitten() {
this.kittens.push({
nama: '',
jenis_kelamin: 'Jantan',
tgl_lahir: '',
registry: [], // HARUS ARRAY agar checkbox bekerja
});
},
removeKitten(index) {
this.kittens.splice(index, 1);
}
}
}
document.addEventListener('DOMContentLoaded', function() {
function setupAutocomplete(selectId, birthId, pedigreeId, microchipId) {
const selectEl = document.getElementById(selectId);
const birthEl = document.getElementById(birthId);
const pedEl = document.getElementById(pedigreeId);
const chipEl = document.getElementById(microchipId);
if (!selectEl) return;
selectEl.addEventListener('change', function() {
const catId = this.value;
if (!catId) {
if (birthEl) birthEl.value = '';
if (pedEl) pedEl.value = '';
if (chipEl) chipEl.value = '';
return;
}
fetch(`/api/cats/${catId}`)
.then(response => response.json())
.then(data => {
if (birthEl) birthEl.value = data.tgl_lahir || '';
if (pedEl) pedEl.value = data.pedigree || '';
if (chipEl) chipEl.value = data.microchip || '';
})
.catch(error => console.error('Fetch Error:', error));
});
}
setupAutocomplete('sire_select', 'sire_birth', 'sire_pedigree', 'sire_microchip');
setupAutocomplete('dam_select', 'dam_birth', 'dam_pedigree', 'dam_microchip');
});
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,306 @@
<x-app-layout>
<div class="max-w-7xl mx-auto pb-12">
{{-- 1. DISPLAY VALIDATION ERRORS --}}
@if ($errors->any())
<div class="mb-6 p-5 bg-rose-50 border-2 border-rose-100 rounded-[2rem] animate-fade-in shadow-sm">
<div class="flex items-center gap-3 mb-2">
<i class="fas fa-exclamation-circle text-rose-500"></i>
<h3 class="text-xs font-black text-rose-700 uppercase tracking-widest">Gagal Memperbarui Data:</h3>
</div>
<ul class="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-1">
@foreach ($errors->all() as $error)
<li class="text-[10px] text-rose-600 font-bold italic">• {{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('matting.update', $matting->id) }}" method="POST" enctype="multipart/form-data"
x-data="kittenManager({{ $matting->kittens->toJson() }})">
@csrf
@method('PATCH')
{{-- Header --}}
<div class="flex items-center justify-between mb-6">
<div class="flex items-center gap-4">
<a href="{{ route('matting.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tighter leading-none mb-1">EDIT BREEDING RECORD</h2>
<p class="text-[10px] text-slate-500 font-bold uppercase tracking-widest italic">Litter ID: #{{ str_pad($matting->id, 5, '0', STR_PAD_LEFT) }} • Update Lineage</p>
</div>
</div>
</div>
{{-- SIRE & DAM SECTION --}}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
{{-- SIRE --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-6 shadow-sm relative overflow-hidden group">
<div class="absolute -top-4 -right-4 opacity-5 group-hover:opacity-10 transition-opacity">
<i class="fas fa-mars text-9xl text-blue-600"></i>
</div>
<div class="relative z-10 space-y-5">
<h4 class="text-[11px] font-black text-blue-600 uppercase tracking-widest border-b border-blue-50 pb-3 italic flex items-center gap-2">
<i class="fas fa-mars"></i> Informasi Sire (Induk Jantan)
</h4>
<div class="space-y-4">
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Pilih Kucing Jantan</label>
<select name="sire_id" id="sire_select" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 font-bold text-xs focus:ring-blue-500">
@foreach($sires as $sire)
<option value="{{ $sire->id }}" {{ old('sire_id', $matting->sire_id) == $sire->id ? 'selected' : '' }}>{{ $sire->nama_kucing }} ({{ $sire->ras }})</option>
@endforeach
</select>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Tgl Lahir</label>
<input type="date" name="tgl_lahir_sire" id="sire_birth" value="{{ old('tgl_lahir_sire', optional($matting->tgl_lahir_sire)->format('Y-m-d')) }}" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">No. Pedigree</label>
<input type="text" name="no_pedigree_bapak" id="sire_pedigree" value="{{ old('no_pedigree_bapak', $matting->no_pedigree_bapak) }}" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">No. Microchip</label>
<input type="text" name="no_microchip_bapak" id="sire_microchip" value="{{ old('no_microchip_bapak', $matting->no_microchip_bapak) }}" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
</div>
</div>
</div>
{{-- DAM --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-6 shadow-sm relative overflow-hidden group">
<div class="absolute -top-4 -right-4 opacity-5 group-hover:opacity-10 transition-opacity">
<i class="fas fa-venus text-9xl text-pink-600"></i>
</div>
<div class="relative z-10 space-y-5">
<h4 class="text-[11px] font-black text-pink-600 uppercase tracking-widest border-b border-pink-50 pb-3 italic flex items-center gap-2">
<i class="fas fa-venus"></i> Informasi Dam (Induk Betina)
</h4>
<div class="space-y-4">
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Pilih Kucing Betina</label>
<select name="dam_id" id="dam_select" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 font-bold text-xs focus:ring-pink-500">
@foreach($dams as $dam)
<option value="{{ $dam->id }}" {{ old('dam_id', $matting->dam_id) == $dam->id ? 'selected' : '' }}>{{ $dam->nama_kucing }} ({{ $dam->ras }})</option>
@endforeach
</select>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Tgl Lahir</label>
<input type="date" name="tgl_lahir_dam" id="dam_birth" value="{{ old('tgl_lahir_dam', optional($matting->tgl_lahir_dam)->format('Y-m-d')) }}" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">No. Pedigree</label>
<input type="text" name="no_pedigree_ibu" id="dam_pedigree" value="{{ old('no_pedigree_ibu', $matting->no_pedigree_ibu) }}" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">No. Microchip</label>
<input type="text" name="no_microchip_ibu" id="dam_microchip" value="{{ old('no_microchip_ibu', $matting->no_microchip_ibu) }}" class="w-full mt-1 rounded-xl border-slate-100 text-xs font-bold bg-slate-50">
</div>
</div>
</div>
</div>
</div>
{{-- PERIODE & STATUS --}}
<div class="bg-slate-900 rounded-[2rem] p-6 text-white shadow-xl mb-10">
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<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' : '' }}>MONITORING</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>
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 mb-1 block">Kawin Awal</label>
<input type="date" name="tgl_kawin_awal" value="{{ old('tgl_kawin_awal', optional($matting->tgl_kawin_awal)->format('Y-m-d')) }}" class="w-full bg-white/10 border-white/10 rounded-xl py-3 text-xs font-black">
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 mb-1 block">Kawin Akhir</label>
<input type="date" name="tgl_kawin_akhir" value="{{ old('tgl_kawin_akhir', optional($matting->tgl_kawin_akhir)->format('Y-m-d')) }}" class="w-full bg-white/10 border-white/10 rounded-xl py-3 text-xs font-black">
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 mb-1 block">Anak Hidup</label>
<input type="number" name="jumlah_anak_hidup" value="{{ old('jumlah_anak_hidup', $matting->jumlah_anak_hidup) }}" class="w-full bg-white/10 border-white/10 rounded-xl py-3 text-xs font-black">
</div>
</div>
</div>
{{-- KITTENS DYNAMIC SECTION --}}
<div class="space-y-6">
<div class="flex items-center justify-between border-b border-slate-100 pb-4">
<h3 class="text-xl font-black text-slate-800 tracking-tighter uppercase italic">Daftar Kittens</h3>
<button type="button" @click="addKitten" class="px-6 py-3 bg-indigo-600 text-white text-[10px] font-black rounded-xl shadow-lg hover:bg-indigo-700 transition-all">
<i class="fas fa-plus-circle mr-2"></i> TAMBAH ANAK
</button>
</div>
<div class="grid grid-cols-1 gap-6">
<template x-for="(kitten, index) in kittens" :key="index">
<div class="bg-white border-2 border-slate-100 p-6 rounded-[2.5rem] relative space-y-6 animate-fade-in hover:border-indigo-200 transition-all">
<input type="hidden" :name="`kittens[${index}][id]`" x-model="kitten.id">
<button type="button" @click="removeKitten(index)" class="absolute top-4 right-4 w-10 h-10 bg-rose-50 text-rose-500 rounded-xl flex items-center justify-center hover:bg-rose-500 hover:text-white transition-all">
<i class="fas fa-times text-xs"></i>
</button>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="md:col-span-2">
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Nama Kitten</label>
<input type="text" :name="`kittens[${index}][nama]`" x-model="kitten.nama" class="w-full mt-1 px-4 py-3 rounded-xl border-slate-100 bg-slate-50 text-xs font-black text-slate-800" required>
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Gender</label>
<select :name="`kittens[${index}][jenis_kelamin]`" x-model="kitten.jenis_kelamin" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-bold">
<option value="Jantan">Jantan</option>
<option value="Betina">Betina</option>
</select>
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Tgl Lahir</label>
<input type="date" :name="`kittens[${index}][tgl_lahir]`" x-model="kitten.tgl_lahir" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-bold" required>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 items-start pt-2">
{{-- PERBAIKAN: REGISTRY MULTIPLE CHOICE --}}
<div class="md:col-span-2 space-y-2">
<label class="text-[9px] font-black uppercase text-slate-400 ml-1 block">Registry / Federasi (Pilihan Ganda)</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<template x-for="regOption in ['ICA', 'CFA', 'WCF', 'FFF']" :key="regOption">
<label class="block cursor-pointer group">
<input type="checkbox"
:name="`kittens[${index}][registry][]`"
:value="regOption"
x-model="kitten.registry"
class="sr-only peer">
<div class="py-2.5 text-center border border-slate-100 rounded-xl text-[9px] font-black text-slate-400
peer-checked:bg-indigo-600 peer-checked:text-white peer-checked:border-indigo-600
transition-all shadow-sm group-hover:border-slate-300">
<span x-text="regOption"></span>
</div>
</label>
</template>
</div>
</div>
<div>
<label class="text-[9px] font-black uppercase text-slate-400 ml-1">Kode EMS</label>
<input type="text" :name="`kittens[${index}][kode_ems]`" x-model="kitten.kode_ems" class="w-full mt-1 rounded-xl border-slate-100 bg-slate-50 text-xs font-black" placeholder="EMS">
</div>
<div class="flex items-center h-11 gap-3 px-4 bg-slate-50 rounded-xl border border-slate-100 mt-5">
<input type="checkbox" :name="`kittens[${index}][is_pedigree]`" x-model="kitten.is_pedigree" class="w-4 h-4 rounded text-indigo-600 focus:ring-indigo-500">
<label class="text-[9px] font-black uppercase text-slate-600">Pedigree OK</label>
</div>
</div>
{{-- File Upload --}}
<div class="pt-4 border-t border-slate-50">
<label class="text-[9px] font-black uppercase text-slate-400 ml-1 mb-2 block italic">Documents (Max 4)</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<template x-for="n in 4" :key="n">
<div class="p-3 bg-slate-50 rounded-xl border border-slate-100">
<div class="flex justify-between items-center mb-1.5">
<span class="text-[8px] font-black text-slate-300 uppercase">Slot <span x-text="n"></span></span>
<template x-if="kitten[`foto_ped_${n}`]">
<span class="flex h-1.5 w-1.5 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.6)]"></span>
</template>
</div>
<input type="file" :name="`kittens[${index}][foto_ped_${n}]`" class="text-[8px] w-full file:mr-2 file:py-1 file:px-2 file:rounded-lg file:border-0 file:text-[8px] file:font-black file:bg-white file:text-indigo-600 cursor-pointer">
</div>
</template>
</div>
</div>
</div>
</template>
</div>
</div>
<div class="mt-12 flex justify-end">
<button type="submit" class="px-12 py-5 bg-slate-900 text-white rounded-[2rem] font-black text-[10px] uppercase tracking-[0.2em] shadow-2xl hover:bg-black transition-all">
<i class="fas fa-sync-alt mr-2"></i> Update Breeding Record
</button>
</div>
</form>
</div>
@push('scripts')
<script>
function kittenManager(initialKittens) {
return {
kittens: initialKittens.map(k => {
// Logic untuk memastikan registry selalu dalam bentuk Array di Alpine
let registryArray = [];
if (Array.isArray(k.registry)) {
registryArray = k.registry;
} else if (typeof k.registry === 'string') {
try {
registryArray = JSON.parse(k.registry);
} catch(e) {
registryArray = k.registry.split(',').map(s => s.trim());
}
}
return {
...k,
tgl_lahir: k.tgl_lahir ? k.tgl_lahir.split('T')[0] : '',
is_pedigree: k.is_pedigree == 1,
registry: registryArray // Sekarang dipastikan Array
};
}),
addKitten() {
const defaultDate = document.querySelector('input[name="tgl_kawin_awal"]').value;
this.kittens.push({
id: null,
nama: '',
jenis_kelamin: 'Jantan',
tgl_lahir: defaultDate,
kode_ems: '',
registry: [], // Inisialisasi array kosong untuk record baru
is_pedigree: false
});
},
removeKitten(index) {
if(confirm('Hapus record anak ini? Profil di Manajemen Pasien juga akan terhapus setelah disimpan.')) {
this.kittens.splice(index, 1);
}
}
}
}
// Logic Autocomplete sama seperti sebelumnya
document.addEventListener('DOMContentLoaded', function() {
function setupAutocomplete(selectId, birthId, pedigreeId, microchipId) {
const selectEl = document.getElementById(selectId);
const birthEl = document.getElementById(birthId);
const pedEl = document.getElementById(pedigreeId);
const chipEl = document.getElementById(microchipId);
if (!selectEl) return;
selectEl.addEventListener('change', function() {
const catId = this.value;
if (!catId) return;
fetch(`/api/cats/${catId}`)
.then(response => response.json())
.then(data => {
if (birthEl) birthEl.value = data.tgl_lahir || '';
if (pedEl) pedEl.value = data.pedigree || '';
if (chipEl) chipEl.value = data.microchip || '';
})
.catch(error => console.error('Error:', error));
});
}
setupAutocomplete('sire_select', 'sire_birth', 'sire_pedigree', 'sire_microchip');
setupAutocomplete('dam_select', 'dam_birth', 'dam_pedigree', 'dam_microchip');
});
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,219 @@
<x-app-layout>
<div class="max-w-7xl mx-auto space-y-6 pb-12">
{{-- SECTION 1: Header & Statistik --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tighter uppercase leading-none">Breeding Log</h2>
<p class="text-[10px] text-slate-400 font-bold uppercase tracking-widest mt-1 italic">Monitoring silsilah & kelahiran • Real-time Lineage Data</p>
</div>
<div class="flex items-center gap-3">
<div class="hidden sm:block bg-white border border-slate-200 px-4 py-2 rounded-xl shadow-sm">
<p class="text-[8px] font-black text-slate-400 uppercase tracking-widest leading-none">Total Program</p>
<p class="text-sm font-black text-indigo-600 mt-1">{{ $mattings->total() }}</p>
</div>
{{-- PROTEKSI TOMBOL CREATE MATTING --}}
@can('check-access', ['matting', 'create'])
<a href="{{ route('matting.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> INPUT MATTING BARU
</a>
@endcan
</div>
</div>
{{-- SECTION 2: Tabel Matting --}}
<div class="bg-white border border-slate-200 rounded-[2rem] overflow-hidden shadow-sm">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse table-auto">
<thead class="bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100">
<tr>
<th class="px-6 py-4">Indukan (Sire x Dam)</th>
<th class="px-4 py-4 text-center">Periode Kawin</th>
<th class="px-4 py-4 text-center">Estimasi Lahir</th>
<th class="px-4 py-4 text-center">Hasil</th>
<th class="px-6 py-4 text-right" style="min-width: 280px;">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 font-medium text-slate-700">
@forelse($mattings as $m)
<tr class="hover:bg-slate-50/50 transition-all group">
{{-- Pasangan Indukan --}}
<td class="px-6 py-4 align-middle">
<div class="flex items-center gap-3">
@if($m->sire->foto || $m->dam->foto)
<div class="flex -space-x-3 shrink-0">
@if($m->sire->foto)
<div class="w-11 h-11 rounded-xl bg-slate-100 border-2 border-white overflow-hidden shadow-sm">
<img src="{{ asset('storage/'.$m->sire->foto) }}" class="w-full h-full object-cover">
</div>
@endif
@if($m->dam->foto)
<div class="w-11 h-11 rounded-xl bg-slate-100 border-2 border-white overflow-hidden shadow-sm">
<img src="{{ asset('storage/'.$m->dam->foto) }}" class="w-full h-full object-cover">
</div>
@endif
</div>
@endif
<div class="min-w-0">
<div class="text-xs font-black text-slate-800 flex items-center gap-1 whitespace-nowrap">
{{ $m->sire->nama_kucing }} <i class="fas fa-heart text-[8px] text-rose-400"></i> {{ $m->dam->nama_kucing }}
</div>
<div class="flex gap-1.5 mt-1.5">
<span class="text-[8px] font-black bg-slate-800 text-white px-2 py-0.5 rounded uppercase tracking-tighter shadow-sm">
{{ $m->sire->kode_ems ?? 'NO EMS' }}
</span>
<span class="text-[8px] font-black bg-indigo-100 text-indigo-700 px-2 py-0.5 rounded uppercase border border-indigo-200 tracking-tighter shadow-sm">
{{ $m->dam->kode_ems ?? 'NO EMS' }}
</span>
</div>
</div>
</div>
</td>
<td class="px-4 py-4 text-center align-middle whitespace-nowrap">
<p class="text-[10px] font-black text-slate-700 leading-none">
{{ $m->tgl_kawin_awal->format('d M') }} - {{ $m->tgl_kawin_akhir->format('d M Y') }}
</p>
<span class="text-[9px] text-slate-400 font-bold italic uppercase">{{ $m->tgl_kawin_awal->diffInDays($m->tgl_kawin_akhir) + 1 }} Hari</span>
</td>
<td class="px-4 py-4 text-center align-middle" x-data="breedingCountdown('{{ $m->tgl_kawin_awal->addDays(63)->format('Y-m-d') }}')">
<div :class="isUrgent ? 'bg-rose-50 border-rose-100' : 'bg-indigo-50 border-indigo-100'"
class="inline-flex flex-col px-3 py-1.5 rounded-xl border border-dashed transition-all w-28">
<div class="flex items-center justify-center gap-1.5 mb-0.5">
<i :class="isUrgent ? 'text-rose-500 animate-pulse' : 'text-indigo-400'" class="fas fa-stopwatch text-[9px]"></i>
<span class="text-[8px] font-black uppercase" :class="isUrgent ? 'text-rose-600' : 'text-indigo-600'">HPL: {{ $m->tgl_kawin_awal->addDays(63)->format('d/m') }}</span>
</div>
<p class="text-[10px] font-black" :class="isUrgent ? 'text-rose-700' : 'text-slate-700'">
<template x-if="daysRemaining > 0"><span x-text="daysRemaining + ' Hari'"></span></template>
<template x-if="daysRemaining === 0"><span>HARI H</span></template>
<template x-if="daysRemaining < 0"><span class="text-slate-400 italic">LEWAT</span></template>
</p>
</div>
</td>
<td class="px-4 py-4 text-center align-middle whitespace-nowrap">
<div class="flex flex-col gap-1 items-center">
<span class="px-3 py-1 bg-emerald-50 text-emerald-600 rounded-lg text-[10px] font-black border border-emerald-100 uppercase">
{{ $m->kittens_count }} Anak
</span>
{!! $m->status_badge !!}
</div>
</td>
<td class="px-6 py-4 text-right align-middle">
<div class="flex justify-end items-center gap-16">
{{-- PROTEKSI TOMBOL EDIT MATTING --}}
@can('check-access', ['matting', 'update'])
<a href="{{ route('matting.edit', $m->id) }}"
class="flex items-center gap-3 p-3 text-slate-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-2xl transition-all group/btn"
title="Edit Data">
<i class="fas fa-pencil-alt text-base"></i>
<span class="text-[10px] font-black uppercase hidden group-hover/btn:inline tracking-widest">Edit</span>
</a>
@endcan
{{-- PROTEKSI TOMBOL HAPUS MATTING --}}
@can('check-access', ['matting', 'delete'])
<form action="{{ route('matting.destroy', $m->id) }}" method="POST" class="inline m-0">
@csrf @method('DELETE')
<button type="button"
onclick="confirmDelete(this, '{{ addslashes($m->sire->nama_kucing) }} x {{ addslashes($m->dam->nama_kucing) }}')"
class="flex items-center gap-3 p-3 text-slate-400 hover:text-rose-500 hover:bg-rose-50 rounded-2xl transition-all group/btn"
title="Hapus Data">
<i class="fas fa-trash-alt text-base"></i>
<span class="text-[10px] font-black uppercase hidden group-hover/btn:inline tracking-widest">Hapus</span>
</button>
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-6 py-20 text-center text-slate-400 uppercase text-[10px] font-black tracking-widest">
Belum ada riwayat breeding
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="p-6 bg-slate-50/30 border-t border-slate-100">
{{ $mattings->links() }}
</div>
</div>
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
{{-- Script Alpine Init & SweetAlert Tetap Sama --}}
document.addEventListener('alpine:init', () => {
Alpine.data('breedingCountdown', (targetDate) => ({
target: new Date(targetDate),
daysRemaining: 0,
isUrgent: false,
init() {
const now = new Date();
now.setHours(0,0,0,0);
const targetDateObj = new Date(this.target);
targetDateObj.setHours(0,0,0,0);
const diffTime = targetDateObj - now;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
this.daysRemaining = diffDays;
this.isUrgent = diffDays <= 7 && diffDays >= 0;
}
}));
});
document.addEventListener('DOMContentLoaded', function() {
const Toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3500,
timerProgressBar: true
});
@if(session('success'))
Toast.fire({ icon: 'success', title: '{{ session('success') }}' });
@endif
@if(session('error'))
Swal.fire({
icon: 'info',
title: 'Informasi Sistem',
text: '{{ session('error') }}',
confirmButtonColor: '#4f46e5',
customClass: { popup: 'rounded-[2rem]' }
});
@endif
});
function confirmDelete(button, pairName) {
Swal.fire({
title: 'Hapus Riwayat?',
html: "Anda akan menghapus data breeding:<br><b class='text-indigo-600'>" + pairName + "</b>",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#ef4444',
cancelButtonColor: '#64748b',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
reverseButtons: true,
customClass: { popup: 'rounded-[2rem]' }
}).then((result) => {
if (result.isConfirmed) {
button.closest('form').submit();
}
});
}
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,199 @@
<x-app-layout>
<div class="max-w-[1600px] mx-auto p-4 sm:px-6 lg:px-8 py-8 space-y-6">
{{-- 1. ACTION HEADER --}}
<div class="flex flex-col md:flex-row justify-between items-center gap-6 bg-white p-8 rounded-[3rem] border border-slate-100 shadow-sm">
<div>
<h1 class="text-3xl font-black text-slate-800 tracking-tighter uppercase leading-none">
Intelligence <span class="text-indigo-600">Center</span>
</h1>
<p class="text-[10px] text-slate-400 font-bold uppercase tracking-[0.3em] mt-2">
Laporan: {{ strtoupper(str_replace('_', ' ', $module)) }}
</p>
</div>
{{-- PROTEKSI EXPORT: Hanya muncul jika memiliki izin 'create' pada modul reports --}}
@can('check-access', ['reports', 'create'])
<div class="flex items-center gap-3">
{{-- Export PDF --}}
<a href="{{ route('admin.reports.index', array_merge(request()->query(), ['export' => 'pdf'])) }}"
class="bg-rose-600 text-white px-6 py-3.5 rounded-2xl text-[10px] font-black uppercase tracking-widest hover:bg-rose-700 shadow-lg shadow-rose-100 transition-all flex items-center gap-2">
<i class="fas fa-file-pdf text-sm"></i> Export PDF
</a>
{{-- Export Excel --}}
<a href="{{ route('admin.reports.index', array_merge(request()->query(), ['export' => 'xlsx'])) }}"
class="flex items-center gap-2 px-6 py-3.5 bg-emerald-50 text-emerald-700 border border-emerald-100 rounded-2xl text-[10px] font-black uppercase tracking-widest hover:bg-emerald-600 hover:text-white hover:border-emerald-600 shadow-lg shadow-emerald-100/50 transition-all active:scale-95 group">
<i class="fas fa-file-excel text-sm text-emerald-600 group-hover:text-white transition-colors"></i>
<span>Export Excel</span>
</a>
</div>
@endcan
</div>
{{-- 2. ADVANCED FILTER PANEL (TETAP UTUH) --}}
<div class="bg-white rounded-[3rem] border border-slate-100 shadow-sm p-8">
<form action="{{ route('admin.reports.index') }}" method="GET" class="grid grid-cols-1 md:grid-cols-4 gap-8">
{{-- Source & Search --}}
<div class="space-y-3">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest ml-2">Data Source</label>
<select name="module" onchange="this.form.submit()" class="w-full bg-slate-50 border-none rounded-2xl text-xs font-bold text-slate-700">
<option value="cats" {{ $module == 'cats' ? 'selected' : '' }}>Populasi Kucing</option>
<option value="vaccine" {{ $module == 'vaccine' ? 'selected' : '' }}>Jadwal Vaksin</option>
<option value="deworming" {{ $module == 'deworming' ? 'selected' : '' }}>Jadwal Obat Cacing</option>
<option value="flea" {{ $module == 'flea' ? 'selected' : '' }}>Jadwal Obat Kutu</option>
<option value="grooming" {{ $module == 'grooming' ? 'selected' : '' }}>Jadwal Grooming</option>
<option value="medicine" {{ $module == 'medicine' ? 'selected' : '' }}>Inventori Obat</option>
<option value="food" {{ $module == 'food' ? 'selected' : '' }}>Inventori Makanan</option>
<option value="supplies" {{ $module == 'supplies' ? 'selected' : '' }}>Perlengkapan</option>
</select>
<input type="text" name="q" value="{{ request('q') }}" placeholder="Search Keyword..." class="w-full bg-slate-50 border-none rounded-2xl text-xs font-bold">
</div>
{{-- PIC & Period --}}
<div class="space-y-3">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest ml-2">Staff & Periode</label>
<select name="pic_id" class="w-full bg-slate-50 border-none rounded-2xl text-xs font-bold">
<option value="">Semua PIC Staff</option>
@foreach($users as $user)
<option value="{{ $user->id }}" {{ request('pic_id') == $user->id ? 'selected' : '' }}>{{ $user->name }}</option>
@endforeach
</select>
<div class="grid grid-cols-2 gap-2">
<input type="date" name="start_date" value="{{ request('start_date') }}" class="w-full bg-slate-50 border-none rounded-2xl text-[10px] font-bold">
<input type="date" name="end_date" value="{{ request('end_date') }}" class="w-full bg-slate-50 border-none rounded-2xl text-[10px] font-bold">
</div>
</div>
{{-- Status Filter --}}
<div class="space-y-3">
<label class="block text-[10px] font-black text-slate-400 uppercase tracking-widest ml-2">Status Analitik</label>
<select name="status" class="w-full bg-slate-50 border-none rounded-2xl text-xs font-bold">
<option value="">Pilih Status</option>
@if($module == 'cats')
<option value="sehat" {{ request('status') == 'sehat' ? 'selected' : '' }}>Sehat</option>
<option value="sakit" {{ request('status') == 'sakit' ? 'selected' : '' }}>Dalam Perawatan</option>
@elseif(in_array($module, ['vaccine', 'deworming', 'flea', 'grooming']))
<option value="not_set" {{ request('status') == 'not_set' ? 'selected' : '' }}>Date Not Set</option>
<option value="overdue" {{ request('status') == 'overdue' ? 'selected' : '' }}>Overdue (Terlewat)</option>
<option value="soon" {{ request('status') == 'soon' ? 'selected' : '' }}>Akan Datang (7 Hari)</option>
@elseif(in_array($module, ['medicine', 'food', 'supplies']))
<option value="critical" {{ request('status') == 'critical' ? 'selected' : '' }}>Stok Kritis (< 5)</option>
<option value="expired" {{ request('status') == 'expired' ? 'selected' : '' }}>Sudah Expired</option>
<option value="safe" {{ request('status') == 'safe' ? 'selected' : '' }}>Stok Aman</option>
@endif
</select>
<button type="submit" class="w-full bg-indigo-600 text-white py-3 rounded-2xl text-[10px] font-black uppercase tracking-widest hover:bg-slate-900 shadow-xl shadow-indigo-100 transition-all">
<i class="fas fa-filter mr-2"></i> Apply Filter
</button>
</div>
{{-- Summary & Reset --}}
<div class="flex flex-col justify-between">
<div class="bg-indigo-50 p-5 rounded-[2rem] border border-indigo-100 text-center">
<p class="text-[9px] font-black text-indigo-400 uppercase tracking-widest mb-1">Total Data</p>
<span class="text-3xl font-black text-indigo-700 leading-none">{{ $results->total() }}</span>
</div>
<a href="{{ route('admin.reports.index') }}" class="w-full bg-slate-100 text-slate-400 py-3 rounded-2xl text-[10px] font-black uppercase tracking-widest text-center hover:bg-rose-50 hover:text-rose-600 transition-all">
<i class="fas fa-sync-alt mr-2"></i> Reset Filter
</a>
</div>
</form>
</div>
{{-- 3. DATA TABLE (TETAP UTUH) --}}
<div class="bg-white rounded-[3rem] border border-slate-100 shadow-sm overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left">
<thead class="bg-slate-50/50 border-b border-slate-100">
<tr>
<th class="p-6 text-[10px] font-black text-slate-400 uppercase tracking-widest">Informasi Utama</th>
<th class="p-6 text-[10px] font-black text-slate-400 uppercase tracking-widest">Detail Data</th>
<th class="p-6 text-[10px] font-black text-slate-400 uppercase tracking-widest">Staff & Jadwal</th>
<th class="p-6 text-[10px] font-black text-slate-400 uppercase tracking-widest text-right">Status Aktif</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@forelse($results as $item)
@php
$dateCol = match($module) {
'cats' => 'tanggal_lahir',
'vaccine' => 'jadwal_vaksin',
'deworming' => 'jadwal_obat_cacing',
'flea' => 'jadwal_obat_kutu',
'grooming' => 'jadwal_grooming',
'medicine', 'food', 'supplies' => 'expired_date',
default => 'created_at'
};
$dateValue = $item->{$dateCol};
@endphp
<tr class="group hover:bg-slate-50/30 transition-all">
<td class="p-6">
<p class="text-xs font-black text-slate-800 uppercase leading-none mb-1">
{{ $item->nama_kucing ?? $item->nama_obat ?? $item->nama_makanan ?? $item->nama_perlengkapan }}
</p>
<p class="text-[9px] text-slate-400 font-bold uppercase tracking-widest italic">ID: #{{ $item->id }}</p>
</td>
<td class="p-6">
@if(isset($item->qty))
<div class="flex items-center gap-2">
<span class="text-xs font-black {{ $item->qty < 5 ? 'text-rose-600' : 'text-slate-700' }}">{{ $item->qty }}</span>
<span class="text-[9px] font-bold text-slate-400 uppercase tracking-widest">{{ $item->unit ?? 'Unit' }}</span>
</div>
@else
<span class="text-[10px] font-black text-slate-500 uppercase">{{ $item->ras ?? '-' }}</span>
@endif
</td>
<td class="p-6">
<div class="flex flex-col gap-1">
<span class="text-[10px] font-black text-slate-600 uppercase">{{ $item->pic->name ?? 'System' }}</span>
<span class="text-[9px] font-bold text-slate-400 italic">
<i class="far fa-calendar-alt mr-1"></i> {{ $dateValue ? \Carbon\Carbon::parse($dateValue)->format('d/m/Y') : 'DATE NOT SET' }}
</span>
</div>
</td>
<td class="p-6 text-right">
@if(in_array($module, ['vaccine', 'deworming', 'flea', 'grooming']))
@if(empty($dateValue))
<span class="px-3 py-1.5 bg-slate-100 text-slate-400 rounded-lg text-[8px] font-black uppercase">Date Not Set</span>
@elseif(\Carbon\Carbon::parse($dateValue)->isPast())
<span class="px-3 py-1.5 bg-rose-100 text-rose-600 rounded-lg text-[8px] font-black uppercase animate-pulse">Overdue</span>
@elseif(\Carbon\Carbon::parse($dateValue)->diffInDays(now()) <= 7)
<span class="px-3 py-1.5 bg-amber-100 text-amber-600 rounded-lg text-[8px] font-black uppercase">Soon</span>
@else
<span class="px-3 py-1.5 bg-emerald-100 text-emerald-600 rounded-lg text-[8px] font-black uppercase">Scheduled</span>
@endif
@elseif(in_array($module, ['medicine', 'food', 'supplies']))
@if(isset($item->expired_date) && \Carbon\Carbon::parse($item->expired_date)->isPast())
<span class="px-3 py-1.5 bg-rose-600 text-white rounded-lg text-[8px] font-black uppercase">Expired</span>
@elseif($item->qty < 5)
<span class="px-3 py-1.5 bg-rose-100 text-rose-600 rounded-lg text-[8px] font-black uppercase">Low Stock</span>
@else
<span class="px-3 py-1.5 bg-slate-100 text-slate-500 rounded-lg text-[8px] font-black uppercase">Safe</span>
@endif
@else
<span class="px-3 py-1.5 {{ $item->riwayat_sakit_terakhir ? 'bg-amber-100 text-amber-600' : 'bg-emerald-100 text-emerald-600' }} rounded-lg text-[8px] font-black uppercase">
{{ $item->riwayat_sakit_terakhir ? 'Treatment' : 'Healthy' }}
</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="4" class="p-20 text-center text-[10px] font-black text-slate-300 uppercase tracking-widest">Data Tidak Ditemukan</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
{{-- PAGINATION --}}
<div class="mt-8">
{{ $results->links() }}
</div>
</div>
</x-app-layout>
@@ -0,0 +1,140 @@
<x-app-layout>
{{-- Container utama: Dikunci pada 1200px agar tampilan terpusat dan elegan --}}
<div class="max-w-[1200px] mx-auto pb-16 px-6 space-y-8 font-sans antialiased tracking-tight">
{{-- 1. HEADER & DYNAMIC STATS --}}
<div class="flex flex-col lg:flex-row items-center justify-between gap-6">
<div class="text-center lg:text-left">
<h2 class="text-2xl font-black text-slate-900 uppercase tracking-tighter leading-none">Lineage Archive</h2>
<div class="flex items-center justify-center lg:justify-start gap-2 mt-2">
<span class="w-8 h-0.5 bg-indigo-600"></span>
<p class="text-[10px] text-slate-400 font-bold uppercase tracking-[0.2em]">Official Breeding Database</p>
</div>
</div>
<div class="flex items-center gap-4">
{{-- Quick Stats Widget (TETAP UTUH) --}}
<div class="bg-white px-5 py-3 rounded-2xl border border-slate-200 shadow-sm flex items-center gap-4">
<div class="text-right">
<p class="text-[8px] text-slate-400 font-black uppercase tracking-widest">Total Record</p>
<p class="text-lg font-black text-slate-800 leading-none">{{ $mattings->total() }}</p>
</div>
<div class="w-10 h-10 bg-indigo-50 text-indigo-600 rounded-xl flex items-center justify-center text-sm shadow-inner">
<i class="fas fa-database"></i>
</div>
</div>
{{-- PROTEKSI EXPORT PDF: Hanya muncul jika memiliki izin 'create' pada modul matting_reports --}}
@can('check-access', ['matting_reports', 'create'])
<a href="{{ request()->fullUrlWithQuery(['export' => 'pdf']) }}" class="bg-slate-900 px-6 py-3 rounded-2xl text-[10px] font-black text-white uppercase hover:bg-indigo-600 transition-all shadow-lg flex items-center gap-3">
<i class="fas fa-file-pdf"></i> PDF Export
</a>
@endcan
</div>
</div>
{{-- 2. FILTER BAR: Proporsional & Ramping (TETAP UTUH) --}}
<form action="{{ route('admin.reports.matting.index') }}" method="GET" class="bg-white p-3 rounded-[1.5rem] border border-slate-200 shadow-sm grid grid-cols-1 md:grid-cols-4 lg:grid-cols-5 gap-3">
<div class="relative">
<i class="fas fa-search absolute left-4 top-1/2 -translate-y-1/2 text-slate-300 text-xs"></i>
<input type="text" name="parent_name" value="{{ request('parent_name') }}" placeholder="Search ancestry..." class="w-full bg-slate-50 border-none rounded-xl text-xs font-bold py-3 pl-11 focus:ring-2 focus:ring-indigo-500/20">
</div>
<select name="ras" class="bg-slate-50 border-none rounded-xl text-xs font-bold py-3 focus:ring-2 focus:ring-indigo-500/20 cursor-pointer">
<option value="">All Breeds</option>
@foreach($breeds as $breed)
<option value="{{ $breed }}" {{ request('ras') == $breed ? 'selected' : '' }}>{{ $breed }}</option>
@endforeach
</select>
<select name="registry" class="bg-slate-50 border-none rounded-xl text-xs font-bold py-3 focus:ring-2 focus:ring-indigo-500/20 cursor-pointer">
<option value="">All Registry</option>
@foreach($registries as $reg)
<option value="{{ $reg }}" {{ request('registry') == $reg ? 'selected' : '' }}>{{ $reg }}</option>
@endforeach
</select>
<input type="date" name="start_date" value="{{ request('start_date') }}" class="bg-slate-50 border-none rounded-xl text-xs font-bold py-3 focus:ring-2 focus:ring-indigo-500/20">
<button type="submit" class="bg-indigo-600 text-white rounded-xl text-[10px] font-black uppercase hover:bg-slate-900 transition-all shadow-md">Apply Filter</button>
</form>
<hr class="border-slate-100 mx-4">
{{-- 3. ARCHIVE LIST: Proportional Card Grid --}}
<div class="space-y-4">
{{-- Legend Header --}}
<div class="hidden lg:grid grid-cols-12 px-10 py-1 text-[9px] font-black text-slate-400 uppercase tracking-[0.2em]">
<div class="col-span-5">Breeding Partnership</div>
<div class="col-span-3 text-center">Record Statistics</div>
<div class="col-span-2 text-center">Registrar PIC</div>
<div class="col-span-2 text-right">Access</div>
</div>
@forelse($mattings as $matting)
<div class="bg-white border border-slate-200 rounded-[2rem] p-5 hover:border-indigo-400 hover:shadow-xl hover:shadow-indigo-50/50 transition-all group relative overflow-hidden">
{{-- Decorative Initial In Latar Belakang --}}
<span class="absolute -right-2 -bottom-4 text-7xl font-black text-slate-50 italic group-hover:text-indigo-50/50 transition-colors select-none z-0">
{{ substr($matting->pic->name ?? 'S', 0, 1) }}
</span>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6 items-center relative z-10">
{{-- Column 1: Identity (Sire/Dam) --}}
<div class="lg:col-span-5 flex items-center gap-6 min-w-0">
<div class="flex -space-x-4 shrink-0">
<img src="{{ $matting->sire->photo ? asset('storage/' . $matting->sire->photo) : asset('images/default-cat.png') }}" class="w-12 h-12 rounded-[1rem] border-4 border-white shadow-md object-cover transform group-hover:-rotate-6 transition-transform">
<img src="{{ $matting->dam->photo ? asset('storage/' . $matting->dam->photo) : asset('images/default-cat.png') }}" class="w-12 h-12 rounded-[1rem] border-4 border-white shadow-md object-cover transform group-hover:rotate-6 transition-transform">
</div>
<div class="min-w-0 flex-1">
<h4 class="text-sm font-black text-slate-800 uppercase truncate leading-tight mb-1">
{{ $matting->sire->nama_kucing }} <span class="text-slate-300 mx-1">×</span> {{ $matting->dam->nama_kucing }}
</h4>
<div class="flex items-center gap-3">
<span class="text-[8px] font-black text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded tracking-widest leading-none">#{{ $matting->id }}</span>
<span class="text-[10px] text-slate-400 font-bold uppercase tracking-tighter leading-none">{{ $matting->sire->ras ?? 'Unknown' }}</span>
</div>
</div>
</div>
{{-- Column 2: Stats --}}
<div class="lg:col-span-3 flex items-center justify-around border-y lg:border-y-0 lg:border-x border-slate-100 py-3 lg:py-0 px-4">
<div class="text-center">
<p class="text-[8px] text-slate-400 font-black uppercase mb-1 tracking-widest">Offspring</p>
<p class="text-[13px] font-black text-slate-800 leading-none">{{ $matting->kittens_count }} <span class="text-[10px] text-slate-300 font-bold uppercase ml-0.5">Paws</span></p>
</div>
<div class="text-center">
<p class="text-[8px] text-slate-400 font-black uppercase mb-1 tracking-widest">Duration</p>
<p class="text-[13px] font-black text-slate-800 leading-none">{{ $matting->tgl_kawin_awal->diffInDays($matting->tgl_kawin_akhir) + 1 }} <span class="text-[10px] text-slate-300 font-bold uppercase ml-0.5">Days</span></p>
</div>
</div>
{{-- Column 3: Registrar --}}
<div class="lg:col-span-2 flex flex-col items-center lg:items-start px-4 min-w-0">
<p class="text-[8px] text-slate-400 font-black uppercase mb-1.5 tracking-widest">Registrar</p>
<p class="text-[11px] font-bold text-slate-600 uppercase italic truncate w-full text-center lg:text-left tracking-tight">
{{ $matting->pic->name ?? 'System' }}
</p>
</div>
{{-- Column 4: Action (PROTEKSI READ: Mengikuti matriks permission matting_reports) --}}
<div class="lg:col-span-2 flex justify-end shrink-0">
@can('check-access', ['matting_reports', 'read'])
<a href="{{ route('admin.reports.matting.show', $matting->id) }}" class="w-full lg:w-auto px-6 py-3 bg-slate-900 text-white text-[10px] font-black uppercase rounded-2xl hover:bg-indigo-600 transition-all shadow-md flex items-center justify-center gap-2 group-hover:scale-105">
<i class="fas fa-certificate"></i> View Record
</a>
@endcan
</div>
</div>
</div>
@empty
<div class="py-24 bg-white border-2 border-dashed border-slate-100 rounded-[2.5rem] text-center">
<i class="fas fa-folder-open text-4xl text-slate-100 mb-4"></i>
<p class="text-slate-400 font-bold uppercase text-[10px] tracking-[0.4em]">Archive is empty</p>
</div>
@endforelse
</div>
{{-- 4. PAGINATION (TETAP UTUH) --}}
<div class="pt-8 flex justify-center">
{{ $mattings->links() }}
</div>
</div>
</x-app-layout>
@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page { margin: 0.8cm; }
body { font-family: 'Helvetica', sans-serif; font-size: 9px; color: #334155; line-height: 1.4; }
/* Header Section */
.header { text-align: center; border-bottom: 3px double #4f46e5; padding-bottom: 10px; margin-bottom: 20px; }
.header h1 { margin: 0; font-size: 18px; color: #1e293b; text-transform: uppercase; letter-spacing: 2px; }
.header p { margin: 5px 0 0; color: #64748b; font-weight: bold; letter-spacing: 1px; }
/* Meta Info Section */
.meta { width: 100%; margin-bottom: 15px; }
.meta td { padding: 2px 0; font-weight: bold; font-size: 8px; color: #475569; }
/* Table Styling */
table { width: 100%; border-collapse: collapse; margin-top: 10px; table-layout: fixed; }
th { background: #f8fafc; border: 1px solid #e2e8f0; padding: 8px 4px; text-transform: uppercase; font-size: 7px; color: #64748b; text-align: center; }
td { border: 1px solid #e2e8f0; padding: 6px 4px; word-wrap: break-word; vertical-align: middle; }
/* Branding Colors */
.row-sire { background-color: #f5f7ff; } /* Light Indigo */
.row-dam { background-color: #fff9fa; } /* Light Rose */
.text-sire { color: #4f46e5; font-weight: bold; }
.text-dam { color: #e11d48; font-weight: bold; }
.badge { padding: 2px 5px; border-radius: 4px; font-size: 7px; font-weight: bold; color: white; display: inline-block; }
.bg-indigo { background-color: #4f46e5; }
.bg-rose { background-color: #e11d48; }
.italic-slate { font-style: italic; color: #94a3b8; font-size: 7px; }
</style>
</head>
<body>
<div class="header">
<h1>ILY CATTERY - STRATEGIC BREEDING REPORT</h1>
<p>PARENTAL LINEAGE & ANALYTICS REPORT</p>
</div>
<table class="meta">
<tr>
<td width="50%">DICETAK OLEH: {{ strtoupper(Auth::user()->name) }}</td>
<td width="50%" style="text-align: right">PERIODE LAPORAN: {{ now()->translatedFormat('d F Y') }}</td>
</tr>
<tr>
<td>TOTAL DATA: {{ count($data) }} RECORDS</td>
<td style="text-align: right">SISTEM: ILYCATS MANAGEMENT UNIT</td>
</tr>
</table>
<table>
<thead>
<tr>
<th width="15%">Nama Indukan (♂/♀)</th>
<th width="12%">Silsilah (Sire x Dam)</th>
<th width="10%">Registry</th>
<th width="10%">EMS Code</th>
<th width="18%">No. Pedigree</th>
<th width="15%">Microchip</th>
<th width="10%">Tgl Kawin</th>
<th width="10%">Status</th>
</tr>
</thead>
<tbody>
@foreach($data as $item)
{{-- Baris Bapak (Sire) --}}
<tr class="row-sire">
<td class="text-sire">
♂ {{ strtoupper($item->sire->nama_kucing ?? 'N/A') }}
<div class="italic-slate">{{ $item->sire->ras ?? '-' }}</div>
</td>
<td style="text-align: center; font-weight: bold; font-size: 7px;">SIRE UNIT</td>
{{-- Registry: Menampilkan Asosiasi (ICA/CFA/WCF) --}}
<td style="text-align: center">
{{ is_array($item->sire->registry) ? implode(', ', $item->sire->registry) : ($item->sire->registry ?? '-') }}
</td>
{{-- EMS Code: Diambil dari kolom kode_ems di tabel cats --}}
<td style="text-align: center" class="text-sire">
{{ $item->sire->kode_ems ?? '-' }}
</td>
{{-- No Pedigree: Diambil dari kolom pedigree di tabel cats --}}
<td style="text-align: center">
{{ $item->sire->pedigree ?? 'NON PEDIGREE' }}
</td>
{{-- Microchip: Diambil dari kolom microchip di tabel cats --}}
<td style="text-align: center">
{{ $item->sire->microchip ?? '-' }}
</td>
{{-- Tgl Kawin: Rowspan untuk menggabung baris bapak & ibu --}}
<td rowspan="2" style="text-align: center; font-weight: bold;">
{{ \Carbon\Carbon::parse($item->tgl_kawin_awal)->format('d/m/Y') }}
</td>
<td style="text-align: center">
<span class="badge bg-indigo">{{ strtoupper($item->sire->status ?? 'BREEDING') }}</span>
</td>
</tr>
{{-- Baris Ibu (Dam) --}}
<tr class="row-dam">
<td class="text-dam">
♀ {{ strtoupper($item->dam->nama_kucing ?? 'N/A') }}
<div class="italic-slate">{{ $item->dam->ras ?? '-' }}</div>
</td>
<td style="text-align: center; font-size: 7px; color: #64748b;">
Mated with {{ $item->sire->nama_kucing ?? 'Sire' }}
</td>
<td style="text-align: center">
{{ is_array($item->dam->registry) ? implode(', ', $item->dam->registry) : ($item->dam->registry ?? '-') }}
</td>
{{-- EMS Code Ibu --}}
<td style="text-align: center" class="text-dam">
{{ $item->dam->kode_ems ?? '-' }}
</td>
{{-- No Pedigree Ibu --}}
<td style="text-align: center">
{{ $item->dam->pedigree ?? 'NON PEDIGREE' }}
</td>
{{-- Microchip Ibu --}}
<td style="text-align: center">
{{ $item->dam->microchip ?? '-' }}
</td>
{{-- Tgl Kawin di-rowspan dari baris sire di atas --}}
<td style="text-align: center">
<span class="badge bg-rose">{{ strtoupper($item->dam->status ?? 'BREEDING') }}</span>
</td>
</tr>
{{-- Separator Row --}}
<tr><td colspan="8" style="border:none; height: 8px; background: white;"></td></tr>
@endforeach
</tbody>
</table>
<div style="margin-top: 30px; font-size: 7px; text-align: center; color: #94a3b8;">
Laporan ini dihasilkan secara otomatis oleh IlyCats Management System. Segala data silsilah telah diverifikasi sesuai standar Registry (ICA/CFA/WCF).
</div>
</body>
</html>
@@ -0,0 +1,170 @@
<x-app-layout>
{{-- Container utama: Fokus, Ramping, & Aestetik --}}
<div class="max-w-[1100px] mx-auto pb-12 px-6 space-y-8 font-sans antialiased tracking-tight">
{{-- 1. HEADER: Slim Identity --}}
<div class="flex items-center justify-between bg-white px-6 py-4 rounded-2xl border border-slate-200/60 shadow-sm">
<div class="flex items-center gap-5">
{{-- Back Button --}}
<a href="{{ route('admin.reports.matting.index') }}" class="w-10 h-10 flex items-center justify-center text-slate-400 hover:text-slate-900 transition-all bg-slate-50 rounded-xl border border-slate-100">
<i class="fas fa-chevron-left text-xs"></i>
</a>
<div class="h-8 w-px bg-slate-100"></div>
<div>
<h2 class="text-[14px] font-black text-slate-800 uppercase tracking-tighter leading-none">Lineage Archive</h2>
<p class="text-[8px] text-slate-400 font-bold uppercase tracking-[0.3em] mt-1.5 italic">Official Record #{{ $matting->id }}</p>
</div>
</div>
{{-- Registrar Signature Style --}}
<div class="flex items-center gap-5 border-l border-slate-100 pl-6">
<div class="text-right leading-none">
<p class="text-[8px] text-slate-400 font-black uppercase mb-1 tracking-tighter">Registrar</p>
<p class="text-[10px] font-bold text-slate-600 uppercase italic">{{ $matting->pic->name ?? 'Admin' }}</p>
</div>
<span class="text-slate-950 font-black text-4xl italic leading-none select-none tracking-tighter opacity-20">
{{ substr($matting->pic->name ?? 'S', 0, 1) }}.
</span>
</div>
</div>
{{-- 2. PARENT CARDS: Sire & Dam --}}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@foreach([
['role' => 'Sire (Pejantan)', 'cat' => $matting->sire, 'theme' => 'indigo', 'icon' => 'fa-mars'],
['role' => 'Dam (Indukan)', 'cat' => $matting->dam, 'theme' => 'rose', 'icon' => 'fa-venus']
] as $p)
<div class="bg-white border border-slate-200/70 rounded-[2rem] p-6 flex items-center gap-8 shadow-sm group overflow-hidden relative">
{{-- Foto Parent --}}
<div class="shrink-0 relative z-10">
<img src="{{ $p['cat']->photo ? asset('storage/' . $p['cat']->photo) : asset('images/default-cat.png') }}"
class="w-20 h-20 object-cover rounded-2xl border-2 border-white shadow-md transition-transform duration-700 group-hover:scale-110" alt="Parent">
<span class="absolute -top-2 -left-2 w-7 h-7 bg-{{ $p['theme'] }}-600 text-white rounded-xl flex items-center justify-center text-[10px] shadow-lg border-2 border-white">
<i class="fas {{ $p['icon'] }}"></i>
</span>
</div>
<div class="flex-1 min-w-0 z-10">
<div class="flex items-center justify-between mb-2">
<span class="text-[9px] font-black text-{{ $p['theme'] }}-600 uppercase tracking-widest">{{ $p['role'] }}</span>
<span class="text-[8px] font-black bg-slate-900 text-white px-2 py-0.5 rounded-md uppercase">EMS: {{ $p['cat']->kode_ems ?? '-' }}</span>
</div>
<h3 class="text-base font-black text-slate-800 uppercase truncate mb-4 tracking-tight leading-none">{{ $p['cat']->nama_kucing }}</h3>
{{-- Data Teknis: Pedigree & Microchip (Fixed Column) --}}
<div class="grid grid-cols-2 gap-x-4 border-t border-slate-50 pt-4 mt-1">
<div class="min-w-0">
<p class="text-[8px] text-slate-400 font-bold uppercase mb-1.5 tracking-wider">Pedigree</p>
<p class="text-[10px] font-mono font-bold text-slate-700 truncate tracking-tighter italic leading-none">
{{ $p['cat']->pedigree ?? '-' }}
</p>
</div>
<div class="min-w-0">
<p class="text-[8px] text-slate-400 font-bold uppercase mb-1.5 tracking-wider">Microchip</p>
<p class="text-[10px] font-mono font-bold text-slate-700 truncate tracking-tighter italic leading-none">
{{ $p['cat']->microchip ?? '-' }}
</p>
</div>
</div>
</div>
</div>
@endforeach
</div>
{{-- 3. TIMELINE PILL: Grand Wide & Bold Version --}}
<div class="flex items-center justify-center py-14">
{{--
- max-w-[1000px]: Diperlebar agar hampir menyamai lebar container utama (1100px).
- py-5: Dibuat sedikit lebih tinggi agar card terlihat lebih kokoh.
- px-72: Padding horizontal ditingkatkan agar font mikro tetap berada di area tengah yang aman.
--}}
<div class="bg-slate-900 text-white w-full max-w-[1000px] py-5 rounded-full flex items-center justify-between px-72 shadow-[0_30px_60px_-15px_rgba(0,0,0,0.6)] border border-slate-800/60 mx-auto group relative">
{{-- Sisi Kiri: Started --}}
<div class="text-center shrink-0">
{{-- Font TETAP Mikro 4.5px --}}
<p class="text-[4.5px] text-slate-500 font-black uppercase mb-1.5 tracking-[0.6em] leading-none opacity-80">Started</p>
{{-- Font TETAP Mikro 7.5px --}}
<span class="text-[7.5px] font-black text-slate-100 tracking-tighter leading-none whitespace-nowrap">
{{ $matting->tgl_kawin_awal->format('d / m / Y') }}
</span>
</div>
{{-- Tengah: The Bridge --}}
<div class="flex flex-col items-center flex-1 px-10">
<i class="fas fa-heart text-rose-500 text-[7px] mb-2 animate-pulse"></i>
<div class="flex items-center w-full max-w-[140px] gap-4">
<div class="h-[0.5px] flex-1 bg-slate-800"></div>
{{-- Font Durasi TETAP Mikro 6px --}}
<span class="text-[6px] font-black uppercase text-indigo-400/80 tracking-[0.2em] italic leading-none whitespace-nowrap">
{{ $matting->tgl_kawin_awal->diffInDays($matting->tgl_kawin_akhir) + 1 }} Days Process
</span>
<div class="h-[0.5px] flex-1 bg-slate-800"></div>
</div>
</div>
{{-- Sisi Kanan: Finished --}}
<div class="text-center shrink-0">
<p class="text-[4.5px] text-slate-500 font-black uppercase mb-1.5 tracking-[0.6em] leading-none opacity-80">Finished</p>
<span class="text-[7.5px] font-black text-slate-100 tracking-tighter leading-none whitespace-nowrap">
{{ $matting->tgl_kawin_akhir->format('d / m / Y') }}
</span>
</div>
</div>
</div>
{{-- 4. OFFSPRING RECORDS --}}
<div class="space-y-6">
<div class="flex items-center gap-4 px-2">
<div class="h-4 w-1 bg-indigo-500 rounded-full"></div>
<h3 class="text-[11px] font-black text-slate-400 uppercase tracking-[0.3em]">Offspring Records ({{ $matting->kittens->count() }})</h3>
<div class="h-px flex-1 bg-slate-100"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@forelse($matting->kittens as $k)
<div class="bg-white border border-slate-200/70 rounded-[2rem] p-6 flex items-center gap-8 shadow-sm hover:border-indigo-100 transition-all group overflow-hidden">
{{-- Foto Kitten --}}
<div class="shrink-0 relative">
<img src="{{ $k->cat->photo ? asset('storage/' . $k->cat->photo) : asset('images/default-cat.png') }}"
class="w-20 h-20 object-cover rounded-2xl border border-slate-50 shadow-md group-hover:scale-105 transition-transform duration-500" alt="Kitten">
<span class="absolute -top-2 -left-2 w-7 h-7 rounded-xl flex items-center justify-center text-[10px] {{ $k->cat->jenis_kelamin == 'Jantan' ? 'bg-blue-600' : 'bg-rose-500' }} text-white border-2 border-white shadow-lg">
<i class="fas {{ $k->cat->jenis_kelamin == 'Jantan' ? 'fa-mars' : 'fa-venus' }}"></i>
</span>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between mb-2">
<span class="text-[9px] font-black text-slate-400 uppercase tracking-widest leading-none">Kitten Profile</span>
<span class="text-[8px] font-bold bg-slate-50 text-slate-400 px-2 py-0.5 rounded border border-slate-100 uppercase tracking-tighter leading-none">EMS: {{ $k->cat->kode_ems ?? '-' }}</span>
</div>
<h4 class="text-base font-black text-slate-800 uppercase truncate leading-tight group-hover:text-indigo-600 transition-colors tracking-tight leading-none mb-4">{{ $k->cat->nama_kucing }}</h4>
{{-- Data Teknis Kitten --}}
<div class="grid grid-cols-2 gap-x-4 border-t border-slate-50 pt-4 mt-1">
<div class="min-w-0">
<p class="text-[8px] text-slate-400 font-bold uppercase leading-none mb-1.5 tracking-wider">Pedigree</p>
<p class="text-[10px] font-mono font-bold text-slate-700 truncate italic tracking-tighter leading-none">
{{ $k->cat->pedigree ?? '-' }}
</p>
</div>
<div class="min-w-0">
<p class="text-[8px] text-slate-400 font-bold uppercase leading-none mb-1.5 tracking-wider">Microchip</p>
<p class="text-[10px] font-mono font-bold text-slate-700 truncate italic tracking-tighter leading-none">
{{ $k->cat->microchip ?? '-' }}
</p>
</div>
</div>
</div>
</div>
@empty
<div class="col-span-full py-16 text-center bg-white rounded-[2.5rem] border border-dashed border-slate-200">
<i class="fas fa-paw text-2xl text-slate-100 mb-4"></i>
<p class="text-slate-400 font-bold uppercase text-[9px] tracking-[0.3em] italic">No offspring registered for this session.</p>
</div>
@endforelse
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,140 @@
<!DOCTYPE html>
<html>
<head>
<title>{{ $title }}</title>
<style>
body { font-family: 'Helvetica', sans-serif; font-size: 10px; color: #333; margin: 0; }
.header { text-align: center; margin-bottom: 30px; border-bottom: 2px solid #4f46e5; padding-bottom: 10px; }
.header h1 { margin: 0; color: #1e293b; font-size: 18px; text-transform: uppercase; }
.header p { margin: 5px 0; color: #64748b; font-size: 8px; letter-spacing: 2px; }
.meta-info { margin-bottom: 20px; width: 100%; }
.meta-info td { vertical-align: top; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; table-layout: fixed; }
th { background-color: #f8fafc; border: 1px solid #e2e8f0; padding: 8px; text-align: left; text-transform: uppercase; color: #475569; font-size: 8px; }
td { border: 1px solid #e2e8f0; padding: 8px; vertical-align: top; word-wrap: break-word; }
/* Status Colors */
.badge { padding: 3px 6px; border-radius: 4px; font-weight: bold; font-size: 7px; text-transform: uppercase; }
.bg-red { background-color: #fee2e2; color: #991b1b; }
.bg-amber { background-color: #fef3c7; color: #92400e; }
.bg-green { background-color: #d1fae5; color: #065f46; }
.bg-slate { background-color: #f1f5f9; color: #475569; }
.footer { position: fixed; bottom: 0; width: 100%; text-align: center; font-size: 8px; color: #94a3b8; border-top: 1px solid #e2e8f0; padding-top: 5px; }
</style>
</head>
<body>
<div class="header">
<h1>ILY CATTERY - INTELLIGENCE REPORT</h1>
<p>MODUL: {{ strtoupper($module) }} | UNIT ANALYTICS SYSTEM</p>
</div>
<table class="meta-info">
<tr>
<td width="50%">
<strong>Dicetak Oleh:</strong> {{ Auth::user()->name }}<br>
<strong>Tanggal Cetak:</strong> {{ now()->translatedFormat('d F Y H:i') }}
</td>
<td width="50%" style="text-align: right;">
<strong>Total Data:</strong> {{ count($data) }} Records<br>
<strong>Status Laporan:</strong> {{ strtoupper($title) }}
</td>
</tr>
</table>
<table>
<thead>
<tr>
<th width="30px">No</th>
{{-- JUDUL KOLOM DINAMIS --}}
@if($module == 'cats')
<th>Nama Kucing</th><th>Ras</th><th>Kelamin</th><th>Tgl Lahir</th><th>Status</th>
@elseif(in_array($module, ['vaccine', 'deworming', 'flea', 'grooming']))
<th>Nama Kucing</th><th>Tindakan</th><th>PIC Staf</th><th>Jadwal</th><th>Status Analitik</th>
@elseif(in_array($module, ['medicine', 'food', 'supplies']))
<th>Nama Produk</th><th>Stok</th><th>Unit</th><th>Tgl Expired</th><th>Kondisi</th>
@else
<th>Informasi Utama</th><th>Detail</th><th>PIC</th><th>Tanggal</th><th>Status</th>
@endif
</tr>
</thead>
<tbody>
@forelse($data as $index => $item)
@php
// Deteksi Kolom Tanggal
$dateCol = match($module) {
'cats' => 'tanggal_lahir',
'vaccine' => 'jadwal_vaksin',
'deworming' => 'jadwal_obat_cacing',
'flea' => 'jadwal_obat_kutu',
'grooming' => 'jadwal_grooming',
'medicine', 'food' => 'expired_date',
default => 'created_at'
};
$dateVal = $item->{$dateCol};
@endphp
<tr>
<td style="text-align: center;">{{ $index + 1 }}</td>
{{-- ISI DATA DINAMIS --}}
@if($module == 'cats')
<td><strong>{{ $item->nama_kucing }}</strong></td>
<td>{{ $item->ras }}</td>
<td>{{ $item->jenis_kelamin }}</td>
<td>{{ $item->tanggal_lahir ? \Carbon\Carbon::parse($item->tanggal_lahir)->format('d/m/Y') : '-' }}</td>
<td>
<span class="badge {{ $item->riwayat_sakit_terakhir ? 'bg-amber' : 'bg-green' }}">
{{ $item->riwayat_sakit_terakhir ? 'In Treatment' : 'Healthy' }}
</span>
</td>
@elseif(in_array($module, ['vaccine', 'deworming', 'flea', 'grooming']))
<td><strong>{{ $item->nama_kucing }}</strong></td>
<td>{{ strtoupper($module) }}</td>
<td>{{ $item->pic->name ?? '-' }}</td>
<td>{{ $dateVal ? \Carbon\Carbon::parse($dateVal)->format('d/m/Y') : 'NOT SET' }}</td>
<td>
@if(empty($dateVal))
<span class="badge bg-slate">Date Not Set</span>
@elseif(\Carbon\Carbon::parse($dateVal)->isPast())
<span class="badge bg-red">Overdue</span>
@elseif(\Carbon\Carbon::parse($dateVal)->diffInDays(now()) <= 7)
<span class="badge bg-amber">Soon</span>
@else
<span class="badge bg-green">Safe</span>
@endif
</td>
@elseif(in_array($module, ['medicine', 'food', 'supplies']))
<td><strong>{{ $item->nama_obat ?? $item->nama_makanan ?? $item->nama_perlengkapan }}</strong></td>
<td>{{ $item->qty }}</td>
<td>{{ $item->unit }}</td>
<td>{{ $item->expired_date ? \Carbon\Carbon::parse($item->expired_date)->format('d/m/Y') : '-' }}</td>
<td>
@if($item->qty < 5)
<span class="badge bg-red">Low Stock</span>
@elseif($item->expired_date && \Carbon\Carbon::parse($item->expired_date)->isPast())
<span class="badge bg-red">Expired</span>
@else
<span class="badge bg-green">Ready</span>
@endif
</td>
@endif
</tr>
@empty
<tr>
<td colspan="6" style="text-align: center; padding: 20px;">Data Tidak Ditemukan</td>
</tr>
@endforelse
</tbody>
</table>
<div class="footer">
Ily Cattery Intelligence System - Dokumen Sah Dihasilkan Secara Otomatis
</div>
</body>
</html>
@@ -0,0 +1,118 @@
<x-app-layout>
<div class="max-w-4xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4">
<a href="{{ url()->previous() }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Tambah Rekam Medis</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">Pemeriksaan & Pemberian Obat Pasien</p>
</div>
</div>
<form action="{{ route('treatments.store') }}" method="POST" enctype="multipart/form-data" class="space-y-6">
@csrf
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-indigo-600 uppercase tracking-widest border-b border-slate-100 pb-3">Data Pasien</h4>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Pilih Pasien</label>
<select name="cat_id" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
@foreach($cats as $cat)
<option value="{{ $cat->id }}" {{ ($selectedCat && $selectedCat->id == $cat->id) || old('cat_id') == $cat->id ? 'selected' : '' }}>
{{ $cat->nama_kucing }} ({{ $cat->ras }})
</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Pemeriksaan</label>
<input type="date" name="tanggal" value="{{ date('Y-m-d') }}" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-indigo-500 transition-all">
</div>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-emerald-600 uppercase tracking-widest border-b border-slate-100 pb-3">Inventori & Dosis</h4>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Pilih Obat (Stok)</label>
<select name="medicine_id" id="medicine_select" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-emerald-500 transition-all">
<option value="" disabled selected>-- Pilih Obat --</option>
@foreach($medicines as $med)
<option value="{{ $med->id }}" data-unit="{{ $med->unit }}" {{ old('medicine_id') == $med->id ? 'selected' : '' }}>
{{ $med->nama_obat }} (Tersedia: {{ (float)$med->qty }} {{ $med->unit }})
</option>
@endforeach
</select>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jumlah Potong Stok</label>
<div class="relative">
<input type="number" name="qty_used" id="qty_input" value="{{ old('qty_used', 1) }}" step="0.01" min="0.01" required class="w-full pl-4 pr-14 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-black text-slate-800">
<span id="unit_label" class="absolute right-4 top-1/2 -translate-y-1/2 text-[9px] font-black text-slate-400 uppercase">UNIT</span>
</div>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Dosis (cc/butir)</label>
<input type="text" name="dosis" value="{{ old('dosis') }}" placeholder="Contoh: 0.5cc" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-emerald-500 transition-all">
</div>
</div>
<p class="text-[9px] text-slate-400 italic px-1 leading-tight">* Gunakan titik (.) untuk desimal, contoh: 2.5</p>
</div>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-rose-600 uppercase tracking-widest border-b border-slate-100 pb-3">Hasil Pemeriksaan</h4>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Keluhan Utama</label>
<input type="text" name="keluhan" value="{{ old('keluhan') }}" required placeholder="Contoh: Mata berair dan bersin" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-rose-500 transition-all">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Diagnosa & Tindakan</label>
<textarea name="diagnosa" rows="4" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-rose-500 transition-all">{{ old('diagnosa') }}</textarea>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Keterangan Tambahan (Opsional)</label>
<textarea name="keterangan" rows="2" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-medium italic">{{ old('keterangan') }}</textarea>
</div>
</div>
</div>
<button type="submit" class="w-full py-5 bg-indigo-600 text-white text-sm font-black rounded-3xl shadow-xl hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-2">
<i class="fas fa-save"></i> SIMPAN REKAM MEDIS & UPDATE STOK
</button>
</form>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const medicineSelect = document.getElementById('medicine_select');
const unitLabel = document.getElementById('unit_label');
function updateUnit() {
const selectedOption = medicineSelect.options[medicineSelect.selectedIndex];
const unit = selectedOption.getAttribute('data-unit');
unitLabel.textContent = unit ? unit : 'UNIT';
}
// Update label saat pilihan berubah
medicineSelect.addEventListener('change', updateUnit);
// Update label saat halaman dimuat (jika ada nilai 'old')
if (medicineSelect.value) {
updateUnit();
}
});
</script>
@endpush
</x-app-layout>
@@ -0,0 +1,137 @@
<x-app-layout>
<div class="max-w-4xl mx-auto space-y-6 pb-12">
<div class="flex items-center gap-4">
<a href="{{ route('treatments.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm active:scale-90">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Rekam Medis</h2>
<p class="text-sm text-slate-500 font-medium italic uppercase tracking-tighter">Memperbarui Catatan Medis & Dosis Obat</p>
</div>
</div>
<div class="p-4 bg-amber-50 border border-amber-100 rounded-2xl flex items-start gap-3">
<i class="fas fa-exclamation-triangle text-amber-500 mt-1"></i>
<div>
<p class="text-xs font-black text-amber-700 uppercase tracking-widest">Informasi Inventori</p>
<p class="text-[11px] text-amber-600 font-medium leading-relaxed">Mengubah jenis atau jumlah obat akan memicu sistem untuk mengembalikan stok lama dan memotong stok baru secara otomatis.</p>
</div>
</div>
@if ($errors->any())
<div class="p-4 bg-rose-50 border border-rose-100 rounded-2xl text-rose-600 text-[11px] font-bold">
<ul class="list-disc ml-4 uppercase tracking-widest">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('treatments.update', $treatment->id) }}" method="POST" enctype="multipart/form-data" class="space-y-6">
@csrf
@method('PATCH')
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-indigo-600 uppercase tracking-widest border-b border-slate-100 pb-3">Data Pasien</h4>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Pasien</label>
<select name="cat_id" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500">
@foreach($cats as $cat)
<option value="{{ $cat->id }}" {{ old('cat_id', $treatment->cat_id) == $cat->id ? 'selected' : '' }}>
{{ $cat->nama_kucing }} ({{ $cat->ras }})
</option>
@endforeach
</select>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Tanggal Pemeriksaan</label>
<input type="date" name="tanggal" value="{{ old('tanggal', $treatment->tanggal->format('Y-m-d')) }}" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-indigo-500">
</div>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-emerald-600 uppercase tracking-widest border-b border-slate-100 pb-3">Dosis & Stok</h4>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Obat Digunakan</label>
<select name="medicine_id" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-emerald-500">
@foreach($medicines as $med)
<option value="{{ $med->id }}" {{ old('medicine_id', $treatment->medicine_id) == $med->id ? 'selected' : '' }}>
{{ $med->nama_obat }} (Stok: {{ $med->qty }} {{ $med->unit }})
</option>
@endforeach
</select>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Jumlah (Qty)</label>
<input type="number" name="qty_used" value="{{ old('qty_used', $treatment->qty_used) }}" min="1" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-black text-slate-800">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Dosis</label>
<input type="text" name="dosis" value="{{ old('dosis', $treatment->dosis) }}" placeholder="Contoh: 1 tablet" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold text-slate-800">
</div>
</div>
</div>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
<h4 class="text-xs font-black text-rose-600 uppercase tracking-widest border-b border-slate-100 pb-3">Detail Pemeriksaan Medis</h4>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Keluhan / Gejala</label>
<input type="text" name="keluhan" value="{{ old('keluhan', $treatment->keluhan) }}" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-rose-500">
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Diagnosa & Tindakan</label>
<textarea name="diagnosa" rows="4" required class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-rose-500">{{ old('diagnosa', $treatment->diagnosa) }}</textarea>
</div>
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Keterangan Tambahan</label>
<textarea name="keterangan" rows="2" class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold italic">{{ old('keterangan', $treatment->keterangan) }}</textarea>
</div>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-4">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Lampiran Foto Bukti (Opsional)</label>
<div class="flex flex-col md:flex-row gap-6 items-center">
<div class="w-32 h-32 rounded-3xl bg-slate-50 border-2 border-dashed border-slate-200 overflow-hidden ring-4 ring-white shadow-md shrink-0 flex items-center justify-center">
<img id="preview" src="{{ $treatment->foto_bukti ? asset('storage/' . $treatment->foto_bukti) : 'https://ui-avatars.com/api/?name=Bukti+Foto&background=f1f5f9&color=94a3b8' }}" class="w-full h-full object-cover">
</div>
<div class="flex-1 w-full">
<input type="file" name="foto_bukti" onchange="previewImage(event)" class="w-full p-3 bg-slate-50 border border-slate-200 rounded-2xl text-xs font-bold file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-[10px] file:font-black file:bg-indigo-600 file:text-white hover:file:bg-indigo-700 cursor-pointer">
<p class="text-[9px] text-slate-400 mt-2 italic">* Upload foto baru untuk mengganti bukti pengobatan sebelumnya.</p>
</div>
</div>
</div>
<div class="flex gap-4">
<button type="submit" class="flex-1 py-5 bg-indigo-600 text-white text-xs font-black rounded-3xl shadow-xl shadow-indigo-100 hover:bg-indigo-700 transition-all active:scale-[0.98] flex items-center justify-center gap-2">
<i class="fas fa-sync-alt"></i> SIMPAN PERUBAHAN & RE-SYNC STOK
</button>
</div>
</form>
</div>
<script>
function previewImage(event) {
const reader = new FileReader();
reader.onload = function() {
const output = document.getElementById('preview');
output.src = reader.result;
}
if(event.target.files[0]) {
reader.readAsDataURL(event.target.files[0]);
}
}
</script>
</x-app-layout>
@@ -0,0 +1,125 @@
<x-app-layout>
<div class="space-y-6 pb-8">
{{-- SECTION 1: Header --}}
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight leading-none mb-1 text-uppercase">Log Rekam Medis</h2>
<p class="text-xs text-slate-500 font-medium italic uppercase tracking-wider">Riwayat kesehatan & penggunaan obat seluruh pasien.</p>
</div>
{{-- PROTEKSI TOMBOL CREATE: Hanya muncul jika punya izin Create --}}
@can('check-access', ['treatments', 'create'])
<a href="{{ route('treatments.create') }}" class="inline-flex items-center px-6 py-4 bg-slate-900 text-white text-[10px] font-black rounded-2xl shadow-xl hover:bg-black transition-all active:scale-95 tracking-widest uppercase">
<i class="fas fa-plus-circle mr-2 text-sm"></i> INPUT REKAM MEDIS
</a>
@endcan
</div>
{{-- SECTION 2: Tabel Rekam Medis --}}
<div class="bg-white border border-slate-200 rounded-[2.5rem] overflow-hidden shadow-sm">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead class="bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100">
<tr>
<th class="px-6 py-4">Tanggal & Pasien</th>
<th class="px-6 py-4">Keluhan & Diagnosa</th>
<th class="px-6 py-4">Obat (Qty)</th>
<th class="px-6 py-4 text-center">Bukti</th>
<th class="px-6 py-4 text-right" style="min-width: 220px;">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 font-medium text-slate-700">
@forelse($treatments as $treatment)
<tr class="hover:bg-slate-50/50 transition-all group">
{{-- Tanggal & Pasien --}}
<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">
<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">
</div>
<div>
<p class="text-[10px] font-black text-indigo-600 uppercase leading-none mb-1">{{ $treatment->tanggal->format('d M Y') }}</p>
<p class="text-sm font-black text-slate-800 leading-none">{{ $treatment->cat->nama_kucing }}</p>
</div>
</div>
</td>
{{-- Keluhan & Diagnosa --}}
<td class="px-6 py-4 max-w-xs">
<p class="text-xs font-black text-slate-800 truncate">{{ $treatment->keluhan }}</p>
<p class="text-[10px] text-slate-500 line-clamp-1 italic uppercase tracking-tighter">{{ $treatment->diagnosa }}</p>
</td>
{{-- Obat & Qty --}}
<td class="px-6 py-4">
<div class="flex items-center gap-2">
<span class="px-2 py-1 bg-emerald-50 text-emerald-600 rounded-lg text-[10px] font-black border border-emerald-100 uppercase">
{{ $treatment->nama_obat }}
</span>
<span class="text-[10px] font-bold text-slate-400">x{{ number_format($treatment->qty_used, 0) }}</span>
</div>
</td>
{{-- Foto Bukti --}}
<td class="px-6 py-4 text-center">
@if($treatment->foto_bukti)
<a href="{{ asset('storage/'.$treatment->foto_bukti) }}" target="_blank" class="w-8 h-8 inline-flex items-center justify-center bg-indigo-50 text-indigo-500 hover:bg-indigo-100 rounded-lg transition-all shadow-sm" title="Lihat Bukti">
<i class="fas fa-image"></i>
</a>
@else
<span class="text-slate-300 text-[10px] font-bold">TIDAK ADA</span>
@endif
</td>
{{-- AKSI --}}
<td class="px-6 py-4 text-right align-middle">
<div class="flex justify-end items-center gap-16">
{{-- PROTEKSI TOMBOL EDIT: Hanya jika punya izin Update --}}
@can('check-access', ['treatments', 'update'])
<a href="{{ route('treatments.edit', $treatment->id) }}"
class="flex items-center gap-3 p-2 text-slate-400 hover:text-amber-600 transition-all group/btn"
title="Edit Rekam Medis">
<i class="fas fa-edit text-base"></i>
<span class="text-[10px] font-black uppercase hidden group-hover/btn:inline tracking-widest">Edit</span>
</a>
@endcan
{{-- PROTEKSI TOMBOL HAPUS: Hanya jika punya izin Delete --}}
@can('check-access', ['treatments', 'delete'])
<form action="{{ route('treatments.destroy', $treatment->id) }}" method="POST" class="inline m-0">
@csrf @method('DELETE')
<button type="button"
onclick="confirmDelete(this, 'Rekam Medis {{ addslashes($treatment->cat->nama_kucing) }}')"
class="flex items-center gap-3 p-2 text-slate-400 hover:text-rose-600 transition-all group/btn"
title="Hapus Data">
<i class="fas fa-trash-alt text-base"></i>
<span class="text-[10px] font-black uppercase hidden group-hover/btn:inline tracking-widest">Hapus</span>
</button>
</form>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-6 py-24 text-center">
<div class="w-16 h-16 bg-slate-50 rounded-3xl flex items-center justify-center mx-auto mb-4">
<i class="fas fa-notes-medical text-2xl text-slate-200"></i>
</div>
<p class="text-slate-400 font-bold uppercase text-[10px] tracking-widest">Belum ada riwayat rekam medis</p>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- Pagination --}}
<div class="p-6 border-t border-slate-100 bg-slate-50/30">
{{ $treatments->links() }}
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,80 @@
<x-app-layout>
<div class="max-w-3xl mx-auto space-y-6 pb-12">
{{-- Header Section --}}
<div class="flex items-center gap-4">
<a href="{{ route('admin.users.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Daftarkan Staf Baru</h2>
<p class="text-sm text-slate-500 font-medium italic">Tambahkan pengguna baru ke sistem earrin cats.</p>
</div>
</div>
{{-- Form Section --}}
<form action="{{ route('admin.users.store') }}" method="POST" class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
@csrf
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{{-- Nama Lengkap --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Lengkap</label>
<input type="text" name="name" value="{{ old('name') }}" required placeholder="Contoh: Budi Santoso"
class="w-full px-4 py-3 bg-slate-50 border @error('name') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('name')" class="mt-1" />
</div>
{{-- Alamat Email --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Alamat Email</label>
<input type="email" name="email" value="{{ old('email') }}" required placeholder="email@earrincats.com"
class="w-full px-4 py-3 bg-slate-50 border @error('email') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('email')" class="mt-1" />
</div>
{{-- Nomor WhatsApp --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nomor WhatsApp</label>
<input type="text" name="phone" value="{{ old('phone') }}" placeholder="0812..."
class="w-full px-4 py-3 bg-slate-50 border @error('phone') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('phone')" class="mt-1" />
</div>
{{-- Otoritas (Role) - SEKARANG DINAMIS --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Otoritas (Role)</label>
<select name="role_id" required class="w-full px-4 py-3 bg-slate-50 border @error('role_id') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-black text-indigo-600 uppercase focus:ring-2 focus:ring-indigo-500 transition-all cursor-pointer">
<option value="" disabled selected>Pilih Otoritas</option>
@foreach($roles as $role)
<option value="{{ $role->id }}" {{ old('role_id') == $role->id ? 'selected' : '' }}>
{{ strtoupper($role->name) }}
</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('role_id')" class="mt-1" />
</div>
{{-- Password --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Password</label>
<input type="password" name="password" required
class="w-full px-4 py-3 bg-slate-50 border @error('password') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('password')" class="mt-1" />
</div>
{{-- Konfirmasi Password --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Konfirmasi Password</label>
<input type="password" name="password_confirmation" required
class="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold focus:ring-2 focus:ring-indigo-500 transition-all">
</div>
</div>
<div class="pt-4">
<button type="submit" class="w-full py-5 bg-slate-900 text-white text-xs font-black rounded-3xl shadow-xl hover:bg-black transition-all active:scale-[0.98] flex items-center justify-center gap-2 uppercase tracking-widest">
<i class="fas fa-user-plus"></i> Daftarkan Pengguna
</button>
</div>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,74 @@
<x-app-layout>
<div class="max-w-3xl mx-auto space-y-6 pb-12">
{{-- Header Section --}}
<div class="flex items-center gap-4">
<a href="{{ route('admin.users.index') }}" class="p-3 bg-white border border-slate-200 rounded-2xl text-slate-400 hover:text-indigo-600 transition-all shadow-sm">
<i class="fas fa-arrow-left"></i>
</a>
<div>
<h2 class="text-2xl font-black text-slate-800 tracking-tight">Edit Profil User</h2>
<p class="text-sm text-slate-500 font-medium italic">Perbarui informasi otoritas dan profil anggota tim.</p>
</div>
</div>
{{-- Form Section --}}
<form action="{{ route('admin.users.update', $user->id) }}" method="POST" class="bg-white border border-slate-200 rounded-[2.5rem] p-8 shadow-sm space-y-6">
@csrf
@method('PATCH')
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{{-- Nama Lengkap --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nama Lengkap</label>
<input type="text" name="name" value="{{ old('name', $user->name) }}" required
class="w-full px-4 py-3 bg-slate-50 border @error('name') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('name')" class="mt-1" />
</div>
{{-- Email --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Email</label>
<input type="email" name="email" value="{{ old('email', $user->email) }}" required
class="w-full px-4 py-3 bg-slate-50 border @error('email') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('email')" class="mt-1" />
</div>
{{-- Nomor WhatsApp --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Nomor WhatsApp</label>
<input type="text" name="phone" value="{{ old('phone', $user->phone) }}" placeholder="Contoh: 081234..."
class="w-full px-4 py-3 bg-slate-50 border @error('phone') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-bold text-slate-800 focus:ring-2 focus:ring-indigo-500 transition-all">
<x-input-error :messages="$errors->get('phone')" class="mt-1" />
</div>
{{-- Otoritas Akses (Role) - Dinamis dari Database --}}
<div class="space-y-2">
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Otoritas Akses (Role)</label>
<select name="role_id" required
class="w-full px-4 py-3 bg-slate-50 border @error('role_id') border-red-500 @else border-slate-200 @enderror rounded-2xl text-sm font-black text-indigo-600 uppercase focus:ring-2 focus:ring-indigo-500 transition-all cursor-pointer">
@foreach($roles as $role)
<option value="{{ $role->id }}" {{ old('role_id', $user->role_id) == $role->id ? 'selected' : '' }}>
{{ strtoupper($role->name) }}
</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('role_id')" class="mt-1" />
</div>
</div>
{{-- Info Password --}}
<div class="p-4 bg-indigo-50 rounded-2xl border border-indigo-100 flex gap-3 items-start">
<i class="fas fa-info-circle text-indigo-500 mt-1"></i>
<p class="text-[11px] text-indigo-700 leading-relaxed font-medium">
Kata sandi tidak ditampilkan di sini demi alasan keamanan. User dapat merubah kata sandi secara mandiri melalui menu <strong>Profil</strong> atau Anda dapat menggunakan fitur <em>Reset Password</em> jika diperlukan.
</p>
</div>
<div class="pt-4">
<button type="submit" class="w-full py-5 bg-slate-900 text-white text-xs font-black rounded-3xl shadow-xl hover:bg-black transition-all active:scale-[0.98] uppercase tracking-widest flex items-center justify-center gap-2">
<i class="fas fa-save"></i> Perbarui Data User
</button>
</div>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,85 @@
<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 --}}
@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>
@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->role->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>
@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>
@endcan
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- PAGINATION --}}
<div class="mt-4">
{{ $users->links() }}
</div>
</div>
</x-app-layout>
+29
View File
@@ -0,0 +1,29 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Profile') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-profile-information-form')
</div>
</div>
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-password-form')
</div>
</div>
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.delete-user-form')
</div>
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,55 @@
<section class="space-y-6">
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Delete Account') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }}
</p>
</header>
<x-danger-button
x-data=""
x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')"
>{{ __('Delete Account') }}</x-danger-button>
<x-modal name="confirm-user-deletion" :show="$errors->userDeletion->isNotEmpty()" focusable>
<form method="post" action="{{ route('profile.destroy') }}" class="p-6">
@csrf
@method('delete')
<h2 class="text-lg font-medium text-gray-900">
{{ __('Are you sure you want to delete your account?') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }}
</p>
<div class="mt-6">
<x-input-label for="password" value="{{ __('Password') }}" class="sr-only" />
<x-text-input
id="password"
name="password"
type="password"
class="mt-1 block w-3/4"
placeholder="{{ __('Password') }}"
/>
<x-input-error :messages="$errors->userDeletion->get('password')" class="mt-2" />
</div>
<div class="mt-6 flex justify-end">
<x-secondary-button x-on:click="$dispatch('close')">
{{ __('Cancel') }}
</x-secondary-button>
<x-danger-button class="ms-3">
{{ __('Delete Account') }}
</x-danger-button>
</div>
</form>
</x-modal>
</section>
@@ -0,0 +1,48 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Update Password') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Ensure your account is using a long, random password to stay secure.') }}
</p>
</header>
<form method="post" action="{{ route('password.update') }}" class="mt-6 space-y-6">
@csrf
@method('put')
<div>
<x-input-label for="update_password_current_password" :value="__('Current Password')" />
<x-text-input id="update_password_current_password" name="current_password" type="password" class="mt-1 block w-full" autocomplete="current-password" />
<x-input-error :messages="$errors->updatePassword->get('current_password')" class="mt-2" />
</div>
<div>
<x-input-label for="update_password_password" :value="__('New Password')" />
<x-text-input id="update_password_password" name="password" type="password" class="mt-1 block w-full" autocomplete="new-password" />
<x-input-error :messages="$errors->updatePassword->get('password')" class="mt-2" />
</div>
<div>
<x-input-label for="update_password_password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="update_password_password_confirmation" name="password_confirmation" type="password" class="mt-1 block w-full" autocomplete="new-password" />
<x-input-error :messages="$errors->updatePassword->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'password-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>
@@ -0,0 +1,64 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Profile Information') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __("Update your account's profile information and email address.") }}
</p>
</header>
<form id="send-verification" method="post" action="{{ route('verification.send') }}">
@csrf
</form>
<form method="post" action="{{ route('profile.update') }}" class="mt-6 space-y-6">
@csrf
@method('patch')
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)" required autofocus autocomplete="name" />
<x-input-error class="mt-2" :messages="$errors->get('name')" />
</div>
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email', $user->email)" required autocomplete="username" />
<x-input-error class="mt-2" :messages="$errors->get('email')" />
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
<div>
<p class="text-sm mt-2 text-gray-800">
{{ __('Your email address is unverified.') }}
<button form="send-verification" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
{{ __('Click here to re-send the verification email.') }}
</button>
</p>
@if (session('status') === 'verification-link-sent')
<p class="mt-2 font-medium text-sm text-green-600">
{{ __('A new verification link has been sent to your email address.') }}
</p>
@endif
</div>
@endif
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'profile-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>
File diff suppressed because one or more lines are too long