Files
Kent-logistics-Laravel/resources/views/admin/popup_invoice.blade.php
2026-02-27 10:51:26 +05:30

1370 lines
57 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Professional Invoice</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--primary: #0f172a;
--secondary: #3b82f6;
--accent: #f59e0b;
--success: #10b981;
--danger: #ef4444;
--warning: #f59e0b;
--light: #f8fafc;
--surface: #ffffff;
--surface2: #f1f5f9;
--border: #e2e8f0;
--text-muted: #64748b;
--text-secondary: #475569;
--radius: 12px;
--radius-sm: 8px;
--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 4px 16px rgba(0,0,0,0.06);
--shadow-lg: 0 8px 32px rgba(0,0,0,0.1);
}
* { box-sizing: border-box; }
body {
font-family: 'Plus Jakarta Sans', sans-serif;
background: linear-gradient(135deg, #e0f2fe 0%, #f0fdf4 50%, #fef9c3 100%);
min-height: 100vh;
color: var(--primary);
padding: 2rem 1rem;
}
/* ── CONTAINER ── */
.invoice-container {
max-width: 1400px;
margin: 0 auto;
background: var(--surface);
border-radius: 20px;
box-shadow: var(--shadow-lg);
overflow: hidden;
border: 1px solid var(--border);
}
/* ── TOP ACCENT BAR ── */
.invoice-accent-bar {
height: 5px;
background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 40%, #10b981 70%, #f59e0b 100%);
}
/* ── HEADER ── */
.invoice-header {
padding: 2rem 2.5rem 1.5rem;
background: var(--surface);
border-bottom: 1px solid var(--border);
position: relative;
}
.invoice-title {
font-size: 2rem;
font-weight: 800;
letter-spacing: -0.03em;
color: var(--primary);
line-height: 1;
margin-bottom: 0.25rem;
}
.invoice-number {
font-family: 'JetBrains Mono', monospace;
font-size: 0.9rem;
font-weight: 600;
color: var(--text-muted);
letter-spacing: 0.04em;
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.8rem;
font-weight: 700;
padding: 0.5rem 1.1rem;
border-radius: 50px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.status-paid { background: #dcfce7; color: #166534; }
.status-overdue { background: #fee2e2; color: #991b1b; }
.status-pending { background: #fef3c7; color: #92400e; }
.status-default { background: #f1f5f9; color: #475569; }
/* ── ID BOXES ── */
.id-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
padding: 1.5rem 2.5rem;
background: var(--surface2);
border-bottom: 1px solid var(--border);
}
.id-box {
background: var(--surface);
border-radius: var(--radius);
padding: 1.1rem 1.25rem;
border: 1px solid var(--border);
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
display: flex;
align-items: center;
gap: 1rem;
transition: box-shadow 0.2s;
}
.id-box:hover { box-shadow: var(--shadow); }
.id-icon-wrap {
width: 42px;
height: 42px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
flex-shrink: 0;
}
.id-icon-blue { background: #eff6ff; color: #3b82f6; }
.id-icon-green { background: #f0fdf4; color: #10b981; }
.id-label {
font-size: 0.68rem;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 0.2rem;
}
.id-value {
font-family: 'JetBrains Mono', monospace;
font-size: 0.92rem;
font-weight: 600;
color: var(--primary);
}
/* ── DATES ── */
.date-strip {
padding: 1.5rem 2.5rem;
border-bottom: 1px solid var(--border);
}
.date-row {
display: flex;
align-items: center;
gap: 1rem;
}
.date-card {
flex: 1;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1rem 1.25rem;
display: flex;
align-items: center;
gap: 0.85rem;
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
}
.date-icon-wrap {
width: 38px;
height: 38px;
border-radius: 9px;
background: #eff6ff;
color: #3b82f6;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
flex-shrink: 0;
}
.date-label {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--text-muted);
margin-bottom: 0.2rem;
}
.date-value {
font-weight: 700;
font-size: 0.95rem;
color: var(--primary);
}
.date-value.overdue { color: #ef4444; }
.date-arrow {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--surface2);
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
font-size: 0.8rem;
flex-shrink: 0;
}
/* ── SECTION PANEL ── */
.panel {
margin: 1.5rem 2.5rem;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
}
.panel-header {
background: var(--surface2);
padding: 0.85rem 1.25rem;
border-bottom: 1px solid var(--border);
font-weight: 700;
font-size: 0.88rem;
color: var(--primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.panel-header i {
color: var(--secondary);
font-size: 0.85rem;
}
.panel-body {
padding: 1.25rem;
background: var(--surface);
}
/* ── CUSTOMER ── */
.customer-name {
font-size: 1.05rem;
font-weight: 700;
color: var(--secondary);
margin-bottom: 0.4rem;
}
.customer-detail {
font-size: 0.85rem;
color: var(--text-secondary);
margin-bottom: 0.3rem;
display: flex;
gap: 0.4rem;
}
.customer-detail strong { color: var(--primary); font-weight: 600; }
/* ── TABLE ── */
.invoice-table {
width: 100%;
min-width: 1100px;
border-collapse: collapse;
font-size: 0.83rem;
}
/* Items table specifically even wider */
.items-table {
min-width: 1300px;
}
.invoice-table thead tr {
background: var(--surface2);
}
.invoice-table thead th {
padding: 0.85rem 1rem;
font-size: 0.71rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-muted);
border-bottom: 2px solid var(--border);
white-space: nowrap;
}
.invoice-table tbody tr {
border-bottom: 1px solid var(--border);
transition: background 0.15s;
}
.invoice-table tbody tr:hover { background: #f0f7ff; }
.invoice-table tbody tr:last-child { border-bottom: none; }
.invoice-table tbody td {
padding: 0.85rem 1rem;
color: var(--text-secondary);
white-space: nowrap;
}
/* Description column can wrap */
.invoice-table tbody td.desc-col {
white-space: normal;
min-width: 180px;
}
.invoice-table tbody tr.grouped-item-row {
background: #f8f9ff;
}
.badge-shop {
display: inline-block;
padding: 0.2rem 0.55rem;
border-radius: 5px;
background: var(--surface2);
border: 1px solid var(--border);
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
font-weight: 600;
color: var(--text-secondary);
}
.price-green { color: #10b981; font-weight: 700; }
.price-blue { color: #3b82f6; font-weight: 700; }
/* ── ACTION BAR ── */
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1.25rem;
border-top: 1px solid var(--border);
background: var(--surface2);
}
.action-bar small { color: var(--text-muted); font-size: 0.8rem; }
.action-bar span { font-weight: 700; color: var(--secondary); }
/* ── BUTTONS ── */
.btn-create-group {
display: inline-flex;
align-items: center;
gap: 0.4rem;
background: var(--secondary);
color: white;
border: none;
border-radius: 8px;
padding: 0.45rem 1rem;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s, opacity 0.2s;
}
.btn-create-group:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-create-group:not(:disabled):hover { background: #2563eb; }
/* ── CHARGE GROUP FORM PANEL ── */
.cg-panel {
margin: 0 2.5rem 1.5rem;
border: 2px solid #3b82f6;
border-radius: var(--radius);
overflow: hidden;
box-shadow: 0 0 0 4px rgba(59,130,246,0.08);
}
.cg-panel-header {
background: linear-gradient(135deg, #1d4ed8, #3b82f6);
padding: 0.9rem 1.25rem;
color: white;
font-weight: 700;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.cg-body { padding: 1.25rem; background: var(--surface); }
.form-label-custom {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--text-muted);
margin-bottom: 0.35rem;
display: block;
}
.form-control-custom,
.form-select-custom {
width: 100%;
padding: 0.55rem 0.85rem;
border: 1.5px solid var(--border);
border-radius: 8px;
font-size: 0.85rem;
font-family: 'Plus Jakarta Sans', sans-serif;
color: var(--primary);
background: var(--surface);
transition: border-color 0.15s, box-shadow 0.15s;
outline: none;
}
.form-control-custom:focus,
.form-select-custom:focus {
border-color: var(--secondary);
box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
}
.basis-box {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 9px;
padding: 1rem;
height: 100%;
}
.basis-box-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--text-muted);
margin-bottom: 0.5rem;
}
.basis-value-display {
font-family: 'JetBrains Mono', monospace;
font-size: 1.2rem;
font-weight: 700;
color: var(--secondary);
}
.basis-label-display {
font-size: 0.75rem;
color: var(--text-muted);
margin-left: 0.5rem;
}
.basis-hint { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.35rem; }
.suggested-total {
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
color: #10b981;
}
/* ── CHARGE GROUPS TABLE ── */
.cg-groups-panel {
margin: 0 2.5rem 1.5rem;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
.cg-groups-header {
background: var(--primary);
color: white;
padding: 0.85rem 1.25rem;
font-weight: 700;
font-size: 0.88rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
/* ── SUMMARY ── */
.summary-wrap {
padding: 0 2.5rem 2rem;
}
.summary-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow);
}
.summary-header-row {
background: var(--primary);
color: white;
padding: 0.85rem 1.25rem;
font-weight: 700;
font-size: 0.88rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.summary-body { padding: 1.25rem; }
.summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.6rem 0;
border-bottom: 1px solid var(--border);
font-size: 0.88rem;
}
.summary-row:last-child { border-bottom: none; padding-top: 0.85rem; }
.summary-row .label { color: var(--text-secondary); font-weight: 500; }
.summary-row .value { font-weight: 700; color: var(--primary); }
.summary-row.total .label { font-size: 1rem; font-weight: 700; color: var(--primary); }
.summary-row.total .value { font-size: 1.15rem; color: #10b981; }
.summary-row .value.red { color: #ef4444; }
/* ── FOOTER ── */
.invoice-footer {
padding: 1.5rem 2.5rem;
border-top: 1px solid var(--border);
text-align: center;
background: var(--surface2);
}
.btn-download {
display: inline-flex;
align-items: center;
gap: 0.45rem;
background: var(--secondary);
color: white;
border: none;
border-radius: 9px;
padding: 0.65rem 1.4rem;
font-size: 0.88rem;
font-weight: 600;
text-decoration: none;
transition: background 0.2s;
margin-right: 0.5rem;
}
.btn-download:hover { background: #2563eb; color: white; }
.btn-share {
display: inline-flex;
align-items: center;
gap: 0.45rem;
background: #10b981;
color: white;
border: none;
border-radius: 9px;
padding: 0.65rem 1.4rem;
font-size: 0.88rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.btn-share:hover { background: #059669; }
.footer-note {
margin-top: 1.25rem;
color: var(--text-muted);
font-size: 0.82rem;
}
/* ── CHECKBOX STYLE ── */
input[type="checkbox"] {
width: 15px;
height: 15px;
accent-color: var(--secondary);
cursor: pointer;
}
/* ── RESPONSIVE ── */
@media (max-width: 768px) {
body { padding: 0.75rem 0.5rem; }
.invoice-header,
.date-strip,
.summary-wrap,
.invoice-footer { padding-left: 1.25rem; padding-right: 1.25rem; }
.id-grid { grid-template-columns: 1fr; padding: 1rem 1.25rem; }
.date-row { flex-direction: column; }
.panel, .cg-panel, .cg-groups-panel { margin-left: 1.25rem; margin-right: 1.25rem; }
.invoice-table { font-size: 0.75rem; }
}
/* ── GROUP ITEMS HIDDEN ROW ── */
.cg-items-row { background: #f8faff; }
.cg-toggle-btn {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.3rem 0.75rem;
border: 1.5px solid #3b82f6;
border-radius: 6px;
font-size: 0.75rem;
font-weight: 600;
color: #3b82f6;
background: transparent;
cursor: pointer;
transition: all 0.15s;
}
.cg-toggle-btn:hover { background: #eff6ff; }
/* ── DIVIDER ── */
.section-divider { height: 1px; background: var(--border); margin: 0 2.5rem; }
</style>
</head>
<body>
<div class="invoice-container">
<!-- ACCENT BAR -->
<div class="invoice-accent-bar"></div>
@php
$showActions = $showActions ?? true;
@endphp
<!-- ═══════════════════════════ HEADER ═══════════════════════════ -->
<div class="invoice-header">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
<div>
<div class="invoice-title">
<i class="fas fa-file-invoice" style="color:#3b82f6;font-size:1.5rem;vertical-align:middle;margin-right:0.4rem;"></i>INVOICE
</div>
<div class="invoice-number mt-1">{{ $invoice->invoice_number }}</div>
</div>
<div>
@if($invoice->status == 'paid')
<span class="status-badge status-paid">
<i class="fas fa-check-circle"></i> Paid
</span>
@elseif($invoice->status == 'overdue')
<span class="status-badge status-overdue">
<i class="fas fa-exclamation-circle"></i> Overdue
</span>
@elseif($invoice->status == 'pending')
<span class="status-badge status-pending">
<i class="fas fa-clock"></i> Pending
</span>
@else
<span class="status-badge status-default">
<i class="fas fa-question-circle"></i> {{ ucfirst($invoice->status) }}
</span>
@endif
</div>
</div>
</div>
<!-- ═══════════════════════════ ID BOXES ═══════════════════════════ -->
<div class="id-grid">
<!-- Invoice ID -->
<div class="id-box">
<div class="id-icon-wrap id-icon-blue">
<i class="fas fa-receipt"></i>
</div>
<div>
<div class="id-label">Invoice ID</div>
<div class="id-value">{{ $invoice->invoice_number }}</div>
</div>
</div>
<!-- Container ID -->
<div class="id-box">
<div class="id-icon-wrap id-icon-green">
<i class="fas fa-box"></i>
</div>
<div>
<div class="id-label">Container ID</div>
<div class="id-value">
@if($invoice->container && $invoice->container->container_number)
{{ $invoice->container->container_number }}
@elseif($invoice->container_id)
{{ $invoice->container_id }}
@else
@include('admin.popup_invoice', ['invoice' => $invoice, 'shipment' => $shipment])
N/A
@endif
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════ DATES ═══════════════════════════ -->
<div class="date-strip">
<div class="date-row">
<div class="date-card">
<div class="date-icon-wrap">
<i class="fas fa-calendar-alt"></i>
</div>
<div>
<div class="date-label">Invoice Date</div>
<div class="date-value">{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}</div>
</div>
</div>
<div class="date-arrow">
<i class="fas fa-arrow-right"></i>
</div>
<div class="date-card">
<div class="date-icon-wrap" style="background:#fff7ed;color:#f59e0b;">
<i class="fas fa-clock"></i>
</div>
<div>
<div class="date-label">Due Date</div>
<div class="date-value {{ $invoice->status == 'overdue' ? 'overdue' : '' }}">
{{ \Carbon\Carbon::parse($invoice->due_date)->format('M d, Y') }}
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════ CUSTOMER ═══════════════════════════ -->
<div class="panel">
<div class="panel-header">
<i class="fas fa-user-circle"></i> Customer Details
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<div class="customer-name">{{ $invoice->customer_name }}</div>
@if($invoice->company_name)
<div class="customer-detail"><strong>Company:</strong> {{ $invoice->company_name }}</div>
@endif
<div class="customer-detail"><strong>Mobile:</strong> {{ $invoice->customer_mobile }}</div>
<div class="customer-detail"><strong>Email:</strong> {{ $invoice->customer_email }}</div>
</div>
<div class="col-md-6">
<div class="customer-detail"><strong>Address:</strong></div>
<div class="customer-detail">{{ $invoice->customer_address }}</div>
<div class="customer-detail"><strong>Pincode:</strong> {{ $invoice->pincode }}</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════ ITEMS TABLE ═══════════════════════════ -->
@php
$isEmbedded = isset($embedded) && $embedded;
@endphp
<div class="panel">
<div class="panel-header">
<i class="fas fa-list"></i> Invoice Items
</div>
<!-- @if($isEmbedded)
<form action="{{ route('admin.invoices.items.update', $invoice->id) }}" method="POST">
@csrf
@method('PUT')
@endif -->
<div class="table-responsive">
<table class="invoice-table items-table">
<thead>
<tr>
<th class="text-center" style="width:44px;">
<input type="checkbox" id="selectAllItems">
</th>
<th class="text-center" style="min-width:50px;">#</th>
<th style="min-width:200px;">Description</th>
<th class="text-center" style="min-width:75px;">CTN</th>
<th class="text-center" style="min-width:75px;">QTY</th>
<th class="text-center" style="min-width:95px;">TTL/QTY</th>
<th class="text-center" style="min-width:75px;">Unit</th>
<th class="text-center" style="min-width:110px;">Price</th>
<th class="text-center" style="min-width:125px;">TTL Amount</th>
<th class="text-center" style="min-width:85px;">CBM</th>
<th class="text-center" style="min-width:95px;">TTL CBM</th>
<th class="text-center" style="min-width:80px;">KG</th>
<th class="text-center" style="min-width:95px;">TTL KG</th>
<th class="text-center" style="min-width:95px;">Shop No</th>
</tr>
</thead>
<tbody>
@foreach($invoice->items as $i => $item)
@php
$alreadyGrouped = in_array($item->id, $groupedItemIds ?? []);
@endphp
<tr class="{{ $alreadyGrouped ? 'grouped-item-row' : '' }}">
<td class="text-center">
<input type="checkbox"
class="item-select-checkbox"
value="{{ $item->id }}"
{{ $alreadyGrouped ? 'disabled' : '' }}>
</td>
<td class="text-center" style="font-weight:600;color:var(--text-muted);">{{ $i + 1 }}</td>
<td class="desc-col" style="font-weight:600;color:var(--primary);">{{ $item->description }}</td>
<td class="text-center">{{ $item->ctn }}</td>
<td class="text-center">{{ $item->qty }}</td>
<td class="text-center" style="font-weight:700;">{{ $item->ttl_qty }}</td>
<td class="text-center">{{ $item->unit }}</td>
@if($isEmbedded)
<td class="text-center" style="min-width:120px;">
<input type="number" step="0.01" min="0"
name="items[{{ $item->id }}][price]"
value="{{ old('items.' . $item->id . '.price', $item->price) }}"
class="form-control form-control-sm text-end">
</td>
<td class="text-center" style="min-width:140px;">
<input type="number" step="0.01" min="0"
name="items[{{ $item->id }}][ttl_amount]"
value="{{ old('items.' . $item->id . '.ttl_amount', $item->ttl_amount) }}"
class="form-control form-control-sm text-end">
</td>
@else
<td class="text-center price-green">{{ number_format($item->price, 2) }}</td>
<td class="text-center price-blue">{{ number_format($item->ttl_amount, 2) }}</td>
@endif
<td class="text-center">{{ $item->cbm }}</td>
<td class="text-center">{{ $item->ttl_cbm }}</td>
<td class="text-center">{{ $item->kg }}</td>
<td class="text-center">{{ $item->ttl_kg }}</td>
<td class="text-center"><span class="badge-shop">{{ $item->shop_no }}</span></td>
</tr>
@endforeach
@if($invoice->items->isEmpty())
<tr>
<td colspan="15" class="text-center py-4" style="color:var(--text-muted);font-weight:600;">
<i class="fas fa-inbox me-2" style="font-size:1.3rem;opacity:.4;"></i><br>No invoice items found.
</td>
</tr>
@endif
</tbody>
</table>
</div>
<!-- ACTION BAR -->
<div class="action-bar">
<small>Selected items for charge group: <span id="selectedItemsCount">0</span></small>
<button type="button" id="btnCreateChargeGroup" class="btn-create-group" disabled>
<i class="fas fa-layer-group"></i> Create Charge Group
</button>
</div>
@if($isEmbedded)
<div class="text-end p-3" style="border-top:1px solid var(--border);">
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-save me-1"></i> Update Items & Recalculate
</button>
</div>
</form>
@endif
</div>
<!-- ═══════════════════════════ CHARGE GROUP FORM ═══════════════════════════ -->
<div id="chargeGroupBox" class="cg-panel d-none">
<div class="cg-panel-header">
<i class="fas fa-layer-group"></i> Create Charge Group for Selected Items
</div>
<div class="cg-body">
<form method="POST" id="chargeGroupForm"
action="{{ route('admin.invoices.charge-group.store', $invoice->id) }}">
@csrf
<div class="row g-3 mb-3">
<div class="col-md-4">
<label class="form-label-custom">Group Name</label>
<input type="text" class="form-control-custom" id="cgGroupName"
name="group_name" placeholder="e.g. Group #1">
</div>
<div class="col-md-4">
<label class="form-label-custom">Price based on</label>
<select class="form-select-custom" id="cgBasis">
<option value="" selected disabled>Select basis</option>
<option value="ttl_qty">TTL/QTY</option>
<option value="amount">AMOUNT</option>
<option value="ttl_cbm">TTL CBM</option>
<option value="ttl_kg">TTL KG</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label-custom">Rate per selected basis</label>
<input type="number" step="0.0001" class="form-control-custom"
id="cgRate" placeholder="Enter rate">
</div>
</div>
<input type="hidden" name="basis_type" id="cgBasisTypeInput">
<input type="hidden" name="basis_value" id="cgBasisValueInput">
<input type="hidden" name="rate" id="cgRateHidden">
<input type="hidden" name="auto_total" id="cgAutoTotal">
<div class="row g-3 mb-3">
<!-- LEFT: Total basis value -->
<div class="col-md-6">
<div class="basis-box">
<div class="basis-box-label">Total basis value</div>
<div style="display:flex;align-items:baseline;gap:0.4rem;">
<span class="basis-value-display" id="cgBasisValue">0</span>
<span class="basis-label-display" id="cgBasisLabel"></span>
</div>
<div class="basis-hint" id="cgBasisHint">
Select basis to see total TTLQTY, CBM, KG or Amount.
</div>
</div>
</div>
<!-- RIGHT: Total charges (admin input) -->
<div class="col-md-6">
<div class="basis-box">
<div class="basis-box-label">Total charges (admin input)</div>
<input type="number"
step="0.01"
class="form-control-custom"
id="cgTotalChargeInput"
name="totalcharge"
placeholder="Enter total charges"
readonly>
<div class="basis-hint" style="margin-top:0.5rem">
Suggested Rate × basis:
<span class="suggested-total" id="cgSuggestedTotal">0</span>
</div>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label-custom" style="margin-bottom:0.5rem;">Selected items in this group</label>
<div class="table-responsive" style="border:1px solid var(--border);border-radius:9px;overflow:hidden;">
<table class="invoice-table">
<thead>
<tr>
<th>#</th>
<th>Description</th>
<th class="text-center">QTY</th>
<th class="text-center">TTL QTY</th>
<th class="text-center">CBM</th>
<th class="text-center">TTL CBM</th>
<th class="text-center">KG</th>
<th class="text-center">TTL KG</th>
<th class="text-center">TTL Amount</th>
</tr>
</thead>
<tbody id="cgItemsTableBody"></tbody>
</table>
</div>
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" id="cgCancelBtn"
style="padding:0.5rem 1.1rem;border:1.5px solid var(--border);border-radius:8px;background:transparent;font-size:0.85rem;font-weight:600;cursor:pointer;color:var(--text-secondary);">
Cancel
</button>
<button type="submit" id="cgSaveBtn"
style="display:inline-flex;align-items:center;gap:0.4rem;padding:0.5rem 1.25rem;border:none;border-radius:8px;background:#10b981;color:white;font-size:0.85rem;font-weight:700;cursor:pointer;">
<i class="fas fa-save"></i> Save Charge Group
</button>
</div>
</form>
</div>
</div>
<!-- ═══════════════════════════ CHARGE GROUPS LIST ═══════════════════════════ -->
@if($invoice->chargeGroups->isNotEmpty())
<div class="cg-groups-panel">
<div class="cg-groups-header">
<i class="fas fa-layer-group"></i> Charge Groups
</div>
<div class="table-responsive">
<table class="invoice-table">
<thead>
<tr>
<th>#</th>
<th>Group Name</th>
<th>Basis</th>
<th class="text-end">Basis Value</th>
<th class="text-end">Rate</th>
<th class="text-end">Total Charge</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach($invoice->chargeGroups as $index => $group)
<tr>
<td>{{ $index + 1 }}</td>
<td style="font-weight:600;">{{ $group->group_name ?? 'Group '.$group->id }}</td>
<td><span style="text-transform:uppercase;font-size:0.75rem;font-weight:700;color:var(--text-muted);">{{ $group->basis_type }}</span></td>
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">{{ number_format($group->basis_value, 3) }}</td>
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">{{ number_format($group->rate, 2) }}</td>
<td class="text-end price-blue">{{ number_format($group->total_charge, 2) }}</td>
<td class="text-center">
<button type="button" class="cg-toggle-btn cg-toggle-items"
data-group-id="{{ $group->id }}">
<i class="fas fa-eye"></i> View
</button>
</td>
</tr>
<tr class="cg-items-row d-none" data-group-id="{{ $group->id }}">
<td colspan="7">
<div style="padding:1rem;background:#f8faff;border-radius:8px;margin:0.25rem 0;">
<div style="font-weight:700;font-size:0.8rem;margin-bottom:0.6rem;color:var(--primary);">Items in this group:</div>
@if($group->items->isEmpty())
<div style="color:var(--text-muted);font-size:0.82rem;">No items linked.</div>
@else
<div class="table-responsive">
<table class="invoice-table">
<thead>
<tr>
<th>#</th>
<th>Description</th>
<th class="text-center">QTY</th>
<th class="text-center">TTL QTY</th>
<th class="text-center">CBM</th>
<th class="text-center">TTL CBM</th>
<th class="text-center">KG</th>
<th class="text-center">TTL KG</th>
<th class="text-end">TTL Amount</th>
<th class="text-end">Rate</th>
<th class="text-end">Total Charge</th>
</tr>
</thead>
<tbody>
@foreach($group->items as $giIndex => $gi)
@php
$it = $gi->item;
$rate = $group->rate;
$itemBasis = 0;
switch ($group->basis_type) {
case 'ttl_qty': $itemBasis = $it->ttl_qty ?? 0; break;
case 'amount': $itemBasis = $it->ttl_amount ?? 0; break;
case 'ttl_cbm': $itemBasis = $it->ttl_cbm ?? 0; break;
case 'ttl_kg': $itemBasis = $it->ttl_kg ?? 0; break;
}
$itemTotal = $itemBasis * $rate;
@endphp
<tr>
<td>{{ $giIndex + 1 }}</td>
<td>{{ $it->description }}</td>
<td class="text-center">{{ $it->qty }}</td>
<td class="text-center">{{ $it->ttl_qty }}</td>
<td class="text-center">{{ $it->cbm }}</td>
<td class="text-center">{{ $it->ttl_cbm }}</td>
<td class="text-center">{{ $it->kg }}</td>
<td class="text-center">{{ $it->ttl_kg }}</td>
<td class="text-end">{{ number_format($it->ttl_amount, 2) }}</td>
<td class="text-end">{{ number_format($rate, 2) }}</td>
<td class="text-end" style="color:#06b6d4;font-weight:700;">{{ number_format($itemTotal, 2) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
<!-- ═══════════════════════════ SUMMARY ═══════════════════════════ -->
<div class="summary-wrap">
<div class="row justify-content-end">
<div class="col-md-5">
<div class="summary-card">
<div class="summary-header-row">
<i class="fas fa-calculator"></i> Final Summary
</div>
<div class="summary-body">
<div class="summary-row">
<span class="label">Amount</span>
<span class="value">{{ number_format($invoice->final_amount, 2) }}</span>
</div>
@if($invoice->tax_type === 'gst')
<div class="summary-row">
<span class="label">CGST ({{ $invoice->cgst_percent ?? ($invoice->gst_percent / 2) }}%)</span>
<span class="value red">{{ number_format($invoice->gst_amount / 2, 2) }}</span>
</div>
<div class="summary-row">
<span class="label">SGST ({{ $invoice->sgst_percent ?? ($invoice->gst_percent / 2) }}%)</span>
<span class="value red">{{ number_format($invoice->gst_amount / 2, 2) }}</span>
</div>
@elseif($invoice->tax_type === 'igst')
<div class="summary-row">
<span class="label">IGST ({{ $invoice->igst_percent ?? $invoice->gst_percent }}%)</span>
<span class="value red">{{ number_format($invoice->gst_amount, 2) }}</span>
</div>
@else
<div class="summary-row">
<span class="label">GST ({{ $invoice->gst_percent }}%)</span>
<span class="value red">{{ number_format($invoice->gst_amount, 2) }}</span>
</div>
@endif
<div class="summary-row total">
<span class="label">Total Payable</span>
<span class="value">{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════ FOOTER ═══════════════════════════ -->
<div class="invoice-footer">
@if($invoice->pdf_path && $showActions)
<a href="{{ asset($invoice->pdf_path) }}" class="btn-download" download>
<i class="fas fa-download"></i> Download PDF
</a>
<button class="btn-share" onclick="shareInvoice()">
<i class="fas fa-share"></i> Share
</button>
@endif
<div class="footer-note">
<p style="margin-bottom:0.25rem;">Thank you for your business!</p>
<p style="margin:0;">For any inquiries, contact us at support@Kent Logistic</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script>
function shareInvoice() {
const shareData = {
title: "Invoice {{ $invoice->invoice_number }}",
text: "Sharing invoice {{ $invoice->invoice_number }}",
url: "{{ asset($invoice->pdf_path) }}"
};
if (navigator.share) {
navigator.share(shareData).catch(() => {});
} else {
navigator.clipboard.writeText(shareData.url);
alert("Link copied! Sharing not supported on this browser.");
}
}
document.addEventListener('DOMContentLoaded', function () {
const selectAll = document.getElementById('selectAllItems');
const itemCheckboxes = document.querySelectorAll('.item-select-checkbox');
const countSpan = document.getElementById('selectedItemsCount');
const btnCreate = document.getElementById('btnCreateChargeGroup');
const chargeGroupBox = document.getElementById('chargeGroupBox');
const cgCancelBtn = document.getElementById('cgCancelBtn');
const cgItemsTableBody = document.getElementById('cgItemsTableBody');
const cgBasisSelect = document.getElementById('cgBasis');
const cgRateInput = document.getElementById('cgRate');
const cgBasisValueSpan = document.getElementById('cgBasisValue');
const cgBasisLabelSpan = document.getElementById('cgBasisLabel');
const cgSuggestedTotalSpan = document.getElementById('cgSuggestedTotal');
const cgTotalChargeInput = document.getElementById('cgTotalChargeInput');
const cgAutoTotalInput = document.getElementById('cgAutoTotal');
const cgBasisTypeInput = document.getElementById('cgBasisTypeInput');
const cgBasisValueInput = document.getElementById('cgBasisValueInput');
const cgRateHidden = document.getElementById('cgRateHidden');
const cgForm = document.getElementById('chargeGroupForm');
function updateSelectionState() {
let selectedCount = 0;
itemCheckboxes.forEach(cb => {
if (cb.checked && !cb.disabled) selectedCount++;
});
if (countSpan) countSpan.textContent = selectedCount;
if (btnCreate) btnCreate.disabled = (selectedCount === 0);
if (selectedCount === 0 && chargeGroupBox) {
chargeGroupBox.classList.add('d-none');
}
}
function getSelectedItemsDataBasic() {
const items = [];
itemCheckboxes.forEach(cb => {
if (cb.checked && !cb.disabled) {
const row = cb.closest('tr');
if (!row) return;
const cellText = (n) =>
row.querySelector(`td:nth-child(${n})`)?.textContent.trim() ?? '';
items.push({
description: cellText(3),
qty: cellText(5),
ttlqty: cellText(6),
cbm: cellText(10),
ttlcbm: cellText(11),
kg: cellText(12),
ttlkg: cellText(13),
amount: cellText(9),
});
}
});
return items;
}
function parseNumber(str) {
if (!str) return 0;
const cleaned = str.replace(/[,\s]/g, '');
const val = parseFloat(cleaned);
return isNaN(val) ? 0 : val;
}
function fillChargeGroupItemsTable() {
if (!cgItemsTableBody) return;
const items = getSelectedItemsDataBasic();
cgItemsTableBody.innerHTML = '';
items.forEach((it, index) => {
cgItemsTableBody.insertAdjacentHTML('beforeend', `
<tr>
<td>${index + 1}</td>
<td>${it.description}</td>
<td class="text-center">${it.qty}</td>
<td class="text-center">${it.ttlqty}</td>
<td class="text-center">${it.cbm}</td>
<td class="text-center">${it.ttlcbm}</td>
<td class="text-center">${it.kg}</td>
<td class="text-center">${it.ttlkg}</td>
<td class="text-center">${it.amount}</td>
</tr>
`);
});
}
function refreshBasisSummaryAndSuggestion() {
if (!chargeGroupBox || chargeGroupBox.classList.contains('d-none')) return;
const items = getSelectedItemsDataBasic();
const basis = cgBasisSelect ? cgBasisSelect.value : '';
let totalBasis = 0;
let label = '';
if (basis === 'ttl_qty') {
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlqty), 0);
label = 'Total TTL/QTY';
} else if (basis === 'amount') {
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.amount), 0);
label = 'Total Amount';
} else if (basis === 'ttl_cbm') {
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlcbm), 0);
label = 'Total TTL CBM';
} else if (basis === 'ttl_kg') {
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlkg), 0);
label = 'Total TTL KG';
}
if (cgBasisValueSpan) cgBasisValueSpan.textContent = totalBasis ? totalBasis.toFixed(3) : '0';
if (cgBasisLabelSpan) cgBasisLabelSpan.textContent = label || '';
const rate = parseNumber(cgRateInput ? cgRateInput.value : '0');
const suggested = rate * totalBasis;
if (cgSuggestedTotalSpan) cgSuggestedTotalSpan.textContent = suggested ? suggested.toFixed(2) : '0';
if (cgBasisTypeInput) cgBasisTypeInput.value = basis || '';
if (cgBasisValueInput) cgBasisValueInput.value = totalBasis || 0;
if (cgRateHidden && cgRateInput) cgRateHidden.value = cgRateInput.value || 0;
if (cgAutoTotalInput) cgAutoTotalInput.value = suggested || 0;
if (cgTotalChargeInput) {
cgTotalChargeInput.value = suggested ? suggested.toFixed(2) : '0';
}
}
itemCheckboxes.forEach(cb => {
cb.addEventListener('change', function () {
updateSelectionState();
const total = itemCheckboxes.length;
const checked = Array.from(itemCheckboxes).filter(c => c.checked && !c.disabled).length;
if (selectAll) {
selectAll.checked = (checked > 0 && checked === total);
selectAll.indeterminate = (checked > 0 && checked < total);
}
if (!chargeGroupBox.classList.contains('d-none')) {
fillChargeGroupItemsTable();
refreshBasisSummaryAndSuggestion();
}
});
});
if (selectAll) {
selectAll.addEventListener('change', function () {
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = selectAll.checked; });
updateSelectionState();
if (!chargeGroupBox.classList.contains('d-none')) {
fillChargeGroupItemsTable();
refreshBasisSummaryAndSuggestion();
}
});
}
if (cgBasisSelect) cgBasisSelect.addEventListener('change', refreshBasisSummaryAndSuggestion);
if (cgRateInput) cgRateInput.addEventListener('input', refreshBasisSummaryAndSuggestion);
if (btnCreate && chargeGroupBox) {
btnCreate.addEventListener('click', function () {
const hasSelection = Array.from(itemCheckboxes).some(cb => cb.checked && !cb.disabled);
if (!hasSelection) return;
chargeGroupBox.classList.remove('d-none');
fillChargeGroupItemsTable();
refreshBasisSummaryAndSuggestion();
chargeGroupBox.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
}
if (cgCancelBtn && chargeGroupBox) {
cgCancelBtn.addEventListener('click', function () {
chargeGroupBox.classList.add('d-none');
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = false; });
if (selectAll) { selectAll.checked = false; selectAll.indeterminate = false; }
updateSelectionState();
});
}
if (cgForm) {
cgForm.addEventListener('submit', function (e) {
const selectedIds = [];
itemCheckboxes.forEach(cb => { if (cb.checked && !cb.disabled) selectedIds.push(cb.value); });
if (selectedIds.length === 0) {
e.preventDefault();
alert('Please select at least one item for this charge group.');
return;
}
if (!cgBasisSelect || !cgBasisSelect.value) {
e.preventDefault();
alert('Please select a basis for this charge group.');
return;
}
if (!cgTotalChargeInput || !cgTotalChargeInput.value || parseFloat(cgTotalChargeInput.value) <= 0) {
e.preventDefault();
alert('Please enter total charges for this group.');
return;
}
const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]');
oldHidden.forEach(el => el.remove());
selectedIds.forEach(id => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'item_ids[]';
input.value = id;
cgForm.appendChild(input);
});
if (chargeGroupBox) chargeGroupBox.classList.add('d-none');
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = false; });
if (selectAll) { selectAll.checked = false; selectAll.indeterminate = false; }
updateSelectionState();
});
}
updateSelectionState();
});
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('click', function (e) {
if (!e.target.classList.contains('cg-toggle-items') &&
!e.target.closest('.cg-toggle-items')) return;
const btn = e.target.closest('.cg-toggle-items') || e.target;
const groupId = btn.getAttribute('data-group-id');
const row = document.querySelector('.cg-items-row[data-group-id="' + groupId + '"]');
if (!row) return;
row.classList.toggle('d-none');
const isHidden = row.classList.contains('d-none');
btn.innerHTML = isHidden
? '<i class="fas fa-eye"></i> View'
: '<i class="fas fa-eye-slash"></i> Hide';
});
});
</script>
</body>
</html>