'use client'; import type { ReactElement } from 'react'; import { SHIPMENT_EXPRESSION_MAX_DEPTH, SHIPMENT_EXPRESSION_MAX_LEAVES, type ShipmentExpression, } from '@/game/shipment-promises'; import { shipmentExpressionInputError, shipmentExpressionInputSize } from '@/game/shipment-expression-input'; import { ShipmentClauseFields } from './shipment-claim-fields'; import { Button } from './ui/button'; const newLeaf = (): ShipmentExpression => ({ territory: 'carthag', minimum: 1 }); /** Hook-free controlled tree. Stateful leaf descendants are rendered by React. */ export function ShipmentExpressionFields({ value, id = 'truth-shipment', disabled, onChange }: { value: ShipmentExpression; id?: string; disabled: boolean; onChange: (expression: ShipmentExpression) => void; }) { const size = shipmentExpressionInputSize(value); const commit = (next: ShipmentExpression) => { if (!disabled && !shipmentExpressionInputError(next)) onChange(next); }; const render = ( node: ShipmentExpression, path: string, replace: (next: ShipmentExpression) => ShipmentExpression, remove?: () => ShipmentExpression, ): ReactElement => { const group = 'op' in node; const allowed = (next: ShipmentExpression) => !disabled && !shipmentExpressionInputError(replace(next)); const change = (next: ShipmentExpression) => commit(replace(next)); const wrap = (op: 'and' | 'or'): ShipmentExpression => ({ op, terms: [node, newLeaf()] }); return
{group ? `Shipment group ${path} · ${node.op.toUpperCase()}` : `Shipment condition ${path}`} {group ? <> {node.terms.map((term, index) => render(term, `${path}.${index + 1}`, next => replace({ ...node, terms: node.terms.map((old, i) => i === index ? next : old) }), () => { const terms = node.terms.filter((_, i) => i !== index); return replace(terms.length === 1 ? terms[0] : { ...node, terms }); }, ))}
{(['and', 'or'] as const).map(op => { const next: ShipmentExpression = { ...node, terms: [...node.terms, { op, terms: [newLeaf(), newLeaf()] }] }; return ; })}
: 'constant' in node ?

Frozen current facts are unavailable in a pure shipment question. Remove this condition.

: }
{remove && }
; }; return
Shipment expression

Every condition describes the same eventual shipment from reserves this turn. AND requires all conditions; OR requires at least one. No requires the whole expression to be false, not every individual condition. Skipping shipment makes every condition false.

App input limits: {SHIPMENT_EXPRESSION_MAX_LEAVES} shipment conditions and {SHIPMENT_EXPRESSION_MAX_DEPTH} levels of grouping. Groups do not count as conditions. These are input limits, not additional printed rules.

{size.leaves} / {SHIPMENT_EXPRESSION_MAX_LEAVES} shipment conditions · {size.depth} / {SHIPMENT_EXPRESSION_MAX_DEPTH} levels of grouping

{render(value, '1', next => next)}
; }