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 { useCallback } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { useToast } from '@/hooks/use-toast';
import { logger } from '@/lib/logger';
@@ -38,15 +39,21 @@ export interface ModerationActions {
export function useModerationActions(config: ModerationActionsConfig): ModerationActions {
const { user, onActionStart, onActionComplete } = config;
const { toast } = useToast();
const queryClient = useQueryClient();
/**
* Perform moderation action (approve/reject)
* Perform moderation action (approve/reject) with optimistic updates
*/
const performAction = useCallback(
async (item: ModerationItem, action: 'approved' | 'rejected', moderatorNotes?: string) => {
onActionStart(item.id);
try {
const performActionMutation = useMutation({
mutationFn: async ({
item,
action,
moderatorNotes
}: {
item: ModerationItem;
action: 'approved' | 'rejected';
moderatorNotes?: string;
}) => {
// Handle photo submissions
if (action === 'approved' && item.submission_type === 'photo') {
const { data: photoSubmission, error: fetchError } = await supabase
@@ -263,20 +270,73 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
description: `The ${item.type} has been ${action}`,
});
logger.log(`✅ Action ${action} completed for ${item.id}`);
} catch (error: unknown) {
logger.error('❌ Error performing action:', { error: getErrorMessage(error) });
toast({
title: 'Error',
description: getErrorMessage(error) || `Failed to ${action} content`,
variant: 'destructive',
});
throw error;
} finally {
onActionComplete();
}
logger.log(`✅ Action ${action} completed for ${item.id}`);
return { item, action };
},
[user, toast, onActionStart, onActionComplete]
onMutate: async ({ item, action }) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['moderation-queue'] });
// Snapshot previous value
const previousData = queryClient.getQueryData(['moderation-queue']);
// Optimistically update cache
queryClient.setQueriesData({ queryKey: ['moderation-queue'] }, (old: any) => {
if (!old?.submissions) return old;
return {
...old,
submissions: old.submissions.map((i: ModerationItem) =>
i.id === item.id
? {
...i,
status: action,
_optimistic: true,
reviewed_at: new Date().toISOString(),
reviewer_id: user?.id,
}
: i
),
};
});
return { previousData };
},
onError: (error, variables, context) => {
// Rollback on error
if (context?.previousData) {
queryClient.setQueryData(['moderation-queue'], context.previousData);
}
logger.error('❌ Error performing action:', { error: getErrorMessage(error) });
toast({
title: 'Action Failed',
description: getErrorMessage(error) || `Failed to ${variables.action} content`,
variant: 'destructive',
});
},
onSuccess: (data) => {
toast({
title: `Content ${data.action}`,
description: `The ${data.item.type} has been ${data.action}`,
});
},
onSettled: () => {
// Always refetch to ensure consistency
queryClient.invalidateQueries({ queryKey: ['moderation-queue'] });
onActionComplete();
},
});
/**
* Wrapper for performAction mutation to maintain API compatibility
*/
const performAction = useCallback(
async (item: ModerationItem, action: 'approved' | 'rejected', moderatorNotes?: string) => {
onActionStart(item.id);
await performActionMutation.mutateAsync({ item, action, moderatorNotes });
},
[onActionStart, performActionMutation]
);
/**