Enhance moderation queue sorting with visual indicators and logging

Add loading spinners, detailed sort query logging, result preview logging, disable sort controls during loading, and mobile-specific loading text changes. Also, fix type mismatch in QueueSortControls and correct refresh strategy logic.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ef7037e7-a631-48a2-94d1-9a4b52d7c35a
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/ef7037e7-a631-48a2-94d1-9a4b52d7c35a/kq6AhNt
This commit is contained in:
pac7
2025-10-13 15:16:16 +00:00
parent 1fe07cc70d
commit b56617efcc
6 changed files with 48 additions and 6 deletions

View File

@@ -41,3 +41,7 @@ externalPort = 3000
[[ports]]
localPort = 37143
externalPort = 3001
[[ports]]
localPort = 41349
externalPort = 3002

View File

@@ -5,6 +5,12 @@ ThrillWiki is a community-driven web application for discovering, reviewing, and
## Recent Changes
**October 2025 - Critical Bug Fixes & Stability Improvements (Latest)**
- **Moderation Queue Sorting Enhancement**: Enhanced sorting controls with visual loading indicators and diagnostic logging:
- Added animated spinners to sort controls (label and direction button) during data fetch operations
- Added comprehensive logging to track multi-level sort queries (escalated DESC → user-selected sort → created_at tertiary)
- Added detailed result preview logging showing first 3 items with sort field values for debugging
- Disabled sort controls during loading to prevent duplicate requests
- Mobile-specific loading text changes from "Ascending/Descending" to "Loading..." for clarity
- **Moderation Queue Sorting Fix**: Resolved sorting controls not updating the UI by fixing type mismatch in QueueSortControls (Radix Select passes string, handler expected SortField) and correcting refresh strategy logic (user-initiated sort/filter changes now bypass "notify" freeze mode and always update display)
- **Auth Loading State Fix**: Resolved perpetual loading state issue in auth buttons by simplifying useAuth hook's loading state management, removing blocking conditional logic, adding explicit setLoading(false) calls in all code paths, and ensuring pending email state cleanup occurs before early returns
- **React Hooks Violation Fix**: Resolved critical hooks ordering issue in useSearch that caused app crashes during hot module reload (HMR) by using stable useMemo with entire options object as dependency

View File

@@ -114,6 +114,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
activeStatusFilter={queueManager.filters.statusFilter}
sortConfig={queueManager.sort.config}
isMobile={isMobile}
isLoading={queueManager.loadingState === 'loading'}
onEntityFilterChange={queueManager.filters.setEntityFilter}
onStatusFilterChange={queueManager.filters.setStatusFilter}
onSortChange={queueManager.sort.setConfig}

View File

@@ -10,6 +10,7 @@ interface QueueFiltersProps {
activeStatusFilter: StatusFilter;
sortConfig: SortConfig;
isMobile: boolean;
isLoading?: boolean;
onEntityFilterChange: (filter: EntityFilter) => void;
onStatusFilterChange: (filter: StatusFilter) => void;
onSortChange: (config: SortConfig) => void;
@@ -31,6 +32,7 @@ export const QueueFilters = ({
activeStatusFilter,
sortConfig,
isMobile,
isLoading = false,
onEntityFilterChange,
onStatusFilterChange,
onSortChange,
@@ -118,6 +120,7 @@ export const QueueFilters = ({
sortConfig={sortConfig}
onSortChange={onSortChange}
isMobile={isMobile}
isLoading={isLoading}
/>
</div>

View File

@@ -1,4 +1,4 @@
import { ArrowUp, ArrowDown } from 'lucide-react';
import { ArrowUp, ArrowDown, Loader2 } 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';
@@ -8,6 +8,7 @@ interface QueueSortControlsProps {
sortConfig: SortConfig;
onSortChange: (config: SortConfig) => void;
isMobile: boolean;
isLoading?: boolean;
}
const SORT_FIELD_LABELS: Record<SortField, string> = {
@@ -19,7 +20,8 @@ const SORT_FIELD_LABELS: Record<SortField, string> = {
export const QueueSortControls = ({
sortConfig,
onSortChange,
isMobile
isMobile,
isLoading = false
}: QueueSortControlsProps) => {
const handleFieldChange = (value: string) => {
const validFields: SortField[] = ['created_at', 'submission_type', 'status'];
@@ -48,14 +50,16 @@ export const QueueSortControls = ({
return (
<div className={`flex gap-2 ${isMobile ? 'flex-col' : 'items-end'}`}>
<div className={`space-y-2 ${isMobile ? 'w-full' : 'min-w-[160px]'}`}>
<Label className={`font-medium ${isMobile ? 'text-xs' : 'text-sm'}`}>
<Label className={`font-medium ${isMobile ? 'text-xs' : 'text-sm'} flex items-center gap-2`}>
Sort By
{isLoading && <Loader2 className="w-3 h-3 animate-spin text-primary" />}
</Label>
<Select
value={sortConfig.field}
onValueChange={handleFieldChange}
disabled={isLoading}
>
<SelectTrigger className={isMobile ? "h-10" : ""}>
<SelectTrigger className={isMobile ? "h-10" : ""} disabled={isLoading}>
<SelectValue>
{SORT_FIELD_LABELS[sortConfig.field]}
</SelectValue>
@@ -75,12 +79,19 @@ export const QueueSortControls = ({
variant="outline"
size={isMobile ? "default" : "icon"}
onClick={handleDirectionToggle}
disabled={isLoading}
className={`flex items-center gap-2 ${isMobile ? 'w-full h-10' : 'h-10 w-10'}`}
title={sortConfig.direction === 'asc' ? 'Ascending' : 'Descending'}
>
<DirectionIcon className="w-4 h-4" />
{isLoading ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<DirectionIcon className="w-4 h-4" />
)}
{isMobile && (
<span className="capitalize">{sortConfig.direction}ending</span>
<span className="capitalize">
{isLoading ? 'Loading...' : `${sortConfig.direction}ending`}
</span>
)}
</Button>
</div>

View File

@@ -224,6 +224,12 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
);
// CRITICAL: Multi-level ordering
console.log('📊 [SORT QUERY] Applying multi-level sort:', {
level1: 'escalated DESC',
level2: `${sort.debouncedConfig.field} ${sort.debouncedConfig.direction.toUpperCase()}`,
level3: sort.debouncedConfig.field !== 'created_at' ? 'created_at ASC' : 'none'
});
// Level 1: Always sort by escalated first (descending)
submissionsQuery = submissionsQuery.order('escalated', { ascending: false });
@@ -291,6 +297,17 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
if (submissionsError) throw submissionsError;
// Log the actual data returned to verify sort order
if (submissions && submissions.length > 0) {
const sortField = sort.debouncedConfig.field;
const preview = submissions.slice(0, 3).map(s => ({
id: s.id.substring(0, 8),
[sortField]: s[sortField],
escalated: s.escalated
}));
console.log(`📋 [SORT RESULT] First 3 items by ${sortField}:`, preview);
}
// Fetch related profiles and entities
const userIds = [
...new Set([