diff --git a/src/components/moderation/ReportsQueue.tsx b/src/components/moderation/ReportsQueue.tsx index 1373e33a..0cfb58d8 100644 --- a/src/components/moderation/ReportsQueue.tsx +++ b/src/components/moderation/ReportsQueue.tsx @@ -333,16 +333,40 @@ export const ReportsQueue = forwardRef((props, ref) => { const handleReportAction = async (reportId: string, action: 'reviewed' | 'dismissed') => { setActionLoading(reportId); try { + // Fetch report details for audit log + const report = reports.find(r => r.id === reportId); + const { error } = await supabase .from('reports') .update({ status: action, + reviewed_by: user?.id, reviewed_at: new Date().toISOString(), }) .eq('id', reportId); if (error) throw error; + // Log audit trail for report resolution + if (user && report) { + try { + await supabase.rpc('log_admin_action', { + _admin_user_id: user.id, + _target_user_id: report.reported_by, + _action: action === 'reviewed' ? 'report_resolved' : 'report_dismissed', + _details: { + report_id: reportId, + reported_entity_type: report.reported_entity_type, + reported_entity_id: report.reported_entity_id, + report_reason: report.reason, + action: action + } + }); + } catch (auditError) { + console.error('Failed to log report action audit:', auditError); + } + } + handleSuccess(`Report ${action}`, `The report has been marked as ${action}`); // Remove report from queue diff --git a/src/hooks/moderation/useModerationActions.ts b/src/hooks/moderation/useModerationActions.ts index 05e1bbec..66ee8bc0 100644 --- a/src/hooks/moderation/useModerationActions.ts +++ b/src/hooks/moderation/useModerationActions.ts @@ -235,6 +235,25 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio if (error) throw error; + // Log audit trail for review moderation + if (table === 'reviews' && user) { + try { + await supabase.rpc('log_admin_action', { + _admin_user_id: user.id, + _target_user_id: item.user_id, + _action: `review_${action}`, + _details: { + review_id: item.id, + entity_type: item.entity_type, + entity_id: item.entity_id, + moderator_notes: moderatorNotes + } + }); + } catch (auditError) { + logger.error('Failed to log review moderation audit', { error: auditError }); + } + } + toast({ title: `Content ${action}`, description: `The ${item.type} has been ${action}`, @@ -266,10 +285,35 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio onActionStart(item.id); try { + // Fetch submission details for audit log + const { data: submission } = await supabase + .from('content_submissions') + .select('user_id, submission_type, status') + .eq('id', item.id) + .single(); + const { error } = await supabase.from('content_submissions').delete().eq('id', item.id); if (error) throw error; + // Log audit trail for deletion + if (user && submission) { + try { + await supabase.rpc('log_admin_action', { + _admin_user_id: user.id, + _target_user_id: submission.user_id, + _action: 'submission_deleted', + _details: { + submission_id: item.id, + submission_type: submission.submission_type, + status_when_deleted: submission.status + } + }); + } catch (auditError) { + logger.error('Failed to log submission deletion audit', { error: auditError }); + } + } + toast({ title: 'Submission deleted', description: 'The submission has been permanently deleted', @@ -302,6 +346,23 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio const { resetRejectedItemsToPending } = await import('@/lib/submissionItemsService'); await resetRejectedItemsToPending(item.id); + // Log audit trail for reset + if (user) { + try { + await supabase.rpc('log_admin_action', { + _admin_user_id: user.id, + _target_user_id: item.user_id, + _action: 'submission_reset', + _details: { + submission_id: item.id, + submission_type: item.submission_type + } + }); + } catch (auditError) { + logger.error('Failed to log submission reset audit', { error: auditError }); + } + } + toast({ title: 'Reset Complete', description: 'Submission and all items have been reset to pending status', @@ -355,6 +416,25 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio if (error) throw error; + // Log audit trail for retry + if (user) { + try { + await supabase.rpc('log_admin_action', { + _admin_user_id: user.id, + _target_user_id: item.user_id, + _action: 'submission_retried', + _details: { + submission_id: item.id, + submission_type: item.submission_type, + items_retried: failedItems.length, + request_id: requestId + } + }); + } catch (auditError) { + logger.error('Failed to log submission retry audit', { error: auditError }); + } + } + toast({ title: 'Items Retried', description: `Successfully retried ${failedItems.length} failed item(s)${requestId ? ` (Request: ${requestId.substring(0, 8)})` : ''}`, diff --git a/src/lib/submissionItemsService.ts b/src/lib/submissionItemsService.ts index e965c9a3..5e8673ef 100644 --- a/src/lib/submissionItemsService.ts +++ b/src/lib/submissionItemsService.ts @@ -1155,6 +1155,13 @@ export async function escalateSubmission( throw new Error('User authentication required to escalate submission'); } + // Fetch submission details for audit log + const { data: submission } = await supabase + .from('content_submissions') + .select('user_id, submission_type') + .eq('id', submissionId) + .single(); + const { error } = await supabase .from('content_submissions') .update({ @@ -1166,4 +1173,22 @@ export async function escalateSubmission( .eq('id', submissionId); if (error) throw error; + + // Log audit trail for escalation + if (submission) { + try { + await supabase.rpc('log_admin_action', { + _admin_user_id: userId, + _target_user_id: submission.user_id, + _action: 'submission_escalated', + _details: { + submission_id: submissionId, + submission_type: submission.submission_type, + escalation_reason: reason + } + }); + } catch (auditError) { + logger.error('Failed to log escalation audit', { error: auditError }); + } + } } diff --git a/supabase/functions/process-selective-approval/index.ts b/supabase/functions/process-selective-approval/index.ts index 11984991..a886ffdd 100644 --- a/supabase/functions/process-selective-approval/index.ts +++ b/supabase/functions/process-selective-approval/index.ts @@ -602,6 +602,35 @@ serve(async (req) => { console.error('[APPROVAL] Failed to update submission status:', { error: updateError.message }); } + // Log audit trail for submission action + try { + const approvedCount = approvalResults.filter(r => r.success).length; + const rejectedCount = approvalResults.filter(r => !r.success).length; + + await supabaseClient.rpc('log_admin_action', { + _admin_user_id: authenticatedUserId, + _target_user_id: submission.user_id, + _action: finalStatus === 'approved' + ? 'submission_approved' + : finalStatus === 'partially_approved' + ? 'submission_partially_approved' + : 'submission_rejected', + _details: { + submission_id: submissionId, + submission_type: submission.submission_type, + items_approved: approvedCount, + items_rejected: rejectedCount, + total_items: approvalResults.length, + final_status: finalStatus, + has_dependency_failure: hasDependencyFailure, + reviewer_notes: reviewerNotes + } + }); + } catch (auditError) { + // Log but don't fail the operation + console.error('[AUDIT] Failed to log admin action:', auditError); + } + const duration = endRequest(tracking); return new Response(