213 lines
12 KiB
PHP
213 lines
12 KiB
PHP
@extends('layout.layout')
|
|
|
|
@php
|
|
$title = 'Laporan';
|
|
$subTitle = 'Ringkasan pembayaran & riwayat transaksi pelanggan';
|
|
@endphp
|
|
|
|
@section('content')
|
|
<div class="card border-0 overflow-hidden">
|
|
<div class="card-header px-6 py-4 border-b">
|
|
<h6 class="card-title mb-0 text-lg font-semibold">Laporan Transaksi</h6>
|
|
</div>
|
|
<div class="card-body p-6">
|
|
|
|
{{-- STATS --}}
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
<div class="bg-success-50 dark:bg-success-900/20 border border-success-200 dark:border-success-800 rounded-xl p-4 flex items-center gap-4">
|
|
<div class="w-11 h-11 bg-success-100 dark:bg-success-900/40 rounded-lg flex items-center justify-center text-success-600 text-xl">
|
|
<iconify-icon icon="iconamoon:check-circle-1-light"></iconify-icon>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-neutral-500 uppercase tracking-wider font-medium">Total Lunas</p>
|
|
<h4 class="text-lg font-bold text-neutral-800">Rp {{ number_format($totalPaid, 0, ',', '.') }}</h4>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-800 rounded-xl p-4 flex items-center gap-4">
|
|
<div class="w-11 h-11 bg-warning-100 dark:bg-warning-900/40 rounded-lg flex items-center justify-center text-warning-600 text-xl">
|
|
<iconify-icon icon="iconamoon:clock-light"></iconify-icon>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-neutral-500 uppercase tracking-wider font-medium">Belum Bayar</p>
|
|
<h4 class="text-lg font-bold text-neutral-800">Rp {{ number_format($totalUnpaid, 0, ',', '.') }}</h4>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-primary-50 dark:bg-primary-900/20 border border-primary-200 dark:border-primary-800 rounded-xl p-4 flex items-center gap-4">
|
|
<div class="w-11 h-11 bg-primary-100 dark:bg-primary-900/40 rounded-lg flex items-center justify-center text-primary-600 text-xl">
|
|
<iconify-icon icon="iconamoon:invoice-light"></iconify-icon>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-neutral-500 uppercase tracking-wider font-medium">Total Transaksi</p>
|
|
<h4 class="text-lg font-bold text-neutral-800">{{ $totalTransactions }}</h4>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-info-50 dark:bg-info-900/20 border border-info-200 dark:border-info-800 rounded-xl p-4 flex items-center gap-4">
|
|
<div class="w-11 h-11 bg-info-100 dark:bg-info-900/40 rounded-lg flex items-center justify-center text-info-600 text-xl">
|
|
<iconify-icon icon="iconamoon:chart-light"></iconify-icon>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-neutral-500 uppercase tracking-wider font-medium">Pemasukan Bulan Ini</p>
|
|
<h4 class="text-lg font-bold text-neutral-800">Rp {{ number_format($totalPaid + $totalUnpaid, 0, ',', '.') }}</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- CHART --}}
|
|
<div class="border border-neutral-200 dark:border-neutral-600 rounded-xl p-5 mb-6">
|
|
<h6 class="font-semibold text-neutral-800 mb-4">Tren Pembayaran 6 Bulan Terakhir</h6>
|
|
<div style="height: 260px;">
|
|
<canvas id="chartTransaksi"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- FILTER --}}
|
|
{{-- FILTER --}}
|
|
<div class="border border-neutral-200 dark:border-neutral-600 rounded-xl p-5 mb-6">
|
|
<form method="GET" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-7 gap-4 items-end">
|
|
<div>
|
|
<label class="block text-xs text-neutral-500 uppercase tracking-wider font-medium mb-1">Pelanggan</label>
|
|
<select name="user_id" class="w-full border border-neutral-300 dark:border-neutral-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-neutral-800">
|
|
<option value="">Semua Pelanggan</option>
|
|
@foreach($users as $u)
|
|
<option value="{{ $u->id }}" {{ $userId == $u->id ? 'selected' : '' }}>
|
|
{{ $u->name }} - {{ $u->meteran->nomor_seri ?? 'No Meteran' }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-neutral-500 uppercase tracking-wider font-medium mb-1">Mulai</label>
|
|
<input type="date" name="start_date" value="{{ $startDate }}" class="w-full border border-neutral-300 dark:border-neutral-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-neutral-800">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-neutral-500 uppercase tracking-wider font-medium mb-1">Sampai</label>
|
|
<input type="date" name="end_date" value="{{ $endDate }}" class="w-full border border-neutral-300 dark:border-neutral-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-neutral-800">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-neutral-500 uppercase tracking-wider font-medium mb-1">Status</label>
|
|
<select name="status" class="w-full border border-neutral-300 dark:border-neutral-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-neutral-800">
|
|
<option value="all" {{ $statusFilter == 'all' ? 'selected' : '' }}>Semua</option>
|
|
<option value="paid" {{ $statusFilter == 'paid' ? 'selected' : '' }}>Lunas</option>
|
|
<option value="unpaid" {{ $statusFilter == 'unpaid' ? 'selected' : '' }}>Belum Bayar</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-neutral-500 uppercase tracking-wider font-medium mb-1">Cari</label>
|
|
<input type="text" name="search" value="{{ $search }}" placeholder="No. Invoice..." class="w-full border border-neutral-300 dark:border-neutral-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-neutral-800">
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<button type="submit" class="px-4 py-2 bg-primary-600 text-white rounded-lg flex items-center gap-2 hover:bg-primary-700 transition text-sm font-medium">
|
|
<iconify-icon icon="iconamoon:filter-light"></iconify-icon> Filter
|
|
</button>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<a href="{{ route('laporan.pdf', request()->query()) }}" class="px-3 py-2 bg-danger-50 text-danger-600 border border-danger-200 rounded-lg flex items-center gap-1 hover:bg-danger-100 transition text-xs font-medium">
|
|
PDF
|
|
</a>
|
|
<a href="{{ route('laporan.excel', request()->query()) }}" class="px-3 py-2 bg-success-50 text-success-600 border border-success-200 rounded-lg flex items-center gap-1 hover:bg-success-100 transition text-xs font-medium">
|
|
CSV
|
|
</a>
|
|
{{-- <a href="{{ route('laporan.csv', request()->query()) }}" class="px-3 py-2 bg-success-50 text-success-600 border border-success-200 rounded-lg flex items-center gap-1 hover:bg-success-100 transition text-xs font-medium">
|
|
CSV
|
|
</a> --}}
|
|
{{-- <a href="{{ route('laporan.excel', request()->query()) }}" class="px-3 py-2 bg-green-50 text-green-600 border border-green-200 rounded-lg flex items-center gap-1 hover:bg-green-100 transition text-xs font-medium">
|
|
Excel
|
|
</a> --}}
|
|
</div>
|
|
</form>
|
|
|
|
{{-- Info User Terpilih --}}
|
|
@if($selectedUser)
|
|
<div class="mt-3 bg-primary-50 dark:bg-primary-900/20 border border-primary-200 dark:border-primary-800 rounded-lg px-4 py-2 text-sm text-primary-700 dark:text-primary-400">
|
|
📊 Menampilkan laporan untuk: <strong>{{ $selectedUser->name }}</strong>
|
|
(ID Meteran: {{ $selectedUser->meteran->nomor_seri ?? '-' }})
|
|
· <a href="{{ route('laporan.index') }}" class="underline">Reset</a>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- TABLE --}}
|
|
<div class="overflow-x-auto border border-neutral-200 dark:border-neutral-600 rounded-lg">
|
|
<table class="w-full border-separate">
|
|
<thead>
|
|
<tr class="bg-neutral-50 dark:bg-neutral-700">
|
|
<th class="px-4 py-3 text-left w-12 font-bold">#</th>
|
|
<th class="px-4 py-3 text-left font-semibold">ID Tagihan</th>
|
|
<th class="px-4 py-3 text-left font-semibold">Pelanggan</th>
|
|
<th class="px-4 py-3 text-left font-semibold">Total</th>
|
|
<th class="px-4 py-3 text-left font-semibold">Status</th>
|
|
<th class="px-4 py-3 text-left font-semibold">Metode</th>
|
|
<th class="px-4 py-3 text-left font-semibold">Tgl Dibuat</th>
|
|
<th class="px-4 py-3 text-left font-semibold">Tgl Dibayar</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($invoices as $index => $inv)
|
|
<tr class="border-b border-neutral-200 dark:border-neutral-600 hover:bg-neutral-50 transition">
|
|
<td class="px-4 py-3">{{ $invoices->firstItem() + $index }}</td>
|
|
<td class="px-4 py-3 font-medium text-neutral-800">{{ $inv->invoice_number }}</td>
|
|
<td class="px-4 py-3">{{ $inv->user->name ?? '-' }}</td>
|
|
<td class="px-4 py-3 font-semibold">Rp {{ number_format($inv->total_amount, 0, ',', '.') }}</td>
|
|
<td class="px-4 py-3">
|
|
@if($inv->status == 'paid')
|
|
<span class="bg-success-100 dark:bg-success-900/30 text-success-600 dark:text-success-400 px-3 py-1 rounded-full font-medium text-xs border border-success-200">
|
|
Lunas
|
|
</span>
|
|
@else
|
|
<span class="bg-warning-100 dark:bg-warning-900/30 text-warning-600 dark:text-warning-400 px-3 py-1 rounded-full font-medium text-xs border border-warning-200">
|
|
Belum Bayar
|
|
</span>
|
|
@endif
|
|
</td>
|
|
<td class="px-4 py-3">{{ $inv->payment_method ? str_replace('_', ' ', ucfirst($inv->payment_method)) : '-' }}</td>
|
|
<td class="px-4 py-3 text-sm">{{ $inv->created_at->format('d M Y, H:i') }}</td>
|
|
<td class="px-4 py-3 text-sm">{{ $inv->paid_at ? $inv->paid_at->format('d M Y, H:i') : '-' }}</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="8" class="text-center py-10 text-neutral-500">
|
|
<iconify-icon icon="iconamoon:inbox-light" class="text-4xl block mx-auto mb-2"></iconify-icon>
|
|
Tidak ada data transaksi
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="mt-6">{{ $invoices->links() }}</div>
|
|
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var canvas = document.getElementById('chartTransaksi');
|
|
console.log('Canvas found:', canvas);
|
|
if (!canvas) return;
|
|
|
|
new Chart(canvas.getContext('2d'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: @json($chartData['months']),
|
|
datasets: [
|
|
{ label: 'Lunas', data: @json($chartData['paid']), backgroundColor: '#16a34a', borderRadius: 6 },
|
|
{ label: 'Belum Bayar', data: @json($chartData['unpaid']), backgroundColor: '#d97706', borderRadius: 6 }
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { position: 'bottom', labels: { usePointStyle: true } } },
|
|
scales: {
|
|
y: { beginAtZero: true, ticks: { callback: v => 'Rp ' + v.toLocaleString('id-ID') } },
|
|
x: { grid: { display: false } }
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script> |