'use client' import { DragDropProvider } from '@dnd-kit/react' import { useState, useEffect, type ComponentProps } from 'react' import { css } from 'styled-system/css' import { Box, Center } from 'styled-system/jsx' import { scenarioQueue } from 'styled-system/recipes' import { Button } from '../Button' import * as Tabs from '../Tabs' import { AddScenarioDialog } from './AddScenarioDialog' import { ScenarioCard } from './ScenarioCard' import { ScenarioCardDraggable } from './ScenarioCardDraggable' import type { ScenarioQueueProps, Scenario } from './types' // ── Helpers ────────────────────────────────────────────────────────────────── function arrayMove(array: T[], from: number, to: number): T[] { const next = [...array] const [item] = next.splice(from, 1) next.splice(to, 0, item) return next } // ── Component ───────────────────────────────────────────────────────────────── export function ScenarioQueue({ scenarios, onReorder, onRequeue, onRemove: _onRemove, renderAddScenarioContent, onBrowseMore, onBuildCustom, }: ScenarioQueueProps) { const queuedRaw = scenarios.filter((s) => s.status === 'queued') const completed = scenarios.filter((s) => s.status === 'completed') // Local ordering state — mirrors queuedRaw but supports optimistic drag reorder const [localQueue, setLocalQueue] = useState(queuedRaw) // Sync when the canonical scenarios list changes externally useEffect(() => { setLocalQueue(queuedRaw) }, [scenarios]) const [dialogOpen, setDialogOpen] = useState(false) const totalCount = localQueue.length + completed.length // useSortable adds index + initialIndex to the draggable at runtime; cast here // since @dnd-kit/react's static Draggable type doesn't declare them. type SortableSource = { index: number; initialIndex: number } & Record const handleDragEnd: NonNullable['onDragEnd']> = (event) => { if (event.canceled) return const source = event.operation?.source as SortableSource | null if (!source) return const oldIndex = source.initialIndex const newIndex = source.index if (oldIndex === newIndex) return const next = arrayMove(localQueue, oldIndex, newIndex) setLocalQueue(next) onReorder?.(next.map((s) => s.id)) } const styles = scenarioQueue() return ( {/* Header */} Scenario Queue {totalCount} scenario{totalCount !== 1 ? 's' : ''} {/* Tabs.Root gets tabsInner directly — no wrapper div needed. flex:1 + minHeight:0 allows content panels to scroll independently without the panel expanding indefinitely. */} In Queue Completed {/* In Queue tab */} {localQueue.length === 0 ? (
No scenarios in queue
) : ( {localQueue.map((scenario, index) => ( ))} )}
{/* Completed tab */} {completed.length === 0 ? (
No completed scenarios
) : ( {completed.map((scenario, index) => ( ))} )}
{/* Pinned "Add Scenario" button — always visible, never scrolls */} setDialogOpen(false)} renderContent={renderAddScenarioContent} onBrowseMore={onBrowseMore} onBuildCustom={onBuildCustom} />
) }