842 lines
29 KiB
PHP
842 lines
29 KiB
PHP
@extends('layouts.sidebar')
|
|
|
|
@section('title', 'Dashboard - SIM Stok')
|
|
@section('active_menu', '')
|
|
|
|
@section('content')
|
|
<style>
|
|
.hero {
|
|
border: 1px solid #dbe2ea;
|
|
background: linear-gradient(120deg, #f1fff3 0%, #ffffff 70%);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
margin-bottom: 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 20px;
|
|
}
|
|
|
|
.hero h2 {
|
|
margin: 0 0 8px;
|
|
font-size: 28px;
|
|
font-family: var(--font-heading);
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
.hero p {
|
|
margin: 0;
|
|
font-size: 15px;
|
|
color: #475569;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 14px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.stat-card {
|
|
position: relative;
|
|
border: 1px solid #e2e8f0;
|
|
border-left-width: 4px;
|
|
border-radius: 10px;
|
|
background: #fff;
|
|
padding: 16px;
|
|
min-width: 0;
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.04);
|
|
}
|
|
|
|
.stat-card.stat-total {
|
|
border-left-color: #3b82f6;
|
|
}
|
|
|
|
.stat-card.stat-low {
|
|
border-left-color: #f59e0b;
|
|
}
|
|
|
|
.stat-card.stat-in {
|
|
border-left-color: #10b981;
|
|
}
|
|
|
|
.stat-card.stat-out {
|
|
border-left-color: #ef4444;
|
|
}
|
|
|
|
.stat-main {
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.stat-title {
|
|
font-size: 12px;
|
|
letter-spacing: 0.03em;
|
|
text-transform: uppercase;
|
|
font-weight: 800;
|
|
color: #64748b;
|
|
font-family: var(--font-heading);
|
|
}
|
|
|
|
.stat-value {
|
|
margin-top: 4px;
|
|
font-size: 35px;
|
|
line-height: 1.1;
|
|
font-weight: 800;
|
|
color: #0f172a;
|
|
font-family: var(--font-heading);
|
|
letter-spacing: -0.02em;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.stat-icon {
|
|
width: 34px;
|
|
height: 34px;
|
|
flex-shrink: 0;
|
|
color: #cbd5e1;
|
|
opacity: 0.95;
|
|
}
|
|
|
|
.stat-trend {
|
|
margin-top: 10px;
|
|
font-size: 13px;
|
|
color: #64748b;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.stat-trend-value {
|
|
display: inline-block;
|
|
margin-left: 6px;
|
|
font-weight: 700;
|
|
color: #0f172a;
|
|
}
|
|
|
|
.panel {
|
|
border: 1px solid #dbe2ea;
|
|
border-radius: 14px;
|
|
background: #fff;
|
|
padding: 20px;
|
|
}
|
|
|
|
.panel h3 {
|
|
font-size: 18px;
|
|
margin: 0 0 10px;
|
|
font-family: var(--font-heading);
|
|
letter-spacing: -0.015em;
|
|
}
|
|
|
|
.list {
|
|
margin: 0;
|
|
padding-left: 20px;
|
|
color: #334155;
|
|
line-height: 1.8;
|
|
font-size: 15px;
|
|
}
|
|
|
|
.compare-grid {
|
|
margin-top: 10px;
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 14px;
|
|
}
|
|
|
|
.compare-card {
|
|
position: relative;
|
|
border: 1px solid #e2e8f0;
|
|
border-left-width: 4px;
|
|
border-radius: 10px;
|
|
background: #fff;
|
|
padding: 16px;
|
|
min-width: 0;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.04);
|
|
}
|
|
|
|
.compare-card.fast {
|
|
border-left-color: #10b981;
|
|
}
|
|
|
|
.compare-card.slow {
|
|
border-left-color: #f59e0b;
|
|
}
|
|
|
|
.compare-main {
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.compare-item-title {
|
|
font-size: 12px;
|
|
letter-spacing: 0.03em;
|
|
text-transform: uppercase;
|
|
font-weight: 800;
|
|
color: #64748b;
|
|
font-family: var(--font-heading);
|
|
margin: 0;
|
|
}
|
|
|
|
.compare-item-name {
|
|
margin: 5px 0 0;
|
|
font-size: 17px;
|
|
font-weight: 700;
|
|
color: #0f172a;
|
|
font-family: var(--font-heading);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.compare-item-value {
|
|
margin: 5px 0 0;
|
|
font-size: 24px;
|
|
line-height: 1.1;
|
|
color: #0f172a;
|
|
font-weight: 800;
|
|
font-family: var(--font-heading);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.compare-icon {
|
|
width: 34px;
|
|
height: 34px;
|
|
flex-shrink: 0;
|
|
color: #cbd5e1;
|
|
opacity: 0.95;
|
|
}
|
|
|
|
.compare-item-sublist {
|
|
margin: 8px 0 0;
|
|
padding-left: 18px;
|
|
font-size: 13px;
|
|
color: #475569;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.compare-more {
|
|
margin-top: 10px;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 8px;
|
|
background: #f8fafc;
|
|
padding: 8px 10px;
|
|
}
|
|
|
|
.compare-more summary {
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
color: #334155;
|
|
font-weight: 700;
|
|
letter-spacing: 0.01em;
|
|
}
|
|
|
|
.compare-more ul {
|
|
margin: 8px 0 0;
|
|
padding-left: 18px;
|
|
font-size: 12px;
|
|
color: #475569;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.yearly-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: end;
|
|
gap: 12px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.yearly-chart {
|
|
display: grid;
|
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
gap: 10px;
|
|
align-items: end;
|
|
min-height: 310px;
|
|
}
|
|
|
|
.month-col {
|
|
text-align: center;
|
|
}
|
|
|
|
.month-bars {
|
|
height: 240px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: end;
|
|
gap: 6px;
|
|
}
|
|
|
|
.month-bar {
|
|
width: 22px;
|
|
border-radius: 5px 5px 0 0;
|
|
}
|
|
|
|
.month-bar.in {
|
|
background: #16a34a;
|
|
}
|
|
|
|
.month-bar.out {
|
|
background: #ef4444;
|
|
}
|
|
|
|
.month-label {
|
|
margin-top: 6px;
|
|
font-size: 12px;
|
|
color: #64748b;
|
|
font-weight: 700;
|
|
font-family: var(--font-heading);
|
|
}
|
|
|
|
.month-pct {
|
|
margin-top: 3px;
|
|
font-size: 12px;
|
|
line-height: 1.35;
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
.month-pct.month-pct-baris {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
line-height: 1.35;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.month-pct .pct-in {
|
|
color: #16a34a;
|
|
}
|
|
|
|
.month-pct .pct-sep {
|
|
color: #94a3b8;
|
|
font-weight: 500;
|
|
margin: 0 2px;
|
|
}
|
|
|
|
.month-pct .pct-out {
|
|
color: #ef4444;
|
|
}
|
|
|
|
.month-pct .pct-none {
|
|
color: #cbd5e1;
|
|
font-weight: 500;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.legend {
|
|
margin-top: 12px;
|
|
font-size: 12px;
|
|
color: #475569;
|
|
display: flex;
|
|
gap: 16px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.stock-alert {
|
|
border: 1px solid #facc15;
|
|
background: #fffbeb;
|
|
color: #854d0e;
|
|
border-radius: 12px;
|
|
padding: 14px 16px;
|
|
margin: 0 0 20px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.stat-muted {
|
|
margin-top: 10px;
|
|
font-size: 12px;
|
|
color: #94a3b8;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.stat-details {
|
|
margin-top: 10px;
|
|
font-size: 11px;
|
|
color: #64748b;
|
|
}
|
|
|
|
.stat-details summary {
|
|
cursor: pointer;
|
|
color: #166534;
|
|
font-weight: 600;
|
|
user-select: none;
|
|
font-family: var(--font-heading);
|
|
}
|
|
|
|
.stat-details summary:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.stat-details-body {
|
|
margin-top: 8px;
|
|
max-height: 140px;
|
|
overflow-y: auto;
|
|
padding: 8px 10px;
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 8px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.stat-details-body ul {
|
|
margin: 0;
|
|
padding-left: 18px;
|
|
}
|
|
|
|
.stat-details-body li {
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.stock-alert details {
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.stock-alert summary {
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
color: #713f12;
|
|
}
|
|
|
|
.wa-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 12px;
|
|
margin: 14px 0;
|
|
}
|
|
|
|
.wa-metric {
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 10px;
|
|
background: #f8fafc;
|
|
padding: 12px 14px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.wa-metric span {
|
|
display: block;
|
|
color: #64748b;
|
|
font-size: 12px;
|
|
font-weight: 800;
|
|
letter-spacing: 0.03em;
|
|
text-transform: uppercase;
|
|
font-family: var(--font-heading);
|
|
}
|
|
|
|
.wa-metric strong {
|
|
display: block;
|
|
margin-top: 4px;
|
|
color: #0f172a;
|
|
font-size: 24px;
|
|
line-height: 1.1;
|
|
font-family: var(--font-heading);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.wa-auto-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 32px;
|
|
padding: 5px 10px;
|
|
border-radius: 999px;
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
font-size: 12px;
|
|
font-weight: 800;
|
|
font-family: var(--font-heading);
|
|
}
|
|
|
|
.wa-auto-badge.pending {
|
|
background: #fef3c7;
|
|
color: #92400e;
|
|
}
|
|
|
|
.wa-message-preview {
|
|
margin-top: 14px;
|
|
border: 1px solid #dbe2ea;
|
|
border-radius: 10px;
|
|
background: #f8fafc;
|
|
padding: 12px 14px;
|
|
color: #334155;
|
|
font-size: 13px;
|
|
line-height: 1.6;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.wa-note {
|
|
margin: 10px 0 0;
|
|
color: #64748b;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
@media (max-width: 760px) {
|
|
.wa-summary,
|
|
.compare-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.yearly-chart {
|
|
overflow-x: auto;
|
|
grid-template-columns: repeat(12, 54px);
|
|
padding-bottom: 8px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<section class="hero">
|
|
<div>
|
|
<h2>Dashboard</h2>
|
|
<p>Selamat datang, {{ $user->name }}.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="stats">
|
|
<article class="stat-card stat-total">
|
|
<div class="stat-main">
|
|
<div class="stat-title">Total Produk</div>
|
|
<div class="stat-value">{{ $totalProduk }}</div>
|
|
@if ($daftarNamaProduk->isNotEmpty())
|
|
<details class="stat-details">
|
|
<summary>Lihat daftar nama produk ({{ $daftarNamaProduk->count() }})</summary>
|
|
<div class="stat-details-body">{{ $daftarNamaProduk->implode(', ') }}</div>
|
|
</details>
|
|
@else
|
|
<p class="stat-muted">Belum ada produk terdaftar.</p>
|
|
@endif
|
|
</div>
|
|
<svg class="stat-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path fill="currentColor"
|
|
d="M6 2h8l4 4v2h1a2 2 0 0 1 2 2v9.5A2.5 2.5 0 0 1 18.5 22h-13A2.5 2.5 0 0 1 3 19.5V10a2 2 0 0 1 2-2h1V4a2 2 0 0 1 2-2m1 6h10V7h-3a1 1 0 0 1-1-1V3H8a1 1 0 0 0-1 1zm-2 2v9.5a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5V10z" />
|
|
</svg>
|
|
</article>
|
|
<article class="stat-card stat-low">
|
|
<div class="stat-main">
|
|
<div class="stat-title">Stok Menipis</div>
|
|
<div class="stat-value">{{ $stokMenipis }}</div>
|
|
@if ($produkStokMenipis->isNotEmpty())
|
|
<details class="stat-details">
|
|
<summary>Lihat barang menipis ({{ $produkStokMenipis->count() }})</summary>
|
|
<div class="stat-details-body">
|
|
<ul>
|
|
@foreach ($produkStokMenipis as $p)
|
|
<li>{{ $p->nama }} (stok {{ $p->stok }}, min. {{ $p->stok_minimum }})</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
@else
|
|
<p class="stat-muted">Tidak ada.</p>
|
|
@endif
|
|
</div>
|
|
<svg class="stat-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path fill="currentColor"
|
|
d="M13 2a1 1 0 0 0-2 0v10.59l-1.3-1.3a1 1 0 1 0-1.4 1.42l3 2.98a1 1 0 0 0 1.4 0l3-2.98a1 1 0 1 0-1.4-1.42l-1.3 1.3zM5 18a1 1 0 0 1 1 1v1h12v-1a1 1 0 1 1 2 0v1a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-1a1 1 0 0 1 1-1" />
|
|
</svg>
|
|
</article>
|
|
<article class="stat-card stat-in">
|
|
<div class="stat-main">
|
|
<div class="stat-title">Barang Masuk</div>
|
|
<div class="stat-value">{{ number_format($jumlahBarangMasuk) }}</div>
|
|
</div>
|
|
<svg class="stat-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path fill="currentColor"
|
|
d="M4 11a1 1 0 0 1 1-1h8.59L11.3 7.7a1 1 0 0 1 1.4-1.4l4 4a1 1 0 0 1 0 1.4l-4 4a1 1 0 1 1-1.4-1.4L13.59 12H5a1 1 0 0 1-1-1m1 7h14a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2" />
|
|
</svg>
|
|
</article>
|
|
<article class="stat-card stat-out">
|
|
<div class="stat-main">
|
|
<div class="stat-title">Barang Keluar</div>
|
|
<div class="stat-value">{{ number_format($jumlahBarangKeluar) }}</div>
|
|
</div>
|
|
<svg class="stat-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path fill="currentColor"
|
|
d="M20 11a1 1 0 0 0-1-1h-8.59l2.29-2.3a1 1 0 0 0-1.4-1.4l-4 4a1 1 0 0 0 0 1.4l4 4a1 1 0 0 0 1.4-1.4L10.41 12H19a1 1 0 0 0 1-1m-1 7H5a1 1 0 1 0 0 2h14a1 1 0 1 0 0-2" />
|
|
</svg>
|
|
</article>
|
|
</div>
|
|
|
|
<section class="panel" style="margin-top:14px;">
|
|
<div class="yearly-header">
|
|
<div>
|
|
<h3 style="margin:0;">Notifikasi Stok Minimum via WhatsApp</h3>
|
|
<div style="font-size:13px; color:#64748b; margin-top:4px;">
|
|
Sistem mengirim pesan otomatis ke nomor penerima saat stok barang mencapai batas minimum.
|
|
</div>
|
|
</div>
|
|
<span class="wa-auto-badge {{ $nomorPenerimaWhatsApp->isNotEmpty() ? '' : 'pending' }}">
|
|
{{ $nomorPenerimaWhatsApp->isNotEmpty() ? 'Otomatis aktif' : 'Nomor belum diatur' }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="wa-summary">
|
|
<div class="wa-metric">
|
|
<span>Barang Stok Minimum</span>
|
|
<strong>{{ number_format($stokMenipis) }}</strong>
|
|
</div>
|
|
<div class="wa-metric">
|
|
<span>Persentase Stok Aman</span>
|
|
<strong>{{ $persentaseStokAman }}%</strong>
|
|
</div>
|
|
<div class="wa-metric">
|
|
<span>Nomor Penerima</span>
|
|
<strong>{{ $nomorPenerimaWhatsApp->isNotEmpty() ? $nomorPenerimaWhatsApp->implode(', ') : '-' }}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="app-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Produk</th>
|
|
<th>Stok</th>
|
|
<th>Stok Minimum</th>
|
|
<th>Kondisi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse ($produkStokMenipis as $produk)
|
|
<tr>
|
|
<td><strong>{{ $produk->nama }}</strong></td>
|
|
<td>{{ number_format((int) $produk->stok) }} unit</td>
|
|
<td>{{ number_format((int) $produk->stok_minimum) }} unit</td>
|
|
<td>Perlu restock</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="4">Semua stok barang masih berada di atas stok minimum.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="wa-message-preview">{{ $pesanWhatsAppStokMinimum }}</div>
|
|
<p class="wa-note">Pesan dikirim otomatis oleh sistem setelah transaksi barang keluar atau barang hilang membuat stok mencapai batas minimum.</p>
|
|
</section>
|
|
|
|
<!-- <div class="stock-alert">
|
|
Stok Menipis: <strong>{{ $stokMenipis }}</strong> produk perlu perhatian.
|
|
Persentase stok aman saat ini <strong>{{ $persentaseStokAman }}%</strong>.
|
|
@if ($produkStokMenipis->isNotEmpty())
|
|
<details>
|
|
<summary>Detail barang menipis ({{ $produkStokMenipis->count() }})</summary>
|
|
<div class="stat-details-body" style="margin-top:8px;">
|
|
{{ $produkStokMenipis->map(fn($p) => $p->nama . ' (stok ' . $p->stok . ')')->implode(', ') }}
|
|
</div>
|
|
</details>
|
|
@else
|
|
<div style="margin-top:8px;">
|
|
<strong>Barang menipis:</strong> Tidak ada.
|
|
</div>
|
|
@endif
|
|
</div> -->
|
|
|
|
<section class="panel" style="margin-top:14px;">
|
|
<div class="yearly-header">
|
|
<div>
|
|
<h3 style="margin:0;">Grafik Tahunan Arus Barang</h3>
|
|
<div style="font-size:13px; color:#64748b; margin-top:4px;">
|
|
Barang masuk: <strong>{{ $totalMasukTahunan }}</strong> unit —
|
|
Barang keluar: <strong>{{ $totalKeluarTahunan }}</strong> unit —
|
|
Presentase masuk: <strong>{{ $persenMasukTahunan }}%</strong>, keluar:
|
|
<strong>{{ $persenKeluarTahunan }}%</strong>
|
|
</div>
|
|
</div>
|
|
<!-- <form method="GET" action="{{ route('dashboard') }}" style="display:flex; gap:8px; align-items:center;">
|
|
<select name="tahun">
|
|
@foreach ($daftarTahun as $tahun)
|
|
<option value="{{ $tahun }}" @selected((int) $tahunDipilih === (int) $tahun)>{{ $tahun }}</option>
|
|
@endforeach
|
|
</select>
|
|
<select name="jenis_data">
|
|
<option value="semua" @selected($jenisData === 'semua')>Masuk dan Keluar</option>
|
|
<option value="masuk" @selected($jenisData === 'masuk')>Masuk</option>
|
|
<option value="keluar" @selected($jenisData === 'keluar')>Keluar</option>
|
|
</select>
|
|
<button type="submit">Tampilkan</button>
|
|
</form> -->
|
|
</div>
|
|
|
|
@php
|
|
$tinggiAreaBatangPx = 228;
|
|
@endphp
|
|
|
|
<div class="yearly-chart">
|
|
@foreach ($trenTahunan as $bulan)
|
|
@php
|
|
$idxBulan = $loop->index;
|
|
$tinggiMasuk = max(2, (int) round(($bulan['masuk'] / $maksTrenTahunan) * $tinggiAreaBatangPx));
|
|
$tinggiKeluar = max(2, (int) round(($bulan['keluar'] / $maksTrenTahunan) * $tinggiAreaBatangPx));
|
|
$kontribMasukPct = $pctMasukKontribTahunan[$idxBulan] ?? null;
|
|
$kontribKeluarPct = $pctKeluarKontribTahunan[$idxBulan] ?? null;
|
|
@endphp
|
|
<div class="month-col">
|
|
<div class="month-bars">
|
|
@if ($jenisData !== 'keluar')
|
|
<div class="month-bar in" style="height: {{ $tinggiMasuk }}px;"
|
|
title="{{ $bulan['label'] }} | Masuk: {{ $bulan['masuk'] }} unit @if ($kontribMasukPct !== null) | Kontribusi ke presentase masuk tahun {{ $persenMasukTahunan }}%: {{ $kontribMasukPct }} poin persen @endif">
|
|
</div>
|
|
@endif
|
|
@if ($jenisData !== 'masuk')
|
|
<div class="month-bar out" style="height: {{ $tinggiKeluar }}px;"
|
|
title="{{ $bulan['label'] }} | Keluar: {{ $bulan['keluar'] }} unit @if ($kontribKeluarPct !== null) | Kontribusi ke presentase keluar tahun {{ $persenKeluarTahunan }}%: {{ $kontribKeluarPct }} poin persen @endif">
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="month-label">{{ $bulan['label'] }}</div>
|
|
<div class="month-pct month-pct-baris">
|
|
@if ($kontribMasukPct !== null && $kontribKeluarPct !== null)
|
|
Presentase masuk: <span class="pct-in">{{ $kontribMasukPct }}%</span>,
|
|
keluar: <span class="pct-out">{{ $kontribKeluarPct }}%</span>
|
|
@else
|
|
<span class="pct-none">—</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
<div class="legend">
|
|
@if ($jenisData !== 'keluar')
|
|
<span><strong style="color:#16a34a;">Hijau</strong> = Barang Masuk</span>
|
|
@endif
|
|
@if ($jenisData !== 'masuk')
|
|
<span><strong style="color:#ef4444;">Merah</strong> = Barang Keluar</span>
|
|
@endif
|
|
</div>
|
|
</section>
|
|
|
|
<!-- <section class="panel" style="margin-top:14px;">
|
|
<h3>Top Produk Paling Aktif</h3>
|
|
<ul class="list">
|
|
@forelse ($topPergerakanProduk as $item)
|
|
<li>{{ $item->product->nama ?? 'Produk' }} - {{ $item->total }} unit transaksi</li>
|
|
@empty
|
|
<li>Belum ada data pergerakan produk.</li>
|
|
@endforelse
|
|
</ul>
|
|
</section> -->
|
|
|
|
<section class="panel" style="margin-top:14px;">
|
|
<h3>Perbandingan Kecepatan Penjualan Produk</h3>
|
|
<!-- <p class="stat-muted" style="margin-top:-2px; margin-bottom:10px;">
|
|
Periode otomatis: {{ $labelBulanPerbandinganPenjualan }} (berdasarkan transaksi barang keluar bulan berjalan).
|
|
</p> -->
|
|
@if ($produkTercepatTerjual && $produkTerlambatTerjual)
|
|
<div class="compare-grid">
|
|
<article class="compare-card fast">
|
|
<div class="compare-main">
|
|
<p class="compare-item-title">Produk Paling Aktif Terjual</p>
|
|
<p class="compare-item-name">{{ $produkTercepatTerjual->product->nama ?? 'Produk' }}</p>
|
|
<p class="compare-item-value">{{ number_format((int) $produkTercepatTerjual->total_terjual) }} unit</p>
|
|
<details class="compare-more">
|
|
<summary>More info (produk mendekati tercepat)</summary>
|
|
@if ($produkCepatTerjualLainnya->isNotEmpty())
|
|
<ul>
|
|
@foreach ($produkCepatTerjualLainnya as $item)
|
|
<li>
|
|
{{ $item->product->nama ?? 'Produk' }} -
|
|
{{ number_format((int) $item->total_terjual) }} unit
|
|
(selisih
|
|
{{ number_format(abs((int) $produkTercepatTerjual->total_terjual - (int) $item->total_terjual)) }}
|
|
unit)
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@else
|
|
<p class="stat-muted" style="margin:8px 0 0;">Belum ada produk lain yang mendekati.</p>
|
|
@endif
|
|
</details>
|
|
</div>
|
|
</article>
|
|
<article class="compare-card slow">
|
|
<div class="compare-main">
|
|
<p class="compare-item-title">Produk Paling Lambat Terjual</p>
|
|
<p class="compare-item-name">{{ $produkTerlambatTerjual->product->nama ?? 'Produk' }}</p>
|
|
<p class="compare-item-value">{{ number_format((int) $produkTerlambatTerjual->total_terjual) }} unit</p>
|
|
<details class="compare-more">
|
|
<summary>More info (produk mendekati terlambat)</summary>
|
|
@if ($produkLambatTerjualLainnya->isNotEmpty())
|
|
<ul>
|
|
@foreach ($produkLambatTerjualLainnya as $item)
|
|
<li>
|
|
{{ $item->product->nama ?? 'Produk' }} -
|
|
{{ number_format((int) $item->total_terjual) }} unit
|
|
(selisih
|
|
{{ number_format(abs((int) $item->total_terjual - (int) $produkTerlambatTerjual->total_terjual)) }}
|
|
unit)
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@else
|
|
<p class="stat-muted" style="margin:8px 0 0;">Belum ada produk lain yang mendekati.</p>
|
|
@endif
|
|
</details>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<p class="stat-trend">
|
|
Selisih performa penjualan:
|
|
<span class="stat-trend-value">{{ number_format($selisihProdukTerjual) }} unit</span>
|
|
</p>
|
|
<p class="stat-muted">Produk tambahan ditampilkan jika selisih penjualan maksimal
|
|
{{ $batasSelisihProdukMirip }} unit dari produk utama.
|
|
</p>
|
|
@else
|
|
<p class="stat-muted">Belum ada data barang keluar untuk menghitung kecepatan penjualan produk.</p>
|
|
@endif
|
|
</section>
|
|
|
|
|
|
<!-- <section class="panel" style="margin-top:14px;">
|
|
<h3>Prioritas Restock</h3>
|
|
<div class="bars">
|
|
@forelse ($produkStokMenipis as $produk)
|
|
@php
|
|
$ratio = $produk->stok_minimum > 0 ? min(100, (int) (($produk->stok / $produk->stok_minimum) * 100)) : 0;
|
|
@endphp
|
|
<div class="bar-row">
|
|
<strong>{{ $produk->nama }}</strong>
|
|
<div class="bar-track">
|
|
<div class="bar-fill" style="width: {{ $ratio }}%"></div>
|
|
</div>
|
|
<span>{{ $produk->stok }}</span>
|
|
</div>
|
|
@empty
|
|
<p>Tidak ada produk yang perlu restock.</p>
|
|
@endforelse
|
|
</div>
|
|
</section> -->
|
|
|
|
<section class="panel" style="margin-top:14px;">
|
|
<h3>Aktivitas Terbaru</h3>
|
|
<table class="app-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Waktu</th>
|
|
<th>Tipe</th>
|
|
<th>Jumlah</th>
|
|
<th>Barang</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse ($aktivitasTerbaru as $aktivitas)
|
|
<tr>
|
|
<td>{{ $aktivitas->created_at?->timezone('Asia/Jakarta')->format('d/m/Y H:i') ?? '—' }}</td>
|
|
<td>{{ strtoupper($aktivitas->tipe) }}</td>
|
|
<td>{{ $aktivitas->jumlah }} unit</td>
|
|
<td>{{ $aktivitas->product->nama ?? 'Produk' }}</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="4">Belum ada aktivitas transaksi.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
@endsection
|