/** * 형상 선택지 (spec §5.2 · plan C5). Only the default of each group can be chosen until the rules for the other * options are confirmed (Q6). The other options are shown but cannot be selected. */ export type MemberId = 'bearing' | 'coping' | 'column' | 'footing' | 'ground'; export interface ShapeOption { label: string; /** 굵은 글씨 in the spec: the default that the 1단계 model builds. */ isDefault: boolean; /** The rule for this option is confirmed (승인 2026-10-26: 하상), so it can be chosen even if it is not the default. */ confirmed?: boolean; } export interface ShapeGroup { title: string; options: ShapeOption[]; } const d = (label: string): ShapeOption => ({ label, isDefault: true }); const x = (label: string): ShapeOption => ({ label, isDefault: false }); const ok = (label: string): ShapeOption => ({ label, isDefault: false, confirmed: true }); export const SHAPE_GROUPS: Record = { bearing: [{ title: '배치', options: [d('분리'), ok('일체')] }], coping: [ { title: '교좌면', options: [d('직각'), x('모따기'), x('라운드')] }, { title: '하면', options: [d('직선'), x('곡선 오목'), x('곡선 볼록')] }, ], column: [ { title: '단면', options: [d('원형'), x('트랙'), x('사각')] }, { title: '중공', options: [d('중실'), x('중공')] }, ], footing: [ { title: '형상', options: [d('직사각'), x('경사')] }, { title: '모따기', options: [d('없음'), x('있음')] }, { title: '말뚝 종류', options: [d('강관'), x('PHC'), x('복합')] }, { title: '경사말뚝', options: [d('없음'), x('있음')] }, ], ground: [{ title: '위치', options: [d('육상'), ok('하상'), ok('해상')] }], }; export const UNDEFINED_REASON = '규칙 미정 (Q6 확정 전 선택 불가)'; /** Options currently selectable: the defaults and the options whose rule is confirmed. */ export function isSelectable(option: ShapeOption): boolean { return option.isDefault || option.confirmed === true; } /** The default summary of a member, e.g. "교좌면 직각 · 하면 직선". */ export function defaultSummary(member: MemberId): string { return SHAPE_GROUPS[member].map((g) => g.options.find((o) => o.isDefault)!.label).join(' · '); } /** Chooses an option in a group. Refuses options whose rules are not confirmed. */ export function choose(member: MemberId, groupIndex: number, optionLabel: string): string { const group = SHAPE_GROUPS[member][groupIndex]; const option = group?.options.find((o) => o.label === optionLabel); if (!option) throw new Error(`알 수 없는 선택지: ${optionLabel}`); if (!isSelectable(option)) throw new Error(`${optionLabel}: ${UNDEFINED_REASON}`); return option.label; }