mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 01:31:12 -05:00
Integrate transaction resilience hook
Integrate the `useTransactionResilience` hook into `SubmissionReviewManager.tsx` to add timeout detection, auto-release functionality, and idempotency key management to moderation actions. The `handleApprove` and `handleReject` functions have been updated to use the `executeTransaction` wrapper for these operations.
This commit is contained in:
@@ -6,6 +6,7 @@ import { handleError, getErrorMessage } from '@/lib/errorHandler';
|
|||||||
import { invokeWithTracking } from '@/lib/edgeFunctionTracking';
|
import { invokeWithTracking } from '@/lib/edgeFunctionTracking';
|
||||||
import { moderationReducer, canApprove, canReject, hasActiveLock } from '@/lib/moderationStateMachine';
|
import { moderationReducer, canApprove, canReject, hasActiveLock } from '@/lib/moderationStateMachine';
|
||||||
import { useLockMonitor } from '@/lib/moderation/lockMonitor';
|
import { useLockMonitor } from '@/lib/moderation/lockMonitor';
|
||||||
|
import { useTransactionResilience } from '@/hooks/useTransactionResilience';
|
||||||
import {
|
import {
|
||||||
fetchSubmissionItems,
|
fetchSubmissionItems,
|
||||||
buildDependencyTree,
|
buildDependencyTree,
|
||||||
@@ -92,6 +93,15 @@ export function SubmissionReviewManager({
|
|||||||
// Lock monitoring integration
|
// Lock monitoring integration
|
||||||
const { extendLock } = useLockMonitor(state, dispatch, submissionId);
|
const { extendLock } = useLockMonitor(state, dispatch, submissionId);
|
||||||
|
|
||||||
|
// Transaction resilience (timeout detection & auto-release)
|
||||||
|
const { executeTransaction } = useTransactionResilience({
|
||||||
|
submissionId,
|
||||||
|
timeoutMs: 30000, // 30s timeout
|
||||||
|
autoReleaseOnUnload: true,
|
||||||
|
autoReleaseOnInactivity: true,
|
||||||
|
inactivityMinutes: 10,
|
||||||
|
});
|
||||||
|
|
||||||
// Moderation actions
|
// Moderation actions
|
||||||
const { escalateSubmission } = useModerationActions({
|
const { escalateSubmission } = useModerationActions({
|
||||||
user,
|
user,
|
||||||
@@ -230,6 +240,7 @@ export function SubmissionReviewManager({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectedItems = items.filter(item => selectedItemIds.has(item.id));
|
const selectedItems = items.filter(item => selectedItemIds.has(item.id));
|
||||||
|
const selectedIds = Array.from(selectedItemIds);
|
||||||
|
|
||||||
// Transition: reviewing → approving
|
// Transition: reviewing → approving
|
||||||
dispatch({ type: 'START_APPROVAL' });
|
dispatch({ type: 'START_APPROVAL' });
|
||||||
@@ -259,6 +270,7 @@ export function SubmissionReviewManager({
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
setValidationResults(validationResultsMap);
|
setValidationResults(validationResultsMap);
|
||||||
|
|
||||||
// Check for blocking errors
|
// Check for blocking errors
|
||||||
@@ -324,15 +336,20 @@ export function SubmissionReviewManager({
|
|||||||
return; // Ask for confirmation
|
return; // Ask for confirmation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proceed with approval
|
// Proceed with approval - wrapped with transaction resilience
|
||||||
|
await executeTransaction(
|
||||||
|
'approval',
|
||||||
|
selectedIds,
|
||||||
|
async (idempotencyKey) => {
|
||||||
const { supabase } = await import('@/integrations/supabase/client');
|
const { supabase } = await import('@/integrations/supabase/client');
|
||||||
|
|
||||||
// Call the edge function for backend processing
|
// Call the edge function for backend processing
|
||||||
const { data, error, requestId } = await invokeWithTracking(
|
const { data, error, requestId } = await invokeWithTracking(
|
||||||
'process-selective-approval',
|
'process-selective-approval',
|
||||||
{
|
{
|
||||||
itemIds: Array.from(selectedItemIds),
|
itemIds: selectedIds,
|
||||||
submissionId
|
submissionId,
|
||||||
|
idempotencyKey, // Pass idempotency key to edge function
|
||||||
},
|
},
|
||||||
user?.id
|
user?.id
|
||||||
);
|
);
|
||||||
@@ -350,7 +367,7 @@ export function SubmissionReviewManager({
|
|||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: 'Items Approved',
|
title: 'Items Approved',
|
||||||
description: `Successfully approved ${selectedItemIds.size} item(s)${requestId ? ` (Request: ${requestId.substring(0, 8)})` : ''}`,
|
description: `Successfully approved ${selectedIds.length} item(s)${requestId ? ` (Request: ${requestId.substring(0, 8)})` : ''}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ApprovalResult { success: boolean; item_id: string; error?: string }
|
interface ApprovalResult { success: boolean; item_id: string; error?: string }
|
||||||
@@ -374,7 +391,7 @@ export function SubmissionReviewManager({
|
|||||||
// If ALL items failed, don't close dialog - show errors
|
// If ALL items failed, don't close dialog - show errors
|
||||||
if (allFailed) {
|
if (allFailed) {
|
||||||
dispatch({ type: 'ERROR', payload: { error: 'All items failed' } });
|
dispatch({ type: 'ERROR', payload: { error: 'All items failed' } });
|
||||||
return;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset warning confirmation state after approval
|
// Reset warning confirmation state after approval
|
||||||
@@ -382,6 +399,10 @@ export function SubmissionReviewManager({
|
|||||||
|
|
||||||
onComplete();
|
onComplete();
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
dispatch({ type: 'ERROR', payload: { error: getErrorMessage(error) } });
|
dispatch({ type: 'ERROR', payload: { error: getErrorMessage(error) } });
|
||||||
handleError(error, {
|
handleError(error, {
|
||||||
@@ -438,11 +459,18 @@ export function SubmissionReviewManager({
|
|||||||
|
|
||||||
if (!user?.id) return;
|
if (!user?.id) return;
|
||||||
|
|
||||||
|
const selectedItems = items.filter(item => selectedItemIds.has(item.id));
|
||||||
|
const selectedIds = selectedItems.map(item => item.id);
|
||||||
|
|
||||||
// Transition: reviewing → rejecting
|
// Transition: reviewing → rejecting
|
||||||
dispatch({ type: 'START_REJECTION' });
|
dispatch({ type: 'START_REJECTION' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selectedItems = items.filter(item => selectedItemIds.has(item.id));
|
// Wrap rejection with transaction resilience
|
||||||
|
await executeTransaction(
|
||||||
|
'rejection',
|
||||||
|
selectedIds,
|
||||||
|
async (idempotencyKey) => {
|
||||||
await rejectSubmissionItems(selectedItems, reason, user.id, cascade);
|
await rejectSubmissionItems(selectedItems, reason, user.id, cascade);
|
||||||
|
|
||||||
// Transition: rejecting → complete
|
// Transition: rejecting → complete
|
||||||
@@ -455,6 +483,10 @@ export function SubmissionReviewManager({
|
|||||||
|
|
||||||
onComplete();
|
onComplete();
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
dispatch({ type: 'ERROR', payload: { error: getErrorMessage(error) } });
|
dispatch({ type: 'ERROR', payload: { error: getErrorMessage(error) } });
|
||||||
handleError(error, {
|
handleError(error, {
|
||||||
|
|||||||
Reference in New Issue
Block a user