Skip to main content

useFilterSortHandler Hook

useFilterSortHandler adalah custom React hook yang powerful untuk mengelola filter dan sort conditions secara otomatis dengan support untuk URL encoding, date formatting, dan API integration.

📋 Overview

🎯 Fungsi Utama

  • Universal Filter Handler: Support berbagai jenis filter dengan operator-based conditions
  • Sort Management: Handle sorting untuk Mantine DataTable dan custom sort conditions
  • Pagination Control: Built-in pagination state management
  • Auto-processing: Otomatis convert filter objects ke query strings
  • URL Encoding: Support untuk URL-safe parameter encoding
  • Date Formatting: Otomatis format date values ke standard format
  • Callback Support: Integrasi dengan API calls untuk real-time updates

🔄 Alur Kerja

🚀 Quick Start

Basic Usage

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';
import type { DataTableSortStatus } from 'mantine-datatable';

function ProductList() {
const {
filter, // Processed filter string
sort, // Processed sort string
page, // Current page
perPage, // Items per page
setFilters, // Update filters
setSortStatus, // Update sort
setPage, // Update page
setPerPage, // Update per page
clearFilters, // Clear all filters
hasActiveFilters // Check if any filters active
} = useFilterSortHandler(
// Initial filters
{
search: '',
category: '',
status: 'active',
price_min: undefined,
price_max: undefined
},
// Initial sort
{ columnAccessor: 'name', direction: 'asc' }
);

// Use in API call
const { data, loading } = useQuery(['products', filter, sort, page, perPage], () =>
fetchProducts({ filter, sort, page, perPage })
);

return (
<div>
<SearchInput value={filters.search} onChange={(value) => setFilters({ search: value })} />
<CategorySelect value={filters.category} onChange={(value) => setFilters({ category: value })} />

<DataTable
records={data?.products}
sortStatus={sortStatus}
onSortStatusChange={setSortStatus}
page={page}
onPageChange={setPage}
recordsPerPage={perPage}
onRecordsPerPageChange={setPerPage}
totalRecords={data?.total}
/>
</div>
);
}

Advanced Configuration

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';

function AdvancedUserManagement() {
const {
filter,
sort,
page,
perPage,
filters,
sortStatus,
setFilters,
setSortStatus,
updateFilter,
removeFilter,
resetToDefaults,
hasActiveFilters,
hasActiveSort
} = useFilterSortHandler(
// Initial filters
{
search: '',
role: 'user',
department: undefined,
created_after: undefined,
created_before: undefined,
is_active: true
},
// Initial sort
{ columnAccessor: 'created_at', direction: 'desc' },
// Options
{
encodeFilters: true,
encodeSort: true,
formatDateValues: true,
autoRefetch: false,
onFiltersChange: (filters, processedFilter) => {
console.log('Filters changed:', filters);
console.log('Processed filter:', processedFilter);

// Trigger API call
fetchUsers({
filter: processedFilter,
sort: sort,
page: page,
perPage: perPage
});
},
onSortChange: (sort, processedSort) => {
console.log('Sort changed:', sort);
console.log('Processed sort:', processedSort);
}
}
);

return (
<div>
<div style={{ marginBottom: '20px' }}>
<input
placeholder="Search users..."
value={filters.search}
onChange={(e) => updateFilter('search', e.target.value)}
/>

<select
value={filters.role}
onChange={(e) => updateFilter('role', e.target.value)}
>
<option value="">All Roles</option>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>

<DatePicker
placeholder="Created after"
value={filters.created_after}
onChange={(date) => updateFilter('created_after', date)}
/>

{hasActiveFilters && (
<button onClick={clearFilters}>Clear Filters</button>
)}
</div>

<UserTable
users={users}
sortStatus={sortStatus}
onSortStatusChange={setSortStatus}
/>
</div>
);
}

📝 API Reference

Hook Interface

interface UseFilterSortHandlerOptions extends FilterSortOptions {
autoRefetch?: boolean;
onFiltersChange?: (filters: Record<string, unknown>, processedFilters: string) => void;
onSortChange?: (sort: DataTableSortStatus | SortCondition | null, processedSort: string) => void;
encodeFilters?: boolean; // URL encoding filters
encodeSort?: boolean; // URL encoding sort
formatDateValues?: boolean; // Format date values to YYYY-MM-DD
}

