diff --git a/src/hooks/moderation/index.ts b/src/hooks/moderation/index.ts
index 0af62212..48f2e2ea 100644
--- a/src/hooks/moderation/index.ts
+++ b/src/hooks/moderation/index.ts
@@ -8,3 +8,12 @@
export { useEntityCache } from './useEntityCache';
export { useProfileCache } from './useProfileCache';
export type { CachedProfile } from './useProfileCache';
+
+export { useModerationFilters } from './useModerationFilters';
+export type { ModerationFilters, ModerationFiltersConfig } from './useModerationFilters';
+
+export { useModerationSort } from './useModerationSort';
+export type { ModerationSort, ModerationSortConfig } from './useModerationSort';
+
+export { usePagination } from './usePagination';
+export type { PaginationState, PaginationConfig } from './usePagination';
diff --git a/src/hooks/moderation/useModerationFilters.ts b/src/hooks/moderation/useModerationFilters.ts
new file mode 100644
index 00000000..1a300f61
--- /dev/null
+++ b/src/hooks/moderation/useModerationFilters.ts
@@ -0,0 +1,191 @@
+/**
+ * Moderation Queue Filters Hook
+ *
+ * Manages filter state for the moderation queue, including:
+ * - Entity type filtering (all, reviews, submissions, photos)
+ * - Status filtering (pending, approved, rejected, etc.)
+ * - Tab management (main queue vs archive)
+ * - Filter persistence and clearing
+ */
+
+import { useState, useCallback, useEffect } from 'react';
+import { useDebounce } from '@/hooks/useDebounce';
+import type { EntityFilter, StatusFilter, QueueTab } from '@/types/moderation';
+
+export interface ModerationFiltersConfig {
+ /** Initial entity filter */
+ initialEntityFilter?: EntityFilter;
+
+ /** Initial status filter */
+ initialStatusFilter?: StatusFilter;
+
+ /** Initial active tab */
+ initialTab?: QueueTab;
+
+ /** Debounce delay for filter changes (ms) */
+ debounceDelay?: number;
+
+ /** Whether to persist filters to localStorage */
+ persist?: boolean;
+
+ /** localStorage key prefix for persistence */
+ storageKey?: string;
+}
+
+export interface ModerationFilters {
+ /** Current entity type filter */
+ entityFilter: EntityFilter;
+
+ /** Current status filter */
+ statusFilter: StatusFilter;
+
+ /** Current active tab */
+ activeTab: QueueTab;
+
+ /** Debounced entity filter (for API calls) */
+ debouncedEntityFilter: EntityFilter;
+
+ /** Debounced status filter (for API calls) */
+ debouncedStatusFilter: StatusFilter;
+
+ /** Set entity filter */
+ setEntityFilter: (filter: EntityFilter) => void;
+
+ /** Set status filter */
+ setStatusFilter: (filter: StatusFilter) => void;
+
+ /** Set active tab */
+ setActiveTab: (tab: QueueTab) => void;
+
+ /** Reset all filters to defaults */
+ clearFilters: () => void;
+
+ /** Check if any non-default filters are active */
+ hasActiveFilters: boolean;
+}
+
+/**
+ * Hook for managing moderation queue filters
+ *
+ * @param config - Configuration options
+ * @returns Filter state and actions
+ *
+ * @example
+ * ```tsx
+ * const filters = useModerationFilters({
+ * persist: true,
+ * debounceDelay: 300
+ * });
+ *
+ * // Use in component
+ *
+ * ```
+ */
+export function useModerationFilters(config: ModerationFiltersConfig = {}): ModerationFilters {
+ const {
+ initialEntityFilter = 'all',
+ initialStatusFilter = 'pending',
+ initialTab = 'mainQueue',
+ debounceDelay = 300,
+ persist = true,
+ storageKey = 'moderationQueue_filters',
+ } = config;
+
+ // Load persisted filters on mount
+ const loadPersistedFilters = useCallback(() => {
+ if (!persist) return null;
+
+ try {
+ const saved = localStorage.getItem(storageKey);
+ if (saved) {
+ return JSON.parse(saved);
+ }
+ } catch (error) {
+ console.error('Failed to load persisted filters:', error);
+ }
+
+ return null;
+ }, [persist, storageKey]);
+
+ const persisted = loadPersistedFilters();
+
+ // Filter state
+ const [entityFilter, setEntityFilterState] = useState(
+ persisted?.entityFilter || initialEntityFilter
+ );
+ const [statusFilter, setStatusFilterState] = useState(
+ persisted?.statusFilter || initialStatusFilter
+ );
+ const [activeTab, setActiveTabState] = useState(
+ persisted?.activeTab || initialTab
+ );
+
+ // Debounced filters for API calls
+ const debouncedEntityFilter = useDebounce(entityFilter, debounceDelay);
+ const debouncedStatusFilter = useDebounce(statusFilter, debounceDelay);
+
+ // Persist filters to localStorage
+ useEffect(() => {
+ if (persist) {
+ try {
+ localStorage.setItem(
+ storageKey,
+ JSON.stringify({
+ entityFilter,
+ statusFilter,
+ activeTab,
+ })
+ );
+ } catch (error) {
+ console.error('Failed to persist filters:', error);
+ }
+ }
+ }, [entityFilter, statusFilter, activeTab, persist, storageKey]);
+
+ // Set entity filter with logging
+ const setEntityFilter = useCallback((filter: EntityFilter) => {
+ console.log('🔍 Entity filter changed:', filter);
+ setEntityFilterState(filter);
+ }, []);
+
+ // Set status filter with logging
+ const setStatusFilter = useCallback((filter: StatusFilter) => {
+ console.log('🔍 Status filter changed:', filter);
+ setStatusFilterState(filter);
+ }, []);
+
+ // Set active tab with logging
+ const setActiveTab = useCallback((tab: QueueTab) => {
+ console.log('🔍 Tab changed:', tab);
+ setActiveTabState(tab);
+ }, []);
+
+ // Clear all filters
+ const clearFilters = useCallback(() => {
+ console.log('🔍 Filters cleared');
+ setEntityFilterState(initialEntityFilter);
+ setStatusFilterState(initialStatusFilter);
+ setActiveTabState(initialTab);
+ }, [initialEntityFilter, initialStatusFilter, initialTab]);
+
+ // Check if non-default filters are active
+ const hasActiveFilters =
+ entityFilter !== initialEntityFilter ||
+ statusFilter !== initialStatusFilter ||
+ activeTab !== initialTab;
+
+ return {
+ entityFilter,
+ statusFilter,
+ activeTab,
+ debouncedEntityFilter,
+ debouncedStatusFilter,
+ setEntityFilter,
+ setStatusFilter,
+ setActiveTab,
+ clearFilters,
+ hasActiveFilters,
+ };
+}
diff --git a/src/hooks/moderation/useModerationSort.ts b/src/hooks/moderation/useModerationSort.ts
new file mode 100644
index 00000000..35daef6e
--- /dev/null
+++ b/src/hooks/moderation/useModerationSort.ts
@@ -0,0 +1,145 @@
+/**
+ * Moderation Queue Sort Hook
+ *
+ * Manages sort configuration for the moderation queue with persistence.
+ */
+
+import { useState, useCallback, useEffect } from 'react';
+import type { SortConfig, SortField, SortDirection } from '@/types/moderation';
+import {
+ getDefaultSortConfig,
+ loadSortConfig,
+ saveSortConfig,
+ toggleSortDirection as toggleDirection,
+ isDefaultSortConfig,
+} from '@/lib/moderation/sorting';
+
+export interface ModerationSortConfig {
+ /** Initial sort configuration */
+ initialConfig?: SortConfig;
+
+ /** Whether to persist sort config */
+ persist?: boolean;
+
+ /** localStorage key for persistence */
+ storageKey?: string;
+
+ /** Callback when sort config changes */
+ onChange?: (config: SortConfig) => void;
+}
+
+export interface ModerationSort {
+ /** Current sort configuration */
+ config: SortConfig;
+
+ /** Sort field */
+ field: SortField;
+
+ /** Sort direction */
+ direction: SortDirection;
+
+ /** Set sort field */
+ setField: (field: SortField) => void;
+
+ /** Set sort direction */
+ setDirection: (direction: SortDirection) => void;
+
+ /** Toggle sort direction */
+ toggleDirection: () => void;
+
+ /** Set both field and direction */
+ setConfig: (config: SortConfig) => void;
+
+ /** Reset to default */
+ reset: () => void;
+
+ /** Check if using default config */
+ isDefault: boolean;
+}
+
+/**
+ * Hook for managing moderation queue sort configuration
+ *
+ * @param config - Configuration options
+ * @returns Sort state and actions
+ *
+ * @example
+ * ```tsx
+ * const sort = useModerationSort({
+ * persist: true,
+ * onChange: (config) => fetchItems(config)
+ * });
+ *
+ * // Use in component
+ *
+ * ```
+ */
+export function useModerationSort(config: ModerationSortConfig = {}): ModerationSort {
+ const {
+ initialConfig,
+ persist = true,
+ storageKey = 'moderationQueue_sortConfig',
+ onChange,
+ } = config;
+
+ // Load persisted or use initial/default config
+ const [sortConfig, setSortConfig] = useState(() => {
+ if (initialConfig) return initialConfig;
+ if (persist) return loadSortConfig(storageKey);
+ return getDefaultSortConfig();
+ });
+
+ // Persist changes
+ useEffect(() => {
+ if (persist) {
+ saveSortConfig(sortConfig, storageKey);
+ }
+ onChange?.(sortConfig);
+ }, [sortConfig, persist, storageKey, onChange]);
+
+ // Set sort field (keep direction)
+ const setField = useCallback((field: SortField) => {
+ setSortConfig((prev) => ({ ...prev, field }));
+ }, []);
+
+ // Set sort direction (keep field)
+ const setDirection = useCallback((direction: SortDirection) => {
+ setSortConfig((prev) => ({ ...prev, direction }));
+ }, []);
+
+ // Toggle sort direction
+ const toggleSortDirection = useCallback(() => {
+ setSortConfig((prev) => ({
+ ...prev,
+ direction: toggleDirection(prev.direction),
+ }));
+ }, []);
+
+ // Set entire config
+ const setConfig = useCallback((newConfig: SortConfig) => {
+ setSortConfig(newConfig);
+ }, []);
+
+ // Reset to default
+ const reset = useCallback(() => {
+ setSortConfig(getDefaultSortConfig());
+ }, []);
+
+ // Check if using default config
+ const isDefault = isDefaultSortConfig(sortConfig);
+
+ return {
+ config: sortConfig,
+ field: sortConfig.field,
+ direction: sortConfig.direction,
+ setField,
+ setDirection,
+ toggleDirection: toggleSortDirection,
+ setConfig,
+ reset,
+ isDefault,
+ };
+}
diff --git a/src/hooks/moderation/usePagination.ts b/src/hooks/moderation/usePagination.ts
new file mode 100644
index 00000000..a929f767
--- /dev/null
+++ b/src/hooks/moderation/usePagination.ts
@@ -0,0 +1,253 @@
+/**
+ * Pagination Hook
+ *
+ * Manages pagination state and actions for the moderation queue.
+ */
+
+import { useState, useCallback, useEffect, useMemo } from 'react';
+
+export interface PaginationConfig {
+ /** Initial page number (1-indexed) */
+ initialPage?: number;
+
+ /** Initial page size */
+ initialPageSize?: number;
+
+ /** Whether to persist pagination state */
+ persist?: boolean;
+
+ /** localStorage key for persistence */
+ storageKey?: string;
+
+ /** Callback when page changes */
+ onPageChange?: (page: number) => void;
+
+ /** Callback when page size changes */
+ onPageSizeChange?: (pageSize: number) => void;
+}
+
+export interface PaginationState {
+ /** Current page (1-indexed) */
+ currentPage: number;
+
+ /** Items per page */
+ pageSize: number;
+
+ /** Total number of items */
+ totalCount: number;
+
+ /** Total number of pages */
+ totalPages: number;
+
+ /** Start index for current page (0-indexed) */
+ startIndex: number;
+
+ /** End index for current page (0-indexed) */
+ endIndex: number;
+
+ /** Whether there is a previous page */
+ hasPrevPage: boolean;
+
+ /** Whether there is a next page */
+ hasNextPage: boolean;
+
+ /** Set current page */
+ setCurrentPage: (page: number) => void;
+
+ /** Set page size */
+ setPageSize: (size: number) => void;
+
+ /** Set total count */
+ setTotalCount: (count: number) => void;
+
+ /** Go to next page */
+ nextPage: () => void;
+
+ /** Go to previous page */
+ prevPage: () => void;
+
+ /** Go to first page */
+ firstPage: () => void;
+
+ /** Go to last page */
+ lastPage: () => void;
+
+ /** Reset pagination */
+ reset: () => void;
+
+ /** Get page range for display */
+ getPageRange: (maxPages?: number) => number[];
+}
+
+/**
+ * Hook for managing pagination state
+ *
+ * @param config - Configuration options
+ * @returns Pagination state and actions
+ *
+ * @example
+ * ```tsx
+ * const pagination = usePagination({
+ * initialPageSize: 25,
+ * persist: true,
+ * onPageChange: (page) => fetchData(page)
+ * });
+ *
+ * // Set total count from API
+ * pagination.setTotalCount(response.count);
+ *
+ * // Use in query
+ * const { startIndex, endIndex } = pagination;
+ * query.range(startIndex, endIndex);
+ * ```
+ */
+export function usePagination(config: PaginationConfig = {}): PaginationState {
+ const {
+ initialPage = 1,
+ initialPageSize = 25,
+ persist = false,
+ storageKey = 'pagination_state',
+ onPageChange,
+ onPageSizeChange,
+ } = config;
+
+ // Load persisted state
+ const loadPersistedState = useCallback(() => {
+ if (!persist) return null;
+
+ try {
+ const saved = localStorage.getItem(storageKey);
+ if (saved) {
+ return JSON.parse(saved);
+ }
+ } catch (error) {
+ console.error('Failed to load pagination state:', error);
+ }
+
+ return null;
+ }, [persist, storageKey]);
+
+ const persisted = loadPersistedState();
+
+ // State
+ const [currentPage, setCurrentPageState] = useState(
+ persisted?.currentPage || initialPage
+ );
+ const [pageSize, setPageSizeState] = useState(
+ persisted?.pageSize || initialPageSize
+ );
+ const [totalCount, setTotalCount] = useState(0);
+
+ // Computed values
+ const totalPages = useMemo(() => Math.ceil(totalCount / pageSize), [totalCount, pageSize]);
+ const startIndex = useMemo(() => (currentPage - 1) * pageSize, [currentPage, pageSize]);
+ const endIndex = useMemo(() => startIndex + pageSize - 1, [startIndex, pageSize]);
+ const hasPrevPage = currentPage > 1;
+ const hasNextPage = currentPage < totalPages;
+
+ // Persist state
+ useEffect(() => {
+ if (persist) {
+ try {
+ localStorage.setItem(
+ storageKey,
+ JSON.stringify({
+ currentPage,
+ pageSize,
+ })
+ );
+ } catch (error) {
+ console.error('Failed to persist pagination state:', error);
+ }
+ }
+ }, [currentPage, pageSize, persist, storageKey]);
+
+ // Set current page with bounds checking
+ const setCurrentPage = useCallback(
+ (page: number) => {
+ const boundedPage = Math.max(1, Math.min(page, totalPages || 1));
+ setCurrentPageState(boundedPage);
+ onPageChange?.(boundedPage);
+ },
+ [totalPages, onPageChange]
+ );
+
+ // Set page size and reset to first page
+ const setPageSize = useCallback(
+ (size: number) => {
+ setPageSizeState(size);
+ setCurrentPageState(1);
+ onPageSizeChange?.(size);
+ },
+ [onPageSizeChange]
+ );
+
+ // Navigation actions
+ const nextPage = useCallback(() => {
+ if (hasNextPage) {
+ setCurrentPage(currentPage + 1);
+ }
+ }, [currentPage, hasNextPage, setCurrentPage]);
+
+ const prevPage = useCallback(() => {
+ if (hasPrevPage) {
+ setCurrentPage(currentPage - 1);
+ }
+ }, [currentPage, hasPrevPage, setCurrentPage]);
+
+ const firstPage = useCallback(() => {
+ setCurrentPage(1);
+ }, [setCurrentPage]);
+
+ const lastPage = useCallback(() => {
+ setCurrentPage(totalPages);
+ }, [totalPages, setCurrentPage]);
+
+ // Reset pagination
+ const reset = useCallback(() => {
+ setCurrentPageState(initialPage);
+ setPageSizeState(initialPageSize);
+ setTotalCount(0);
+ }, [initialPage, initialPageSize]);
+
+ // Get page range for pagination controls
+ const getPageRange = useCallback(
+ (maxPages: number = 5): number[] => {
+ if (totalPages <= maxPages) {
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
+ }
+
+ const half = Math.floor(maxPages / 2);
+ let start = Math.max(1, currentPage - half);
+ let end = Math.min(totalPages, start + maxPages - 1);
+
+ // Adjust start if we're near the end
+ if (end - start < maxPages - 1) {
+ start = Math.max(1, end - maxPages + 1);
+ }
+
+ return Array.from({ length: end - start + 1 }, (_, i) => start + i);
+ },
+ [currentPage, totalPages]
+ );
+
+ return {
+ currentPage,
+ pageSize,
+ totalCount,
+ totalPages,
+ startIndex,
+ endIndex,
+ hasPrevPage,
+ hasNextPage,
+ setCurrentPage,
+ setPageSize,
+ setTotalCount,
+ nextPage,
+ prevPage,
+ firstPage,
+ lastPage,
+ reset,
+ getPageRange,
+ };
+}
diff --git a/src/lib/moderation/index.ts b/src/lib/moderation/index.ts
index 21e6163f..23ef81d8 100644
--- a/src/lib/moderation/index.ts
+++ b/src/lib/moderation/index.ts
@@ -42,3 +42,16 @@ export type {
ModerationConfig,
DeleteSubmissionConfig,
} from './actions';
+
+// Sorting utilities
+export {
+ sortModerationItems,
+ getDefaultSortConfig,
+ loadSortConfig,
+ saveSortConfig,
+ toggleSortDirection,
+ getSortFieldLabel,
+ isDefaultSortConfig,
+} from './sorting';
+
+export type { SortConfig, SortField, SortDirection } from '@/types/moderation';
diff --git a/src/lib/moderation/sorting.ts b/src/lib/moderation/sorting.ts
new file mode 100644
index 00000000..31a15512
--- /dev/null
+++ b/src/lib/moderation/sorting.ts
@@ -0,0 +1,151 @@
+/**
+ * Moderation Queue Sorting Utilities
+ *
+ * Provides sorting functions and utilities for moderation queue items.
+ */
+
+import type { ModerationItem, SortField, SortDirection, SortConfig } from '@/types/moderation';
+
+/**
+ * Sort moderation items based on configuration
+ *
+ * @param items - Array of moderation items to sort
+ * @param config - Sort configuration
+ * @returns Sorted array of items
+ */
+export function sortModerationItems(
+ items: ModerationItem[],
+ config: SortConfig
+): ModerationItem[] {
+ const { field, direction } = config;
+
+ return [...items].sort((a, b) => {
+ let comparison = 0;
+
+ switch (field) {
+ case 'created_at':
+ comparison = new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
+ break;
+
+ case 'username':
+ const usernameA = a.user_profile?.username || a.user_profile?.display_name || '';
+ const usernameB = b.user_profile?.username || b.user_profile?.display_name || '';
+ comparison = usernameA.localeCompare(usernameB);
+ break;
+
+ case 'submission_type':
+ comparison = (a.submission_type || '').localeCompare(b.submission_type || '');
+ break;
+
+ case 'status':
+ comparison = a.status.localeCompare(b.status);
+ break;
+
+ case 'escalated':
+ const escalatedA = a.escalated ? 1 : 0;
+ const escalatedB = b.escalated ? 1 : 0;
+ comparison = escalatedB - escalatedA; // Escalated items first
+ break;
+
+ default:
+ comparison = 0;
+ }
+
+ return direction === 'asc' ? comparison : -comparison;
+ });
+}
+
+/**
+ * Get default sort configuration
+ *
+ * @returns Default sort config
+ */
+export function getDefaultSortConfig(): SortConfig {
+ return {
+ field: 'created_at',
+ direction: 'asc',
+ };
+}
+
+/**
+ * Load sort configuration from localStorage
+ *
+ * @param key - localStorage key
+ * @returns Saved sort config or default
+ */
+export function loadSortConfig(key: string = 'moderationQueue_sortConfig'): SortConfig {
+ try {
+ const saved = localStorage.getItem(key);
+ if (saved) {
+ return JSON.parse(saved);
+ }
+ } catch (error) {
+ console.error('Failed to load sort config:', error);
+ }
+
+ return getDefaultSortConfig();
+}
+
+/**
+ * Save sort configuration to localStorage
+ *
+ * @param config - Sort configuration to save
+ * @param key - localStorage key
+ */
+export function saveSortConfig(
+ config: SortConfig,
+ key: string = 'moderationQueue_sortConfig'
+): void {
+ try {
+ localStorage.setItem(key, JSON.stringify(config));
+ } catch (error) {
+ console.error('Failed to save sort config:', error);
+ }
+}
+
+/**
+ * Toggle sort direction
+ *
+ * @param currentDirection - Current sort direction
+ * @returns Toggled direction
+ */
+export function toggleSortDirection(currentDirection: SortDirection): SortDirection {
+ return currentDirection === 'asc' ? 'desc' : 'asc';
+}
+
+/**
+ * Get human-readable label for sort field
+ *
+ * @param field - Sort field
+ * @returns Human-readable label
+ */
+export function getSortFieldLabel(field: SortField): string {
+ switch (field) {
+ case 'created_at':
+ return 'Date Created';
+ case 'username':
+ return 'Submitter';
+ case 'submission_type':
+ return 'Type';
+ case 'status':
+ return 'Status';
+ case 'escalated':
+ return 'Escalated';
+ default:
+ return field;
+ }
+}
+
+/**
+ * Check if sort config is default
+ *
+ * @param config - Sort configuration to check
+ * @returns True if config matches default
+ */
+export function isDefaultSortConfig(config: SortConfig): boolean {
+ const defaultConfig = getDefaultSortConfig();
+ return (
+ config.field === defaultConfig.field &&
+ config.direction === defaultConfig.direction
+ );
+}