customer-database / index.html
nj22A's picture
Add 2 files
d676950 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Customer Database</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background-color: #f2f2f7;
-webkit-font-smoothing: antialiased;
}
.frosted-glass {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(255, 255, 255, 0.5);
}
.customer-card {
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
border-radius: 12px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
}
.customer-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.status-badge {
border-radius: 10px;
font-size: 13px;
font-weight: 500;
padding: 3px 10px;
}
.segmented-control {
display: flex;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.05);
padding: 4px;
}
.segmented-control input[type="radio"] {
display: none;
}
.segmented-control label {
flex: 1;
text-align: center;
padding: 6px 12px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.segmented-control input[type="radio"]:checked + label {
background-color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}
.action-button {
transition: all 0.2s ease;
border-radius: 10px;
}
.action-button:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.fade-in {
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.modal-overlay {
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.modal-content {
border-radius: 14px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
}
.text-field {
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.02);
border: 1px solid rgba(0, 0, 0, 0.05);
}
.text-field:focus {
background-color: white;
border-color: rgba(0, 122, 255, 0.3);
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
}
.search-bar {
background-color: rgba(0, 0, 0, 0.03);
border-radius: 10px;
transition: all 0.2s ease;
}
.search-bar:focus-within {
background-color: white;
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
}
.empty-state {
background: linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(245,245,245,0.9) 100%);
border-radius: 14px;
}
</style>
</head>
<body class="min-h-screen">
<!-- Header -->
<header class="frosted-glass fixed top-0 left-0 right-0 z-20">
<div class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900">Customers</h1>
<p class="text-sm text-gray-500 mt-1">Manage your customer database</p>
</div>
<button id="addCustomerBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-full flex items-center justify-center gap-2 transition-colors shadow-sm">
<i class="fas fa-plus"></i>
<span class="text-sm font-medium">New</span>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 pt-24 pb-8">
<!-- Filters and Search -->
<div class="mb-6">
<div class="flex flex-col md:flex-row gap-4">
<div class="flex-1">
<div class="search-bar flex items-center px-3 py-2">
<i class="fas fa-search text-gray-400 mr-2"></i>
<input type="text" id="searchInput" placeholder="Search customers..."
class="w-full bg-transparent outline-none text-gray-700 placeholder-gray-400">
</div>
</div>
<div class="w-full md:w-64">
<div class="segmented-control">
<input type="radio" id="filter-all" name="status-filter" value="all" checked>
<label for="filter-all" class="text-gray-700">All</label>
<input type="radio" id="filter-active" name="status-filter" value="Active">
<label for="filter-active" class="text-gray-700">Active</label>
<input type="radio" id="filter-inactive" name="status-filter" value="Inactive">
<label for="filter-inactive" class="text-gray-700">Inactive</label>
</div>
</div>
</div>
</div>
<!-- Customer List -->
<div id="customerList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Customer cards will be inserted here by JavaScript -->
</div>
<!-- Empty State -->
<div id="emptyState" class="empty-state hidden p-8 text-center rounded-xl shadow-sm">
<div class="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<i class="fas fa-user-friends text-gray-400 text-xl"></i>
</div>
<h3 class="text-lg font-medium text-gray-800 mb-1">No customers found</h3>
<p class="text-gray-500 mb-4">Try adjusting your search or filter criteria</p>
<button id="resetFiltersBtn" class="text-blue-500 font-medium text-sm">Reset all filters</button>
</div>
<!-- Pagination -->
<div class="flex justify-center mt-8">
<div class="flex items-center gap-2">
<button id="prevPage" class="action-button w-10 h-10 flex items-center justify-center text-gray-500 disabled:opacity-40">
<i class="fas fa-chevron-left"></i>
</button>
<div class="flex gap-1">
<!-- Page numbers will be inserted here by JavaScript -->
</div>
<button id="nextPage" class="action-button w-10 h-10 flex items-center justify-center text-gray-500 disabled:opacity-40">
<i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
</main>
<!-- Add/Edit Customer Modal -->
<div id="customerModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity modal-overlay" aria-hidden="true"></div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full modal-content">
<div class="bg-white px-6 pt-6 pb-4">
<div class="flex justify-between items-start">
<h3 id="modalTitle" class="text-xl leading-6 font-bold text-gray-900">New Customer</h3>
<button id="closeModal" class="text-gray-400 hover:text-gray-500 rounded-full w-8 h-8 flex items-center justify-center action-button">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mt-6">
<form id="customerForm">
<input type="hidden" id="customerId">
<div class="mb-4">
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
<input type="text" id="name" name="name" class="text-field w-full px-4 py-3 focus:outline-none">
</div>
<div class="mb-4">
<label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
<input type="email" id="email" name="email" class="text-field w-full px-4 py-3 focus:outline-none">
</div>
<div class="mb-4">
<label for="phone" class="block text-sm font-medium text-gray-700 mb-2">Phone</label>
<input type="tel" id="phone" name="phone" class="text-field w-full px-4 py-3 focus:outline-none">
</div>
<div class="mb-4">
<label for="status" class="block text-sm font-medium text-gray-700 mb-2">Status</label>
<select id="status" name="status" class="text-field w-full px-4 py-3 focus:outline-none">
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
<option value="Pending">Pending</option>
<option value="Banned">Banned</option>
</select>
</div>
<div class="mb-4">
<label for="joinDate" class="block text-sm font-medium text-gray-700 mb-2">Join Date</label>
<input type="date" id="joinDate" name="joinDate" class="text-field w-full px-4 py-3 focus:outline-none">
</div>
</form>
</div>
</div>
<div class="bg-gray-50 px-6 py-4 flex flex-row-reverse gap-3">
<button id="saveCustomer" type="button" class="w-full inline-flex justify-center rounded-xl border border-transparent shadow-sm px-6 py-3 bg-blue-500 text-base font-medium text-white hover:bg-blue-600 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm transition-colors">
Save
</button>
<button id="cancelModal" type="button" class="w-full inline-flex justify-center rounded-xl border border-gray-300 shadow-sm px-6 py-3 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm transition-colors">
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- Confirmation Modal -->
<div id="confirmModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity modal-overlay" aria-hidden="true"></div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full modal-content">
<div class="bg-white px-6 pt-6 pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<i class="fas fa-exclamation text-red-500"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-bold text-gray-900">Delete Customer</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Are you sure you want to delete this customer? This action cannot be undone.</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-6 py-4 flex flex-row-reverse gap-3">
<button id="confirmDelete" type="button" class="w-full inline-flex justify-center rounded-xl border border-transparent shadow-sm px-6 py-3 bg-red-500 text-base font-medium text-white hover:bg-red-600 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm transition-colors">
Delete
</button>
<button id="cancelDelete" type="button" class="w-full inline-flex justify-center rounded-xl border border-gray-300 shadow-sm px-6 py-3 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm transition-colors">
Cancel
</button>
</div>
</div>
</div>
</div>
<script>
// Sample customer data
const customers = [
{ id: 1, name: "John Doe", email: "john@example.com", phone: "555-123-4567", status: "Active", joinDate: "2023-01-15" },
{ id: 2, name: "Jane Smith", email: "jane@example.com", phone: "555-987-6543", status: "Active", joinDate: "2023-02-20" },
{ id: 3, name: "Robert Johnson", email: "robert@example.com", phone: "555-456-7890", status: "Inactive", joinDate: "2022-11-05" },
{ id: 4, name: "Emily Davis", email: "emily@example.com", phone: "555-789-0123", status: "Pending", joinDate: "2023-03-10" },
{ id: 5, name: "Michael Wilson", email: "michael@example.com", phone: "555-234-5678", status: "Active", joinDate: "2023-01-30" },
{ id: 6, name: "Sarah Brown", email: "sarah@example.com", phone: "555-876-5432", status: "Banned", joinDate: "2022-12-15" },
{ id: 7, name: "David Taylor", email: "david@example.com", phone: "555-345-6789", status: "Active", joinDate: "2023-02-25" },
{ id: 8, name: "Jessica Martinez", email: "jessica@example.com", phone: "555-765-4321", status: "Pending", joinDate: "2023-03-05" },
{ id: 9, name: "Thomas Anderson", email: "thomas@example.com", phone: "555-456-1234", status: "Inactive", joinDate: "2022-10-20" },
{ id: 10, name: "Lisa Jackson", email: "lisa@example.com", phone: "555-654-3210", status: "Active", joinDate: "2023-01-10" },
{ id: 11, name: "William White", email: "william@example.com", phone: "555-567-8901", status: "Active", joinDate: "2023-02-15" },
{ id: 12, name: "Karen Harris", email: "karen@example.com", phone: "555-543-2109", status: "Banned", joinDate: "2022-11-30" },
{ id: 13, name: "Christopher Clark", email: "chris@example.com", phone: "555-678-9012", status: "Active", joinDate: "2023-03-01" },
{ id: 14, name: "Amanda Lewis", email: "amanda@example.com", phone: "555-432-1098", status: "Pending", joinDate: "2023-03-15" },
{ id: 15, name: "Matthew Robinson", email: "matt@example.com", phone: "555-789-1234", status: "Active", joinDate: "2023-01-20" },
{ id: 16, name: "Stephanie Walker", email: "stephanie@example.com", phone: "555-321-0987", status: "Inactive", joinDate: "2022-12-01" },
{ id: 17, name: "Daniel Young", email: "daniel@example.com", phone: "555-890-1234", status: "Active", joinDate: "2023-02-10" },
{ id: 18, name: "Nicole King", email: "nicole@example.com", phone: "555-210-9876", status: "Active", joinDate: "2023-01-05" },
{ id: 19, name: "Kevin Wright", email: "kevin@example.com", phone: "555-901-2345", status: "Banned", joinDate: "2022-11-15" },
{ id: 20, name: "Rachel Scott", email: "rachel@example.com", phone: "555-109-8765", status: "Active", joinDate: "2023-02-28" }
];
// DOM elements
const customerList = document.getElementById('customerList');
const searchInput = document.getElementById('searchInput');
const prevPageBtn = document.getElementById('prevPage');
const nextPageBtn = document.getElementById('nextPage');
const addCustomerBtn = document.getElementById('addCustomerBtn');
const customerModal = document.getElementById('customerModal');
const confirmModal = document.getElementById('confirmModal');
const closeModalBtn = document.getElementById('closeModal');
const cancelModalBtn = document.getElementById('cancelModal');
const saveCustomerBtn = document.getElementById('saveCustomer');
const confirmDeleteBtn = document.getElementById('confirmDelete');
const cancelDeleteBtn = document.getElementById('cancelDelete');
const customerForm = document.getElementById('customerForm');
const modalTitle = document.getElementById('modalTitle');
const customerIdInput = document.getElementById('customerId');
const emptyState = document.getElementById('emptyState');
const resetFiltersBtn = document.getElementById('resetFiltersBtn');
const statusFilters = document.querySelectorAll('input[name="status-filter"]');
// Table state
let currentPage = 1;
const itemsPerPage = 9;
let filteredCustomers = [...customers];
let customerToDelete = null;
let currentStatusFilter = 'all';
// Initialize the table
function initTable() {
renderCustomerCards();
setupEventListeners();
updatePagination();
}
// Render customer cards
function renderCustomerCards() {
customerList.innerHTML = '';
// Get customers for current page
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const paginatedCustomers = filteredCustomers.slice(startIndex, endIndex);
// Show empty state if no customers
if (filteredCustomers.length === 0) {
emptyState.classList.remove('hidden');
customerList.classList.add('hidden');
} else {
emptyState.classList.add('hidden');
customerList.classList.remove('hidden');
}
// Render cards
paginatedCustomers.forEach(customer => {
const card = document.createElement('div');
card.className = 'customer-card bg-white p-5 fade-in';
card.innerHTML = `
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-lg font-semibold text-gray-900">${customer.name}</h3>
<p class="text-sm text-gray-500">ID: ${customer.id}</p>
</div>
<span class="status-badge ${
customer.status === 'Active' ? 'bg-green-100 text-green-800' :
customer.status === 'Inactive' ? 'bg-gray-100 text-gray-800' :
customer.status === 'Pending' ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800'
}">
${customer.status}
</span>
</div>
<div class="space-y-2 mb-5">
<div class="flex items-center text-sm text-gray-600">
<i class="fas fa-envelope mr-2 text-gray-400"></i>
<span>${customer.email}</span>
</div>
<div class="flex items-center text-sm text-gray-600">
<i class="fas fa-phone mr-2 text-gray-400"></i>
<span>${customer.phone}</span>
</div>
<div class="flex items-center text-sm text-gray-600">
<i class="fas fa-calendar-day mr-2 text-gray-400"></i>
<span>Joined ${formatDate(customer.joinDate)}</span>
</div>
</div>
<div class="flex justify-end space-x-2">
<button class="action-button w-10 h-10 flex items-center justify-center text-blue-500 edit-btn" data-id="${customer.id}">
<i class="fas fa-pencil-alt"></i>
</button>
<button class="action-button w-10 h-10 flex items-center justify-center text-red-500 delete-btn" data-id="${customer.id}">
<i class="fas fa-trash-alt"></i>
</button>
</div>
`;
customerList.appendChild(card);
});
// Update pagination buttons
prevPageBtn.disabled = currentPage === 1;
nextPageBtn.disabled = endIndex >= filteredCustomers.length;
}
// Format date for display
function formatDate(dateString) {
const options = { year: 'numeric', month: 'short', day: 'numeric' };
return new Date(dateString).toLocaleDateString(undefined, options);
}
// Update pagination controls
function updatePagination() {
const pageNumbers = document.querySelector('.flex.gap-1');
pageNumbers.innerHTML = '';
const totalPages = Math.ceil(filteredCustomers.length / itemsPerPage);
const maxVisiblePages = 5;
let startPage, endPage;
if (totalPages <= maxVisiblePages) {
startPage = 1;
endPage = totalPages;
} else {
const maxPagesBeforeCurrent = Math.floor(maxVisiblePages / 2);
const maxPagesAfterCurrent = Math.ceil(maxVisiblePages / 2) - 1;
if (currentPage <= maxPagesBeforeCurrent) {
startPage = 1;
endPage = maxVisiblePages;
} else if (currentPage + maxPagesAfterCurrent >= totalPages) {
startPage = totalPages - maxVisiblePages + 1;
endPage = totalPages;
} else {
startPage = currentPage - maxPagesBeforeCurrent;
endPage = currentPage + maxPagesAfterCurrent;
}
}
// Previous ellipsis
if (startPage > 1) {
const ellipsis = document.createElement('span');
ellipsis.className = 'w-10 h-10 flex items-center justify-center text-gray-500';
ellipsis.textContent = '...';
pageNumbers.appendChild(ellipsis);
}
// Page numbers
for (let i = startPage; i <= endPage; i++) {
const pageBtn = document.createElement('button');
pageBtn.className = `w-10 h-10 flex items-center justify-center rounded-full ${
i === currentPage ? 'bg-blue-500 text-white' : 'text-gray-700 hover:bg-gray-100'
}`;
pageBtn.textContent = i;
pageBtn.addEventListener('click', () => {
currentPage = i;
renderCustomerCards();
});
pageNumbers.appendChild(pageBtn);
}
// Next ellipsis
if (endPage < totalPages) {
const ellipsis = document.createElement('span');
ellipsis.className = 'w-10 h-10 flex items-center justify-center text-gray-500';
ellipsis.textContent = '...';
pageNumbers.appendChild(ellipsis);
}
}
// Filter customers based on search input and status
function filterCustomers() {
const searchTerm = searchInput.value.toLowerCase();
filteredCustomers = customers.filter(customer => {
const matchesSearch = !searchTerm ||
customer.name.toLowerCase().includes(searchTerm) ||
customer.email.toLowerCase().includes(searchTerm) ||
customer.phone.includes(searchTerm);
const matchesStatus = currentStatusFilter === 'all' ||
customer.status === currentStatusFilter;
return matchesSearch && matchesStatus;
});
currentPage = 1;
renderCustomerCards();
updatePagination();
}
// Open modal for adding/editing customer
function openCustomerModal(customer = null) {
if (customer) {
modalTitle.textContent = 'Edit Customer';
customerIdInput.value = customer.id;
document.getElementById('name').value = customer.name;
document.getElementById('email').value = customer.email;
document.getElementById('phone').value = customer.phone;
document.getElementById('status').value = customer.status;
document.getElementById('joinDate').value = customer.joinDate;
} else {
modalTitle.textContent = 'New Customer';
customerForm.reset();
customerIdInput.value = '';
document.getElementById('joinDate').value = new Date().toISOString().split('T')[0];
document.getElementById('status').value = 'Active';
}
customerModal.classList.remove('hidden');
}
// Save customer (add or update)
function saveCustomer() {
const id = customerIdInput.value;
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const phone = document.getElementById('phone').value;
const status = document.getElementById('status').value;
const joinDate = document.getElementById('joinDate').value;
if (!name || !email || !phone || !status || !joinDate) {
alert('Please fill in all fields');
return;
}
if (id) {
// Update existing customer
const index = customers.findIndex(c => c.id == id);
if (index !== -1) {
customers[index] = { id: parseInt(id), name, email, phone, status, joinDate };
}
} else {
// Add new customer
const newId = Math.max(...customers.map(c => c.id)) + 1;
customers.push({ id: newId, name, email, phone, status, joinDate });
}
customerModal.classList.add('hidden');
filterCustomers();
}
// Delete customer
function deleteCustomer() {
if (!customerToDelete) return;
const index = customers.findIndex(c => c.id == customerToDelete);
if (index !== -1) {
customers.splice(index, 1);
}
confirmModal.classList.add('hidden');
customerToDelete = null;
filterCustomers();
}
// Setup event listeners
function setupEventListeners() {
// Search input
searchInput.addEventListener('input', filterCustomers);
// Pagination buttons
prevPageBtn.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
renderCustomerCards();
}
});
nextPageBtn.addEventListener('click', () => {
const totalPages = Math.ceil(filteredCustomers.length / itemsPerPage);
if (currentPage < totalPages) {
currentPage++;
renderCustomerCards();
}
});
// Status filter
statusFilters.forEach(filter => {
filter.addEventListener('change', () => {
currentStatusFilter = filter.value;
filterCustomers();
});
});
// Add customer button
addCustomerBtn.addEventListener('click', () => openCustomerModal());
// Edit and delete buttons (delegated)
customerList.addEventListener('click', (e) => {
if (e.target.classList.contains('edit-btn') || e.target.closest('.edit-btn')) {
const btn = e.target.classList.contains('edit-btn') ? e.target : e.target.closest('.edit-btn');
const customerId = parseInt(btn.dataset.id);
const customer = customers.find(c => c.id === customerId);
if (customer) openCustomerModal(customer);
}
if (e.target.classList.contains('delete-btn') || e.target.closest('.delete-btn')) {
const btn = e.target.classList.contains('delete-btn') ? e.target : e.target.closest('.delete-btn');
customerToDelete = parseInt(btn.dataset.id);
confirmModal.classList.remove('hidden');
}
});
// Modal controls
closeModalBtn.addEventListener('click', () => customerModal.classList.add('hidden'));
cancelModalBtn.addEventListener('click', () => customerModal.classList.add('hidden'));
saveCustomerBtn.addEventListener('click', saveCustomer);
// Confirmation modal controls
confirmDeleteBtn.addEventListener('click', deleteCustomer);
cancelDeleteBtn.addEventListener('click', () => {
confirmModal.classList.add('hidden');
customerToDelete = null;
});
// Reset filters button
resetFiltersBtn.addEventListener('click', () => {
searchInput.value = '';
document.getElementById('filter-all').checked = true;
currentStatusFilter = 'all';
filterCustomers();
});
// Close modals when clicking outside
window.addEventListener('click', (e) => {
if (e.target === customerModal) customerModal.classList.add('hidden');
if (e.target === confirmModal) confirmModal.classList.add('hidden');
});
}
// Initialize the table when DOM is loaded
document.addEventListener('DOMContentLoaded', initTable);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nj22A/customer-database" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>