feat: Implement Sprint 3 Performance Optimizations

This commit is contained in:
gpt-engineer-app[bot]
2025-11-02 21:52:59 +00:00
parent a9644c0bee
commit d057ddc8cc
7 changed files with 746 additions and 97 deletions

View File

@@ -1,4 +1,5 @@
import { useState, useImperativeHandle, forwardRef, useMemo, useCallback } from 'react';
import { useState, useImperativeHandle, forwardRef, useMemo, useCallback, useRef } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { Card, CardContent } from '@/components/ui/card';
import { TooltipProvider } from '@/components/ui/tooltip';
import { useToast } from '@/hooks/use-toast';
@@ -94,6 +95,16 @@ export const ModerationQueue = forwardRef<ModerationQueueRef, ModerationQueuePro
// Keyboard shortcuts help dialog
const [showShortcutsHelp, setShowShortcutsHelp] = useState(false);
// Virtual scrolling setup
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: queueManager.items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 420, // Estimated average height of QueueItem (card + spacing)
overscan: 3, // Render 3 items above/below viewport for smoother scrolling
enabled: queueManager.items.length > 10, // Only enable virtual scrolling for 10+ items
});
// UI-specific handlers
const handleNoteChange = (id: string, value: string) => {
setNotes(prev => ({ ...prev, [id]: value }));
@@ -258,37 +269,103 @@ export const ModerationQueue = forwardRef<ModerationQueueRef, ModerationQueuePro
/>
) : (
<TooltipProvider>
<div className="space-y-6">
{queueManager.items.map((item, index) => (
<ModerationErrorBoundary key={item.id} submissionId={item.id}>
<QueueItem
key={item.id}
item={item}
isMobile={isMobile}
actionLoading={queueManager.actionLoading}
isLockedByMe={queueManager.queue.isLockedByMe(item.id, item.assigned_to, item.locked_until)}
isLockedByOther={queueManager.queue.isLockedByOther(item.id, item.assigned_to, item.locked_until)}
lockStatus={getLockStatus({ assigned_to: item.assigned_to, locked_until: item.locked_until }, user?.id || '')}
currentLockSubmissionId={queueManager.queue.currentLock?.submissionId}
notes={notes}
isAdmin={isAdmin()}
isSuperuser={isSuperuser()}
queueIsLoading={queueManager.queue.isLoading}
onNoteChange={handleNoteChange}
onApprove={queueManager.performAction}
onResetToPending={queueManager.resetToPending}
onRetryFailed={queueManager.retryFailedItems}
onOpenPhotos={handleOpenPhotos}
onOpenReviewManager={handleOpenReviewManager}
onOpenItemEditor={handleOpenItemEditor}
onClaimSubmission={queueManager.queue.claimSubmission}
onDeleteSubmission={handleDeleteSubmission}
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
onInteractionBlur={(id) => queueManager.markInteracting(id, false)}
/>
</ModerationErrorBoundary>
))}
</div>
{queueManager.items.length <= 10 ? (
// Standard rendering for small lists (no virtual scrolling overhead)
<div className="space-y-6">
{queueManager.items.map((item) => (
<ModerationErrorBoundary key={item.id} submissionId={item.id}>
<QueueItem
item={item}
isMobile={isMobile}
actionLoading={queueManager.actionLoading}
isLockedByMe={queueManager.queue.isLockedByMe(item.id, item.assigned_to, item.locked_until)}
isLockedByOther={queueManager.queue.isLockedByOther(item.id, item.assigned_to, item.locked_until)}
lockStatus={getLockStatus({ assigned_to: item.assigned_to, locked_until: item.locked_until }, user?.id || '')}
currentLockSubmissionId={queueManager.queue.currentLock?.submissionId}
notes={notes}
isAdmin={isAdmin()}
isSuperuser={isSuperuser()}
queueIsLoading={queueManager.queue.isLoading}
onNoteChange={handleNoteChange}
onApprove={queueManager.performAction}
onResetToPending={queueManager.resetToPending}
onRetryFailed={queueManager.retryFailedItems}
onOpenPhotos={handleOpenPhotos}
onOpenReviewManager={handleOpenReviewManager}
onOpenItemEditor={handleOpenItemEditor}
onClaimSubmission={queueManager.queue.claimSubmission}
onDeleteSubmission={handleDeleteSubmission}
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
onInteractionBlur={(id) => queueManager.markInteracting(id, false)}
/>
</ModerationErrorBoundary>
))}
</div>
) : (
// Virtual scrolling for large lists (10+ items)
<div
ref={parentRef}
className="overflow-auto"
style={{
height: '70vh',
contain: 'strict',
}}
>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
>
{virtualizer.getVirtualItems().map((virtualItem) => {
const item = queueManager.items[virtualItem.index];
return (
<div
key={item.id}
data-index={virtualItem.index}
ref={virtualizer.measureElement}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualItem.start}px)`,
}}
className="pb-6"
>
<ModerationErrorBoundary submissionId={item.id}>
<QueueItem
item={item}
isMobile={isMobile}
actionLoading={queueManager.actionLoading}
isLockedByMe={queueManager.queue.isLockedByMe(item.id, item.assigned_to, item.locked_until)}
isLockedByOther={queueManager.queue.isLockedByOther(item.id, item.assigned_to, item.locked_until)}
lockStatus={getLockStatus({ assigned_to: item.assigned_to, locked_until: item.locked_until }, user?.id || '')}
currentLockSubmissionId={queueManager.queue.currentLock?.submissionId}
notes={notes}
isAdmin={isAdmin()}
isSuperuser={isSuperuser()}
queueIsLoading={queueManager.queue.isLoading}
onNoteChange={handleNoteChange}
onApprove={queueManager.performAction}
onResetToPending={queueManager.resetToPending}
onRetryFailed={queueManager.retryFailedItems}
onOpenPhotos={handleOpenPhotos}
onOpenReviewManager={handleOpenReviewManager}
onOpenItemEditor={handleOpenItemEditor}
onClaimSubmission={queueManager.queue.claimSubmission}
onDeleteSubmission={handleDeleteSubmission}
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
onInteractionBlur={(id) => queueManager.markInteracting(id, false)}
/>
</ModerationErrorBoundary>
</div>
);
})}
</div>
</div>
)}
</TooltipProvider>
)}