// ========================================
// 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 */}
);
}
// ========== 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' })
}
);
}