interface UseFilterSortHandlerReturn<TFilters> {
// Processed query parameters
filter?: string;
sort?: string;
page: number;
perPage: number;

// State setters
setPage: (page: number) => void;
setPerPage: (perPage: number) => void;
setFilters: (values: Partial<TFilters>) => void;
setSortStatus: (sort: DataTableSortStatus | SortCondition | null) => void;

// Internal state
filters: TFilters;
sortStatus: DataTableSortStatus | SortCondition | null;

// Actions
updateFilter: (key: string, value: unknown) => void;
removeFilter: (key: string) => void;
clearFilters: () => void;
resetToDefaults: () => void;

// Utilities
hasActiveFilters: boolean;
hasActiveSort: boolean;
filterValues: Record<string, unknown>; // Simple values for forms
}

function useFilterSortHandler<TFilters extends Record<string, unknown>>(
initialFilters: TFilters,
initialSort?: DataTableSortStatus | SortCondition | null,
options?: UseFilterSortHandlerOptions
): UseFilterSortHandlerReturn<TFilters>;

Parameters

ParameterTypeDefaultDescription
initialFiltersTFilters-Required. Initial filter state object
initialSortDataTableSortStatus | SortCondition | nullnullInitial sort condition
optionsUseFilterSortHandlerOptions{}Additional configuration options

Return Values

PropertyTypeDescription
filterstring | undefinedProcessed filter string for API calls
sortstring | undefinedProcessed sort string for API calls
pagenumberCurrent pagination page (starts from 1)
perPagenumberItems per page (default: 10)
filtersTFiltersCurrent filter state object
sortStatusDataTableSortStatus | SortCondition | nullCurrent sort state
setFiltersfunctionUpdate multiple filters at once
setSortStatusfunctionUpdate sort status
setPagefunctionUpdate current page
setPerPagefunctionUpdate items per page
updateFilterfunctionUpdate single filter value
removeFilterfunctionRemove specific filter
clearFiltersfunctionReset all filters to initial values
resetToDefaultsfunctionReset both filters and sort to defaults
hasActiveFiltersbooleanTrue if any non-default filters are active
hasActiveSortbooleanTrue if any sort is active
filterValuesRecord<string, unknown>Filter values for form inputs

🎨 Examples

E-commerce Product Filtering

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';

function ProductCatalog() {
const {
filter,
sort,
page,
perPage,
filters,
setFilters,
setSortStatus,
setPage,
setPerPage,
updateFilter,
hasActiveFilters
} = useFilterSortHandler(
{
search: '',
category_id: undefined,
brand_id: undefined,
min_price: undefined,
max_price: undefined,
in_stock: undefined,
rating: undefined
},
{ columnAccessor: 'created_at', direction: 'desc' },
{
encodeFilters: true,
formatDateValues: false,
onFiltersChange: (newFilters, processedFilter) => {
// Update URL without page reload
const url = new URL(window.location);
url.searchParams.set('filter', processedFilter);
window.history.pushState({}, '', url);
}
}
);

// API integration
const { data: productsData, isLoading } = useQuery(
['products', filter, sort, page, perPage],
() => api.products.list({ filter, sort, page, perPage }),
{ keepPreviousData: true }
);

return (
<div>
{/* Filters Sidebar */}
<aside>
<SearchFilter
value={filters.search}
onChange={(value) => updateFilter('search', value)}
/>

<CategoryFilter
value={filters.category_id}
onChange={(value) => updateFilter('category_id', value)}
/>

<PriceRangeFilter
min={filters.min_price}
max={filters.max_price}
onMinChange={(value) => updateFilter('min_price', value)}
onMaxChange={(value) => updateFilter('max_price', value)}
/>

{hasActiveFilters && (
<Button onClick={() => {
clearFilters();
setPage(1); // Reset to first page
}}>
Clear All Filters
</Button>
)}
</aside>

{/* Product Grid */}
<main>
<SortControls
sortStatus={sortStatus}
onSortChange={setSortStatus}
/>

<ProductGrid
products={productsData?.products}
loading={isLoading}
/>

<Pagination
page={page}
onChange={setPage}
total={productsData?.total || 0}
perPage={perPage}
onPerPageChange={setPerPage}
/>
</main>
</div>
);
}

