Files
Kent-logistics-Laravel/resources/views/admin/container_show.blade.php

905 lines
30 KiB
PHP
Raw Normal View History

2026-02-27 10:51:26 +05:30
@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');
2026-03-09 10:24:44 +05:30
html, body {
margin: 0;
padding: 0;
width: 100%;
overflow-x: hidden;
}
2026-02-27 10:51:26 +05:30
.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;
2026-03-09 10:24:44 +05:30
position: relative;
2026-02-27 10:51:26 +05:30
}
.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;
}
2026-03-09 10:24:44 +05:30
.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;
2026-02-27 10:51:26 +05:30
display: flex;
2026-03-09 10:24:44 +05:30
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;
2026-02-27 10:51:26 +05:30
}
2026-03-09 10:24:44 +05:30
.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 {
2026-02-27 10:51:26 +05:30
font-size: 11px;
text-transform: uppercase;
2026-03-09 10:24:44 +05:30
letter-spacing: 0.4px;
color: #64748b;
font-weight: 600;
2026-02-27 10:51:26 +05:30
}
2026-03-09 10:24:44 +05:30
.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;
2026-02-27 10:51:26 +05:30
font-weight: 600;
2026-03-09 10:24:44 +05:30
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;
2026-02-27 10:51:26 +05:30
}
.cm-filter-bar {
2026-03-09 10:24:44 +05:30
padding: 4px 20px 0 20px;
2026-02-27 10:51:26 +05:30
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;
2026-03-09 10:24:44 +05:30
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;
2026-02-27 10:51:26 +05:30
}
.cm-table-wrapper {
position: relative;
2026-03-09 10:24:44 +05:30
min-width: 1200px;
2026-02-27 10:51:26 +05:30
}
.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;
2026-03-09 10:24:44 +05:30
background: linear-gradient(100deg, #fde4b3 0%, #fbd48a 100%);
color: #0c0909;
2026-02-27 10:51:26 +05:30
font-weight: 700;
font-size: 12px;
padding: 11px 14px;
2026-03-09 10:24:44 +05:30
border-bottom: 2px solid rgba(255,255,255,0.15);
border-right: 1px solid rgba(255,255,255,0.18);
2026-02-27 10:51:26 +05:30
white-space: nowrap;
text-align: center;
letter-spacing: 0.2px;
2026-03-09 10:24:44 +05:30
text-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
.cm-table thead tr th:first-child {
border-top-left-radius: 10px;
2026-02-27 10:51:26 +05:30
}
.cm-table thead tr th:last-child {
2026-03-09 10:24:44 +05:30
border-top-right-radius: 10px;
2026-02-27 10:51:26 +05:30
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;
2026-03-09 10:24:44 +05:30
white-space: nowrap;
2026-02-27 10:51:26 +05:30
}
.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);
}
2026-03-09 10:24:44 +05:30
.cm-cell-readonly {
background: #f3f4ff;
cursor: not-allowed;
border-color: #e2e3ff;
}
2026-02-27 10:51:26 +05:30
2026-03-09 10:24:44 +05:30
.cm-save-btn-floating {
position: fixed;
right: 26px;
bottom: 22px;
z-index: 50;
font-size: 13px;
2026-02-27 10:51:26 +05:30
font-weight: 600;
2026-03-09 10:24:44 +05:30
border-radius: 22px;
padding: 8px 20px;
2026-02-27 10:51:26 +05:30
background: linear-gradient(90deg, #4c6fff, #8e54e9);
border: none;
color: #fff;
2026-03-09 10:24:44 +05:30
box-shadow: 0 4px 16px rgba(76,111,255,0.35);
2026-02-27 10:51:26 +05:30
transition: opacity 0.2s, transform 0.15s;
cursor: pointer;
}
2026-03-09 10:24:44 +05:30
.cm-save-btn-floating:hover {
opacity: 0.94;
2026-02-27 10:51:26 +05:30
transform: translateY(-1px);
}
2026-03-09 10:24:44 +05:30
.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);
}
2026-02-27 10:51:26 +05:30
.cm-empty {
padding: 30px 20px;
color: #8a93a6;
font-size: 13px;
}
2026-03-09 10:24:44 +05:30
.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;
}
2026-02-27 10:51:26 +05:30
2026-03-09 10:24:44 +05:30
@media (max-width: 991px) {
.cm-total-cards-row {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
2026-02-27 10:51:26 +05:30
@media (max-width: 767px) {
2026-03-09 10:24:44 +05:30
.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;
}
2026-02-27 10:51:26 +05:30
}
</style>
<div class="container-fluid cm-wrapper">
<div class="card cm-header-card">
<div class="card-body">
<div>
2026-03-09 10:24:44 +05:30
<h4 class="cm-header-title">Container {{ $container->container_number }}</h4>
2026-02-27 10:51:26 +05:30
<div class="cm-header-sub">
2026-03-09 10:24:44 +05:30
Edit loading list directly like Excel. TT columns autocalculate from CTN, QTY, CBM, KG, PRICE.
2026-02-27 10:51:26 +05:30
</div>
</div>
2026-03-09 10:24:44 +05:30
<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>
2026-02-27 10:51:26 +05:30
</div>
</div>
<div class="card cm-main-card">
<div class="card-header">
<h5>Container Information</h5>
</div>
2026-03-09 10:24:44 +05:30
{{-- 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>
2026-02-27 10:51:26 +05:30
</div>
2026-03-09 10:24:44 +05:30
<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>
2026-02-27 10:51:26 +05:30
</div>
2026-03-09 10:24:44 +05:30
<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
2026-02-27 10:51:26 +05:30
</div>
2026-03-09 10:24:44 +05:30
</div>
2026-02-27 10:51:26 +05:30
</div>
</div>
2026-03-09 10:24:44 +05:30
@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
2026-02-27 10:51:26 +05:30
@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">
2026-03-09 10:24:44 +05:30
Total rows: {{ $container->rows->count() }} &nbsp;&nbsp; Edit cells then click "Save Changes".
2026-02-27 10:51:26 +05:30
</span>
2026-03-09 10:24:44 +05:30
<input type="text" id="cmRowSearch" class="cm-filter-input"
placeholder="Quick search..." onkeyup="cmFilterRows()">
2026-02-27 10:51:26 +05:30
</div>
2026-03-09 10:24:44 +05:30
<form id="cm-edit-rows-form" action="{{ route('containers.rows.update', $container->id) }}" method="POST">
2026-02-27 10:51:26 +05:30
@csrf
<div class="cm-table-scroll-outer">
2026-03-09 10:24:44 +05:30
<div class="cm-table-wrapper">
<table class="cm-table" id="cmExcelTable">
<thead>
2026-02-27 10:51:26 +05:30
<tr>
<th>#</th>
@foreach($allHeadings as $heading)
<th>{{ $heading }}</th>
@endforeach
</tr>
2026-03-09 10:24:44 +05:30
</thead>
<tbody>
2026-02-27 10:51:26 +05:30
@foreach($container->rows as $index => $row)
<tr>
<td class="cm-row-num">{{ $index + 1 }}</td>
@foreach($allHeadings as $heading)
2026-03-09 10:24:44 +05:30
@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;
2026-03-12 12:34:27 +05:30
// जर container pending नसेल तर सर्व fields readonly
$isReadOnly = $isTotalColumn || $container->status !== 'pending';
2026-03-09 10:24:44 +05:30
@endphp
2026-02-27 10:51:26 +05:30
<td>
2026-03-09 10:24:44 +05:30
<input
type="text"
2026-03-12 12:34:27 +05:30
class="cm-cell-input {{ $isReadOnly ? 'cm-cell-readonly' : '' }}"
2026-03-09 10:24:44 +05:30
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' }}"
2026-03-12 12:34:27 +05:30
{{ $isReadOnly ? 'readonly' : '' }}
2026-03-09 10:24:44 +05:30
>
2026-02-27 10:51:26 +05:30
</td>
@endforeach
2026-03-12 12:34:27 +05:30
2026-02-27 10:51:26 +05:30
</tr>
@endforeach
2026-03-09 10:24:44 +05:30
</tbody>
</table>
</div>
2026-02-27 10:51:26 +05:30
</div>
</form>
@endif
</div>
</div>
2026-03-09 10:24:44 +05:30
@if(!$container->rows->isEmpty())
2026-03-12 12:34:27 +05:30
<button
id="cmSaveBtnFloating"
class="cm-save-btn-floating {{ $container->status !== 'pending' ? 'cm-disabled' : '' }}"
{{ $container->status !== 'pending' ? 'disabled' : '' }}
>
Save Changes
</button>
2026-03-09 10:24:44 +05:30
@endif
2026-03-12 12:34:27 +05:30
2026-02-27 10:51:26 +05:30
<script>
2026-03-12 12:34:27 +05:30
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;
2026-02-27 10:51:26 +05:30
}
}
2026-03-12 12:34:27 +05:30
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;
}
});
2026-03-09 10:24:44 +05:30
2026-03-12 12:34:27 +05:30
if (ttlQtyInput && ctnInput && qtyInput) {
const newTtlQty = ctn * qty;
ttlQtyInput.value = formatNumber(newTtlQty, 0);
ttlQty = newTtlQty;
2026-03-09 10:24:44 +05:30
}
2026-03-12 12:34:27 +05:30
if (ttlCbmInput && cbmInput && ctnInput) {
const newTtlCbm = cbm * ctn;
ttlCbmInput.value = formatNumber(newTtlCbm, 3);
ttlCbm = newTtlCbm;
2026-03-09 10:24:44 +05:30
}
2026-03-12 12:34:27 +05:30
if (ttlKgInput && kgInput && ctnInput) {
const newTtlKg = kg * ctn;
ttlKgInput.value = formatNumber(newTtlKg, 2);
ttlKg = newTtlKg;
2026-03-09 10:24:44 +05:30
}
2026-03-12 12:34:27 +05:30
if (amountInput && priceInput && ttlQtyInput) {
const newAmount = price * ttlQty;
amountInput.value = formatNumber(newAmount, 2);
amount = newAmount;
}
}
2026-03-09 10:24:44 +05:30
2026-03-12 12:34:27 +05:30
if (table) {
table.addEventListener('input', function (e) {
const target = e.target;
if (!target.classList.contains('cm-cell-input')) return;
2026-03-09 10:24:44 +05:30
2026-03-12 12:34:27 +05:30
// readonly / non-pending cells साठी block
if (target.classList.contains('cm-cell-readonly') || target.hasAttribute('readonly')) {
target.blur();
return;
2026-03-09 10:24:44 +05:30
}
2026-03-12 12:34:27 +05:30
const row = target.closest('tr');
if (row) {
recalcRow(row);
2026-03-09 10:24:44 +05:30
}
2026-03-12 12:34:27 +05:30
});
}
2026-03-09 10:24:44 +05:30
2026-03-12 12:34:27 +05:30
if (form && btn) {
btn.addEventListener('click', function () {
// जर बटण आधीच disabled असेल (non-pending status किंवा processing)
if (btn.classList.contains('cm-disabled') || btn.hasAttribute('disabled')) {
return;
2026-03-09 10:24:44 +05:30
}
2026-03-12 12:34:27 +05:30
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(() => ({}));
2026-03-09 10:24:44 +05:30
})
2026-03-12 12:34:27 +05:30
.then(() => {
showToast('Changes saved successfully.');
})
.catch(() => {
showToast('Error while saving changes.', true);
})
.finally(() => {
btn.classList.remove('cm-disabled');
});
});
}
});
2026-02-27 10:51:26 +05:30
</script>
2026-03-12 12:34:27 +05:30
2026-02-27 10:51:26 +05:30
@endsection