/** @jsxImportSource react */ import { useMemo, type JSX } from "react"; import { Box, Text } from "ink"; import type { Plan as AlchemyPlan, BindingAction, CRUD } from "../../Plan.ts"; import { buildNamespaceTree, flattenTree, type DerivedAction, } from "../NamespaceTree.ts"; export interface PlanProps { plan: AlchemyPlan; } export function Plan({ plan }: PlanProps): JSX.Element { const items = useMemo( () => [ ...Object.values(plan.resources), ...Object.values(plan.deletions), ] as CRUD[], [plan], ); const flatItems = useMemo(() => { const tree = buildNamespaceTree(items); return flattenTree(tree); }, [items]); if (items.length === 0) { return No changes planned; } const counts = items.reduce((acc, item) => (acc[item.action]++, acc), { create: 0, update: 0, delete: 0, noop: 0, replace: 0, }); const actions = (["create", "update", "delete", "replace"] as const).filter( (action) => counts[action] > 0, ); return ( Plan : {actions.flatMap((action, i) => { const count = counts[action]; const color = actionColor(action); if (count === 0) return []; const box = ( {count} to {action} ); return i === actions.length - 1 ? [box] : [box, | ]; })} {flatItems.map((item) => { const indent = " ".repeat(item.depth); const color = getActionColor(item.action); const icon = getActionIcon(item.action); const key = item.path.join("/"); if (item.type === "namespace") { return ( {indent} {icon} {item.id} ); } if (item.type === "binding") { return ( {indent} {icon} {item.bindingSid} ); } // Resource item return ( {indent} {icon} {item.id} ({item.resourceType}) {item.bindingCount !== undefined && item.bindingCount > 0 && ( ({item.bindingCount} bindings) )} ); })} ); } type Color = Parameters[0]["color"]; type AnyAction = CRUD["action"] | BindingAction | DerivedAction; const getActionColor = (action: AnyAction): Color => ({ noop: "gray", create: "green", update: "yellow", delete: "red", replace: "magenta", mixed: "cyan", })[action] ?? "gray"; const getActionIcon = (action: AnyAction): string => ({ create: "+", update: "~", delete: "-", noop: "•", replace: "!", mixed: "*", })[action] ?? "?"; const actionColor = (action: CRUD["action"]): Color => getActionColor(action);