lct-hack/frontend/src/pages/trainee/Call.tsx

89 lines
3.3 KiB
TypeScript
Raw Normal View History

// АРМ курсанта: приём вызова и разговор голосом.
// Карточка КИО — lct-10, таймеры — lct-11.
lct-10: карточка КИО — форма, слияние правок, read-only Фронт не хранит карточку как истину: локально живут только правки курсанта, ещё не подтверждённые сервером. Распознанное сервером значение перекрывает набранное, эхо своей правки её подтверждает. Иначе автозаполнение и ручной ввод дерутся за одно поле — худший класс багов для отладки в последнюю ночь. Описание полей отдельно от отрисовки: по нему же карточка рисуется read-only на мониторе, пульте и АРМ ДДС. Сверка формы с контрактом встроена в make typecheck: все 40 полей домена на форме, а поле, добавленное в контракт и забытое в форме, теперь поймается само. Это пропущенный факт по вине интерфейса, а не курсанта. Открытый вопрос в карточке: нужно ли автозаполнение карточки из речи вообще — полнота КИО оценивается кодом E5, и если сервер впишет адрес сам, курсант перестанет за неё отвечать.
2026-09-17 21:16:51 +03:00
import { KioCard } from "@/features/kio-card/KioCard";
import { display } from "@/features/kio-card/merge";
import { useCall } from "@/features/call/useCall";
import { sessionIdFromUrl } from "@/shared/api/session";
const STATUS_LABEL: Record<string, string> = {
connecting: "подключение",
open: "на связи",
reconnecting: "связь потеряна, переподключение",
closed: "отключено",
};
export function Call() {
const sessionId = sessionIdFromUrl();
const call = useCall(sessionId);
if (!sessionId) {
return (
<main className="page">
<h1>АРМ курсанта</h1>
<p className="warn">В адресе нет номера занятия: откройте ссылку вида /trainee?session=…</p>
</main>
);
}
return (
<main className="page">
<h1>АРМ оператора 112</h1>
<table className="grid">
<tbody>
<tr><th>Канал</th><td className={call.status === "open" ? "" : "warn"}>{STATUS_LABEL[call.status]}</td></tr>
<tr><th>Вызов</th><td>{call.incoming ? `${call.incoming.caller_number} · сценарий ${call.incoming.scenario_id} · режим ${call.incoming.mode}` : "ожидание"}</td></tr>
<tr><th>Гарнитура</th><td>{call.micOn ? "микрофон включён" : "выключен"}</td></tr>
<tr><th>Звонящий</th><td>{call.callerSpeaking ? "говорит" : "молчит"}</td></tr>
</tbody>
</table>
<p>
{call.phase === "incoming" && !call.micOn && (
<button type="button" onClick={() => void call.answer()}>Ответить</button>
)}
{call.micOn && <button type="button" onClick={call.hangup}>Завершить вызов</button>}{" "}
{call.micOn && call.incoming?.mode !== "exam" && (
<button type="button" onClick={call.hint}>Подсказка</button>
)}
</p>
{call.error && <p className="violated">{call.error}</p>}
lct-10: карточка КИО — форма, слияние правок, read-only Фронт не хранит карточку как истину: локально живут только правки курсанта, ещё не подтверждённые сервером. Распознанное сервером значение перекрывает набранное, эхо своей правки её подтверждает. Иначе автозаполнение и ручной ввод дерутся за одно поле — худший класс багов для отладки в последнюю ночь. Описание полей отдельно от отрисовки: по нему же карточка рисуется read-only на мониторе, пульте и АРМ ДДС. Сверка формы с контрактом встроена в make typecheck: все 40 полей домена на форме, а поле, добавленное в контракт и забытое в форме, теперь поймается само. Это пропущенный факт по вине интерфейса, а не курсанта. Открытый вопрос в карточке: нужно ли автозаполнение карточки из речи вообще — полнота КИО оценивается кодом E5, и если сервер впишет адрес сам, курсант перестанет за неё отвечать.
2026-09-17 21:16:51 +03:00
<div className="columns">
<section>
<h2>Карточка информационного обмена</h2>
<KioCard
state={call.card}
required={call.incoming?.required_fields ?? []}
readOnly={!call.micOn}
onChange={call.patchKio}
/>
<p>
<button
type="button"
disabled={!call.micOn || !display(call.card, "dds")}
onClick={() => call.dispatch(display(call.card, "dds") as never)}
>
Передать в ДДС
</button>
</p>
</section>
<section>
<h2>Разговор</h2>
<table className="grid">
<tbody>
{call.lines.map((line, index) => (
<tr key={index}>
<th>{line.speaker === "caller" ? "звонящий" : "оператор"}</th>
<td className={line.partial ? "warn" : ""}>{line.text}</td>
</tr>
))}
{call.lines.length === 0 && <tr><td colSpan={2}>—</td></tr>}
</tbody>
</table>
lct-10: карточка КИО — форма, слияние правок, read-only Фронт не хранит карточку как истину: локально живут только правки курсанта, ещё не подтверждённые сервером. Распознанное сервером значение перекрывает набранное, эхо своей правки её подтверждает. Иначе автозаполнение и ручной ввод дерутся за одно поле — худший класс багов для отладки в последнюю ночь. Описание полей отдельно от отрисовки: по нему же карточка рисуется read-only на мониторе, пульте и АРМ ДДС. Сверка формы с контрактом встроена в make typecheck: все 40 полей домена на форме, а поле, добавленное в контракт и забытое в форме, теперь поймается само. Это пропущенный факт по вине интерфейса, а не курсанта. Открытый вопрос в карточке: нужно ли автозаполнение карточки из речи вообще — полнота КИО оценивается кодом E5, и если сервер впишет адрес сам, курсант перестанет за неё отвечать.
2026-09-17 21:16:51 +03:00
</section>
</div>
</main>
);
}