// Карточка КИО. Один и тот же компонент рисуется у курсанта, на пульте, // на внешнем мониторе и на АРМ ДДС — там в режиме `readOnly`. import { type CardState, display } from "./merge"; import { type Field, visibleGroups } from "./fields"; interface Props { state: CardState; /** Обязательные поля приходят сценарием в `call.incoming`. */ required?: string[]; readOnly?: boolean; onChange?: (path: string, value: unknown) => void; } export function KioCard({ state, required = [], readOnly = false, onChange }: Props) { const dds = display(state, "dds") as string | null; return (
{visibleGroups(dds).map((group) => ( {group.fields.map((field) => ( ))}
{group.title}
))}
); } function Row({ field, value, pending, required, readOnly, onChange, }: { field: Field; value: unknown; pending: boolean; required: boolean; readOnly?: boolean; onChange?: (path: string, value: unknown) => void; }) { const empty = value === null || value === undefined || value === ""; return ( {field.label} {required && ·} {readOnly ? ( {formatValue(value)} ) : ( )} ); } function Input({ field, value, onChange, }: { field: Field; value: unknown; onChange?: (path: string, value: unknown) => void; }) { const emit = (next: unknown) => onChange?.(field.path, next); if (field.kind === "bool") { return ( emit(e.target.checked)} /> ); } if (field.kind === "select") { return ( ); } if (field.kind === "number") { return ( { const text = e.target.value; if (text === "") return emit(null); const parsed = Number(text); // Нечисловое значение не уходит на сервер: карточка — норматив, // а не свободный текст. if (Number.isFinite(parsed) && parsed >= 0) emit(parsed); }} /> ); } return ( emit(e.target.value)} /> ); } function formatValue(value: unknown): string { if (value === true) return "да"; if (value === false) return "нет"; if (value === null || value === undefined || value === "") return "—"; return String(value); }