892 lines
30 KiB
PHP
892 lines
30 KiB
PHP
@extends('admin.layouts.app')
|
||
|
||
@section('page-title', 'Container Details')
|
||
|
||
@section('content')
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');
|
||
|
||
html, body {
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100%;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
.cm-header-card {
|
||
background: linear-gradient(100deg, #4c6fff 0%, #8e54e9 100%);
|
||
border-radius: 14px;
|
||
border: none;
|
||
box-shadow: 0 6px 24px rgba(76,111,255,0.22);
|
||
margin-bottom: 18px;
|
||
color: #fff;
|
||
}
|
||
.cm-header-card .card-body {
|
||
padding: 14px 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.cm-header-title {
|
||
margin: 0;
|
||
font-size: 19px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.3px;
|
||
}
|
||
.cm-header-sub {
|
||
font-size: 12px;
|
||
opacity: 0.88;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.cm-main-card {
|
||
border-radius: 14px;
|
||
border: none;
|
||
box-shadow: 0 4px 20px rgba(15,35,52,0.10);
|
||
overflow: hidden;
|
||
background: #fff;
|
||
position: relative;
|
||
}
|
||
.cm-main-card .card-header {
|
||
background: #fff;
|
||
border-bottom: 1px solid #edf0f5;
|
||
padding: 12px 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.cm-main-card .card-header h5 {
|
||
margin: 0;
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #1a2340;
|
||
}
|
||
|
||
.cm-info-cards-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr)); /* 3 cards one row */
|
||
gap: 14px;
|
||
padding: 14px 18px 8px 18px;
|
||
}
|
||
.cm-info-card {
|
||
border-radius: 16px;
|
||
padding: 12px 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
box-shadow: 0 4px 16px rgba(15,35,52,0.12);
|
||
border: 1px solid rgba(148,163,184,0.25);
|
||
background: #fff;
|
||
min-height: 70px;
|
||
}
|
||
.cm-info-card-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #0f172a;
|
||
font-size: 16px;
|
||
box-shadow: 0 3px 10px rgba(15,23,42,0.10);
|
||
flex-shrink: 0;
|
||
}
|
||
.cm-info-card-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
}
|
||
.cm-info-card-label {
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.4px;
|
||
color: #64748b;
|
||
font-weight: 600;
|
||
}
|
||
.cm-info-card-value {
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: #0f172a;
|
||
white-space: nowrap;
|
||
}
|
||
.cm-card-container {
|
||
background: linear-gradient(135deg, #e0f2ff, #eef4ff);
|
||
}
|
||
.cm-card-container .cm-info-card-icon {
|
||
background: linear-gradient(135deg, #2563eb, #4f46e5);
|
||
color: #e5edff;
|
||
}
|
||
.cm-card-date {
|
||
background: linear-gradient(135deg, #ecfdf3, #e0fbea);
|
||
}
|
||
.cm-card-date .cm-info-card-icon {
|
||
background: linear-gradient(135deg, #16a34a, #22c55e);
|
||
color: #ecfdf3;
|
||
}
|
||
.cm-card-excel {
|
||
background: linear-gradient(135deg, #fff7ed, #fffbeb);
|
||
}
|
||
.cm-card-excel .cm-info-card-icon {
|
||
background: linear-gradient(135deg, #f97316, #fb923c);
|
||
color: #fff7ed;
|
||
}
|
||
|
||
/* TOTAL BOXES */
|
||
.cm-total-cards-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: 14px;
|
||
padding: 4px 18px 14px 18px;
|
||
}
|
||
.cm-total-card {
|
||
border-radius: 18px;
|
||
padding: 12px 18px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
box-shadow: 0 8px 30px rgba(15,23,42,0.08);
|
||
border: 1px solid rgba(148,163,184,0.25);
|
||
}
|
||
.cm-total-icon {
|
||
width: 34px;
|
||
height: 34px;
|
||
border-radius: 999px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 17px;
|
||
flex-shrink: 0;
|
||
color: #0f172a;
|
||
background: #fff;
|
||
box-shadow: 0 4px 14px rgba(15,23,42,0.15);
|
||
}
|
||
.cm-total-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
}
|
||
.cm-total-label {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #4b5563;
|
||
}
|
||
.cm-total-value {
|
||
font-size: 18px;
|
||
font-weight: 800;
|
||
color: #111827;
|
||
}
|
||
.cm-total-card-ctn {
|
||
background: linear-gradient(135deg, #e0f2ff, #eef2ff);
|
||
border-left: 4px solid #3b82f6;
|
||
}
|
||
.cm-total-card-qty {
|
||
background: linear-gradient(135deg, #dcfce7, #ecfdf5);
|
||
border-left: 4px solid #22c55e;
|
||
}
|
||
.cm-total-card-cbm {
|
||
background: linear-gradient(135deg, #fef9c3, #fffbeb);
|
||
border-left: 4px solid #f59e0b;
|
||
}
|
||
.cm-total-card-kg {
|
||
background: linear-gradient(135deg, #fee2e2, #fef2f2);
|
||
border-left: 4px solid #ef4444;
|
||
}
|
||
|
||
.cm-filter-bar {
|
||
padding: 4px 20px 0 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.cm-row-count {
|
||
font-size: 12px;
|
||
color: #8a93a6;
|
||
font-weight: 500;
|
||
}
|
||
.cm-filter-input {
|
||
max-width: 240px;
|
||
font-size: 12px;
|
||
border-radius: 20px;
|
||
border: 1px solid #dde2ee;
|
||
padding: 6px 14px;
|
||
outline: none;
|
||
transition: border 0.2s;
|
||
}
|
||
.cm-filter-input:focus {
|
||
border-color: #4c6fff;
|
||
box-shadow: 0 0 0 3px rgba(76,111,255,0.1);
|
||
}
|
||
|
||
.cm-table-scroll-outer {
|
||
margin: 10px 14px 0 14px;
|
||
border-radius: 14px;
|
||
border: 1.5px solid #c9a359;
|
||
box-shadow: 0 4px 14px rgba(76,111,255,0.18);
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
position: relative;
|
||
}
|
||
.cm-table-wrapper {
|
||
position: relative;
|
||
min-width: 1200px;
|
||
}
|
||
|
||
.cm-table {
|
||
font-size: 12px;
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
font-family: 'DM Sans', sans-serif;
|
||
}
|
||
.cm-table thead tr th {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 3;
|
||
background: linear-gradient(100deg, #fde4b3 0%, #fbd48a 100%);
|
||
color: #0c0909;
|
||
font-weight: 700;
|
||
font-size: 12px;
|
||
padding: 11px 14px;
|
||
border-bottom: 2px solid rgba(255,255,255,0.15);
|
||
border-right: 1px solid rgba(255,255,255,0.18);
|
||
white-space: nowrap;
|
||
text-align: center;
|
||
letter-spacing: 0.2px;
|
||
text-shadow: 0 1px 2px rgba(0,0,0,0.35);
|
||
}
|
||
.cm-table thead tr th:first-child {
|
||
border-top-left-radius: 10px;
|
||
}
|
||
.cm-table thead tr th:last-child {
|
||
border-top-right-radius: 10px;
|
||
border-right: none;
|
||
}
|
||
.cm-table thead tr th:first-child,
|
||
.cm-table tbody tr td:first-child {
|
||
width: 46px;
|
||
min-width: 46px;
|
||
max-width: 46px;
|
||
text-align: center;
|
||
}
|
||
.cm-table tbody tr td {
|
||
padding: 8px 14px;
|
||
border-bottom: 1px solid #f0f3fb;
|
||
border-right: 1px solid #f0f3fb;
|
||
color: #2d3a55;
|
||
font-size: 12.5px;
|
||
text-align: center;
|
||
vertical-align: middle;
|
||
background: #fff;
|
||
transition: background 0.15s;
|
||
white-space: nowrap;
|
||
}
|
||
.cm-table tbody tr td:last-child {
|
||
border-right: none;
|
||
}
|
||
.cm-table tbody tr:nth-child(even) td {
|
||
background: #f8f9ff;
|
||
}
|
||
.cm-table tbody tr:hover td {
|
||
background: #edf3ff !important;
|
||
}
|
||
.cm-row-num {
|
||
color: #8a93a6;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.cm-cell-input {
|
||
width: 100%;
|
||
min-width: 90px;
|
||
background: transparent;
|
||
border: 1.5px solid transparent;
|
||
border-radius: 5px;
|
||
font-size: 12.5px;
|
||
font-family: 'DM Sans', sans-serif;
|
||
color: #2d3a55;
|
||
padding: 3px 6px;
|
||
text-align: center;
|
||
transition: border 0.15s, background 0.15s, box-shadow 0.15s;
|
||
outline: none;
|
||
}
|
||
.cm-cell-input:hover {
|
||
border-color: #c9d4f5;
|
||
background: #f5f8ff;
|
||
}
|
||
.cm-cell-input:focus {
|
||
border-color: #4c6fff;
|
||
background: #fff;
|
||
box-shadow: 0 0 0 3px rgba(76,111,255,0.12);
|
||
}
|
||
.cm-cell-readonly {
|
||
background: #f3f4ff;
|
||
cursor: not-allowed;
|
||
border-color: #e2e3ff;
|
||
}
|
||
|
||
.cm-save-btn-floating {
|
||
position: fixed;
|
||
right: 26px;
|
||
bottom: 22px;
|
||
z-index: 50;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
border-radius: 22px;
|
||
padding: 8px 20px;
|
||
background: linear-gradient(90deg, #4c6fff, #8e54e9);
|
||
border: none;
|
||
color: #fff;
|
||
box-shadow: 0 4px 16px rgba(76,111,255,0.35);
|
||
transition: opacity 0.2s, transform 0.15s;
|
||
cursor: pointer;
|
||
}
|
||
.cm-save-btn-floating:hover {
|
||
opacity: 0.94;
|
||
transform: translateY(-1px);
|
||
}
|
||
.cm-save-btn-floating.cm-disabled {
|
||
opacity: 0.6;
|
||
cursor: default;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.cm-toast {
|
||
position: fixed;
|
||
right: 26px;
|
||
bottom: 70px;
|
||
background: #0f172a;
|
||
color: #f9fafb;
|
||
font-size: 12.5px;
|
||
padding: 8px 14px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 8px 24px rgba(15,23,42,0.25);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.25s, transform 0.2s;
|
||
transform: translateY(8px);
|
||
z-index: 60;
|
||
}
|
||
.cm-toast.cm-show {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.cm-empty {
|
||
padding: 30px 20px;
|
||
color: #8a93a6;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.cm-table-scroll-outer::-webkit-scrollbar {
|
||
height: 6px;
|
||
}
|
||
.cm-table-scroll-outer::-webkit-scrollbar-track {
|
||
background: #f0f3fb;
|
||
border-radius: 4px;
|
||
}
|
||
.cm-table-scroll-outer::-webkit-scrollbar-thumb {
|
||
background: #c5cce8;
|
||
border-radius: 4px;
|
||
}
|
||
.cm-table-scroll-outer::-webkit-scrollbar-thumb:hover {
|
||
background: #8a99d0;
|
||
}
|
||
|
||
@media (max-width: 991px) {
|
||
.cm-total-cards-row {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
}
|
||
@media (max-width: 767px) {
|
||
.cm-header-card .card-body {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
.cm-info-cards-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
.cm-table-scroll-outer {
|
||
overflow-x: auto;
|
||
}
|
||
.cm-table-wrapper {
|
||
min-width: 900px;
|
||
}
|
||
}
|
||
@media (max-width: 575px) {
|
||
.cm-total-cards-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<div class="container-fluid cm-wrapper">
|
||
<div class="card cm-header-card">
|
||
<div class="card-body">
|
||
<div>
|
||
<h4 class="cm-header-title">Container {{ $container->container_number }}</h4>
|
||
<div class="cm-header-sub">
|
||
Edit loading list directly – like Excel. TT columns auto‑calculate from CTN, QTY, CBM, KG, PRICE.
|
||
</div>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<a href="{{ route('containers.index') }}" class="btn btn-light btn-sm">Back to list</a>
|
||
|
||
<a href="{{ route('containers.download.pdf', $container->id) }}"
|
||
class="btn btn-sm btn-outline-primary">
|
||
Download PDF
|
||
</a>
|
||
|
||
<a href="{{ route('containers.download.excel', $container->id) }}"
|
||
class="btn btn-sm btn-outline-success">
|
||
Download Excel
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card cm-main-card">
|
||
<div class="card-header">
|
||
<h5>Container Information</h5>
|
||
</div>
|
||
|
||
{{-- 3 INFO CARDS IN SINGLE ROW --}}
|
||
<div class="cm-info-cards-row">
|
||
<div class="cm-info-card cm-card-container">
|
||
<div class="cm-info-card-icon">
|
||
<i class="bi bi-box-seam"></i>
|
||
</div>
|
||
<div class="cm-info-card-body">
|
||
<div class="cm-info-card-label">Container</div>
|
||
<div class="cm-info-card-value">
|
||
{{ $container->container_name ?? $container->container_number }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cm-info-card cm-card-date">
|
||
<div class="cm-info-card-icon">
|
||
<i class="bi bi-calendar-event"></i>
|
||
</div>
|
||
<div class="cm-info-card-body">
|
||
<div class="cm-info-card-label">Date</div>
|
||
<div class="cm-info-card-value">
|
||
{{ $container->container_date ? $container->container_date->format('d-m-Y') : '' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cm-info-card cm-card-excel">
|
||
<div class="cm-info-card-icon">
|
||
<i class="bi bi-file-earmark-excel"></i>
|
||
</div>
|
||
<div class="cm-info-card-body">
|
||
<div class="cm-info-card-label">Excel File</div>
|
||
<div class="cm-info-card-value">
|
||
@if($container->excel_file)
|
||
<a href="{{ url($container->excel_file) }}" target="_blank" style="color:#0f172a;text-decoration:none;">
|
||
Download / View
|
||
</a>
|
||
@else
|
||
<span style="color:#b0b8cc;">Not uploaded</span>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@php
|
||
$totalCtn = 0;
|
||
$totalQty = 0;
|
||
$totalCbm = 0.0;
|
||
$totalKg = 0.0;
|
||
|
||
if(!$container->rows->isEmpty()){
|
||
foreach ($container->rows as $row) {
|
||
if (!is_array($row->data)) continue;
|
||
foreach ($row->data as $h => $v) {
|
||
$norm = strtoupper(str_replace([' ', '/', '-', '.'],'', $h));
|
||
$val = is_numeric(str_replace([','], '', $v)) ? floatval(str_replace([','], '', $v)) : 0;
|
||
|
||
if (str_contains($norm, 'TOTALCTN') || $norm === 'CTN' || str_contains($norm,'TOTALCNTR') || str_contains($norm,'TOTALCARTON')) {
|
||
$totalCtn += $val;
|
||
}
|
||
|
||
if (
|
||
str_contains($norm,'TOTALQTY') ||
|
||
str_contains($norm,'ITLQTY') ||
|
||
str_contains($norm,'TTLQTY')
|
||
) {
|
||
$totalQty += $val;
|
||
}
|
||
|
||
if (
|
||
str_contains($norm,'TOTALCBM') ||
|
||
str_contains($norm,'TTLCBM') ||
|
||
str_contains($norm,'ITLCBM')
|
||
) {
|
||
$totalCbm += $val;
|
||
}
|
||
|
||
if (
|
||
str_contains($norm,'TOTALKG') ||
|
||
str_contains($norm,'TTKG')
|
||
) {
|
||
$totalKg += $val;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
@endphp
|
||
|
||
@if(!$container->rows->isEmpty())
|
||
<div class="cm-total-cards-row">
|
||
<div class="cm-total-card cm-total-card-ctn">
|
||
<div class="cm-total-icon">
|
||
<i class="bi bi-box"></i>
|
||
</div>
|
||
<div class="cm-total-text">
|
||
<div class="cm-total-label">Total CTN</div>
|
||
<div class="cm-total-value">
|
||
{{ number_format($totalCtn, 0) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cm-total-card cm-total-card-qty">
|
||
<div class="cm-total-icon">
|
||
<i class="bi bi-check-circle"></i>
|
||
</div>
|
||
<div class="cm-total-text">
|
||
<div class="cm-total-label">Total QTY</div>
|
||
<div class="cm-total-value">
|
||
{{ number_format($totalQty, 0) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cm-total-card cm-total-card-cbm">
|
||
<div class="cm-total-icon">
|
||
<i class="bi bi-border-width"></i>
|
||
</div>
|
||
<div class="cm-total-text">
|
||
<div class="cm-total-label">Total CBM</div>
|
||
<div class="cm-total-value">
|
||
{{ number_format($totalCbm, 3) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cm-total-card cm-total-card-kg">
|
||
<div class="cm-total-icon">
|
||
<i class="bi bi-exclamation-triangle"></i>
|
||
</div>
|
||
<div class="cm-total-text">
|
||
<div class="cm-total-label">Total KG</div>
|
||
<div class="cm-total-value">
|
||
{{ number_format($totalKg, 2) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
@if($container->rows->isEmpty())
|
||
<div class="cm-empty">No entries found for this container.</div>
|
||
@else
|
||
@php
|
||
$allHeadings = [];
|
||
foreach ($container->rows as $row) {
|
||
if (is_array($row->data)) {
|
||
$allHeadings = array_unique(array_merge($allHeadings, array_keys($row->data)));
|
||
}
|
||
}
|
||
@endphp
|
||
|
||
<div class="cm-filter-bar">
|
||
<span class="cm-row-count">
|
||
Total rows: {{ $container->rows->count() }} Edit cells then click "Save Changes".
|
||
</span>
|
||
<input type="text" id="cmRowSearch" class="cm-filter-input"
|
||
placeholder="Quick search..." onkeyup="cmFilterRows()">
|
||
</div>
|
||
|
||
<form id="cm-edit-rows-form" action="{{ route('containers.rows.update', $container->id) }}" method="POST">
|
||
@csrf
|
||
<div class="cm-table-scroll-outer">
|
||
<div class="cm-table-wrapper">
|
||
<table class="cm-table" id="cmExcelTable">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
@foreach($allHeadings as $heading)
|
||
<th>{{ $heading }}</th>
|
||
@endforeach
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@foreach($container->rows as $index => $row)
|
||
<tr>
|
||
<td class="cm-row-num">{{ $index + 1 }}</td>
|
||
@foreach($allHeadings as $heading)
|
||
@php
|
||
$value = $row->data[$heading] ?? '';
|
||
|
||
$norm = strtoupper(str_replace([' ', '/', '-', '.'],'', $heading));
|
||
|
||
$isCtn = str_contains($norm, 'CTN');
|
||
|
||
$isTotalQty = (
|
||
str_contains($norm, 'TOTALQTY') ||
|
||
str_contains($norm, 'ITLQTY') ||
|
||
str_contains($norm, 'TTLQTY')
|
||
);
|
||
$isQty = !$isTotalQty && (
|
||
str_contains($norm, 'QTY') ||
|
||
str_contains($norm, 'PCS') ||
|
||
str_contains($norm, 'PIECES')
|
||
);
|
||
|
||
$isTotalCbm = (
|
||
str_contains($norm, 'TOTALCBM') ||
|
||
str_contains($norm, 'TTLCBM') ||
|
||
str_contains($norm, 'ITLCBM')
|
||
);
|
||
$isCbm = !$isTotalCbm && str_contains($norm, 'CBM');
|
||
|
||
$isTotalKg = (
|
||
str_contains($norm, 'TOTALKG') ||
|
||
str_contains($norm, 'TTKG')
|
||
);
|
||
$isKg = !$isTotalKg && (str_contains($norm, 'KG') || str_contains($norm, 'WEIGHT'));
|
||
|
||
$isPrice = (str_contains($norm, 'PRICE') || str_contains($norm, 'RATE'));
|
||
|
||
$isAmount = (
|
||
str_contains($norm, 'AMOUNT') ||
|
||
str_contains($norm, 'TTLAMOUNT') ||
|
||
str_contains($norm, 'TOTALAMOUNT')
|
||
);
|
||
|
||
$isTotalColumn = $isTotalQty || $isTotalCbm || $isTotalKg || $isAmount;
|
||
@endphp
|
||
<td>
|
||
<input
|
||
type="text"
|
||
class="cm-cell-input {{ $isTotalColumn ? 'cm-cell-readonly' : '' }}"
|
||
name="rows[{{ $row->id }}][{{ $heading }}]"
|
||
value="{{ $value }}"
|
||
data-row-id="{{ $row->id }}"
|
||
data-col-key="{{ $heading }}"
|
||
data-ctn="{{ $isCtn ? '1' : '0' }}"
|
||
data-qty="{{ $isQty ? '1' : '0' }}"
|
||
data-ttlqty="{{ $isTotalQty ? '1' : '0' }}"
|
||
data-cbm="{{ $isCbm ? '1' : '0' }}"
|
||
data-ttlcbm="{{ $isTotalCbm ? '1' : '0' }}"
|
||
data-kg="{{ $isKg ? '1' : '0' }}"
|
||
data-ttlkg="{{ $isTotalKg ? '1' : '0' }}"
|
||
data-price="{{ $isPrice ? '1' : '0' }}"
|
||
data-amount="{{ $isAmount ? '1' : '0' }}"
|
||
@if($isTotalColumn) readonly @endif
|
||
>
|
||
</td>
|
||
@endforeach
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
@if(!$container->rows->isEmpty())
|
||
<button id="cmSaveBtnFloating" class="cm-save-btn-floating">
|
||
Save Changes
|
||
</button>
|
||
<div id="cmToast" class="cm-toast">
|
||
Changes saved successfully.
|
||
</div>
|
||
@endif
|
||
|
||
<script>
|
||
function cmFilterRows() {
|
||
const input = document.getElementById('cmRowSearch');
|
||
if (!input) return;
|
||
const filter = input.value.toLowerCase();
|
||
const table = document.getElementById('cmExcelTable');
|
||
if (!table) return;
|
||
const rows = table.getElementsByTagName('tr');
|
||
for (let i = 1; i < rows.length; i++) {
|
||
const cells = rows[i].getElementsByTagName('td');
|
||
let match = false;
|
||
for (let j = 0; j < cells.length; j++) {
|
||
const txt = cells[j].textContent || cells[j].innerText;
|
||
if (txt.toLowerCase().indexOf(filter) > -1) {
|
||
match = true;
|
||
break;
|
||
}
|
||
}
|
||
rows[i].style.display = match ? '' : 'none';
|
||
}
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
const form = document.getElementById('cm-edit-rows-form');
|
||
const btn = document.getElementById('cmSaveBtnFloating');
|
||
const toast = document.getElementById('cmToast');
|
||
const table = document.getElementById('cmExcelTable');
|
||
|
||
function showToast(message, isError = false) {
|
||
if (!toast) return;
|
||
toast.textContent = message;
|
||
toast.style.background = isError ? '#b91c1c' : '#0f172a';
|
||
toast.classList.add('cm-show');
|
||
setTimeout(() => toast.classList.remove('cm-show'), 2500);
|
||
}
|
||
|
||
function parseNumber(str) {
|
||
if (!str) return 0;
|
||
const cleaned = String(str).replace(/,/g, '').trim();
|
||
const val = parseFloat(cleaned);
|
||
return isNaN(val) ? 0 : val;
|
||
}
|
||
|
||
function formatNumber(val, decimals) {
|
||
if (isNaN(val)) val = 0;
|
||
return val.toFixed(decimals);
|
||
}
|
||
|
||
function recalcRow(row) {
|
||
const inputs = row.querySelectorAll('.cm-cell-input');
|
||
|
||
let ctn = 0, qty = 0, ttlQty = 0;
|
||
let cbm = 0, ttlCbm = 0;
|
||
let kg = 0, ttlKg = 0;
|
||
let price = 0, amount = 0;
|
||
|
||
let ctnInput = null,
|
||
qtyInput = null,
|
||
ttlQtyInput = null,
|
||
cbmInput = null,
|
||
ttlCbmInput = null,
|
||
kgInput = null,
|
||
ttlKgInput = null,
|
||
priceInput = null,
|
||
amountInput = null;
|
||
|
||
inputs.forEach(inp => {
|
||
const val = parseNumber(inp.value);
|
||
|
||
if (inp.dataset.ctn === '1') {
|
||
ctn = val;
|
||
ctnInput = inp;
|
||
} else if (inp.dataset.qty === '1') {
|
||
qty = val;
|
||
qtyInput = inp;
|
||
} else if (inp.dataset.ttlqty === '1') {
|
||
ttlQty = val;
|
||
ttlQtyInput = inp;
|
||
} else if (inp.dataset.cbm === '1') {
|
||
cbm = val;
|
||
cbmInput = inp;
|
||
} else if (inp.dataset.ttlcbm === '1') {
|
||
ttlCbm = val;
|
||
ttlCbmInput = inp;
|
||
} else if (inp.dataset.kg === '1') {
|
||
kg = val;
|
||
kgInput = inp;
|
||
} else if (inp.dataset.ttlkg === '1') {
|
||
ttlKg = val;
|
||
ttlKgInput = inp;
|
||
} else if (inp.dataset.price === '1') {
|
||
price = val;
|
||
priceInput = inp;
|
||
} else if (inp.dataset.amount === '1') {
|
||
amount = val;
|
||
amountInput = inp;
|
||
}
|
||
});
|
||
|
||
if (ttlQtyInput && ctnInput && qtyInput) {
|
||
const newTtlQty = ctn * qty;
|
||
ttlQtyInput.value = formatNumber(newTtlQty, 0);
|
||
ttlQty = newTtlQty;
|
||
}
|
||
|
||
if (ttlCbmInput && cbmInput && ctnInput) {
|
||
const newTtlCbm = cbm * ctn;
|
||
ttlCbmInput.value = formatNumber(newTtlCbm, 3);
|
||
ttlCbm = newTtlCbm;
|
||
}
|
||
|
||
if (ttlKgInput && kgInput && ctnInput) {
|
||
const newTtlKg = kg * ctn;
|
||
ttlKgInput.value = formatNumber(newTtlKg, 2);
|
||
ttlKg = newTtlKg;
|
||
}
|
||
|
||
if (amountInput && priceInput && ttlQtyInput) {
|
||
const newAmount = price * ttlQty;
|
||
amountInput.value = formatNumber(newAmount, 2);
|
||
amount = newAmount;
|
||
}
|
||
}
|
||
|
||
if (table) {
|
||
table.addEventListener('input', function (e) {
|
||
const target = e.target;
|
||
if (!target.classList.contains('cm-cell-input')) return;
|
||
|
||
if (target.classList.contains('cm-cell-readonly') || target.hasAttribute('readonly')) {
|
||
target.blur();
|
||
return;
|
||
}
|
||
|
||
const row = target.closest('tr');
|
||
if (row) {
|
||
recalcRow(row);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (form && btn) {
|
||
btn.addEventListener('click', function () {
|
||
btn.classList.add('cm-disabled');
|
||
const formData = new FormData(form);
|
||
|
||
fetch(form.action, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
'X-CSRF-TOKEN': document.querySelector('input[name="_token"]').value
|
||
},
|
||
body: formData
|
||
})
|
||
.then(async res => {
|
||
if (!res.ok) {
|
||
const text = await res.text();
|
||
throw new Error(text || 'Failed to save');
|
||
}
|
||
return res.json().catch(() => ({}));
|
||
})
|
||
.then(() => {
|
||
showToast('Changes saved successfully.');
|
||
})
|
||
.catch(() => {
|
||
showToast('Error while saving changes.', true);
|
||
})
|
||
.finally(() => {
|
||
btn.classList.remove('cm-disabled');
|
||
});
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
@endsection
|