mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 16:51:13 -05:00
Refactor: Simplify architecture
This commit is contained in:
@@ -105,12 +105,12 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
<QueueFilters
|
<QueueFilters
|
||||||
activeEntityFilter={queueManager.filters.entityFilter}
|
activeEntityFilter={queueManager.filters.entityFilter}
|
||||||
activeStatusFilter={queueManager.filters.statusFilter}
|
activeStatusFilter={queueManager.filters.statusFilter}
|
||||||
sortConfig={queueManager.sort.config}
|
sortConfig={queueManager.filters.sortConfig}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
isLoading={queueManager.loadingState === 'loading'}
|
isLoading={queueManager.loadingState === 'loading'}
|
||||||
onEntityFilterChange={queueManager.filters.setEntityFilter}
|
onEntityFilterChange={queueManager.filters.setEntityFilter}
|
||||||
onStatusFilterChange={queueManager.filters.setStatusFilter}
|
onStatusFilterChange={queueManager.filters.setStatusFilter}
|
||||||
onSortChange={queueManager.sort.setConfig}
|
onSortChange={queueManager.filters.setSortConfig}
|
||||||
onClearFilters={queueManager.filters.clearFilters}
|
onClearFilters={queueManager.filters.clearFilters}
|
||||||
showClearButton={queueManager.filters.hasActiveFilters}
|
showClearButton={queueManager.filters.hasActiveFilters}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,14 +12,9 @@ export type { CachedProfile } from './useProfileCache';
|
|||||||
export { useModerationFilters } from './useModerationFilters';
|
export { useModerationFilters } from './useModerationFilters';
|
||||||
export type { ModerationFilters, ModerationFiltersConfig } from './useModerationFilters';
|
export type { ModerationFilters, ModerationFiltersConfig } from './useModerationFilters';
|
||||||
|
|
||||||
// Removed - sorting functionality deleted
|
|
||||||
|
|
||||||
export { usePagination } from './usePagination';
|
export { usePagination } from './usePagination';
|
||||||
export type { PaginationState, PaginationConfig } from './usePagination';
|
export type { PaginationState, PaginationConfig } from './usePagination';
|
||||||
|
|
||||||
export { useModerationSort } from './useModerationSort';
|
|
||||||
export type { UseModerationSortReturn } from './useModerationSort';
|
|
||||||
|
|
||||||
export { useRealtimeSubscriptions } from './useRealtimeSubscriptions';
|
export { useRealtimeSubscriptions } from './useRealtimeSubscriptions';
|
||||||
export type {
|
export type {
|
||||||
RealtimeSubscriptionConfig,
|
RealtimeSubscriptionConfig,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
import { useState, useCallback, useEffect, useMemo } from 'react';
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useDebounce } from '@/hooks/useDebounce';
|
import { useDebounce } from '@/hooks/useDebounce';
|
||||||
import type { EntityFilter, StatusFilter, QueueTab } from '@/types/moderation';
|
import type { EntityFilter, StatusFilter, QueueTab, SortConfig, SortField } from '@/types/moderation';
|
||||||
|
|
||||||
export interface ModerationFiltersConfig {
|
export interface ModerationFiltersConfig {
|
||||||
/** Initial entity filter */
|
/** Initial entity filter */
|
||||||
@@ -30,6 +30,9 @@ export interface ModerationFiltersConfig {
|
|||||||
|
|
||||||
/** localStorage key prefix for persistence */
|
/** localStorage key prefix for persistence */
|
||||||
storageKey?: string;
|
storageKey?: string;
|
||||||
|
|
||||||
|
/** Initial sort configuration */
|
||||||
|
initialSortConfig?: SortConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModerationFilters {
|
export interface ModerationFilters {
|
||||||
@@ -62,6 +65,24 @@ export interface ModerationFilters {
|
|||||||
|
|
||||||
/** Check if any non-default filters are active */
|
/** Check if any non-default filters are active */
|
||||||
hasActiveFilters: boolean;
|
hasActiveFilters: boolean;
|
||||||
|
|
||||||
|
/** Current sort configuration (immediate) */
|
||||||
|
sortConfig: SortConfig;
|
||||||
|
|
||||||
|
/** Debounced sort configuration (use this for queries) */
|
||||||
|
debouncedSortConfig: SortConfig;
|
||||||
|
|
||||||
|
/** Update the sort configuration */
|
||||||
|
setSortConfig: (config: SortConfig) => void;
|
||||||
|
|
||||||
|
/** Sort by a specific field, toggling direction if already sorting by that field */
|
||||||
|
sortBy: (field: SortField) => void;
|
||||||
|
|
||||||
|
/** Toggle the sort direction */
|
||||||
|
toggleSortDirection: () => void;
|
||||||
|
|
||||||
|
/** Reset sort to default */
|
||||||
|
resetSort: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +112,7 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
debounceDelay = 300,
|
debounceDelay = 300,
|
||||||
persist = true,
|
persist = true,
|
||||||
storageKey = 'moderationQueue_filters',
|
storageKey = 'moderationQueue_filters',
|
||||||
|
initialSortConfig = { field: 'created_at', direction: 'asc' },
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
// Load persisted filters on mount
|
// Load persisted filters on mount
|
||||||
@@ -109,6 +131,25 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
return null;
|
return null;
|
||||||
}, [persist, storageKey]);
|
}, [persist, storageKey]);
|
||||||
|
|
||||||
|
// Load persisted sort
|
||||||
|
const loadPersistedSort = useCallback((): SortConfig => {
|
||||||
|
if (!persist) return initialSortConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(`${storageKey}_sort`);
|
||||||
|
if (saved) {
|
||||||
|
const parsed = JSON.parse(saved);
|
||||||
|
if (parsed.field && parsed.direction) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load persisted sort:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialSortConfig;
|
||||||
|
}, [persist, storageKey, initialSortConfig]);
|
||||||
|
|
||||||
const persisted = loadPersistedFilters();
|
const persisted = loadPersistedFilters();
|
||||||
|
|
||||||
// Filter state
|
// Filter state
|
||||||
@@ -122,10 +163,16 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
persisted?.activeTab || initialTab
|
persisted?.activeTab || initialTab
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sort state
|
||||||
|
const [sortConfig, setSortConfigState] = useState<SortConfig>(loadPersistedSort);
|
||||||
|
|
||||||
// Debounced filters for API calls
|
// Debounced filters for API calls
|
||||||
const debouncedEntityFilter = useDebounce(entityFilter, debounceDelay);
|
const debouncedEntityFilter = useDebounce(entityFilter, debounceDelay);
|
||||||
const debouncedStatusFilter = useDebounce(statusFilter, debounceDelay);
|
const debouncedStatusFilter = useDebounce(statusFilter, debounceDelay);
|
||||||
|
|
||||||
|
// Debounced sort (0ms for immediate feedback)
|
||||||
|
const debouncedSortConfig = useDebounce(sortConfig, 0);
|
||||||
|
|
||||||
// Persist filters to localStorage
|
// Persist filters to localStorage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (persist) {
|
if (persist) {
|
||||||
@@ -144,6 +191,17 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
}
|
}
|
||||||
}, [entityFilter, statusFilter, activeTab, persist, storageKey]);
|
}, [entityFilter, statusFilter, activeTab, persist, storageKey]);
|
||||||
|
|
||||||
|
// Persist sort to localStorage
|
||||||
|
useEffect(() => {
|
||||||
|
if (persist) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(`${storageKey}_sort`, JSON.stringify(sortConfig));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to persist sort:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [sortConfig, persist, storageKey]);
|
||||||
|
|
||||||
// Set entity filter with logging
|
// Set entity filter with logging
|
||||||
const setEntityFilter = useCallback((filter: EntityFilter) => {
|
const setEntityFilter = useCallback((filter: EntityFilter) => {
|
||||||
console.log('🔍 Entity filter changed:', filter);
|
console.log('🔍 Entity filter changed:', filter);
|
||||||
@@ -162,19 +220,48 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
setActiveTabState(tab);
|
setActiveTabState(tab);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Sort callbacks
|
||||||
|
const setSortConfig = useCallback((config: SortConfig) => {
|
||||||
|
console.log('📝 [SORT] Sort config changed:', config);
|
||||||
|
setSortConfigState(config);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const sortBy = useCallback((field: SortField) => {
|
||||||
|
setSortConfigState(prev => ({
|
||||||
|
field,
|
||||||
|
direction: prev.field === field
|
||||||
|
? (prev.direction === 'asc' ? 'desc' : 'asc')
|
||||||
|
: 'asc'
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleSortDirection = useCallback(() => {
|
||||||
|
setSortConfigState(prev => ({
|
||||||
|
...prev,
|
||||||
|
direction: prev.direction === 'asc' ? 'desc' : 'asc'
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const resetSort = useCallback(() => {
|
||||||
|
setSortConfigState(initialSortConfig);
|
||||||
|
}, [initialSortConfig]);
|
||||||
|
|
||||||
// Clear all filters
|
// Clear all filters
|
||||||
const clearFilters = useCallback(() => {
|
const clearFilters = useCallback(() => {
|
||||||
console.log('🔍 Filters cleared');
|
console.log('🔍 Filters cleared');
|
||||||
setEntityFilterState(initialEntityFilter);
|
setEntityFilterState(initialEntityFilter);
|
||||||
setStatusFilterState(initialStatusFilter);
|
setStatusFilterState(initialStatusFilter);
|
||||||
setActiveTabState(initialTab);
|
setActiveTabState(initialTab);
|
||||||
}, [initialEntityFilter, initialStatusFilter, initialTab]);
|
setSortConfigState(initialSortConfig);
|
||||||
|
}, [initialEntityFilter, initialStatusFilter, initialTab, initialSortConfig]);
|
||||||
|
|
||||||
// Check if non-default filters are active
|
// Check if non-default filters are active
|
||||||
const hasActiveFilters =
|
const hasActiveFilters =
|
||||||
entityFilter !== initialEntityFilter ||
|
entityFilter !== initialEntityFilter ||
|
||||||
statusFilter !== initialStatusFilter ||
|
statusFilter !== initialStatusFilter ||
|
||||||
activeTab !== initialTab;
|
activeTab !== initialTab ||
|
||||||
|
sortConfig.field !== initialSortConfig.field ||
|
||||||
|
sortConfig.direction !== initialSortConfig.direction;
|
||||||
|
|
||||||
return useMemo(() => ({
|
return useMemo(() => ({
|
||||||
entityFilter,
|
entityFilter,
|
||||||
@@ -187,6 +274,12 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
setActiveTab,
|
setActiveTab,
|
||||||
clearFilters,
|
clearFilters,
|
||||||
hasActiveFilters,
|
hasActiveFilters,
|
||||||
|
sortConfig,
|
||||||
|
debouncedSortConfig,
|
||||||
|
setSortConfig,
|
||||||
|
sortBy,
|
||||||
|
toggleSortDirection,
|
||||||
|
resetSort,
|
||||||
}), [
|
}), [
|
||||||
entityFilter,
|
entityFilter,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
@@ -198,5 +291,11 @@ export function useModerationFilters(config: ModerationFiltersConfig = {}): Mode
|
|||||||
setActiveTab,
|
setActiveTab,
|
||||||
clearFilters,
|
clearFilters,
|
||||||
hasActiveFilters,
|
hasActiveFilters,
|
||||||
|
sortConfig,
|
||||||
|
debouncedSortConfig,
|
||||||
|
setSortConfig,
|
||||||
|
sortBy,
|
||||||
|
toggleSortDirection,
|
||||||
|
resetSort,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import {
|
|||||||
useProfileCache,
|
useProfileCache,
|
||||||
useModerationFilters,
|
useModerationFilters,
|
||||||
usePagination,
|
usePagination,
|
||||||
useModerationSort,
|
|
||||||
useRealtimeSubscriptions,
|
useRealtimeSubscriptions,
|
||||||
} from "./index";
|
} from "./index";
|
||||||
import { useModerationQueue } from "@/hooks/useModerationQueue";
|
import { useModerationQueue } from "@/hooks/useModerationQueue";
|
||||||
import { smartMergeArray } from "@/lib/smartStateUpdate";
|
|
||||||
import type { ModerationItem, EntityFilter, StatusFilter, LoadingState } from "@/types/moderation";
|
import type { ModerationItem, EntityFilter, StatusFilter, LoadingState } from "@/types/moderation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +42,6 @@ export interface ModerationQueueManager {
|
|||||||
// Sub-hooks (exposed for granular control)
|
// Sub-hooks (exposed for granular control)
|
||||||
filters: ReturnType<typeof useModerationFilters>;
|
filters: ReturnType<typeof useModerationFilters>;
|
||||||
pagination: ReturnType<typeof usePagination>;
|
pagination: ReturnType<typeof usePagination>;
|
||||||
sort: ReturnType<typeof useModerationSort>;
|
|
||||||
queue: ReturnType<typeof useModerationQueue>;
|
queue: ReturnType<typeof useModerationQueue>;
|
||||||
|
|
||||||
// Realtime
|
// Realtime
|
||||||
@@ -110,8 +108,6 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const sort = useModerationSort();
|
|
||||||
|
|
||||||
const queue = useModerationQueue();
|
const queue = useModerationQueue();
|
||||||
const entityCache = useEntityCache();
|
const entityCache = useEntityCache();
|
||||||
const profileCache = useProfileCache();
|
const profileCache = useProfileCache();
|
||||||
@@ -133,10 +129,10 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
|
|
||||||
const FETCH_COOLDOWN_MS = 1000;
|
const FETCH_COOLDOWN_MS = 1000;
|
||||||
|
|
||||||
// Store settings, filters, sort, and pagination in refs to stabilize fetchItems
|
// Store settings, filters, and pagination in refs to stabilize fetchItems
|
||||||
const settingsRef = useRef(settings);
|
const settingsRef = useRef(settings);
|
||||||
const filtersRef = useRef(filters);
|
const filtersRef = useRef(filters);
|
||||||
const sortRef = useRef(sort.debouncedConfig);
|
const sortRef = useRef(filters.debouncedSortConfig);
|
||||||
const paginationRef = useRef(pagination);
|
const paginationRef = useRef(pagination);
|
||||||
|
|
||||||
// Sync refs with state
|
// Sync refs with state
|
||||||
@@ -149,8 +145,8 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
}, [filters]);
|
}, [filters]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sortRef.current = sort.debouncedConfig;
|
sortRef.current = filters.debouncedSortConfig;
|
||||||
}, [sort.debouncedConfig]);
|
}, [filters.debouncedSortConfig]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
paginationRef.current = pagination;
|
paginationRef.current = pagination;
|
||||||
@@ -465,24 +461,9 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "replace":
|
case "replace":
|
||||||
const mergeResult = smartMergeArray(items, moderationItems, {
|
// Full replace - simple and predictable
|
||||||
compareFields: [
|
setItems(moderationItems);
|
||||||
"status",
|
console.log("🔄 Queue updated (replace mode)");
|
||||||
"content",
|
|
||||||
"reviewed_at",
|
|
||||||
"reviewed_by",
|
|
||||||
"reviewer_notes",
|
|
||||||
"assigned_to",
|
|
||||||
"locked_until",
|
|
||||||
],
|
|
||||||
preserveOrder: false,
|
|
||||||
addToTop: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (mergeResult.hasChanges) {
|
|
||||||
setItems(mergeResult.items);
|
|
||||||
console.log("🔄 Queue updated (replace mode)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentPreserveInteraction) {
|
if (!currentPreserveInteraction) {
|
||||||
setPendingNewItems([]);
|
setPendingNewItems([]);
|
||||||
@@ -515,14 +496,6 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
user,
|
user,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isSuperuser,
|
isSuperuser,
|
||||||
filters.debouncedEntityFilter,
|
|
||||||
filters.debouncedStatusFilter,
|
|
||||||
filters.activeTab,
|
|
||||||
pagination.setTotalCount,
|
|
||||||
pagination.startIndex,
|
|
||||||
pagination.endIndex,
|
|
||||||
sort.debouncedConfig.field,
|
|
||||||
sort.debouncedConfig.direction,
|
|
||||||
profileCache,
|
profileCache,
|
||||||
entityCache,
|
entityCache,
|
||||||
toast
|
toast
|
||||||
@@ -856,8 +829,8 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
console.log('🔄 [SORT/FILTER CHANGE] Detected change:', {
|
console.log('🔄 [SORT/FILTER CHANGE] Detected change:', {
|
||||||
entityFilter: filters.debouncedEntityFilter,
|
entityFilter: filters.debouncedEntityFilter,
|
||||||
statusFilter: filters.debouncedStatusFilter,
|
statusFilter: filters.debouncedStatusFilter,
|
||||||
sortField: sort.debouncedConfig.field,
|
sortField: filters.debouncedSortConfig.field,
|
||||||
sortDirection: sort.debouncedConfig.direction,
|
sortDirection: filters.debouncedSortConfig.direction,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -866,8 +839,8 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
}, [
|
}, [
|
||||||
filters.debouncedEntityFilter,
|
filters.debouncedEntityFilter,
|
||||||
filters.debouncedStatusFilter,
|
filters.debouncedStatusFilter,
|
||||||
sort.debouncedConfig.field,
|
filters.debouncedSortConfig.field,
|
||||||
sort.debouncedConfig.direction,
|
filters.debouncedSortConfig.direction,
|
||||||
user,
|
user,
|
||||||
fetchItems,
|
fetchItems,
|
||||||
pagination
|
pagination
|
||||||
@@ -948,7 +921,6 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
actionLoading,
|
actionLoading,
|
||||||
filters,
|
filters,
|
||||||
pagination,
|
pagination,
|
||||||
sort,
|
|
||||||
queue,
|
queue,
|
||||||
newItemsCount,
|
newItemsCount,
|
||||||
pendingNewItems,
|
pendingNewItems,
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
import { useState, useCallback, useEffect } from 'react';
|
|
||||||
import { useDebounce } from '@/hooks/useDebounce';
|
|
||||||
import type { SortConfig, SortField } from '@/types/moderation';
|
|
||||||
|
|
||||||
const STORAGE_KEY = 'moderationQueue_sortConfig';
|
|
||||||
const SORT_DEBOUNCE_MS = 0; // Changed from 300ms to 0ms for immediate feedback
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default sort configuration
|
|
||||||
* Sorts by creation date ascending (oldest first)
|
|
||||||
*/
|
|
||||||
const DEFAULT_SORT: SortConfig = {
|
|
||||||
field: 'created_at',
|
|
||||||
direction: 'asc'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load sort configuration from localStorage
|
|
||||||
*/
|
|
||||||
function loadSortConfig(): SortConfig {
|
|
||||||
try {
|
|
||||||
const stored = localStorage.getItem(STORAGE_KEY);
|
|
||||||
if (stored) {
|
|
||||||
const parsed = JSON.parse(stored);
|
|
||||||
// Validate structure
|
|
||||||
if (parsed.field && parsed.direction) {
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Failed to load sort config:', error);
|
|
||||||
}
|
|
||||||
return DEFAULT_SORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save sort configuration to localStorage
|
|
||||||
*/
|
|
||||||
function saveSortConfig(config: SortConfig): void {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Failed to save sort config:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UseModerationSortReturn {
|
|
||||||
/** Current sort configuration (immediate) */
|
|
||||||
config: SortConfig;
|
|
||||||
/** Debounced sort configuration (use this for queries) */
|
|
||||||
debouncedConfig: SortConfig;
|
|
||||||
/** Update the sort configuration */
|
|
||||||
setConfig: (config: SortConfig) => void;
|
|
||||||
/** Sort by a specific field, toggling direction if already sorting by that field */
|
|
||||||
sortBy: (field: SortField) => void;
|
|
||||||
/** Toggle the sort direction */
|
|
||||||
toggleDirection: () => void;
|
|
||||||
/** Reset to default sort */
|
|
||||||
reset: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook for managing moderation queue sort state
|
|
||||||
*
|
|
||||||
* Provides sort configuration with localStorage persistence
|
|
||||||
* and convenient methods for updating sort settings.
|
|
||||||
*/
|
|
||||||
export function useModerationSort(): UseModerationSortReturn {
|
|
||||||
const [config, setConfigState] = useState<SortConfig>(loadSortConfig);
|
|
||||||
|
|
||||||
// Debounce the config to prevent rapid-fire fetches
|
|
||||||
const debouncedConfig = useDebounce(config, SORT_DEBOUNCE_MS);
|
|
||||||
|
|
||||||
// Persist to localStorage whenever config changes
|
|
||||||
useEffect(() => {
|
|
||||||
saveSortConfig(config);
|
|
||||||
}, [config]);
|
|
||||||
|
|
||||||
const setConfig = useCallback((newConfig: SortConfig) => {
|
|
||||||
console.log('📝 [SORT] setConfig called:', newConfig);
|
|
||||||
setConfigState(newConfig);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const sortBy = useCallback((field: SortField) => {
|
|
||||||
setConfigState(prev => ({
|
|
||||||
field,
|
|
||||||
// Toggle direction if clicking the same field, otherwise default to ascending
|
|
||||||
direction: prev.field === field
|
|
||||||
? (prev.direction === 'asc' ? 'desc' : 'asc')
|
|
||||||
: 'asc'
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const toggleDirection = useCallback(() => {
|
|
||||||
setConfigState(prev => ({
|
|
||||||
...prev,
|
|
||||||
direction: prev.direction === 'asc' ? 'desc' : 'asc'
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
|
||||||
setConfigState(DEFAULT_SORT);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
config,
|
|
||||||
debouncedConfig,
|
|
||||||
setConfig,
|
|
||||||
sortBy,
|
|
||||||
toggleDirection,
|
|
||||||
reset
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user