Implement complete roadmap

This commit is contained in:
gpt-engineer-app[bot]
2025-11-02 21:22:03 +00:00
parent 665918741e
commit 41560d9c42
16 changed files with 770 additions and 144 deletions

View File

@@ -1,4 +1,4 @@
import { memo } from 'react';
import { memo, useCallback } from 'react';
import {
CheckCircle, XCircle, RefreshCw, AlertCircle, Lock, Trash2,
Edit, Info, ExternalLink, ChevronDown, ListTree, Calendar
@@ -60,6 +60,59 @@ export const QueueItemActions = memo(({
onInteractionBlur,
onClaim
}: QueueItemActionsProps) => {
// Memoize all handlers to prevent re-renders
const handleNoteChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
onNoteChange(item.id, e.target.value);
}, [onNoteChange, item.id]);
const handleApprove = useCallback(() => {
onApprove(item, 'approved', notes[item.id]);
}, [onApprove, item, notes]);
const handleReject = useCallback(() => {
onApprove(item, 'rejected', notes[item.id]);
}, [onApprove, item, notes]);
const handleResetToPending = useCallback(() => {
onResetToPending(item);
}, [onResetToPending, item]);
const handleRetryFailed = useCallback(() => {
onRetryFailed(item);
}, [onRetryFailed, item]);
const handleOpenReviewManager = useCallback(() => {
onOpenReviewManager(item.id);
}, [onOpenReviewManager, item.id]);
const handleOpenItemEditor = useCallback(() => {
onOpenItemEditor(item.id);
}, [onOpenItemEditor, item.id]);
const handleDeleteSubmission = useCallback(() => {
onDeleteSubmission(item);
}, [onDeleteSubmission, item]);
const handleFocus = useCallback(() => {
onInteractionFocus(item.id);
}, [onInteractionFocus, item.id]);
const handleBlur = useCallback(() => {
onInteractionBlur(item.id);
}, [onInteractionBlur, item.id]);
const handleReverseNoteChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
onNoteChange(`reverse-${item.id}`, e.target.value);
}, [onNoteChange, item.id]);
const handleReverseApprove = useCallback(() => {
onApprove(item, 'approved', notes[`reverse-${item.id}`]);
}, [onApprove, item, notes]);
const handleReverseReject = useCallback(() => {
onApprove(item, 'rejected', notes[`reverse-${item.id}`]);
}, [onApprove, item, notes]);
return (
<>
{/* Action buttons based on status */}
@@ -142,9 +195,9 @@ export const QueueItemActions = memo(({
id={`notes-${item.id}`}
placeholder="Add notes about your moderation decision..."
value={notes[item.id] || ''}
onChange={(e) => onNoteChange(item.id, e.target.value)}
onFocus={() => onInteractionFocus(item.id)}
onBlur={() => onInteractionBlur(item.id)}
onChange={handleNoteChange}
onFocus={handleFocus}
onBlur={handleBlur}
rows={isMobile ? 2 : 4}
className={!isMobile ? 'min-h-[120px]' : ''}
disabled={isLockedByOther || currentLockSubmissionId !== item.id}
@@ -157,7 +210,7 @@ export const QueueItemActions = memo(({
{item.type === 'content_submission' && (
<>
<Button
onClick={() => onOpenReviewManager(item.id)}
onClick={handleOpenReviewManager}
disabled={actionLoading === item.id || isLockedByOther || currentLockSubmissionId !== item.id}
variant="outline"
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
@@ -171,7 +224,7 @@ export const QueueItemActions = memo(({
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => onOpenItemEditor(item.id)}
onClick={handleOpenItemEditor}
disabled={actionLoading === item.id}
variant="ghost"
className={isMobile ? 'h-11' : ''}
@@ -190,7 +243,7 @@ export const QueueItemActions = memo(({
)}
<Button
onClick={() => onApprove(item, 'approved', notes[item.id])}
onClick={handleApprove}
disabled={actionLoading === item.id || isLockedByOther || currentLockSubmissionId !== item.id}
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
@@ -200,7 +253,7 @@ export const QueueItemActions = memo(({
</Button>
<Button
variant="destructive"
onClick={() => onApprove(item, 'rejected', notes[item.id])}
onClick={handleReject}
disabled={actionLoading === item.id || isLockedByOther || currentLockSubmissionId !== item.id}
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
@@ -224,7 +277,7 @@ export const QueueItemActions = memo(({
</div>
</div>
<Button
onClick={() => onResetToPending(item)}
onClick={handleResetToPending}
disabled={actionLoading === item.id}
variant="outline"
className="w-full"
@@ -247,7 +300,7 @@ export const QueueItemActions = memo(({
</div>
<div className="flex gap-2">
<Button
onClick={() => onOpenReviewManager(item.id)}
onClick={handleOpenReviewManager}
disabled={actionLoading === item.id}
variant="outline"
className="flex-1"
@@ -256,7 +309,7 @@ export const QueueItemActions = memo(({
Review Items
</Button>
<Button
onClick={() => onResetToPending(item)}
onClick={handleResetToPending}
disabled={actionLoading === item.id}
variant="outline"
className="flex-1"
@@ -265,7 +318,7 @@ export const QueueItemActions = memo(({
Reset All
</Button>
<Button
onClick={() => onRetryFailed(item)}
onClick={handleRetryFailed}
disabled={actionLoading === item.id}
className="flex-1 bg-yellow-600 hover:bg-yellow-700"
>
@@ -348,16 +401,16 @@ export const QueueItemActions = memo(({
<Textarea
placeholder="Add notes about reversing this decision..."
value={notes[`reverse-${item.id}`] || ''}
onChange={(e) => onNoteChange(`reverse-${item.id}`, e.target.value)}
onFocus={() => onInteractionFocus(item.id)}
onBlur={() => onInteractionBlur(item.id)}
onChange={handleReverseNoteChange}
onFocus={handleFocus}
onBlur={handleBlur}
rows={2}
/>
<div className={`flex gap-2 ${isMobile ? 'flex-col' : ''}`}>
{item.status === 'approved' && (
<Button
variant="destructive"
onClick={() => onApprove(item, 'rejected', notes[`reverse-${item.id}`])}
onClick={handleReverseReject}
disabled={actionLoading === item.id}
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
@@ -368,7 +421,7 @@ export const QueueItemActions = memo(({
)}
{item.status === 'rejected' && (
<Button
onClick={() => onApprove(item, 'approved', notes[`reverse-${item.id}`])}
onClick={handleReverseApprove}
disabled={actionLoading === item.id}
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
@@ -387,7 +440,7 @@ export const QueueItemActions = memo(({
<div className="pt-2">
<Button
variant="destructive"
onClick={() => onDeleteSubmission(item)}
onClick={handleDeleteSubmission}
disabled={actionLoading === item.id}
className={`w-full ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}