UI Update Customer Section

This commit is contained in:
Utkarsh Khedkar
2025-11-21 16:15:10 +05:30
16 changed files with 3476 additions and 848 deletions

View File

@@ -173,7 +173,7 @@
width: 100%;
min-width: 1100px;
border-collapse: separate;
border-spacing: 0;
border-spacing: 0 8px; /* Add gap between rows */
margin-bottom: 0;
}
@@ -189,7 +189,6 @@
border-top-right-radius: 17px;
}
.table thead th {
background: transparent !important;
border: none;
@@ -197,7 +196,7 @@
color: #343535;
letter-spacing: 0.02em;
font-size: 13px;
padding: 12px 8px;
padding: 16px 12px;
white-space: nowrap;
position: relative;
border-bottom: 2px solid #e8e2cf;
@@ -206,50 +205,58 @@
}
.table thead th:first-child {
padding-left: 20px;
padding-left: 25px;
}
.table thead th:last-child {
padding-right: 20px;
padding-right: 25px;
}
/* Soft blue background for ALL table rows */
.table-striped tbody tr {
background: #fff;
background: #f0f8ff !important; /* Soft blue background for all rows */
transition: all 0.2s ease;
border-bottom: 1px solid #f1f3f4;
border-radius: 12px; /* Rounded corners for each row */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* Subtle shadow for separation */
}
.table-striped tbody tr:hover {
background: #f8fafc !important;
}
.table-striped tbody tr:nth-of-type(odd) {
background: #f9fafc;
background: #e6f3ff !important; /* Slightly darker blue on hover */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* Enhanced shadow on hover */
transform: translateY(-1px); /* Lift effect on hover */
}
/* Remove striped pattern - all rows same soft blue */
.table-striped tbody tr:nth-of-type(odd),
.table-striped tbody tr:nth-of-type(even) {
background: #ffffff;
background: #f0f8ff !important; /* Same soft blue for all rows */
}
/* Center all table cells */
/* Center all table cells with proper spacing */
.table td {
padding: 10px 8px;
padding: 16px 12px; /* Increased vertical padding */
border: none;
vertical-align: middle;
white-space: nowrap;
font-size: 13px;
color: #4a5568;
border-bottom: 1px solid #f1f3f4;
text-align: center !important; /* Center all cell content */
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
}
/* First and last cell rounded corners */
.table td:first-child {
padding-left: 20px;
padding-left: 25px;
font-weight: 600;
color: #2d3748;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
}
.table td:last-child {
padding-right: 20px;
padding-right: 25px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
}
/* Invoice Number with Curved Boxes */
@@ -257,13 +264,13 @@
display: flex;
align-items: center;
justify-content: center; /* Center the invoice number */
gap: 8px;
padding: 6px 0;
gap: 12px;
padding: 8px 0;
}
.invoice-icon {
width: 32px;
height: 32px;
width: 36px;
height: 36px;
border-radius: 8px; /* Curved border radius */
display: flex;
align-items: center;
@@ -324,31 +331,67 @@
text-decoration: none;
}
/* Badge Styling - Centered */
/* Badge Styling - Centered with custom backgrounds and icons */
.badge {
font-size: 11px;
font-weight: 600;
padding: 4px 10px;
border-radius: 8px;
font-size: 11px !important;
font-weight: 600 !important;
padding: 6px 12px 6px 8px !important;
border-radius: 20px !important;
text-transform: uppercase;
letter-spacing: 0.3px;
display: inline-block;
text-align: center;
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: 40px;
box-sizing: border-box;
line-height: 1.2;
gap: 6px;
}
.bg-success {
background: linear-gradient(135deg, #18ce77 60%, #08ac52 110%) !important;
color: #fff !important;
/* Status icons */
.status-icon {
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.bg-warning {
background: linear-gradient(135deg, #ffb23c 53%, #e17800 110%) !important;
color: #fff !important;
/* Custom status badge backgrounds with icons */
.badge-paid {
background: url('/images/status-bg-paid.png') !important;
}
.bg-danger {
background: linear-gradient(135deg, #ff5a4e 65%, #d90010 110%) !important;
color: #fff !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;
}
/* Action Button - Centered */
@@ -356,7 +399,7 @@
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
border: none;
border-radius: 6px;
padding: 5px 10px;
padding: 6px 12px;
font-weight: 600;
font-size: 12px;
transition: all 0.3s ease;
@@ -461,102 +504,33 @@
overflow-y: auto;
}
/* Responsive Design */
@media (max-width: 1200px) {
.invoice-tools-row {
flex-direction: column;
align-items: stretch;
gap: 15px;
}
.search-box {
max-width: 100%;
min-width: auto;
}
.filter-group {
justify-content: space-between;
width: 100%;
}
/* Date Range Picker Styles */
.date-range-container {
display: flex;
align-items: center;
gap: 8px;
}
@media (max-width: 768px) {
.invoice-management-bar {
padding: 12px 20px;
}
.invoice-tools-row {
padding: 15px;
}
.table th, .table td {
font-size: 12px;
padding: 8px 6px;
}
.badge {
font-size: 10px;
padding: 3px 8px;
}
.btn-primary {
padding: 4px 8px;
font-size: 11px;
}
.filter-group {
flex-direction: column;
align-items: stretch;
}
.filter-select {
min-width: auto;
width: 100%;
}
.create-invoice-btn {
width: 100%;
justify-content: center;
}
.invoice-icon {
width: 28px;
height: 28px;
font-size: 12px;
}
}
/* Empty State - Centered */
.text-muted {
color: #8a9bb9 !important;
font-style: italic;
padding: 30px !important;
text-align: center;
.date-input {
background: white;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 8px 12px;
font-size: 14px;
color: #334155;
outline: none;
min-width: 130px;
box-shadow: 0 2px 5px rgba(0,0,0,0.04);
}
/* Card Styling */
.card {
border-radius: 0 0 17px 17px;
border: none;
margin-bottom: 0 !important;
box-shadow: none;
.date-input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.card-header {
background: #f8f9fa !important;
border-bottom: 1px solid #e9ecef;
border-radius: 0 !important;
padding: 15px 25px;
text-align: center;
}
.card-header h4 {
margin: 0;
color: #2451af;
font-weight: 700;
font-size: 1.3rem;
text-align: center;
.date-separator {
color: #64748b;
font-weight: 500;
}
/* Stats Summary - Centered */
@@ -622,6 +596,262 @@
.table tbody tr td .btn-primary {
margin: 0 auto;
}
/* Empty State - Centered */
.text-muted {
color: #8a9bb9 !important;
font-style: italic;
padding: 30px !important;
text-align: center;
font-size: 14px;
}
/* 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: 15px 25px;
text-align: center;
}
.card-header h4 {
margin: 0;
color: #2451af;
font-weight: 700;
font-size: 1.3rem;
text-align: center;
}
/* Mobile Responsive Styles */
.mobile-invoice-card {
display: none;
background: #f0f8ff;
border-radius: 12px;
padding: 15px;
margin-bottom: 12px;
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: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #e2e8f0;
}
.mobile-invoice-number {
display: flex;
align-items: center;
gap: 8px;
}
.mobile-invoice-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: bold;
}
.mobile-invoice-number-text {
font-weight: 600;
color: #2469d6;
font-size: 14px;
}
.mobile-invoice-status {
font-size: 10px !important;
padding: 4px 8px 4px 6px !important;
min-width: 70px;
}
.mobile-invoice-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 12px;
}
.mobile-detail-item {
display: flex;
flex-direction: column;
}
.mobile-detail-label {
font-size: 11px;
color: #718096;
margin-bottom: 2px;
}
.mobile-detail-value {
font-size: 13px;
font-weight: 500;
color: #2d3748;
}
.mobile-invoice-actions {
display: flex;
justify-content: space-between;
gap: 8px;
}
.mobile-action-btn {
flex: 1;
text-align: center;
padding: 6px 10px;
font-size: 12px;
border-radius: 6px;
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.mobile-action-btn.view {
background: #f0f8ff;
color: #2469d6;
border: 1px solid #2469d6;
}
.mobile-action-btn.edit {
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
color: white;
border: none;
}
/* Responsive Design */
@media (max-width: 1200px) {
.invoice-tools-row {
flex-direction: column;
align-items: stretch;
gap: 15px;
}
.search-box {
max-width: 100%;
min-width: auto;
}
.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: 12px 20px;
}
.invoice-tools-row {
padding: 15px;
}
.table th, .table td {
font-size: 12px;
padding: 12px 8px;
}
.badge {
font-size: 10px !important;
padding: 5px 10px 5px 6px !important;
min-width: 70px;
gap: 4px;
}
.status-icon {
font-size: 10px;
}
.btn-primary {
padding: 4px 8px;
font-size: 11px;
}
.filter-group {
flex-direction: column;
align-items: stretch;
}
.filter-select {
min-width: auto;
width: 100%;
}
.create-invoice-btn {
width: 100%;
justify-content: center;
}
.invoice-icon {
width: 28px;
height: 28px;
font-size: 12px;
}
/* Show mobile cards and hide table on small screens */
.desktop-table {
display: none;
}
.mobile-invoice-card {
display: block;
}
.date-range-container {
flex-direction: column;
width: 100%;
}
.date-input {
width: 100%;
}
.stats-summary {
grid-template-columns: 1fr;
margin: 15px;
}
}
@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;
}
}
</style>
<div class="container-fluid py-3">
@@ -650,19 +880,12 @@
<option value="overdue">Overdue</option>
</select>
<select class="filter-select" id="dateFilter">
<option value="">All Dates</option>
<option value="today">Today</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
</select>
<select class="filter-select" id="amountFilter">
<option value="">Any Amount</option>
<option value="0-1000">₹0 - ₹1,000</option>
<option value="1000-5000">₹1,000 - ₹5,000</option>
<option value="5000+">₹5,000+</option>
</select>
<!-- Date Range Picker -->
<div class="date-range-container">
<input type="date" class="date-input" id="startDate">
<span class="date-separator">to</span>
<input type="date" class="date-input" id="endDate">
</div>
</div>
<!-- Create Invoice Button -->
@@ -692,8 +915,8 @@
</div>
</div>
<!-- ALL INVOICES TABLE -->
<div class="card shadow-sm">
<!-- 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: 12px;">
@@ -747,10 +970,14 @@
<td class="amount-cell">{{ number_format($invoice->final_amount_with_gst, 2) }}</td>
<td>
<span class="badge
@if($invoice->status=='paid') bg-success
@elseif($invoice->status=='overdue') bg-danger
@else bg-warning @endif">
<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>
@@ -775,6 +1002,75 @@
</div>
</div>
</div>
<!-- ALL INVOICES - Mobile Cards -->
<div class="mobile-invoices-container">
@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 edit">
<i class="bi bi-pencil"></i> Edit
</a>
</div>
</div>
@empty
<div class="text-muted text-center py-4">No invoices found</div>
@endforelse
</div>
</div>
</div>
</div>
@@ -827,20 +1123,26 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
// Search functionality
// Search and filter functionality for both desktop and mobile
const searchInput = document.getElementById('invoiceSearch');
const statusFilter = document.getElementById('statusFilter');
const dateFilter = document.getElementById('dateFilter');
const amountFilter = document.getElementById('amountFilter');
const startDateInput = document.getElementById('startDate');
const endDateInput = document.getElementById('endDate');
// Desktop table elements
const table = document.getElementById('invoicesTable');
const tableRows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
const tableRows = table ? table.getElementsByTagName('tbody')[0].getElementsByTagName('tr') : [];
// Mobile card elements
const mobileCards = document.querySelectorAll('.mobile-invoice-card');
function filterInvoices() {
const searchTerm = searchInput.value.toLowerCase();
const statusValue = statusFilter.value;
const dateValue = dateFilter.value;
const amountValue = amountFilter.value;
const startDate = startDateInput.value;
const endDate = endDateInput.value;
// Filter desktop table rows
for (let row of tableRows) {
const cells = row.getElementsByTagName('td');
if (cells.length < 9) continue;
@@ -848,25 +1150,55 @@ document.addEventListener('DOMContentLoaded', function() {
const invoiceNumber = cells[1].textContent.toLowerCase();
const customerName = cells[2].textContent.toLowerCase();
const status = cells[6].textContent.toLowerCase();
const amount = parseFloat(cells[3].textContent.replace('₹', '').replace(',', ''));
const invoiceDate = cells[7].textContent;
const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm);
const matchesStatus = !statusValue || status.includes(statusValue);
const matchesAmount = !amountValue || (
amountValue === '0-1000' ? amount <= 1000 :
amountValue === '1000-5000' ? amount > 1000 && amount <= 5000 :
amountValue === '5000+' ? amount > 5000 : true
);
// Date filtering
let matchesDate = true;
if (startDate || endDate) {
const cellDate = new Date(invoiceDate);
const start = startDate ? new Date(startDate) : null;
const end = endDate ? new Date(endDate) : null;
if (start && cellDate < start) matchesDate = false;
if (end && cellDate > end) matchesDate = false;
}
row.style.display = matchesSearch && matchesStatus && matchesAmount ? '' : 'none';
row.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none';
}
// Filter mobile cards
for (let card of mobileCards) {
const invoiceNumber = card.querySelector('.mobile-invoice-number-text').textContent.toLowerCase();
const customerName = card.querySelector('.mobile-detail-item:nth-child(1) .mobile-detail-value').textContent.toLowerCase();
const status = card.querySelector('.badge').textContent.toLowerCase();
const invoiceDate = card.querySelector('.mobile-detail-item:nth-child(5) .mobile-detail-value').textContent;
const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm);
const matchesStatus = !statusValue || status.includes(statusValue);
// Date filtering
let matchesDate = true;
if (startDate || endDate) {
const cellDate = new Date(invoiceDate);
const start = startDate ? new Date(startDate) : null;
const end = endDate ? new Date(endDate) : null;
if (start && cellDate < start) matchesDate = false;
if (end && cellDate > end) matchesDate = false;
}
card.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none';
}
}
// Add event listeners for filtering
searchInput.addEventListener('input', filterInvoices);
statusFilter.addEventListener('change', filterInvoices);
dateFilter.addEventListener('change', filterInvoices);
amountFilter.addEventListener('change', filterInvoices);
startDateInput.addEventListener('change', filterInvoices);
endDateInput.addEventListener('change', filterInvoices);
// Add hover effects to table rows
for (let row of tableRows) {