mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 11:51:14 -05:00
Transitioned from Plan to Build mode
Replit-Commit-Author: Agent Replit-Commit-Session-Id: e14c2292-b0e5-43fe-b301-a4ad668949e9 Replit-Commit-Checkpoint-Type: full_checkpoint
This commit is contained in:
@@ -97,6 +97,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
const { user } = useAuth();
|
||||
const queue = useModerationQueue();
|
||||
const fetchInProgressRef = useRef(false);
|
||||
const itemsRef = useRef<ModerationItem[]>([]);
|
||||
|
||||
// Get admin settings for polling configuration
|
||||
const {
|
||||
@@ -110,6 +111,11 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
const refreshStrategy = getAutoRefreshStrategy();
|
||||
const preserveInteraction = getPreserveInteractionState();
|
||||
|
||||
// Sync itemsRef with items state
|
||||
useEffect(() => {
|
||||
itemsRef.current = items;
|
||||
}, [items]);
|
||||
|
||||
const fetchItems = useCallback(async (entityFilter: EntityFilter = 'all', statusFilter: StatusFilter = 'pending', silent = false) => {
|
||||
if (!user) {
|
||||
return;
|
||||
@@ -378,8 +384,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
});
|
||||
setSubmissionMemo(newMemoMap);
|
||||
|
||||
// Apply smart merge for state updates
|
||||
const mergeResult = smartMergeArray(items, moderationItems, {
|
||||
// Apply smart merge for state updates using ref for current items
|
||||
const mergeResult = smartMergeArray(itemsRef.current, moderationItems, {
|
||||
compareFields: ['status', 'reviewed_at', 'reviewer_notes'],
|
||||
preserveOrder: silent && preserveInteraction,
|
||||
addToTop: false,
|
||||
@@ -413,7 +419,6 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
user,
|
||||
refreshStrategy,
|
||||
preserveInteraction,
|
||||
interactingWith,
|
||||
toast
|
||||
]);
|
||||
|
||||
@@ -421,32 +426,40 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
const debouncedEntityFilter = useDebounce(activeEntityFilter, 500);
|
||||
const debouncedStatusFilter = useDebounce(activeStatusFilter, 500);
|
||||
|
||||
// Store latest filter values in ref to avoid dependency issues
|
||||
const filtersRef = useRef({ entityFilter: debouncedEntityFilter, statusFilter: debouncedStatusFilter });
|
||||
useEffect(() => {
|
||||
filtersRef.current = { entityFilter: debouncedEntityFilter, statusFilter: debouncedStatusFilter };
|
||||
}, [debouncedEntityFilter, debouncedStatusFilter]);
|
||||
|
||||
// Expose refresh method via ref
|
||||
useImperativeHandle(ref, () => ({
|
||||
refresh: () => {
|
||||
fetchItems(debouncedEntityFilter, debouncedStatusFilter, false); // Manual refresh shows loading
|
||||
fetchItems(filtersRef.current.entityFilter, filtersRef.current.statusFilter, false); // Manual refresh shows loading
|
||||
}
|
||||
}), [debouncedEntityFilter, debouncedStatusFilter, fetchItems]);
|
||||
}), [fetchItems]);
|
||||
|
||||
// Initial fetch on mount and filter changes
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
fetchItems(debouncedEntityFilter, debouncedStatusFilter, false); // Show loading
|
||||
}
|
||||
}, [debouncedEntityFilter, debouncedStatusFilter, user, fetchItems]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedEntityFilter, debouncedStatusFilter, user]);
|
||||
|
||||
// Polling for auto-refresh
|
||||
useEffect(() => {
|
||||
if (!user || refreshMode !== 'auto' || isInitialLoad) return;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
fetchItems(debouncedEntityFilter, debouncedStatusFilter, true); // Silent refresh
|
||||
fetchItems(filtersRef.current.entityFilter, filtersRef.current.statusFilter, true); // Silent refresh
|
||||
}, pollInterval);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [user, refreshMode, pollInterval, debouncedEntityFilter, debouncedStatusFilter, isInitialLoad, fetchItems]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user, refreshMode, pollInterval, isInitialLoad]);
|
||||
|
||||
// Real-time subscription for lock status
|
||||
useEffect(() => {
|
||||
@@ -1623,6 +1636,12 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
placeholder="Add notes about your moderation decision..."
|
||||
value={notes[item.id] || ''}
|
||||
onChange={(e) => setNotes(prev => ({ ...prev, [item.id]: e.target.value }))}
|
||||
onFocus={() => setInteractingWith(prev => new Set(prev).add(item.id))}
|
||||
onBlur={() => setInteractingWith(prev => {
|
||||
const next = new Set(prev);
|
||||
next.delete(item.id);
|
||||
return next;
|
||||
})}
|
||||
rows={2}
|
||||
/>
|
||||
</div>
|
||||
@@ -1772,6 +1791,12 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
placeholder="Add notes about reversing this decision..."
|
||||
value={notes[`reverse-${item.id}`] || ''}
|
||||
onChange={(e) => setNotes(prev => ({ ...prev, [`reverse-${item.id}`]: e.target.value }))}
|
||||
onFocus={() => setInteractingWith(prev => new Set(prev).add(item.id))}
|
||||
onBlur={() => setInteractingWith(prev => {
|
||||
const next = new Set(prev);
|
||||
next.delete(item.id);
|
||||
return next;
|
||||
})}
|
||||
rows={2}
|
||||
/>
|
||||
<div className={`flex gap-2 ${isMobile ? 'flex-col' : ''}`}>
|
||||
|
||||
Reference in New Issue
Block a user