User Management with Complex Filters

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';
import { DateRangePicker } from '@mantine/dates';

function UserManagement() {
const {
filter,
sort,
page,
perPage,
filters,
sortStatus,
updateFilter,
removeFilter,
setPage,
setSortStatus,
hasActiveFilters,
hasActiveSort
} = useFilterSortHandler(
{
// Text filters
search: '',
email: '',

// Select filters
role: undefined,
department: undefined,
status: undefined,

// Date filters
created_after: undefined,
created_before: undefined,
last_login_after: undefined,

// Boolean filters
is_active: undefined,
email_verified: undefined,

// Array filters
skills: [],
permissions: []
},
{ columnAccessor: 'created_at', direction: 'desc' },
{
encodeFilters: true,
formatDateValues: true,
onFiltersChange: async (filters, processedFilter) => {
// Debounced API call
await new Promise(resolve => setTimeout(resolve, 300));

try {
await fetchUsers({
filter: processedFilter,
sort: sort,
page: 1, // Always reset to first page on filter change
perPage: perPage
});
setPage(1);
} catch (error) {
console.error('Failed to fetch users:', error);
}
}
}
);

return (
<div>
<div className="filters-section">
{/* Search Fields */}
<Group>
<TextInput
placeholder="Search name or username"
value={filters.search}
onChange={(e) => updateFilter('search', e.target.value)}
/>

<TextInput
placeholder="Email address"
value={filters.email}
onChange={(e) => updateFilter('email', e.target.value)}
/>
</Group>

{/* Select Filters */}
<Group>
<Select
placeholder="Role"
value={filters.role}
onChange={(value) => updateFilter('role', value)}
data={[
{ value: 'admin', label: 'Admin' },
{ value: 'user', label: 'User' },
{ value: 'moderator', label: 'Moderator' }
]}
/>

<Select
placeholder="Department"
value={filters.department}
onChange={(value) => updateFilter('department', value)}
data={departmentOptions}
/>
</Group>

{/* Date Range Filters */}
<DateRangePicker
placeholder="Registration date range"
value={[
filters.created_after ? new Date(filters.created_after) : null,
filters.created_before ? new Date(filters.created_before) : null
]}
onChange={(dates) => {
updateFilter('created_after', dates[0]);
updateFilter('created_before', dates[1]);
}}
/>

{/* Boolean Filters */}
<Group>
<Checkbox
label="Active users only"
checked={filters.is_active}
onChange={(e) => updateFilter('is_active', e.target.checked)}
/>

<Checkbox
label="Email verified"
checked={filters.email_verified}
onChange={(e) => updateFilter('email_verified', e.target.checked)}
/>
</Group>

{/* Filter Controls */}
{hasActiveFilters && (
<Group>
<Badge variant="light">
{Object.entries(filters).filter(([_, value]) =>
value !== undefined && value !== '' && value !== false
).length} filters active
</Badge>

<Button size="sm" onClick={() => {
clearFilters();
setPage(1);
}}>
Clear Filters
</Button>
</Group>
)}
</div>

{/* Users Table */}
<DataTable
records={users}
columns={[
{ accessor: 'name', sortable: true },
{ accessor: 'email', sortable: true },
{ accessor: 'role', sortable: true },
{ accessor: 'department', sortable: true },
{ accessor: 'created_at', sortable: true },
{ accessor: 'last_login', sortable: true }
]}
sortStatus={sortStatus}
onSortStatusChange={setSortStatus}
page={page}
onPageChange={setPage}
recordsPerPage={perPage}
onRecordsPerPageChange={setPerPage}
totalRecords={totalUsers}
/>

{/* Active Sort Indicator */}
{hasActiveSort && (
<Group mt="md">
<Badge>
Sorted by: {sortStatus?.columnAccessor} ({sortStatus?.direction})
</Badge>
<Button
size="xs"
variant="outline"
onClick={() => setSortStatus(null)}
>
Clear Sort
</Button>
</Group>
)}
</div>
);
}

Report Dashboard with Date Filtering

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';

