Edit file File name : customers.php Content :<?php session_start(); if (!isset($_SESSION['user_id'])) { header('Location: index.php'); exit(); } require_once '_helpers.php'; $conn = get_db_connection(); $business = $conn->query("SELECT * FROM business_settings LIMIT 1")->fetch_assoc(); // Fetch all customers with their balance details $all_customers_with_balance = []; $customers_result = $conn->query("SELECT id, name, email, phone, logo FROM customers ORDER BY name ASC"); if ($customers_result) { $customers_map = []; while ($row = $customers_result->fetch_assoc()) { $customers_map[$row['id']] = $row; $customers_map[$row['id']]['balance'] = 0; $customers_map[$row['id']]['overdue'] = 0; } $invoices_result = $conn->query("SELECT customer_id, total, status, due_date FROM invoices WHERE status IN ('Due', 'Overdue')"); if ($invoices_result) { while ($row = $invoices_result->fetch_assoc()) { $customer_id = $row['customer_id']; if (isset($customers_map[$customer_id])) { $customers_map[$customer_id]['balance'] += (float)$row['total']; if ($row['status'] === 'Overdue') { $customers_map[$customer_id]['overdue'] += (float)$row['total']; } } } } $all_customers_with_balance = array_values($customers_map); } $conn->close(); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Customers - <?php echo htmlspecialchars($business['name']); ?></title> <script src="https://cdn.tailwindcss.com"></script> <style>:root { --primary: <?php echo htmlspecialchars($business['primary_color'] ?? '217 91% 60%'); ?>; } .bg-primary { background-color: hsl(var(--primary)); } .text-primary { color: hsl(var(--primary)); } .border-primary { border-color: hsl(var(--primary)); }</style> </head> <body class="bg-slate-50"> <div class="flex h-screen"> <?php include '_sidebar.php'; ?> <div class="flex-1 flex flex-col overflow-hidden lg:ml-64"> <?php include '_header.php'; ?> <main class="flex-1 overflow-x-hidden overflow-y-auto bg-slate-50 p-6"> <div class="flex flex-col gap-8"> <div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4"> <div> <h1 class="text-3xl font-bold tracking-tight">Customers</h1> </div> <div class="flex items-center gap-2 self-end sm:self-auto"> <button class="px-4 py-2 border rounded-full text-sm font-medium hover:bg-slate-100 flex items-center gap-2"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg> Import </button> <a href="customer-create.php" class="px-4 py-2 bg-primary text-white rounded-full text-sm font-semibold hover:bg-opacity-90 flex items-center gap-2"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="16"/><line x1="8" x2="16" y1="12" y2="12"/></svg> Add customer </a> </div> </div> <div class="relative w-full max-w-sm"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg> <input id="customer-search" type="text" placeholder="Search by name" class="pl-10 w-full h-10 px-3 py-2 border border-slate-300 rounded-full bg-white text-sm"> </div> <div class="bg-white rounded-lg shadow-sm border"> <table class="w-full text-sm" id="customers-table"> <thead class="bg-slate-50"> <tr> <th class="p-4 text-left font-semibold text-slate-600">Name</th> <th class="p-4 text-left font-semibold text-slate-600 hidden md:table-cell">Email</th> <th class="p-4 text-left font-semibold text-slate-600 hidden lg:table-cell">Phone</th> <th class="p-4 text-right font-semibold text-slate-600">Balance</th> <th class="p-4"></th> </tr> </thead> <tbody> <?php foreach ($all_customers_with_balance as $customer): ?> <tr class="border-b hover:bg-slate-50 customer-row" data-name="<?php echo strtolower(htmlspecialchars($customer['name'])); ?>"> <td class="p-4 font-medium"> <div class="flex items-center gap-3"> <div class="h-8 w-8 rounded-full bg-primary/10 text-primary flex items-center justify-center font-semibold text-xs"> <?php echo htmlspecialchars(get_initials($customer['name'])); ?> </div> <div> <a href="#" class="hover:underline text-slate-800"><?php echo htmlspecialchars($customer['name']); ?></a> <span class="text-slate-500 md:hidden block text-xs"><?php echo htmlspecialchars($customer['email']); ?></span> </div> </div> </td> <td class="p-4 text-slate-600 hidden md:table-cell"><?php echo htmlspecialchars($customer['email']); ?></td> <td class="p-4 text-slate-600 hidden lg:table-cell"><?php echo htmlspecialchars($customer['phone']); ?></td> <td class="p-4 text-right"> <div class="flex flex-col items-end"> <span class="font-medium"><?php echo format_currency($customer['balance']); ?></span> <?php if ($customer['overdue'] > 0): ?> <span class="text-xs text-red-600"><?php echo format_currency($customer['overdue']); ?> overdue</span> <?php endif; ?> </div> </td> <td class="p-4 relative"> <button class="dropdown-toggle p-2 rounded-full hover:bg-slate-100"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg> </button> <div class="dropdown-menu hidden absolute right-8 top-1/2 -translate-y-1/2 w-48 bg-white rounded-md shadow-lg border z-10 p-1"> <a href="#" class="customer-action flex items-center gap-2 w-full text-left px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-50 rounded-md" data-action="view"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>View</a> <a href="#" class="customer-action flex items-center gap-2 w-full text-left px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-50 rounded-md" data-action="edit"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>Edit</a> <hr class="my-1"> <button class="customer-action flex items-center gap-2 w-full text-left px-3 py-1.5 text-sm text-red-600 hover:bg-red-50 rounded-md" data-action="delete" data-id="<?php echo htmlspecialchars($customer['id']); ?>" data-name="<?php echo htmlspecialchars($customer['name']); ?>"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>Delete</button> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> </div> </main> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { // Search functionality const searchInput = document.getElementById('customer-search'); const table = document.getElementById('customers-table'); const rows = table.querySelectorAll('.customer-row'); searchInput.addEventListener('input', (e) => { const searchTerm = e.target.value.toLowerCase(); rows.forEach(row => { const name = row.dataset.name; if (name.includes(searchTerm)) { row.style.display = ''; } else { row.style.display = 'none'; } }); }); // Dropdown menu functionality document.body.addEventListener('click', (e) => { document.querySelectorAll('.dropdown-menu').forEach(menu => { if (!menu.parentElement.contains(e.target)) { menu.classList.add('hidden'); } }); const dropdownToggle = e.target.closest('.dropdown-toggle'); if (dropdownToggle) { e.stopPropagation(); const menu = dropdownToggle.nextElementSibling; // Close all other menus before opening this one document.querySelectorAll('.dropdown-menu').forEach(m => { if (m !== menu) m.classList.add('hidden'); }); menu.classList.toggle('hidden'); } const customerAction = e.target.closest('.customer-action'); if(customerAction) { const action = customerAction.dataset.action; if(action === 'delete') { e.preventDefault(); const customerId = customerAction.dataset.id; const customerName = customerAction.dataset.name; if(confirm(`Are you sure you want to delete ${customerName}? This action cannot be undone.`)) { const formData = new FormData(); formData.append('action', 'delete_customer'); formData.append('customer_id', customerId); fetch('api.php', { method: 'POST', body: formData }) .then(res => res.json()) .then(data => { if(data.success) { alert(data.data.message); location.reload(); } else { alert('Error: ' + data.message); } }); } } } }); }); </script> </body> </html> Save