1044 lines
49 KiB
PHP
1044 lines
49 KiB
PHP
@php
|
||
/** @var bool $isPdf */
|
||
$isPdf = $isPdf ?? false;
|
||
@endphp
|
||
|
||
@if($isPdf)
|
||
{{-- ========== PDF MODE (mpdf साठी) ========== --}}
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title></title>
|
||
<style>
|
||
@page {
|
||
margin: 10mm 8mm 10mm 8mm;
|
||
header: none;
|
||
footer: none;
|
||
}
|
||
|
||
:root { --admin-font: "Inter", sans-serif; }
|
||
|
||
html, body {
|
||
margin: 0;
|
||
padding: 0;
|
||
font-family: var(--admin-font);
|
||
font-size: 12px;
|
||
}
|
||
|
||
body, input, select, textarea, button, table, th, td {
|
||
font-family: var(--admin-font);
|
||
}
|
||
|
||
.report-container {
|
||
background: #ffffff;
|
||
padding: 16px 18px;
|
||
border-radius: 10px;
|
||
border: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.report-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 10px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.header-logo { height: 45px; width: auto; }
|
||
|
||
.report-title { font-size: 18px; font-weight: 700; color: #1a202c; margin: 0; line-height: 1.2; }
|
||
.report-company { font-size: 11px; color: #718096; font-weight: 500; margin-top: 2px; }
|
||
|
||
.stats-container {
|
||
margin: 10px 0 8px 0;
|
||
}
|
||
|
||
.stats-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 5px;
|
||
}
|
||
|
||
.stats-table > tbody > tr > td {
|
||
width: 33.33%;
|
||
padding: 0;
|
||
border: none;
|
||
background: transparent;
|
||
vertical-align: top;
|
||
}
|
||
|
||
.stat-card-wrap {
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stat-inner {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
.stat-strip {
|
||
width: 5px;
|
||
padding: 0;
|
||
}
|
||
|
||
.stat-content {
|
||
padding: 8px 10px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.strip-info { background: #3b82f6; }
|
||
.strip-purple { background: #8b5cf6; }
|
||
.strip-default { background: #4f46e5; }
|
||
.strip-success { background: #10b981; }
|
||
.strip-danger { background: #ef4444; }
|
||
.strip-warning { background: #f59e0b; }
|
||
|
||
.bg-info { background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%); }
|
||
.bg-purple { background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%); }
|
||
.bg-default { background: linear-gradient(135deg, #f8f9ff 0%, #f0f4ff 100%); }
|
||
.bg-success { background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%); }
|
||
.bg-danger { background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%); }
|
||
.bg-warning { background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%); }
|
||
|
||
.stat-icon-box {
|
||
display: inline-block;
|
||
width: 30px;
|
||
height: 30px;
|
||
border-radius: 7px;
|
||
text-align: center;
|
||
line-height: 30px;
|
||
margin-right: 8px;
|
||
vertical-align: middle;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.icon-info { background: rgba(59,130,246,0.12); color: #3b82f6; }
|
||
.icon-purple { background: rgba(139,92,246,0.12); color: #8b5cf6; }
|
||
.icon-default { background: rgba(79,70,229,0.12); color: #4f46e5; }
|
||
.icon-success { background: rgba(16,185,129,0.12); color: #10b981; }
|
||
.icon-danger { background: rgba(239,68,68,0.12); color: #ef4444; }
|
||
.icon-warning { background: rgba(245,158,11,0.12); color: #f59e0b; }
|
||
|
||
.stat-text { display: inline-block; vertical-align: middle; }
|
||
.stat-value { font-size: 15px; font-weight: 700; color: #1a202c; line-height: 1.2; display: block; }
|
||
.stat-label { font-size: 9px; color: #718096; font-weight: 600; display: block; margin-top: 2px; }
|
||
|
||
.table-container {
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.report-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 10px;
|
||
}
|
||
|
||
.report-table thead {
|
||
background: #ffffff;
|
||
}
|
||
|
||
.report-table th {
|
||
padding: 6px 4px;
|
||
text-align: center;
|
||
font-size: 9px;
|
||
font-weight: 600;
|
||
border: 0.5px solid #e5e7eb;
|
||
white-space: nowrap;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: #ffffff !important;
|
||
}
|
||
|
||
.report-table td {
|
||
padding: 5px 4px;
|
||
font-size: 9px;
|
||
color: #374151;
|
||
border: 0.5px solid #e5e7eb;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.amt-cell { font-weight: 600; }
|
||
.amt-payable { color: #1e40af; }
|
||
.amt-paid { color: #059669; }
|
||
.amt-remaining{ color: #dc2626; }
|
||
.amt-gst { color: #d97706; }
|
||
.amt-base { color: #374151; }
|
||
|
||
.count-badge {
|
||
display: inline-block;
|
||
background: #eff6ff;
|
||
color: #1d4ed8;
|
||
font-weight: 700;
|
||
font-size: 9px;
|
||
padding: 1px 6px;
|
||
border-radius: 12px;
|
||
border: 0.5px solid #bfdbfe;
|
||
}
|
||
|
||
.status-badge {
|
||
display: inline-block;
|
||
font-size: 8px;
|
||
font-weight: 700;
|
||
padding: 3px 6px;
|
||
border-radius: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
.status-paid { background: #dcfce7; color: #166534; border: 0.5px solid #bbf7d0; }
|
||
.status-pending { background: #fef3c7; color: #92400e; border: 0.5px solid #fde68a; }
|
||
.status-overdue { background: #fee2e2; color: #991b1b; border: 0.5px solid #fecaca; }
|
||
.status-paying { background: #dbeafe; color: #1e40af; border: 0.5px solid #bfdbfe; }
|
||
|
||
.empty-state { text-align: center; padding: 20px 10px; color: #6b7280; }
|
||
.empty-state h3 { margin: 0 0 4px 0; font-size: 12px; }
|
||
.empty-state p { margin: 0; font-size: 10px; }
|
||
</style>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||
</head>
|
||
<body>
|
||
<div class="report-container">
|
||
|
||
{{-- HEADER --}}
|
||
<div class="report-header">
|
||
<img src="{{ public_path('images/kent_logo2.png') }}" class="header-logo" alt="Logo">
|
||
<div>
|
||
<div class="report-title">KENT International Pvt. Ltd.</div>
|
||
<div class="report-company">Container Report</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- STATS CARDS --}}
|
||
@php
|
||
$totalContainers = $reports->count();
|
||
$totalInvoices = $reports->sum('total_invoices');
|
||
$totalPayable = $reports->sum('total_payable');
|
||
$totalPaid = $reports->sum('total_paid');
|
||
$totalRemaining = $reports->sum('total_remaining');
|
||
$overdueCount = $reports->where('container_status','overdue')->count();
|
||
@endphp
|
||
|
||
<div class="stats-container">
|
||
<table class="stats-table">
|
||
{{-- Row 1 --}}
|
||
<tr>
|
||
{{-- Total Containers --}}
|
||
<td>
|
||
<div class="stat-card-wrap">
|
||
<table class="stat-inner">
|
||
<tr>
|
||
<td class="stat-strip strip-info"> </td>
|
||
<td class="stat-content bg-info">
|
||
<span class="stat-icon-box icon-info">
|
||
<i class="fas fa-box"></i>
|
||
</span>
|
||
<span class="stat-text">
|
||
<span class="stat-value">{{ $totalContainers }}</span>
|
||
<span class="stat-label">Total Containers</span>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</td>
|
||
|
||
{{-- Total Invoices --}}
|
||
<td>
|
||
<div class="stat-card-wrap">
|
||
<table class="stat-inner">
|
||
<tr>
|
||
<td class="stat-strip strip-purple"> </td>
|
||
<td class="stat-content bg-purple">
|
||
<span class="stat-icon-box icon-purple">
|
||
<i class="fas fa-file-invoice"></i>
|
||
</span>
|
||
<span class="stat-text">
|
||
<span class="stat-value">{{ $totalInvoices }}</span>
|
||
<span class="stat-label">Total Invoices</span>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</td>
|
||
|
||
{{-- Total Payable --}}
|
||
<td>
|
||
<div class="stat-card-wrap">
|
||
<table class="stat-inner">
|
||
<tr>
|
||
<td class="stat-strip strip-default"> </td>
|
||
<td class="stat-content bg-default">
|
||
<span class="stat-icon-box icon-default">
|
||
<i class="fas fa-rupee-sign"></i>
|
||
</span>
|
||
<span class="stat-text">
|
||
<span class="stat-value">₹{{ number_format($totalPayable, 0) }}</span>
|
||
<span class="stat-label">Total Payable</span>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
|
||
{{-- Row 2 --}}
|
||
<tr>
|
||
{{-- Total Paid --}}
|
||
<td>
|
||
<div class="stat-card-wrap">
|
||
<table class="stat-inner">
|
||
<tr>
|
||
<td class="stat-strip strip-success"> </td>
|
||
<td class="stat-content bg-success">
|
||
<span class="stat-icon-box icon-success">
|
||
<i class="fas fa-check-circle"></i>
|
||
</span>
|
||
<span class="stat-text">
|
||
<span class="stat-value">₹{{ number_format($totalPaid, 0) }}</span>
|
||
<span class="stat-label">Total Paid</span>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</td>
|
||
|
||
{{-- Total Remaining --}}
|
||
<td>
|
||
<div class="stat-card-wrap">
|
||
<table class="stat-inner">
|
||
<tr>
|
||
<td class="stat-strip strip-danger"> </td>
|
||
<td class="stat-content bg-danger">
|
||
<span class="stat-icon-box icon-danger">
|
||
<i class="fas fa-hourglass-half"></i>
|
||
</span>
|
||
<span class="stat-text">
|
||
<span class="stat-value">₹{{ number_format($totalRemaining, 0) }}</span>
|
||
<span class="stat-label">Total Remaining</span>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</td>
|
||
|
||
{{-- Overdue Containers --}}
|
||
<td>
|
||
<div class="stat-card-wrap">
|
||
<table class="stat-inner">
|
||
<tr>
|
||
<td class="stat-strip strip-warning"> </td>
|
||
<td class="stat-content bg-warning">
|
||
<span class="stat-icon-box icon-warning">
|
||
<i class="fas fa-exclamation-triangle"></i>
|
||
</span>
|
||
<span class="stat-text">
|
||
<span class="stat-value">{{ $overdueCount }}</span>
|
||
<span class="stat-label">Overdue Containers</span>
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
|
||
{{-- TABLE --}}
|
||
<div class="table-container">
|
||
<table class="report-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Sr.</th>
|
||
<th>Container No</th>
|
||
<th>Container Date</th>
|
||
<th>Total Mark Nos</th>
|
||
<th>Total Customers</th>
|
||
<th>Total Invoices</th>
|
||
<th>Invoice Amount<br>(Before GST)</th>
|
||
<th>GST Amount</th>
|
||
<th>Payable Amount<br>(Incl. GST)</th>
|
||
<th>Paid Amount</th>
|
||
<th>Remaining Amount</th>
|
||
<th>Container Status</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@forelse($reports as $i => $r)
|
||
@php $cs = strtolower($r->container_status); @endphp
|
||
<tr>
|
||
<td style="color:#9ca3af;font-weight:600;">{{ $i + 1 }}</td>
|
||
<td style="font-weight:700;color:#1e40af;">{{ $r->container_number }}</td>
|
||
<td>
|
||
{{ $r->container_date ? \Carbon\Carbon::parse($r->container_date)->format('d-m-Y') : '-' }}
|
||
</td>
|
||
<td><span class="count-badge">{{ $r->total_mark_nos }}</span></td>
|
||
<td><span class="count-badge">{{ $r->total_customers }}</span></td>
|
||
<td>
|
||
<span class="count-badge" style="background:#f3e8ff;color:#6d28d9;border-color:#ddd6fe;">
|
||
{{ $r->total_invoices }}
|
||
</span>
|
||
</td>
|
||
<td class="amt-cell amt-base">₹{{ number_format($r->total_invoice_amount, 2) }}</td>
|
||
<td class="amt-cell amt-gst">₹{{ number_format($r->total_gst_amount, 2) }}</td>
|
||
<td class="amt-cell amt-payable">₹{{ number_format($r->total_payable, 2) }}</td>
|
||
<td class="amt-cell amt-paid">₹{{ number_format($r->total_paid, 2) }}</td>
|
||
<td class="amt-cell amt-remaining">₹{{ number_format($r->total_remaining, 2) }}</td>
|
||
<td>
|
||
<span class="status-badge
|
||
@if($cs === 'paid') status-paid
|
||
@elseif($cs === 'overdue') status-overdue
|
||
@elseif($cs === 'paying') status-paying
|
||
@else status-pending
|
||
@endif">
|
||
{{ ucfirst($cs) }}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
@empty
|
||
<tr>
|
||
<td colspan="12">
|
||
<div class="empty-state">
|
||
<h3>No Container Reports Found</h3>
|
||
<p>No containers with invoices found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
|
||
@else
|
||
{{-- ========== NORMAL UI MODE (Browser साठी) ========== --}}
|
||
@extends('admin.layouts.app')
|
||
|
||
@section('page-title', 'Container Report')
|
||
|
||
@section('content')
|
||
<style>
|
||
:root { --admin-font: "Inter", sans-serif; }
|
||
|
||
html, body { overflow-x: hidden !important; }
|
||
|
||
body, input, select, textarea, button, table, th, td {
|
||
font-family: var(--admin-font);
|
||
}
|
||
|
||
.report-container {
|
||
background: #ffffff;
|
||
padding: 25px;
|
||
border-radius: 16px;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.08);
|
||
margin: 20px auto;
|
||
max-width: 100%;
|
||
border: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.report-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18px;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 20px;
|
||
border-bottom: 2px solid #f8f9fa;
|
||
}
|
||
|
||
.header-icon {
|
||
width: 55px; height: 55px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 14px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
box-shadow: 0 6px 20px rgba(102,126,234,0.3);
|
||
}
|
||
|
||
.header-icon i { font-size: 24px; color: white; }
|
||
|
||
.report-title { font-size: 24px; font-weight: 700; color: #1a202c; margin-bottom: 4px; }
|
||
.report-subtitle { font-size: 14px; color: #718096; font-weight: 500; }
|
||
|
||
.stats-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: 12px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.stat-card {
|
||
padding: 14px 16px;
|
||
border-radius: 12px;
|
||
border-left: 4px solid #4f46e5;
|
||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f4ff 100%);
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||
display: flex; align-items: center; gap: 12px;
|
||
}
|
||
|
||
.stat-card.success { border-left-color: #10b981; background: linear-gradient(135deg,#ecfdf5,#d1fae5); }
|
||
.stat-card.warning { border-left-color: #f59e0b; background: linear-gradient(135deg,#fffbeb,#fef3c7); }
|
||
.stat-card.danger { border-left-color: #ef4444; background: linear-gradient(135deg,#fef2f2,#fee2e2); }
|
||
.stat-card.info { border-left-color: #3b82f6; background: linear-gradient(135deg,#eff6ff,#dbeafe); }
|
||
.stat-card.purple { border-left-color: #8b5cf6; background: linear-gradient(135deg,#faf5ff,#f3e8ff); }
|
||
.stat-card.teal { border-left-color: #14b8a6; background: linear-gradient(135deg,#f0fdfa,#ccfbf1); }
|
||
|
||
.stat-icon {
|
||
width: 42px; height: 42px; border-radius: 10px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: rgba(79,70,229,0.1); flex-shrink: 0;
|
||
}
|
||
.stat-icon i { font-size: 17px; color: #4f46e5; }
|
||
.stat-card.success .stat-icon { background: rgba(16,185,129,0.1); }
|
||
.stat-card.success .stat-icon i { color: #10b981; }
|
||
.stat-card.warning .stat-icon { background: rgba(245,158,11,0.1); }
|
||
.stat-card.warning .stat-icon i { color: #f59e0b; }
|
||
.stat-card.danger .stat-icon { background: rgba(239,68,68,0.1); }
|
||
.stat-card.danger .stat-icon i { color: #ef4444; }
|
||
.stat-card.info .stat-icon { background: rgba(59,130,246,0.1); }
|
||
.stat-card.info .stat-icon i { color: #3b82f6; }
|
||
.stat-card.purple .stat-icon { background: rgba(139,92,246,0.1); }
|
||
.stat-card.purple .stat-icon i { color: #8b5cf6; }
|
||
.stat-card.teal .stat-icon { background: rgba(20,184,166,0.1); }
|
||
.stat-card.teal .stat-icon i { color: #14b8a6; }
|
||
|
||
.stat-value { font-size: 20px; font-weight: 700; color: #1a202c; line-height: 1.2; }
|
||
.stat-label { font-size: 11px; color: #718096; font-weight: 500; }
|
||
|
||
.filter-section {
|
||
background: #f8fafc; border-radius: 12px;
|
||
padding: 18px 20px; margin-bottom: 20px;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.filter-row { display: flex; gap: 15px; flex-wrap: wrap; align-items: flex-end; }
|
||
|
||
.filter-group { display: flex; flex-direction: column; gap: 6px; min-width: 170px; flex: 1; }
|
||
|
||
.filter-label {
|
||
font-size: 12px; font-weight: 600; color: #374151;
|
||
display: flex; align-items: center; gap: 5px;
|
||
}
|
||
|
||
.filter-control {
|
||
padding: 9px 11px; border-radius: 8px;
|
||
border: 1.5px solid #e5e7eb; background: #fff;
|
||
font-size: 13px; color: #1f2937;
|
||
transition: all 0.2s;
|
||
}
|
||
.filter-control:focus { outline: none; border-color: #4f46e5; box-shadow: 0 0 0 3px rgba(79,70,229,0.12); }
|
||
|
||
.table-container {
|
||
overflow-x: auto;
|
||
border-radius: 12px;
|
||
border: 1px solid #e5e7eb;
|
||
background: white;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||
margin-top: 10px;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.report-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
min-width: 1400px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.report-table thead {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
position: sticky; top: 0; z-index: 10;
|
||
}
|
||
|
||
.report-table th {
|
||
padding: 14px 16px;
|
||
text-align: center;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
white-space: nowrap;
|
||
min-width: 110px;
|
||
}
|
||
|
||
.report-table td {
|
||
padding: 12px 16px;
|
||
font-size: 13px;
|
||
color: #374151;
|
||
border-bottom: 1px solid #f3f4f6;
|
||
vertical-align: middle;
|
||
white-space: nowrap;
|
||
text-align: center;
|
||
}
|
||
|
||
.report-table tbody tr:hover { background: #f8faff; }
|
||
|
||
.amt-cell { font-weight: 600; font-family: 'JetBrains Mono', monospace; }
|
||
.amt-payable { color: #1e40af; }
|
||
.amt-paid { color: #059669; }
|
||
.amt-remaining{ color: #dc2626; }
|
||
.amt-gst { color: #d97706; }
|
||
.amt-base { color: #374151; }
|
||
|
||
.count-badge {
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
background: #eff6ff; color: #1d4ed8;
|
||
font-weight: 700; font-size: 13px;
|
||
padding: 3px 10px; border-radius: 20px;
|
||
border: 1px solid #bfdbfe;
|
||
min-width: 32px;
|
||
}
|
||
|
||
.status-badge {
|
||
display: inline-flex; align-items: center; gap: 5px;
|
||
font-size: 11px; font-weight: 700;
|
||
padding: 5px 12px; border-radius: 20px;
|
||
text-transform: uppercase; letter-spacing: 0.4px;
|
||
white-space: nowrap;
|
||
}
|
||
.status-paid { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0; }
|
||
.status-pending { background: #fef3c7; color: #92400e; border: 1px solid #fde68a; }
|
||
.status-overdue { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }
|
||
.status-paying { background: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe; }
|
||
|
||
.empty-state { text-align: center; padding: 50px 20px; color: #6b7280; }
|
||
.empty-icon {
|
||
width: 60px; height: 60px; background: #f8f9fa;
|
||
border-radius: 50%; display: flex; align-items: center;
|
||
justify-content: center; margin: 0 auto 15px;
|
||
border: 2px dashed #e5e7eb;
|
||
}
|
||
.empty-icon i { font-size: 24px; color: #9ca3af; }
|
||
|
||
.pagination-container {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
margin-top: 15px; padding: 12px 0; border-top: 1px solid #eef3fb;
|
||
}
|
||
.pagination-info { font-size: 13px; color: #9ba5bb; font-weight: 600; }
|
||
.pagination-controls { display: flex; align-items: center; gap: 8px; }
|
||
.pagination-img-btn {
|
||
background: #fff; border: 1px solid #e3eaf6;
|
||
border-radius: 6px; cursor: pointer;
|
||
display: flex; align-items: center; justify-content: center;
|
||
min-width: 36px; height: 32px;
|
||
}
|
||
.pagination-img-btn[disabled] { opacity: 0.4; cursor: not-allowed; }
|
||
.pagination-page-btn {
|
||
background: #fff; border: 1px solid #e3eaf6; color: #1a2951;
|
||
padding: 5px 11px; border-radius: 6px; font-size: 13px;
|
||
font-weight: 600; cursor: pointer; min-width: 34px;
|
||
}
|
||
.pagination-page-btn.active { background: #1a2951; color: #fff; border-color: #1a2951; }
|
||
.pagination-pages { display: flex; gap: 4px; align-items: center; }
|
||
|
||
@media (max-width: 768px) {
|
||
.report-container { padding: 15px; }
|
||
.report-table { min-width: 1400px; }
|
||
}
|
||
</style>
|
||
|
||
<div class="report-container">
|
||
|
||
{{-- HEADER --}}
|
||
<div class="report-header">
|
||
<div class="header-icon">
|
||
<i class="fas fa-boxes"></i>
|
||
</div>
|
||
<div>
|
||
<h1 class="report-title">Container Report</h1>
|
||
<p class="report-subtitle">Container-wise invoice summary — amounts, payments & status</p>
|
||
</div>
|
||
|
||
<div class="ms-auto d-flex gap-2">
|
||
<a href="{{ route('admin.reports.containers.excel') }}"
|
||
id="excelBtn"
|
||
class="btn btn-sm btn-success"
|
||
style="border-radius:8px;font-weight:600;">
|
||
<i class="fas fa-file-excel me-1"></i> Excel
|
||
</a>
|
||
<a href="{{ route('admin.reports.containers.pdf') }}"
|
||
id="pdfBtn"
|
||
class="btn btn-sm btn-danger"
|
||
style="border-radius:8px;font-weight:600;">
|
||
<i class="fas fa-file-pdf me-1"></i> PDF
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- STATS CARDS --}}
|
||
<div class="stats-container">
|
||
<div class="stat-card info">
|
||
<div class="stat-icon"><i class="fas fa-box"></i></div>
|
||
<div>
|
||
<div class="stat-value" id="statTotalContainers">{{ count($reports) }}</div>
|
||
<div class="stat-label">Total Containers</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card purple">
|
||
<div class="stat-icon"><i class="fas fa-file-invoice"></i></div>
|
||
<div>
|
||
<div class="stat-value" id="statTotalInvoices">{{ $reports->sum('total_invoices') }}</div>
|
||
<div class="stat-label">Total Invoices</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon"><i class="fas fa-rupee-sign"></i></div>
|
||
<div>
|
||
<div class="stat-value" id="statTotalPayable">
|
||
₹{{ number_format($reports->sum('total_payable'), 0) }}
|
||
</div>
|
||
<div class="stat-label">Total Payable</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card success">
|
||
<div class="stat-icon"><i class="fas fa-check-circle"></i></div>
|
||
<div>
|
||
<div class="stat-value" id="statTotalPaid">
|
||
₹{{ number_format($reports->sum('total_paid'), 0) }}
|
||
</div>
|
||
<div class="stat-label">Total Paid</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card danger">
|
||
<div class="stat-icon"><i class="fas fa-hourglass-half"></i></div>
|
||
<div>
|
||
<div class="stat-value" id="statTotalRemaining">
|
||
₹{{ number_format($reports->sum('total_remaining'), 0) }}
|
||
</div>
|
||
<div class="stat-label">Total Remaining</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card warning">
|
||
<div class="stat-icon"><i class="fas fa-exclamation-triangle"></i></div>
|
||
<div>
|
||
<div class="stat-value" id="statOverdue">
|
||
{{ $reports->where('container_status', 'overdue')->count() }}
|
||
</div>
|
||
<div class="stat-label">Overdue Containers</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- FILTERS --}}
|
||
<div class="filter-section">
|
||
<div class="filter-row">
|
||
<div class="filter-group">
|
||
<label class="filter-label"><i class="fas fa-calendar-alt"></i> From Date</label>
|
||
<input type="date" class="filter-control" id="fromDate">
|
||
</div>
|
||
<div class="filter-group">
|
||
<label class="filter-label"><i class="fas fa-calendar-check"></i> To Date</label>
|
||
<input type="date" class="filter-control" id="toDate">
|
||
</div>
|
||
<div class="filter-group">
|
||
<label class="filter-label"><i class="fas fa-flag"></i> Status</label>
|
||
<select class="filter-control" id="statusFilter">
|
||
<option value="">All Status</option>
|
||
<option value="paid">Paid</option>
|
||
<option value="paying">Paying</option>
|
||
<option value="pending">Pending</option>
|
||
<option value="overdue">Overdue</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- TABLE --}}
|
||
<div class="table-container">
|
||
<table class="report-table">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
<th>Container No</th>
|
||
<th>Container Date</th>
|
||
<th>Total Mark Nos</th>
|
||
<th>Total Customers</th>
|
||
<th>Total Invoices</th>
|
||
<th>Invoice Amount<br><small style="font-weight:400;opacity:.8;">(Before GST)</small></th>
|
||
<th>GST Amount</th>
|
||
<th>Payable Amount<br><small style="font-weight:400;opacity:.8;">(Incl. GST)</small></th>
|
||
<th>Paid Amount</th>
|
||
<th>Remaining Amount</th>
|
||
<th>Container Status</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="reportTableBody">
|
||
@forelse($reports as $i => $r)
|
||
@php $cs = strtolower($r->container_status); @endphp
|
||
<tr data-status="{{ strtolower($r->container_status) }}"
|
||
data-date="{{ $r->container_date }}">
|
||
<td class="text-muted fw-600">{{ $i + 1 }}</td>
|
||
<td style="font-weight:700;color:#1e40af;">{{ $r->container_number }}</td>
|
||
<td>{{ $r->container_date ? \Carbon\Carbon::parse($r->container_date)->format('d-m-Y') : '-' }}</td>
|
||
<td><span class="count-badge">{{ $r->total_mark_nos }}</span></td>
|
||
<td><span class="count-badge">{{ $r->total_customers }}</span></td>
|
||
<td><span class="count-badge" style="background:#f3e8ff;color:#6d28d9;border-color:#ddd6fe;">{{ $r->total_invoices }}</span></td>
|
||
<td class="amt-cell amt-base">₹{{ number_format($r->total_invoice_amount, 2) }}</td>
|
||
<td class="amt-cell amt-gst">₹{{ number_format($r->total_gst_amount, 2) }}</td>
|
||
<td class="amt-cell amt-payable">₹{{ number_format($r->total_payable, 2) }}</td>
|
||
<td class="amt-cell amt-paid">₹{{ number_format($r->total_paid, 2) }}</td>
|
||
<td class="amt-cell amt-remaining">₹{{ number_format($r->total_remaining, 2) }}</td>
|
||
<td>
|
||
<span class="status-badge
|
||
@if($cs === 'paid') status-paid
|
||
@elseif($cs === 'overdue') status-overdue
|
||
@elseif($cs === 'paying') status-paying
|
||
@else status-pending
|
||
@endif">
|
||
@if($cs === 'paid') <i class="fas fa-check-circle"></i>
|
||
@elseif($cs === 'overdue') <i class="fas fa-exclamation-circle"></i>
|
||
@elseif($cs === 'paying') <i class="fas fa-spinner"></i>
|
||
@else <i class="fas fa-clock"></i>
|
||
@endif
|
||
{{ ucfirst($cs) }}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
@empty
|
||
<tr>
|
||
<td colspan="12">
|
||
<div class="empty-state">
|
||
<div class="empty-icon"><i class="fas fa-inbox"></i></div>
|
||
<h3>No Container Reports Found</h3>
|
||
<p>No containers with invoices found.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
{{-- PAGINATION --}}
|
||
<div class="pagination-container">
|
||
<div class="pagination-info" id="pageInfo">
|
||
Showing 1–{{ min(15, count($reports)) }} of {{ count($reports) }} entries
|
||
</div>
|
||
<div class="pagination-controls">
|
||
<button class="pagination-img-btn" id="prevPageBtn" disabled>
|
||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2"
|
||
stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<div class="pagination-pages" id="paginationPages"></div>
|
||
<button class="pagination-img-btn" id="nextPageBtn"
|
||
{{ count($reports) <= 15 ? 'disabled' : '' }}>
|
||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2"
|
||
stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
const allReports = @json($reports);
|
||
const ITEMS_PER_PAGE = 15;
|
||
let filteredReports = [...allReports];
|
||
let currentPage = 1;
|
||
|
||
const pdfBaseUrl = "{{ route('admin.reports.containers.pdf') }}";
|
||
const excelBaseUrl = "{{ route('admin.reports.containers.excel') }}";
|
||
|
||
const fmt = (n) => Number(n || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
const fmtDate = (d) => d ? new Date(d).toLocaleDateString('en-GB') : '-';
|
||
|
||
// ── Filter values ──────────────────────────────────────────────
|
||
function getFilterValues() {
|
||
return {
|
||
from_date : document.getElementById('fromDate').value || '',
|
||
to_date : document.getElementById('toDate').value || '',
|
||
status : document.getElementById('statusFilter').value || '',
|
||
};
|
||
}
|
||
|
||
// ── Build URL with current filter params ───────────────────────
|
||
function buildUrl(base) {
|
||
const f = getFilterValues();
|
||
const params = new URLSearchParams();
|
||
if (f.from_date) params.set('from_date', f.from_date);
|
||
if (f.to_date) params.set('to_date', f.to_date);
|
||
if (f.status) params.set('status', f.status);
|
||
const qs = params.toString();
|
||
return qs ? base + '?' + qs : base;
|
||
}
|
||
|
||
// ── Update PDF & Excel button hrefs whenever filter changes ────
|
||
function updateDownloadLinks() {
|
||
document.getElementById('pdfBtn').href = buildUrl(pdfBaseUrl);
|
||
document.getElementById('excelBtn').href = buildUrl(excelBaseUrl);
|
||
}
|
||
|
||
// ── Status badge HTML ──────────────────────────────────────────
|
||
function statusBadgeHtml(status) {
|
||
const s = (status || '').toLowerCase();
|
||
const map = {
|
||
paid: { cls: 'status-paid', icon: 'fa-check-circle', label: 'Paid' },
|
||
overdue: { cls: 'status-overdue', icon: 'fa-exclamation-circle', label: 'Overdue' },
|
||
paying: { cls: 'status-paying', icon: 'fa-spinner', label: 'Paying' },
|
||
pending: { cls: 'status-pending', icon: 'fa-clock', label: 'Pending' },
|
||
};
|
||
const c = map[s] || map.pending;
|
||
return `<span class="status-badge ${c.cls}"><i class="fas ${c.icon}"></i> ${c.label}</span>`;
|
||
}
|
||
|
||
// ── Render table ───────────────────────────────────────────────
|
||
function renderTable() {
|
||
const tbody = document.getElementById('reportTableBody');
|
||
if (!tbody) return;
|
||
|
||
tbody.innerHTML = '';
|
||
|
||
if (filteredReports.length === 0) {
|
||
tbody.innerHTML = `<tr><td colspan="12">
|
||
<div class="empty-state">
|
||
<div class="empty-icon"><i class="fas fa-inbox"></i></div>
|
||
<h3>No Container Reports Found</h3>
|
||
<p>No containers match the selected filters.</p>
|
||
</div></td></tr>`;
|
||
updateStats();
|
||
return;
|
||
}
|
||
|
||
const start = (currentPage - 1) * ITEMS_PER_PAGE;
|
||
const slice = filteredReports.slice(start, start + ITEMS_PER_PAGE);
|
||
|
||
slice.forEach((r, idx) => {
|
||
const tr = document.createElement('tr');
|
||
tr.setAttribute('data-status', (r.container_status || '').toLowerCase());
|
||
tr.setAttribute('data-date', r.container_date || '');
|
||
|
||
tr.innerHTML = `
|
||
<td class="text-muted fw-600">${start + idx + 1}</td>
|
||
<td style="font-weight:700;color:#1e40af;">${r.container_number || '-'}</td>
|
||
<td>${fmtDate(r.container_date)}</td>
|
||
<td><span class="count-badge">${r.total_mark_nos || 0}</span></td>
|
||
<td><span class="count-badge">${r.total_customers || 0}</span></td>
|
||
<td><span class="count-badge" style="background:#f3e8ff;color:#6d28d9;border-color:#ddd6fe;">${r.total_invoices || 0}</span></td>
|
||
<td class="amt-cell amt-base">₹${fmt(r.total_invoice_amount)}</td>
|
||
<td class="amt-cell amt-gst">₹${fmt(r.total_gst_amount)}</td>
|
||
<td class="amt-cell amt-payable">₹${fmt(r.total_payable)}</td>
|
||
<td class="amt-cell amt-paid">₹${fmt(r.total_paid)}</td>
|
||
<td class="amt-cell amt-remaining">₹${fmt(r.total_remaining)}</td>
|
||
<td>${statusBadgeHtml(r.container_status)}</td>
|
||
`;
|
||
tbody.appendChild(tr);
|
||
});
|
||
|
||
updateStats();
|
||
renderPagination();
|
||
}
|
||
|
||
// ── Update stat cards ──────────────────────────────────────────
|
||
function updateStats() {
|
||
document.getElementById('statTotalContainers').textContent = filteredReports.length;
|
||
document.getElementById('statTotalInvoices').textContent = filteredReports.reduce((s,r) => s + Number(r.total_invoices||0), 0);
|
||
|
||
const tp = filteredReports.reduce((s,r) => s + Number(r.total_payable||0), 0);
|
||
const tpd = filteredReports.reduce((s,r) => s + Number(r.total_paid||0), 0);
|
||
const tr = filteredReports.reduce((s,r) => s + Number(r.total_remaining||0), 0);
|
||
|
||
document.getElementById('statTotalPayable').textContent = '₹' + Math.round(tp).toLocaleString('en-IN');
|
||
document.getElementById('statTotalPaid').textContent = '₹' + Math.round(tpd).toLocaleString('en-IN');
|
||
document.getElementById('statTotalRemaining').textContent = '₹' + Math.round(tr).toLocaleString('en-IN');
|
||
|
||
const overdue = filteredReports.filter(r => (r.container_status||'').toLowerCase() === 'overdue').length;
|
||
document.getElementById('statOverdue').textContent = overdue;
|
||
}
|
||
|
||
// ── Pagination ─────────────────────────────────────────────────
|
||
function renderPagination() {
|
||
const total = filteredReports.length;
|
||
const totalPages = Math.ceil(total / ITEMS_PER_PAGE);
|
||
const start = ((currentPage - 1) * ITEMS_PER_PAGE) + 1;
|
||
const end = Math.min(currentPage * ITEMS_PER_PAGE, total);
|
||
|
||
document.getElementById('pageInfo').textContent = `Showing ${start}–${end} of ${total} entries`;
|
||
|
||
const prevBtn = document.getElementById('prevPageBtn');
|
||
const nextBtn = document.getElementById('nextPageBtn');
|
||
prevBtn.disabled = (currentPage === 1);
|
||
nextBtn.disabled = (currentPage === totalPages || totalPages === 0);
|
||
|
||
const pagesDiv = document.getElementById('paginationPages');
|
||
pagesDiv.innerHTML = '';
|
||
|
||
if (totalPages <= 1) return;
|
||
|
||
const range = 2;
|
||
const showPages = [];
|
||
|
||
if (totalPages <= 7) {
|
||
for (let i = 1; i <= totalPages; i++) showPages.push(i);
|
||
} else {
|
||
showPages.push(1);
|
||
if (currentPage > range + 2) showPages.push('...');
|
||
for (let i = Math.max(2, currentPage - range); i <= Math.min(totalPages - 1, currentPage + range); i++) {
|
||
showPages.push(i);
|
||
}
|
||
if (currentPage < totalPages - range - 1) showPages.push('...');
|
||
showPages.push(totalPages);
|
||
}
|
||
|
||
showPages.forEach(p => {
|
||
if (p === '...') {
|
||
const span = document.createElement('span');
|
||
span.textContent = '...';
|
||
span.style.padding = '0 4px';
|
||
span.style.color = '#9ca3af';
|
||
pagesDiv.appendChild(span);
|
||
} else {
|
||
const btn = document.createElement('button');
|
||
btn.className = 'pagination-page-btn' + (p === currentPage ? ' active' : '');
|
||
btn.textContent = p;
|
||
btn.addEventListener('click', () => { currentPage = p; renderTable(); });
|
||
pagesDiv.appendChild(btn);
|
||
}
|
||
});
|
||
}
|
||
|
||
document.getElementById('prevPageBtn')?.addEventListener('click', () => {
|
||
if (currentPage > 1) { currentPage--; renderTable(); }
|
||
});
|
||
|
||
document.getElementById('nextPageBtn')?.addEventListener('click', () => {
|
||
const totalPages = Math.ceil(filteredReports.length / ITEMS_PER_PAGE);
|
||
if (currentPage < totalPages) { currentPage++; renderTable(); }
|
||
});
|
||
|
||
// ── Apply filters ──────────────────────────────────────────────
|
||
function applyFilters() {
|
||
const f = getFilterValues();
|
||
|
||
filteredReports = allReports.filter(r => {
|
||
if (f.from_date && r.container_date && r.container_date < f.from_date) return false;
|
||
if (f.to_date && r.container_date && r.container_date > f.to_date) return false;
|
||
if (f.status && (r.container_status || '').toLowerCase() !== f.status) return false;
|
||
return true;
|
||
});
|
||
|
||
currentPage = 1;
|
||
renderTable();
|
||
updateDownloadLinks(); // ← PDF/Excel URL filter सकट update होतो
|
||
}
|
||
|
||
document.getElementById('fromDate')?.addEventListener('change', applyFilters);
|
||
document.getElementById('toDate')?.addEventListener('change', applyFilters);
|
||
document.getElementById('statusFilter')?.addEventListener('change', applyFilters);
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
renderTable();
|
||
updateDownloadLinks();
|
||
});
|
||
</script>
|
||
@endsection
|
||
@endif |