'use client'; import { TERRITORIES } from '@/game/board'; import { Input } from '@/components/ui/input'; export type ShipmentClauseInput = { territory: string; minimum: number; sector?: number }; export function invalidShipmentClause(claim: ShipmentClauseInput) { return !Number.isSafeInteger(claim.minimum) || claim.minimum < 1 || claim.minimum > 20 || claim.sector !== undefined && (!Number.isSafeInteger(claim.sector) || !TERRITORIES.find(destination => destination.id === claim.territory)?.sectors.includes(claim.sector)); } /** Destination and physical minimum shared by flat and nested question builders. */ export function ShipmentClauseFields({ id, value, onChange }: { id: string; value: ShipmentClauseInput; onChange: (claim: ShipmentClauseInput) => void; }) { const printed = TERRITORIES.find(destination => destination.id === value.territory); // Mixed-shipment leaves have their own `kind` and remain on their existing // territory-only parser until that separate question family is integrated. const allowSector = !('kind' in value); return <> {allowSector && <> } onChange({ ...value, minimum: event.currentTarget.valueAsNumber })} />

A whole minimum of one to twenty physical forces in the same shipment from reserves this turn. Skipping makes this shipment condition false.

; } export function ShipmentClaimFields({ id, clauses, join, onClauses, onJoin }: { id: string; clauses: ShipmentClauseInput[]; join: 'single' | 'and' | 'or'; onClauses: (clauses: ShipmentClauseInput[]) => void; onJoin: (join: 'single' | 'and' | 'or') => void; }) { return <> {clauses.slice(0, join === 'single' ? 1 : 2).map((claim, index) => { const update = (next: ShipmentClauseInput) => onClauses(clauses.map((previous, i) => i === index ? next : previous)); return
Shipment condition {index + 1}
; })}

All conditions describe the same shipment from reserves. AND requires both; OR requires at least one. A No answer requires the whole statement to be false. Skipping shipment makes every shipment condition false.

; }