88 lines
3.5 KiB
PHP
88 lines
3.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Http\Controllers;
|
||
|
|
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use App\Models\{Cat, Medicine, Food, Supply, Matting, Treatment};
|
||
|
|
use Carbon\Carbon;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
|
||
|
|
class DashboardController extends Controller
|
||
|
|
{
|
||
|
|
public function index()
|
||
|
|
{
|
||
|
|
// 1. Statistik Utama
|
||
|
|
$stats = [
|
||
|
|
'total_cats' => Cat::count(),
|
||
|
|
'total_staff' => \App\Models\User::count(),
|
||
|
|
'sick_cats' => Cat::whereNotNull('riwayat_sakit_terakhir')->count(),
|
||
|
|
'total_mating' => Matting::where('status', 'proses')->count(),
|
||
|
|
];
|
||
|
|
|
||
|
|
// 2. Birth Watch: Persalinan 7 Hari ke Depan (Rumus: H+63)
|
||
|
|
$upcomingBirths = Matting::with(['dam', 'sire', 'pic'])
|
||
|
|
->where('status', 'proses')
|
||
|
|
->whereNotNull('tanggal_kawin')
|
||
|
|
->whereBetween('tanggal_kawin', [
|
||
|
|
now()->subDays(70)->toDateString(),
|
||
|
|
now()->subDays(56)->toDateString()
|
||
|
|
])
|
||
|
|
->get()
|
||
|
|
->map(function ($mating) {
|
||
|
|
$mating->edd = Carbon::parse($mating->tanggal_kawin)->addDays(63);
|
||
|
|
$mating->days_to_go = (int) now()->diffInDays($mating->edd, false);
|
||
|
|
return $mating;
|
||
|
|
})
|
||
|
|
->filter(fn($m) => $m->days_to_go >= -2 && $m->days_to_go <= 7)
|
||
|
|
->sortBy('days_to_go');
|
||
|
|
|
||
|
|
// 3. Medical Alerts
|
||
|
|
$medicalAlerts = [
|
||
|
|
'vaccine' => Cat::whereNotNull('jadwal_vaksin')->where('jadwal_vaksin', '<=', now()->addDays(3))->count(),
|
||
|
|
'flea' => Cat::whereNotNull('jadwal_obat_kutu')->where('jadwal_obat_kutu', '<=', now()->addDays(3))->count(),
|
||
|
|
'deworming' => Cat::whereNotNull('jadwal_obat_cacing')->where('jadwal_obat_cacing', '<=', now()->addDays(3))->count(),
|
||
|
|
'grooming' => Cat::whereNotNull('jadwal_grooming')->where('jadwal_grooming', '<=', now()->addDays(3))->count(),
|
||
|
|
];
|
||
|
|
|
||
|
|
// 4. Inventory Critical (Stok < 5)
|
||
|
|
$lowStock = [
|
||
|
|
'medicines' => Medicine::where('qty', '<', 5)->get(['nama_obat as name', 'qty', 'unit']),
|
||
|
|
'foods' => Food::where('qty', '<', 5)->get(['nama_makanan as name', 'qty', 'unit']),
|
||
|
|
'supplies' => Supply::where('qty', '<', 5)->get(['nama_perlengkapan as name', 'qty', 'unit']),
|
||
|
|
];
|
||
|
|
$totalLowStock = collect($lowStock)->flatten(1)->count();
|
||
|
|
|
||
|
|
// 5. Chart Data: Sebaran Ras (Persian, British Shorthair, dll)
|
||
|
|
$chartData = Cat::select('ras', DB::raw('count(*) as total'))->groupBy('ras')->get();
|
||
|
|
|
||
|
|
// 6. Weight Trends: Rata-rata Berat (6 Bulan Terakhir)
|
||
|
|
$weightTrends = DB::table('weight_histories')
|
||
|
|
->select(
|
||
|
|
DB::raw('AVG(berat) as avg_weight'),
|
||
|
|
DB::raw("DATE_FORMAT(tanggal_timbang, '%M') as month"),
|
||
|
|
DB::raw('MONTH(tanggal_timbang) as month_num')
|
||
|
|
)
|
||
|
|
->where('tanggal_timbang', '>=', now()->subMonths(6))
|
||
|
|
->groupBy('month', 'month_num')
|
||
|
|
->orderBy('month_num')
|
||
|
|
->get();
|
||
|
|
|
||
|
|
if ($weightTrends->isEmpty()) {
|
||
|
|
$weightTrends = collect([(object)['month' => now()->format('F'), 'avg_weight' => 0]]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 7. Recent Activity
|
||
|
|
$recentTreatments = Treatment::with(['cat', 'pic'])->latest()->take(5)->get();
|
||
|
|
|
||
|
|
return view('pages.dashboard.index', compact(
|
||
|
|
'stats',
|
||
|
|
'upcomingBirths',
|
||
|
|
'medicalAlerts',
|
||
|
|
'lowStock',
|
||
|
|
'totalLowStock',
|
||
|
|
'chartData',
|
||
|
|
'weightTrends',
|
||
|
|
'recentTreatments'
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|