feat: Remove all sorting functionality

This commit is contained in:
gpt-engineer-app[bot]
2025-10-13 13:15:14 +00:00
parent 83e10817c5
commit 90ae7d9a41
10 changed files with 13 additions and 468 deletions

View File

@@ -1,14 +1,12 @@
import { Filter, MessageSquare, FileText, Image, ArrowUp, ArrowDown } from 'lucide-react'; import { Filter, MessageSquare, FileText, Image } from 'lucide-react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import type { EntityFilter, StatusFilter, SortConfig, SortField } from '@/types/moderation'; import type { EntityFilter, StatusFilter } from '@/types/moderation';
interface ActiveFiltersDisplayProps { interface ActiveFiltersDisplayProps {
entityFilter: EntityFilter; entityFilter: EntityFilter;
statusFilter: StatusFilter; statusFilter: StatusFilter;
sortConfig: SortConfig;
defaultEntityFilter?: EntityFilter; defaultEntityFilter?: EntityFilter;
defaultStatusFilter?: StatusFilter; defaultStatusFilter?: StatusFilter;
defaultSortField?: SortField;
} }
const getEntityFilterIcon = (filter: EntityFilter) => { const getEntityFilterIcon = (filter: EntityFilter) => {
@@ -20,29 +18,17 @@ const getEntityFilterIcon = (filter: EntityFilter) => {
} }
}; };
const getSortFieldLabel = (field: SortField): string => { // Removed - sorting functionality deleted
switch (field) {
case 'username': return 'Submitter';
case 'submission_type': return 'Type';
case 'escalated': return 'Escalated';
case 'status': return 'Status';
case 'created_at': return 'Date';
default: return field;
}
};
export const ActiveFiltersDisplay = ({ export const ActiveFiltersDisplay = ({
entityFilter, entityFilter,
statusFilter, statusFilter,
sortConfig,
defaultEntityFilter = 'all', defaultEntityFilter = 'all',
defaultStatusFilter = 'pending', defaultStatusFilter = 'pending'
defaultSortField = 'created_at'
}: ActiveFiltersDisplayProps) => { }: ActiveFiltersDisplayProps) => {
const hasActiveFilters = const hasActiveFilters =
entityFilter !== defaultEntityFilter || entityFilter !== defaultEntityFilter ||
statusFilter !== defaultStatusFilter || statusFilter !== defaultStatusFilter;
sortConfig.field !== defaultSortField;
if (!hasActiveFilters) return null; if (!hasActiveFilters) return null;
@@ -60,12 +46,6 @@ export const ActiveFiltersDisplay = ({
{statusFilter} {statusFilter}
</Badge> </Badge>
)} )}
{sortConfig.field !== defaultSortField && (
<Badge variant="secondary" className="flex items-center gap-1">
{sortConfig.direction === 'asc' ? <ArrowUp className="w-3 h-3" /> : <ArrowDown className="w-3 h-3" />}
Sort: {getSortFieldLabel(sortConfig.field)}
</Badge>
)}
</div> </div>
); );
}; };

View File

@@ -112,11 +112,9 @@ 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}
isMobile={isMobile} isMobile={isMobile}
onEntityFilterChange={queueManager.filters.setEntityFilter} onEntityFilterChange={queueManager.filters.setEntityFilter}
onStatusFilterChange={queueManager.filters.setStatusFilter} onStatusFilterChange={queueManager.filters.setStatusFilter}
onSortConfigChange={queueManager.sort.setConfig}
onClearFilters={queueManager.filters.clearFilters} onClearFilters={queueManager.filters.clearFilters}
showClearButton={queueManager.filters.hasActiveFilters} showClearButton={queueManager.filters.hasActiveFilters}
/> />
@@ -126,7 +124,6 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
<ActiveFiltersDisplay <ActiveFiltersDisplay
entityFilter={queueManager.filters.entityFilter} entityFilter={queueManager.filters.entityFilter}
statusFilter={queueManager.filters.statusFilter} statusFilter={queueManager.filters.statusFilter}
sortConfig={queueManager.sort.config}
/> />
)} )}

View File

