회기 원장 상태 복원 보강
This commit is contained in:
parent
13d1c39a33
commit
9a7dd98cb6
7 changed files with 101 additions and 32 deletions
|
|
@ -216,18 +216,24 @@ function latestEpisodeFor(
|
|||
prescription: PracticePrescriptionItem,
|
||||
episodes: PracticeEpisodeItem[],
|
||||
): PracticeEpisodeItem | null {
|
||||
const matching = episodes.filter((episode) => {
|
||||
const payload = episode.assessment_payload as Record<string, unknown>;
|
||||
return (
|
||||
payload.prescription_id === prescription.prescription_key ||
|
||||
payload.prescription_id ===
|
||||
prescription.prescription_payload.prescription_id ||
|
||||
payload.competency_id === prescription.competency_id
|
||||
);
|
||||
});
|
||||
const matching = episodes.filter((episode) =>
|
||||
episodeMatchesPrescription(episode, prescription),
|
||||
);
|
||||
return matching.at(-1) ?? null;
|
||||
}
|
||||
|
||||
function episodeMatchesPrescription(
|
||||
episode: PracticeEpisodeItem,
|
||||
prescription: PracticePrescriptionItem,
|
||||
): boolean {
|
||||
const payload = episode.assessment_payload as Record<string, unknown>;
|
||||
return (
|
||||
payload.prescription_id === prescription.prescription_key ||
|
||||
payload.prescription_id === prescription.prescription_payload.prescription_id ||
|
||||
payload.competency_id === prescription.competency_id
|
||||
);
|
||||
}
|
||||
|
||||
interface RuntimeObservationSnapshot {
|
||||
progress: PracticeProgress;
|
||||
attemptCount: number;
|
||||
|
|
@ -272,6 +278,24 @@ function runtimeObservationSnapshot(
|
|||
};
|
||||
}
|
||||
|
||||
function runtimeEpisodeForSession(
|
||||
data: DeliberatePracticeReadModel,
|
||||
intent: PracticeLaunchIntent,
|
||||
sessionId: string,
|
||||
): PracticeEpisodeItem | null {
|
||||
const prescription = prescriptionForLaunchIntent(data, intent);
|
||||
if (!prescription) return null;
|
||||
return (
|
||||
data.episodes
|
||||
.filter(
|
||||
(episode) =>
|
||||
episode.session_id === sessionId &&
|
||||
episodeMatchesPrescription(episode, prescription),
|
||||
)
|
||||
.at(-1) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
function prioritizedPrescriptions(
|
||||
data: DeliberatePracticeReadModel,
|
||||
): PracticePrescriptionItem[] {
|
||||
|
|
@ -974,16 +998,25 @@ function RuntimePracticeObservation({
|
|||
data: DeliberatePracticeReadModel;
|
||||
onObserved: () => void;
|
||||
}) {
|
||||
const persistedRuntimeEpisode = runtimeEpisodeForSession(
|
||||
data,
|
||||
intent,
|
||||
sessionId,
|
||||
);
|
||||
const [baseline, setBaseline] = useState<RuntimeObservationSnapshot>(() =>
|
||||
runtimeObservationSnapshot(data, intent),
|
||||
);
|
||||
const [state, setState] = useState<RuntimeObservationState>("idle");
|
||||
const [state, setState] = useState<RuntimeObservationState>(() =>
|
||||
persistedRuntimeEpisode ? "success" : "idle",
|
||||
);
|
||||
const [result, setResult] =
|
||||
useState<PracticeAttemptSubmissionResponse | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setBaseline(runtimeObservationSnapshot(data, intent));
|
||||
setState("idle");
|
||||
setState(
|
||||
runtimeEpisodeForSession(data, intent, sessionId) ? "success" : "idle",
|
||||
);
|
||||
setResult(null);
|
||||
// read model reload는 반영 전 기준을 바꾸지 않는다. 새 회기나 새 처방만 초기화한다.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -992,6 +1025,8 @@ function RuntimePracticeObservation({
|
|||
const connectedPrescription = prescriptionForLaunchIntent(data, intent);
|
||||
const current = runtimeObservationSnapshot(data, intent);
|
||||
const currentProgress = result?.progress ?? current.progress;
|
||||
const ledgerRecorded = Boolean(result || persistedRuntimeEpisode);
|
||||
const hydratedFromLedger = Boolean(persistedRuntimeEpisode && !result);
|
||||
|
||||
if (intent.kind === "transfer") {
|
||||
return (
|
||||
|
|
@ -1085,30 +1120,43 @@ function RuntimePracticeObservation({
|
|||
관찰합니다.
|
||||
</p>
|
||||
</div>
|
||||
<Badge tone={state === "success" ? "accent" : "neutral"}>
|
||||
{state === "success" ? "원장 반영됨" : "학습자만 실행"}
|
||||
<Badge tone={ledgerRecorded ? "accent" : "neutral"}>
|
||||
{ledgerRecorded ? "원장 반영됨" : "학습자만 실행"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="dp-runtime-observation__comparison">
|
||||
<section>
|
||||
<span>반영 전</span>
|
||||
<strong>{PROGRESS_COPY[baseline.progress].title}</strong>
|
||||
<small>
|
||||
익숙한 장면 근거 {baseline.familiarDemonstrations}회, 새 장면 근거{" "}
|
||||
{baseline.unseenTransferDemonstrations}회
|
||||
</small>
|
||||
</section>
|
||||
{result ? (
|
||||
{hydratedFromLedger ? (
|
||||
<section className="is-after">
|
||||
<span>반영 후</span>
|
||||
<span>현재 원장 상태</span>
|
||||
<strong>{PROGRESS_COPY[currentProgress].label}</strong>
|
||||
<small>
|
||||
익숙한 장면 근거 {current.familiarDemonstrations}회, 새 장면 근거{" "}
|
||||
{current.unseenTransferDemonstrations}회
|
||||
</small>
|
||||
</section>
|
||||
) : null}
|
||||
) : (
|
||||
<>
|
||||
<section>
|
||||
<span>반영 전</span>
|
||||
<strong>{PROGRESS_COPY[baseline.progress].title}</strong>
|
||||
<small>
|
||||
익숙한 장면 근거 {baseline.familiarDemonstrations}회, 새 장면 근거{" "}
|
||||
{baseline.unseenTransferDemonstrations}회
|
||||
</small>
|
||||
</section>
|
||||
{result ? (
|
||||
<section className="is-after">
|
||||
<span>반영 후</span>
|
||||
<strong>{PROGRESS_COPY[currentProgress].label}</strong>
|
||||
<small>
|
||||
익숙한 장면 근거 {current.familiarDemonstrations}회, 새 장면 근거{" "}
|
||||
{current.unseenTransferDemonstrations}회
|
||||
</small>
|
||||
</section>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{state === "waiting" ? (
|
||||
|
|
@ -1126,6 +1174,8 @@ function RuntimePracticeObservation({
|
|||
<p>
|
||||
{result?.idempotent_replay
|
||||
? "같은 회기 근거를 중복 없이 확인했습니다."
|
||||
: hydratedFromLedger
|
||||
? "이미 기록된 이 회기 근거를 원장에서 다시 불러왔습니다."
|
||||
: "새 관찰 근거를 원장에 반영하고 최신 진행 상태를 다시 불러왔습니다."}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue