mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 11:51:14 -05:00
feat: Implement direct edit button for queue items
This commit is contained in:
@@ -6,6 +6,7 @@ import { useUserRole } from '@/hooks/useUserRole';
|
|||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { PhotoModal } from './PhotoModal';
|
import { PhotoModal } from './PhotoModal';
|
||||||
import { SubmissionReviewManager } from './SubmissionReviewManager';
|
import { SubmissionReviewManager } from './SubmissionReviewManager';
|
||||||
|
import { ItemEditDialog } from './ItemEditDialog';
|
||||||
import { useIsMobile } from '@/hooks/use-mobile';
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
import { useAdminSettings } from '@/hooks/useAdminSettings';
|
import { useAdminSettings } from '@/hooks/useAdminSettings';
|
||||||
import { useModerationQueueManager } from '@/hooks/moderation';
|
import { useModerationQueueManager } from '@/hooks/moderation';
|
||||||
@@ -20,6 +21,7 @@ import { AutoRefreshIndicator } from './AutoRefreshIndicator';
|
|||||||
import { NewItemsAlert } from './NewItemsAlert';
|
import { NewItemsAlert } from './NewItemsAlert';
|
||||||
import { EmptyQueueState } from './EmptyQueueState';
|
import { EmptyQueueState } from './EmptyQueueState';
|
||||||
import { QueuePagination } from './QueuePagination';
|
import { QueuePagination } from './QueuePagination';
|
||||||
|
import { fetchSubmissionItems, type SubmissionItemWithDeps } from '@/lib/submissionItemsService';
|
||||||
import type { ModerationQueueRef } from '@/types/moderation';
|
import type { ModerationQueueRef } from '@/types/moderation';
|
||||||
import type { PhotoItem } from '@/types/photos';
|
import type { PhotoItem } from '@/types/photos';
|
||||||
|
|
||||||
@@ -62,6 +64,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState(0);
|
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState(0);
|
||||||
const [reviewManagerOpen, setReviewManagerOpen] = useState(false);
|
const [reviewManagerOpen, setReviewManagerOpen] = useState(false);
|
||||||
const [selectedSubmissionId, setSelectedSubmissionId] = useState<string | null>(null);
|
const [selectedSubmissionId, setSelectedSubmissionId] = useState<string | null>(null);
|
||||||
|
const [showItemEditDialog, setShowItemEditDialog] = useState(false);
|
||||||
|
const [editingItem, setEditingItem] = useState<SubmissionItemWithDeps | null>(null);
|
||||||
|
|
||||||
// UI-specific handlers
|
// UI-specific handlers
|
||||||
const handleNoteChange = (id: string, value: string) => {
|
const handleNoteChange = (id: string, value: string) => {
|
||||||
@@ -79,6 +83,36 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
setReviewManagerOpen(true);
|
setReviewManagerOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenItemEditor = async (submissionId: string) => {
|
||||||
|
try {
|
||||||
|
const items = await fetchSubmissionItems(submissionId);
|
||||||
|
|
||||||
|
// Find first pending item, fallback to first available
|
||||||
|
let itemToEdit = items.find(item => item.status === 'pending');
|
||||||
|
if (!itemToEdit && items.length > 0) {
|
||||||
|
itemToEdit = items[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemToEdit) {
|
||||||
|
setEditingItem(itemToEdit);
|
||||||
|
setShowItemEditDialog(true);
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
title: 'No Items Found',
|
||||||
|
description: 'This submission has no items to edit',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching items for edit:', error);
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'Failed to load submission items',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Expose imperative API
|
// Expose imperative API
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
refresh: async () => {
|
refresh: async () => {
|
||||||
@@ -179,6 +213,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
onRetryFailed={queueManager.retryFailedItems}
|
onRetryFailed={queueManager.retryFailedItems}
|
||||||
onOpenPhotos={handleOpenPhotos}
|
onOpenPhotos={handleOpenPhotos}
|
||||||
onOpenReviewManager={handleOpenReviewManager}
|
onOpenReviewManager={handleOpenReviewManager}
|
||||||
|
onOpenItemEditor={handleOpenItemEditor}
|
||||||
onClaimSubmission={queueManager.queue.claimSubmission}
|
onClaimSubmission={queueManager.queue.claimSubmission}
|
||||||
onDeleteSubmission={queueManager.deleteSubmission}
|
onDeleteSubmission={queueManager.deleteSubmission}
|
||||||
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
|
onInteractionFocus={(id) => queueManager.markInteracting(id, true)}
|
||||||
@@ -218,6 +253,19 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
|||||||
onComplete={() => setReviewManagerOpen(false)}
|
onComplete={() => setReviewManagerOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{editingItem && (
|
||||||
|
<ItemEditDialog
|
||||||
|
item={editingItem}
|
||||||
|
open={showItemEditDialog}
|
||||||
|
onOpenChange={setShowItemEditDialog}
|
||||||
|
onComplete={async () => {
|
||||||
|
setShowItemEditDialog(false);
|
||||||
|
setEditingItem(null);
|
||||||
|
await queueManager.refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ interface QueueItemProps {
|
|||||||
onRetryFailed: (item: ModerationItem) => void;
|
onRetryFailed: (item: ModerationItem) => void;
|
||||||
onOpenPhotos: (photos: any[], index: number) => void;
|
onOpenPhotos: (photos: any[], index: number) => void;
|
||||||
onOpenReviewManager: (submissionId: string) => void;
|
onOpenReviewManager: (submissionId: string) => void;
|
||||||
|
onOpenItemEditor: (submissionId: string) => void;
|
||||||
onClaimSubmission: (submissionId: string) => void;
|
onClaimSubmission: (submissionId: string) => void;
|
||||||
onDeleteSubmission: (item: ModerationItem) => void;
|
onDeleteSubmission: (item: ModerationItem) => void;
|
||||||
onInteractionFocus: (id: string) => void;
|
onInteractionFocus: (id: string) => void;
|
||||||
@@ -75,6 +76,7 @@ export const QueueItem = memo(({
|
|||||||
onRetryFailed,
|
onRetryFailed,
|
||||||
onOpenPhotos,
|
onOpenPhotos,
|
||||||
onOpenReviewManager,
|
onOpenReviewManager,
|
||||||
|
onOpenItemEditor,
|
||||||
onClaimSubmission,
|
onClaimSubmission,
|
||||||
onDeleteSubmission,
|
onDeleteSubmission,
|
||||||
onInteractionFocus,
|
onInteractionFocus,
|
||||||
@@ -430,7 +432,7 @@ export const QueueItem = memo(({
|
|||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onOpenReviewManager(item.id)}
|
onClick={() => onOpenItemEditor(item.id)}
|
||||||
disabled={actionLoading === item.id}
|
disabled={actionLoading === item.id}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className={isMobile ? 'h-11' : ''}
|
className={isMobile ? 'h-11' : ''}
|
||||||
@@ -441,7 +443,7 @@ export const QueueItem = memo(({
|
|||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>Edit submission items directly as a moderator</p>
|
<p>Quick edit first pending item</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user