Account Section UI Changes
This commit is contained in:
@@ -9,36 +9,42 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
|
|
||||||
class AdminCustomerController extends Controller
|
class AdminCustomerController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// LIST CUSTOMERS (with search + status filter)
|
// LIST CUSTOMERS (with search + status filter)
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$search = $request->search;
|
$search = $request->search;
|
||||||
$status = $request->status;
|
$status = $request->status;
|
||||||
|
|
||||||
$query = User::with(['marks', 'orders'])->orderBy('id', 'desc');
|
$query = User::with(['marks', 'orders'])->orderBy('id', 'desc');
|
||||||
|
|
||||||
// SEARCH FILTER
|
// SEARCH FILTER
|
||||||
if (!empty($search)) {
|
if (!empty($search)) {
|
||||||
$query->where(function ($q) use ($search) {
|
$query->where(function ($q) use ($search) {
|
||||||
$q->where('customer_name', 'like', "%$search%")
|
$q->where('customer_name', 'like', "%$search%")
|
||||||
->orWhere('email', 'like', "%$search%")
|
->orWhere('email', 'like', "%$search%")
|
||||||
->orWhere('mobile_no', 'like', "%$search%")
|
->orWhere('mobile_no', 'like', "%$search%")
|
||||||
->orWhere('customer_id', 'like', "%$search%");
|
->orWhere('customer_id', 'like', "%$search%");
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// STATUS FILTER
|
|
||||||
if (!empty($status) && in_array($status, ['active', 'inactive'])) {
|
|
||||||
$query->where('status', $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
$customers = $query->get();
|
|
||||||
|
|
||||||
return view('admin.customers', compact('customers', 'search', 'status'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// STATUS FILTER
|
||||||
|
if (!empty($status) && in_array($status, ['active', 'inactive'])) {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all customers for statistics (without pagination)
|
||||||
|
$allCustomers = $query->get();
|
||||||
|
|
||||||
|
// Get paginated customers for the table (10 per page)
|
||||||
|
$customers = $query->paginate(10);
|
||||||
|
|
||||||
|
return view('admin.customers', compact('customers', 'allCustomers', 'search', 'status'));
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// SHOW ADD CUSTOMER FORM
|
// SHOW ADD CUSTOMER FORM
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
@@ -130,5 +136,6 @@ class AdminCustomerController extends Controller
|
|||||||
|
|
||||||
return back()->with('success', 'Customer status updated.');
|
return back()->with('success', 'Customer status updated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class RequestController extends Controller
|
|||||||
'pincode' => $request->pincode,
|
'pincode' => $request->pincode,
|
||||||
'date' => Carbon::now()->toDateString(), // Auto current date
|
'date' => Carbon::now()->toDateString(), // Auto current date
|
||||||
'status' => 'pending', // Default status
|
'status' => 'pending', // Default status
|
||||||
|
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ✅ Response
|
// ✅ Response
|
||||||
|
|||||||
BIN
public/invoices/invoice-INV-2025-000026.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000026.pdf
Normal file
Binary file not shown.
@@ -523,6 +523,20 @@ tr:hover td{ background:#fbfdff; }
|
|||||||
.entry-summary-cards { gap: 12px; }
|
.entry-summary-cards { gap: 12px; }
|
||||||
.entry-summary-card { min-width: 140px; }
|
.entry-summary-card { min-width: 140px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Table pagination wrapper */
|
||||||
|
.table-pagination-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal pagination wrapper */
|
||||||
|
.modal-pagination-wrapper {
|
||||||
|
margin-top: 15px;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="account-container">
|
<div class="account-container">
|
||||||
@@ -551,6 +565,7 @@ tr:hover td{ background:#fbfdff; }
|
|||||||
|
|
||||||
<!-- Panels -->
|
<!-- Panels -->
|
||||||
<div class="account-panels" id="account-panels">
|
<div class="account-panels" id="account-panels">
|
||||||
|
<!-- Payment Sent Table -->
|
||||||
<div class="panel-card">
|
<div class="panel-card">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<span>Payment Sent to China</span>
|
<span>Payment Sent to China</span>
|
||||||
@@ -564,11 +579,34 @@ tr:hover td{ background:#fbfdff; }
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="paymentTableBody">
|
<tbody id="paymentTableBody">
|
||||||
<tr><td colspan="8" class="empty-state">Loading entries...</td></tr>
|
<!-- Entries will be loaded here -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- Pagination for Payment Table -->
|
||||||
|
<div class="table-pagination-wrapper">
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="paymentPageInfo">Showing 0 entries</div>
|
||||||
|
<div class="pagination-controls">
|
||||||
|
<button class="pagination-img-btn" id="paymentPrevBtn" title="Previous page">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="pagination-pages" id="paymentPaginationPages">
|
||||||
|
<!-- Page numbers will be inserted here -->
|
||||||
|
</div>
|
||||||
|
<button class="pagination-img-btn" id="paymentNextBtn" title="Next page">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Order Dispatch Table -->
|
||||||
<div class="panel-card">
|
<div class="panel-card">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<span>Order Dispatch Status</span>
|
<span>Order Dispatch Status</span>
|
||||||
@@ -583,9 +621,31 @@ tr:hover td{ background:#fbfdff; }
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="orderTableBody">
|
<tbody id="orderTableBody">
|
||||||
<tr><td colspan="8" class="empty-state">Loading entries...</td></tr>
|
<!-- Entries will be loaded here -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- Pagination for Order Table -->
|
||||||
|
<div class="table-pagination-wrapper">
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="orderPageInfo">Showing 0 entries</div>
|
||||||
|
<div class="pagination-controls">
|
||||||
|
<button class="pagination-img-btn" id="orderPrevBtn" 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="orderPaginationPages">
|
||||||
|
<!-- Page numbers will be inserted here -->
|
||||||
|
</div>
|
||||||
|
<button class="pagination-img-btn" id="orderNextBtn" title="Next page">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -649,29 +709,29 @@ tr:hover td{ background:#fbfdff; }
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="consolidateOrdersBody">
|
<tbody id="consolidateOrdersBody">
|
||||||
<tr><td colspan="14" class="empty-state">Loading available orders...</td></tr>
|
<!-- Orders will be loaded here -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Pagination Controls -->
|
<!-- Pagination for Create Order Modal -->
|
||||||
<div class="pagination-container">
|
<div class="modal-pagination-wrapper">
|
||||||
<div class="pagination-info" id="pageInfo">Showing 1 to 10 of 0 entries</div>
|
<div class="pagination-container">
|
||||||
<div class="pagination-controls">
|
<div class="pagination-info" id="modalPageInfo">Showing 0 entries</div>
|
||||||
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page">
|
<div class="pagination-controls">
|
||||||
<!-- Left arrow SVG -->
|
<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">
|
<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"/>
|
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="pagination-pages" id="paginationPages">
|
<div class="pagination-pages" id="modalPaginationPages">
|
||||||
<!-- Page numbers will be inserted here -->
|
<!-- Page numbers will be inserted here -->
|
||||||
|
</div>
|
||||||
|
<button class="pagination-img-btn" id="modalNextBtn" title="Next page">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="pagination-img-btn" id="nextPageBtn" title="Next page">
|
|
||||||
<!-- 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>
|
</div>
|
||||||
@@ -682,8 +742,6 @@ tr:hover td{ background:#fbfdff; }
|
|||||||
<button type="submit" class="btn">Create Order</button>
|
<button type="submit" class="btn">Create Order</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="helper-note">Tip: Select orders from the list to include them in this consolidated entry. You can also search orders above.</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -787,7 +845,6 @@ function jsonFetch(url, opts = {}) {
|
|||||||
opts.headers = Object.assign({'Content-Type':'application/json','X-CSRF-TOKEN': csrfToken}, opts.headers || {});
|
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);
|
if(opts.body && typeof opts.body !== 'string') opts.body = JSON.stringify(opts.body);
|
||||||
return fetch(url, opts).then(r => {
|
return fetch(url, opts).then(r => {
|
||||||
// attempt to parse json even on non-ok to get backend message
|
|
||||||
return r.json().catch(() => { throw new Error('Invalid server response'); });
|
return r.json().catch(() => { throw new Error('Invalid server response'); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -796,9 +853,13 @@ let entries = [];
|
|||||||
let availableOrders = [];
|
let availableOrders = [];
|
||||||
let currentEntry = null;
|
let currentEntry = null;
|
||||||
|
|
||||||
/* Pagination state */
|
/* Single pagination state for both tables */
|
||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
const ordersPerPage = 10;
|
const entriesPerPage = 10;
|
||||||
|
|
||||||
|
/* Separate pagination state for modal */
|
||||||
|
let modalCurrentPage = 1;
|
||||||
|
const modalOrdersPerPage = 10;
|
||||||
|
|
||||||
/* small util */
|
/* small util */
|
||||||
function escapeHtml(s){ if(s === null || s === undefined) return ''; return String(s).replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":"'"}[m])); }
|
function escapeHtml(s){ if(s === null || s === undefined) return ''; return String(s).replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":"'"}[m])); }
|
||||||
@@ -828,9 +889,17 @@ function bindUI(){
|
|||||||
document.getElementById('cancelCreateModal').addEventListener('click', closeCreateOrderModal);
|
document.getElementById('cancelCreateModal').addEventListener('click', closeCreateOrderModal);
|
||||||
document.getElementById('createOrderInlineForm').addEventListener('submit', submitCreateOrderInline);
|
document.getElementById('createOrderInlineForm').addEventListener('submit', submitCreateOrderInline);
|
||||||
|
|
||||||
// Pagination buttons
|
// Payment Table Pagination
|
||||||
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
document.getElementById('paymentPrevBtn').addEventListener('click', () => goToPreviousPage());
|
||||||
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
document.getElementById('paymentNextBtn').addEventListener('click', () => goToNextPage());
|
||||||
|
|
||||||
|
// Order Table Pagination
|
||||||
|
document.getElementById('orderPrevBtn').addEventListener('click', () => goToPreviousPage());
|
||||||
|
document.getElementById('orderNextBtn').addEventListener('click', () => goToNextPage());
|
||||||
|
|
||||||
|
// Modal Pagination
|
||||||
|
document.getElementById('modalPrevBtn').addEventListener('click', () => goToModalPreviousPage());
|
||||||
|
document.getElementById('modalNextBtn').addEventListener('click', () => goToModalNextPage());
|
||||||
|
|
||||||
document.getElementById('refreshBtn').addEventListener('click', () => { loadDashboard(); });
|
document.getElementById('refreshBtn').addEventListener('click', () => { loadDashboard(); });
|
||||||
document.getElementById('searchBtn').addEventListener('click', handleSearch);
|
document.getElementById('searchBtn').addEventListener('click', handleSearch);
|
||||||
@@ -848,34 +917,58 @@ function bindUI(){
|
|||||||
function goToPreviousPage() {
|
function goToPreviousPage() {
|
||||||
if (currentPage > 1) {
|
if (currentPage > 1) {
|
||||||
currentPage--;
|
currentPage--;
|
||||||
renderConsolidateOrders(availableOrders);
|
renderBothTables();
|
||||||
updatePaginationControls();
|
updateBothPaginationControls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToNextPage() {
|
function goToNextPage() {
|
||||||
const totalPages = Math.ceil(availableOrders.length / ordersPerPage);
|
const totalPages = Math.ceil(entries.length / entriesPerPage);
|
||||||
if (currentPage < totalPages) {
|
if (currentPage < totalPages) {
|
||||||
currentPage++;
|
currentPage++;
|
||||||
renderConsolidateOrders(availableOrders);
|
renderBothTables();
|
||||||
updatePaginationControls();
|
updateBothPaginationControls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePaginationControls() {
|
function goToModalPreviousPage() {
|
||||||
const totalPages = Math.ceil(availableOrders.length / ordersPerPage);
|
if (modalCurrentPage > 1) {
|
||||||
const prevBtn = document.getElementById('prevPageBtn');
|
modalCurrentPage--;
|
||||||
const nextBtn = document.getElementById('nextPageBtn');
|
renderConsolidateOrders(availableOrders);
|
||||||
const pageInfo = document.getElementById('pageInfo');
|
updateModalPaginationControls();
|
||||||
const paginationPages = document.getElementById('paginationPages');
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToModalNextPage() {
|
||||||
|
const totalPages = Math.ceil(availableOrders.length / modalOrdersPerPage);
|
||||||
|
if (modalCurrentPage < totalPages) {
|
||||||
|
modalCurrentPage++;
|
||||||
|
renderConsolidateOrders(availableOrders);
|
||||||
|
updateModalPaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBothPaginationControls() {
|
||||||
|
const totalPages = Math.ceil(entries.length / entriesPerPage);
|
||||||
|
|
||||||
|
// Update both pagination controls
|
||||||
|
updatePaginationControls('payment', totalPages);
|
||||||
|
updatePaginationControls('order', totalPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls(tableType, totalPages) {
|
||||||
|
const prevBtn = document.getElementById(tableType + 'PrevBtn');
|
||||||
|
const nextBtn = document.getElementById(tableType + 'NextBtn');
|
||||||
|
const pageInfo = document.getElementById(tableType + 'PageInfo');
|
||||||
|
const paginationPages = document.getElementById(tableType + 'PaginationPages');
|
||||||
|
|
||||||
prevBtn.disabled = currentPage === 1;
|
prevBtn.disabled = currentPage === 1;
|
||||||
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
|
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
|
||||||
|
|
||||||
// Update page info text
|
// Update page info text
|
||||||
const startIndex = (currentPage - 1) * ordersPerPage + 1;
|
const startIndex = (currentPage - 1) * entriesPerPage + 1;
|
||||||
const endIndex = Math.min(currentPage * ordersPerPage, availableOrders.length);
|
const endIndex = Math.min(currentPage * entriesPerPage, entries.length);
|
||||||
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${availableOrders.length} entries`;
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${entries.length} entries`;
|
||||||
|
|
||||||
// Generate page numbers
|
// Generate page numbers
|
||||||
paginationPages.innerHTML = '';
|
paginationPages.innerHTML = '';
|
||||||
@@ -908,49 +1001,113 @@ function updatePaginationControls() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateModalPaginationControls() {
|
||||||
|
const totalPages = Math.ceil(availableOrders.length / modalOrdersPerPage);
|
||||||
|
const prevBtn = document.getElementById('modalPrevBtn');
|
||||||
|
const nextBtn = document.getElementById('modalNextBtn');
|
||||||
|
const pageInfo = document.getElementById('modalPageInfo');
|
||||||
|
const paginationPages = document.getElementById('modalPaginationPages');
|
||||||
|
|
||||||
|
prevBtn.disabled = modalCurrentPage === 1;
|
||||||
|
nextBtn.disabled = modalCurrentPage === totalPages || totalPages === 0;
|
||||||
|
|
||||||
|
// Update page info text
|
||||||
|
const startIndex = (modalCurrentPage - 1) * modalOrdersPerPage + 1;
|
||||||
|
const endIndex = Math.min(modalCurrentPage * modalOrdersPerPage, availableOrders.length);
|
||||||
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${availableOrders.length} orders`;
|
||||||
|
|
||||||
|
// Generate page numbers
|
||||||
|
paginationPages.innerHTML = '';
|
||||||
|
|
||||||
|
if (totalPages <= 7) {
|
||||||
|
// Show all pages
|
||||||
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
addModalPageButton(i, paginationPages);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show first page, current page range, and last page
|
||||||
|
addModalPageButton(1, paginationPages);
|
||||||
|
|
||||||
|
if (modalCurrentPage > 3) {
|
||||||
|
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = Math.max(2, modalCurrentPage - 1);
|
||||||
|
const end = Math.min(totalPages - 1, modalCurrentPage + 1);
|
||||||
|
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
addModalPageButton(i, paginationPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modalCurrentPage < totalPages - 2) {
|
||||||
|
paginationPages.innerHTML += '<span class="pagination-ellipsis">...</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
addModalPageButton(totalPages, paginationPages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function addPageButton(pageNumber, container) {
|
function addPageButton(pageNumber, container) {
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.className = 'pagination-page-btn';
|
button.className = 'pagination-page-btn';
|
||||||
|
|
||||||
if (pageNumber === currentPage) {
|
if (pageNumber === currentPage) {
|
||||||
button.classList.add('active');
|
button.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
button.textContent = pageNumber;
|
button.textContent = pageNumber;
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
currentPage = pageNumber;
|
currentPage = pageNumber;
|
||||||
renderConsolidateOrders(availableOrders);
|
renderBothTables();
|
||||||
updatePaginationControls();
|
updateBothPaginationControls();
|
||||||
});
|
});
|
||||||
container.appendChild(button);
|
container.appendChild(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addModalPageButton(pageNumber, container) {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.className = 'pagination-page-btn';
|
||||||
|
|
||||||
|
if (pageNumber === modalCurrentPage) {
|
||||||
|
button.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
button.textContent = pageNumber;
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
modalCurrentPage = pageNumber;
|
||||||
|
renderConsolidateOrders(availableOrders);
|
||||||
|
updateModalPaginationControls();
|
||||||
|
});
|
||||||
|
container.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBothTables() {
|
||||||
|
renderPaymentTable(entries);
|
||||||
|
renderOrderTable(entries);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Create Order Modal Functions ---------- */
|
/* ---------- Create Order Modal Functions ---------- */
|
||||||
function openCreateOrderModal(){
|
function openCreateOrderModal(){
|
||||||
const modal = document.getElementById('createOrderModal');
|
const modal = document.getElementById('createOrderModal');
|
||||||
modal.classList.add('modal-open');
|
modal.classList.add('modal-open');
|
||||||
loadAvailableOrders();
|
loadAvailableOrders();
|
||||||
// focus first input
|
|
||||||
setTimeout(()=> document.getElementById('inline_description').focus(), 220);
|
setTimeout(()=> document.getElementById('inline_description').focus(), 220);
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeCreateOrderModal(){
|
function closeCreateOrderModal(){
|
||||||
const modal = document.getElementById('createOrderModal');
|
const modal = document.getElementById('createOrderModal');
|
||||||
modal.classList.remove('modal-open');
|
modal.classList.remove('modal-open');
|
||||||
// reset form and pagination
|
|
||||||
document.getElementById('createOrderInlineForm').reset();
|
document.getElementById('createOrderInlineForm').reset();
|
||||||
currentPage = 1;
|
modalCurrentPage = 1; // Reset modal pagination when closing
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Loaders ---------- */
|
/* ---------- Loaders ---------- */
|
||||||
function loadDashboard(){
|
function loadDashboard(){
|
||||||
// show loading placeholders
|
|
||||||
document.getElementById('paymentTableBody').innerHTML = '<tr><td colspan="8" class="empty-state">Loading entries...</td></tr>';
|
|
||||||
document.getElementById('orderTableBody').innerHTML = '<tr><td colspan="8" class="empty-state">Loading entries...</td></tr>';
|
|
||||||
jsonFetch('/admin/account/dashboard')
|
jsonFetch('/admin/account/dashboard')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if(!res.success) throw new Error(res.message || 'Failed to load dashboard');
|
if(!res.success) throw new Error(res.message || 'Failed to load dashboard');
|
||||||
entries = res.entries || [];
|
entries = res.entries || [];
|
||||||
renderPaymentTable(entries);
|
renderBothTables();
|
||||||
renderOrderTable(entries);
|
|
||||||
document.getElementById('entriesCount').textContent = entries.length;
|
document.getElementById('entriesCount').textContent = entries.length;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
@@ -967,14 +1124,14 @@ function loadAvailableOrders(){
|
|||||||
.then(res => {
|
.then(res => {
|
||||||
if(!res.success) throw new Error(res.message || 'Failed to load orders');
|
if(!res.success) throw new Error(res.message || 'Failed to load orders');
|
||||||
availableOrders = res.orders || [];
|
availableOrders = res.orders || [];
|
||||||
currentPage = 1; // Reset to first page when loading new orders
|
modalCurrentPage = 1; // Reset to first page when loading new orders
|
||||||
renderConsolidateOrders(availableOrders);
|
renderConsolidateOrders(availableOrders);
|
||||||
updatePaginationControls();
|
updateModalPaginationControls();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
tbody.innerHTML = '<tr><td colspan="14" class="empty-state">Unable to load orders</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="14" class="empty-state">Unable to load orders</td></tr>';
|
||||||
updatePaginationControls();
|
updateModalPaginationControls();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -982,11 +1139,18 @@ function loadAvailableOrders(){
|
|||||||
function renderPaymentTable(list){
|
function renderPaymentTable(list){
|
||||||
const body = document.getElementById('paymentTableBody');
|
const body = document.getElementById('paymentTableBody');
|
||||||
body.innerHTML = '';
|
body.innerHTML = '';
|
||||||
|
|
||||||
if(!list || list.length === 0){
|
if(!list || list.length === 0){
|
||||||
body.innerHTML = '<tr><td colspan="8" class="empty-state">No entries found</td></tr>';
|
body.innerHTML = '<tr><td colspan="8" class="empty-state">No entries found</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.forEach(entry => {
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * entriesPerPage;
|
||||||
|
const endIndex = startIndex + entriesPerPage;
|
||||||
|
const paginatedEntries = list.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
paginatedEntries.forEach(entry => {
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
tr.innerHTML = `
|
tr.innerHTML = `
|
||||||
<td>${escapeHtml(entry.entry_no)}</td>
|
<td>${escapeHtml(entry.entry_no)}</td>
|
||||||
@@ -1011,11 +1175,18 @@ function renderPaymentTable(list){
|
|||||||
function renderOrderTable(list){
|
function renderOrderTable(list){
|
||||||
const body = document.getElementById('orderTableBody');
|
const body = document.getElementById('orderTableBody');
|
||||||
body.innerHTML = '';
|
body.innerHTML = '';
|
||||||
|
|
||||||
if(!list || list.length === 0){
|
if(!list || list.length === 0){
|
||||||
body.innerHTML = '<tr><td colspan="8" class="empty-state">No entries found</td></tr>';
|
body.innerHTML = '<tr><td colspan="8" class="empty-state">No entries found</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.forEach(entry => {
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * entriesPerPage;
|
||||||
|
const endIndex = startIndex + entriesPerPage;
|
||||||
|
const paginatedEntries = list.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
paginatedEntries.forEach(entry => {
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
const pending = Number(entry.pending_amount || 0);
|
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>`;
|
const pendingHtml = pending <= 0 ? '<span class="status-badge pending-badge-green">Completed</span>' : `<span class="status-badge pending-badge-red">${formatCurrency(pending)}</span>`;
|
||||||
@@ -1047,12 +1218,13 @@ function renderConsolidateOrders(list){
|
|||||||
|
|
||||||
if(!list || list.length === 0){
|
if(!list || list.length === 0){
|
||||||
body.innerHTML = '<tr><td colspan="14" class="empty-state">No available orders</td></tr>';
|
body.innerHTML = '<tr><td colspan="14" class="empty-state">No available orders</td></tr>';
|
||||||
|
updateModalPaginationControls();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate pagination
|
// Calculate pagination for modal
|
||||||
const startIndex = (currentPage - 1) * ordersPerPage;
|
const startIndex = (modalCurrentPage - 1) * modalOrdersPerPage;
|
||||||
const endIndex = startIndex + ordersPerPage;
|
const endIndex = startIndex + modalOrdersPerPage;
|
||||||
const paginatedOrders = list.slice(startIndex, endIndex);
|
const paginatedOrders = list.slice(startIndex, endIndex);
|
||||||
|
|
||||||
paginatedOrders.forEach(order => {
|
paginatedOrders.forEach(order => {
|
||||||
@@ -1156,14 +1328,19 @@ async function submitCreateOrderInline(e){
|
|||||||
/* ---------- Search ---------- */
|
/* ---------- Search ---------- */
|
||||||
function handleSearch(){
|
function handleSearch(){
|
||||||
const q = document.getElementById('main-search').value.trim().toLowerCase();
|
const q = document.getElementById('main-search').value.trim().toLowerCase();
|
||||||
if(!q){ renderPaymentTable(entries); renderOrderTable(entries); return; }
|
if(!q){
|
||||||
|
renderBothTables();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const filtered = entries.filter(e => {
|
const filtered = entries.filter(e => {
|
||||||
return String(e.entry_no || '').toLowerCase().includes(q) ||
|
return String(e.entry_no || '').toLowerCase().includes(q) ||
|
||||||
String(e.description || '').toLowerCase().includes(q) ||
|
String(e.description || '').toLowerCase().includes(q) ||
|
||||||
String(e.region || '').toLowerCase().includes(q);
|
String(e.region || '').toLowerCase().includes(q);
|
||||||
});
|
});
|
||||||
renderPaymentTable(filtered);
|
entries = filtered;
|
||||||
renderOrderTable(filtered);
|
currentPage = 1; // Reset to first page when searching
|
||||||
|
renderBothTables();
|
||||||
|
updateBothPaginationControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Entry details & installments ---------- */
|
/* ---------- Entry details & installments ---------- */
|
||||||
@@ -1289,10 +1466,6 @@ async function submitInstallment(e){
|
|||||||
if(btn){ btn.disabled = false; btn.textContent = 'Create Installment'; }
|
if(btn){ btn.disabled = false; btn.textContent = 'Create Installment'; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Utilities ---------- */
|
|
||||||
// ensure consolidate area visible by default
|
|
||||||
document.addEventListener('DOMContentLoaded', () => { document.getElementById('consolidateArea').style.display = 'block'; });
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -288,7 +288,140 @@
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Pagination Styles (Same as Account Dashboard) ---------- */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image-based pagination buttons */
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.search-filter-container {
|
.search-filter-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -303,6 +436,16 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -315,7 +458,7 @@
|
|||||||
<!-- Stats Cards with REAL DATA -->
|
<!-- Stats Cards with REAL DATA -->
|
||||||
<div class="stats-row">
|
<div class="stats-row">
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div class="stats-count">{{ $customers->count() }}</div>
|
<div class="stats-count">{{ $allCustomers->count() }}</div>
|
||||||
<div class="stats-label">Total Customers</div>
|
<div class="stats-label">Total Customers</div>
|
||||||
<i class="bi bi-people-fill stats-icon"></i>
|
<i class="bi bi-people-fill stats-icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -323,7 +466,7 @@
|
|||||||
<div class="stats-card" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
|
<div class="stats-card" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
|
||||||
<div class="stats-count">
|
<div class="stats-count">
|
||||||
@php
|
@php
|
||||||
$newThisMonth = $customers->filter(function($customer) {
|
$newThisMonth = $allCustomers->filter(function($customer) {
|
||||||
return $customer->created_at->format('Y-m') === now()->format('Y-m');
|
return $customer->created_at->format('Y-m') === now()->format('Y-m');
|
||||||
})->count();
|
})->count();
|
||||||
@endphp
|
@endphp
|
||||||
@@ -336,7 +479,7 @@
|
|||||||
<div class="stats-card" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
|
<div class="stats-card" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
|
||||||
<div class="stats-count">
|
<div class="stats-count">
|
||||||
@php
|
@php
|
||||||
$activeCustomers = $customers->where('status', 'active')->count();
|
$activeCustomers = $allCustomers->where('status', 'active')->count();
|
||||||
@endphp
|
@endphp
|
||||||
{{ $activeCustomers }}
|
{{ $activeCustomers }}
|
||||||
</div>
|
</div>
|
||||||
@@ -347,7 +490,7 @@
|
|||||||
<div class="stats-card" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
|
<div class="stats-card" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
|
||||||
<div class="stats-count">
|
<div class="stats-count">
|
||||||
@php
|
@php
|
||||||
$premiumCount = $customers->where('customer_type', 'premium')->count();
|
$premiumCount = $allCustomers->where('customer_type', 'premium')->count();
|
||||||
@endphp
|
@endphp
|
||||||
{{ $premiumCount }}
|
{{ $premiumCount }}
|
||||||
</div>
|
</div>
|
||||||
@@ -413,7 +556,7 @@
|
|||||||
<th class="table-header" width="120">Actions</th>
|
<th class="table-header" width="120">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="customersTableBody">
|
||||||
@forelse($customers as $c)
|
@forelse($customers as $c)
|
||||||
<tr>
|
<tr>
|
||||||
<!-- Customer Info Column -->
|
<!-- Customer Info Column -->
|
||||||
@@ -489,6 +632,64 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="pageInfo">
|
||||||
|
Showing {{ $customers->firstItem() ?? 0 }} to {{ $customers->lastItem() ?? 0 }} of {{ $customers->total() }} entries
|
||||||
|
</div>
|
||||||
|
<div class="pagination-controls">
|
||||||
|
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page" {{ $customers->onFirstPage() ? '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">
|
||||||
|
@for ($i = 1; $i <= $customers->lastPage(); $i++)
|
||||||
|
<a href="{{ $customers->url($i) }}"
|
||||||
|
class="pagination-page-btn {{ $customers->currentPage() == $i ? 'active' : '' }}">
|
||||||
|
{{ $i }}
|
||||||
|
</a>
|
||||||
|
@endfor
|
||||||
|
</div>
|
||||||
|
<button class="pagination-img-btn" id="nextPageBtn" title="Next page" {{ $customers->hasMorePages() ? '' : '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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Add hover effects to table rows
|
||||||
|
const tableRows = document.querySelectorAll('.table tbody tr');
|
||||||
|
tableRows.forEach(row => {
|
||||||
|
row.addEventListener('mouseenter', function() {
|
||||||
|
this.style.transform = 'translateX(5px)';
|
||||||
|
});
|
||||||
|
|
||||||
|
row.addEventListener('mouseleave', function() {
|
||||||
|
this.style.transform = 'translateX(0)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pagination button handlers
|
||||||
|
document.getElementById('prevPageBtn').addEventListener('click', function() {
|
||||||
|
@if(!$customers->onFirstPage())
|
||||||
|
window.location.href = '{{ $customers->previousPageUrl() }}';
|
||||||
|
@endif
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('nextPageBtn').addEventListener('click', function() {
|
||||||
|
@if($customers->hasMorePages())
|
||||||
|
window.location.href = '{{ $customers->nextPageUrl() }}';
|
||||||
|
@endif
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -556,6 +556,138 @@ body, .container-fluid {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== PAGINATION STYLES ===== */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== RESPONSIVE BREAKPOINTS ===== */
|
/* ===== RESPONSIVE BREAKPOINTS ===== */
|
||||||
|
|
||||||
/* Laptop (1200px and below) */
|
/* Laptop (1200px and below) */
|
||||||
@@ -598,6 +730,16 @@ body, .container-fluid {
|
|||||||
.table td {
|
.table td {
|
||||||
padding: 10px 5px;
|
padding: 10px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile Landscape (768px and below) */
|
/* Mobile Landscape (768px and below) */
|
||||||
@@ -900,6 +1042,9 @@ body, .container-fluid {
|
|||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-header bg-light">
|
<div class="card-header bg-light">
|
||||||
<strong>Recent Orders</strong>
|
<strong>Recent Orders</strong>
|
||||||
|
<span style="font-size:13px;color:#6577a3;margin-left:10px;">
|
||||||
|
Total orders: <span id="ordersCount">{{ $orders->count() }}</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body table-responsive">
|
<div class="card-body table-responsive">
|
||||||
<table class="table table-striped table-bordered align-middle text-center">
|
<table class="table table-striped table-bordered align-middle text-center">
|
||||||
@@ -923,7 +1068,7 @@ body, .container-fluid {
|
|||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="ordersTableBody">
|
||||||
@forelse($orders as $order)
|
@forelse($orders as $order)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $order->id }}</td>
|
<td>{{ $order->id }}</td>
|
||||||
@@ -963,6 +1108,28 @@ body, .container-fluid {
|
|||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="pageInfo">Showing 1 to 10 of {{ $orders->count() }} entries</div>
|
||||||
|
<div class="pagination-controls">
|
||||||
|
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page">
|
||||||
|
<!-- 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">
|
||||||
|
<!-- 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1167,6 +1334,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const closeBtn = document.getElementById('closeCreateOrderModal');
|
const closeBtn = document.getElementById('closeCreateOrderModal');
|
||||||
const clearFormBtn = document.getElementById('clearForm');
|
const clearFormBtn = document.getElementById('clearForm');
|
||||||
|
|
||||||
|
// Pagination state
|
||||||
|
let currentPage = 1;
|
||||||
|
const ordersPerPage = 10;
|
||||||
|
let allOrders = @json($orders->values());
|
||||||
|
|
||||||
|
// Initialize pagination
|
||||||
|
initializePagination();
|
||||||
|
|
||||||
// Reset temp data function
|
// Reset temp data function
|
||||||
const resetTempData = () => {
|
const resetTempData = () => {
|
||||||
fetch('{{ route("admin.orders.temp.reset") }}', {
|
fetch('{{ route("admin.orders.temp.reset") }}', {
|
||||||
@@ -1265,6 +1440,168 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ---------- Pagination Functions ---------- */
|
||||||
|
function initializePagination() {
|
||||||
|
renderOrdersTable(allOrders);
|
||||||
|
updatePaginationControls();
|
||||||
|
|
||||||
|
// Bind pagination buttons
|
||||||
|
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
||||||
|
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToPreviousPage() {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
renderOrdersTable(allOrders);
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToNextPage() {
|
||||||
|
const totalPages = Math.ceil(allOrders.length / ordersPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++;
|
||||||
|
renderOrdersTable(allOrders);
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls() {
|
||||||
|
const totalPages = Math.ceil(allOrders.length / ordersPerPage);
|
||||||
|
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) * ordersPerPage + 1;
|
||||||
|
const endIndex = Math.min(currentPage * ordersPerPage, allOrders.length);
|
||||||
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${allOrders.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;
|
||||||
|
renderOrdersTable(allOrders);
|
||||||
|
updatePaginationControls();
|
||||||
|
});
|
||||||
|
container.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOrdersTable(orders) {
|
||||||
|
const tbody = document.getElementById('ordersTableBody');
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
|
if (!orders || orders.length === 0) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="16" class="text-muted">No orders found</td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * ordersPerPage;
|
||||||
|
const endIndex = startIndex + ordersPerPage;
|
||||||
|
const paginatedOrders = orders.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
paginatedOrders.forEach(order => {
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
tr.innerHTML = `
|
||||||
|
<td>${order.id}</td>
|
||||||
|
<td>
|
||||||
|
<a href="javascript:void(0)"
|
||||||
|
class="fw-semibold text-primary open-order-modal"
|
||||||
|
data-id="${order.id}">
|
||||||
|
${order.order_id}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>${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 ? Number(order.ttl_amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : '0.00'}</td>
|
||||||
|
<td>${order.cbm || ''}</td>
|
||||||
|
<td>${order.ttl_cbm || ''}</td>
|
||||||
|
<td>${order.kg || ''}</td>
|
||||||
|
<td>${order.ttl_kg || ''}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-info text-dark">${order.status ? order.status.charAt(0).toUpperCase() + order.status.slice(1) : ''}</span>
|
||||||
|
</td>
|
||||||
|
<td>${new Date(order.created_at).toLocaleDateString('en-GB')}</td>
|
||||||
|
<td>
|
||||||
|
<a href="/admin/orders/${order.id}" class="btn btn-sm btn-outline-primary">
|
||||||
|
<i class="bi bi-eye"></i> View
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
|
||||||
|
// Re-bind order details modal for newly rendered rows
|
||||||
|
const orderLink = tr.querySelector('.open-order-modal');
|
||||||
|
if (orderLink) {
|
||||||
|
orderLink.addEventListener('click', function() {
|
||||||
|
let id = this.dataset.id;
|
||||||
|
let modal = new bootstrap.Modal(document.getElementById('orderDetailsModal'));
|
||||||
|
|
||||||
|
document.getElementById('orderDetailsBody').innerHTML =
|
||||||
|
"<p class='text-center text-muted'>Loading...</p>";
|
||||||
|
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
fetch(`/admin/orders/view/${id}`)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('orderDetailsBody').innerHTML = html;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
document.getElementById('orderDetailsBody').innerHTML =
|
||||||
|
"<p class='text-danger text-center'>Failed to load order details.</p>";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -737,6 +737,139 @@
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Pagination Styles (Same as Account Dashboard) ---------- */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image-based pagination buttons */
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.invoice-tools-row {
|
.invoice-tools-row {
|
||||||
@@ -835,6 +968,16 @@
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
@@ -944,7 +1087,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody id="invoicesTableBody">
|
||||||
@php
|
@php
|
||||||
$totalInvoices = $invoices->count();
|
$totalInvoices = $invoices->count();
|
||||||
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
||||||
@@ -1006,7 +1149,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ALL INVOICES - Mobile Cards -->
|
<!-- ALL INVOICES - Mobile Cards -->
|
||||||
<div class="mobile-invoices-container">
|
<div class="mobile-invoices-container" id="mobileInvoicesContainer">
|
||||||
@php
|
@php
|
||||||
$totalInvoices = $invoices->count();
|
$totalInvoices = $invoices->count();
|
||||||
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
||||||
@@ -1073,6 +1216,28 @@
|
|||||||
<div class="text-muted text-center py-4">No invoices found</div>
|
<div class="text-muted text-center py-4">No invoices found</div>
|
||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1095,6 +1260,20 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
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
|
// Invoice popup functionality
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
if (e.target.closest('.open-invoice-popup')) {
|
if (e.target.closest('.open-invoice-popup')) {
|
||||||
@@ -1130,13 +1309,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const statusFilter = document.getElementById('statusFilter');
|
const statusFilter = document.getElementById('statusFilter');
|
||||||
const startDateInput = document.getElementById('startDate');
|
const startDateInput = document.getElementById('startDate');
|
||||||
const endDateInput = document.getElementById('endDate');
|
const endDateInput = document.getElementById('endDate');
|
||||||
|
|
||||||
// Desktop table elements
|
|
||||||
const table = document.getElementById('invoicesTable');
|
|
||||||
const tableRows = table ? table.getElementsByTagName('tbody')[0].getElementsByTagName('tr') : [];
|
|
||||||
|
|
||||||
// Mobile card elements
|
|
||||||
const mobileCards = document.querySelectorAll('.mobile-invoice-card');
|
|
||||||
|
|
||||||
function filterInvoices() {
|
function filterInvoices() {
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
const searchTerm = searchInput.value.toLowerCase();
|
||||||
@@ -1144,56 +1316,38 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const startDate = startDateInput.value;
|
const startDate = startDateInput.value;
|
||||||
const endDate = endDateInput.value;
|
const endDate = endDateInput.value;
|
||||||
|
|
||||||
// Filter desktop table rows
|
filteredInvoices = allInvoices.filter(invoice => {
|
||||||
for (let row of tableRows) {
|
let include = true;
|
||||||
const cells = row.getElementsByTagName('td');
|
|
||||||
if (cells.length < 9) continue;
|
// Search filter
|
||||||
|
if (searchTerm) {
|
||||||
const invoiceNumber = cells[1].textContent.toLowerCase();
|
const matchesSearch =
|
||||||
const customerName = cells[2].textContent.toLowerCase();
|
invoice.invoice_number.toLowerCase().includes(searchTerm) ||
|
||||||
const status = cells[6].textContent.toLowerCase();
|
invoice.customer_name.toLowerCase().includes(searchTerm);
|
||||||
const invoiceDate = cells[7].textContent;
|
if (!matchesSearch) include = false;
|
||||||
|
}
|
||||||
const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm);
|
|
||||||
const matchesStatus = !statusValue || status.includes(statusValue);
|
// Status filter
|
||||||
|
if (statusValue && invoice.status !== statusValue) {
|
||||||
// Date filtering
|
include = false;
|
||||||
let matchesDate = true;
|
}
|
||||||
|
|
||||||
|
// Date filter
|
||||||
if (startDate || endDate) {
|
if (startDate || endDate) {
|
||||||
const cellDate = new Date(invoiceDate);
|
const invoiceDate = new Date(invoice.invoice_date);
|
||||||
const start = startDate ? new Date(startDate) : null;
|
const start = startDate ? new Date(startDate) : null;
|
||||||
const end = endDate ? new Date(endDate) : null;
|
const end = endDate ? new Date(endDate) : null;
|
||||||
|
|
||||||
if (start && cellDate < start) matchesDate = false;
|
if (start && invoiceDate < start) include = false;
|
||||||
if (end && cellDate > end) matchesDate = false;
|
if (end && invoiceDate > end) include = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
row.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none';
|
return include;
|
||||||
}
|
});
|
||||||
|
|
||||||
// Filter mobile cards
|
currentPage = 1;
|
||||||
for (let card of mobileCards) {
|
renderTable();
|
||||||
const invoiceNumber = card.querySelector('.mobile-invoice-number-text').textContent.toLowerCase();
|
updatePaginationControls();
|
||||||
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
|
// Add event listeners for filtering
|
||||||
@@ -1202,12 +1356,214 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
startDateInput.addEventListener('change', filterInvoices);
|
startDateInput.addEventListener('change', filterInvoices);
|
||||||
endDateInput.addEventListener('change', filterInvoices);
|
endDateInput.addEventListener('change', filterInvoices);
|
||||||
|
|
||||||
// Add hover effects to table rows
|
// Pagination Functions
|
||||||
for (let row of tableRows) {
|
function goToPreviousPage() {
|
||||||
row.addEventListener('mouseenter', function() {
|
if (currentPage > 1) {
|
||||||
this.style.cursor = 'pointer';
|
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) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="10" 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>
|
||||||
|
<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>
|
||||||
|
<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>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
|
|
||||||
<form method="POST" action="{{ route('admin.logout') }}" class="mt-4 px-3">
|
<form method="POST" action="{{ route('admin.logout') }}" class="mt-4 px-3">
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="btn btn-danger w-100"><i class="bi bi-box-arrow-right"></i> Logout</button>
|
<!-- <button type="submit" class="btn btn-danger w-100"><i class="bi bi-box-arrow-right"></i> Logout</button>-->
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,139 @@
|
|||||||
from { box-shadow: 0 0 0px #b4a02455 inset; }
|
from { box-shadow: 0 0 0px #b4a02455 inset; }
|
||||||
to { box-shadow: 0 0 10px #b4a024aa inset; }
|
to { box-shadow: 0 0 10px #b4a024aa inset; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Pagination Styles ---------- */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image-based pagination buttons */
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if(session('success'))
|
@if(session('success'))
|
||||||
@@ -304,63 +437,207 @@
|
|||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="markListTableBody">
|
||||||
@foreach($markList as $mark)
|
<!-- Data will be loaded dynamically -->
|
||||||
<tr>
|
|
||||||
<td>{{ $mark->id }}</td>
|
|
||||||
<td>{{ $mark->mark_no }}</td>
|
|
||||||
<td>{{ $mark->origin }}</td>
|
|
||||||
<td>{{ $mark->destination }}</td>
|
|
||||||
<td>{{ $mark->customer_name }}</td>
|
|
||||||
<td>{{ $mark->customer_id }}</td>
|
|
||||||
<td>{{ $mark->mobile_no }}</td>
|
|
||||||
<td>{{ \Carbon\Carbon::parse($mark->date)->format('d-m-Y') }}</td>
|
|
||||||
<td>
|
|
||||||
@if($mark->status == 'active')
|
|
||||||
<span class="badge badge-active">
|
|
||||||
<i class="bi bi-check-circle-fill status-icon"></i>
|
|
||||||
Active
|
|
||||||
</span>
|
|
||||||
@else
|
|
||||||
<span class="badge badge-inactive">
|
|
||||||
<i class="bi bi-exclamation-triangle-fill status-icon"></i>
|
|
||||||
In-Active
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@if($mark->status == 'active')
|
|
||||||
<a href="{{ route('admin.marklist.toggle', $mark->id) }}" class="btn-action btn-action-deactivate">
|
|
||||||
<i class="bi bi-power btn-icon"></i>
|
|
||||||
Deactivate
|
|
||||||
</a>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('admin.marklist.toggle', $mark->id) }}" class="btn-action btn-action-activate">
|
|
||||||
<i class="bi bi-play-circle btn-icon"></i>
|
|
||||||
Activate
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@if($markList->isEmpty())
|
<!-- Pagination Controls -->
|
||||||
<div class="py-4 text-center text-muted fst-italic">No mark numbers found.</div>
|
<div class="pagination-container">
|
||||||
@endif
|
<div class="pagination-info" id="pageInfo">Showing 0 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 id="noResults" class="py-4 text-center text-muted fst-italic" style="display: none;">No mark numbers found.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('globalSearch').addEventListener('input', function () {
|
// Pagination state
|
||||||
const filter = this.value.toLowerCase();
|
let currentPage = 1;
|
||||||
const rows = document.querySelectorAll('#markListTable tbody tr');
|
const itemsPerPage = 10;
|
||||||
|
let allMarkList = @json($markList);
|
||||||
|
let filteredMarkList = [...allMarkList];
|
||||||
|
|
||||||
rows.forEach(row => {
|
// Initialize pagination
|
||||||
const text = row.textContent.toLowerCase();
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
row.style.display = text.includes(filter) ? '' : 'none';
|
renderTable();
|
||||||
});
|
updatePaginationControls();
|
||||||
|
|
||||||
|
// Bind pagination events
|
||||||
|
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
||||||
|
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
||||||
|
|
||||||
|
// Bind search event
|
||||||
|
document.getElementById('globalSearch').addEventListener('input', handleSearch);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pagination Functions
|
||||||
|
function goToPreviousPage() {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToNextPage() {
|
||||||
|
const totalPages = Math.ceil(filteredMarkList.length / itemsPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls() {
|
||||||
|
const totalPages = Math.ceil(filteredMarkList.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, filteredMarkList.length);
|
||||||
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${filteredMarkList.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search Function
|
||||||
|
function handleSearch() {
|
||||||
|
const filter = this.value.toLowerCase();
|
||||||
|
filteredMarkList = allMarkList.filter(mark => {
|
||||||
|
return Object.values(mark).some(value =>
|
||||||
|
String(value).toLowerCase().includes(filter)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
currentPage = 1;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render Table Function - FIXED: Using direct URL construction
|
||||||
|
function renderTable() {
|
||||||
|
const tbody = document.getElementById('markListTableBody');
|
||||||
|
const noResults = document.getElementById('noResults');
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
|
if (filteredMarkList.length === 0) {
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
noResults.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
noResults.style.display = 'none';
|
||||||
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const paginatedItems = filteredMarkList.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
paginatedItems.forEach(mark => {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${mark.id}</td>
|
||||||
|
<td>${mark.mark_no}</td>
|
||||||
|
<td>${mark.origin}</td>
|
||||||
|
<td>${mark.destination}</td>
|
||||||
|
<td>${mark.customer_name}</td>
|
||||||
|
<td>${mark.customer_id}</td>
|
||||||
|
<td>${mark.mobile_no}</td>
|
||||||
|
<td>${new Date(mark.date).toLocaleDateString('en-GB')}</td>
|
||||||
|
<td>
|
||||||
|
${mark.status == 'active'
|
||||||
|
? `<span class="badge badge-active">
|
||||||
|
<i class="bi bi-check-circle-fill status-icon"></i>
|
||||||
|
Active
|
||||||
|
</span>`
|
||||||
|
: `<span class="badge badge-inactive">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill status-icon"></i>
|
||||||
|
In-Active
|
||||||
|
</span>`
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
${mark.status == 'active'
|
||||||
|
? `<a href="/admin/mark-list/status/${mark.id}" class="btn-action btn-action-deactivate">
|
||||||
|
<i class="bi bi-power btn-icon"></i>
|
||||||
|
Deactivate
|
||||||
|
</a>`
|
||||||
|
: `<a href="/admin/mark-list/status/${mark.id}" class="btn-action btn-action-activate">
|
||||||
|
<i class="bi bi-play-circle btn-icon"></i>
|
||||||
|
Activate
|
||||||
|
</a>`
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -146,6 +146,139 @@ html, body {
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Pagination Styles (Same as Account Dashboard) ---------- */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image-based pagination buttons */
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{{-- Make sure you include Font Awesome CDN in your main layout file, e.g., in <head>:
|
{{-- Make sure you include Font Awesome CDN in your main layout file, e.g., in <head>:
|
||||||
@@ -179,9 +312,8 @@ html, body {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody id="ordersTableBody">
|
||||||
@foreach($orders as $order)
|
@foreach($orders as $order)
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$mark = $order->markList ?? null;
|
$mark = $order->markList ?? null;
|
||||||
$invoice = $order->invoice ?? null;
|
$invoice = $order->invoice ?? null;
|
||||||
@@ -239,11 +371,32 @@ html, body {
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@endforeach
|
@endforeach
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div> {{-- End table-wrapper --}}
|
</div> {{-- End table-wrapper --}}
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="pageInfo">Showing 1 to {{ min(10, count($orders)) }} of {{ count($orders) }} 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" {{ count($orders) > 10 ? '' : '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>
|
||||||
@else
|
@else
|
||||||
<p class="no-data">
|
<p class="no-data">
|
||||||
<i class="fas fa-info-circle mr-2"></i> No orders found.
|
<i class="fas fa-info-circle mr-2"></i> No orders found.
|
||||||
@@ -251,4 +404,158 @@ html, body {
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Pagination state
|
||||||
|
let currentPage = 1;
|
||||||
|
const itemsPerPage = 10;
|
||||||
|
let allOrders = @json($orders);
|
||||||
|
let filteredOrders = [...allOrders];
|
||||||
|
|
||||||
|
// Initialize pagination
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
|
||||||
|
// Bind pagination events
|
||||||
|
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
||||||
|
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pagination Functions
|
||||||
|
function goToPreviousPage() {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToNextPage() {
|
||||||
|
const totalPages = Math.ceil(filteredOrders.length / itemsPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls() {
|
||||||
|
const totalPages = Math.ceil(filteredOrders.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, filteredOrders.length);
|
||||||
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${filteredOrders.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('ordersTableBody');
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
|
if (filteredOrders.length === 0) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="14" class="text-center py-4 text-muted">No orders found.</td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const paginatedItems = filteredOrders.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
paginatedItems.forEach(order => {
|
||||||
|
const mark = order.markList || null;
|
||||||
|
const invoice = order.invoice || null;
|
||||||
|
const shipment = order.shipments?.[0] || null;
|
||||||
|
const invoiceStatus = (invoice?.status || '').toLowerCase();
|
||||||
|
const shipmentStatus = (shipment?.status || '').toLowerCase().replace('_', ' ');
|
||||||
|
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${order.order_id || '-'}</td>
|
||||||
|
<td>${shipment?.shipment_id || '-'}</td>
|
||||||
|
<td>${mark?.customer_id || '-'}</td>
|
||||||
|
<td>${mark?.company_name || '-'}</td>
|
||||||
|
<td>${mark?.origin || order.origin || '-'}</td>
|
||||||
|
<td>${mark?.destination || order.destination || '-'}</td>
|
||||||
|
<td>${order.created_at ? new Date(order.created_at).toLocaleDateString('en-GB') : '-'}</td>
|
||||||
|
<td>${invoice?.invoice_number || '-'}</td>
|
||||||
|
<td>${invoice?.invoice_date ? new Date(invoice.invoice_date).toLocaleDateString('en-GB') : '-'}</td>
|
||||||
|
<td>${invoice?.final_amount ? '₹' + Number(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '-'}</td>
|
||||||
|
<td>${invoice?.final_amount_with_gst ? '₹' + Number(invoice.final_amount_with_gst).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '-'}</td>
|
||||||
|
<td>
|
||||||
|
${invoice?.status
|
||||||
|
? `<span class="status-badge status-${invoiceStatus}">${invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1)}</span>`
|
||||||
|
: '<span class="status-badge status-pending">Pending</span>'
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
${shipment?.status
|
||||||
|
? `<span class="status-badge status-${shipmentStatus.replace(' ', '_')}">${shipment.status.charAt(0).toUpperCase() + shipment.status.slice(1)}</span>`
|
||||||
|
: '<span class="shipstatus">-</span>'
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="/admin/orders/${order.id}" title="View Details">
|
||||||
|
<i class="fas fa-eye action-btn"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -541,6 +541,139 @@
|
|||||||
background: #a8a8a8;
|
background: #a8a8a8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Pagination Styles (Same as Account Dashboard) ---------- */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image-based pagination buttons */
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.stats-container {
|
.stats-container {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
@@ -604,6 +737,16 @@
|
|||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
@@ -833,12 +976,42 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="pageInfo">Showing 1 to {{ min(10, count($reports)) }} of {{ count($reports) }} 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" {{ count($reports) > 10 ? '' : '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>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Pagination state
|
||||||
|
let currentPage = 1;
|
||||||
|
const itemsPerPage = 10;
|
||||||
|
let allReports = @json($reports);
|
||||||
|
let filteredReports = [...allReports];
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Initialize statistics
|
// Initialize statistics and pagination
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
|
||||||
// Set default dates for demo purposes
|
// Set default dates for demo purposes
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@@ -857,6 +1030,10 @@
|
|||||||
document.getElementById('companyFilter').addEventListener('change', filterTable);
|
document.getElementById('companyFilter').addEventListener('change', filterTable);
|
||||||
document.getElementById('statusFilter').addEventListener('change', filterTable);
|
document.getElementById('statusFilter').addEventListener('change', filterTable);
|
||||||
|
|
||||||
|
// Bind pagination events
|
||||||
|
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
||||||
|
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
||||||
|
|
||||||
function updateStatistics() {
|
function updateStatistics() {
|
||||||
const rows = document.querySelectorAll('#reportTableBody tr');
|
const rows = document.querySelectorAll('#reportTableBody tr');
|
||||||
let totalShipments = 0;
|
let totalShipments = 0;
|
||||||
@@ -893,62 +1070,218 @@
|
|||||||
document.getElementById('overdueInvoices').textContent = overdueInvoices;
|
document.getElementById('overdueInvoices').textContent = overdueInvoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pagination Functions
|
||||||
|
function goToPreviousPage() {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToNextPage() {
|
||||||
|
const totalPages = Math.ceil(filteredReports.length / itemsPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls() {
|
||||||
|
const totalPages = Math.ceil(filteredReports.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, filteredReports.length);
|
||||||
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${filteredReports.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('reportTableBody');
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
|
if (filteredReports.length === 0) {
|
||||||
|
tbody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="12">
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="fas fa-inbox"></i>
|
||||||
|
</div>
|
||||||
|
<h3>No Shipping Reports Found</h3>
|
||||||
|
<p>There are no shipping reports matching your current filters</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const paginatedItems = filteredReports.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
paginatedItems.forEach(report => {
|
||||||
|
const ist = (report.invoice_status || '').toLowerCase();
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.setAttribute('data-status', report.shipment_status);
|
||||||
|
row.setAttribute('data-invoice-status', ist);
|
||||||
|
row.setAttribute('data-company', report.company_name || '');
|
||||||
|
row.setAttribute('data-date', report.shipment_date);
|
||||||
|
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>
|
||||||
|
<span class="data-highlight" title="${report.order_id}">
|
||||||
|
<i class="fas fa-hashtag"></i>
|
||||||
|
${report.order_id}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="data-highlight" title="${report.shipment_id}">
|
||||||
|
<i class="fas fa-barcode"></i>
|
||||||
|
${report.shipment_id}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td title="${report.company_name || '-'}">${report.company_name || '-'}</td>
|
||||||
|
<td title="${report.customer_name || '-'}">${report.customer_name || '-'}</td>
|
||||||
|
<td>
|
||||||
|
<span class="data-highlight" title="${report.origin}">
|
||||||
|
<i class="fas fa-map-marker-alt"></i>
|
||||||
|
${report.origin}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="data-highlight" title="${report.destination}">
|
||||||
|
<i class="fas fa-location-arrow"></i>
|
||||||
|
${report.destination}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>${new Date(report.shipment_date).toLocaleDateString('en-GB')}</td>
|
||||||
|
<td>
|
||||||
|
<span class="data-highlight" title="${report.invoice_number}">
|
||||||
|
<i class="fas fa-file-invoice"></i>
|
||||||
|
${report.invoice_number}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>${new Date(report.invoice_date).toLocaleDateString('en-GB')}</td>
|
||||||
|
<td>
|
||||||
|
<span class="data-highlight" title="₹${Number(report.final_amount).toLocaleString('en-IN')}">
|
||||||
|
<i class="fas fa-rupee-sign"></i>
|
||||||
|
${Number(report.final_amount).toLocaleString('en-IN')}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="status-badge
|
||||||
|
${ist === 'paid' ? 'status-paid' : ''}
|
||||||
|
${ist === 'pending' ? 'status-pending' : ''}
|
||||||
|
${ist === 'overdue' ? 'status-overdue' : ''}">
|
||||||
|
<i class="fas fa-circle"></i>
|
||||||
|
${ist.charAt(0).toUpperCase() + ist.slice(1)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="status-badge ship-${report.shipment_status}">
|
||||||
|
<i class="fas fa-circle"></i>
|
||||||
|
${report.shipment_status.charAt(0).toUpperCase() + report.shipment_status.slice(1).replace('_', ' ')}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function filterTable() {
|
function filterTable() {
|
||||||
const fromDate = document.getElementById('fromDate').value;
|
const fromDate = document.getElementById('fromDate').value;
|
||||||
const toDate = document.getElementById('toDate').value;
|
const toDate = document.getElementById('toDate').value;
|
||||||
const companyFilter = document.getElementById('companyFilter').value;
|
const companyFilter = document.getElementById('companyFilter').value;
|
||||||
const statusFilter = document.getElementById('statusFilter').value;
|
const statusFilter = document.getElementById('statusFilter').value;
|
||||||
|
|
||||||
const rows = document.querySelectorAll('#reportTableBody tr');
|
filteredReports = allReports.filter(report => {
|
||||||
let visibleCount = 0;
|
let include = true;
|
||||||
|
|
||||||
rows.forEach(row => {
|
|
||||||
// Skip empty state row
|
|
||||||
if (row.querySelector('.empty-state')) return;
|
|
||||||
|
|
||||||
let showRow = true;
|
|
||||||
|
|
||||||
// Date filter
|
// Date filter
|
||||||
if (fromDate && toDate) {
|
if (fromDate && toDate) {
|
||||||
const rowDate = row.getAttribute('data-date');
|
const reportDate = new Date(report.shipment_date);
|
||||||
if (rowDate) {
|
const from = new Date(fromDate);
|
||||||
const shipmentDate = new Date(rowDate);
|
const to = new Date(toDate);
|
||||||
const from = new Date(fromDate);
|
to.setHours(23, 59, 59, 999);
|
||||||
const to = new Date(toDate);
|
|
||||||
to.setHours(23, 59, 59, 999); // End of day
|
|
||||||
|
|
||||||
if (shipmentDate < from || shipmentDate > to) {
|
if (reportDate < from || reportDate > to) {
|
||||||
showRow = false;
|
include = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Company filter
|
// Company filter
|
||||||
if (companyFilter) {
|
if (companyFilter && report.company_name !== companyFilter) {
|
||||||
const companyName = row.getAttribute('data-company');
|
include = false;
|
||||||
if (companyName !== companyFilter) {
|
|
||||||
showRow = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status filter
|
// Status filter
|
||||||
if (statusFilter && row.getAttribute('data-status') !== statusFilter) {
|
if (statusFilter && report.shipment_status !== statusFilter) {
|
||||||
showRow = false;
|
include = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show/hide row
|
return include;
|
||||||
row.style.display = showRow ? '' : 'none';
|
|
||||||
if (showRow) visibleCount++;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show empty state if no rows visible
|
currentPage = 1;
|
||||||
const emptyState = document.querySelector('.empty-state');
|
renderTable();
|
||||||
if (emptyState) {
|
updatePaginationControls();
|
||||||
const emptyRow = emptyState.closest('tr');
|
|
||||||
emptyRow.style.display = visibleCount === 0 ? '' : 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update statistics with filtered data
|
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,298 +5,325 @@
|
|||||||
@section('content')
|
@section('content')
|
||||||
<div class="container-fluid px-0">
|
<div class="container-fluid px-0">
|
||||||
|
|
||||||
|
@php
|
||||||
|
$perPage = 5; // ✅ FORCED to show pagination even with few records
|
||||||
|
$currentPage = request()->get('page', 1);
|
||||||
|
$currentPage = max(1, (int)$currentPage);
|
||||||
|
$total = $requests->count();
|
||||||
|
$totalPages = ceil($total / $perPage);
|
||||||
|
$currentItems = $requests->slice(($currentPage - 1) * $perPage, $perPage);
|
||||||
|
@endphp
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* 🌟 Smooth fade-in animation */
|
/* [ALL YOUR ORIGINAL CSS HERE - SAME AS BEFORE] */
|
||||||
@keyframes fadeInUp {
|
@keyframes fadeInUp {0% { transform: translateY(20px); opacity: 0; }100% { transform: translateY(0); opacity: 1; }}
|
||||||
0% {
|
.card, .custom-table-wrapper { animation: fadeInUp 0.8s ease both; }
|
||||||
transform: translateY(20px);
|
.custom-table tbody tr { transition: all 0.25s ease-in-out; }
|
||||||
opacity: 0;
|
.custom-table tbody tr:hover { background-color: #fffbea; transform: scale(1.01); box-shadow: 0 2px 6px rgba(0,0,0,0.05); }
|
||||||
}
|
.priority-badge {
|
||||||
100% {
|
display: inline-flex; align-items: center; font-size: 13.5px; padding: 6px 16px; border-radius: 12px; font-weight: 600;
|
||||||
transform: translateY(0);
|
box-shadow: 0 1px 2px 0 rgba(130,130,130,0.15); width: 90px; min-height: 28px; justify-content: center;
|
||||||
opacity: 1;
|
color: #fff; margin: 2px 0; transition: transform 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
.priority-badge:hover { transform: scale(1.08); }
|
||||||
|
.priority-high { background: linear-gradient(135deg, #ff8a8a, #d12929); }
|
||||||
|
.priority-medium { background: linear-gradient(135deg, #ffe390, #f5b041); }
|
||||||
|
.priority-low { background: linear-gradient(135deg, #b8f0c2, #1d8660); }
|
||||||
|
.custom-table thead th {
|
||||||
|
text-align: center; font-weight: 700; color: #000; padding: 14px; font-size: 17px; letter-spacing: 0.5px;
|
||||||
|
border-bottom: 2px solid #bfbfbf; background-color: #fde4b3;
|
||||||
|
}
|
||||||
|
.custom-table thead tr:first-child th:first-child { border-top-left-radius: 12px; }
|
||||||
|
.custom-table thead tr:first-child th:last-child { border-top-right-radius: 12px; }
|
||||||
|
.custom-table tbody tr:last-child td:first-child { border-bottom-left-radius: 12px; }
|
||||||
|
.custom-table tbody tr:last-child td:last-child { border-bottom-right-radius: 12px; }
|
||||||
|
.input-group input { border-radius: 10px 0 0 10px; border: 1px solid #ccc; box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); }
|
||||||
|
.input-group .btn { border-radius: 0 10px 10px 0; transition: all 0.2s ease-in-out; }
|
||||||
|
.input-group .btn:hover { background: #ffd65a; border-color: #ffd65a; color: #000; }
|
||||||
|
.card { border-radius: 16px; border: none; box-shadow: 0 4px 10px rgba(0,0,0,0.08); background: #fff; }
|
||||||
|
.badge {
|
||||||
|
font-size: 11px !important; font-weight: 600 !important; padding: 7px 13px !important; border-radius: 20px !important;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.3px; display: inline-flex !important; align-items: center; justify-content: center;
|
||||||
|
color: #fff !important; text-shadow: 0 1px 2px rgba(0,0,0,0.3); border: 2px solid transparent !important;
|
||||||
|
line-height: 1.2; gap: 6px; animation: pulse 2s infinite; width: 99px;
|
||||||
|
}
|
||||||
|
.status-icon { font-size: 0px; display: flex; align-items: center; justify-content: center; }
|
||||||
|
.badge.badge-pending { background: linear-gradient(135deg, #fef3c7, #fde68a) !important; color: #d97706 !important; border-color: #f59e0b !important; width: 85px; }
|
||||||
|
.badge.badge-approved { background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important; color: #065f46 !important; border-color: #10b981 !important; width: 85px; }
|
||||||
|
.badge.badge-rejected { background: linear-gradient(135deg, #fecaca, #fca5a5) !important; color: #991b1b !important; border-color: #ef4444 !important; width: 85px; }
|
||||||
|
@keyframes pulse {0% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }50% { box-shadow: 0 0 14px rgba(0,0,0,0.15); }100% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }}
|
||||||
|
.count-badge { --bs-badge-padding-x: 0.65em; --bs-badge-padding-y: 0.35em; --bs-badge-font-size: 0.75em; --bs-badge-font-weight: 700; --bs-badge-color: #fff; --bs-badge-border-radius: var(--bs-border-radius); }
|
||||||
|
h4.fw-bold { background: linear-gradient(90deg, #000000ff 0%, #030302ff 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: 800; letter-spacing: 1px; }
|
||||||
|
.custom-table tbody td:last-child { text-align: center !important; vertical-align: middle !important; }
|
||||||
|
|
||||||
|
/* ===== PAGINATION STYLES ===== */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
/* ✨ Container animation */
|
.pagination-info {
|
||||||
.card, .custom-table-wrapper {
|
font-size: 13px;
|
||||||
animation: fadeInUp 0.8s ease both;
|
color: #9ba5bb;
|
||||||
}
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
/* 🪄 Table hover effect */
|
.pagination-controls {
|
||||||
.custom-table tbody tr {
|
display: flex;
|
||||||
transition: all 0.25s ease-in-out;
|
align-items: center;
|
||||||
}
|
gap: 8px;
|
||||||
.custom-table tbody tr:hover {
|
}
|
||||||
background-color: #fffbea;
|
|
||||||
transform: scale(1.01);
|
|
||||||
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 🎯 Priority Badges */
|
.pagination-btn {
|
||||||
.priority-badge {
|
background: #fff;
|
||||||
display: inline-flex;
|
border: 1px solid #e3eaf6;
|
||||||
align-items: center;
|
color: #1a2951;
|
||||||
font-size: 13.5px;
|
padding: 8px 12px;
|
||||||
padding: 6px 16px;
|
border-radius: 6px;
|
||||||
border-radius: 12px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
box-shadow: 0 1px 2px 0 rgba(130,130,130,0.15);
|
cursor: pointer;
|
||||||
width: 90px;
|
transition: all 0.3s ease;
|
||||||
min-height: 28px;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
color: #fff;
|
justify-content: center;
|
||||||
margin: 2px 0;
|
min-width: 40px;
|
||||||
transition: transform 0.2s ease-in-out;
|
height: 32px;
|
||||||
}
|
}
|
||||||
.priority-badge:hover {
|
|
||||||
transform: scale(1.08);
|
|
||||||
}
|
|
||||||
.priority-high { background: linear-gradient(135deg, #ff8a8a, #d12929); }
|
|
||||||
.priority-medium { background: linear-gradient(135deg, #ffe390, #f5b041); }
|
|
||||||
.priority-low { background: linear-gradient(135deg, #b8f0c2, #1d8660); }
|
|
||||||
|
|
||||||
/* 🎨 Table Header (keep original bg-light color) */
|
.pagination-btn:hover:not(:disabled) {
|
||||||
.custom-table thead th {
|
background: #1a2951;
|
||||||
text-align: center;
|
color: white;
|
||||||
font-weight: 700;
|
border-color: #1a2951;
|
||||||
color: #000;
|
}
|
||||||
padding: 14px;
|
|
||||||
font-size: 17px;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
border-bottom: 2px solid #bfbfbf;
|
|
||||||
background-color: #fde4b3; /* Bootstrap bg-light */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 🟢 Rounded Corners for Header */
|
.pagination-btn:disabled {
|
||||||
.custom-table thead tr:first-child th:first-child {
|
background: #f8fafc;
|
||||||
border-top-left-radius: 12px;
|
color: #cbd5e0;
|
||||||
}
|
border-color: #e2e8f0;
|
||||||
.custom-table thead tr:first-child th:last-child {
|
cursor: not-allowed;
|
||||||
border-top-right-radius: 12px;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 🧾 Table bottom corners */
|
.pagination-page-btn {
|
||||||
.custom-table tbody tr:last-child td:first-child {
|
background: #fff;
|
||||||
border-bottom-left-radius: 12px;
|
border: 1px solid #e3eaf6;
|
||||||
}
|
color: #1a2951;
|
||||||
.custom-table tbody tr:last-child td:last-child {
|
padding: 6px 12px;
|
||||||
border-bottom-right-radius: 12px;
|
border-radius: 6px;
|
||||||
}
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* 💫 Search box styling */
|
.pagination-page-btn:hover {
|
||||||
.input-group input {
|
background: #1a2951;
|
||||||
border-radius: 10px 0 0 10px;
|
color: white;
|
||||||
border: 1px solid #ccc;
|
border-color: #1a2951;
|
||||||
box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);
|
}
|
||||||
}
|
|
||||||
.input-group .btn {
|
|
||||||
border-radius: 0 10px 10px 0;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
.input-group .btn:hover {
|
|
||||||
background: #ffd65a;
|
|
||||||
border-color: #ffd65a;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 🎨 Card Style */
|
.pagination-page-btn.active {
|
||||||
.card {
|
background: #1a2951;
|
||||||
border-radius: 16px;
|
color: white;
|
||||||
border: none;
|
border-color: #1a2951;
|
||||||
box-shadow: 0 4px 10px rgba(0,0,0,0.08);
|
}
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ⚡ Status Badges - NEW ATTRACTIVE STYLES */
|
.pagination-pages {
|
||||||
.badge {
|
display: flex;
|
||||||
font-size: 11px !important;
|
gap: 4px;
|
||||||
font-weight: 600 !important;
|
align-items: center;
|
||||||
padding: 7px 13px !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;
|
|
||||||
box-sizing: border-box;
|
|
||||||
line-height: 1.2;
|
|
||||||
gap: 6px;
|
|
||||||
animation: pulse 2s infinite;
|
|
||||||
width: 99px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Status icons */
|
.pagination-ellipsis {
|
||||||
.status-icon {
|
color: #9ba5bb;
|
||||||
font-size: 0px;
|
font-size: 13px;
|
||||||
display: flex;
|
padding: 0 4px;
|
||||||
align-items: center;
|
}
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom status badge backgrounds */
|
.pagination-img-btn {
|
||||||
.badge-pending {
|
background: #fff;
|
||||||
background: url('/images/status-bg-pending.png') !important;
|
border: 1px solid #e3eaf6;
|
||||||
}
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.badge-approved {
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
background: url('/images/status-bg-approved.png') !important;
|
background: #1a2951;
|
||||||
}
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
.badge-rejected {
|
.pagination-img-btn:disabled {
|
||||||
background: url('/images/status-bg-rejected.png') !important;
|
background: #f8fafc;
|
||||||
}
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fallback colors if images don't load */
|
.pagination-img-btn svg {
|
||||||
.badge.badge-pending {
|
width: 16px;
|
||||||
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
height: 16px;
|
||||||
color: #d97706 !important;
|
transition: filter 0.3s ease;
|
||||||
border-color: #f59e0b !important;
|
}
|
||||||
width: 85px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge.badge-approved {
|
.pagination-img-btn:hover:not(:disabled) svg {
|
||||||
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
color: #065f46 !important;
|
}
|
||||||
border-color: #10b981 !important;
|
|
||||||
width: 85px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge.badge-rejected {
|
.pagination-img-btn:disabled svg {
|
||||||
background: linear-gradient(135deg, #fecaca, #fca5a5) !important;
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
color: #991b1b !important;
|
}
|
||||||
border-color: #ef4444 !important;
|
|
||||||
width: 85px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animation effects for badges */
|
/* Responsive pagination */
|
||||||
@keyframes pulse {
|
@media (max-width: 768px) {
|
||||||
0% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }
|
.pagination-container {
|
||||||
50% { box-shadow: 0 0 14px rgba(0,0,0,0.15); }
|
flex-direction: column;
|
||||||
100% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }
|
gap: 10px;
|
||||||
}
|
align-items: stretch;
|
||||||
|
}
|
||||||
/* Count badges at top */
|
.pagination-controls {
|
||||||
.count-badge {
|
justify-content: center;
|
||||||
--bs-badge-padding-x: 0.65em;
|
}
|
||||||
--bs-badge-padding-y: 0.35em;
|
}
|
||||||
--bs-badge-font-size: 0.75em;
|
|
||||||
--bs-badge-font-weight: 700;
|
|
||||||
--bs-badge-color: #fff;
|
|
||||||
--bs-badge-border-radius: var(--bs-border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ✨ Heading style */
|
|
||||||
h4.fw-bold {
|
|
||||||
background: linear-gradient(90deg, #000000ff 0%, #030302ff 100%);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ✅ Center align for last column */
|
|
||||||
.custom-table tbody td:last-child {
|
|
||||||
text-align: center !important;
|
|
||||||
vertical-align: middle !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Heading and status badges row -->
|
<!-- Counts -->
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2 mt-3">
|
<div class="d-flex justify-content-between align-items-center mb-2 mt-3">
|
||||||
<h4 class="fw-bold mb-0">User Requests</h4>
|
<h4 class="fw-bold mb-0">User Requests (Total: {{ $total }})</h4>
|
||||||
<div>
|
<div>
|
||||||
<span class="count-badge badge rounded-pill bg-warning text-dark me-1">
|
<span class="count-badge badge rounded-pill bg-warning text-dark me-1">{{ $requests->where('status', 'pending')->count() }} Pending</span>
|
||||||
{{ $requests->where('status', 'pending')->count() }} Pending
|
<span class="count-badge badge rounded-pill bg-success me-1">{{ $requests->where('status', 'approved')->count() }} Approved</span>
|
||||||
</span>
|
<span class="count-badge badge rounded-pill bg-danger">{{ $requests->where('status', 'rejected')->count() }} Rejected</span>
|
||||||
<span class="count-badge badge rounded-pill bg-success me-1">
|
|
||||||
{{ $requests->where('status', 'approved')->count() }} Approved
|
|
||||||
</span>
|
|
||||||
<span class="count-badge badge rounded-pill bg-danger">
|
|
||||||
{{ $requests->where('status', 'rejected')->count() }} Rejected
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search + Table -->
|
||||||
<div class="card mb-4 shadow-sm">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-body pb-1">
|
<div class="card-body pb-1">
|
||||||
<form method="GET" action="">
|
<form method="GET" action="">
|
||||||
<div class="input-group mb-2">
|
<div class="input-group mb-2">
|
||||||
<input type="text" name="search" value="{{ old('search', request('search')) }}" class="form-control" placeholder="Search Request by name, email, Company or Request ID">
|
<input type="text" name="search" value="{{ request('search') }}" class="form-control" placeholder="🔍 Search by name, email, company...">
|
||||||
<button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
|
<button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="table-responsive custom-table-wrapper">
|
<div class="table-responsive custom-table-wrapper">
|
||||||
<table class="table align-middle mb-0 custom-table">
|
<table class="table align-middle mb-0 custom-table">
|
||||||
<thead class="bg-light">
|
<thead><tr>
|
||||||
<tr>
|
<th>#</th><th>Request ID</th><th>Name</th><th>Company</th><th>Email</th><th>Mobile</th><th>Address</th><th>Priority</th><th>Date</th><th>Status</th><th>Actions</th>
|
||||||
<th>#</th>
|
</tr></thead>
|
||||||
<th>Request ID</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Company</th>
|
|
||||||
<th>Email</th>
|
|
||||||
<th>Mobile</th>
|
|
||||||
<th>Address</th>
|
|
||||||
<th>Priority</th>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach($requests as $index => $req)
|
@forelse($currentItems as $index => $req)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $requests->count() - $index }}</td>
|
<td><strong>{{ ($currentPage - 1) * $perPage + $index + 1 }}</strong></td>
|
||||||
<td>{{ $req->request_id }}</td>
|
<td>{{ $req->request_id }}</td>
|
||||||
<td>{{ $req->customer_name }}</td>
|
<td>{{ $req->customer_name }}</td>
|
||||||
<td>{{ $req->company_name }}</td>
|
<td>{{ $req->company_name }}</td>
|
||||||
<td>{{ $req->email }}</td>
|
<td>{{ $req->email }}</td>
|
||||||
<td>{{ $req->mobile_no }}</td>
|
<td>{{ $req->mobile_no }}</td>
|
||||||
<td>{{ $req->address }}</td>
|
<td>{{ Str::limit($req->address, 30) }}</td>
|
||||||
<td>
|
<td>
|
||||||
@if(strtolower($req->priority) == 'high')
|
@if(strtolower($req->priority) == 'high')<span class="priority-badge priority-high">High</span>
|
||||||
<span class="priority-badge priority-high">High</span>
|
@elseif(strtolower($req->priority) == 'medium')<span class="priority-badge priority-medium">Medium</span>
|
||||||
@elseif(strtolower($req->priority) == 'medium')
|
@elseif(strtolower($req->priority) == 'low')<span class="priority-badge priority-low">Low</span>
|
||||||
<span class="priority-badge priority-medium">Medium</span>
|
@else{{ $req->priority ?? 'N/A' }}@endif
|
||||||
@elseif(strtolower($req->priority) == 'low')
|
|
||||||
<span class="priority-badge priority-low">Low</span>
|
|
||||||
@else
|
|
||||||
{{ $req->priority }}
|
|
||||||
@endif
|
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $req->date }}</td>
|
<td>{{ $req->date }}</td>
|
||||||
<td>
|
<td>
|
||||||
@if($req->status == 'approved')
|
@if($req->status == 'approved')<span class="badge badge-approved"><i class="bi bi-check-circle-fill status-icon"></i>Approved</span>
|
||||||
<span class="badge badge-approved">
|
@elseif($req->status == 'rejected')<span class="badge badge-rejected"><i class="bi bi-x-circle-fill status-icon"></i>Rejected</span>
|
||||||
<i class="bi bi-check-circle-fill status-icon"></i>
|
@else<span class="badge badge-pending"><i class="bi bi-clock-fill status-icon"></i>Pending</span>@endif
|
||||||
Approved
|
|
||||||
</span>
|
|
||||||
@elseif($req->status == 'rejected')
|
|
||||||
<span class="badge badge-rejected">
|
|
||||||
<i class="bi bi-x-circle-fill status-icon"></i>
|
|
||||||
Rejected
|
|
||||||
</span>
|
|
||||||
@else
|
|
||||||
<span class="badge badge-pending">
|
|
||||||
<i class="bi bi-clock-fill status-icon"></i>
|
|
||||||
Pending
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
</td>
|
||||||
<td>N/A</td>
|
<td>N/A</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@empty
|
||||||
|
<tr><td colspan="11" class="text-center text-muted py-4">No records found.</td></tr>
|
||||||
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@if($requests->isEmpty())
|
</div>
|
||||||
<div class="py-4 text-center text-muted">No records found.</div>
|
|
||||||
@endif
|
{{-- ✅ PAGINATION - WITH ARROW BUTTONS --}}
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info">
|
||||||
|
Showing {{ ($currentPage - 1) * $perPage + 1 }} to {{ min($currentPage * $perPage, $total) }} of {{ $total }} entries
|
||||||
|
</div>
|
||||||
|
<div class="pagination-controls">
|
||||||
|
{{-- Previous Page --}}
|
||||||
|
@if($currentPage > 1)
|
||||||
|
<a href="{{ request()->fullUrlWithQuery(['page' => $currentPage - 1]) }}" class="pagination-img-btn" 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>
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<button class="pagination-img-btn" disabled 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>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Page Numbers --}}
|
||||||
|
<div class="pagination-pages">
|
||||||
|
@php
|
||||||
|
$start = max(1, $currentPage - 1);
|
||||||
|
$end = min($totalPages, $currentPage + 1);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if($start > 1)
|
||||||
|
<a href="{{ request()->fullUrlWithQuery(['page' => 1]) }}" class="pagination-page-btn">1</a>
|
||||||
|
@if($start > 2)
|
||||||
|
<span class="pagination-ellipsis">...</span>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@for($i = $start; $i <= $end; $i++)
|
||||||
|
@if($i == $currentPage)
|
||||||
|
<span class="pagination-page-btn active">{{ $i }}</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ request()->fullUrlWithQuery(['page' => $i]) }}" class="pagination-page-btn">{{ $i }}</a>
|
||||||
|
@endif
|
||||||
|
@endfor
|
||||||
|
|
||||||
|
@if($end < $totalPages)
|
||||||
|
@if($end < $totalPages - 1)
|
||||||
|
<span class="pagination-ellipsis">...</span>
|
||||||
|
@endif
|
||||||
|
<a href="{{ request()->fullUrlWithQuery(['page' => $totalPages]) }}" class="pagination-page-btn">{{ $totalPages }}</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Next Page --}}
|
||||||
|
@if($currentPage < $totalPages)
|
||||||
|
<a href="{{ request()->fullUrlWithQuery(['page' => $currentPage + 1]) }}" class="pagination-img-btn" 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>
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<button class="pagination-img-btn" disabled 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>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -917,6 +917,151 @@
|
|||||||
.shipment-row.visible {
|
.shipment-row.visible {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Pagination Styles (Same as Account Dashboard) ---------- */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-top: 1px solid #eef3fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
color: #1a2951;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover:not(:disabled) {
|
||||||
|
background: #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: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn:hover {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-page-btn.active {
|
||||||
|
background: #1a2951;
|
||||||
|
color: white;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
color: #9ba5bb;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image-based pagination buttons */
|
||||||
|
.pagination-img-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3eaf6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
|
background: #1a2951;
|
||||||
|
border-color: #1a2951;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:hover:not(:disabled) img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-img-btn:disabled img {
|
||||||
|
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pagination-container {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid py-4">
|
<div class="container-fluid py-4">
|
||||||
@@ -1153,6 +1298,28 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<div class="pagination-info" id="pageInfo">Showing 1 to {{ $shipments->count() }} of {{ $shipments->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>
|
</div>
|
||||||
|
|
||||||
@@ -1185,12 +1352,25 @@
|
|||||||
<!-- MODAL LOAD SCRIPT (AJAX) -->
|
<!-- MODAL LOAD SCRIPT (AJAX) -->
|
||||||
<!-- ========================= -->
|
<!-- ========================= -->
|
||||||
<script>
|
<script>
|
||||||
// Status Filter Functionality
|
// Pagination state
|
||||||
|
let currentPage = 1;
|
||||||
|
const itemsPerPage = 10;
|
||||||
|
let allShipments = @json($shipments);
|
||||||
|
let filteredShipments = [...allShipments];
|
||||||
|
|
||||||
|
// Initialize pagination on page load
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
|
||||||
|
// Bind pagination events
|
||||||
|
document.getElementById('prevPageBtn').addEventListener('click', goToPreviousPage);
|
||||||
|
document.getElementById('nextPageBtn').addEventListener('click', goToNextPage);
|
||||||
|
|
||||||
|
// Status Filter Functionality
|
||||||
const statusFilter = document.getElementById('statusFilter');
|
const statusFilter = document.getElementById('statusFilter');
|
||||||
const searchInput = document.getElementById('searchInput');
|
const searchInput = document.getElementById('searchInput');
|
||||||
const carrierFilter = document.getElementById('carrierFilter');
|
const carrierFilter = document.getElementById('carrierFilter');
|
||||||
const shipmentRows = document.querySelectorAll('.shipment-row');
|
|
||||||
|
|
||||||
// Function to filter shipments
|
// Function to filter shipments
|
||||||
function filterShipments() {
|
function filterShipments() {
|
||||||
@@ -1198,48 +1378,34 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const searchTerm = searchInput.value.toLowerCase();
|
const searchTerm = searchInput.value.toLowerCase();
|
||||||
const selectedCarrier = carrierFilter.value;
|
const selectedCarrier = carrierFilter.value;
|
||||||
|
|
||||||
shipmentRows.forEach(row => {
|
filteredShipments = allShipments.filter(shipment => {
|
||||||
const status = row.getAttribute('data-status');
|
let include = true;
|
||||||
const shipmentId = row.getAttribute('data-shipment-id').toLowerCase();
|
|
||||||
const origin = row.cells[2].textContent.toLowerCase();
|
|
||||||
const destination = row.cells[3].textContent.toLowerCase();
|
|
||||||
|
|
||||||
let statusMatch = selectedStatus === 'all' || status === selectedStatus;
|
|
||||||
let searchMatch = searchTerm === '' ||
|
|
||||||
shipmentId.includes(searchTerm) ||
|
|
||||||
origin.includes(searchTerm) ||
|
|
||||||
destination.includes(searchTerm);
|
|
||||||
let carrierMatch = selectedCarrier === 'all'; // You can add carrier data attribute if needed
|
|
||||||
|
|
||||||
if (statusMatch && searchMatch && carrierMatch) {
|
// Status filter
|
||||||
row.classList.remove('hidden');
|
if (selectedStatus !== 'all' && shipment.status !== selectedStatus) {
|
||||||
row.classList.add('visible');
|
include = false;
|
||||||
} else {
|
|
||||||
row.classList.add('hidden');
|
|
||||||
row.classList.remove('visible');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search filter
|
||||||
|
if (searchTerm) {
|
||||||
|
const matchesSearch =
|
||||||
|
shipment.shipment_id.toLowerCase().includes(searchTerm) ||
|
||||||
|
shipment.origin.toLowerCase().includes(searchTerm) ||
|
||||||
|
shipment.destination.toLowerCase().includes(searchTerm);
|
||||||
|
if (!matchesSearch) include = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrier filter (you can add carrier data attribute if needed)
|
||||||
|
if (selectedCarrier !== 'all') {
|
||||||
|
// Add carrier filtering logic here if you have carrier data
|
||||||
|
}
|
||||||
|
|
||||||
|
return include;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show message if no results
|
currentPage = 1;
|
||||||
const visibleRows = document.querySelectorAll('.shipment-row:not(.hidden)');
|
renderTable();
|
||||||
const noResultsRow = document.querySelector('.shipment-row[colspan]');
|
updatePaginationControls();
|
||||||
|
|
||||||
if (visibleRows.length === 0 && !noResultsRow) {
|
|
||||||
// Add no results message
|
|
||||||
const tbody = document.getElementById('shipmentsTableBody');
|
|
||||||
const noResultsHtml = `
|
|
||||||
<tr>
|
|
||||||
<td colspan="11" class="text-center py-5 text-muted">
|
|
||||||
<i class="bi bi-search display-4 d-block mb-3"></i>
|
|
||||||
No shipments found matching your criteria
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
tbody.innerHTML = noResultsHtml;
|
|
||||||
} else if (visibleRows.length > 0 && noResultsRow) {
|
|
||||||
// Remove no results message
|
|
||||||
noResultsRow.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event listeners for filters
|
// Event listeners for filters
|
||||||
@@ -1251,6 +1417,171 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
filterShipments();
|
filterShipments();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pagination Functions
|
||||||
|
function goToPreviousPage() {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToNextPage() {
|
||||||
|
const totalPages = Math.ceil(filteredShipments.length / itemsPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++;
|
||||||
|
renderTable();
|
||||||
|
updatePaginationControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginationControls() {
|
||||||
|
const totalPages = Math.ceil(filteredShipments.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, filteredShipments.length);
|
||||||
|
pageInfo.textContent = `Showing ${startIndex} to ${endIndex} of ${filteredShipments.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('shipmentsTableBody');
|
||||||
|
|
||||||
|
if (filteredShipments.length === 0) {
|
||||||
|
tbody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="11" class="text-center py-5 text-muted">
|
||||||
|
<i class="bi bi-search display-4 d-block mb-3"></i>
|
||||||
|
No shipments found matching your criteria
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pagination
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const paginatedItems = filteredShipments.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 table rows
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
sortedItems.forEach((shipment, index) => {
|
||||||
|
const displayIndex = filteredShipments.length - (startIndex + index);
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
row.className = 'shipment-row';
|
||||||
|
row.setAttribute('data-status', shipment.status);
|
||||||
|
row.setAttribute('data-shipment-id', shipment.shipment_id);
|
||||||
|
|
||||||
|
row.innerHTML = `
|
||||||
|
<td class="fw-bold">${displayIndex}</td>
|
||||||
|
<td>
|
||||||
|
<a href="#" class="text-primary fw-bold" onclick="openShipmentDetails(${shipment.id})">
|
||||||
|
${shipment.shipment_id}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>${shipment.origin}</td>
|
||||||
|
<td>${shipment.destination}</td>
|
||||||
|
<td><span class="badge bg-light text-dark">${shipment.total_qty}</span></td>
|
||||||
|
<td><span class="badge bg-light text-dark">${shipment.total_kg} kg</span></td>
|
||||||
|
<td><span class="badge bg-light text-dark">${shipment.total_cbm} CBM</span></td>
|
||||||
|
<td class="fw-bold text-success">₹${parseFloat(shipment.total_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge badge-${shipment.status}">
|
||||||
|
${shipment.status.charAt(0).toUpperCase() + shipment.status.slice(1).replace('_', ' ')}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>${new Date(shipment.shipment_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })}</td>
|
||||||
|
<td>
|
||||||
|
<div class="action-container">
|
||||||
|
<button type="button" class="btn-edit-status" onclick="toggleStatusDropdown(this, ${shipment.id})" title="Edit Status">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
</button>
|
||||||
|
<div class="status-dropdown" id="statusDropdown-${shipment.id}">
|
||||||
|
<form action="/admin/shipments/update-status" method="POST" class="status-form">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||||
|
<input type="hidden" name="shipment_id" value="${shipment.id}">
|
||||||
|
<button type="submit" name="status" value="pending" class="status-option pending">
|
||||||
|
<span class="status-indicator pending"></span>
|
||||||
|
Pending
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="status" value="in_transit" class="status-option in_transit">
|
||||||
|
<span class="status-indicator in_transit"></span>
|
||||||
|
In Transit
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="status" value="dispatched" class="status-option dispatched">
|
||||||
|
<span class="status-indicator dispatched"></span>
|
||||||
|
Dispatched
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="status" value="delivered" class="status-option delivered">
|
||||||
|
<span class="status-indicator delivered"></span>
|
||||||
|
Delivered
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function openShipmentDetails(id) {
|
function openShipmentDetails(id) {
|
||||||
let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal'));
|
let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal'));
|
||||||
let content = document.getElementById('shipmentDetailsContent');
|
let content = document.getElementById('shipmentDetailsContent');
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ Route::prefix('admin')->group(function () {
|
|||||||
|
|
||||||
Route::post('/logout', [AdminAuthController::class, 'logout'])
|
Route::post('/logout', [AdminAuthController::class, 'logout'])
|
||||||
->name('admin.logout');
|
->name('admin.logout');
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user