From cc3ec7a6e4eb294d097fd686932e09536be809bf Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:01:49 +0000 Subject: [PATCH] Fix auto-refresh and queue claiming --- src/components/moderation/ModerationQueue.tsx | 34 ++++++++++++++----- src/lib/smartStateUpdate.ts | 15 ++++++-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/components/moderation/ModerationQueue.tsx b/src/components/moderation/ModerationQueue.tsx index 6140aa46..01805c54 100644 --- a/src/components/moderation/ModerationQueue.tsx +++ b/src/components/moderation/ModerationQueue.tsx @@ -354,25 +354,38 @@ export const ModerationQueue = forwardRef((props, ref) => { })), ]; - // Sort by creation date (newest first for better UX) - formattedItems.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); + // Sort by creation date (newest first) with stable secondary sort by ID + formattedItems.sort((a, b) => { + const timeA = new Date(a.created_at).getTime(); + const timeB = new Date(b.created_at).getTime(); + + // Primary sort by time + if (timeA !== timeB) { + return timeB - timeA; + } + + // Secondary stable sort by ID for items with identical timestamps + return a.id.localeCompare(b.id); + }); // Use smart merging for silent refreshes if strategy is 'merge' if (silent && refreshStrategy === 'merge') { const mergeResult = smartMergeArray(items, formattedItems, { - compareFields: ['status', 'reviewed_at', 'reviewer_notes'], + compareFields: ['status', 'reviewed_at', 'reviewed_by', 'reviewer_notes', 'content', 'submission_type'], preserveOrder: true, addToTop: true, }); - // If there are changes and we should preserve interaction + // If there are changes if (mergeResult.hasChanges) { + const actuallyNewItems = mergeResult.changes.added.length; + // Debug logging for smart merge console.log('🔄 Smart merge detected changes:', { - added: mergeResult.changes.added.length, + added: actuallyNewItems, updated: mergeResult.changes.updated.length, removed: mergeResult.changes.removed.length, - newItemsCount: mergeResult.changes.added.length, + totalItems: mergeResult.items.length, protectedIds: Array.from(preserveInteraction ? interactingWith : new Set()), strategy: refreshStrategy, preserveInteraction @@ -391,10 +404,13 @@ export const ModerationQueue = forwardRef((props, ref) => { setItems(mergedWithProtection); - // Update new items count - if (mergeResult.changes.added.length > 0) { - setNewItemsCount(prev => prev + mergeResult.changes.added.length); + // Only set new items count if there are genuinely new items + if (actuallyNewItems > 0) { + setNewItemsCount(actuallyNewItems); } + } else { + // No changes detected - keep current state completely unchanged + console.log('✅ No changes detected, keeping current state'); } } else { // Full replacement for non-silent refreshes or 'replace' strategy diff --git a/src/lib/smartStateUpdate.ts b/src/lib/smartStateUpdate.ts index 4a3cd144..32c36696 100644 --- a/src/lib/smartStateUpdate.ts +++ b/src/lib/smartStateUpdate.ts @@ -130,13 +130,22 @@ function hasItemChanged( compareFields?: (keyof T)[] ): boolean { if (!compareFields || compareFields.length === 0) { - // Deep comparison if no specific fields provided - return JSON.stringify(currentItem) !== JSON.stringify(newItem); + // If no fields specified, assume no change (too sensitive to compare everything) + // This prevents false positives from object reference changes + return false; } // Compare only specified fields for (const field of compareFields) { - if (currentItem[field] !== newItem[field]) { + const currentValue = currentItem[field]; + const newValue = newItem[field]; + + // Handle nested objects/arrays + if (typeof currentValue === 'object' && typeof newValue === 'object') { + if (JSON.stringify(currentValue) !== JSON.stringify(newValue)) { + return true; + } + } else if (currentValue !== newValue) { return true; } }