3481 lines
107 KiB
PHP
3481 lines
107 KiB
PHP
@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(135deg, #667eea 0%, #764ba2 100%);
|
||
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(135deg, #667eea 0%, #764ba2 100%);
|
||
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:8=500px;
|
||
|
||
}
|
||
|
||
|
||
.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-top:25px;
|
||
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(135deg, #667eea 0%, #764ba2 100%);
|
||
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, #e6ebf5 0%, #f9fbff 100%);
|
||
|
||
|
||
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, #e6ebf5 0%, #f9fbff 100%);
|
||
|
||
|
||
|
||
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; background:linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding:20px; border-radius:12px; color:white; margin-top:-20px; margin-left:-20px; margin-right:-15px; margin-top:-15px;">
|
||
<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; background:linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding:16px; border-radius:8px; color:white; margin-top:-15px; margin-left:-20px; margin-right:-15px;">
|
||
<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 => ({'&':'&','<':'<','>':'>','"':'"',"'":"'"}[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>
|
||
|
||
<!-- Order Quantity - Clickable number without box -->
|
||
<td>
|
||
<span onclick="openEntryOrdersModal('${escapeHtml(entry.entry_no)}')"
|
||
style="cursor: pointer; color: #276dea; font-weight: 600; text-decoration: underline; text-decoration-color: #ccc;">
|
||
${entry.order_quantity ?? '-'}
|
||
</span>
|
||
</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 |