layer-chicken/resources/views/filament/pages/analisys.blade.php

490 lines
22 KiB
PHP

<x-filament-panels::page>
<div class="mb-6">
<form wire:submit="getViewData">
{{ $this->form }}
</form>
</div>
<div class="space-y-6 pb-12">
{{-- Main Stats Grid --}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
@foreach ($mainStats as $stat)
<div
class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-5 transition hover:shadow-md">
<div class="flex flex-col gap-1">
<span
class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-tight">{{ $stat['label'] }}</span>
<div class="flex items-baseline gap-1">
@if ($stat['prefix'])
<span
class="text-xs font-semibold text-gray-400 dark:text-gray-500">{{ $stat['prefix'] }}</span>
@endif
<span class="text-2xl font-bold text-gray-900 dark:text-white">{{ $stat['value'] }}</span>
</div>
<span
class="text-[10px] text-gray-400 dark:text-gray-600 italic tracking-wide uppercase">{{ $stat['desc'] }}</span>
</div>
</div>
@endforeach
</div>
{{-- First Row: Production Chart & Expense Distribution --}}
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div
class="lg:col-span-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-chart-bar class="w-4 h-4 text-primary-500" />
Tren Produksi Telur (7-14 Hari Terakhir)
</h3>
<div class="h-64">
<canvas id="productionChart"></canvas>
</div>
</div>
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-chart-pie class="w-4 h-4 text-primary-500" />
Alokasi Pengeluaran
</h3>
<div class="h-64 flex flex-col items-center">
<canvas id="expenseChart"></canvas>
</div>
</div>
</div>
{{-- Second Row: Financial Trend & Top Customers --}}
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div
class="lg:col-span-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-presentation-chart-line class="w-4 h-4 text-primary-500" />
Performa Keuangan (6 Bulan Terakhir)
</h3>
<div class="h-64">
<canvas id="financialChart"></canvas>
</div>
</div>
<div
class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm overflow-hidden flex flex-col">
<div
class="px-5 py-3 border-b border-gray-100 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-800/50 flex items-center gap-2">
<x-heroicon-o-star class="w-4 h-4 text-amber-500" />
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300">Top 5 Pelanggan</h3>
</div>
<div class="flex-1 divide-y divide-gray-100 dark:divide-gray-800">
@foreach ($topCustomers as $customer)
<div
class="px-5 py-4 flex items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-800/40 transition">
<div class="flex flex-col">
<span
class="text-sm font-bold text-gray-800 dark:text-gray-200">{{ $customer->name }}</span>
<span
class="text-xs text-gray-400 tracking-tight">{{ $customer->phone ?? 'Telp tidak ada' }}</span>
</div>
<div class="text-right">
<span
class="text-xs font-semibold text-gray-400 uppercase block leading-none mb-1">Total
Belanja</span>
<span class="text-sm font-black text-primary-600">Rp
{{ $formatNumber($customer->total_spent) }}</span>
</div>
</div>
@endforeach
</div>
</div>
</div>
{{-- Third Row: Sales by Unit & Payment Status --}}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 pt-6 border-t border-gray-100 dark:border-gray-800">
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-shopping-bag class="w-4 h-4 text-primary-500" />
Penjualan Berdasarkan Satuan (Order)
</h3>
<div class="h-64 flex flex-col items-center">
<canvas id="salesUnitChart"></canvas>
</div>
</div>
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-shield-check class="w-4 h-4 text-primary-500" />
Status Pelunasan (Barang Tertunda)
</h3>
<div class="h-64 flex flex-col items-center">
<canvas id="debtStatusChart"></canvas>
</div>
</div>
</div>
{{-- Fourth Row: Feed Stocks --}}
<div
class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6 mt-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-circle-stack class="w-4 h-4 text-primary-500" />
Level Stok Pakan Saat Ini
</h3>
<div class="h-80">
<canvas id="feedStockChart"></canvas>
</div>
</div>
{{-- Fifth Row: Profit Trend & Warehouse Productivity --}}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 pt-6 border-t border-gray-100 dark:border-gray-800">
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-arrow-trending-up class="w-4 h-4 text-emerald-500" />
Tren Laba Bersih (6 Bulan Terakhir)
</h3>
<div class="h-64">
<canvas id="profitTrendChart"></canvas>
</div>
</div>
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-home-modern class="w-4 h-4 text-primary-500" />
Produktivitas per Gudang
</h3>
<div class="h-64">
<canvas id="warehouseChart"></canvas>
</div>
</div>
</div>
{{-- Sixth Row: Revenue Split --}}
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 pt-6">
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300 mb-6 flex items-center gap-2">
<x-heroicon-o-arrows-right-left class="w-4 h-4 text-primary-500" />
Sumber Pendapatan
</h3>
<div class="h-64 flex flex-col items-center">
<canvas id="revenueSplitChart"></canvas>
</div>
</div>
{{-- Placeholder/Extra space to balance --}}
<div class="lg:col-span-2"></div>
</div>
{{-- Units Breakdown --}}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
{{-- Production --}}
<div
class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm overflow-hidden">
<div class="px-5 py-3 border-b border-gray-100 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-800/50">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300">Total Stok (By Unit)</h3>
</div>
<div class="divide-y divide-gray-100 dark:divide-gray-800">
@forelse($totalEggsByUnit as $item)
<div
class="px-5 py-3 flex justify-between items-center transition hover:bg-gray-50 dark:hover:bg-gray-800/40">
<span
class="text-sm text-gray-600 dark:text-gray-400 font-medium">{{ $item['unit_name'] }}</span>
<div class="flex gap-4">
<div class="text-right">
<span class="text-[10px] text-gray-400 font-bold uppercase block">Bagus</span>
<span
class="text-sm font-bold text-emerald-600 dark:text-emerald-400">{{ $item['total_good'] }}</span>
</div>
<div class="text-right border-l border-gray-100 dark:border-gray-800 pl-4">
<span class="text-[10px] text-gray-400 font-bold uppercase block">Pecah</span>
<span
class="text-sm font-bold text-rose-600 dark:text-rose-400">{{ $item['total_broken'] }}</span>
</div>
</div>
</div>
@empty
<div class="px-5 py-10 text-center text-gray-400 text-sm italic">Belum ada data produksi</div>
@endforelse
</div>
</div>
{{-- Delayed --}}
<div
class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm overflow-hidden">
<div class="px-5 py-3 border-b border-gray-100 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-800/50">
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300">Barang Tertunda (By Unit)</h3>
</div>
<div class="divide-y divide-gray-100 dark:divide-gray-800">
@forelse($totalDelayedByUnit as $item)
<div
class="px-5 py-3 flex justify-between items-center transition hover:bg-gray-50 dark:hover:bg-gray-800/40">
<span
class="text-sm text-gray-600 dark:text-gray-400 font-medium">{{ $item['unit_name'] }}</span>
<span class="text-sm font-bold text-gray-900 dark:text-white">{{ $item['total'] }}</span>
</div>
@empty
<div class="px-5 py-10 text-center text-gray-400 text-sm italic">Tidak ada barang tertunda</div>
@endforelse
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
let chartInstances = {};
function destroyCharts() {
Object.values(chartInstances).forEach(chart => {
if (chart) chart.destroy();
});
chartInstances = {};
}
function initCharts() {
destroyCharts();
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
boxWidth: 10,
font: {
size: 10
}
}
}
}
};
// Production Chart
const ctxProduction = document.getElementById('productionChart');
if (ctxProduction) {
chartInstances.production = new Chart(ctxProduction, {
type: 'line',
data: {
labels: {!! json_encode($productionChart['labels']) !!},
datasets: [{
label: 'Lampu/Bagus',
data: {!! json_encode($productionChart['good']) !!},
borderColor: '#10b981',
backgroundColor: '#10b98122',
tension: 0.4,
fill: true
},
{
label: 'Pecah',
data: {!! json_encode($productionChart['broken']) !!},
borderColor: '#f43f5e',
backgroundColor: '#f43f5e22',
tension: 0.4,
fill: true
}
]
},
options: chartOptions
});
}
// Financial Chart
const ctxFinancial = document.getElementById('financialChart');
if (ctxFinancial) {
chartInstances.financial = new Chart(ctxFinancial, {
type: 'bar',
data: {
labels: {!! json_encode($financialChart['labels']) !!},
datasets: [{
label: 'Pendapatan',
data: {!! json_encode($financialChart['revenue']) !!},
backgroundColor: '#6366f1',
borderRadius: 4
},
{
label: 'Total Biaya',
data: {!! json_encode($financialChart['expenses']) !!},
backgroundColor: '#94a3b8',
borderRadius: 4
}
]
},
options: chartOptions
});
}
// Expense Category Chart
const ctxExpense = document.getElementById('expenseChart');
if (ctxExpense) {
chartInstances.expense = new Chart(ctxExpense, {
type: 'doughnut',
data: {
labels: {!! json_encode($expenseDistribution['labels']) !!},
datasets: [{
data: {!! json_encode($expenseDistribution['data']) !!},
backgroundColor: ['#a855f7', '#f97316', '#64748b'],
borderWidth: 0
}]
},
options: {
...chartOptions,
cutout: '70%'
}
});
}
// Sales by Unit Chart
const ctxSalesUnit = document.getElementById('salesUnitChart');
if (ctxSalesUnit) {
chartInstances.salesUnit = new Chart(ctxSalesUnit, {
type: 'pie',
data: {
labels: {!! json_encode($salesByUnit['labels']) !!},
datasets: [{
data: {!! json_encode($salesByUnit['data']) !!},
backgroundColor: ['#3b82f6', '#06b6d4', '#8b5cf6', '#ec4899'],
}]
},
options: chartOptions
});
}
// Debt Status Chart
const ctxDebtStatus = document.getElementById('debtStatusChart');
if (ctxDebtStatus) {
chartInstances.debtStatus = new Chart(ctxDebtStatus, {
type: 'doughnut',
data: {
labels: ['Sudah Bayar', 'Belum Bayar'],
datasets: [{
data: [{!! $debtStatus['paid'] !!}, {!! $debtStatus['unpaid'] !!}],
backgroundColor: ['#10b981', '#f43f5e'],
}]
},
options: {
...chartOptions,
cutout: '70%'
}
});
}
// Feed Stock Chart
const ctxFeedStock = document.getElementById('feedStockChart');
if (ctxFeedStock) {
chartInstances.feedStock = new Chart(ctxFeedStock, {
type: 'bar',
data: {
labels: {!! json_encode($feedStocks['labels']) !!},
datasets: [{
label: 'Stok Tersedia',
data: {!! json_encode($feedStocks['data']) !!},
backgroundColor: '#f59e0b',
borderRadius: 6
}]
},
options: {
...chartOptions,
indexAxis: 'y',
}
});
}
// Profit Trend Chart
const ctxProfitTrend = document.getElementById('profitTrendChart');
if (ctxProfitTrend) {
chartInstances.profitTrend = new Chart(ctxProfitTrend, {
type: 'line',
data: {
labels: {!! json_encode($financialChart['labels']) !!},
datasets: [{
label: 'Laba Bersih',
data: {!! json_encode($financialChart['profit']) !!},
borderColor: '#10b981',
backgroundColor: '#10b98122',
tension: 0.4,
fill: true
}]
},
options: chartOptions
});
}
// Warehouse Productivity Chart
const ctxWarehouse = document.getElementById('warehouseChart');
if (ctxWarehouse) {
chartInstances.warehouse = new Chart(ctxWarehouse, {
type: 'bar',
data: {
labels: {!! json_encode($productionByWarehouse['labels']) !!},
datasets: [{
label: 'Total Produksi',
data: {!! json_encode($productionByWarehouse['data']) !!},
backgroundColor: '#6366f1',
borderRadius: 4
}]
},
options: chartOptions
});
}
// Revenue Split Chart
const ctxRevenueSplit = document.getElementById('revenueSplitChart');
if (ctxRevenueSplit) {
chartInstances.revenueSplit = new Chart(ctxRevenueSplit, {
type: 'doughnut',
data: {
labels: {!! json_encode($revenueSplit['labels']) !!},
datasets: [{
data: {!! json_encode($revenueSplit['data']) !!},
backgroundColor: ['#6366f1', '#94a3b8'],
}]
},
options: {
...chartOptions,
cutout: '70%'
}
});
}
}
document.addEventListener('DOMContentLoaded', () => {
initCharts();
});
document.addEventListener('stats-updated', () => {
// Give Livewire time to update the DOM if necessary
setTimeout(() => {
initCharts();
}, 50);
});
// Also hook into Livewire re-renders as backup
document.addEventListener('livewire:initialized', () => {
Livewire.on('stats-updated', () => {
setTimeout(() => {
initCharts();
}, 50);
});
});
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@200;300;400;500;600;700;800&display=swap');
:root {
--fi-page-content-padding: 2rem;
}
.fi-section {
background: transparent !important;
box-shadow: none !important;
border: none !important;
}
* {
font-family: 'Plus Jakarta Sans', sans-serif;
}
.tracking-tighter {
letter-spacing: -0.05em;
}
.tracking-tight {
letter-spacing: -0.025em;
}
</style>
</x-filament-panels::page>