import React, { useRef, useState } from "react"; import { cn } from "@/lib/utils"; import { ScenarioItem } from "./scenario-item"; import { AddScenarioButton } from "./borrowing-capacity-atoms"; /** * Left-sidebar organism for the Borrowing Capacity page. * * Renders a titled list of selectable, editable `ScenarioItem` rows followed * by an `AddScenarioButton`. Drag-and-drop reordering is handled internally * via the HTML5 DnD API; the consumer receives the final order via `onReorder`. */ export interface ScenarioListScenario { /** Unique identifier */ id: string; /** Display name (e.g. "Default Scenario") */ name: string; /** Secondary descriptor (e.g. "Owner Occupier") */ description?: string; } export interface ScenarioListProps { /** Section heading (default: "New Loans") */ title?: string; /** Ordered list of scenarios to render */ scenarios: ScenarioListScenario[]; /** ID of the currently active scenario */ selectedId?: string; /** Called when a scenario row is clicked */ onSelect?: (id: string) => void; /** Called when "Update Scenario" is clicked in a kebab menu */ onEdit?: (id: string) => void; /** Called when "Delete Scenario" is clicked in a kebab menu */ onDelete?: (id: string) => void; /** Called when the Add button is clicked */ onAdd?: () => void; /** Label for the add button (default: "Add New Scenario") */ addLabel?: string; /** * Maximum number of scenarios allowed. When `scenarios.length >= maxScenarios` * the add button is disabled. */ maxScenarios?: number; /** All items show a drag handle when true (default: true) */ isDraggable?: boolean; /** * Called with the new ordered ID array after a drag-and-drop reorder. * The consumer is responsible for updating the `scenarios` prop accordingly. */ onReorder?: (orderedIds: string[]) => void; className?: string; } export function ScenarioList({ title = "New Loans", scenarios, selectedId, onSelect, onEdit, onDelete, onAdd, addLabel = "Add New Scenario", maxScenarios, isDraggable = true, onReorder, className, }: ScenarioListProps) { const isAddDisabled = maxScenarios !== undefined && scenarios.length >= maxScenarios; // ── Drag & drop state ─────────────────────────────────────────────────────── const draggingId = useRef(null); const [dragOverId, setDragOverId] = useState(null); const handleDragStart = (id: string) => (e: React.DragEvent) => { draggingId.current = id; e.dataTransfer.effectAllowed = "move"; }; const handleDragOver = (id: string) => (e: React.DragEvent) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; if (id !== draggingId.current) setDragOverId(id); }; const handleDragLeave = () => setDragOverId(null); const handleDrop = (targetId: string) => (e: React.DragEvent) => { e.preventDefault(); setDragOverId(null); const sourceId = draggingId.current; draggingId.current = null; if (!sourceId || sourceId === targetId || !onReorder) return; const ids = scenarios.map((s) => s.id); const fromIdx = ids.indexOf(sourceId); const toIdx = ids.indexOf(targetId); if (fromIdx === -1 || toIdx === -1) return; const reordered = [...ids]; reordered.splice(fromIdx, 1); reordered.splice(toIdx, 0, sourceId); onReorder(reordered); }; const handleDragEnd = () => { draggingId.current = null; setDragOverId(null); }; return (

{title}

{scenarios.map((scenario) => ( onSelect?.(scenario.id)} onEdit={onEdit ? () => onEdit(scenario.id) : undefined} onDelete={onDelete ? () => onDelete(scenario.id) : undefined} onDragStart={isDraggable ? handleDragStart(scenario.id) : undefined} onDragOver={isDraggable ? handleDragOver(scenario.id) : undefined} onDragLeave={isDraggable ? handleDragLeave : undefined} onDrop={isDraggable ? handleDrop(scenario.id) : undefined} onDragEnd={isDraggable ? handleDragEnd : undefined} /> ))}
); }