lct-16 и половина lct-19: разбор, отчёт, внешний монитор, эталон
Эталонный диалог собирается кодом из фактов и чек-листа: написанный руками, он разошёлся бы с фактами при первой же правке сценария, и курсанта оштрафовали бы за правильный ответ. Отчёт: метрики фактом против норматива со ссылкой, отметка E1 на каждый недобытый факт с эталонным вопросом, расхождение самооценки — что курсант заметил сам, чего не заметил, что отметил зря. Не заметил — самое ценное для разбора. Внешний монитор — не отдельное приложение, а другой режим отрисовки тех же событий: крупный таймер опроса, ход разговора, карточка, после оценки разбор на весь экран. Коррекция преподавателем сохраняет автооценку рядом: видно, что скорректировано и кем. Найдено: все метрики весили одинаково, и курсант, не задавший ни одного вопроса, но заполнивший карточку руками, получал 87 из 100. Предварительные веса (полнота опроса — 4) дают 74; окончательные утверждает методист, вопрос записан в DEBRIEF.md.
This commit is contained in:
parent
25143645d3
commit
fadfa3e479
20 changed files with 781 additions and 9 deletions
|
|
@ -12,7 +12,13 @@ import { EnergyGate } from "@/shared/audio/levels";
|
|||
import { CALLER_RATE, Playback } from "@/shared/audio/playback";
|
||||
import { type CardState, applyPatch, edit, empty as emptyCard } from "@/features/kio-card/merge";
|
||||
import type { ChecklistItem } from "@/features/self-assessment/SelfAssessment";
|
||||
import type { CallIncoming, DDSCode, ServerToTrainee, TimerSnapshot } from "@/shared/types/generated";
|
||||
import type {
|
||||
CallIncoming,
|
||||
DDSCode,
|
||||
ServerToTrainee,
|
||||
SessionReport,
|
||||
TimerSnapshot,
|
||||
} from "@/shared/types/generated";
|
||||
|
||||
/** Правки копятся и уходят одной дельтой: 300 мс тишины — и отправка. */
|
||||
const PATCH_DEBOUNCE_MS = 300;
|
||||
|
|
@ -37,6 +43,7 @@ export function useCall(sessionId: string | null) {
|
|||
const [checklist, setChecklist] = useState<ChecklistItem[]>([]);
|
||||
const [selfAssessed, setSelfAssessed] = useState(false);
|
||||
const [scoreReady, setScoreReady] = useState(false);
|
||||
const [report, setReport] = useState<SessionReport | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const channel = useRef<ReturnType<typeof callChannel> | null>(null);
|
||||
|
|
@ -111,6 +118,10 @@ export function useCall(sessionId: string | null) {
|
|||
break;
|
||||
case "score.ready":
|
||||
setScoreReady(true);
|
||||
void fetch(`/api/sessions/${sessionId}/report`)
|
||||
.then((response) => (response.ok ? response.json() : null))
|
||||
.then(setReport)
|
||||
.catch(() => setReport(null));
|
||||
break;
|
||||
case "error":
|
||||
setError(event.message);
|
||||
|
|
@ -182,7 +193,7 @@ export function useCall(sessionId: string | null) {
|
|||
|
||||
return {
|
||||
status, phase, incoming, lines, timers, callerSpeaking, micOn, error, card,
|
||||
checklist, selfAssessed, scoreReady,
|
||||
checklist, selfAssessed, scoreReady, report,
|
||||
answer, hangup, hint, patchKio, dispatch, submitSelfAssessment,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue