Implement Phase 6: Lock Management

This commit is contained in:
gpt-engineer-app[bot]
2025-10-12 22:57:37 +00:00
parent 7e6b99a68b
commit 0d0e352a1e
6 changed files with 281 additions and 70 deletions

View File

@@ -50,11 +50,15 @@ interface ModerationItem {
import { ValidationSummary } from './ValidationSummary';
import type { ValidationResult } from '@/lib/entityValidationSchemas';
import type { LockStatus } from '@/lib/moderation/lockHelpers';
interface QueueItemProps {
item: ModerationItem;
isMobile: boolean;
actionLoading: string | null;
lockedSubmissions: Set<string>;
isLockedByMe: boolean;
isLockedByOther: boolean;
lockStatus: LockStatus;
currentLockSubmissionId?: string;
notes: Record<string, string>;
isAdmin: boolean;
@@ -88,7 +92,9 @@ export const QueueItem = memo(({
item,
isMobile,
actionLoading,
lockedSubmissions,
isLockedByMe,
isLockedByOther,
lockStatus,
currentLockSubmissionId,
notes,
isAdmin,
@@ -162,7 +168,7 @@ export const QueueItem = memo(({
Needs Retry
</Badge>
)}
{lockedSubmissions.has(item.id) && item.type === 'content_submission' && (
{isLockedByOther && item.type === 'content_submission' && (
<Badge variant="outline" className="bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 border-orange-300 dark:border-orange-700">
<Lock className="w-3 h-3 mr-1" />
Locked by Another Moderator
@@ -427,7 +433,7 @@ export const QueueItem = memo(({
{(item.status === 'pending' || item.status === 'flagged') && (
<>
{/* Claim button for unclaimed submissions */}
{!lockedSubmissions.has(item.id) && currentLockSubmissionId !== item.id && (
{!isLockedByOther && currentLockSubmissionId !== item.id && (
<div className="mb-4">
<Alert className="border-blue-200 bg-blue-50 dark:bg-blue-950/20">
<AlertCircle className="h-4 w-4 text-blue-600" />
@@ -460,7 +466,7 @@ export const QueueItem = memo(({
onFocus={() => onInteractionFocus(item.id)}
onBlur={() => onInteractionBlur(item.id)}
rows={2}
disabled={lockedSubmissions.has(item.id) || currentLockSubmissionId !== item.id}
disabled={isLockedByOther || currentLockSubmissionId !== item.id}
/>
</div>
@@ -469,7 +475,7 @@ export const QueueItem = memo(({
{item.type === 'content_submission' && (
<Button
onClick={() => onOpenReviewManager(item.id)}
disabled={actionLoading === item.id || lockedSubmissions.has(item.id) || currentLockSubmissionId !== item.id}
disabled={actionLoading === item.id || isLockedByOther || currentLockSubmissionId !== item.id}
variant="outline"
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
@@ -481,7 +487,7 @@ export const QueueItem = memo(({
<Button
onClick={() => onApprove(item, 'approved', notes[item.id])}
disabled={actionLoading === item.id || lockedSubmissions.has(item.id) || currentLockSubmissionId !== item.id}
disabled={actionLoading === item.id || isLockedByOther || currentLockSubmissionId !== item.id}
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
>
@@ -491,7 +497,7 @@ export const QueueItem = memo(({
<Button
variant="destructive"
onClick={() => onApprove(item, 'rejected', notes[item.id])}
disabled={actionLoading === item.id || lockedSubmissions.has(item.id) || currentLockSubmissionId !== item.id}
disabled={actionLoading === item.id || isLockedByOther || currentLockSubmissionId !== item.id}
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
>
@@ -664,9 +670,8 @@ export const QueueItem = memo(({
if (prevProps.notes[`reverse-${prevProps.item.id}`] !== nextProps.notes[`reverse-${nextProps.item.id}`]) return false;
// Check lock status
const prevLocked = prevProps.lockedSubmissions.has(prevProps.item.id);
const nextLocked = nextProps.lockedSubmissions.has(nextProps.item.id);
if (prevLocked !== nextLocked) return false;
if (prevProps.isLockedByOther !== nextProps.isLockedByOther) return false;
if (prevProps.lockStatus !== nextProps.lockStatus) return false;
// Deep comparison of critical fields (use strict equality for reference stability)
if (prevProps.item.status !== nextProps.item.status) return false;