mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
Implement complete roadmap
This commit is contained in:
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user