@@ -2,17 +2,14 @@ import { Filter, MessageSquare, FileText, Image, X } from 'lucide-react';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { QueueSortControls } from './QueueSortControls'; import type { EntityFilter, StatusFilter } from '@/types/moderation';
import type { EntityFilter, StatusFilter, SortConfig } from '@/types/moderation';
interface QueueFiltersProps { interface QueueFiltersProps {
activeEntityFilter: EntityFilter; activeEntityFilter: EntityFilter;
activeStatusFilter: StatusFilter; activeStatusFilter: StatusFilter;
sortConfig: SortConfig;
isMobile: boolean; isMobile: boolean;
onEntityFilterChange: (filter: EntityFilter) => void; onEntityFilterChange: (filter: EntityFilter) => void;
onStatusFilterChange: (filter: StatusFilter) => void; onStatusFilterChange: (filter: StatusFilter) => void;
onSortConfigChange: (config: SortConfig) => void;
onClearFilters: () => void; onClearFilters: () => void;
showClearButton: boolean; showClearButton: boolean;
} }
@@ -29,11 +26,9 @@ const getEntityFilterIcon = (filter: EntityFilter) => {
export const QueueFilters = ({ export const QueueFilters = ({
activeEntityFilter, activeEntityFilter,
activeStatusFilter, activeStatusFilter,
sortConfig,
isMobile, isMobile,
onEntityFilterChange, onEntityFilterChange,
onStatusFilterChange, onStatusFilterChange,
onSortConfigChange,
onClearFilters, onClearFilters,
showClearButton showClearButton
}: QueueFiltersProps) => { }: QueueFiltersProps) => {
@@ -112,13 +107,6 @@ export const QueueFilters = ({
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
{/* Sort Controls */}
<QueueSortControls
sortConfig={sortConfig}
onSortChange={onSortConfigChange}
isMobile={isMobile}
/>
</div> </div>
{/* Clear Filters Button */} {/* Clear Filters Button */}

View File

@@ -1,84 +0,0 @@
import { ArrowUp, ArrowDown } from 'lucide-react';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Button } from '@/components/ui/button';
import type { SortConfig, SortField } from '@/types/moderation';
interface QueueSortControlsProps {
sortConfig: SortConfig;
onSortChange: (config: SortConfig) => void;
isMobile?: boolean;
variant?: 'inline' | 'standalone';
showLabel?: boolean;
}
const 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;
}
};
export const QueueSortControls = ({
sortConfig,
onSortChange,
isMobile = false,
variant = 'inline',
showLabel = true
}: QueueSortControlsProps) => {
const handleFieldChange = (field: SortField) => {
onSortChange({ ...sortConfig, field });
};
const handleDirectionToggle = () => {
onSortChange({
...sortConfig,
direction: sortConfig.direction === 'asc' ? 'desc' : 'asc'
});
};
return (
<div className={`space-y-2 ${isMobile ? 'w-full' : 'min-w-[180px]'}`}>
{showLabel && (
<Label className={`font-medium ${isMobile ? 'text-xs' : 'text-sm'}`}>
Sort By
</Label>
)}
<div className="flex gap-2">
<Select
value={sortConfig.field}
onValueChange={handleFieldChange}
>
<SelectTrigger className={isMobile ? "h-10 flex-1" : "flex-1"}>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="created_at">{getSortFieldLabel('created_at')}</SelectItem>
<SelectItem value="username">{getSortFieldLabel('username')}</SelectItem>
<SelectItem value="submission_type">{getSortFieldLabel('submission_type')}</SelectItem>
<SelectItem value="status">{getSortFieldLabel('status')}</SelectItem>
<SelectItem value="escalated">{getSortFieldLabel('escalated')}</SelectItem>
</SelectContent>
</Select>
<Button
variant="outline"
size={isMobile ? "default" : "sm"}
onClick={handleDirectionToggle}
className={isMobile ? "h-10" : ""}
title={sortConfig.direction === 'asc' ? 'Sort Descending' : 'Sort Ascending'}
>
{sortConfig.direction === 'asc' ? (
<ArrowUp className="w-4 h-4" />
) : (
<ArrowDown className="w-4 h-4" />
)}
</Button>
</div>
</div>
);
};

View File

@@ -12,8 +12,7 @@ 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';
export { useModerationSort } from './useModerationSort'; // Removed - sorting functionality deleted
export type { ModerationSort, ModerationSortConfig } from './useModerationSort';
export { usePagination } from './usePagination'; export { usePagination } from './usePagination';
export type { PaginationState, PaginationConfig } from './usePagination'; export type { PaginationState, PaginationConfig } from './usePagination';

View File

@@ -6,13 +6,12 @@ import {
useEntityCache, useEntityCache,
useProfileCache, useProfileCache,
useModerationFilters, useModerationFilters,
useModerationSort,
usePagination, usePagination,
useRealtimeSubscriptions, useRealtimeSubscriptions,
} from "./index"; } from "./index";
import { useModerationQueue } from "@/hooks/useModerationQueue"; import { useModerationQueue } from "@/hooks/useModerationQueue";
import { smartMergeArray } from "@/lib/smartStateUpdate"; import { smartMergeArray } from "@/lib/smartStateUpdate";
import type { ModerationItem, EntityFilter, StatusFilter, LoadingState, SortConfig } from "@/types/moderation"; import type { ModerationItem, EntityFilter, StatusFilter, LoadingState } from "@/types/moderation";
/** /**
* Configuration for useModerationQueueManager * Configuration for useModerationQueueManager
@@ -44,7 +43,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
@@ -99,11 +97,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
}, },
}); });
const sort = useModerationSort({ // Removed - sorting functionality deleted
initialConfig: { field: "created_at", direction: "asc" },
persist: true,
storageKey: "moderationQueue_sortConfig",
});
const queue = useModerationQueue(); const queue = useModerationQueue();
const entityCache = useEntityCache(); const entityCache = useEntityCache();
@@ -221,9 +215,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
status status
) )
`, `,
) );
.order("escalated", { ascending: false })
.order("created_at", { ascending: true });
// Apply tab-based status filtering // Apply tab-based status filtering
const tab = filters.activeTab; const tab = filters.activeTab;
@@ -929,7 +921,6 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
actionLoading, actionLoading,
filters, filters,
pagination, pagination,
sort,
queue, queue,
newItemsCount, newItemsCount,
pendingNewItems, pendingNewItems,

View File

@@ -1,145 +0,0 @@
/**
* 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
* <Select value={sort.field} onValueChange={sort.setField}>
* <SelectItem value="created_at">Date</SelectItem>
* <SelectItem value="username">User</SelectItem>
* </Select>
* ```
*/
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<SortConfig>(() => {
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,
};
}

View File

@@ -43,18 +43,7 @@ export type {
DeleteSubmissionConfig, DeleteSubmissionConfig,
} from './actions'; } from './actions';
// Sorting utilities // Removed - sorting functionality deleted
export {
sortModerationItems,
getDefaultSortConfig,
loadSortConfig,
saveSortConfig,
toggleSortDirection,
getSortFieldLabel,
isDefaultSortConfig,
} from './sorting';
export type { SortConfig, SortField, SortDirection } from '@/types/moderation';
// Realtime subscription utilities // Realtime subscription utilities
export { export {

View File

@@ -1,151 +0,0 @@
/**
* 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
);
}

View File

@@ -99,26 +99,7 @@ export type StatusFilter = 'all' | 'pending' | 'partially_approved' | 'flagged'
*/ */
export type QueueTab = 'mainQueue' | 'archive'; export type QueueTab = 'mainQueue' | 'archive';
/** // Removed - sorting functionality deleted
* Fields that can be used for sorting the moderation queue
*/
export type SortField = 'created_at' | 'username' | 'submission_type' | 'status' | 'escalated';
/**
* Direction for sorting (ascending or descending)
*/
export type SortDirection = 'asc' | 'desc';
/**
* Configuration for sorting the moderation queue
*/
export interface SortConfig {
/** Field to sort by */
field: SortField;
/** Direction to sort */
direction: SortDirection;
}
/** /**
* Loading states for the moderation queue * Loading states for the moderation queue