function SalesReport() {
const {
filter,
sort,
page,
perPage,
filters,
setFilters,
setPage,
setSortStatus,
updateFilter,
hasActiveFilters
} = useFilterSortHandler(
{
date_from: dayjs().startOf('month').format('YYYY-MM-DD'),
date_to: dayjs().format('YYYY-MM-DD'),
sales_rep_id: undefined,
region_id: undefined,
product_category: undefined,
min_amount: undefined,
max_amount: undefined
},
{ columnAccessor: 'created_at', direction: 'desc' },
{
formatDateValues: true,
encodeFilters: true,
onFiltersChange: (filters, processedFilter) => {
// Track filter changes for analytics
analytics.track('report_filters_changed', {
filters: filters,
processedFilter: processedFilter
});
}
}
);

// Real-time data fetching
const { data: reportData, isLoading } = useQuery(
['sales-report', filter, sort, page, perPage],
() => api.reports.sales({ filter, sort, page, perPage }),
{
refetchInterval: 30000, // Refresh every 30 seconds
keepPreviousData: true
}
);

return (
<div>
<h1>Sales Report</h1>

{/* Report Filters */}
<Card shadow="sm" mb="lg">
<DateRangePicker
label="Date Range"
value={[
filters.date_from ? new Date(filters.date_from) : null,
filters.date_to ? new Date(filters.date_to) : null
]}
onChange={(dates) => {
updateFilter('date_from', dates[0]);
updateFilter('date_to', dates[1]);
setPage(1); // Reset to first page
}}
/>

<Select
label="Sales Representative"
value={filters.sales_rep_id}
onChange={(value) => updateFilter('sales_rep_id', value)}
data={salesReps}
/>

<NumberInput
label="Minimum Amount"
value={filters.min_amount}
onChange={(value) => updateFilter('min_amount', value)}
min={0}
/>

<NumberInput
label="Maximum Amount"
value={filters.max_amount}
onChange={(value) => updateFilter('max_amount', value)}
min={0}
/>
</Card>

{/* Summary Cards */}
<Group mb="lg">
<Card>
<Text size="sm" color="dimmed">Total Sales</Text>
<Text size="xl" weight="bold">
${reportData?.summary.total_sales?.toLocaleString()}
</Text>
</Card>

<Card>
<Text size="sm" color="dimmed">Total Orders</Text>
<Text size="xl" weight="bold">
{reportData?.summary.total_orders?.toLocaleString()}
</Text>
</Card>
</Group>

{/* Data Table */}
<DataTable
records={reportData?.sales}
columns={[
{ accessor: 'order_id', sortable: true },
{ accessor: 'customer_name', sortable: true },
{ accessor: 'sales_rep', sortable: true },
{ accessor: 'amount', sortable: true },
{ accessor: 'created_at', sortable: true }
]}
sortStatus={sortStatus}
onSortStatusChange={setSortStatus}
page={page}
onPageChange={setPage}
recordsPerPage={perPage}
onRecordsPerPageChange={setPerPage}
totalRecords={reportData?.total}
loading={isLoading}
/>
</div>
);
}

🔧 Advanced Configuration

Custom Filter Processing

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';

function CustomFilterHandler() {
const { filter, setFilters } = useFilterSortHandler(
{
search: '',
category: undefined,
tags: [], // Array filter
custom_field: undefined
},
null,
{
encodeFilters: true,
onFiltersChange: (filters, processedFilter) => {
// Custom processing before API call
const customProcessed = processSpecialFilters(filters);

// Call API with custom processed filters
api.getData({ filter: customProcessed });
}
}
);

const processSpecialFilters = (filters) => {
const processed = { ...filters };

// Handle array filters (tags)
if (filters.tags?.length > 0) {
processed.tags = filters.tags.join(','); // Convert array to comma-separated string
}

// Handle custom field transformation
if (filters.custom_field) {
processed.custom_field = transformCustomValue(filters.custom_field);
}

return processed;
};

return (
<div>
<TagsInput
value={filters.tags}
onChange={(tags) => setFilters({ tags })}
/>
</div>
);
}

Integration with React Query

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';
import { useQuery, useMutation } from '@tanstack/react-query';

