// АРМ курсанта: приём вызова и разговор голосом. // Карточка КИО — lct-10, таймеры — lct-11. import { useState } from "react"; import { KioCard } from "@/features/kio-card/KioCard"; import { StandStatus } from "@/features/health/StandStatus"; import { ModeBanner, hintsAllowed } from "@/features/mode-banner/ModeBanner"; import { Debrief } from "@/features/debrief/Debrief"; import { SelfAssessment } from "@/features/self-assessment/SelfAssessment"; import { InterviewTimer, Timers } from "@/features/timers/Timers"; import { display } from "@/features/kio-card/merge"; import { useCall } from "@/features/call/useCall"; import { sessionIdFromUrl } from "@/shared/api/session"; import type { DDSCode } from "@/shared/types/generated"; const STATUS_LABEL: Record = { connecting: "подключение", open: "на связи", reconnecting: "связь потеряна, переподключение", closed: "отключено", }; const RESOLVE_LABELS = { consultation: "справочно-консультационная помощь", transfer_region: "передан в систему-112 другого региона", } as const; function ddsCode(value: unknown): DDSCode | undefined { return value === "01" || value === "02" || value === "03" || value === "04" || value === "gkh" ? value : undefined; } export function Call() { const [resolveComment, setResolveComment] = useState(""); const [resolved, setResolved] = useState<"consultation" | "transfer_region" | null>(null); const sessionId = sessionIdFromUrl(); const call = useCall(sessionId); if (!sessionId) { return (

АРМ курсанта

В адресе нет номера занятия: откройте ссылку вида /trainee?session=…

); } if (call.briefing) { const services = display(call.card, "notify"); const notifiedServices = Array.isArray(services) ? services.map(String) : []; return
112
Карточка информационного обменаТекстовое упражнение · сценарий {call.briefing.scenario_id}
● {call.status === "open" ? "АРМ подключён" : STATUS_LABEL[call.status]}{call.cardSubmitted ? "Карточка отправлена" : "Карточка заполняется"}
Учебная вводнаяТекстовый режим · без входящего звонка
{call.briefing.text}
Службы: {notifiedServices.length ? notifiedServices.map((service) => {service}) : появятся после выбора признаков} {!call.cardSubmitted && } {call.cardSubmitted && Карточка отправлена}
{call.error &&

{call.error}

} {call.cardSubmitted && !call.report && !call.reportError &&

Карточка отправлена. Ожидаем итоговую оценку…

} {call.reportError &&

{call.reportError}

} {call.report && }
; } const service = ddsCode(display(call.card, "dds")); const notify = display(call.card, "notify"); const autoRoute = Boolean(display(call.card, "incident_code")) && Array.isArray(notify) && notify.length > 0; return (
112
Канал связи{STATUS_LABEL[call.status]}{call.micOn ? "● Микрофон включён" : "Микрофон выключен"}
Входящий вызов{call.incoming?.caller_number || "Ожидание вызова"}{call.incoming ? `Сценарий ${call.incoming.scenario_id} · ${call.callerSpeaking ? "заявитель говорит" : "заявитель молчит"}` : "Карточка не назначена"}
АРМ оператора 112Карточка информационного обмена
{call.phase === "incoming" && !call.micOn && } {call.micOn && ● Разговор идёт} {call.micOn && hintsAllowed(call.incoming?.mode) && } {call.phase === "ended" ? "Вызов завершён" : "Заполните адрес и признаки происшествия со слов заявителя"}
{call.error &&

{call.error}

}
Службы · список оповещения рассчитывается по признакам
{call.micOn && }
{/* Не каждый вызов заканчивается карточкой: справка и чужой регион — отдельные исходы, и курсант выбирает их сам (docs/spec/TICKETS.md). */}
Закрыть вызов без карточки

setResolveComment(event.target.value)} />

{" "}

{resolved &&

Вызов закрыт: {RESOLVE_LABELS[resolved]}

}

Нормативы и разговор

{call.lines.map((line, index) => ( ))} {call.lines.length === 0 && }
{line.speaker === "caller" ? "звонящий" : "оператор"} {line.text}
—
{call.phase === "ended" && !call.selfAssessed && call.checklist.length > 0 && ( )} {call.selfAssessed && !call.report &&

Самооценка принята, оценка считается…

} {call.report && }
); }