2025-11-06 17:09:52 +05:30
@ extends ( 'admin.layouts.app' )
2025-11-11 14:51:35 +05:30
@ section ( 'page-title' , 'Account Dashboard' )
2025-11-06 17:09:52 +05:30
@ section ( 'content' )
2025-11-21 16:07:43 +05:30
< meta name = " csrf-token " content = " { { csrf_token() }} " >
2025-11-11 14:51:35 +05:30
< style >
2025-11-21 16:07:43 +05:30
/* ---------- Base ---------- */
: root {
-- primary - 1 : #1a2951;
-- primary - 2 : #243a72;
-- muted : #9ba5bb;
-- card - bg : linear - gradient ( 180 deg , #ffffff,#f5f7fb);
-- glass : rgba ( 255 , 255 , 255 , 0.6 );
-- success : #34c86c;
-- danger : #ef4f4f;
-- accent : #276dea;
-- rounded : 12 px ;
}
body {
2025-11-12 19:56:06 +05:30
font - family : 'Segoe UI' , Arial , sans - serif ;
2025-11-21 16:07:43 +05:30
background : linear - gradient ( 135 deg , #eef2f7, #f9fbff);
margin : 0 ; padding : 0 ;
color : #253047;
- webkit - font - smoothing : antialiased ;
}
/* container */
. account - container {
padding : 28 px 34 px ;
2025-11-26 23:07:12 +05:30
max - width : 1400 px ;
2025-11-21 16:07:43 +05:30
margin : 18 px auto ;
box - sizing : border - box ;
}
/* header */
. account - header {
margin - bottom : 18 px ;
background : linear - gradient ( 90 deg , var ( -- primary - 1 ), var ( -- primary - 2 ));
padding : 22 px 26 px ;
border - radius : var ( -- rounded );
box - shadow : 0 6 px 18 px rgba ( 34 , 50 , 90 , 0.12 );
2025-11-12 19:56:06 +05:30
color : #fff;
2025-11-21 16:07:43 +05:30
}
. account - header h2 { font - size : 26 px ; font - weight : 700 ; margin : 0 0 6 px 0 ; }
. account - header p { font - size : 14 px ; margin : 2 px 0 ; opacity : 0.95 }
/* top actions row */
. top - actions {
2025-12-23 21:11:53 +05:30
align - items : center ; justify - content : space - between ;
2025-11-21 16:07:43 +05:30
gap : 12 px ; margin : 16 px 0 20 px 0 ; flex - wrap : wrap ;
}
. top - actions . left {
display : flex ; gap : 12 px ; align - items : center ; flex - wrap : wrap ;
}
. search - row input {
width : 360 px ; padding : 10 px 14 px ; border - radius : 8 px ; border : 1 px 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 6 px 18 px rgba ( 26 , 41 , 81 , 0.06 );}
. btn {
display : inline - flex ; align - items : center ; justify - content : center ; gap : 8 px ;
background : linear - gradient ( 90 deg , var ( -- primary - 1 ), var ( -- primary - 2 ));
color : #fff; border:none; padding:10px 16px; border-radius:10px; font-weight:600;
cursor : pointer ; transition : transform . 15 s ease , box - shadow . 15 s ;
}
2025-12-01 10:34:27 +05:30
. btn . ghost { background : transparent ; color : var ( -- primary - 1 ); border : 1.5 px solid #dbe4f5; box-shadow:none; }
2025-12-23 21:11:53 +05:30
. btn : hover { transform : translateY ( - 3 px ); box - shadow : 0 8 px 26 px rgba ( 227 , 229 , 234 , 0.12 ); }
2025-11-21 16:07:43 +05:30
/* account panels */
2025-11-26 23:07:12 +05:30
. account - panels {
display : flex ;
gap : 22 px ;
2025-12-01 10:34:27 +05:30
align - items : stretch ;
2025-11-26 23:07:12 +05:30
flex - wrap : wrap ;
}
2025-12-01 10:34:27 +05:30
/* Payment block with pagination */
. payment - block {
flex : 1 ;
min - width : 48 % ;
display : flex ;
flex - direction : column ;
}
2025-11-21 16:07:43 +05:30
. panel - card {
2025-11-26 23:07:12 +05:30
background : var ( -- card - bg );
border - radius : 12 px ;
box - shadow : 0 8 px 20 px rgba ( 25 , 40 , 80 , 0.06 );
2025-12-23 21:11:53 +05:30
padding : 20 px ; /* 005 */
2025-11-26 23:07:12 +05:30
box - sizing : border - box ;
overflow - x : auto ;
transition : transform . 12 s , box - shadow . 12 s ;
2025-12-01 10:34:27 +05:30
min - height : 520 px ;
2025-12-23 21:11:53 +05:30
/* display: flex; */ /* 005 */
2025-11-26 23:07:12 +05:30
flex - direction : column ;
2025-12-01 10:34:27 +05:30
flex : 1 ;
height : 100 % ;
2025-11-21 16:07:43 +05:30
}
. panel - card : hover { transform : translateY ( - 4 px ); box - shadow : 0 12 px 28 px rgba ( 25 , 40 , 80 , 0.08 ); }
2025-11-26 23:07:12 +05:30
. panel - title {
font - weight : 700 ;
font - size : 16 px ;
color : var ( -- primary - 1 );
margin - bottom : 16 px ;
display : flex ;
align - items : center ;
justify - content : space - between ;
}
2025-11-21 16:07:43 +05:30
/* table */
2025-11-26 23:07:12 +05:30
table {
width : 100 % ;
border - collapse : collapse ;
min - width : 720 px ;
font - size : 14 px ;
flex : 1 ;
}
th , td {
padding : 12 px 14 px ;
text - align : left ;
border - bottom : 1 px solid #eef3fb;
white - space : nowrap ;
color : #2d3b53;
}
th {
background : linear - gradient ( 90 deg , #f6f9ff,#fbfdff);
color : #4a5570;
font - weight : 700 ;
font - size : 13 px ;
}
2025-11-21 16:07:43 +05:30
tr : hover td { background : #fbfdff; }
. entry - link { color : var ( -- accent ); text - decoration : underline ; cursor : pointer ; font - weight : 700 ; }
/* badges */
2025-11-26 23:07:12 +05:30
/* === Modern Status Badges === */
. status - badge {
display : inline - block ;
padding : 6 px 16 px ;
border - radius : 999 px ;
font - size : 13 px ;
font - weight : 700 ;
color : #fff;
min - width : 76 px ;
text - align : center ;
box - shadow : 0 2 px 8 px rgba ( 33 , 43 , 90 , 0.07 );
letter - spacing : 0.1 px ;
2025-12-01 10:34:27 +05:30
background : #6b7280;
2025-11-26 23:07:12 +05:30
transition : box - shadow 0.22 s , transform 0.17 s , background 0.22 s ;
vertical - align : middle ;
margin : 3 px 2 px ;
cursor : default ;
backdrop - filter : blur ( 2 px );
width : 99 px ;
}
. status - badge : hover {
transform : translateY ( - 3 px ) scale ( 1.045 );
box - shadow : 0 4 px 16 px rgba ( 33 , 43 , 90 , 0.16 );
opacity : 0.96 ;
}
/* High-impact, soft gradients for each status */
. status - unpaid {
background : linear - gradient ( 90 deg , #ff5959, #dc3545);
border : 1.5 px solid #d42c3f21;
width : 95 px ;
}
. status - paid {
background : linear - gradient ( 90 deg , #36d399 0%, #4ade80 100%);
border : 1.5 px solid #31b47a1a;
width : 95 px ;
}
. status - loading {
background : linear - gradient ( 90 deg , #509cf8 0%, #3f79d3 100%);
border : 1.5 px solid #1665c320;
width : 95 px ;
}
. status - dispatched {
background : linear - gradient ( 90 deg , #9775fa 0%, #845ef7 100%);
border : 1.5 px solid #9775fa40;
width : 95 px ;
}
. pending - badge - red {
background : linear - gradient ( 90 deg , #f43f5e, #ef4444);
border : 1.5 px solid #f43f5e41;
width : 95 px ;
}
. pending - badge - green {
background : linear - gradient ( 90 deg , #10b981 0%, #22d3ee 100%);
border : 1.5 px solid #10b98141;
width : 95 px ;
}
. status - delivered {
background : linear - gradient ( 90 deg , #22c55e 0%, #16a34a 100%);
border : 1.5 px solid #22c55e44;
width : 95 px ;
}
2025-11-21 16:07:43 +05:30
/* 3-state toggle */
. toggle - switch - btn {
appearance : none ;
- webkit - appearance : none ;
2025-12-23 21:11:53 +05:30
width : 64 px ;
height : 26.5 px ; /* 005 */
2025-12-01 10:34:27 +05:30
background : #f25b5b;
2025-11-21 16:07:43 +05:30
border : 2 px solid #f25b5b;
border - radius : 999 px ;
position : relative ;
outline : none ;
cursor : pointer ;
transition : background . 22 s , border . 22 s ;
}
. toggle - switch - btn :: before {
content : " " ;
width : 20 px ;
height : 20 px ;
background : #fff;
border - radius : 50 % ;
position : absolute ;
top : 1.7 px ;
left : 2 px ;
transition : transform . 22 s ;
box - shadow : 0 2 px 8 px rgba ( 0 , 0 , 0 , 0.15 );
}
/* YELLOW */
. toggle - switch - btn . mid {
background : #f2c94c;
border - color : #f2c94c;
}
. toggle - switch - btn . mid :: before {
transform : translateX ( 20 px );
}
/* GREEN */
. toggle - switch - btn . checked {
background : #43d05c;
border - color : #43d05c;
}
. toggle - switch - btn . checked :: before {
transform : translateX ( 38 px );
}
/* plus button */
2025-11-26 23:07:12 +05:30
. plus - btn {
display : inline - block ;
width : 36 px ;
height : 36 px ;
border - radius : 10 px ;
background : #fff;
color : var ( -- primary - 1 );
border : 1.5 px solid #e6edf8;
font - size : 1.15 rem ;
font - weight : 700 ;
text - align : center ;
line - height : 34 px ;
cursor : pointer ;
transition : transform . 12 s ;
}
2025-11-21 16:07:43 +05:30
. plus - btn : hover { transform : translateY ( - 3 px ); box - shadow : 0 8 px 16 px rgba ( 33 , 47 , 90 , 0.04 ); }
2025-11-26 23:07:12 +05:30
/* ---------- 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 : 18 px ;
}
. create - order - modal . modal - open { display : flex ; }
. create - order - modal . modal - box {
background : #fff;
border - radius : 12 px ;
padding : 20 px 24 px ;
box - shadow : 0 14 px 40 px rgba ( 18 , 30 , 60 , 0.12 );
max - width : 1200 px ;
width : 100 % ;
max - height : 92 vh ;
overflow : auto ;
}
2025-11-21 16:07:43 +05:30
/* consolidated orders area */
. consolidate - area {
background : linear - gradient ( 90 deg , #fbfbff,#f9fcff);
2025-11-26 23:07:12 +05:30
border : 1 px solid #eef5ff;
padding : 14 px ;
border - radius : 10 px ;
box - shadow : inset 0 1 px 0 rgba ( 255 , 255 , 255 , 0.6 );
margin - top : 12 px ;
overflow : auto ;
min - height : 400 px ;
display : flex ;
flex - direction : column ;
2025-11-21 16:07:43 +05:30
}
/* compact table in consolidate */
2025-11-26 23:07:12 +05:30
#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 : 15 px ;
padding : 12 px 0 ;
border - top : 1 px solid #eef3fb;
2025-12-23 21:11:53 +05:30
/* margin-right:550px; */
2025-11-26 23:07:12 +05:30
}
. pagination - info {
font - size : 13 px ;
color : var ( -- muted );
font - weight : 600 ;
}
. pagination - controls {
display : flex ;
align - items : center ;
gap : 8 px ;
2025-12-23 21:11:53 +05:30
position : absolute ;
right : 16 px ; /* 005 */
2025-12-01 10:34:27 +05:30
2025-12-03 16:17:14 +05:30
}
. pagination - controls1 {
display : flex ;
align - items : center ;
gap : 8 px ;
2025-12-23 21:11:53 +05:30
/* margin-right:-550px; */
2025-12-03 16:17:14 +05:30
2025-11-26 23:07:12 +05:30
}
2025-12-03 16:17:14 +05:30
. pagination - controls2 {
display : flex ;
align - items : center ;
gap : 8 px ;
margin - right :- 550 px ;
}
2025-11-26 23:07:12 +05:30
. pagination - btn {
background : #fff;
border : 1 px solid #e3eaf6;
color : var ( -- primary - 1 );
padding : 8 px 12 px ;
border - radius : 6 px ;
font - size : 13 px ;
font - weight : 600 ;
cursor : pointer ;
transition : all 0.3 s ease ;
display : flex ;
align - items : center ;
justify - content : center ;
min - width : 40 px ;
height : 32 px ;
}
. 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 : 1 px solid #e3eaf6;
color : var ( -- primary - 1 );
padding : 6 px 12 px ;
border - radius : 6 px ;
font - size : 13 px ;
font - weight : 600 ;
cursor : pointer ;
transition : all 0.3 s ease ;
min - width : 36 px ;
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 : 4 px ;
align - items : center ;
}
. pagination - ellipsis {
color : var ( -- muted );
font - size : 13 px ;
padding : 0 4 px ;
}
/* Image-based pagination buttons */
. pagination - img - btn {
background : #fff;
border : 1 px solid #e3eaf6;
border - radius : 6 px ;
cursor : pointer ;
transition : all 0.3 s ease ;
display : flex ;
align - items : center ;
justify - content : center ;
min - width : 40 px ;
height : 32 px ;
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 : 16 px ;
height : 16 px ;
filter : brightness ( 0 ) saturate ( 100 % ) invert ( 26 % ) sepia ( 89 % ) saturate ( 748 % ) hue - rotate ( 201 deg ) brightness ( 93 % ) contrast ( 89 % );
transition : filter 0.3 s ease ;
}
. pagination - img - btn : hover : not ( : disabled ) img {
filter : brightness ( 0 ) saturate ( 100 % ) invert ( 100 % ) sepia ( 100 % ) saturate ( 0 % ) hue - rotate ( 288 deg ) brightness ( 106 % ) contrast ( 101 % );
}
. pagination - img - btn : disabled img {
filter : brightness ( 0 ) saturate ( 100 % ) invert ( 84 % ) sepia ( 8 % ) saturate ( 165 % ) hue - rotate ( 179 deg ) brightness ( 89 % ) contrast ( 86 % );
}
2025-11-21 16:07:43 +05:30
/* ---------- Entry Details Modal (existing) ---------- */
2025-11-26 23:07:12 +05:30
. 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 : 18 px ;
}
2025-11-21 16:07:43 +05:30
. modal - fade1 . modal - open { display : flex ; }
2025-11-26 23:07:12 +05:30
. modal - box1 {
background : #fff;
border - radius : 12 px ;
padding : 20 px 24 px ;
box - shadow : 0 14 px 40 px rgba ( 18 , 30 , 60 , 0.12 );
max - width : 1200 px ;
width : 100 % ;
max - height : 92 vh ;
overflow : auto ;
min - height : 500 px ;
}
2025-12-02 18:11:58 +05:30
#entryOrdersModal {
z - index : 1300 ;
}
2025-11-21 16:07:43 +05:30
/* entry summary cards */
2025-11-26 23:07:12 +05:30
. entry - summary - cards {
display : flex ;
gap : 16 px ;
margin - bottom : 20 px ;
flex - wrap : wrap ;
}
. entry - summary - card {
background : #fbfdff;
border : 1 px solid #eef6ff;
padding : 16 px ;
border - radius : 10 px ;
min - width : 180 px ;
box - shadow : 0 6 px 18 px rgba ( 22 , 36 , 72 , 0.03 );
flex : 1 ;
}
2025-11-21 16:07:43 +05:30
. entry - summary - label { font - size : 12 px ; color : var ( -- muted ); }
. entry - summary - value { font - size : 18 px ; font - weight : 700 ; color : #253047; margin-top:6px; }
/* installment modal */
#installmentModal .modal-box1 { max-width:720px; min-width:380px; }
/* small helpers */
. helper - note { font - size : 12 px ; color : #7687a3; margin-top:6px; display:block; }
. empty - state { padding : 18 px ; text - align : center ; color : #6f7b8f; }
. kv { font - weight : 700 ; color : #26364f; }
2025-11-26 23:07:12 +05:30
/* form styles */
2025-12-02 18:11:58 +05:30
. input , select {
width : 100 % ;
padding : 12 px 14 px ;
border - radius : 8 px ;
border : 1.3 px solid #e3eaf6;
background : #fff;
font - size : 14 px ;
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 12 px center ;
padding - right : 40 px ;
transition : all 0.2 s ease ;
}
. installment - status - dropdown : hover {
border - color : #c2d1f0;
}
. installment - status - dropdown : focus {
outline : none ;
border - color : #4d7cfe;
box - shadow : 0 0 0 3 px rgba ( 77 , 124 , 254 , 0.1 );
}
/* Status-specific styling for selected option */
. installment - status - dropdown option : checked {
font - weight : 600 ;
}
2025-11-26 23:07:12 +05:30
. create - grid {
display : grid ;
grid - template - columns : 1 fr 1 fr ;
gap : 16 px ;
align - items : start ;
margin - bottom : 16 px ;
}
. create - actions {
display : flex ;
gap : 12 px ;
justify - content : flex - end ;
margin - top : 16 px ;
}
2025-12-02 18:11:58 +05:30
/* Combined filters row styling */
. combined - filters - row {
2025-12-23 21:11:53 +05:30
display : ruby ; /* 005 */
2025-12-01 10:34:27 +05:30
gap : 12 px ;
align - items : center ;
margin - bottom : 16 px ;
flex - wrap : wrap ;
padding : 14 px 16 px ;
background : linear - gradient ( 90 deg , #f8fbff, #f5f9ff);
border - radius : 10 px ;
border : 1 px solid #eef3fb;
2025-12-02 18:11:58 +05:30
width : 365 px ;
}
. right {
2025-12-23 21:11:53 +05:30
/* margin - left : auto ;
margin - top :- 16 px ; */ /* 005 */
2025-12-02 18:11:58 +05:30
}
. filter - group1 {
display : flex ;
flex - direction : column ;
gap : 6 px ;
width : 159 px ;
}
. filter - group2 {
display : flex ;
flex - direction : column ;
gap : 6 px ;
width : 160 px ;
2025-12-01 10:34:27 +05:30
}
2025-12-02 18:11:58 +05:30
. filter - group3 {
flex : 1 ;
width : 159 px ;
}
. filter - label {
display : block ;
font - size : 13 px ;
font - weight : 600 ;
color : #44546a;
margin - bottom : 6 px ;
}
. payment - filters - section {
display : flex ;
align - items : flex - end ;
gap : 16 px ;
}
. filter - group4 {
2025-12-01 10:34:27 +05:30
display : flex ;
flex - direction : column ;
gap : 6 px ;
2025-12-02 18:11:58 +05:30
width : 160 px ;
2025-12-01 10:34:27 +05:30
}
2025-12-02 18:11:58 +05:30
. filter - label {
display : block ;
font - size : 13 px ;
font - weight : 600 ;
color : #44546a;
margin - bottom : 2 px ;
}
/* Status option colors */
. filter - control option {
padding : 10 px 12 px ;
font - size : 14 px ;
}
. 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 : 12 px ;
height : 12 px ;
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 : 8 px 12 px ;
}
. 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 : 6 px ;
width : 510 px ;
}
/* Modern filter alternative with icons */
. filter - status - buttons {
display : flex ;
gap : 8 px ;
margin - top : 8 px ;
}
. status - btn {
display : flex ;
align - items : center ;
gap : 6 px ;
padding : 6 px 12 px ;
border - radius : 6 px ;
border : 1 px solid #e3eaf6;
background : #fff;
font - size : 13 px ;
font - weight : 500 ;
cursor : pointer ;
transition : all 0.2 s ease ;
}
. status - btn : hover {
background - color : #f8fafc;
transform : translateY ( - 1 px );
}
. status - btn . active {
border - color : transparent ;
box - shadow : 0 2 px 4 px 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;
}
2025-12-01 10:34:27 +05:30
. filter - label {
font - size : 13 px ;
font - weight : 600 ;
color : var ( -- primary - 1 );
}
. filter - control {
padding : 8 px 12 px ;
border - radius : 8 px ;
border : 1 px solid #e3eaf6;
background : #fff;
font - size : 14 px ;
min - width : 140 px ;
}
. filter - control : focus {
outline : none ;
border - color : var ( -- primary - 1 );
box - shadow : 0 0 0 2 px rgba ( 26 , 41 , 81 , 0.1 );
}
2025-12-02 18:11:58 +05:30
/* Table-specific filter sections */
. payment - filters - section , . order - filters - section {
2025-12-01 10:34:27 +05:30
display : flex ;
2025-12-02 18:11:58 +05:30
gap : 12 px ;
align - items : center ;
flex - wrap : wrap ;
margin - bottom : 16 px ;
padding : 12 px 14 px ;
background : linear - gradient ( 90 deg , #f9fbff, #f7faff);
border - radius : 8 px ;
border : 1 px solid #eef3fb;
2025-12-01 10:34:27 +05:30
}
/* Action buttons in table */
. action - btn {
background : transparent ;
border : none ;
cursor : pointer ;
padding : 6 px 8 px ;
border - radius : 6 px ;
transition : all 0.2 s 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 : 4 px ;
justify - content : center ;
}
/* Edit modal styles */
. edit - modal . modal - box1 {
max - width : 800 px ;
2025-12-02 18:11:58 +05:30
z - index : 1250 ;
2025-12-01 10:34:27 +05:30
}
. edit - form - grid {
display : grid ;
grid - template - columns : 1 fr 1 fr ;
gap : 16 px ;
margin - bottom : 16 px ;
}
. edit - form - actions {
display : flex ;
gap : 12 px ;
justify - content : flex - end ;
margin - top : 20 px ;
}
/* Order management in edit modal */
. order - management - section {
margin - top : 20 px ;
border - top : 1 px solid #eef3fb;
padding - top : 16 px ;
}
. order - management - header {
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : 12 px ;
}
. order - management - title {
font - weight : 700 ;
color : var ( -- primary - 1 );
font - size : 16 px ;
}
. orders - table - container {
max - height : 300 px ;
overflow - y : auto ;
border : 1 px solid #eef3fb;
border - radius : 8 px ;
margin - bottom : 12 px ;
}
. orders - table {
width : 100 % ;
border - collapse : collapse ;
font - size : 13 px ;
}
. orders - table th {
background : #f8fbff;
padding : 10 px 12 px ;
text - align : left ;
font - weight : 600 ;
color : var ( -- primary - 1 );
border - bottom : 1 px solid #eef3fb;
position : sticky ;
top : 0 ;
}
. orders - table td {
padding : 10 px 12 px ;
border - bottom : 1 px solid #f1f6ff;
}
. order - actions {
display : flex ;
gap : 6 px ;
}
. remove - order - btn {
background : var ( -- danger );
color : white ;
border : none ;
border - radius : 4 px ;
padding : 4 px 8 px ;
font - size : 12 px ;
cursor : pointer ;
transition : background 0.2 s ;
}
2025-12-23 21:11:53 +05:30
. combined - top - row . btn : hover {
color : #ffffff !important;
background - color : inherit ! important ;
border - color : inherit ! important ;
transform : none ! important ;
box - shadow : none ! important ;
}
2025-12-01 10:34:27 +05:30
. remove - order - btn : hover {
background : #d42c3f;
}
/* Add Order Modal */
. add - order - modal . modal - box1 {
max - width : 1000 px ;
}
. add - order - actions {
display : flex ;
justify - content : flex - end ;
gap : 12 px ;
margin - top : 16 px ;
}
2025-11-21 16:07:43 +05:30
/* responsive */
@ media ( max - width : 980 px ){
2025-11-26 23:07:12 +05:30
. create - grid { grid - template - columns : 1 fr ; }
2025-11-21 16:07:43 +05:30
. panel - card { min - width : 100 % ; }
. search - row input { width : 220 px ; }
2025-11-26 23:07:12 +05:30
. pagination - container { flex - direction : column ; gap : 10 px ; align - items : stretch ; }
. pagination - controls { justify - content : center ; }
. account - container { padding : 20 px ; }
. panel - card { padding : 18 px ; }
2025-12-02 18:11:58 +05:30
. combined - filters - row { flex - direction : column ; align - items : stretch ; }
2025-12-01 10:34:27 +05:30
. filter - group { width : 100 % ; }
. filter - control { min - width : auto ; }
. edit - form - grid { grid - template - columns : 1 fr ; }
2025-12-02 18:11:58 +05:30
. payment - filters - section , . order - filters - section { flex - direction : column ; align - items : stretch ; }
2025-11-26 23:07:12 +05:30
}
@ media ( max - width : 768 px ){
. account - header { padding : 18 px ; }
. top - actions { flex - direction : column ; align - items : stretch ; }
. top - actions . left { justify - content : center ; }
. entry - summary - cards { gap : 12 px ; }
. entry - summary - card { min - width : 140 px ; }
2025-12-01 10:34:27 +05:30
. edit - form - grid { grid - template - columns : 1 fr ; }
. order - management - header { flex - direction : column ; align - items : flex - start ; gap : 10 px ; }
2025-11-21 16:07:43 +05:30
}
2025-11-27 19:39:36 +05:30
/* Table pagination wrapper */
. table - pagination - wrapper {
margin - top : 20 px ;
border - top : 1 px solid #eef3fb;
padding - top : 15 px ;
}
/* Modal pagination wrapper */
. modal - pagination - wrapper {
margin - top : 15 px ;
border - top : 1 px solid #eef3fb;
padding - top : 12 px ;
}
2025-12-01 10:34:27 +05:30
/* Right-aligned pagination */
. pagination - right {
justify - content : flex - end ;
}
2025-12-02 18:11:58 +05:30
/* Combined top row styling */
. combined - top - row {
display : flex ;
gap : 12 px ;
align - items : center ;
flex - wrap : wrap ;
}
2025-12-22 16:49:27 +05:30
/* नवीन responsive styles */
@ media ( max - width : 980 px ){
. account - container {
padding : 20 px ! important ;
margin : 0 auto ;
}
. panel - card {
min - width : 100 % ! important ;
padding : 18 px ! important ;
}
. search - row input {
width : 220 px ! important ;
}
. pagination - container {
flex - direction : column ;
gap : 10 px ;
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 : 768 px ){
body {
overflow - x : hidden ;
}
. account - container {
padding : 15 px ! important ;
}
. account - header {
padding : 18 px ! important ;
}
. top - actions {
flex - direction : column ;
align - items : stretch ;
gap : 10 px ;
}
. top - actions . left {
justify - content : center ;
}
. account - panels {
gap : 15 px ;
}
. search - row input {
width : 100 % ! important ;
max - width : 300 px ;
}
/* pagination adjustments for mobile */
. pagination - controls ,
. pagination - controls1 ,
. pagination - controls2 {
margin - right : 0 ! important ;
justify - content : center ;
}
. pagination - container {
margin - right : 0 ! important ;
}
}
/* Zoom out specific fix */
@ media screen and ( max - width : 1200 px ) {
. account - container {
padding : 20 px ;
max - width : 95 % ;
}
. payment - block {
min - width : 100 % ;
}
}
/* Extra small screens */
@ media screen and ( max - width : 576 px ) {
. account - container {
padding : 10 px ;
}
. account - header h2 {
font - size : 22 px ;
}
. panel - card {
padding : 15 px ;
min - height : auto ;
}
. pagination - container {
padding : 10 px 0 ;
}
. pagination - info {
font - size : 12 px ;
}
}
/* Prevent horizontal scroll on very small screens */
@ media screen and ( max - width : 400 px ) {
body {
min - width : 320 px ;
}
. account - container {
padding : 10 px 5 px ;
}
. table - responsive {
overflow - x : auto ;
- webkit - overflow - scrolling : touch ;
}
}
/* Fix for sidebar gap on zoom out */
html , body {
max - width : 100 vw ;
overflow - x : hidden ;
}
/* Ensure all containers are properly constrained */
. container , . container - fluid , . account - container {
max - width : 100 % ;
overflow - x : hidden ;
}
2025-11-11 14:51:35 +05:30
</ style >
< div class = " account-container " >
<!-- Header -->
< div class = " account-header " >
< h2 > Account Dashboard </ h2 >
2025-11-21 16:07:43 +05:30
< p > Viewing : All Regions — Workflow : Manage payments and dispatch .</ p >
2025-11-11 14:51:35 +05:30
</ div >
2025-11-21 16:07:43 +05:30
2025-12-02 18:11:58 +05:30
<!-- Combined Top Row - Search , Create Installment , Date Filters , Refresh -->
2025-11-21 16:07:43 +05:30
< div class = " top-actions " >
2025-12-02 18:11:58 +05:30
< div class = " combined-top-row " >
<!-- Search Section -->
2025-11-21 16:07:43 +05:30
< 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 >
2025-12-02 18:11:58 +05:30
<!-- Create Installment Button -->
2025-12-05 17:16:02 +05:30
@ can ( 'account.create_order' )
< button class = " btn " id = " openCreateModalBtn " >+ Create New Order </ button >
@ endcan
2025-12-02 18:11:58 +05:30
<!-- 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 -->
2025-11-21 16:07:43 +05:30
< button class = " btn " id = " refreshBtn " > ⟳ Refresh </ button >
2025-12-02 18:11:58 +05:30
</ div >
2025-11-11 14:51:35 +05:30
</ div >
2025-11-21 16:07:43 +05:30
<!-- Panels -->
2025-11-11 14:51:35 +05:30
< div class = " account-panels " id = " account-panels " >
2025-12-01 10:34:27 +05:30
<!-- 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 >
2025-12-02 18:11:58 +05:30
<!-- 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 >
2025-12-01 10:34:27 +05:30
< 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 >
2025-11-21 16:07:43 +05:30
</ div >
2025-12-02 18:11:58 +05:30
2025-12-01 10:34:27 +05:30
<!-- Pagination for Payment Table OUTSIDE table but SAME column -->
< div class = " table-pagination-wrapper pagination-right " >
2025-11-27 19:39:36 +05:30
< 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 >
2025-11-11 14:51:35 +05:30
</ div >
2025-11-21 16:07:43 +05:30
2025-11-27 19:39:36 +05:30
<!-- Order Dispatch Table -->
2025-11-11 14:51:35 +05:30
< div class = " panel-card " >
2025-11-21 16:07:43 +05:30
< div class = " panel-title " >
< span > Order Dispatch Status </ span >
< span style = " font-size:13px;color:var(--muted) " > Actions : < strong >+ Add Installment </ strong ></ span >
</ div >
2025-12-01 10:34:27 +05:30
2025-12-02 18:11:58 +05:30
<!-- 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 >
2025-12-01 10:34:27 +05:30
2025-11-11 14:51:35 +05:30
< 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 >
2025-11-21 16:07:43 +05:30
< tbody id = " orderTableBody " >
2025-11-27 19:39:36 +05:30
<!-- Entries will be loaded here -->
2025-11-11 14:51:35 +05:30
</ tbody >
</ table >
2025-12-01 10:34:27 +05:30
<!-- No pagination for second table -->
2025-11-11 14:51:35 +05:30
</ div >
</ div >
</ div >
2025-11-26 23:07:12 +05:30
<!-- 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; " >
2025-12-23 21:11:53 +05:30
< div style = " font-size:20px; font-weight:800; " > Create New Installment </ div >
2025-11-26 23:07:12 +05:30
< 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 " >
2025-11-27 19:39:36 +05:30
<!-- Orders will be loaded here -->
2025-11-26 23:07:12 +05:30
</ tbody >
</ table >
2025-11-27 19:39:36 +05:30
<!-- Pagination for Create Order Modal -->
2025-12-01 10:34:27 +05:30
< div class = " modal-pagination-wrapper pagination-right " >
2025-11-27 19:39:36 +05:30
< div class = " pagination-container " >
< div class = " pagination-info " id = " modalPageInfo " > Showing 0 entries </ div >
2025-12-03 16:17:14 +05:30
< div class = " pagination-controls2 " >
2025-11-27 19:39:36 +05:30
< 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 >
2025-11-26 23:07:12 +05:30
</ div >
</ div >
</ div >
</ div >
</ div >
< div class = " create-actions " >
< button type = " button " class = " btn ghost " id = " cancelCreateModal " > Cancel </ button >
2025-12-05 17:16:02 +05:30
< button type = " submit " class = " btn " > Create Order </ button >
2025-11-26 23:07:12 +05:30
</ div >
</ form >
</ div >
</ div >
2025-11-21 16:07:43 +05:30
<!-- ENTRY DETAILS MODAL -->
2025-11-12 19:56:06 +05:30
< div class = " modal-fade1 " id = " entryDetailsModal " >
< div class = " modal-box1 entry-details-modal " >
2025-11-21 16:07:43 +05:30
< div style = " display:flex;justify-content:space-between;align-items:center;margin-bottom:12px; " >
< div >
< h2 style = " margin:0;font-size:20px;color:#223256;font-weight:800 " > Entry Details — < span id = " entryDetailsId " >-</ span ></ h2 >
< div style = " font-size:13px;color:var(--muted) " > Complete view of all installments for this entry .</ div >
</ div >
< div >< button class = " btn ghost " onclick = " closeEntryDetailsModal() " > Close </ button ></ div >
2025-11-12 19:56:06 +05:30
</ div >
2025-11-21 16:07:43 +05:30
2025-11-12 19:56:06 +05:30
< div class = " entry-summary-cards " >
< div class = " entry-summary-card " >
< div class = " entry-summary-label " > Original Amount </ div >
2025-11-21 16:07:43 +05:30
< div class = " entry-summary-value " id = " originalAmount " >-</ div >
2025-11-12 19:56:06 +05:30
</ div >
< div class = " entry-summary-card " >
< div class = " entry-summary-label " > Total Processed </ div >
2025-11-21 16:07:43 +05:30
< div class = " entry-summary-value " id = " totalProcessed " >-</ div >
2025-11-12 19:56:06 +05:30
</ div >
< div class = " entry-summary-card " >
< div class = " entry-summary-label " > Pending Balance </ div >
2025-11-21 16:07:43 +05:30
< div class = " entry-summary-value " id = " pendingBalance " >-</ div >
2025-11-12 19:56:06 +05:30
</ div >
< div class = " entry-summary-card " >
< div class = " entry-summary-label " > Total Installments </ div >
2025-11-21 16:07:43 +05:30
< div class = " entry-summary-value " id = " totalInstallments " >-</ div >
2025-11-12 19:56:06 +05:30
</ div >
</ div >
2025-11-21 16:07:43 +05:30
< table class = " entry-installments-table " style = " width:100%; border-collapse:collapse; " >
2025-11-12 19:56:06 +05:30
< 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 " >
2025-11-21 16:07:43 +05:30
< tr >< td colspan = " 6 " class = " empty-state " > No installments yet </ td ></ tr >
2025-11-12 19:56:06 +05:30
</ tbody >
</ table >
2025-11-21 16:07:43 +05:30
< div style = " display:flex; justify-content: flex-end; gap:12px; margin-top:16px; " >
< button type = " button " class = " btn ghost " onclick = " closeEntryDetailsModal() " > Close </ button >
2025-12-05 17:16:02 +05:30
@ can ( 'account.add_installment' )
2025-11-21 16:07:43 +05:30
< button type = " button " class = " btn " id = " addInstallmentFromDetails " >+ Add New Installment </ button >
2025-12-05 17:16:02 +05:30
@ endcan
2025-11-12 19:56:06 +05:30
</ div >
</ div >
</ div >
2025-12-02 18:11:58 +05:30
<!-- ENTRY ORDERS MODAL -->
< div class = " modal-fade1 " id = " entryOrdersModal " >
< div class = " modal-box1 " style = " max-width: 1000px; " >
< div style = " display:flex;align-items:center;justify-content:space-between;margin-bottom:12px; " >
< div >
< h2 style = " margin:0;font-size:20px;color:#223256;font-weight:800; " >
Entry Orders
< span id = " entryOrdersEntryNo-span " style = " font-size:14px;color:#6b7280;margin-left:8px; " ></ span >
</ h2 >
< div style = " font-size:13px;color:#6f7b8f; " >
All orders associated with this entry .
</ div >
</ div >
< button class = " btn ghost " type = " button " onclick = " closeEntryOrdersModal() " >
Close
</ button >
</ div >
< div class = " orders-table-container " >
< table class = " orders-table " style = " width:100%;border-collapse:collapse;font-size:13px; " >
< 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 " > No orders associated with this entry </ td >
</ tr >
</ tbody >
</ table >
</ div >
</ div >
</ div >
2025-11-21 16:07:43 +05:30
<!-- Installment Modal -->
2025-11-11 14:51:35 +05:30
< div class = " modal-fade1 " id = " installmentModal " >
2025-11-21 16:07:43 +05:30
< div class = " modal-box1 " style = " max-width:720px; " >
< div style = " display:flex;align-items:center; justify-content:space-between; margin-bottom:12px; " >
< div style = " font-size:18px;font-weight:800;color:#243a72; " >+ Add New Installment </ div >
2025-11-26 23:07:12 +05:30
< button class = " btn ghost " onclick = " closeInstallmentModal() " > ✕ </ button >
2025-11-11 14:51:35 +05:30
</ div >
2025-11-21 16:07:43 +05:30
< 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 >
2025-11-11 14:51:35 +05:30
< form id = " installmentForm " autocomplete = " off " >
2025-11-21 16:07:43 +05:30
< 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') }} " >
2025-11-11 14:51:35 +05:30
</ div >
2025-11-21 16:07:43 +05:30
< 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 >
2025-11-11 14:51:35 +05:30
</ div >
2025-11-21 16:07:43 +05:30
< 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 >
2025-11-11 14:51:35 +05:30
</ select >
</ div >
</ div >
2025-11-21 16:07:43 +05:30
< div style = " display:flex; justify-content:flex-end; gap:12px; margin-top:14px; " >
< button type = " button " class = " btn ghost " onclick = " closeInstallmentModal() " > Cancel </ button >
2025-12-05 17:16:02 +05:30
@ can ( 'account.add_installment' )
< button type = " submit " class = " btn " > Create Installment2 </ button >
@ endcan
2025-11-11 14:51:35 +05:30
</ div >
</ form >
</ div >
</ div >
2025-12-01 10:34:27 +05:30
<!-- 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 >
2025-12-04 11:21:46 +05:30
< 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 >
2025-12-02 18:11:58 +05:30
</ div >
2025-12-04 11:21:46 +05:30
<!-- < div >
2025-12-01 10:34:27 +05:30
< 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 >
2025-12-04 11:21:46 +05:30
</ div >-->
2025-12-01 10:34:27 +05:30
< 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 >
2025-12-03 16:17:14 +05:30
< div class = " pagination-controls1 " >
2025-12-01 10:34:27 +05:30
< 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 >
2025-12-05 17:16:02 +05:30
< 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 >
2025-11-21 16:07:43 +05:30
< script >
/* ---------- Helpers & state ---------- */
2025-12-02 18:11:58 +05:30
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
}
}
2025-11-21 16:07:43 +05:30
const csrfToken = document . querySelector ( 'meta[name="csrf-token"]' ) . getAttribute ( 'content' );
function jsonFetch ( url , opts = {}) {
2025-12-01 10:34:27 +05:30
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' );
})
);
2025-11-21 16:07:43 +05:30
}
2025-12-01 10:34:27 +05:30
2025-11-21 16:07:43 +05:30
let entries = [];
let availableOrders = [];
let currentEntry = null ;
2025-12-01 10:34:27 +05:30
let currentEditEntry = null ;
2025-11-21 16:07:43 +05:30
2025-11-27 19:39:36 +05:30
/* Single pagination state for both tables */
2025-11-26 23:07:12 +05:30
let currentPage = 1 ;
2025-11-27 19:39:36 +05:30
const entriesPerPage = 10 ;
2025-12-01 10:34:27 +05:30
/* Separate pagination state for modals */
2025-11-27 19:39:36 +05:30
let modalCurrentPage = 1 ;
const modalOrdersPerPage = 10 ;
2025-12-01 10:34:27 +05:30
let addOrderCurrentPage = 1 ;
2025-12-02 18:11:58 +05:30
/* Date filter state */
let fromDateFilter = '' ;
let toDateFilter = '' ;
/* Status filter state */
2025-12-01 10:34:27 +05:30
let paymentStatusFilter = '' ;
let orderStatusFilter = '' ;
2025-11-26 23:07:12 +05:30
2025-11-21 16:07:43 +05:30
/* small util */
function escapeHtml ( s ){ if ( s === null || s === undefined ) return '' ; return String ( s ) . replace ( / [ &<> " ']/g, m => ( { '&':'&','<':'<','>':'>',' " ':' & quot ; ',"' " : " & #39;"}[m])); }
function formatCurrency ( v ){ return '₹' + Number ( v || 0 ) . toLocaleString ( undefined , { minimumFractionDigits : 0 , maximumFractionDigits : 2 }); }
function capitalize ( s ){ if ( ! s ) return '' ; s = String ( s ); return s . charAt ( 0 ) . toUpperCase () + s . slice ( 1 ); }
function statusClass ( status ){
switch ( String ( status || '' ) . toLowerCase ()){
case 'unpaid' : return 'status-unpaid' ;
case 'paid' : return 'status-paid' ;
case 'loading' : return 'status-loading' ;
case 'dispatched' : return 'status-dispatched' ;
2025-12-01 10:34:27 +05:30
case 'packed' : return 'status-loading' ;
case 'delivered' : return 'status-delivered' ;
case 'pending' : return 'pending-badge-red' ;
2025-11-21 16:07:43 +05:30
default : return 'status-loading' ;
}
}
/* ---------- Init ---------- */
window . addEventListener ( 'DOMContentLoaded' , () => {
bindUI ();
loadDashboard ();
});
2025-11-12 19:56:06 +05:30
2025-11-21 16:07:43 +05:30
/* ---------- UI binding ---------- */
function bindUI (){
2025-11-26 23:07:12 +05:30
// Create Order Modal
2025-12-05 17:16:02 +05:30
//document.getElementById('openCreateModalBtn').addEventListener('click', openCreateOrderModal);
const createBtn = document . getElementById ( 'openCreateModalBtn' );
if ( createBtn ) {
createBtn . addEventListener ( 'click' , openCreateOrderModal );
}
2025-11-26 23:07:12 +05:30
document . getElementById ( 'closeCreateModal' ) . addEventListener ( 'click' , closeCreateOrderModal );
document . getElementById ( 'cancelCreateModal' ) . addEventListener ( 'click' , closeCreateOrderModal );
2025-11-21 16:07:43 +05:30
document . getElementById ( 'createOrderInlineForm' ) . addEventListener ( 'submit' , submitCreateOrderInline );
2025-11-26 23:07:12 +05:30
2025-12-01 10:34:27 +05:30
// Payment Table Pagination (only pagination controls)
2025-11-27 19:39:36 +05:30
document . getElementById ( 'paymentPrevBtn' ) . addEventListener ( 'click' , () => goToPreviousPage ());
document . getElementById ( 'paymentNextBtn' ) . addEventListener ( 'click' , () => goToNextPage ());
2025-12-01 10:34:27 +05:30
// No pagination controls for order table
2025-11-27 19:39:36 +05:30
// Modal Pagination
document . getElementById ( 'modalPrevBtn' ) . addEventListener ( 'click' , () => goToModalPreviousPage ());
document . getElementById ( 'modalNextBtn' ) . addEventListener ( 'click' , () => goToModalNextPage ());
2025-11-26 23:07:12 +05:30
2025-12-01 10:34:27 +05:30
// Add Order Modal Pagination
document . getElementById ( 'addOrderPrevBtn' ) . addEventListener ( 'click' , () => goToAddOrderPreviousPage ());
document . getElementById ( 'addOrderNextBtn' ) . addEventListener ( 'click' , () => goToAddOrderNextPage ());
2025-12-02 18:11:58 +05:30
document . getElementById ( 'refreshBtn' ) . addEventListener ( 'click' , () => {
loadDashboard ();
clearDateFilters ();
clearStatusFilters ();
});
2025-11-21 16:07:43 +05:30
document . getElementById ( 'searchBtn' ) . addEventListener ( 'click' , handleSearch );
2025-12-05 17:16:02 +05:30
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 ();
});
}
2025-11-21 16:07:43 +05:30
// Installment form submit
document . getElementById ( 'installmentForm' ) . addEventListener ( 'submit' , submitInstallment );
2025-12-01 10:34:27 +05:30
// 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 );
2025-12-02 18:11:58 +05:30
// Date Filter bindings
document . getElementById ( 'fromDateFilter' ) . addEventListener ( 'change' , ( e ) => {
fromDateFilter = e . target . value ;
applyAllFilters ();
2025-12-01 10:34:27 +05:30
});
2025-12-02 18:11:58 +05:30
document . getElementById ( 'toDateFilter' ) . addEventListener ( 'change' , ( e ) => {
toDateFilter = e . target . value ;
applyAllFilters ();
2025-12-01 10:34:27 +05:30
});
2025-12-02 18:11:58 +05:30
// Status Filter bindings
document . getElementById ( 'paymentStatusFilter' ) . addEventListener ( 'change' , ( e ) => {
paymentStatusFilter = e . target . value ;
applyAllFilters ();
2025-12-01 10:34:27 +05:30
});
document . getElementById ( 'orderStatusFilter' ) . addEventListener ( 'change' , ( e ) => {
orderStatusFilter = e . target . value ;
2025-12-02 18:11:58 +05:30
applyAllFilters ();
2025-12-01 10:34:27 +05:30
});
}
2025-12-02 18:11:58 +05:30
/* ---------- Filter Functions ---------- */
function applyAllFilters () {
2025-12-01 10:34:27 +05:30
let filteredEntries = [ ... entries ];
2025-12-02 18:11:58 +05:30
// 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 );
2025-12-01 10:34:27 +05:30
}
2025-12-02 18:11:58 +05:30
// Apply payment status filter (ONLY for payment table)
let paymentFilteredEntries = [ ... filteredEntries ];
2025-12-01 10:34:27 +05:30
if ( paymentStatusFilter ) {
2025-12-02 18:11:58 +05:30
paymentFilteredEntries = paymentFilteredEntries . filter ( entry =>
2025-12-01 10:34:27 +05:30
entry . payment_status . toLowerCase () === paymentStatusFilter . toLowerCase ()
);
}
2025-12-02 18:11:58 +05:30
// Apply order status filter (ONLY for order table)
let orderFilteredEntries = [ ... filteredEntries ];
if ( orderStatusFilter ) {
orderFilteredEntries = orderFilteredEntries . filter ( entry =>
entry . dispatch_status . toLowerCase () === orderStatusFilter . toLowerCase ()
);
}
2025-12-01 10:34:27 +05:30
// Reset to first page when filtering
currentPage = 1 ;
2025-12-02 18:11:58 +05:30
// Render each table with its own filtered data
renderPaymentTable ( paymentFilteredEntries );
renderOrderTable ( orderFilteredEntries );
2025-12-01 10:34:27 +05:30
updatePaginationControls ();
}
2025-12-02 18:11:58 +05:30
function applyOrderTableFilters ( entriesList ) {
let filtered = [ ... entriesList ];
2025-12-01 10:34:27 +05:30
2025-12-02 18:11:58 +05:30
// Apply order status filter (for order table only)
2025-12-01 10:34:27 +05:30
if ( orderStatusFilter ) {
2025-12-02 18:11:58 +05:30
filtered = filtered . filter ( entry =>
2025-12-01 10:34:27 +05:30
entry . dispatch_status . toLowerCase () === orderStatusFilter . toLowerCase ()
);
}
2025-12-02 18:11:58 +05:30
return filtered ;
2025-12-01 10:34:27 +05:30
}
2025-12-02 18:11:58 +05:30
function clearDateFilters () {
document . getElementById ( 'fromDateFilter' ) . value = '' ;
document . getElementById ( 'toDateFilter' ) . value = '' ;
fromDateFilter = '' ;
toDateFilter = '' ;
2025-12-01 10:34:27 +05:30
}
2025-12-02 18:11:58 +05:30
function clearStatusFilters () {
document . getElementById ( 'paymentStatusFilter' ) . value = '' ;
2025-12-01 10:34:27 +05:30
document . getElementById ( 'orderStatusFilter' ) . value = '' ;
2025-12-02 18:11:58 +05:30
paymentStatusFilter = '' ;
2025-12-01 10:34:27 +05:30
orderStatusFilter = '' ;
2025-11-21 16:07:43 +05:30
}
2025-11-26 23:07:12 +05:30
/* ---------- Pagination Functions ---------- */
function goToPreviousPage () {
if ( currentPage > 1 ) {
currentPage -- ;
2025-11-27 19:39:36 +05:30
renderBothTables ();
2025-12-01 10:34:27 +05:30
updatePaginationControls ();
2025-11-26 23:07:12 +05:30
}
}
function goToNextPage () {
2025-11-27 19:39:36 +05:30
const totalPages = Math . ceil ( entries . length / entriesPerPage );
2025-11-26 23:07:12 +05:30
if ( currentPage < totalPages ) {
currentPage ++ ;
2025-11-27 19:39:36 +05:30
renderBothTables ();
2025-12-01 10:34:27 +05:30
updatePaginationControls ();
2025-11-27 19:39:36 +05:30
}
}
function goToModalPreviousPage () {
if ( modalCurrentPage > 1 ) {
modalCurrentPage -- ;
renderConsolidateOrders ( availableOrders );
updateModalPaginationControls ();
}
}
function goToModalNextPage () {
const totalPages = Math . ceil ( availableOrders . length / modalOrdersPerPage );
if ( modalCurrentPage < totalPages ) {
modalCurrentPage ++ ;
2025-11-26 23:07:12 +05:30
renderConsolidateOrders ( availableOrders );
2025-11-27 19:39:36 +05:30
updateModalPaginationControls ();
2025-11-26 23:07:12 +05:30
}
}
2025-12-01 10:34:27 +05:30
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 ();
}
2025-11-27 19:39:36 +05:30
}
2025-12-01 10:34:27 +05:30
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' );
2025-11-26 23:07:12 +05:30
prevBtn . disabled = currentPage === 1 ;
nextBtn . disabled = currentPage === totalPages || totalPages === 0 ;
// Update page info text
2025-11-27 19:39:36 +05:30
const startIndex = ( currentPage - 1 ) * entriesPerPage + 1 ;
const endIndex = Math . min ( currentPage * entriesPerPage , entries . length );
pageInfo . textContent = `Showing ${startIndex} to ${endIndex} of ${entries.length} entries` ;
2025-11-26 23:07:12 +05:30
// 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 );
}
}
2025-11-27 19:39:36 +05:30
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 );
}
}
2025-12-01 10:34:27 +05:30
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 );
}
}
2025-11-26 23:07:12 +05:30
function addPageButton ( pageNumber , container ) {
const button = document . createElement ( 'button' );
button . className = 'pagination-page-btn' ;
2025-11-27 19:39:36 +05:30
2025-11-26 23:07:12 +05:30
if ( pageNumber === currentPage ) {
button . classList . add ( 'active' );
}
2025-11-27 19:39:36 +05:30
2025-11-26 23:07:12 +05:30
button . textContent = pageNumber ;
button . addEventListener ( 'click' , () => {
currentPage = pageNumber ;
2025-11-27 19:39:36 +05:30
renderBothTables ();
2025-12-01 10:34:27 +05:30
updatePaginationControls ();
2025-11-27 19:39:36 +05:30
});
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 ;
2025-11-26 23:07:12 +05:30
renderConsolidateOrders ( availableOrders );
2025-11-27 19:39:36 +05:30
updateModalPaginationControls ();
2025-11-26 23:07:12 +05:30
});
container . appendChild ( button );
2025-11-21 16:07:43 +05:30
}
2025-11-26 23:07:12 +05:30
2025-12-01 10:34:27 +05:30
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 );
}
2025-11-27 19:39:36 +05:30
function renderBothTables () {
renderPaymentTable ( entries );
renderOrderTable ( entries );
}
2025-11-26 23:07:12 +05:30
/* ---------- Create Order Modal Functions ---------- */
function openCreateOrderModal (){
const modal = document . getElementById ( 'createOrderModal' );
modal . classList . add ( 'modal-open' );
2025-11-21 16:07:43 +05:30
loadAvailableOrders ();
setTimeout (() => document . getElementById ( 'inline_description' ) . focus (), 220 );
}
2025-11-26 23:07:12 +05:30
function closeCreateOrderModal (){
const modal = document . getElementById ( 'createOrderModal' );
modal . classList . remove ( 'modal-open' );
document . getElementById ( 'createOrderInlineForm' ) . reset ();
2025-11-27 19:39:36 +05:30
modalCurrentPage = 1 ; // Reset modal pagination when closing
2025-11-21 16:07:43 +05:30
}
/* ---------- Loaders ---------- */
function loadDashboard (){
2025-12-01 10:34:27 +05:30
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 ();
});
}
2025-12-03 16:17:14 +05:30
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 ();
});
}
2025-12-01 10:34:27 +05:30
2025-11-21 16:07:43 +05:30
/* ---------- Renderers ---------- */
function renderPaymentTable ( list ){
2025-12-02 18:11:58 +05:30
const body = document . getElementById ( 'paymentTableBody' );
body . innerHTML = '' ;
if ( ! list || list . length === 0 ) {
2025-12-05 17:16:02 +05:30
body . innerHTML = '<tr><td colspan="9" class="empty-state">No entries found</td></tr>' ;
return ;
2025-12-02 18:11:58 +05:30
}
2025-12-05 17:16:02 +05:30
2025-12-02 18:11:58 +05:30
const startIndex = ( currentPage - 1 ) * entriesPerPage ;
const endIndex = startIndex + entriesPerPage ;
const paginatedEntries = list . slice ( startIndex , endIndex );
2025-12-05 17:16:02 +05:30
2025-12-02 18:11:58 +05:30
paginatedEntries . forEach ( entry => {
2025-12-05 17:16:02 +05:30
const tr = document . createElement ( 'tr' );
2025-12-02 18:11:58 +05:30
2025-12-05 17:16:02 +05:30
const canActions = [ 'unpaid' , 'pending' ] . includes ( entry . payment_status . toLowerCase ());
2025-12-04 11:21:46 +05:30
2025-12-05 17:16:02 +05:30
// permissions passed from Blade
const canEdit = window . CAN_EDIT_ORDER ;
const canDelete = window . CAN_DELETE_ORDER ;
const canToggle = window . CAN_TOGGLE_PAYMENT ;
2025-12-04 11:21:46 +05:30
2025-12-05 17:16:02 +05:30
tr . innerHTML = `
< td > $ { escapeHtml ( entry . entry_no )} </ td >
< td > $ { escapeHtml ( entry . entry_date )} </ td >
< td > $ { escapeHtml ( entry . description )} </ td >
< td >
< button type = " button " class = " entry-link "
onclick = " openEntryOrdersModal(' ${ escapeHtml(entry.entry_no) } ') " >
$ { entry . order_quantity ? ? '-' }
</ button >
</ td >
< td > $ { escapeHtml ( entry . region )} </ td >
< td >
< button class = " toggle-switch-btn "
data - entry = " ${ entry.entry_no } "
data - pos = " $ { entry.toggle_pos ?? 0} "
$ { ! canToggle ? 'disabled class="toggle-switch-btn disabled-toggle"' : '' }
></ button >
</ td >
< td > $ { formatCurrency ( entry . amount )} </ td >
< td >
< span class = " status-badge ${ statusClass(entry.payment_status) } " >
$ { capitalize ( entry . payment_status )}
</ span >
</ td >
< td >
< div class = " action-btns " >
$ {( canActions && canEdit ) ? `
< button class = " action-btn edit-btn "
data - entry = " ${ entry.entry_no } "
title = " Edit entry " >
✎
</ button >
` : '' }
2025-12-04 11:21:46 +05:30
2025-12-05 17:16:02 +05:30
$ {( canActions && canDelete ) ? `
< button class = " action-btn delete-btn "
data - entry = " ${ entry.entry_no } "
title = " Delete entry " >
🗑
</ button >
` : '' }
2025-12-04 11:21:46 +05:30
2025-12-05 17:16:02 +05:30
</ 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 );
2025-12-02 18:11:58 +05:30
});
2025-12-05 17:16:02 +05:30
}
2025-12-04 11:21:46 +05:30
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 );
});
}
2025-11-21 16:07:43 +05:30
function renderOrderTable ( list ){
const body = document . getElementById ( 'orderTableBody' );
body . innerHTML = '' ;
2025-11-27 19:39:36 +05:30
2025-11-21 16:07:43 +05:30
if ( ! list || list . length === 0 ){
body . innerHTML = '<tr><td colspan="8" class="empty-state">No entries found</td></tr>' ;
return ;
}
2025-11-27 19:39:36 +05:30
2025-12-01 10:34:27 +05:30
// Calculate pagination (using same currentPage as first table)
2025-11-27 19:39:36 +05:30
const startIndex = ( currentPage - 1 ) * entriesPerPage ;
const endIndex = startIndex + entriesPerPage ;
const paginatedEntries = list . slice ( startIndex , endIndex );
paginatedEntries . forEach ( entry => {
2025-11-21 16:07:43 +05:30
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 = '' ;
2025-11-26 23:07:12 +05:30
2025-11-21 16:07:43 +05:30
if ( ! list || list . length === 0 ){
2025-11-26 23:07:12 +05:30
body . innerHTML = '<tr><td colspan="14" class="empty-state">No available orders</td></tr>' ;
2025-11-27 19:39:36 +05:30
updateModalPaginationControls ();
2025-11-21 16:07:43 +05:30
return ;
}
2025-11-26 23:07:12 +05:30
2025-11-27 19:39:36 +05:30
// Calculate pagination for modal
const startIndex = ( modalCurrentPage - 1 ) * modalOrdersPerPage ;
const endIndex = startIndex + modalOrdersPerPage ;
2025-11-26 23:07:12 +05:30
const paginatedOrders = list . slice ( startIndex , endIndex );
paginatedOrders . forEach ( order => {
2025-11-21 16:07:43 +05:30
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 );
});
}
2025-11-12 19:56:06 +05:30
2025-12-02 18:11:58 +05:30
function renderAddOrderModal ( orders ) {
const tbody = document . getElementById ( 'addOrderTableBody' );
tbody . innerHTML = '' ;
2025-12-01 10:34:27 +05:30
2025-12-02 18:11:58 +05:30
if ( ! orders || ! orders . length ) {
tbody . innerHTML = `
< tr >
< td colspan = " 8 " class = " empty-state " > No orders found </ td >
</ tr >
` ;
return ;
2025-11-21 16:07:43 +05:30
}
2025-11-12 19:56:06 +05:30
2025-12-02 18:11:58 +05:30
const start = ( addOrderCurrentPage - 1 ) * modalOrdersPerPage ;
const end = Math . min ( start + modalOrdersPerPage , orders . length );
const pageOrders = orders . slice ( start , end );
2025-11-21 16:07:43 +05:30
2025-12-02 18:11:58 +05:30
// Hya entry sathi already associated order IDs collect kara
const attachedIds = new Set (
( currentEditEntry ? . orders || []) . map ( o => o . id )
);
2025-11-21 16:07:43 +05:30
2025-12-02 18:11:58 +05:30
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 );
2025-11-21 16:07:43 +05:30
});
}
2025-12-02 18:11:58 +05:30
2025-12-01 10:34:27 +05:30
/* ---------- 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 || '' ;
2025-12-04 11:21:46 +05:30
// document.getElementById('edit_payment_status').value = entry.payment_status || 'unpaid';
2025-12-01 10:34:27 +05:30
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 " >
2025-12-02 18:11:58 +05:30
< button type = " button " class = " remove-order-btn " data - order - id = " ${ order.id } " > Remove </ button >
2025-12-01 10:34:27 +05:30
</ 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' );
2025-12-02 18:11:58 +05:30
// नवीन 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
2025-12-01 10:34:27 +05:30
loadEntryOrders ( entryNo );
loadAvailableOrders ();
2025-12-02 18:11:58 +05:30
await refreshEntryHeader ( entryNo );
2025-12-01 10:34:27 +05:30
} 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 ,
2025-12-04 11:21:46 +05:30
// payment_status: form.payment_status.value,
2025-12-01 10:34:27 +05:30
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' ;
}
}
}
2025-12-02 18:11:58 +05:30
2025-12-23 22:15:45 +05:30
function openEntryOrdersModal ( entryNo ) {
document . getElementById ( 'entryOrdersEntryNo-span' ) . textContent = `(${entryNo})` ;
2025-12-02 18:11:58 +05:30
2025-12-23 22:15:45 +05:30
const tbody = document . getElementById ( 'entryOrdersTableBody' );
2025-12-02 18:11:58 +05:30
tbody . innerHTML = `
2025-12-23 22:15:45 +05:30
< tr >
< td colspan = " 7 " class = " empty-state " > Loading orders ...</ td >
</ tr >
2025-12-02 18:11:58 +05:30
` ;
2025-12-23 22:15:45 +05:30
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 ;
}
2025-12-02 18:11:58 +05:30
2025-12-23 22:15:45 +05:30
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 = '' ;
orders . forEach ( order => {
const tr = document . createElement ( 'tr' );
const amountValue =
order . ttl_amount ? ?
order . ttlamount ? ?
order . total_amount ? ?
order . order_amount ? ?
order . amount ? ?
0 ;
tr . innerHTML = `
< td > $ { escapeHtml ( order . order_id )} </ td >
< td > $ { escapeHtml ( 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 ( amountValue )} </ td >
` ;
tbody . appendChild ( tr );
});
})
. catch (() => {
tbody . innerHTML = `
< tr >
< td colspan = " 7 " class = " empty-state " > Error loading orders </ td >
</ tr >
` ;
});
document . getElementById ( 'entryOrdersModal' ) . classList . add ( 'modal-open' );
}
2025-12-02 18:11:58 +05:30
function closeEntryOrdersModal () {
document . getElementById ( 'entryOrdersModal' ) . classList . remove ( 'modal-open' );
}
2025-12-01 10:34:27 +05:30
/* ---------- Add Order Modal Functions ---------- */
function openAddOrderModal () {
if ( ! currentEditEntry ) return ;
2025-12-03 16:17:14 +05:30
2025-12-01 10:34:27 +05:30
document . getElementById ( 'addOrderModal' ) . classList . add ( 'modal-open' );
2025-12-03 16:17:14 +05:30
// नेहमी इथेच ताजे orders load कर
2025-12-01 10:34:27 +05:30
addOrderCurrentPage = 1 ;
2025-12-03 16:17:14 +05:30
loadAvailableOrdersForAddModal ();
2025-12-01 10:34:27 +05:30
}
2025-12-03 16:17:14 +05:30
2025-12-01 10:34:27 +05:30
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 );
}
}
2025-11-26 23:07:12 +05:30
/* ---------- Create Order Inline (Now in Popup) ---------- */
2025-11-21 16:07:43 +05:30
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 ();
2025-11-26 23:07:12 +05:30
closeCreateOrderModal ();
2025-11-21 16:07:43 +05:30
loadDashboard ();
} catch ( err ){
alert ( err . message || 'Failed to create order' );
console . error ( err );
} finally {
const btn = form . querySelector ( 'button[type="submit"]' );
2025-12-01 10:34:27 +05:30
if ( btn ){ btn . disabled = false ; btn . textContent = 'Create Installment' ; }
2025-11-21 16:07:43 +05:30
}
}
/* ---------- Search ---------- */
function handleSearch (){
const q = document . getElementById ( 'main-search' ) . value . trim () . toLowerCase ();
2025-11-27 19:39:36 +05:30
if ( ! q ){
renderBothTables ();
2025-12-01 10:34:27 +05:30
updatePaginationControls ();
2025-11-27 19:39:36 +05:30
return ;
}
2025-11-21 16:07:43 +05:30
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 );
});
2025-11-27 19:39:36 +05:30
entries = filtered ;
currentPage = 1 ; // Reset to first page when searching
renderBothTables ();
2025-12-01 10:34:27 +05:30
updatePaginationControls ();
2025-11-21 16:07:43 +05:30
}
/* ---------- Entry details & installments ---------- */
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 ;
document . getElementById ( 'entryDetailsId' ) . textContent = entry . entry_no ;
document . getElementById ( 'originalAmount' ) . textContent = formatCurrency ( entry . amount );
const totalProcessed = Number ( entry . amount ) - Number ( entry . pending_amount );
document . getElementById ( 'totalProcessed' ) . textContent = formatCurrency ( totalProcessed );
document . getElementById ( 'pendingBalance' ) . textContent = formatCurrency ( entry . pending_amount );
document . getElementById ( 'totalInstallments' ) . textContent = entry . installments . length ;
const tbody = document . getElementById ( 'installmentsTableBody' );
tbody . innerHTML = '' ;
entry . installments . forEach (( ins , idx ) => {
const tr = document . createElement ( 'tr' );
tr . innerHTML = `
< td > $ { idx === 0 ? 'Original Entry' : 'Installment ' + idx } </ td >
< td > $ { escapeHtml ( ins . proc_date )} </ td >
< td > $ { escapeHtml ( ins . description )} </ td >
< td > $ { escapeHtml ( ins . region )} </ td >
< td > $ { formatCurrency ( ins . amount )} </ td >
< td >
2025-12-05 17:16:02 +05:30
< select class = " installment-status-dropdown "
data - id = " ${ ins.id } "
onchange = " updateInstallmentStatus( ${ ins.id } , this.value) " >
< option value = " Pending " $ { ins . status === 'Pending' ? 'selected' : '' }
style = " color: #f59e0b; font-weight: 500; padding: 10px; " >
⏳ Pending
</ option >
< option value = " Loading " $ { ins . status === 'Loading' ? 'selected' : '' }
style = " color: #3b82f6; font-weight: 500; padding: 10px; " >
📦 Loading
</ option >
< option value = " Packed " $ { ins . status === 'Packed' ? 'selected' : '' }
style = " color: #8b5cf6; font-weight: 500; padding: 10px; " >
📦 Packed
</ option >
< option value = " Dispatched " $ { ins . status === 'Dispatched' ? 'selected' : '' }
style = " color: #10b981; font-weight: 500; padding: 10px; " >
🚚 Dispatched
</ option >
< option value = " Delivered " $ { ins . status === 'Delivered' ? 'selected' : '' }
style = " color: #0c6b2e; font-weight: 500; padding: 10px; " >
✅ Delivered
</ option >
</ select >
2025-12-02 18:11:58 +05:30
2025-11-21 16:07:43 +05:30
</ td >
` ;
tbody . appendChild ( tr );
});
document . getElementById ( 'entryDetailsModal' ) . classList . add ( 'modal-open' );
} catch ( err ) {
console . error ( err );
alert ( 'Unable to load entry details' );
2025-11-12 19:56:06 +05:30
}
}
function closeEntryDetailsModal () {
document . getElementById ( 'entryDetailsModal' ) . classList . remove ( 'modal-open' );
}
2025-11-21 16:07:43 +05:30
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 >
2025-11-12 19:56:06 +05:30
` ;
2025-11-21 16:07:43 +05:30
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' ; }
}
}
2025-12-01 10:34:27 +05:30
2025-11-11 14:51:35 +05:30
</ script >
2025-11-21 16:07:43 +05:30
2025-11-26 23:07:12 +05:30
@ endsection