1774 lines
56 KiB
PHP
1774 lines
56 KiB
PHP
@extends('admin.layouts.app')
|
|
|
|
@section('page-title', 'Invoice List')
|
|
|
|
@section('content')
|
|
<style>
|
|
/* Import Inter Font */
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
|
|
|
/*Remove horizontal scroll bar*/
|
|
/* html, body {
|
|
overflow-x: hidden !important;
|
|
font-family: 'Inter', sans-serif !important;
|
|
} */
|
|
|
|
/* Invoice Management Styles */
|
|
.invoice-management-box {
|
|
background: #fff;
|
|
border-radius: 17px;
|
|
box-shadow: 0 7px 38px #dde3fa77, 0 2px 9px #e5e7ff80;
|
|
margin-bottom: 33px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.invoice-management-bar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
border-radius: 17px 17px 0 0;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 54px;
|
|
padding: 15px 26px 10px 22px;
|
|
border-bottom: 1.4px solid #e8e2cf;
|
|
box-shadow: 0 1px 13px #ffe2a888;
|
|
}
|
|
|
|
.invoice-management-title {
|
|
font-size: 1.32rem;
|
|
font-weight: 800;
|
|
color: #ffffffff;
|
|
letter-spacing: .08em;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 11px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.invoice-management-title i {
|
|
font-size: 1.12em;
|
|
color: #336ad3;
|
|
}
|
|
|
|
/* ===== UPDATED FILTER BAR WITH DATE RANGE ===== */
|
|
.invoice-tools-row {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
padding: 16px 24px;
|
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
box-shadow: 0 10px 25px -5px rgba(99, 102, 241, 0.3);
|
|
position: relative;
|
|
border-radius: 17px 17px 0 0;
|
|
top:5px;
|
|
}
|
|
|
|
.filter-bar-container {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
flex-wrap: wrap;
|
|
width: 100%;
|
|
}
|
|
|
|
/* Search Box Styling */
|
|
.search-box {
|
|
display: flex;
|
|
align-items: center;
|
|
background: white;
|
|
border-radius: 50px;
|
|
padding: 8px 20px;
|
|
box-shadow: 0 4px 15px rgba(0,0,0,0.08), inset 0 1px 2px rgba(255,255,255,0.5);
|
|
border: 1px solid rgba(255,255,255,0.3);
|
|
flex: 2;
|
|
min-width: 250px;
|
|
transition: all 0.3s ease;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.search-box:focus-within {
|
|
box-shadow: 0 8px 20px rgba(79, 70, 229, 0.25);
|
|
transform: translateY(-2px);
|
|
border-color: white;
|
|
}
|
|
|
|
.search-box i {
|
|
color: #667eea;
|
|
margin-right: 12px;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.search-box input {
|
|
border: none;
|
|
outline: none;
|
|
background: transparent;
|
|
width: 100%;
|
|
font-size: 15px;
|
|
color: #1f2937;
|
|
font-family: 'Inter', sans-serif;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.search-box input::placeholder {
|
|
color: #9ca3af;
|
|
font-family: 'Inter', sans-serif;
|
|
font-weight: 300;
|
|
}
|
|
|
|
/* Filter Selects Styling */
|
|
.filter-select-wrapper {
|
|
flex: 1;
|
|
min-width: 140px;
|
|
}
|
|
|
|
.filter-select {
|
|
width: 100%;
|
|
background: white;
|
|
border: 1px solid rgba(255,255,255,0.5);
|
|
border-radius: 50px;
|
|
padding: 10px 20px;
|
|
font-size: 14px;
|
|
color: #1f2937;
|
|
outline: none;
|
|
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
|
font-family: 'Inter', sans-serif;
|
|
font-weight: 400;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
appearance: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23667eea' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 15px center;
|
|
background-size: 16px;
|
|
}
|
|
|
|
.filter-select:hover {
|
|
background-color: white;
|
|
border-color: white;
|
|
}
|
|
|
|
.filter-select:focus {
|
|
border-color: white;
|
|
box-shadow: 0 0 0 3px rgba(255,255,255,0.2);
|
|
background-color: white;
|
|
}
|
|
|
|
/* ===== NEW: Status dropdown option colors ===== */
|
|
.filter-select option[value="paid"] {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.filter-select option[value="pending"] {
|
|
background-color: #fef3c7;
|
|
color: #d97706;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.filter-select option[value="overdue"] {
|
|
background-color: #e9d5ff;
|
|
color: #6b21a8;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.filter-select option[value="all"] {
|
|
background-color: white;
|
|
color: #1f2937;
|
|
}
|
|
|
|
/* For Firefox compatibility */
|
|
.filter-select option {
|
|
padding: 8px;
|
|
}
|
|
/* ===== END status dropdown option colors ===== */
|
|
|
|
/* Date Range Styling */
|
|
.date-range-wrapper {
|
|
flex: 1.5;
|
|
min-width: 280px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
/* background: rgba(255, 255, 255, 0.2); */
|
|
padding: 5px 12px;
|
|
border-radius: 50px;
|
|
backdrop-filter: blur(5px);
|
|
}
|
|
|
|
.date-input-container {
|
|
display: flex;
|
|
align-items: center;
|
|
background: white;
|
|
border-radius: 50px;
|
|
padding: 5px 12px;
|
|
flex: 1;
|
|
border: 1px solid rgba(255,255,255,0.5);
|
|
}
|
|
|
|
.date-input-container i {
|
|
color: #667eea;
|
|
margin-right: 6px;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.date-input {
|
|
border: none;
|
|
outline: none;
|
|
background: transparent;
|
|
width: 100%;
|
|
font-size: 13px;
|
|
color: #1f2937;
|
|
font-family: 'Inter', sans-serif;
|
|
padding: 5px 0;
|
|
}
|
|
|
|
.date-input::placeholder {
|
|
color: #9ca3af;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.date-separator {
|
|
color: white;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
text-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
|
}
|
|
|
|
/* Responsive Design */
|
|
@media (max-width: 1100px) {
|
|
.filter-bar-container {
|
|
gap: 12px;
|
|
}
|
|
|
|
.search-box {
|
|
min-width: 200px;
|
|
}
|
|
|
|
.date-range-wrapper {
|
|
min-width: 260px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.invoice-tools-row {
|
|
padding: 16px;
|
|
}
|
|
|
|
.filter-bar-container {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.search-box {
|
|
min-width: 100%;
|
|
}
|
|
|
|
.filter-select-wrapper {
|
|
min-width: 100%;
|
|
}
|
|
|
|
.date-range-wrapper {
|
|
min-width: 100%;
|
|
flex-direction: column;
|
|
background: transparent;
|
|
backdrop-filter: none;
|
|
padding: 0;
|
|
gap: 8px;
|
|
}
|
|
|
|
.date-input-container {
|
|
width: 100%;
|
|
}
|
|
|
|
.date-separator {
|
|
display: none;
|
|
}
|
|
}
|
|
/* ===== END UPDATED FILTER BAR ===== */
|
|
|
|
.invoice-management-main {
|
|
background: #fff;
|
|
border-radius: 0 0 17px 17px;
|
|
padding: 0;
|
|
}
|
|
|
|
/* Table Styling - CENTERED CONTENT */
|
|
.table-container {
|
|
width: 100%;
|
|
overflow-x: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
scroll-behavior: smooth;
|
|
border-radius: 0 0 17px 17px;
|
|
}
|
|
|
|
.table-container::-webkit-scrollbar {
|
|
height: 8px;
|
|
}
|
|
.table-container::-webkit-scrollbar-thumb {
|
|
background: linear-gradient(90deg, #a7b8ff, #6c8eff);
|
|
border-radius: 10px;
|
|
}
|
|
.table-container::-webkit-scrollbar-thumb:hover {
|
|
background: linear-gradient(90deg, #5a78ff, #3f63e0);
|
|
}
|
|
.table-container::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.table {
|
|
width: 100%;
|
|
min-width: 1200px;
|
|
border-collapse: separate;
|
|
border-spacing: 0 12px;
|
|
margin-bottom: 0;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Center all table content */
|
|
.table thead tr {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
|
|
.table thead th:first-child {
|
|
border-top-left-radius: 17px;
|
|
}
|
|
.table thead th:last-child {
|
|
border-top-right-radius: 17px;
|
|
}
|
|
|
|
.table thead th {
|
|
background: transparent !important;
|
|
border: none;
|
|
font-weight: 700;
|
|
color: #ffffffff;
|
|
letter-spacing: 0.02em;
|
|
font-size: 14px;
|
|
padding: 20px 15px;
|
|
white-space: nowrap;
|
|
position: relative;
|
|
border-bottom: 2px solid #e8e2cf;
|
|
text-align: center !important;
|
|
vertical-align: middle;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.table thead th:first-child {
|
|
padding-left: 30px;
|
|
}
|
|
.table thead th:last-child {
|
|
padding-right: 30px;
|
|
}
|
|
|
|
/* Soft blue background for ALL table rows */
|
|
.table-striped tbody tr {
|
|
background: #f0f8ff !important;
|
|
transition: all 0.15s ease;
|
|
|
|
|
|
border-radius: 6px;
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
|
}
|
|
|
|
|
|
.table-striped tbody tr td {
|
|
padding: 6px 10px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.table-striped tbody tr:hover {
|
|
background: #e6f3ff !important;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
|
transform: translateY(-0.5px);
|
|
}
|
|
|
|
|
|
.table-striped tbody tr:nth-of-type(odd),
|
|
.table-striped tbody tr:nth-of-type(even) {
|
|
background: #f0f8ff !important;
|
|
}
|
|
|
|
|
|
.table td {
|
|
padding: 18px 15px;
|
|
border: none;
|
|
vertical-align: middle;
|
|
white-space: nowrap;
|
|
font-size: 14px;
|
|
color: #4a5568;
|
|
text-align: center !important;
|
|
border-top: 1px solid transparent;
|
|
border-bottom: 1px solid transparent;
|
|
font-family: 'Inter', sans-serif;
|
|
font-weight: 400;
|
|
}
|
|
|
|
|
|
.table td:first-child {
|
|
padding-left: 30px;
|
|
font-weight: 600;
|
|
color: #2d3748;
|
|
border-top-left-radius: 12px;
|
|
border-bottom-left-radius: 12px;
|
|
}
|
|
|
|
.table td:last-child {
|
|
padding-right: 30px;
|
|
border-top-right-radius: 12px;
|
|
border-bottom-right-radius: 12px;
|
|
}
|
|
|
|
/* Invoice Number with Curved Boxes */
|
|
.invoice-number-cell {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 15px;
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.invoice-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-size: 16px;
|
|
flex-shrink: 0;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
|
font-weight: bold;
|
|
border: 2px solid rgba(255,255,255,0.3);
|
|
}
|
|
|
|
/* Different curved background colors for each invoice */
|
|
.invoice-icon-1 {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-2 {
|
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-3 {
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-4 {
|
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-5 {
|
|
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-6 {
|
|
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-7 {
|
|
background: linear-gradient(135deg, #d299c2 0%, #fef9d7 100%);
|
|
border-radius: 10px;
|
|
}
|
|
.invoice-icon-8 {
|
|
background: linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%);
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.invoice-number-link {
|
|
color: #2469d6;
|
|
font-weight: 600;
|
|
text-decoration: none;
|
|
transition: all 0.3s ease;
|
|
font-size: 14px;
|
|
text-align: center;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.invoice-number-link:hover {
|
|
color: #1a4fb3;
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* Badge Styling - Centered with custom backgrounds and icons */
|
|
.badge {
|
|
font-size: 12px !important;
|
|
font-weight: 600 !important;
|
|
padding: 8px 15px 8px 12px !important;
|
|
border-radius: 20px !important;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.3px;
|
|
display: inline-flex !important;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-size: cover !important;
|
|
background-position: center !important;
|
|
color: #fff !important;
|
|
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
|
border: 2px solid transparent !important;
|
|
min-width: 45px;
|
|
box-sizing: border-box;
|
|
line-height: 1.2;
|
|
gap: 6px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Status icons */
|
|
.status-icon {
|
|
font-size: 13px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
/* Custom status badge backgrounds with icons */
|
|
.badge-paid {
|
|
background: url('/images/status-bg-paid.png') !important;
|
|
}
|
|
|
|
.badge-pending {
|
|
background: url('/images/status-bg-pending.png') !important;
|
|
}
|
|
|
|
.badge-overdue {
|
|
background: url('/images/status-bg-overdue.png') !important;
|
|
}
|
|
|
|
/* Fallback colors if images don't load - ALL WITH SAME SIZE */
|
|
.badge.badge-paid {
|
|
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
|
color: #065f46 !important;
|
|
border-color: #10b981 !important;
|
|
width: 98px;
|
|
}
|
|
|
|
.badge.badge-pending {
|
|
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
|
color: #d97706 !important;
|
|
border-color: #f59e0b !important;
|
|
}
|
|
|
|
.badge.badge-overdue {
|
|
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
|
color: #6b21a8 !important;
|
|
border-color: #8b5cf6 !important;
|
|
}
|
|
|
|
/* Entry Button - Centered */
|
|
.btn-entry {
|
|
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 8px 16px;
|
|
font-weight: 600;
|
|
font-size: 13px;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 1px 4px rgba(52, 146, 248, 0.3);
|
|
color: white;
|
|
white-space: nowrap;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 5px;
|
|
text-decoration: none;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.btn-entry:hover {
|
|
background: linear-gradient(135deg, #46b4fd 55%, #226ad6 110%);
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 2px 8px rgba(52, 146, 248, 0.4);
|
|
color: white;
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* Amount Styling - Centered */
|
|
.amount-cell {
|
|
font-weight: 600;
|
|
color: #194073;
|
|
font-size: 14px;
|
|
text-align: center !important;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.gst-cell {
|
|
font-weight: 500;
|
|
color: #63709b;
|
|
font-size: 14px;
|
|
text-align: center !important;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Date Styling - Centered */
|
|
.date-cell {
|
|
font-size: 13px;
|
|
color: #718096;
|
|
font-weight: 500;
|
|
text-align: center !important;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Customer Name Styling - Centered */
|
|
.customer-cell {
|
|
font-weight: 500;
|
|
color: #2d3748;
|
|
font-size: 14px;
|
|
max-width: 120px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
text-align: center !important;
|
|
margin: 0 auto;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Modal Styling */
|
|
.modal.fade .modal-dialog {
|
|
transition: transform 0.3s ease-out;
|
|
transform: translate(0, -50px);
|
|
}
|
|
|
|
.modal.show .modal-dialog {
|
|
transform: none;
|
|
}
|
|
|
|
.modal-content {
|
|
border: none;
|
|
border-radius: 20px;
|
|
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.modal-header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
border-bottom: none;
|
|
padding: 15px 25px;
|
|
border-radius: 20px 20px 0 0;
|
|
}
|
|
|
|
.modal-title {
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
color: white;
|
|
margin: 0;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.modal-header .btn-close {
|
|
filter: invert(1);
|
|
opacity: 0.8;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.modal-header .btn-close:hover {
|
|
opacity: 1;
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 30px;
|
|
background: #f8fafc;
|
|
max-height: 70vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* Stats Summary - Centered */
|
|
.stats-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
gap: 15px;
|
|
margin: 25px 30px;
|
|
padding: 0;
|
|
}
|
|
|
|
.stat-card {
|
|
background: white;
|
|
padding: 18px;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
text-align: center;
|
|
border-left: 3px solid #667eea;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 1.6rem;
|
|
font-weight: 700;
|
|
color: #2d3748;
|
|
margin-bottom: 5px;
|
|
text-align: center;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 0.9rem;
|
|
color: #718096;
|
|
font-weight: 600;
|
|
text-align: center;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Column Headers - Centered */
|
|
.column-header {
|
|
text-align: center !important;
|
|
font-weight: 600;
|
|
color: #2d3748;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Remove extra spacing */
|
|
.no-extra-space {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.compact-view {
|
|
padding-top: 0 !important;
|
|
padding-bottom: 0 !important;
|
|
}
|
|
|
|
/* Ensure all content in table is centered */
|
|
.table tbody tr td {
|
|
text-align: center !important;
|
|
}
|
|
|
|
.table tbody tr td .invoice-number-cell {
|
|
justify-content: center;
|
|
}
|
|
|
|
.table tbody tr td .btn-entry {
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* Empty State - Centered */
|
|
.text-muted {
|
|
color: #8a9bb9 !important;
|
|
font-style: italic;
|
|
padding: 30px !important;
|
|
text-align: center;
|
|
font-size: 14px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Card Styling */
|
|
.card {
|
|
border-radius: 0 0 17px 17px;
|
|
border: none;
|
|
margin-bottom: 0 !important;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.card-header {
|
|
background: #f8f9fa !important;
|
|
border-bottom: 1px solid #e9ecef;
|
|
border-radius: 0 !important;
|
|
padding: 18px 30px;
|
|
text-align: center;
|
|
}
|
|
|
|
.card-header h4 {
|
|
margin: 0;
|
|
color: #2451af;
|
|
font-weight: 700;
|
|
font-size: 1.3rem;
|
|
text-align: center;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Mobile Responsive Styles */
|
|
.mobile-invoice-card {
|
|
display: none;
|
|
background: #f0f8ff;
|
|
border-radius: 12px;
|
|
padding: 18px;
|
|
margin-bottom: 15px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
border-left: 4px solid #667eea;
|
|
}
|
|
|
|
.mobile-invoice-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 12px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
}
|
|
|
|
.mobile-invoice-number {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.mobile-invoice-icon {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.mobile-invoice-number-text {
|
|
font-weight: 600;
|
|
color: #2469d6;
|
|
font-size: 15px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.mobile-invoice-status {
|
|
font-size: 11px !important;
|
|
padding: 6px 12px 6px 8px !important;
|
|
min-width: 80px;
|
|
}
|
|
|
|
.mobile-invoice-details {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 12px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.mobile-detail-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.mobile-detail-label {
|
|
font-size: 12px;
|
|
color: #718096;
|
|
margin-bottom: 3px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.mobile-detail-value {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #2d3748;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.mobile-invoice-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
}
|
|
|
|
.mobile-action-btn {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 8px 12px;
|
|
font-size: 13px;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 5px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.mobile-action-btn.view {
|
|
background: #f0f8ff;
|
|
color: #2469d6;
|
|
border: 1px solid #2469d6;
|
|
}
|
|
|
|
.mobile-action-btn.entry {
|
|
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
|
|
color: white;
|
|
border: none;
|
|
}
|
|
|
|
/* ---------- Pagination Styles ---------- */
|
|
.pagination-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-top: 20px;
|
|
padding: 15px 30px;
|
|
border-top: 1px solid #eef3fb;
|
|
}
|
|
|
|
.pagination-info {
|
|
font-size: 14px;
|
|
color: #9ba5bb;
|
|
font-weight: 600;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.pagination-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.pagination-btn {
|
|
background: #fff;
|
|
border: 1px solid #e3eaf6;
|
|
color: #1a2951;
|
|
padding: 10px 15px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 45px;
|
|
height: 38px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.pagination-btn:hover:not(:disabled) {
|
|
background: #1a2951;
|
|
color: white;
|
|
border-color: #1a2951;
|
|
}
|
|
|
|
.pagination-btn:disabled {
|
|
background: #f8fafc;
|
|
color: #cbd5e0;
|
|
border-color: #e2e8f0;
|
|
cursor: not-allowed;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.pagination-page-btn {
|
|
background: #fff;
|
|
border: 1px solid #e3eaf6;
|
|
color: #1a2951;
|
|
padding: 8px 15px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
min-width: 40px;
|
|
text-align: center;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
.pagination-page-btn:hover {
|
|
background: #1a2951;
|
|
color: white;
|
|
border-color: #1a2951;
|
|
}
|
|
|
|
.pagination-page-btn.active {
|
|
background: #1a2951;
|
|
color: white;
|
|
border-color: #1a2951;
|
|
}
|
|
|
|
.pagination-pages {
|
|
display: flex;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
|
|
.pagination-ellipsis {
|
|
color: #9ba5bb;
|
|
font-size: 14px;
|
|
padding: 0 6px;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* Image-based pagination buttons */
|
|
.pagination-img-btn {
|
|
background: #fff;
|
|
border: 1px solid #e3eaf6;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 45px;
|
|
height: 38px;
|
|
padding: 0;
|
|
}
|
|
|
|
.pagination-img-btn:hover:not(:disabled) {
|
|
background: #1a2951;
|
|
border-color: #1a2951;
|
|
}
|
|
|
|
.pagination-img-btn:disabled {
|
|
background: #f8fafc;
|
|
border-color: #e2e8f0;
|
|
cursor: not-allowed;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.pagination-img-btn img {
|
|
width: 18px;
|
|
height: 18px;
|
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
|
transition: filter 0.3s ease;
|
|
}
|
|
|
|
.pagination-img-btn:hover:not(:disabled) img {
|
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
|
}
|
|
|
|
.pagination-img-btn:disabled img {
|
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
|
}
|
|
|
|
/* Responsive Design */
|
|
@media (max-width: 1200px) {
|
|
.filter-group {
|
|
justify-content: space-between;
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
.stats-summary {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.invoice-management-bar {
|
|
padding: 15px 20px;
|
|
}
|
|
|
|
.table th, .table td {
|
|
font-size: 13px;
|
|
padding: 15px 10px;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 11px !important;
|
|
padding: 6px 12px 6px 8px !important;
|
|
min-width: 75px;
|
|
gap: 4px;
|
|
}
|
|
|
|
.status-icon {
|
|
font-size: 11px;
|
|
}
|
|
|
|
.btn-entry {
|
|
padding: 6px 12px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.invoice-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Show mobile cards and hide table on small screens */
|
|
.desktop-table {
|
|
display: none;
|
|
}
|
|
|
|
.mobile-invoice-card {
|
|
display: block;
|
|
}
|
|
|
|
.stats-summary {
|
|
grid-template-columns: 1fr;
|
|
margin: 20px;
|
|
}
|
|
|
|
.pagination-container {
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.pagination-controls {
|
|
justify-content: center;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.invoice-management-title {
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.card-header h4 {
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.mobile-invoice-details {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.mobile-invoice-actions {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.search-box {
|
|
min-width: auto;
|
|
padding: 10px 15px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="container-fluid py-3">
|
|
<!-- INVOICE MANAGEMENT -->
|
|
<div class="invoice-management-box">
|
|
<div class="invoice-management-bar">
|
|
<span class="invoice-management-title">
|
|
<i class="bi bi-receipt"></i> Invoice Management
|
|
</span>
|
|
</div>
|
|
|
|
<!-- UPDATED FILTER BAR WITH DATE RANGE -->
|
|
<div class="invoice-tools-row">
|
|
<form method="GET" action="{{ route('admin.invoices.index') }}" style="width: 100%;">
|
|
<div class="filter-bar-container">
|
|
<!-- Search Box -->
|
|
<div class="search-box">
|
|
<i class="bi bi-search"></i>
|
|
<input type="text"
|
|
id="searchInput"
|
|
name="search"
|
|
value="{{ request('search') }}"
|
|
placeholder="Search Invoices...">
|
|
</div>
|
|
|
|
<!-- Status Filter -->
|
|
<div class="filter-select-wrapper">
|
|
<select class="filter-select" id="statusFilter" name="status">
|
|
<option value="all" {{ request('status')=='all' ? 'selected' : '' }}>All Status</option>
|
|
<option value="paid" {{ request('status')=='paid' ? 'selected' : '' }}>Paid</option>
|
|
<option value="pending" {{ request('status')=='pending' ? 'selected' : '' }}>Pending</option>
|
|
<option value="overdue" {{ request('status')=='overdue' ? 'selected' : '' }}>Overdue</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Date Range Filter (FROM - TO) with dd-mm-yyyy format -->
|
|
<div class="date-range-wrapper">
|
|
<div class="date-input-container">
|
|
<i class="bi bi-calendar3"></i>
|
|
<input type="text"
|
|
class="date-input"
|
|
id="startDate"
|
|
name="start_date"
|
|
value="{{ request('start_date') ? date('d-m-Y', strtotime(request('start_date'))) : '' }}"
|
|
placeholder="dd-mm-yyyy"
|
|
onfocus="(this.type='date')"
|
|
onblur="if(!this.value) this.type='text'">
|
|
</div>
|
|
<span class="date-separator">to</span>
|
|
<div class="date-input-container">
|
|
<i class="bi bi-calendar3"></i>
|
|
<input type="text"
|
|
class="date-input"
|
|
id="endDate"
|
|
name="end_date"
|
|
value="{{ request('end_date') ? date('d-m-Y', strtotime(request('end_date'))) : '' }}"
|
|
placeholder="dd-mm-yyyy"
|
|
onfocus="(this.type='date')"
|
|
onblur="if(!this.value) this.type='text'">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="invoice-management-main no-extra-space">
|
|
<!-- Quick Stats Summary -->
|
|
<div class="stats-summary">
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ $invoices->count() }}</div>
|
|
<div class="stat-label">Total Invoices</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">₹{{ number_format($invoices->sum('final_amount_with_gst'), 2) }}</div>
|
|
<div class="stat-label">Total Revenue</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ $invoices->where('status', 'paid')->count() }}</div>
|
|
<div class="stat-label">Paid Invoices</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ $invoices->where('status', 'pending')->count() }}</div>
|
|
<div class="stat-label">Pending Invoices</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ALL INVOICES - Desktop Table -->
|
|
<div class="card shadow-sm desktop-table">
|
|
<div class="card-header bg-light d-flex justify-content-between align-items-center compact-view">
|
|
<h4 class="mb-0">All Invoices</h4>
|
|
<span class="text-muted" style="font-size: 13px;">
|
|
{{ $invoices->count() }} invoices
|
|
</span>
|
|
</div>
|
|
|
|
<div class="card-body p-0">
|
|
<div class="table-container">
|
|
<table class="table table-striped align-middle" id="invoicesTable">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="column-header">#</th>
|
|
<th class="column-header">Invoice Number</th>
|
|
<th class="column-header">Customer</th>
|
|
<th class="column-header">Container</th> {{-- NEW --}}
|
|
<th class="column-header">Final Amount</th>
|
|
<th class="column-header">GST %</th>
|
|
<th class="column-header">Total w/GST</th>
|
|
<th class="column-header">Status</th>
|
|
<th class="column-header">Invoice Date</th>
|
|
<th class="column-header">Due Date</th>
|
|
<th class="column-header">Action</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody id="invoicesTableBody">
|
|
@php
|
|
$totalInvoices = $invoices->count();
|
|
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
|
@endphp
|
|
|
|
@forelse($sortedInvoices as $i => $invoice)
|
|
<tr>
|
|
<td>{{ $totalInvoices - $i }}</td>
|
|
|
|
<td>
|
|
<div class="invoice-number-cell">
|
|
<div class="invoice-icon invoice-icon-{{ (($totalInvoices - $i) % 8) + 1 }}">
|
|
<i class="bi bi-file-earmark-text"></i>
|
|
</div>
|
|
<a href="#" class="invoice-number-link open-invoice-popup" data-id="{{ $invoice->id }}">
|
|
{{ $invoice->invoice_number }}
|
|
</a>
|
|
</div>
|
|
</td>
|
|
|
|
<td class="customer-cell">
|
|
{{ $invoice->customer_name }}
|
|
</td>
|
|
|
|
{{-- NEW: Container column --}}
|
|
<td class="customer-cell">
|
|
@if($invoice->container)
|
|
{{ $invoice->container->container_number }}
|
|
{{-- जर फक्त ID हवी असेल तर:
|
|
#{{ $invoice->container->id }}
|
|
--}}
|
|
@else
|
|
—
|
|
@endif
|
|
</td>
|
|
|
|
<td class="amount-cell">
|
|
₹{{ number_format($invoice->final_amount, 2) }}
|
|
</td>
|
|
|
|
<td class="gst-cell">
|
|
{{ $invoice->gst_percent }}%
|
|
</td>
|
|
|
|
<td class="amount-cell">
|
|
₹{{ number_format($invoice->final_amount_with_gst, 2) }}
|
|
</td>
|
|
|
|
<td>
|
|
<span class="badge badge-{{ $invoice->status }}">
|
|
@if($invoice->status == 'paid')
|
|
<i class="bi bi-check-circle-fill status-icon"></i>
|
|
@elseif($invoice->status == 'pending')
|
|
<i class="bi bi-clock-fill status-icon"></i>
|
|
@elseif($invoice->status == 'overdue')
|
|
<i class="bi bi-exclamation-triangle-fill status-icon"></i>
|
|
@endif
|
|
{{ ucfirst($invoice->status) }}
|
|
</span>
|
|
</td>
|
|
|
|
<td class="date-cell">
|
|
{{ $invoice->invoice_date }}
|
|
</td>
|
|
|
|
<td class="date-cell">
|
|
{{ $invoice->due_date }}
|
|
</td>
|
|
|
|
<td>
|
|
<a href="{{ route('admin.invoices.edit', $invoice->id) }}"
|
|
class="btn-entry">
|
|
<i class="bi bi-pencil"></i> Entry
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
{{-- 1 new column वाढवलाय म्हणून colspan 11 --}}
|
|
<td colspan="11" class="text-muted">No invoices found</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ALL INVOICES - Mobile Cards -->
|
|
<div class="mobile-invoices-container" id="mobileInvoicesContainer">
|
|
@php
|
|
$totalInvoices = $invoices->count();
|
|
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
|
@endphp
|
|
|
|
@forelse($sortedInvoices as $i => $invoice)
|
|
<div class="mobile-invoice-card" data-invoice-id="{{ $invoice->id }}">
|
|
<div class="mobile-invoice-header">
|
|
<div class="mobile-invoice-number">
|
|
<div class="mobile-invoice-icon invoice-icon-{{ (($totalInvoices - $i) % 8) + 1 }}">
|
|
<i class="bi bi-file-earmark-text"></i>
|
|
</div>
|
|
<span class="mobile-invoice-number-text">{{ $invoice->invoice_number }}</span>
|
|
</div>
|
|
<span class="badge badge-{{ $invoice->status }} mobile-invoice-status">
|
|
@if($invoice->status == 'paid')
|
|
<i class="bi bi-check-circle-fill status-icon"></i>
|
|
@elseif($invoice->status == 'pending')
|
|
<i class="bi bi-clock-fill status-icon"></i>
|
|
@elseif($invoice->status == 'overdue')
|
|
<i class="bi bi-exclamation-triangle-fill status-icon"></i>
|
|
@endif
|
|
{{ ucfirst($invoice->status) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="mobile-invoice-details">
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Customer</span>
|
|
<span class="mobile-detail-value">{{ $invoice->customer_name }}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Amount</span>
|
|
<span class="mobile-detail-value">₹{{ number_format($invoice->final_amount, 2) }}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">GST</span>
|
|
<span class="mobile-detail-value">{{ $invoice->gst_percent }}%</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Total</span>
|
|
<span class="mobile-detail-value">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Invoice Date</span>
|
|
<span class="mobile-detail-value">{{ $invoice->invoice_date }}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Due Date</span>
|
|
<span class="mobile-detail-value">{{ $invoice->due_date }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mobile-invoice-actions">
|
|
<a href="#" class="mobile-action-btn view open-invoice-popup" data-id="{{ $invoice->id }}">
|
|
<i class="bi bi-eye"></i> View
|
|
</a>
|
|
<a href="{{ route('admin.invoices.edit', $invoice->id) }}" class="mobile-action-btn entry">
|
|
<i class="bi bi-pencil"></i> Entry
|
|
</a>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="text-muted text-center py-4">No invoices found</div>
|
|
@endforelse
|
|
</div>
|
|
|
|
<!-- Pagination Controls -->
|
|
<div class="pagination-container">
|
|
<div class="pagination-info" id="pageInfo">Showing 1 to {{ $invoices->count() }} of {{ $invoices->count() }} entries</div>
|
|
<div class="pagination-controls">
|
|
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page" disabled>
|
|
<!-- Left arrow SVG -->
|
|
<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="paginationPages">
|
|
<!-- Page numbers will be inserted here -->
|
|
</div>
|
|
<button class="pagination-img-btn" id="nextPageBtn" title="Next page" disabled>
|
|
<!-- Right arrow SVG -->
|
|
<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>
|
|
|
|
{{-- POPUP MODAL --}}
|
|
<div class="modal fade" id="invoiceModal" tabindex="-1">
|
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Invoice Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
|
|
<div class="modal-body" id="invoiceModalBody">
|
|
<p class="text-center text-muted">Loading...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Pagination state
|
|
let currentPage = 1;
|
|
const itemsPerPage = 10;
|
|
let allInvoices = @json($invoices);
|
|
let filteredInvoices = [...allInvoices];
|
|
|
|
// Initialize pagination
|
|
renderTable();
|
|
updatePaginationControls();
|
|
|
|
// Bind pagination events
|
|
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
|
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
|
|
|
// Invoice popup functionality
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('.open-invoice-popup')) {
|
|
e.preventDefault();
|
|
const id = e.target.closest('.open-invoice-popup').dataset.id;
|
|
const modal = new bootstrap.Modal(document.getElementById('invoiceModal'));
|
|
|
|
document.getElementById('invoiceModalBody').innerHTML =
|
|
"<div class='text-center py-4'><div class='spinner-border text-primary' role='status'></div><p class='mt-2 text-muted'>Loading invoice details...</p></div>";
|
|
|
|
modal.show();
|
|
|
|
fetch(`/admin/invoices/${id}/popup`)
|
|
.then(res => {
|
|
if (!res.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return res.text();
|
|
})
|
|
.then(html => {
|
|
document.getElementById('invoiceModalBody').innerHTML = html;
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading invoice details:', error);
|
|
document.getElementById('invoiceModalBody').innerHTML =
|
|
"<div class='text-center py-4 text-danger'><i class='bi bi-exclamation-triangle fs-1'></i><p class='mt-2'>Failed to load invoice details. Please try again.</p></div>";
|
|
});
|
|
}
|
|
});
|
|
|
|
// Function to parse dd-mm-yyyy to Date object
|
|
function parseDate(dateStr) {
|
|
if (!dateStr) return null;
|
|
const parts = dateStr.split('-');
|
|
if (parts.length === 3) {
|
|
// dd-mm-yyyy format
|
|
return new Date(parts[2], parts[1] - 1, parts[0]);
|
|
}
|
|
return new Date(dateStr);
|
|
}
|
|
|
|
// Search and filter functionality
|
|
const searchInput = document.getElementById('searchInput');
|
|
const statusFilter = document.getElementById('statusFilter');
|
|
const startDate = document.getElementById('startDate');
|
|
const endDate = document.getElementById('endDate');
|
|
|
|
function filterInvoices() {
|
|
const searchTerm = searchInput.value.toLowerCase();
|
|
const statusValue = statusFilter.value;
|
|
const startDateValue = startDate.value;
|
|
const endDateValue = endDate.value;
|
|
|
|
filteredInvoices = allInvoices.filter(invoice => {
|
|
let include = true;
|
|
|
|
// Search filter
|
|
if (searchTerm) {
|
|
const matchesSearch =
|
|
invoice.invoice_number.toLowerCase().includes(searchTerm) ||
|
|
invoice.customer_name.toLowerCase().includes(searchTerm);
|
|
if (!matchesSearch) include = false;
|
|
}
|
|
|
|
// Status filter
|
|
if (statusValue && statusValue !== 'all' && invoice.status !== statusValue) {
|
|
include = false;
|
|
}
|
|
|
|
// Date range filter
|
|
if (startDateValue || endDateValue) {
|
|
const invoiceDate = parseDate(invoice.invoice_date);
|
|
|
|
if (startDateValue) {
|
|
const start = parseDate(startDateValue);
|
|
if (invoiceDate < start) include = false;
|
|
}
|
|
|
|
if (endDateValue) {
|
|
const end = parseDate(endDateValue);
|
|
if (invoiceDate > end) include = false;
|
|
}
|
|
}
|
|
|
|
return include;
|
|
});
|
|
|
|
currentPage = 1;
|
|
renderTable();
|
|
updatePaginationControls();
|
|
}
|
|
|
|
// Add event listeners for filtering
|
|
searchInput.addEventListener('input', filterInvoices);
|
|
statusFilter.addEventListener('change', filterInvoices);
|
|
startDate.addEventListener('change', filterInvoices);
|
|
endDate.addEventListener('change', filterInvoices);
|
|
|
|
// Also trigger search on Enter key in search input
|
|
searchInput.addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
filterInvoices();
|
|
}
|
|
});
|
|
|
|
// Pagination Functions
|
|
function goToPreviousPage() {
|
|
if (currentPage > 1) {
|
|
currentPage--;
|
|
renderTable();
|
|
updatePaginationControls();
|
|
}
|
|
}
|
|
|
|
function goToNextPage() {
|
|
const totalPages = Math.ceil(filteredInvoices.length / itemsPerPage);
|
|
if (currentPage < totalPages) {
|
|
currentPage++;
|
|
renderTable();
|
|
updatePaginationControls();
|
|
}
|
|
}
|
|
|
|
function updatePaginationControls() {
|
|
const totalPages = Math.ceil(filteredInvoices.length / itemsPerPage);
|
|
const prevBtn = document.getElementById('prevPageBtn');
|
|
const nextBtn = document.getElementById('nextPageBtn');
|
|
const pageInfo = document.getElementById('pageInfo');
|
|
const paginationPages = document.getElementById('paginationPages');
|
|
|
|
prevBtn.disabled = currentPage === 1;
|
|
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
|
|
|
|
// Update page info text
|
|
const startIndex = (currentPage - 1) * itemsPerPage + 1;
|
|
const endIndex = Math.min(currentPage * itemsPerPage, filteredInvoices.length);
|
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${filteredInvoices.length} entries`;
|
|
|
|
// Generate page numbers
|
|
paginationPages.innerHTML = '';
|
|
|
|
if (totalPages <= 7) {
|
|
// Show all pages
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
addPageButton(i, paginationPages);
|
|
}
|
|
} else {
|
|
// Show first page, current page range, and last page
|
|
addPageButton(1, paginationPages);
|
|
|
|
if (currentPage > 3) {
|
|
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
|
|
}
|
|
|
|
const start = Math.max(2, currentPage - 1);
|
|
const end = Math.min(totalPages - 1, currentPage + 1);
|
|
|
|
for (let i = start; i <= end; i++) {
|
|
addPageButton(i, paginationPages);
|
|
}
|
|
|
|
if (currentPage < totalPages - 2) {
|
|
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
|
|
}
|
|
|
|
addPageButton(totalPages, paginationPages);
|
|
}
|
|
}
|
|
|
|
function addPageButton(pageNumber, container) {
|
|
const button = document.createElement('button');
|
|
button.className = 'pagination-page-btn';
|
|
if (pageNumber === currentPage) {
|
|
button.classList.add('active');
|
|
}
|
|
button.textContent = pageNumber;
|
|
button.addEventListener('click', () => {
|
|
currentPage = pageNumber;
|
|
renderTable();
|
|
updatePaginationControls();
|
|
});
|
|
container.appendChild(button);
|
|
}
|
|
|
|
// Render Table Function
|
|
function renderTable() {
|
|
const tbody = document.getElementById('invoicesTableBody');
|
|
const mobileContainer = document.getElementById('mobileInvoicesContainer');
|
|
|
|
if (filteredInvoices.length === 0) {
|
|
// 1 column वाढवल्यामुळे colspan 11
|
|
tbody.innerHTML = '<tr><td colspan="11" class="text-muted">No invoices found</td></tr>';
|
|
mobileContainer.innerHTML = '<div class="text-muted text-center py-4">No invoices found</div>';
|
|
return;
|
|
}
|
|
|
|
// Calculate pagination
|
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
|
const endIndex = startIndex + itemsPerPage;
|
|
const paginatedItems = filteredInvoices.slice(startIndex, endIndex);
|
|
|
|
// Sort by creation date (newest first)
|
|
const sortedItems = [...paginatedItems].sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
|
|
|
// Render desktop table
|
|
tbody.innerHTML = '';
|
|
sortedItems.forEach((invoice, index) => {
|
|
const displayIndex = filteredInvoices.length - (startIndex + index);
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${displayIndex}</td>
|
|
<td>
|
|
<div class="invoice-number-cell">
|
|
<div class="invoice-icon invoice-icon-${(displayIndex % 8) + 1}">
|
|
<i class="bi bi-file-earmark-text"></i>
|
|
</div>
|
|
<a href="#" class="invoice-number-link open-invoice-popup" data-id="${invoice.id}">
|
|
${invoice.invoice_number}
|
|
</a>
|
|
</div>
|
|
</td>
|
|
<td class="customer-cell">${invoice.customer_name}</td>
|
|
<!-- NEW: Container column -->
|
|
<td class="customer-cell">
|
|
${invoice.container ? (invoice.container.container_number ?? '—') : '—'}
|
|
</td>
|
|
<td class="amount-cell">₹${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
|
<td class="gst-cell">${invoice.gst_percent}%</td>
|
|
<td class="amount-cell">₹${parseFloat(invoice.final_amount_with_gst).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
|
<td>
|
|
<span class="badge badge-${invoice.status}">
|
|
${invoice.status === 'paid' ? '<i class="bi bi-check-circle-fill status-icon"></i>' : ''}
|
|
${invoice.status === 'pending' ? '<i class="bi bi-clock-fill status-icon"></i>' : ''}
|
|
${invoice.status === 'overdue' ? '<i class="bi bi-exclamation-triangle-fill status-icon"></i>' : ''}
|
|
${invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1)}
|
|
</span>
|
|
</td>
|
|
<td class="date-cell">${invoice.invoice_date}</td>
|
|
<td class="date-cell">${invoice.due_date}</td>
|
|
<td>
|
|
<a href="/admin/invoices/${invoice.id}/edit" class="btn-entry">
|
|
<i class="bi bi-pencil"></i> Entry
|
|
</a>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
// Render mobile cards
|
|
mobileContainer.innerHTML = '';
|
|
sortedItems.forEach((invoice, index) => {
|
|
const displayIndex = filteredInvoices.length - (startIndex + index);
|
|
const card = document.createElement('div');
|
|
card.className = 'mobile-invoice-card';
|
|
card.setAttribute('data-invoice-id', invoice.id);
|
|
card.innerHTML = `
|
|
<div class="mobile-invoice-header">
|
|
<div class="mobile-invoice-number">
|
|
<div class="mobile-invoice-icon invoice-icon-${(displayIndex % 8) + 1}">
|
|
<i class="bi bi-file-earmark-text"></i>
|
|
</div>
|
|
<span class="mobile-invoice-number-text">${invoice.invoice_number}</span>
|
|
</div>
|
|
<span class="badge badge-${invoice.status} mobile-invoice-status">
|
|
${invoice.status === 'paid' ? '<i class="bi bi-check-circle-fill status-icon"></i>' : ''}
|
|
${invoice.status === 'pending' ? '<i class="bi bi-clock-fill status-icon"></i>' : ''}
|
|
${invoice.status === 'overdue' ? '<i class="bi bi-exclamation-triangle-fill status-icon"></i>' : ''}
|
|
${invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1)}
|
|
</span>
|
|
</div>
|
|
<div class="mobile-invoice-details">
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Customer</span>
|
|
<span class="mobile-detail-value">${invoice.customer_name}</span>
|
|
</div>
|
|
<!-- NEW: Container for mobile -->
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Container</span>
|
|
<span class="mobile-detail-value">
|
|
${invoice.container ? (invoice.container.container_number ?? '—') : '—'}
|
|
</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Amount</span>
|
|
<span class="mobile-detail-value">₹${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">GST</span>
|
|
<span class="mobile-detail-value">${invoice.gst_percent}%</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Total</span>
|
|
<span class="mobile-detail-value">₹${parseFloat(invoice.final_amount_with_gst).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Invoice Date</span>
|
|
<span class="mobile-detail-value">${invoice.invoice_date}</span>
|
|
</div>
|
|
<div class="mobile-detail-item">
|
|
<span class="mobile-detail-label">Due Date</span>
|
|
<span class="mobile-detail-value">${invoice.due_date}</span>
|
|
</div>
|
|
</div>
|
|
<div class="mobile-invoice-actions">
|
|
<a href="#" class="mobile-action-btn view open-invoice-popup" data-id="${invoice.id}">
|
|
<i class="bi bi-eye"></i> View
|
|
</a>
|
|
<a href="/admin/invoices/${invoice.id}/edit" class="mobile-action-btn entry">
|
|
<i class="bi bi-pencil"></i> Entry
|
|
</a>
|
|
</div>
|
|
`;
|
|
mobileContainer.appendChild(card);
|
|
});
|
|
}
|
|
|
|
// Add hover effects to table rows
|
|
document.addEventListener('mouseover', function(e) {
|
|
if (e.target.closest('tr')) {
|
|
const row = e.target.closest('tr');
|
|
if (row.parentElement.tagName === 'TBODY') {
|
|
row.style.cursor = 'pointer';
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
@endsection |