Add customer list filters and total count display
- Add search filters for customer name, email, and URL - Add filter for customers with email - Display total number of customers - Update API to handle filter parameters
This commit is contained in:
@ -39,6 +39,12 @@ export default function AdminCustomers() {
|
||||
const [page, setPage] = useState(initialPage);
|
||||
const [pageSize, setPageSize] = useState(initialPageSize);
|
||||
|
||||
// Search filters state
|
||||
const [nameFilter, setNameFilter] = useState('');
|
||||
const [emailFilter, setEmailFilter] = useState('');
|
||||
const [urlFilter, setUrlFilter] = useState('');
|
||||
const [hasEmailFilter, setHasEmailFilter] = useState(false);
|
||||
|
||||
// State for data
|
||||
const [customers, setCustomers] = useState<Customer[]>([]);
|
||||
const [pagination, setPagination] = useState<PaginationInfo>({
|
||||
@ -57,11 +63,16 @@ export default function AdminCustomers() {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Build query string with pagination
|
||||
// Build query string with pagination and filters
|
||||
const params = new URLSearchParams();
|
||||
params.append('page', initialPage.toString());
|
||||
params.append('pageSize', initialPageSize.toString());
|
||||
|
||||
if (nameFilter) params.append('name', nameFilter);
|
||||
if (emailFilter) params.append('email', emailFilter);
|
||||
if (urlFilter) params.append('url', urlFilter);
|
||||
if (hasEmailFilter) params.append('hasEmail', 'true');
|
||||
|
||||
const response = await fetch(`/api/customers?${params.toString()}`);
|
||||
|
||||
if (!response.ok) {
|
||||
@ -80,7 +91,7 @@ export default function AdminCustomers() {
|
||||
};
|
||||
|
||||
fetchCustomers();
|
||||
}, [initialPage, initialPageSize]);
|
||||
}, [initialPage, initialPageSize, nameFilter, emailFilter, urlFilter, hasEmailFilter]);
|
||||
|
||||
// Handle page change
|
||||
const handlePageChange = (newPage: number) => {
|
||||
@ -96,8 +107,15 @@ export default function AdminCustomers() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-semibold text-gray-900 dark:text-gray-100">Customers</h1>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold text-gray-900 dark:text-gray-100">Customers</h1>
|
||||
{!isLoading && (
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
Total: {pagination.totalCount} customer{pagination.totalCount !== 1 ? 's' : ''}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Link
|
||||
href="/admin/customers/new"
|
||||
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
@ -106,6 +124,66 @@ export default function AdminCustomers() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Search Filters */}
|
||||
<div className="mb-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div>
|
||||
<label htmlFor="name-filter" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Search by Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name-filter"
|
||||
value={nameFilter}
|
||||
onChange={(e) => setNameFilter(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white sm:text-sm"
|
||||
placeholder="Enter name..."
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email-filter" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Search by Email
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="email-filter"
|
||||
value={emailFilter}
|
||||
onChange={(e) => setEmailFilter(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white sm:text-sm"
|
||||
placeholder="Enter email..."
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="url-filter" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Search by URL
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="url-filter"
|
||||
value={urlFilter}
|
||||
onChange={(e) => setUrlFilter(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white sm:text-sm"
|
||||
placeholder="Enter URL..."
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
Email Filter
|
||||
</label>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="has-email"
|
||||
checked={hasEmailFilter}
|
||||
onChange={(e) => setHasEmailFilter(e.target.checked)}
|
||||
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"
|
||||
/>
|
||||
<label htmlFor="has-email" className="ml-2 block text-sm text-gray-700 dark:text-gray-300">
|
||||
Has Email
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error Message */}
|
||||
{error && (
|
||||
<div className="bg-red-50 border-l-4 border-red-400 p-4 my-4 dark:bg-red-900/20 dark:border-red-500">
|
||||
|
||||
Reference in New Issue
Block a user