// ======================================== // Queue Panel // ======================================== // Content panel for Queue tab in IssueHub import { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import { AlertCircle, CheckCircle, Clock, ListTodo, GitMerge, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; import { Button } from '@/components/ui/Button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/Select'; import { QueueCard } from '@/components/issue/queue/QueueCard'; import { QueueBoard } from '@/components/issue/queue/QueueBoard'; import { SolutionDrawer } from '@/components/issue/queue/SolutionDrawer'; import { useIssueQueue, useIssueQueueById, useQueueHistory, useQueueMutations } from '@/hooks'; import type { QueueItem } from '@/lib/api'; // ========== Loading Skeleton ========== function QueuePanelSkeleton() { return (
{/* Stats Cards Skeleton */}
{[1, 2, 3, 4].map((i) => (
))}
{/* Queue Cards Skeleton */}
{[1, 2].map((i) => (
))}
); } // ========== Empty State ========== function QueueEmptyState() { const { formatMessage } = useIntl(); return (

{formatMessage({ id: 'issues.queue.emptyState.title' })}

{formatMessage({ id: 'issues.queue.emptyState.description' })}

); } // ========== Main Panel Component ========== export function QueuePanel() { const { formatMessage } = useIntl(); const [selectedItem, setSelectedItem] = useState(null); const [selectedQueueId, setSelectedQueueId] = useState(''); const activeQueueQuery = useIssueQueue(); const { data: historyIndex } = useQueueHistory(); const selectedQueueQuery = useIssueQueueById(selectedQueueId); const { activateQueue, deactivateQueue, deleteQueue, mergeQueues, splitQueue, isActivating, isDeactivating, isDeleting, isMerging, isSplitting, } = useQueueMutations(); const queue = selectedQueueId && selectedQueueQuery.data ? selectedQueueQuery.data : activeQueueQuery.data; const isLoading = activeQueueQuery.isLoading || (selectedQueueId ? selectedQueueQuery.isLoading && !selectedQueueQuery.data : false); const error = activeQueueQuery.error || selectedQueueQuery.error; const taskCount = queue?.tasks?.length || 0; const solutionCount = queue?.solutions?.length || 0; const conflictCount = queue?.conflicts?.length || 0; const groupCount = Object.keys(queue?.grouped_items || {}).length; const totalItems = taskCount + solutionCount; const activeQueueId = historyIndex?.active_queue_id || null; const activeQueueIds = historyIndex?.active_queue_ids || []; const queueId = queue?.id; // Keep selector in sync with active queue id useEffect(() => { if (selectedQueueId) return; if (activeQueueId) setSelectedQueueId(activeQueueId); else if (queueId) setSelectedQueueId(queueId); }, [activeQueueId, queueId, selectedQueueId]); const handleActivate = async (queueId: string) => { try { await activateQueue(queueId); } catch (err) { console.error('Failed to activate queue:', err); } }; const handleDeactivate = async () => { try { await deactivateQueue(); } catch (err) { console.error('Failed to deactivate queue:', err); } }; const handleDelete = async (queueId: string) => { try { await deleteQueue(queueId); } catch (err) { console.error('Failed to delete queue:', err); } }; const handleMerge = async (sourceId: string, targetId: string) => { try { await mergeQueues(sourceId, targetId); } catch (err) { console.error('Failed to merge queues:', err); } }; const handleSplit = async (sourceQueueId: string, itemIds: string[]) => { try { await splitQueue(sourceQueueId, itemIds); } catch (err) { console.error('Failed to split queue:', err); } }; const handleItemClick = (item: QueueItem) => { setSelectedItem(item); }; const handleCloseDrawer = () => { setSelectedItem(null); }; if (isLoading) { return ; } if (error) { return (

{formatMessage({ id: 'issues.queue.error.title' })}

{(error as Error).message || formatMessage({ id: 'issues.queue.error.message' })}

); } if (!queue || totalItems === 0) { return ; } // Check if queue is active (multi-queue index preferred) const isActive = queueId ? activeQueueIds.includes(queueId) : totalItems > 0 && conflictCount === 0; return (
{/* Queue History / Active Queue Selector */} {historyIndex && (
{formatMessage({ id: 'issues.queue.history.title' })}
{formatMessage({ id: 'issues.queue.history.active' })}:{' '} {activeQueueId || '—'}
)} {/* Stats Cards */}
{totalItems}

{formatMessage({ id: 'issues.queue.stats.totalItems' })}

{groupCount}

{formatMessage({ id: 'issues.queue.stats.groups' })}

{taskCount}

{formatMessage({ id: 'issues.queue.stats.tasks' })}

{solutionCount}

{formatMessage({ id: 'issues.queue.stats.solutions' })}

{/* Conflicts Warning */} {conflictCount > 0 && (

{formatMessage({ id: 'issues.queue.conflicts.title' })}

{conflictCount} {formatMessage({ id: 'issues.queue.conflicts.description' })}

)} {/* Queue Card (actions + summary) */} {/* Queue Board (DnD reorder/move) */} {/* Solution Detail Drawer */} {/* Status Footer */}
{isActive ? ( <> {formatMessage({ id: 'issues.queue.status.ready' })} ) : ( <> {formatMessage({ id: 'issues.queue.status.pending' })} )}
{isActive ? ( ) : ( )} {isActive ? formatMessage({ id: 'issues.queue.status.active' }) : formatMessage({ id: 'issues.queue.status.inactive' }) }
); }