function QueryIntegratedComponent() {
const {
filter,
sort,
page,
perPage,
filters,
setFilters,
setPage,
setPerPage,
setSortStatus,
clearFilters
} = useFilterSortHandler(
{ search: '', status: undefined },
{ columnAccessor: 'created_at', direction: 'desc' },
{
autoRefetch: true,
onFiltersChange: (filters, processedFilter) => {
// Invalidate and refetch query when filters change
queryClient.invalidateQueries(['data']);
}
}
);

// Query with automatic refetch
const { data, isLoading, error } = useQuery(
['data', filter, sort, page, perPage],
() => fetchData({ filter, sort, page, perPage }),
{
keepPreviousData: true,
staleTime: 5000
}
);

// Mutation with filter reset
const createMutation = useMutation(createItem, {
onSuccess: () => {
// Clear filters and reset to first page after successful creation
clearFilters();
setPage(1);
queryClient.invalidateQueries(['data']);
}
});

return (
<div>
{/* Filters and controls */}
<input
value={filters.search}
onChange={(e) => setFilters({ search: e.target.value })}
/>

{/* Data display */}
{isLoading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <DataList items={data.items} />}
</div>
);
}

Debounced Filter Updates

import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler';
import { useCallback } from 'react';

function DebouncedFilterComponent() {
const {
filter,
filters,
setFilters,
updateFilter,
hasActiveFilters
} = useFilterSortHandler(
{ search: '', category: undefined },
null,
{
onFiltersChange: useCallback(
debounce((filters, processedFilter) => {
// Debounced API call
api.fetchData({ filter: processedFilter });
}, 500),
[]
)
}
);

return (
<div>
<TextInput
placeholder="Search..."
value={filters.search}
onChange={(e) => updateFilter('search', e.target.value)}
/>

{/* Display will update immediately but API call is debounced */}
<div>Current filter: {filter}</div>
</div>
);
}

// Debounce utility
function debounce(func, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(null, args), delay);
};
}

🎯 Filter Operators Support

Supported Operators

Hook ini support berbagai operator untuk advanced filtering:

// Equality operators
{ name: 'John' } // equals
{ name: { eq: 'John' } } // explicit equals
{ name: { ne: 'John' } } // not equals

// Comparison operators
{ age: { gt: 18 } } // greater than
{ age: { gte: 18 } } // greater than or equal
{ age: { lt: 65 } } // less than
{ age: { lte: 65 } } // less than or equal

// String operators
{ name: { contains: 'John' } } // contains substring
{ name: { startswith: 'J' } } // starts with
{ name: { endswith: 'n' } } // ends with

// Array operators
{ tags: { in: ['tag1', 'tag2'] } } // in array
{ tags: { nin: ['tag1', 'tag2'] } } // not in array

// Null operators
{ category: { isnull: true } } // is null
{ category: { notnull: true } } // is not null

// Boolean operators
{ active: true } // boolean true
{ archived: false } // boolean false

// Date operators (with date formatting)
{ created_at: { gt: '2024-01-01' } }
{ created_at: { lte: '2024-12-31' } }

Complex Filter Examples

function ComplexFilters() {
const { setFilters } = useFilterSortHandler(
{
// Text search with contains
search: { contains: 'john' },

// Date range
created_at: {
gte: '2024-01-01',
lte: '2024-12-31'
},

// Numeric range
price: {
gte: 100,
lte: 1000
},

// Multiple categories
category: { in: ['electronics', 'books'] },

// Exclude archived items
archived: { ne: true },

// Complex nested filters
or: [
{ featured: true },
{ rating: { gte: 4.5 } }
]
}
);

return <div>Complex filter example</div>;
}

🔧 Dependencies

Required Dependencies

npm install mantine-datatable dayjs

Internal Dependencies

  • @/utils/filter-sort-handler - Core filter and sort processing
  • @/utils/filter-handler - Individual filter processing
  • @/utils/sort-handler - Sort condition processing

Type Dependencies

import type { DataTableSortStatus } from 'mantine-datatable';

interface SortCondition {
key: string;
direction: 'asc' | 'desc';
}

🚨 Error Handling

Error Recovery

function ErrorHandlingFilters() {
const [error, setError] = useState(null);

const {
filter,
setFilters,
clearFilters
} = useFilterSortHandler(
{ search: '', category: undefined },
null,
{
onFiltersChange: async (filters, processedFilter) => {
try {
setError(null);
await fetchData({ filter: processedFilter });
} catch (err) {
setError(err.message);
// Optionally revert filters
// clearFilters();
}
}
}
);

return (
<div>
{error && (
<Alert color="red" title="Filter Error">
{error}
<Button size="sm" onClick={clearFilters}>
Reset Filters
</Button>
</Alert>
)}

{/* Filter inputs */}
</div>
);
}

