Files
Kent-logistics-Laravel/resources/views/admin/account.blade.php
Utkarsh Khedkar 82d9c10130 Dashboard Changes
2025-12-25 10:22:20 +05:30

3473 lines
107 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.

@extends('admin.layouts.app')
@section('page-title', 'Account Dashboard')
@section('content')
<meta name="csrf-token" content="{{ csrf_token() }}">
<style>
/* ---------- Base ---------- */
:root{
--primary-1:#1a2951;
--primary-2:#243a72;
--muted:#9ba5bb;
--card-bg: linear-gradient(180deg,#ffffff,#f5f7fb);
--glass: rgba(255,255,255,0.6);
--success:#34c86c;
--danger:#ef4f4f;
--accent:#276dea;
--rounded:12px;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #eef2f7, #f9fbff);
margin:0; padding:0;
color:#253047;
-webkit-font-smoothing:antialiased;
}
/* container */
.account-container {
padding: 28px 34px;
max-width:1400px;
margin: 18px auto;
box-sizing:border-box;
}
/* header */
.account-header {
margin-bottom: 18px;
background: linear-gradient(90deg,var(--primary-1),var(--primary-2));
padding: 22px 26px;
border-radius: var(--rounded);
box-shadow: 0 6px 18px rgba(34,50,90,0.12);
color: #fff;
}
.account-header h2 { font-size: 26px; font-weight:700; margin:0 0 6px 0; }
.account-header p { font-size:14px; margin:2px 0; opacity:0.95 }
/* top actions row */
.top-actions {
align-items:center; justify-content:space-between;
gap:12px; margin:16px 0 20px 0; flex-wrap:wrap;
}
.top-actions .left {
display:flex; gap:12px; align-items:center; flex-wrap:wrap;
}
.search-row input {
width:360px; padding:10px 14px; border-radius:8px; border:1px solid #d6dde9;
background: #fff; font-size:14px; box-shadow:0 1px 3px rgba(0,0,0,0.03);
}
.search-row input:focus{ outline:none; border-color:var(--primary-1); box-shadow:0 6px 18px rgba(26,41,81,0.06);}
.btn {
display:inline-flex; align-items:center; justify-content:center; gap:8px;
background: linear-gradient(90deg,var(--primary-1),var(--primary-2));
color:#fff; border:none; padding:10px 16px; border-radius:10px; font-weight:600;
cursor:pointer; transition: transform .15s ease, box-shadow .15s;
}
.btn.ghost { background: transparent; color:var(--primary-1); border:1.5px solid #dbe4f5; box-shadow:none; }
.btn:hover{ transform: translateY(-3px); box-shadow: 0 8px 26px rgba(227, 229, 234, 0.12); }
/* account panels */
.account-panels {
display:flex;
gap:22px;
align-items:stretch;
flex-wrap:wrap;
}
/* Payment block with pagination */
.payment-block {
flex:1;
min-width:48%;
display: flex;
flex-direction: column;
}
.panel-card {
background: var(--card-bg);
border-radius:12px;
box-shadow:0 8px 20px rgba(25,40,80,0.06);
padding:20px; /* 005 */
box-sizing:border-box;
overflow-x:auto;
transition: transform .12s, box-shadow .12s;
min-height: 520px;
/* display: flex; */ /* 005 */
flex-direction: column;
flex: 1;
height: 100%;
}
.panel-card:hover{ transform: translateY(-4px); box-shadow:0 12px 28px rgba(25,40,80,0.08); }
.panel-title {
font-weight:700;
font-size:16px;
color:var(--primary-1);
margin-bottom:16px;
display:flex;
align-items:center;
justify-content:space-between;
}
/* table */
table {
width:100%;
border-collapse:collapse;
min-width:720px;
font-size:14px;
flex:1;
}
th, td {
padding:12px 14px;
text-align:left;
border-bottom:1px solid #eef3fb;
white-space:nowrap;
color:#2d3b53;
}
th {
background: linear-gradient(90deg,#f6f9ff,#fbfdff);
color:#4a5570;
font-weight:700;
font-size:13px;
}
tr:hover td{ background:#fbfdff; }
.entry-link{ color:var(--accent); text-decoration:underline; cursor:pointer; font-weight:700; }
/* badges */
/* === Modern Status Badges === */
.status-badge {
display: inline-block;
padding: 6px 16px;
border-radius: 999px;
font-size: 13px;
font-weight: 700;
color: #fff;
min-width: 76px;
text-align: center;
box-shadow: 0 2px 8px rgba(33, 43, 90, 0.07);
letter-spacing: 0.1px;
background: #6b7280;
transition: box-shadow 0.22s, transform 0.17s, background 0.22s;
vertical-align: middle;
margin: 3px 2px;
cursor: default;
backdrop-filter: blur(2px);
width: 99px;
}
.status-badge:hover {
transform: translateY(-3px) scale(1.045);
box-shadow: 0 4px 16px rgba(33, 43, 90, 0.16);
opacity: 0.96;
}
/* High-impact, soft gradients for each status */
.status-unpaid {
background: linear-gradient(90deg, #ff5959, #dc3545);
border: 1.5px solid #d42c3f21;
width: 95px;
}
.status-paid {
background: linear-gradient(90deg, #36d399 0%, #4ade80 100%);
border: 1.5px solid #31b47a1a;
width: 95px;
}
.status-loading {
background: linear-gradient(90deg, #509cf8 0%, #3f79d3 100%);
border: 1.5px solid #1665c320;
width: 95px;
}
.status-dispatched {
background: linear-gradient(90deg, #9775fa 0%, #845ef7 100%);
border: 1.5px solid #9775fa40;
width: 95px;
}
.pending-badge-red {
background: linear-gradient(90deg, #f43f5e, #ef4444);
border: 1.5px solid #f43f5e41;
width: 95px;
}
.pending-badge-green {
background: linear-gradient(90deg, #10b981 0%, #22d3ee 100%);
border: 1.5px solid #10b98141;
width: 95px;
}
.status-delivered {
background: linear-gradient(90deg, #22c55e 0%, #16a34a 100%);
border: 1.5px solid #22c55e44;
width: 95px;
}
/* 3-state toggle */
.toggle-switch-btn {
appearance:none;
-webkit-appearance:none;
width:64px;
height:26.5px; /* 005 */
background:#f25b5b;
border:2px solid #f25b5b;
border-radius:999px;
position:relative;
outline:none;
cursor:pointer;
transition: background .22s, border .22s;
}
.toggle-switch-btn::before{
content:"";
width:20px;
height:20px;
background:#fff;
border-radius:50%;
position:absolute;
top:1.7px;
left:2px;
transition: transform .22s;
box-shadow:0 2px 8px rgba(0,0,0,0.15);
}
/* YELLOW */
.toggle-switch-btn.mid {
background:#f2c94c;
border-color:#f2c94c;
}
.toggle-switch-btn.mid::before {
transform: translateX(20px);
}
/* GREEN */
.toggle-switch-btn.checked {
background:#43d05c;
border-color:#43d05c;
}
.toggle-switch-btn.checked::before {
transform: translateX(38px);
}
/* plus button */
.plus-btn {
display:inline-block;
width:36px;
height:36px;
border-radius:10px;
background:#fff;
color:var(--primary-1);
border:1.5px solid #e6edf8;
font-size:1.15rem;
font-weight:700;
text-align:center;
line-height:34px;
cursor:pointer;
transition: transform .12s;
}
.plus-btn:hover{ transform: translateY(-3px); box-shadow:0 8px 16px rgba(33,47,90,0.04); }
/* ---------- Create Order Popup Modal ---------- */
.create-order-modal {
position:fixed;
inset:0;
background:rgba(16,24,50,0.44);
display:none;
align-items:center;
justify-content:center;
z-index:1200;
padding:18px;
}
.create-order-modal.modal-open { display:flex; }
.create-order-modal .modal-box {
background:#fff;
border-radius:12px;
padding:20px 24px;
box-shadow:0 14px 40px rgba(18,30,60,0.12);
max-width:1200px;
width:100%;
max-height:92vh;
overflow:auto;
}
/* consolidated orders area */
.consolidate-area {
background: linear-gradient(90deg,#fbfbff,#f9fcff);
border:1px solid #eef5ff;
padding:14px;
border-radius:10px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
margin-top:12px;
overflow:auto;
min-height: 400px;
display: flex;
flex-direction: column;
}
/* compact table in consolidate */
#consolidateOrdersTable th, #consolidateOrdersTable td { padding:10px 12px; font-size:13px; border-bottom:1px solid #f1f6ff; }
/* ---------- Pagination Styles ---------- */
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
padding: 12px 0;
border-top: 1px solid #eef3fb;
/* margin-right:550px; */
}
.pagination-info {
font-size: 13px;
color: var(--muted);
font-weight: 600;
}
.pagination-controls {
display: flex;
align-items: center;
gap: 8px;
position: absolute;
right: 16px; /* 005 */
}
.pagination-controls1 {
display: flex;
align-items: center;
gap: 8px;
/* margin-right:-550px; */
}
.pagination-controls2 {
display: flex;
align-items: center;
gap: 8px;
margin-right:-550px;
}
.pagination-btn {
background: #fff;
border: 1px solid #e3eaf6;
color: var(--primary-1);
padding: 8px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
min-width: 40px;
height: 32px;
}
.pagination-btn:hover:not(:disabled) {
background: var(--primary-1);
color: white;
border-color: var(--primary-1);
}
.pagination-btn:disabled {
background: #f8fafc;
color: #cbd5e0;
border-color: #e2e8f0;
cursor: not-allowed;
opacity: 0.6;
}
.pagination-page-btn {
background: #fff;
border: 1px solid #e3eaf6;
color: var(--primary-1);
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
min-width: 36px;
text-align: center;
}
.pagination-page-btn:hover {
background: var(--primary-1);
color: white;
border-color: var(--primary-1);
}
.pagination-page-btn.active {
background: var(--primary-1);
color: white;
border-color: var(--primary-1);
}
.pagination-pages {
display: flex;
gap: 4px;
align-items: center;
}
.pagination-ellipsis {
color: var(--muted);
font-size: 13px;
padding: 0 4px;
}
/* Image-based pagination buttons */
.pagination-img-btn {
background: #fff;
border: 1px solid #e3eaf6;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
min-width: 40px;
height: 32px;
padding: 0;
}
.pagination-img-btn:hover:not(:disabled) {
background: var(--primary-1);
border-color: var(--primary-1);
}
.pagination-img-btn:disabled {
background: #f8fafc;
border-color: #e2e8f0;
cursor: not-allowed;
opacity: 0.5;
}
.pagination-img-btn img {
width: 16px;
height: 16px;
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
transition: filter 0.3s ease;
}
.pagination-img-btn:hover:not(:disabled) img {
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
}
.pagination-img-btn:disabled img {
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
}
/* ---------- Entry Details Modal (Enhanced) ---------- */
.modal-fade1 {
position:fixed;
inset:0;
background:rgba(16,24,50,0.44);
display:none;
align-items:center;
justify-content:center;
z-index:1200;
padding:18px;
}
.modal-fade1.modal-open { display:flex; }
.modal-box1 {
background:#fff;
border-radius:12px;
padding:20px 24px;
box-shadow:0 14px 40px rgba(18,30,60,0.12);
max-width:1200px;
width:100%;
max-height:92vh;
overflow:auto;
min-height: 500px;
}
#entryOrdersModal {
z-index: 1300;
}
#entryOrdersModal {
z-index: 1300;
}
/* entry summary cards */
.entry-summary-cards {
display:flex;
gap:16px;
margin-bottom:20px;
flex-wrap:wrap;
}
.entry-summary-card {
background:#fbfdff;
border:1px solid #eef6ff;
padding:16px;
border-radius:10px;
min-width:180px;
box-shadow:0 6px 18px rgba(22,36,72,0.03);
flex:1;
}
.entry-summary-label{ font-size:12px; color:var(--muted); }
.entry-summary-value{ font-size:18px; font-weight:700; color:#253047; margin-top:6px; }
/* Enhanced dropdown */
.installment-status-dropdown {
cursor: pointer;
appearance: none;
width: 100%;
padding: 10px 40px 10px 12px;
border-radius: 8px;
border: 1.5px solid #e3eaf6;
background: white;
font-size: 13px;
font-weight: 500;
transition: all 0.2s ease;
background-image: url('data:image/svg+xml;charset=US-ASCII,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%236b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>');
background-repeat: no-repeat;
background-position: right 12px center;
min-width: 140px;
}
.installment-status-dropdown:hover {
border-color: #c2d1f0;
background-color: #f8fafc;
}
.installment-status-dropdown:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(39, 109, 234, 0.1);
}
/* Status-specific dropdown options */
.installment-status-dropdown option {
padding: 12px !important;
font-size: 13px;
font-weight: 500;
}
.installment-status-dropdown option[value="Pending"] {
color: #f59e0b;
}
.installment-status-dropdown option[value="Loading"] {
color: #3b82f6;
}
.installment-status-dropdown option[value="Packed"] {
color: #8b5cf6;
}
.installment-status-dropdown option[value="Dispatched"] {
color: #10b981;
}
.installment-status-dropdown option[value="Delivered"] {
color: #0c6b2e;
}
/* ---------- Entry Orders Modal (Enhanced) ---------- */
.entry-orders-modal .modal-box1 {
padding: 0 !important;
overflow: hidden;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(18, 30, 60, 0.25);
}
.entry-orders-header {
background: linear-gradient(90deg, var(--primary-1), var(--primary-2));
padding: 24px 30px;
color: white;
border-radius: 16px 16px 0 0;
}
.entry-orders-header h2 {
margin: 0 0 8px 0;
font-size: 24px;
font-weight: 700;
color: white;
}
.entry-orders-header .subtitle {
font-size: 14px;
opacity: 0.9;
display: flex;
align-items: center;
gap: 8px;
}
.entry-orders-content {
padding: 30px;
max-height: 70vh;
overflow-y: auto;
}
/* Orders table in modal */
.orders-table-container {
max-height: 400px;
overflow-y: auto;
border: 1px solid #eef3fb;
border-radius: 12px;
margin: 20px 0;
}
.orders-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
.orders-table th {
background: linear-gradient(90deg, #f8fbff, #f5f9ff);
padding: 14px 16px;
text-align: left;
font-weight: 700;
color: var(--primary-1);
border-bottom: 2px solid #eef3fb;
position: sticky;
top: 0;
}
.orders-table td {
padding: 14px 16px;
border-bottom: 1px solid #f1f6ff;
}
.orders-table tr:hover {
background: #fbfdff;
}
.orders-table tr:last-child td {
border-bottom: none;
}
/* Order ID with badge style */
.order-id-badge {
display: inline-block;
background: linear-gradient(90deg, #f0f7ff, #e6f0ff);
color: var(--primary-1);
padding: 4px 10px;
border-radius: 6px;
font-weight: 600;
font-size: 12px;
border: 1px solid #dbe4f5;
}
/* Summary row */
.orders-summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
background: linear-gradient(90deg, #f8fbff, #f5f9ff);
border-radius: 10px;
margin-top: 20px;
border: 1px solid #eef3fb;
}
.orders-summary-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.orders-summary-value {
font-size: 20px;
font-weight: 800;
color: var(--primary-1);
}
.orders-summary-label {
font-size: 12px;
color: var(--muted);
margin-top: 4px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* installment modal */
#installmentModal .modal-box1 { max-width:720px; min-width:380px; }
/* small helpers */
.helper-note{ font-size:12px; color:#7687a3; margin-top:6px; display:block; }
.empty-state{ padding:18px; text-align:center; color:#6f7b8f; }
.kv { font-weight:700; color:#26364f; }
/* form styles */
.input, select {
width:100%;
padding:12px 14px;
border-radius:8px;
border:1.3px solid #e3eaf6;
background:#fff;
font-size:14px;
box-sizing:border-box;
}
/* Additional styling for the installment dropdown - doesn't affect original class */
.installment-status-dropdown {
cursor: pointer;
appearance: none;
background-image: url('data:image/svg+xml;charset=US-ASCII,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%236b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>');
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 40px;
transition: all 0.2s ease;
}
.installment-status-dropdown:hover {
border-color: #c2d1f0;
}
.installment-status-dropdown:focus {
outline: none;
border-color: #4d7cfe;
box-shadow: 0 0 0 3px rgba(77, 124, 254, 0.1);
}
/* Status-specific styling for selected option */
.installment-status-dropdown option:checked {
font-weight: 600;
}
.create-grid {
display:grid;
grid-template-columns: 1fr 1fr;
gap:16px;
align-items:start;
margin-bottom:16px;
}
.create-actions {
display:flex;
gap:12px;
justify-content:flex-end;
margin-top:16px;
}
/* Combined filters row styling */
.combined-filters-row {
display: ruby; /* 005 */
gap: 12px;
align-items: center;
margin-bottom: 16px;
flex-wrap: wrap;
padding: 14px 16px;
background: linear-gradient(90deg, #f8fbff, #f5f9ff);
border-radius: 10px;
border: 1px solid #eef3fb;
width: 365px;
}
.right{
/* margin-left:auto;
margin-top:-16px; */ /* 005 */
}
.filter-group1 {
display: flex;
flex-direction: column;
gap: 6px;
width: 159px;
}
.filter-group2 {
display: flex;
flex-direction: column;
gap: 6px;
width: 160px;
}
.filter-group3 {
flex: 1;
width: 159px;
}
.filter-label {
display: block;
font-size: 13px;
font-weight: 600;
color: #44546a;
margin-bottom: 6px;
}
.payment-filters-section {
display: flex;
align-items: flex-end;
gap: 16px;
}
.filter-group4 {
display: flex;
flex-direction: column;
gap: 6px;
width: 160px;
}
.filter-label {
display: block;
font-size: 13px;
font-weight: 600;
color: #44546a;
margin-bottom: 2px;
}
/* Status option colors */
.filter-control option {
padding: 10px 12px;
font-size: 14px;
}
.filter-control option[value=""] {
color: #6b778c;
}
.filter-control option[value="pending"] {
color: #f59e0b;
font-weight: 500;
}
.filter-control option[value="loading"] {
color: #3b82f6;
font-weight: 500;
}
.filter-control option[value="packed"] {
color: #8b5cf6;
font-weight: 500;
}
.filter-control option[value="dispatched"] {
color: #10b981;
font-weight: 500;
}
.filter-control option[value="delivered"] {
color: #0c6b2e;
font-weight: 500;
}
.status-color {
width: 12px;
height: 12px;
border-radius: 50%;
}
.status-color.pending { background-color: #f59e0b; }
.status-color.loading { background-color: #3b82f6; }
.status-color.packed { background-color: #8b5cf6; }
.status-color.dispatched { background-color: #10b981; }
.status-color.delivered { background-color: #0c6b2e; }
/* Style for options with icons */
.filter-control option {
padding: 8px 12px;
}
.filter-control option[value=""] {
color: #6b778c;
}
.filter-control option[value="paid"] {
color: #0c6b2e;
font-weight: 500;
}
.filter-control option[value="unpaid"] {
color: #c9372c;
font-weight: 500;
}
.filter-control option[value="pending"] {
color: #f59e0b;
font-weight: 500;
}
.filter-group4 {
display: flex;
flex-direction: column;
gap: 6px;
width: 510px;
}
/* Modern filter alternative with icons */
.filter-status-buttons {
display: flex;
gap: 8px;
margin-top: 8px;
}
.status-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #e3eaf6;
background: #fff;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.status-btn:hover {
background-color: #f8fafc;
transform: translateY(-1px);
}
.status-btn.active {
border-color: transparent;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}
.status-btn[data-status="paid"] {
color: #0c6b2e;
background-color: #f0f9f0;
}
.status-btn[data-status="paid"].active {
background-color: #e1f7e1;
}
.status-btn[data-status="paid"] i {
color: #0c6b2e;
}
.status-btn[data-status="unpaid"] {
color: #c9372c;
background-color: #fef0f0;
}
.status-btn[data-status="unpaid"].active {
background-color: #fde8e8;
}
.status-btn[data-status="unpaid"] i {
color: #c9372c;
}
.status-btn[data-status="pending"] {
color: #f59e0b;
background-color: #fff7e6;
}
.status-btn[data-status="pending"].active {
background-color: #fff0cc;
}
.status-btn[data-status="pending"] i {
color: #f59e0b;
}
.status-btn[data-status="all"] {
color: #44546a;
background-color: #f8fafc;
}
.status-btn[data-status="all"].active {
background-color: #f1f5f9;
}
.status-btn[data-status="all"] i {
color: #44546a;
}
.filter-label {
font-size: 13px;
font-weight: 600;
color: var(--primary-1);
}
.filter-control {
padding: 8px 12px;
border-radius: 8px;
border: 1px solid #e3eaf6;
background: #fff;
font-size: 14px;
min-width: 140px;
}
.filter-control:focus {
outline: none;
border-color: var(--primary-1);
box-shadow: 0 0 0 2px rgba(26, 41, 81, 0.1);
}
/* Table-specific filter sections */
.payment-filters-section, .order-filters-section {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 16px;
padding: 12px 14px;
background: linear-gradient(90deg, #f9fbff, #f7faff);
border-radius: 8px;
border: 1px solid #eef3fb;
}
/* Action buttons in table */
.action-btn {
background: transparent;
border: none;
cursor: pointer;
padding: 6px 8px;
border-radius: 6px;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
}
.edit-btn {
color: var(--primary-1);
}
.edit-btn:hover {
background: rgba(26, 41, 81, 0.08);
}
.delete-btn {
color: var(--danger);
}
.delete-btn:hover {
background: rgba(239, 79, 79, 0.08);
}
.action-btns {
display: flex;
gap: 4px;
justify-content: center;
}
/* Edit modal styles */
.edit-modal .modal-box1 {
max-width: 800px;
z-index: 1250;
}
.edit-form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 16px;
}
.edit-form-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
margin-top: 20px;
}
/* Order management in edit modal */
.order-management-section {
margin-top: 20px;
border-top: 1px solid #eef3fb;
padding-top: 16px;
}
.order-management-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.order-management-title {
font-weight: 700;
color: var(--primary-1);
font-size: 16px;
}
.orders-table-container {
max-height: 300px;
overflow-y: auto;
border: 1px solid #eef3fb;
border-radius: 8px;
margin-bottom: 12px;
}
.orders-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.orders-table th {
background: #f8fbff;
padding: 10px 12px;
text-align: left;
font-weight: 600;
color: var(--primary-1);
border-bottom: 1px solid #eef3fb;
position: sticky;
top: 0;
}
.orders-table td {
padding: 10px 12px;
border-bottom: 1px solid #f1f6ff;
}
.order-actions {
display: flex;
gap: 6px;
}
.remove-order-btn {
background: var(--danger);
color: white;
border: none;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
transition: background 0.2s;
}
.combined-top-row .btn:hover {
color: #ffffff !important;
background-color: inherit !important;
border-color: inherit !important;
transform: none !important;
box-shadow: none !important;
}
.remove-order-btn:hover {
background: #d42c3f;
}
/* Add Order Modal */
.add-order-modal .modal-box1 {
max-width: 1000px;
}
.add-order-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 16px;
}
/* responsive */
@media (max-width:980px){
.create-grid { grid-template-columns: 1fr; }
.panel-card { min-width:100%; }
.search-row input{ width:220px; }
.pagination-container { flex-direction: column; gap: 10px; align-items: stretch; }
.pagination-controls { justify-content: center; }
.account-container { padding: 20px; }
.panel-card { padding: 18px; }
.combined-filters-row { flex-direction: column; align-items: stretch; }
.filter-group { width: 100%; }
.filter-control { min-width: auto; }
.edit-form-grid { grid-template-columns: 1fr; }
.payment-filters-section, .order-filters-section { flex-direction: column; align-items: stretch; }
}
@media (max-width:768px){
.account-header { padding: 18px; }
.top-actions { flex-direction: column; align-items: stretch; }
.top-actions .left { justify-content: center; }
.entry-summary-cards { gap: 12px; }
.entry-summary-card { min-width: 140px; }
.edit-form-grid { grid-template-columns: 1fr; }
.order-management-header { flex-direction: column; align-items: flex-start; gap: 10px; }
}
/* Table pagination wrapper */
.table-pagination-wrapper {
margin-top: 20px;
border-top: 1px solid #eef3fb;
padding-top: 15px;
}
/* Modal pagination wrapper */
.modal-pagination-wrapper {
margin-top: 15px;
border-top: 1px solid #eef3fb;
padding-top: 12px;
}
/* Right-aligned pagination */
.pagination-right {
justify-content: flex-end;
}
/* Combined top row styling */
.combined-top-row {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
/* नवीन responsive styles */
@media (max-width:980px){
.account-container {
padding: 20px !important;
margin: 0 auto;
}
.panel-card {
min-width:100% !important;
padding: 18px !important;
}
.search-row input{
width:220px !important;
}
.pagination-container {
flex-direction: column;
gap: 10px;
align-items: stretch;
margin-right: 0 !important;
}
.combined-filters-row {
flex-direction: column;
align-items: stretch;
}
.filter-group1,
.filter-group2,
.filter-group3,
.filter-group4 {
width: 100% !important;
}
}
@media (max-width:768px){
body {
overflow-x: hidden;
}
.account-container {
padding: 15px !important;
}
.account-header {
padding: 18px !important;
}
.top-actions {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
.top-actions .left {
justify-content: center;
}
.account-panels {
gap: 15px;
}
.search-row input {
width: 100% !important;
max-width: 300px;
}
/* pagination adjustments for mobile */
.pagination-controls,
.pagination-controls1,
.pagination-controls2 {
margin-right: 0 !important;
justify-content: center;
}
.pagination-container {
margin-right: 0 !important;
}
/* Responsive modals */
.entry-details-modal .modal-box1,
.entry-orders-modal .modal-box1 {
margin: 10px;
border-radius: 12px;
}
.entry-details-header,
.entry-orders-header {
padding: 18px 20px;
}
.entry-details-content,
.entry-orders-content {
padding: 20px;
}
.entry-summary-cards {
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 12px;
}
.entry-summary-value {
font-size: 20px;
}
.orders-summary-row {
flex-direction: column;
gap: 15px;
text-align: center;
}
}
/* Zoom out specific fix */
@media screen and (max-width: 1200px) {
.account-container {
padding: 20px;
max-width: 95%;
}
.payment-block {
min-width: 100%;
}
}
/* Extra small screens */
@media screen and (max-width: 576px) {
.account-container {
padding: 10px;
}
.account-header h2 {
font-size: 22px;
}
.panel-card {
padding: 15px;
min-height: auto;
}
.pagination-container {
padding: 10px 0;
}
.pagination-info {
font-size: 12px;
}
.entry-summary-cards {
grid-template-columns: 1fr;
}
}
/* Prevent horizontal scroll on very small screens */
@media screen and (max-width: 400px) {
body {
min-width: 320px;
}
.account-container {
padding: 10px 5px;
}
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
}
/* Fix for sidebar gap on zoom out */
html, body {
max-width: 100vw;
overflow-x: hidden;
}
/* Ensure all containers are properly constrained */
.container, .container-fluid, .account-container {
max-width: 100%;
overflow-x: hidden;
}
</style>
<div class="account-container">
<!-- Header -->
<div class="account-header">
<h2>Account Dashboard</h2>
<p>Viewing: All Regions Workflow: Manage payments and dispatch.</p>
</div>
<!-- Combined Top Row - Search, Create Installment, Date Filters, Refresh -->
<div class="top-actions">
<div class="combined-top-row">
<!-- Search Section -->
<div class="search-row">
<input type="text" id="main-search" placeholder="Search by Entry No or Description" aria-label="Search entries">
<button class="btn ghost" id="searchBtn">Search</button>
</div>
<!-- Create Installment Button -->
@can('account.create_order')
<button class="btn" id="openCreateModalBtn">+ Create New Order</button>
@endcan
<!-- Date Filters -->
<div class="combined-filters-row">
<div class="filter-group1">
<label class="filter-label">From Date</label>
<input type="date" id="fromDateFilter" class="filter-control">
</div>
<div class="filter-group2">
<label class="filter-label">To Date</label>
<input type="date" id="toDateFilter" class="filter-control">
</div>
</div>
<div class="right">
<!-- Refresh Button -->
<button class="btn" id="refreshBtn"> Refresh</button>
</div>
</div>
<!-- Panels -->
<div class="account-panels" id="account-panels">
<!-- Payment Sent block + pagination एकत्र -->
<div class="payment-block">
<div class="panel-card">
<div class="panel-title">
<span>Payment Sent to China</span>
<span style="font-size:13px;color:var(--muted)">Total entries: <span id="entriesCount">0</span></span>
</div>
<!-- PAYMENT TABLE FILTERS (inside table) -->
<div class="payment-filters-section">
<div class="filter-group3">
<label class="filter-label">Payment Status</label>
<select id="paymentStatusFilter" class="filter-control">
<option value="">📋 All Statuses</option>
<option value="paid"> Paid</option>
<option value="unpaid"> Unpaid</option>
<option value="pending"> Pending</option>
</select>
</div>
</div>
<table id="main-payment-table">
<thead>
<tr>
<th>Entry No</th><th>Date</th><th>Description</th>
<th>Order Quantity</th><th>Region</th><th>Payment</th><th>Amount</th><th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="paymentTableBody">
<!-- Entries will be loaded here -->
</tbody>
</table>
</div>
<!-- Pagination for Payment Table OUTSIDE table but SAME column -->
<div class="table-pagination-wrapper pagination-right">
<div class="pagination-container">
<div class="pagination-info" id="paymentPageInfo">Showing 0 entries</div>
<div class="pagination-controls">
<button class="pagination-img-btn" id="paymentPrevBtn" title="Previous page">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="pagination-pages" id="paymentPaginationPages">
<!-- Page numbers will be inserted here -->
</div>
<button class="pagination-img-btn" id="paymentNextBtn" title="Next page">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- Order Dispatch Table -->
<div class="panel-card">
<div class="panel-title">
<span>Order Dispatch Status</span>
<span style="font-size:13px;color:var(--muted)">Actions: <strong>+ Add Installment</strong></span>
</div>
<!-- ORDER TABLE FILTERS (inside table) -->
<div class="order-filters-section">
<div class="filter-group4">
<label class="filter-label">Dispatch Status</label>
<select id="orderStatusFilter" class="filter-control">
<option value="">📋 All Statuses</option>
<option value="pending"> Pending</option>
<option value="loading">📦 Loading</option>
<option value="packed">📦 Packed</option>
<option value="dispatched">🚚 Dispatched</option>
<option value="delivered"> Delivered</option>
</select>
</div>
</div>
<table id="main-order-table">
<thead>
<tr>
<th>Entry No</th><th>Date</th><th>Description</th>
<th>Region</th><th>Amount</th><th>Status</th>
<th>Pending</th><th></th>
</tr>
</thead>
<tbody id="orderTableBody">
<!-- Entries will be loaded here -->
</tbody>
</table>
<!-- No pagination for second table -->
</div>
</div>
</div>
<!-- CREATE ORDER POPUP MODAL -->
<div class="create-order-modal" id="createOrderModal">
<div class="modal-box">
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:16px;">
<div style="font-size:20px; font-weight:800;">Create New Installment</div>
<button class="btn ghost" id="closeCreateModal" title="Close create form"></button>
</div>
<form id="createOrderInlineForm" autocomplete="off">
<div class="create-grid">
<div>
<label for="inline_description">Description *</label>
<input class="input" type="text" id="inline_description" name="description" required placeholder="Short description for consolidated entry">
</div>
<div>
<label for="inline_region">Region *</label>
<select id="inline_region" name="region" class="input" required>
<option value="China">China</option>
<option value="Europe">Europe</option>
<option value="US">US</option>
</select>
</div>
<div>
<label for="inline_amount">Total Amount () *</label>
<input class="input" type="number" id="inline_amount" name="amount" min="0" required>
</div>
<div>
<label for="inline_entry_date">Entry Date *</label>
<input class="input" type="date" id="inline_entry_date" name="entry_date" value="{{ date('Y-m-d') }}" required>
</div>
</div>
<div style="margin-top:16px;">
<div style="display:flex; align-items:center; margin-bottom:8px;">
<div style="font-weight:700; color:#233063; margin-right:6px;">Consolidated Orders</div>
<div style="margin-left:auto; font-size:13px; color:var(--muted);">Select orders to include in the consolidated entry</div>
</div>
<div class="consolidate-area" id="consolidateArea">
<table id="consolidateOrdersTable">
<thead>
<tr>
<th></th>
<th>Order ID</th>
<th>Mark No</th>
<th>Origin</th>
<th>Destination</th>
<th>CTN</th>
<th>QTY</th>
<th>TTL/QTY</th>
<th>Total Amount ()</th>
<th>CBM</th>
<th>TTL CBM</th>
<th>KG</th>
<th>TTL KG</th>
</tr>
</thead>
<tbody id="consolidateOrdersBody">
<!-- Orders will be loaded here -->
</tbody>
</table>
<!-- Pagination for Create Order Modal -->
<div class="modal-pagination-wrapper pagination-right">
<div class="pagination-container">
<div class="pagination-info" id="modalPageInfo">Showing 0 entries</div>
<div class="pagination-controls2">
<button class="pagination-img-btn" id="modalPrevBtn" title="Previous page">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="pagination-pages" id="modalPaginationPages">
<!-- Page numbers will be inserted here -->
</div>
<button class="pagination-img-btn" id="modalNextBtn" title="Next page">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="create-actions">
<button type="button" class="btn ghost" id="cancelCreateModal">Cancel</button>
<button type="submit" class="btn">Create Order</button>
</div>
</form>
</div>
</div>
<!-- ENTRY DETAILS MODAL (Enhanced) -->
<div class="modal-fade1 entry-details-modal" id="entryDetailsModal">
<div class="modal-box1">
<div class="entry-details-header">
<h2>Entry Details</h2>
<div class="subtitle">
<span id="entryDetailsId">Loading...</span>
<span> Complete view of all installments for this entry</span>
</div>
</div>
<div class="entry-details-content">
<div class="entry-summary-cards">
<div class="entry-summary-card">
<div class="entry-summary-label">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
</svg>
Original Amount
</div>
<div class="entry-summary-value" id="originalAmount">-</div>
</div>
<div class="entry-summary-card">
<div class="entry-summary-label">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
</svg>
Total Processed
</div>
<div class="entry-summary-value" id="totalProcessed">-</div>
</div>
<div class="entry-summary-card">
<div class="entry-summary-label">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
</svg>
Pending Balance
</div>
<div class="entry-summary-value" id="pendingBalance">-</div>
</div>
<div class="entry-summary-card">
<div class="entry-summary-label">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
Total Installments
</div>
<div class="entry-summary-value" id="totalInstallments">-</div>
</div>
</div>
<div style="margin-top: 30px;">
<h3 style="font-size: 18px; font-weight: 700; color: var(--primary-1); margin-bottom: 16px;">
Installment History
</h3>
<div class="orders-table-container">
<table class="entry-installments-table">
<thead>
<tr>
<th>Installment</th>
<th>Date</th>
<th>Description</th>
<th>Region</th>
<th>Amount</th>
<th>Status</th>
</tr>
</thead>
<tbody id="installmentsTableBody">
<tr><td colspan="6" class="empty-state">No installments yet</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div style="padding: 20px 30px; border-top: 1px solid #eef3fb; display: flex; justify-content: space-between; align-items: center;">
<button type="button" class="btn ghost" onclick="closeEntryDetailsModal()">
Close
</button>
@can('account.add_installment')
<button type="button" class="btn" id="addInstallmentFromDetails">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
Add New Installment
</button>
@endcan
</div>
</div>
</div>
<!-- ENTRY ORDERS MODAL (Enhanced) -->
<div class="modal-fade1 entry-orders-modal" id="entryOrdersModal">
<div class="modal-box1">
<div class="entry-orders-header">
<h2>Entry Orders</h2>
<div class="subtitle">
<span id="entryOrdersEntryNo-span">Loading...</span>
<span> All orders associated with this entry</span>
</div>
</div>
<div class="entry-orders-content">
<div class="orders-table-container">
<table class="orders-table">
<thead>
<tr>
<th>Order ID</th>
<th>Mark No</th>
<th>Origin</th>
<th>Destination</th>
<th>CTN</th>
<th>QTY</th>
<th>Amount</th>
</tr>
</thead>
<tbody id="entryOrdersTableBody">
<tr>
<td colspan="7" class="empty-state">Loading orders...</td>
</tr>
</tbody>
</table>
</div>
<div class="orders-summary-row">
<div class="orders-summary-item">
<div class="orders-summary-value" id="ordersTotalCount">0</div>
<div class="orders-summary-label">Total Orders</div>
</div>
<div class="orders-summary-item">
<div class="orders-summary-value" id="ordersTotalQuantity">0</div>
<div class="orders-summary-label">Total Quantity</div>
</div>
<div class="orders-summary-item">
<div class="orders-summary-value" id="ordersTotalAmount">₹0</div>
<div class="orders-summary-label">Total Amount</div>
</div>
</div>
</div>
<div style="padding: 20px 30px; border-top: 1px solid #eef3fb; display: flex; justify-content: flex-end;">
<button class="btn ghost" type="button" onclick="closeEntryOrdersModal()">
Close
</button>
</div>
</div>
</div>
<!-- Installment Modal -->
<div class="modal-fade1" id="installmentModal">
<div class="modal-box1" style="max-width:720px;">
<div style="display:flex;align-items:center; justify-content:space-between; margin-bottom:12px;">
<div style="font-size:18px;font-weight:800;color:#243a72;">+ Add New Installment</div>
<button class="btn ghost" onclick="closeInstallmentModal()"></button>
</div>
<div style="font-size:14px;color:#6f7b8f;margin-bottom:14px;">Create a new processing entry for the remaining pending amount</div>
<div id="instDetailsRow" style="display:flex; gap:18px; margin-bottom:12px; flex-wrap:wrap;"></div>
<form id="installmentForm" autocomplete="off">
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
<div>
<label>Processing Date *</label>
<input type="date" name="proc_date" required style="width:100%; padding:10px; border-radius:8px; border:1.4px solid #e7eefb;" value="{{ date('Y-m-d') }}">
</div>
<div>
<label>Processing Amount *</label>
<input type="number" min="1" name="proc_amount" required placeholder="Enter amount" style="width:100%; padding:10px; border-radius:8px; border:1.4px solid #e7eefb;">
<span class="helper-note">Maximum allowed: <strong id="maxPending">-</strong></span>
</div>
<div>
<label>Status</label>
<select name="status" required style="width:100%; padding:10px; border-radius:8px; border:1.4px solid #e7eefb;">
<option>Pending</option><option>Loading</option><option>Packed</option><option>Dispatched</option><option>Delivered</option>
</select>
</div>
</div>
<div style="display:flex; justify-content:flex-end; gap:12px; margin-top:14px;">
<button type="button" class="btn ghost" onclick="closeInstallmentModal()">Cancel</button>
@can('account.add_installment')
<button type="submit" class="btn">Create Installment2</button>
@endcan
</div>
</form>
</div>
</div>
<!-- Edit Entry Modal -->
<div class="modal-fade1 edit-modal" id="editEntryModal">
<div class="modal-box1">
<div style="display:flex;align-items:center; justify-content:space-between; margin-bottom:12px;">
<div style="font-size:18px;font-weight:800;color:#243a72;">Edit Entry</div>
<button class="btn ghost" onclick="closeEditModal()"></button>
</div>
<div style="font-size:14px;color:#6f7b8f;margin-bottom:14px;">Update the description and order quantity for this entry</div>
<form id="editEntryForm" autocomplete="off">
<input type="hidden" id="edit_entry_no" name="entry_no">
<div class="edit-form-grid">
<div>
<label>Description *</label>
<input type="text" id="edit_description" name="description" required class="input" placeholder="Entry description">
</div>
<div>
<label>
Order Quantity
<span style="font-size:12px; color:#6b7280; margin-left:4px;">
(Auto Update)
</span>
</label>
<input
type="number"
id="edit_order_quantity"
name="order_quantity"
min="0"
class="input"
placeholder="Order quantity"
readonly
style="cursor:pointer;"
onclick="openEntryOrdersModal(document.getElementById('edit_entry_no').value)"
>
<small class="helper-note">
Click on quantity to view associated orders.
</small>
</div>
<!-- <div>
<label>Payment Status</label>
<select id="edit_payment_status" name="payment_status" class="input">
<option value="unpaid">Unpaid</option>
<option value="paid">Paid</option>
<option value="pending">Pending</option>
</select>
</div>-->
<div>
<label>Region</label>
<select id="edit_region" name="region" class="input" required>
<option value="China">China</option>
<option value="Europe">Europe</option>
<option value="US">US</option>
</select>
</div>
<div>
<label>Amount () *</label>
<input type="number" id="edit_amount" name="amount" min="0" required class="input" placeholder="Entry amount">
</div>
</div>
<!-- Order Management Section -->
<div class="order-management-section">
<div class="order-management-header">
<div class="order-management-title">Associated Orders</div>
<button type="button" class="btn ghost" id="addOrderBtn">+ Add Orders</button>
</div>
<div class="orders-table-container">
<table class="orders-table">
<thead>
<tr>
<th>Order ID</th>
<th>Mark No</th>
<th>Origin</th>
<th>Destination</th>
<th>CTN</th>
<th>QTY</th>
<th>Amount ()</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="editOrdersTableBody">
<!-- Orders will be loaded here -->
</tbody>
</table>
</div>
<div id="editOrdersEmptyState" class="empty-state" style="display:none;">
No orders associated with this entry
</div>
</div>
<div class="edit-form-actions">
<button type="button" class="btn ghost" onclick="closeEditModal()">Cancel</button>
<button type="submit" class="btn">Update Entry</button>
</div>
</form>
</div>
</div>
<!-- Add Order Modal -->
<div class="modal-fade1 add-order-modal" id="addOrderModal">
<div class="modal-box1">
<div style="display:flex;align-items:center; justify-content:space-between; margin-bottom:12px;">
<div style="font-size:18px;font-weight:800;color:#243a72;">Add Orders to Entry</div>
<button class="btn ghost" onclick="closeAddOrderModal()"></button>
</div>
<div style="font-size:14px;color:#6f7b8f;margin-bottom:14px;">Select orders to associate with this entry</div>
<div class="consolidate-area" style="min-height: 400px;">
<table id="addOrderTable">
<thead>
<tr>
<th></th>
<th>Order ID</th>
<th>Mark No</th>
<th>Origin</th>
<th>Destination</th>
<th>CTN</th>
<th>QTY</th>
<th>Amount ()</th>
</tr>
</thead>
<tbody id="addOrderTableBody">
<!-- Orders will be loaded here -->
</tbody>
</table>
<!-- Pagination for Add Order Modal -->
<div class="modal-pagination-wrapper pagination-right">
<div class="pagination-container">
<div class="pagination-info" id="addOrderPageInfo">Showing 0 entries</div>
<div class="pagination-controls1">
<button class="pagination-img-btn" id="addOrderPrevBtn" title="Previous page">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="pagination-pages" id="addOrderPaginationPages">
<!-- Page numbers will be inserted here -->
</div>
<button class="pagination-img-btn" id="addOrderNextBtn" title="Next page">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
</div>
</div>
<div class="add-order-actions">
<button type="button" class="btn ghost" onclick="closeAddOrderModal()">Cancel</button>
<button type="button" class="btn" id="confirmAddOrdersBtn">Add Selected Orders</button>
</div>
</div>
</div>
<script>
window.CAN_EDIT_ORDER = @json(auth()->user()->can('account.edit_order'));
window.CAN_DELETE_ORDER = @json(auth()->user()->can('account.delete_order'));
window.CAN_TOGGLE_PAYMENT = @json(auth()->user()->can('account.toggle_payment_status'));
</script>
<script>
/* ---------- Helpers & state ---------- */
function setToggleVisual(btn, pos) {
// pos: 0 = unpaid (red), 1 = pending (yellow), 2 = paid (green)
btn.classList.remove('mid', 'checked');
if (pos === 1) {
btn.classList.add('mid'); // yellow
} else if (pos === 2) {
btn.classList.add('checked'); // green
}
}
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
function jsonFetch(url, opts = {}) {
opts.headers = Object.assign(
{ 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken },
opts.headers || {}
);
if (opts.body && typeof opts.body !== 'string') {
opts.body = JSON.stringify(opts.body);
}
return fetch(url, opts).then(r =>
r.json().catch(() => {
throw new Error('Invalid server response');
})
);
}
let entries = [];
let availableOrders = [];
let currentEntry = null;
let currentEditEntry = null;
/* Single pagination state for both tables */
let currentPage = 1;
const entriesPerPage = 10;
/* Separate pagination state for modals */
let modalCurrentPage = 1;
const modalOrdersPerPage = 10;
let addOrderCurrentPage = 1;
/* Date filter state */
let fromDateFilter = '';
let toDateFilter = '';
/* Status filter state */
let paymentStatusFilter = '';
let orderStatusFilter = '';
/* small util */
function escapeHtml(s){ if(s === null || s === undefined) return ''; return String(s).replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":"&#39;"}[m])); }
function formatCurrency(v){ return '₹' + Number(v || 0).toLocaleString(undefined, { minimumFractionDigits:0, maximumFractionDigits:2 }); }
function capitalize(s){ if(!s) return ''; s = String(s); return s.charAt(0).toUpperCase() + s.slice(1); }
function statusClass(status){
switch(String(status || '').toLowerCase()){
case 'unpaid': return 'status-unpaid';
case 'paid': return 'status-paid';
case 'loading': return 'status-loading';
case 'dispatched': return 'status-dispatched';
case 'packed': return 'status-loading';
case 'delivered': return 'status-delivered';
case 'pending': return 'pending-badge-red';
default: return 'status-loading';
}
}
/* ---------- Init ---------- */
window.addEventListener('DOMContentLoaded', () => {
bindUI();
loadDashboard();
});
/* ---------- UI binding ---------- */
function bindUI(){
// Create Order Modal
//document.getElementById('openCreateModalBtn').addEventListener('click', openCreateOrderModal);
const createBtn = document.getElementById('openCreateModalBtn');
if (createBtn) {
createBtn.addEventListener('click', openCreateOrderModal);
}
document.getElementById('closeCreateModal').addEventListener('click', closeCreateOrderModal);
document.getElementById('cancelCreateModal').addEventListener('click', closeCreateOrderModal);
document.getElementById('createOrderInlineForm').addEventListener('submit', submitCreateOrderInline);
// Payment Table Pagination (only pagination controls)
document.getElementById('paymentPrevBtn').addEventListener('click', () => goToPreviousPage());
document.getElementById('paymentNextBtn').addEventListener('click', () => goToNextPage());
// No pagination controls for order table
// Modal Pagination
document.getElementById('modalPrevBtn').addEventListener('click', () => goToModalPreviousPage());
document.getElementById('modalNextBtn').addEventListener('click', () => goToModalNextPage());
// Add Order Modal Pagination
document.getElementById('addOrderPrevBtn').addEventListener('click', () => goToAddOrderPreviousPage());
document.getElementById('addOrderNextBtn').addEventListener('click', () => goToAddOrderNextPage());
document.getElementById('refreshBtn').addEventListener('click', () => {
loadDashboard();
clearDateFilters();
clearStatusFilters();
});
document.getElementById('searchBtn').addEventListener('click', handleSearch);
const addInstallBtn = document.getElementById('addInstallmentFromDetails');
if (addInstallBtn) {
addInstallBtn.addEventListener('click', () => {
if (!currentEntry) return;
openInstallmentModal(
currentEntry.entry_no,
currentEntry.description,
currentEntry.region,
currentEntry.pending_amount
);
closeEntryDetailsModal();
});
}
// Installment form submit
document.getElementById('installmentForm').addEventListener('submit', submitInstallment);
// Edit form submit
document.getElementById('editEntryForm').addEventListener('submit', submitEditEntry);
// Add Order button in Edit Modal
document.getElementById('addOrderBtn').addEventListener('click', openAddOrderModal);
// Confirm Add Orders button
document.getElementById('confirmAddOrdersBtn').addEventListener('click', confirmAddOrders);
// Date Filter bindings
document.getElementById('fromDateFilter').addEventListener('change', (e) => {
fromDateFilter = e.target.value;
applyAllFilters();
});
document.getElementById('toDateFilter').addEventListener('change', (e) => {
toDateFilter = e.target.value;
applyAllFilters();
});
// Status Filter bindings
document.getElementById('paymentStatusFilter').addEventListener('change', (e) => {
paymentStatusFilter = e.target.value;
applyAllFilters();
});
document.getElementById('orderStatusFilter').addEventListener('change', (e) => {
orderStatusFilter = e.target.value;
applyAllFilters();
});
}
/* ---------- Filter Functions ---------- */
function applyAllFilters() {
let filteredEntries = [...entries];
// Apply date filters (both tables)
if (fromDateFilter) {
filteredEntries = filteredEntries.filter(entry => entry.entry_date >= fromDateFilter);
}
if (toDateFilter) {
filteredEntries = filteredEntries.filter(entry => entry.entry_date <= toDateFilter);
}
// Apply payment status filter (ONLY for payment table)
let paymentFilteredEntries = [...filteredEntries];
if (paymentStatusFilter) {
paymentFilteredEntries = paymentFilteredEntries.filter(entry =>
entry.payment_status.toLowerCase() === paymentStatusFilter.toLowerCase()
);
}
// Apply order status filter (ONLY for order table)
let orderFilteredEntries = [...filteredEntries];
if (orderStatusFilter) {
orderFilteredEntries = orderFilteredEntries.filter(entry =>
entry.dispatch_status.toLowerCase() === orderStatusFilter.toLowerCase()
);
}
// Reset to first page when filtering
currentPage = 1;
// Render each table with its own filtered data
renderPaymentTable(paymentFilteredEntries);
renderOrderTable(orderFilteredEntries);
updatePaginationControls();
}
function applyOrderTableFilters(entriesList) {
let filtered = [...entriesList];
// Apply order status filter (for order table only)
if (orderStatusFilter) {
filtered = filtered.filter(entry =>
entry.dispatch_status.toLowerCase() === orderStatusFilter.toLowerCase()
);
}
return filtered;
}
function clearDateFilters() {
document.getElementById('fromDateFilter').value = '';
document.getElementById('toDateFilter').value = '';
fromDateFilter = '';
toDateFilter = '';
}
function clearStatusFilters() {
document.getElementById('paymentStatusFilter').value = '';
document.getElementById('orderStatusFilter').value = '';
paymentStatusFilter = '';
orderStatusFilter = '';
}
/* ---------- Pagination Functions ---------- */
function goToPreviousPage() {
if (currentPage > 1) {
currentPage--;
renderBothTables();
updatePaginationControls();
}
}
function goToNextPage() {
const totalPages = Math.ceil(entries.length / entriesPerPage);
if (currentPage < totalPages) {
currentPage++;
renderBothTables();
updatePaginationControls();
}
}
function goToModalPreviousPage() {
if (modalCurrentPage > 1) {
modalCurrentPage--;
renderConsolidateOrders(availableOrders);
updateModalPaginationControls();
}
}
function goToModalNextPage() {
const totalPages = Math.ceil(availableOrders.length / modalOrdersPerPage);
if (modalCurrentPage < totalPages) {
modalCurrentPage++;
renderConsolidateOrders(availableOrders);
updateModalPaginationControls();
}
}
function goToAddOrderPreviousPage() {
if (addOrderCurrentPage > 1) {
addOrderCurrentPage--;
renderAddOrderModal(availableOrders);
updateAddOrderPaginationControls();
}
}
function goToAddOrderNextPage() {
const totalPages = Math.ceil(availableOrders.length / modalOrdersPerPage);
if (addOrderCurrentPage < totalPages) {
addOrderCurrentPage++;
renderAddOrderModal(availableOrders);
updateAddOrderPaginationControls();
}
}
function updatePaginationControls() {
const totalPages = Math.ceil(entries.length / entriesPerPage);
const prevBtn = document.getElementById('paymentPrevBtn');
const nextBtn = document.getElementById('paymentNextBtn');
const pageInfo = document.getElementById('paymentPageInfo');
const paginationPages = document.getElementById('paymentPaginationPages');
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
// Update page info text
const startIndex = (currentPage - 1) * entriesPerPage + 1;
const endIndex = Math.min(currentPage * entriesPerPage, entries.length);
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${entries.length} entries`;
// Generate page numbers
paginationPages.innerHTML = '';
if (totalPages <= 7) {
// Show all pages
for (let i = 1; i <= totalPages; i++) {
addPageButton(i, paginationPages);
}
} else {
// Show first page, current page range, and last page
addPageButton(1, paginationPages);
if (currentPage > 3) {
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
}
const start = Math.max(2, currentPage - 1);
const end = Math.min(totalPages - 1, currentPage + 1);
for (let i = start; i <= end; i++) {
addPageButton(i, paginationPages);
}
if (currentPage < totalPages - 2) {
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
}
addPageButton(totalPages, paginationPages);
}
}
function updateModalPaginationControls() {
const totalPages = Math.ceil(availableOrders.length / modalOrdersPerPage);
const prevBtn = document.getElementById('modalPrevBtn');
const nextBtn = document.getElementById('modalNextBtn');
const pageInfo = document.getElementById('modalPageInfo');
const paginationPages = document.getElementById('modalPaginationPages');
prevBtn.disabled = modalCurrentPage === 1;
nextBtn.disabled = modalCurrentPage === totalPages || totalPages === 0;
// Update page info text
const startIndex = (modalCurrentPage - 1) * modalOrdersPerPage + 1;
const endIndex = Math.min(modalCurrentPage * modalOrdersPerPage, availableOrders.length);
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${availableOrders.length} orders`;
// Generate page numbers
paginationPages.innerHTML = '';
if (totalPages <= 7) {
// Show all pages
for (let i = 1; i <= totalPages; i++) {
addModalPageButton(i, paginationPages);
}
} else {
// Show first page, current page range, and last page
addModalPageButton(1, paginationPages);
if (modalCurrentPage > 3) {
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
}
const start = Math.max(2, modalCurrentPage - 1);
const end = Math.min(totalPages - 1, modalCurrentPage + 1);
for (let i = start; i <= end; i++) {
addModalPageButton(i, paginationPages);
}
if (modalCurrentPage < totalPages - 2) {
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
}
addModalPageButton(totalPages, paginationPages);
}
}
function updateAddOrderPaginationControls() {
const totalPages = Math.ceil(availableOrders.length / modalOrdersPerPage);
const prevBtn = document.getElementById('addOrderPrevBtn');
const nextBtn = document.getElementById('addOrderNextBtn');
const pageInfo = document.getElementById('addOrderPageInfo');
const paginationPages = document.getElementById('addOrderPaginationPages');
prevBtn.disabled = addOrderCurrentPage === 1;
nextBtn.disabled = addOrderCurrentPage === totalPages || totalPages === 0;
// Update page info text
const startIndex = (addOrderCurrentPage - 1) * modalOrdersPerPage + 1;
const endIndex = Math.min(addOrderCurrentPage * modalOrdersPerPage, availableOrders.length);
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${availableOrders.length} orders`;
// Generate page numbers
paginationPages.innerHTML = '';
if (totalPages <= 7) {
// Show all pages
for (let i = 1; i <= totalPages; i++) {
addAddOrderPageButton(i, paginationPages);
}
} else {
// Show first page, current page range, and last page
addAddOrderPageButton(1, paginationPages);
if (addOrderCurrentPage > 3) {
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
}
const start = Math.max(2, addOrderCurrentPage - 1);
const end = Math.min(totalPages - 1, addOrderCurrentPage + 1);
for (let i = start; i <= end; i++) {
addAddOrderPageButton(i, paginationPages);
}
if (addOrderCurrentPage < totalPages - 2) {
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
}
addAddOrderPageButton(totalPages, paginationPages);
}
}
function addPageButton(pageNumber, container) {
const button = document.createElement('button');
button.className = 'pagination-page-btn';
if (pageNumber === currentPage) {
button.classList.add('active');
}
button.textContent = pageNumber;
button.addEventListener('click', () => {
currentPage = pageNumber;
renderBothTables();
updatePaginationControls();
});
container.appendChild(button);
}
function addModalPageButton(pageNumber, container) {
const button = document.createElement('button');
button.className = 'pagination-page-btn';
if (pageNumber === modalCurrentPage) {
button.classList.add('active');
}
button.textContent = pageNumber;
button.addEventListener('click', () => {
modalCurrentPage = pageNumber;
renderConsolidateOrders(availableOrders);
updateModalPaginationControls();
});
container.appendChild(button);
}
function addAddOrderPageButton(pageNumber, container) {
const button = document.createElement('button');
button.className = 'pagination-page-btn';
if (pageNumber === addOrderCurrentPage) {
button.classList.add('active');
}
button.textContent = pageNumber;
button.addEventListener('click', () => {
addOrderCurrentPage = pageNumber;
renderAddOrderModal(availableOrders);
updateAddOrderPaginationControls();
});
container.appendChild(button);
}
function renderBothTables() {
renderPaymentTable(entries);
renderOrderTable(entries);
}
/* ---------- Create Order Modal Functions ---------- */
function openCreateOrderModal(){
const modal = document.getElementById('createOrderModal');
modal.classList.add('modal-open');
loadAvailableOrders();
setTimeout(()=> document.getElementById('inline_description').focus(), 220);
}
function closeCreateOrderModal(){
const modal = document.getElementById('createOrderModal');
modal.classList.remove('modal-open');
document.getElementById('createOrderInlineForm').reset();
modalCurrentPage = 1; // Reset modal pagination when closing
}
/* ---------- Loaders ---------- */
function loadDashboard(){
jsonFetch('/admin/account/dashboard')
.then(res => {
if(!res.success) throw new Error(res.message || 'Failed to load dashboard');
entries = res.entries || [];
renderBothTables();
updatePaginationControls(); // Initialize pagination after loading
document.getElementById('entriesCount').textContent = entries.length;
})
.catch(err => {
console.error(err);
document.getElementById('paymentTableBody').innerHTML = '<tr><td colspan="9" class="empty-state">Unable to load entries</td></tr>';
document.getElementById('orderTableBody').innerHTML = '<tr><td colspan="8" class="empty-state">Unable to load entries</td></tr>';
});
}
function loadAvailableOrders() {
const tbody = document.getElementById('consolidateOrdersBody');
tbody.innerHTML = '<tr><td colspan="14" class="empty-state">Loading available orders...</td></tr>';
jsonFetch('/admin/account/available-orders', { method: 'GET' })
.then(res => {
if (!res.success) throw new Error(res.message || 'Failed to load orders');
availableOrders = res.orders || [];
modalCurrentPage = 1; // Reset to first page when loading new orders
renderConsolidateOrders(availableOrders);
updateModalPaginationControls();
})
.catch(err => {
console.error(err);
tbody.innerHTML = '<tr><td colspan="14" class="empty-state">Unable to load orders</td></tr>';
updateModalPaginationControls();
});
}
function loadAvailableOrdersForAddModal() {
const tbody = document.getElementById('addOrderTableBody');
if (tbody) {
tbody.innerHTML = `
<tr>
<td colspan="8" class="empty-state">Loading available orders...</td>
</tr>
`;
}
jsonFetch('/admin/account/available-orders', {
method: 'GET'
})
.then(res => {
if (!res.success) {
throw new Error(res.message || 'Failed to load available orders');
}
availableOrders = res.orders || [];
addOrderCurrentPage = 1;
renderAddOrderModal(availableOrders);
updateAddOrderPaginationControls();
})
.catch(err => {
console.error(err);
if (tbody) {
tbody.innerHTML = `
<tr>
<td colspan="8" class="empty-state">Unable to load available orders</td>
</tr>
`;
}
updateAddOrderPaginationControls();
});
}
/* ---------- Renderers ---------- */
function renderPaymentTable(list){
const body = document.getElementById('paymentTableBody');
body.innerHTML = '';
if (!list || list.length === 0) {
body.innerHTML = '<tr><td colspan="9" class="empty-state">No entries found</td></tr>';
return;
}
const startIndex = (currentPage - 1) * entriesPerPage;
const endIndex = startIndex + entriesPerPage;
const paginatedEntries = list.slice(startIndex, endIndex);
paginatedEntries.forEach(entry => {
const tr = document.createElement('tr');
const canActions = ['unpaid','pending'].includes(entry.payment_status.toLowerCase());
// permissions passed from Blade
const canEdit = window.CAN_EDIT_ORDER;
const canDelete = window.CAN_DELETE_ORDER;
const canToggle = window.CAN_TOGGLE_PAYMENT;
tr.innerHTML = `
<td>${escapeHtml(entry.entry_no)}</td>
<td>${escapeHtml(entry.entry_date)}</td>
<td>${escapeHtml(entry.description)}</td>
<td>
<button type="button" class="entry-link"
onclick="openEntryOrdersModal('${escapeHtml(entry.entry_no)}')">
${entry.order_quantity ?? '-'}
</button>
</td>
<td>${escapeHtml(entry.region)}</td>
<td>
<button class="toggle-switch-btn"
data-entry="${entry.entry_no}"
data-pos="${entry.toggle_pos ?? 0}"
${!canToggle ? 'disabled class="toggle-switch-btn disabled-toggle"' : ''}
></button>
</td>
<td>${formatCurrency(entry.amount)}</td>
<td>
<span class="status-badge ${statusClass(entry.payment_status)}">
${capitalize(entry.payment_status)}
</span>
</td>
<td>
<div class="action-btns">
${(canActions && canEdit) ? `
<button class="action-btn edit-btn"
data-entry="${entry.entry_no}"
title="Edit entry">
</button>
` : ''}
${(canActions && canDelete) ? `
<button class="action-btn delete-btn"
data-entry="${entry.entry_no}"
title="Delete entry">
🗑
</button>
` : ''}
</div>
</td>
`;
/* Toggle Button Logic */
const toggleBtn = tr.querySelector('.toggle-switch-btn');
setToggleVisual(toggleBtn, Number(toggleBtn.dataset.pos));
if (canToggle) {
toggleBtn.addEventListener("click", () => cycleToggle(toggleBtn));
} else {
toggleBtn.style.opacity = "0.5";
toggleBtn.style.cursor = "not-allowed";
}
/* EDIT binding */
if (canActions && canEdit) {
const editBtn = tr.querySelector(".edit-btn");
editBtn?.addEventListener("click", () => openEditModal(entry));
}
/* DELETE binding */
if (canActions && canDelete) {
const delBtn = tr.querySelector(".delete-btn");
delBtn?.addEventListener("click", () => deleteEntry(entry.entry_no));
}
body.appendChild(tr);
});
}
function cycleToggle(btn) {
// वर्तमान position घेऊन पुढचा स्टेट कॅल्क्युलेट करा
let pos = parseInt(btn.dataset.pos || '0', 10); // 0 = unpaid, 1 = pending, 2 = paid
pos = (pos + 1) % 3;
btn.dataset.pos = pos;
setToggleVisual(btn, pos); // रंग वगैरे अपडेट
const entryNo = btn.dataset.entry;
jsonFetch('/admin/account/toggle-payment', {
method: 'POST',
body: {
entry_no: entryNo, // controller मध्ये जी नावे आहेत तीच
toggle_pos: pos
}
}).then(res => {
if (!res.success) {
alert(res.message || 'Failed to update payment status');
// error असेल तर UI परत जुन्या स्टेटला ने
pos = (pos + 2) % 3;
btn.dataset.pos = pos;
setToggleVisual(btn, pos);
return;
}
// 1) global entries array update करा (dashboard data)
if (Array.isArray(entries)) {
const idx = entries.findIndex(e => e.entry_no === entryNo);
if (idx !== -1) {
entries[idx].toggle_pos = res.entry.toggle_pos;
entries[idx].payment_status = res.entry.payment_status;
}
}
// 2) current row मधला status badge आणि actions अपडेट करा
const row = btn.closest('tr');
if (row) {
// status badge
const statusCell = row.querySelector('.status-badge');
if (statusCell) {
statusCell.textContent = res.entry.payment_status;
statusCell.className = 'status-badge ' + statusClass(res.entry.payment_status);
}
// paid झाल्यावर edit/delete लपवा
const actions = row.querySelector('.action-btns');
if (actions) {
if (res.entry.payment_status.toLowerCase() === 'paid') {
actions.style.display = 'none';
} else {
actions.style.display = 'flex'; // किंवा तुझा default layout
}
}
}
}).catch(() => {
alert('Network error while updating payment status');
// network error UI मागे घे
pos = (pos + 2) % 3;
btn.dataset.pos = pos;
setToggleVisual(btn, pos);
});
}
function renderOrderTable(list){
const body = document.getElementById('orderTableBody');
body.innerHTML = '';
if(!list || list.length === 0){
body.innerHTML = '<tr><td colspan="8" class="empty-state">No entries found</td></tr>';
return;
}
// Calculate pagination (using same currentPage as first table)
const startIndex = (currentPage - 1) * entriesPerPage;
const endIndex = startIndex + entriesPerPage;
const paginatedEntries = list.slice(startIndex, endIndex);
paginatedEntries.forEach(entry => {
const tr = document.createElement('tr');
const pending = Number(entry.pending_amount || 0);
const pendingHtml = pending <= 0 ? '<span class="status-badge pending-badge-green">Completed</span>' : `<span class="status-badge pending-badge-red">${formatCurrency(pending)}</span>`;
tr.innerHTML = `
<td><a class="entry-link" data-entry="${escapeHtml(entry.entry_no)}">${escapeHtml(entry.entry_no)}</a></td>
<td>${escapeHtml(entry.entry_date)}</td>
<td>${escapeHtml(entry.description)}</td>
<td>${escapeHtml(entry.region)}</td>
<td>${formatCurrency(entry.amount)}</td>
<td><span class="status-badge ${statusClass(entry.dispatch_status)}">${capitalize(entry.dispatch_status)}</span></td>
<td>${pendingHtml}</td>
<td>${pending > 0 ? `<button class="plus-btn" data-entry="${escapeHtml(entry.entry_no)}" title="Add installment">+</button>` : ''}</td>
`;
body.appendChild(tr);
// bind entry link
const link = tr.querySelector('.entry-link');
link.addEventListener('click', (e) => openEntryDetailsModal(e.target.dataset.entry));
// bind plus
const plus = tr.querySelector('.plus-btn');
if(plus) plus.addEventListener('click', (e) => openInstallmentModal(e.target.dataset.entry, entry.description, entry.region, entry.pending_amount));
});
}
function renderConsolidateOrders(list){
const body = document.getElementById('consolidateOrdersBody');
body.innerHTML = '';
if(!list || list.length === 0){
body.innerHTML = '<tr><td colspan="14" class="empty-state">No available orders</td></tr>';
updateModalPaginationControls();
return;
}
// Calculate pagination for modal
const startIndex = (modalCurrentPage - 1) * modalOrdersPerPage;
const endIndex = startIndex + modalOrdersPerPage;
const paginatedOrders = list.slice(startIndex, endIndex);
paginatedOrders.forEach(order => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td><input type="checkbox" value="${order.id}"></td>
<td><a href="#" class="order-link" data-id="${order.id}">${escapeHtml(order.order_id || ('#'+order.id))}</a></td>
<td>${escapeHtml(order.mark_no || '')}</td>
<td>${order.origin ?? ''}</td>
<td>${order.destination ?? ''}</td>
<td>${order.ctn ?? ''}</td>
<td>${order.qty ?? ''}</td>
<td>${order.ttl_qty ?? ''}</td>
<td>${order.ttl_amount ?? ''}</td>
<td>${order.cbm ?? ''}</td>
<td>${order.ttl_cbm ?? ''}</td>
<td>${order.kg ?? ''}</td>
<td>${order.ttl_kg ?? ''}</td>
`;
body.appendChild(tr);
});
}
function renderAddOrderModal(orders) {
const tbody = document.getElementById('addOrderTableBody');
tbody.innerHTML = '';
if (!orders || !orders.length) {
tbody.innerHTML = `
<tr>
<td colspan="8" class="empty-state">No orders found</td>
</tr>
`;
return;
}
const start = (addOrderCurrentPage - 1) * modalOrdersPerPage;
const end = Math.min(start + modalOrdersPerPage, orders.length);
const pageOrders = orders.slice(start, end);
// Hya entry sathi already associated order IDs collect kara
const attachedIds = new Set(
(currentEditEntry?.orders || []).map(o => o.id)
);
pageOrders.forEach(order => {
const tr = document.createElement('tr');
const idString = (order.orderid ?? order.id ?? '').toString().trim();
const numericId = parseInt(idString, 10);
const formattedId = isNaN(numericId)
? escapeHtml(idString)
: 'KNT-25-' + String(numericId).padStart(8, '0');
const isAttached = attachedIds.has(order.id);
tr.innerHTML = `
<td>
<input type="checkbox"
class="add-order-checkbox"
value="${order.id}"
${isAttached ? 'checked disabled' : ''}>
</td>
<td>${formattedId}</td>
<td>${escapeHtml(order.markno ?? order.mark_no ?? '')}</td>
<td>${escapeHtml(order.origin ?? '')}</td>
<td>${escapeHtml(order.destination ?? '')}</td>
<td>${escapeHtml(order.ctn ?? '')}</td>
<td>${escapeHtml(order.qty ?? '')}</td>
<td>${formatCurrency(order.ttl_amount ?? order.ttlamount ?? order.total_amount ?? order.amount ?? 0)}</td>
`;
tbody.appendChild(tr);
});
}
/* ---------- Edit Entry Functions ---------- */
function openEditModal(entry) {
currentEditEntry = entry;
document.getElementById('edit_entry_no').value = entry.entry_no;
document.getElementById('edit_description').value = entry.description || '';
document.getElementById('edit_order_quantity').value = entry.order_quantity || '';
// document.getElementById('edit_payment_status').value = entry.payment_status || 'unpaid';
document.getElementById('edit_region').value = entry.region || 'China';
document.getElementById('edit_amount').value = entry.amount || '';
// Load associated orders
loadEntryOrders(entry.entry_no);
document.getElementById('editEntryModal').classList.add('modal-open');
}
function closeEditModal() {
document.getElementById('editEntryModal').classList.remove('modal-open');
currentEditEntry = null;
}
function loadEntryOrders(entryNo) {
const tbody = document.getElementById('editOrdersTableBody');
const emptyState = document.getElementById('editOrdersEmptyState');
tbody.innerHTML = '<tr><td colspan="8" class="empty-state">Loading orders...</td></tr>';
jsonFetch(`/admin/account/entry-orders/${encodeURIComponent(entryNo)}`)
.then(res => {
if (!res.success) throw new Error(res.message || 'Failed to load orders');
tbody.innerHTML = '';
if (!res.orders || res.orders.length === 0) {
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
res.orders.forEach(order => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${escapeHtml(order.order_id || ('#'+order.id))}</td>
<td>${escapeHtml(order.mark_no || '')}</td>
<td>${order.origin ?? ''}</td>
<td>${order.destination ?? ''}</td>
<td>${order.ctn ?? ''}</td>
<td>${order.qty ?? ''}</td>
<td>${order.ttl_amount ?? ''}</td>
<td>
<div class="order-actions">
<button type="button" class="remove-order-btn" data-order-id="${order.id}">Remove</button>
</div>
</td>
`;
tbody.appendChild(tr);
// Bind remove button
const removeBtn = tr.querySelector('.remove-order-btn');
removeBtn.addEventListener('click', () => removeOrderFromEntry(order.id, entryNo));
});
})
.catch(err => {
console.error(err);
tbody.innerHTML = '<tr><td colspan="8" class="empty-state">Unable to load orders</td></tr>';
});
}
async function refreshEntryHeader(entryNo) {
try {
const res = await jsonFetch('/admin/account/entry/' + encodeURIComponent(entryNo));
if (!res.success) return;
const entry = res.entry;
const qtyInput = document.getElementById('editorderquantity');
if (qtyInput) qtyInput.value = entry.order_quantity ?? 0;
const idx = entries.findIndex(e => e.entry_no === entry.entry_no);
if (idx !== -1) {
entries[idx].order_quantity = entry.order_quantity;
renderBothTables();
updatePaginationControls();
}
} catch (e) {
console.error(e);
}
}
async function removeOrderFromEntry(orderId, entryNo) {
if (!confirm('Are you sure you want to remove this order from the entry?')) {
return;
}
try {
const res = await jsonFetch('/admin/account/remove-order-from-entry', {
method: 'POST',
body: {
entry_no: entryNo,
order_id: orderId
}
});
if (!res.success) throw new Error(res.message || 'Failed to remove order');
// नवीन quantity backend कडून घ्या
const newQty = res.entry.order_quantity ?? res.entry.orderquantity ?? 0;
// 1) currentEditEntry update
if (currentEditEntry) {
currentEditEntry.order_quantity = newQty;
}
// 2) Edit popup मधला field लगेच update
const oqInput = document.getElementById('edit_order_quantity');
if (oqInput) {
oqInput.value = newQty;
}
// 3) इतर UI refresh
loadEntryOrders(entryNo);
loadAvailableOrders();
await refreshEntryHeader(entryNo);
} catch (err) {
alert(err.message || 'Failed to remove order');
console.error(err);
}
}
async function submitEditEntry(e) {
e.preventDefault();
const form = e.target;
const payload = {
entry_no: form.entry_no.value,
description: form.description.value.trim(),
order_quantity: Number(form.order_quantity.value) || 0,
// payment_status: form.payment_status.value,
region: form.region.value,
amount: Number(form.amount.value) || 0
};
if (!payload.description) {
alert('Please enter a valid description.');
return;
}
try {
const btn = form.querySelector('button[type="submit"]');
btn.disabled = true;
btn.textContent = 'Updating...';
const res = await jsonFetch('/admin/account/update-entry', {
method: 'POST',
body: payload
});
if (!res.success) throw new Error(res.message || 'Update failed');
// Success
closeEditModal();
loadDashboard();
} catch (err) {
alert(err.message || 'Failed to update entry');
console.error(err);
} finally {
const btn = form.querySelector('button[type="submit"]');
if (btn) {
btn.disabled = false;
btn.textContent = 'Update Entry';
}
}
}
function openEntryOrdersModal(entryNo) {
// Set entry number in header
document.getElementById('entryOrdersEntryNo-span').textContent = `(${entryNo})`;
// Reset summary values
document.getElementById('ordersTotalCount').textContent = '0';
document.getElementById('ordersTotalQuantity').textContent = '0';
document.getElementById('ordersTotalAmount').textContent = '₹0';
// table loading state
const tbody = document.getElementById('entryOrdersTableBody');
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">Loading orders...</td>
</tr>
`;
// API call: /admin/account/entry-orders/{entryno}
jsonFetch(`/admin/account/entry-orders/${encodeURIComponent(entryNo)}`, {
method: 'GET'
})
.then(res => {
if (!res.success) {
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">Failed to load orders</td>
</tr>
`;
return;
}
const orders = res.orders || [];
if (!orders.length) {
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">No orders associated with this entry</td>
</tr>
`;
return;
}
tbody.innerHTML = '';
let totalQuantity = 0;
let totalAmount = 0;
orders.forEach(order => {
const tr = document.createElement('tr');
const idString = (order.orderid ?? order.id ?? '').toString().trim();
const numericId = parseInt(idString, 10);
const formattedId = isNaN(numericId)
? escapeHtml(idString)
: 'KNT-25-' + String(numericId).padStart(8, '0');
// Calculate values for summary
const quantity = Number(order.qty || order.order_qty || 0);
const amountValue = Number(order.ttl_amount ?? order.ttlamount ?? order.total_amount ?? order.order_amount ?? order.amount ?? 0);
totalQuantity += quantity;
totalAmount += amountValue;
tr.innerHTML = `
<td>
<span class="order-id-badge">${formattedId}</span>
</td>
<td>${escapeHtml(order.markno ?? order.mark_no ?? '')}</td>
<td>${escapeHtml(order.origin ?? '')}</td>
<td>${escapeHtml(order.destination ?? '')}</td>
<td>${escapeHtml(order.ctn ?? '')}</td>
<td><strong>${quantity}</strong></td>
<td><strong>${formatCurrency(amountValue)}</strong></td>
`;
tbody.appendChild(tr);
});
// Update summary
document.getElementById('ordersTotalCount').textContent = orders.length;
document.getElementById('ordersTotalQuantity').textContent = totalQuantity.toLocaleString();
document.getElementById('ordersTotalAmount').textContent = formatCurrency(totalAmount);
})
.catch(() => {
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">Error loading orders</td>
</tr>
`;
});
document.getElementById('entryOrdersModal').classList.add('modal-open');
}
function closeEntryOrdersModal() {
document.getElementById('entryOrdersModal').classList.remove('modal-open');
}
/* ---------- Add Order Modal Functions ---------- */
function openAddOrderModal() {
if (!currentEditEntry) return;
document.getElementById('addOrderModal').classList.add('modal-open');
// नेहमी इथेच ताजे orders load कर
addOrderCurrentPage = 1;
loadAvailableOrdersForAddModal();
}
function closeAddOrderModal() {
document.getElementById('addOrderModal').classList.remove('modal-open');
}
async function confirmAddOrders() {
if (!currentEditEntry) return;
const selected = Array.from(
document.querySelectorAll('#addOrderTableBody input[type="checkbox"]:checked')
).map(i => Number(i.value));
if (selected.length === 0) {
alert('Please select at least one order to add.');
return;
}
try {
const res = await jsonFetch('/admin/account/add-orders-to-entry', {
method: 'POST',
body: {
entry_no: currentEditEntry.entry_no,
order_ids: selected
}
});
if (!res.success) throw new Error(res.message || 'Failed to add orders');
loadEntryOrders(currentEditEntry.entry_no);
loadAvailableOrders();
await refreshEntryHeader(currentEditEntry.entry_no); // ⬅ इथे नवं
closeAddOrderModal();
} catch (err) {
alert(err.message || 'Failed to add orders');
console.error(err);
}
}
/* ---------- Delete Entry Function ---------- */
async function deleteEntry(entryNo) {
if (!confirm(`Are you sure you want to delete entry ${entryNo}? This action cannot be undone.`)) {
return;
}
try {
const res = await jsonFetch('/admin/account/delete-entry', {
method: 'POST',
body: { entry_no: entryNo }
});
if (!res.success) throw new Error(res.message || 'Delete failed');
// Success
loadDashboard();
} catch (err) {
alert(err.message || 'Failed to delete entry');
console.error(err);
}
}
/* ---------- Create Order Inline (Now in Popup) ---------- */
async function submitCreateOrderInline(e){
e.preventDefault();
const form = e.target;
const selected = Array.from(document.querySelectorAll('#consolidateOrdersBody input[type=checkbox]:checked')).map(i=>Number(i.value));
const payload = {
description: form.description.value.trim(),
region: form.region.value,
amount: Number(form.amount.value) || 0,
entry_date: form.entry_date.value,
selected_orders: selected
};
if(!payload.description || payload.amount <= 0){
alert('Please enter a valid description and amount.');
return;
}
try {
const btn = form.querySelector('button[type="submit"]');
btn.disabled = true;
btn.textContent = 'Creating...';
const res = await jsonFetch('/admin/account/create-order', { method:'POST', body: payload });
if(!res.success) throw new Error(res.message || 'Create failed');
// success
form.reset();
closeCreateOrderModal();
loadDashboard();
} catch(err){
alert(err.message || 'Failed to create order');
console.error(err);
} finally {
const btn = form.querySelector('button[type="submit"]');
if(btn){ btn.disabled = false; btn.textContent = 'Create Installment'; }
}
}
/* ---------- Search ---------- */
function handleSearch(){
const q = document.getElementById('main-search').value.trim().toLowerCase();
if(!q){
renderBothTables();
updatePaginationControls();
return;
}
const filtered = entries.filter(e => {
return String(e.entry_no || '').toLowerCase().includes(q) ||
String(e.description || '').toLowerCase().includes(q) ||
String(e.region || '').toLowerCase().includes(q);
});
entries = filtered;
currentPage = 1; // Reset to first page when searching
renderBothTables();
updatePaginationControls();
}
/* ---------- Entry details & installments (Enhanced) ---------- */
async function openEntryDetailsModal(entryNo) {
try {
const res = await jsonFetch('/admin/account/entry/' + encodeURIComponent(entryNo));
if (!res.success) throw new Error(res.message || 'Failed to load entry');
const entry = res.entry;
currentEntry = entry;
// Set header info
document.getElementById('entryDetailsId').textContent = entry.entry_no;
// Calculate values
const originalAmount = Number(entry.amount || 0);
const pendingAmount = Number(entry.pending_amount || 0);
const totalProcessed = originalAmount - pendingAmount;
// Update summary cards
document.getElementById('originalAmount').textContent = formatCurrency(originalAmount);
document.getElementById('totalProcessed').textContent = formatCurrency(totalProcessed);
document.getElementById('pendingBalance').textContent = formatCurrency(pendingAmount);
document.getElementById('totalInstallments').textContent = entry.installments?.length || 0;
// Render installments table
const tbody = document.getElementById('installmentsTableBody');
tbody.innerHTML = '';
if (!entry.installments || entry.installments.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="6" class="empty-state">
<div style="padding: 30px; text-align: center;">
<div style="font-size: 48px; color: #eef3fb; margin-bottom: 16px;">📋</div>
<div style="font-size: 16px; color: #6f7b8f; font-weight: 500;">
No installments found for this entry
</div>
<div style="font-size: 14px; color: #9ba5bb; margin-top: 8px;">
Add your first installment to get started
</div>
</div>
</td>
</tr>
`;
} else {
entry.installments.forEach((ins, idx) => {
const tr = document.createElement('tr');
// Determine installment label
let installmentLabel = 'Original Entry';
if (idx > 0) {
installmentLabel = `Installment ${idx}`;
}
// Status dropdown options with colors
const statusOptions = [
{ value: 'Pending', label: '⏳ Pending', color: '#f59e0b' },
{ value: 'Loading', label: '📦 Loading', color: '#3b82f6' },
{ value: 'Packed', label: '📦 Packed', color: '#8b5cf6' },
{ value: 'Dispatched', label: '🚚 Dispatched', color: '#10b981' },
{ value: 'Delivered', label: '✅ Delivered', color: '#0c6b2e' }
];
let statusOptionsHtml = '';
statusOptions.forEach(opt => {
const selected = ins.status === opt.value ? 'selected' : '';
statusOptionsHtml += `<option value="${opt.value}" ${selected} style="color: ${opt.color}; font-weight: 500;">${opt.label}</option>`;
});
tr.innerHTML = `
<td>
<div style="display: flex; align-items: center; gap: 8px;">
<div style="width: 32px; height: 32px; border-radius: 8px; background: linear-gradient(135deg, #f0f7ff, #e6f0ff); display: flex; align-items: center; justify-content: center; font-weight: 700; color: var(--primary-1);">
${idx + 1}
</div>
<span style="font-weight: 600;">${installmentLabel}</span>
</div>
</td>
<td>
<div style="display: flex; align-items: center; gap: 6px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
${escapeHtml(ins.proc_date || ins.date || '')}
</div>
</td>
<td>${escapeHtml(ins.description || '')}</td>
<td>
<span style="padding: 4px 8px; background: #f0f7ff; border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--primary-1);">
${escapeHtml(ins.region || '')}
</span>
</td>
<td>
<span style="font-weight: 700; color: var(--primary-1);">
${formatCurrency(ins.amount || 0)}
</span>
</td>
<td>
<select class="installment-status-dropdown"
data-id="${ins.id}"
onchange="updateInstallmentStatus(${ins.id}, this.value)"
title="Update installment status">
${statusOptionsHtml}
</select>
</td>
`;
tbody.appendChild(tr);
});
}
// Show modal
document.getElementById('entryDetailsModal').classList.add('modal-open');
} catch (err) {
console.error(err);
alert('Unable to load entry details');
}
}
function closeEntryDetailsModal() {
document.getElementById('entryDetailsModal').classList.remove('modal-open');
}
async function updateInstallmentStatus(id, status) {
try {
const res = await jsonFetch('/admin/account/installment/update-status', {
method: 'POST',
body: { installment_id: id, status: status }
});
if (!res.success) {
alert('Failed to update status');
return;
}
// Refresh details modal instantly
openEntryDetailsModal(res.entry.entry_no);
} catch (err) {
console.error(err);
alert('Unable to update installment status');
}
}
/* ---------- Installment modal ---------- */
function openInstallmentModal(entryNo, desc, region, pending){
currentEntry = { entry_no: entryNo, description: desc, region: region, pending_amount: pending };
document.getElementById('instDetailsRow').innerHTML = `
<div style="min-width:120px"><div class="kv">Entry No</div><div>${escapeHtml(entryNo)}</div></div>
<div style="min-width:160px"><div class="kv">Description</div><div>${escapeHtml(desc)}</div></div>
<div style="min-width:120px"><div class="kv">Region</div><div>${escapeHtml(region)}</div></div>
<div style="min-width:120px"><div class="kv">Pending</div><div>${formatCurrency(pending)}</div></div>
`;
document.getElementById('maxPending').textContent = formatCurrency(pending);
document.getElementById('installmentModal').classList.add('modal-open');
// set example default value to pending (but not exceed)
const amountInput = document.querySelector('#installmentForm input[name="proc_amount"]');
if(amountInput) amountInput.value = pending ? Number(pending) : '';
}
function closeInstallmentModal(){ document.getElementById('installmentModal').classList.remove('modal-open'); }
async function submitInstallment(e){
e.preventDefault();
if(!currentEntry){ alert('No entry selected'); return; }
const form = e.target;
const payload = { entry_no: currentEntry.entry_no, proc_date: form.proc_date.value, amount: Number(form.proc_amount.value) || 0, status: form.status.value };
if(payload.amount > Number(currentEntry.pending_amount || 0)){
alert('Amount cannot exceed pending amount'); return;
}
try {
const btn = form.querySelector('button[type="submit"]');
btn.disabled = true; btn.textContent = 'Submitting...';
const res = await jsonFetch('/admin/account/installment/create', { method:'POST', body: payload });
if(!res.success) throw new Error(res.message || 'Failed to create installment');
closeInstallmentModal();
await loadDashboard();
openEntryDetailsModal(currentEntry.entry_no); // open updated details
} catch(err){
console.error(err);
alert(err.message || 'Failed to create installment');
} finally {
const btn = form.querySelector('button[type="submit"]');
if(btn){ btn.disabled = false; btn.textContent = 'Create Installment'; }
}
}
</script>
@endsection