import { useEffect, useId, useMemo, useState, type ReactNode, type RefObject, } from "react"; import { Play, X } from "lucide-react"; import { Button } from "../../components/ui/button"; export interface FleetActionOption { id: ActionId; label: string; group: string; scopeLabel?: string; disabled?: boolean; disabledReason?: string; tone?: "default" | "destructive"; runLabel?: string; } interface FleetActionSelectorProps { actions: readonly FleetActionOption[]; selectedCount: number; hiddenSelectedCount?: number; busy?: boolean; busyLabel?: string; selectRef?: RefObject; onRun: (action: ActionId) => void | Promise; onClear: () => void; renderContext?: (action: ActionId | "") => ReactNode; label?: string; } /** * Selection-aware command picker shared by all Fleet Ledger grids. Eligibility * remains data owned by the calling grid; this module owns only presentation, * explicit execution and accessible unavailable reasons. */ export function FleetActionSelector({ actions, selectedCount, hiddenSelectedCount = 0, busy = false, selectRef, onRun, onClear, renderContext, label = "Batch action", }: FleetActionSelectorProps) { const [selectedActionId, setSelectedActionId] = useState(""); const hintId = useId(); const groups = useMemo(() => { const order: string[] = []; const grouped = new Map[]>(); for (const action of actions) { if (!grouped.has(action.group)) { order.push(action.group); grouped.set(action.group, []); } grouped.get(action.group)!.push(action); } return order.map((group) => ({ group, actions: grouped.get(group)! })); }, [actions]); const selectedAction = actions.find((action) => action.id === selectedActionId); const unavailableReason = selectedAction?.disabled ? selectedAction.disabledReason ?? "This action is not available for the current selection." : null; const actionScope = selectedAction?.scopeLabel; useEffect(() => { if (selectedActionId && !actions.some((action) => action.id === selectedActionId)) { setSelectedActionId(""); } }, [actions, selectedActionId]); const run = async () => { if (!selectedAction || selectedAction.disabled || busy) return; await onRun(selectedAction.id); setSelectedActionId(""); }; return (
{actionScope ?? `${selectedCount} selected`} {actionScope ? ( Action scope ) : hiddenSelectedCount > 0 ? ( {hiddenSelectedCount} hidden by filter ) : ( Selected rows )}
{renderContext ? renderContext(selectedActionId) : null} {unavailableReason ? ( {unavailableReason} ) : null}
); }