Validation

function ValidatedFilters() {
const {
filter,
filters,
setFilters,
updateFilter
} = useFilterSortHandler(
{
email: '',
age: undefined,
date_range: undefined
},
null,
{
onFiltersChange: (filters, processedFilter) => {
// Validate filters before processing
if (filters.email && !isValidEmail(filters.email)) {
throw new Error('Invalid email format');
}

if (filters.age && (filters.age < 18 || filters.age > 100)) {
throw new Error('Age must be between 18 and 100');
}

// Process valid filters
fetchData({ filter: processedFilter });
}
}
);

const isValidEmail = (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};

return (
<div>
<TextInput
label="Email"
value={filters.email}
onChange={(e) => updateFilter('email', e.target.value)}
error={filters.email && !isValidEmail(filters.email) ? 'Invalid email' : null}
/>

<NumberInput
label="Age"
value={filters.age}
onChange={(value) => updateFilter('age', value)}
min={18}
max={100}
/>
</div>
);
}

📚 Best Practices

1. Performance Optimization

// Use useCallback for filter change handlers
const handleFilterChange = useCallback((key, value) => {
updateFilter(key, value);
}, [updateFilter]);

// Debounce rapid filter changes
const debouncedUpdateFilter = useMemo(
() => debounce(updateFilter, 300),
[updateFilter]
);

2. Type Safety

// Define strict filter types
interface UserFilters {
search: string;
role?: 'admin' | 'user' | 'moderator';
department?: string;
created_after?: string;
is_active?: boolean;
}

function UserList() {
const { filters, setFilters } = useFilterSortHandler<UserFilters>(
{ search: '', role: undefined, department: undefined, created_after: undefined, is_active: undefined },
null,
{ formatDateValues: true }
);

// TypeScript will enforce correct types
setFilters({ role: 'admin' }); // Valid
// setFilters({ role: 'invalid' }); // TypeScript error
}

3. URL Synchronization

function URLSyncedFilters() {
const { filter, setFilters, setPage } = useFilterSortHandler(
getInitialFiltersFromURL(),
null,
{
onFiltersChange: (filters, processedFilter) => {
// Update URL without page reload
const url = new URL(window.location);

if (processedFilter) {
url.searchParams.set('filter', processedFilter);
} else {
url.searchParams.delete('filter');
}

url.searchParams.set('page', '1'); // Reset page

window.history.pushState({}, '', url);
}
}
);

// Listen to browser back/forward
useEffect(() => {
const handlePopState = () => {
const urlFilters = getFiltersFromURL();
setFilters(urlFilters);
};

window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}, [setFilters]);

return <div>URL-synced filters</div>;
}

🔍 Troubleshooting

Common Issues

Problem: Filters not updating UI Solution: Ensure you're using the returned filters state, not the initial filters

Problem: Sort not working with DataTable Solution: Use sortStatus and setSortStatus from the hook return values

Problem: Date filters not formatting correctly Solution: Set formatDateValues: true in options

Problem: API calls not triggering Solution: Implement onFiltersChange callback to handle API calls

Debug Mode

function DebugFilters() {
const {
filter,
sort,
filters,
sortStatus,
hasActiveFilters,
hasActiveSort
} = useFilterSortHandler(
{ search: '', category: undefined },
{ columnAccessor: 'name', direction: 'asc' },
{
onFiltersChange: (filters, processedFilter) => {
console.log('Raw filters:', filters);
console.log('Processed filter:', processedFilter);
},
onSortChange: (sort, processedSort) => {
console.log('Raw sort:', sort);
console.log('Processed sort:', processedSort);
}
}
);

return (
<div>
<div>Debug Info:</div>
<pre>Filter: {JSON.stringify(filter, null, 2)}</pre>
<pre>Sort: {JSON.stringify(sort, null, 2)}</pre>
<pre>Filters State: {JSON.stringify(filters, null, 2)}</pre>
<pre>Sort Status: {JSON.stringify(sortStatus, null, 2)}</pre>
<div>Has Active Filters: {hasActiveFilters.toString()}</div>
<div>Has Active Sort: {hasActiveSort.toString()}</div>
</div>
);
}