feat(confidence-engine): v0.54b integrate Investigation Overview UI seam + bounded scroll cleanup
- Wire transient overview state from ScenarioForm to ReasoningWorkspace - Inline rendering of investigation overview below milestone invitation - Remove obsolete scrollIntoView after overview request (scrolled away from rendered content) - All four overview props consumed in ReasoningWorkspace render path - Targeted Vitest: 9/9 PASS (tests/ui/investigation-overview-ui.test.jsx) - Production build: compiles successfully
This commit is contained in:
@@ -1331,6 +1331,11 @@ export default function ReasoningWorkspace({
|
||||
onSituationGraphChange,
|
||||
/* ── test init seam (no effect → immediate state) ───────── */
|
||||
initialPostAnalyseStatus,
|
||||
/* ── v0.54b — investigation overview transient state ─── */
|
||||
overviewState,
|
||||
setOverviewState,
|
||||
overviewLoading,
|
||||
handleRequestOverview,
|
||||
}) {
|
||||
const [investigationHistory, setInvestigationHistory] = useState([]);
|
||||
const turnCounter = useRef(0);
|
||||
@@ -1943,20 +1948,43 @@ export default function ReasoningWorkspace({
|
||||
})()}
|
||||
</div>
|
||||
) : openUnknowns.length === 0 && clarifiedQuestions.length > 0 && !cuSynthesisLoading ? (
|
||||
<div className="rounded-xl border-[2.5px] border-teal-300/60 bg-gradient-to-b from-teal-50/40 to-white px-7 pt-5 pb-6">
|
||||
<p className="text-sm leading-relaxed text-gray-700 mb-4">
|
||||
{'You\'ve now worked through all of the questions we surfaced. Would you like to see an overview of what we understand so far?'}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
const el = document.getElementById("cu-scroll-target");
|
||||
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}}
|
||||
style={{ cursor: "pointer" }}
|
||||
className="rounded-lg border border-teal-600 bg-white px-4 py-2 text-sm font-medium text-teal-700 hover:bg-teal-50 transition"
|
||||
>
|
||||
Review current understanding
|
||||
</button>
|
||||
<div className="space-y-4">
|
||||
{/* Milestone invitation + overview action */}
|
||||
<div className="rounded-xl border-[2.5px] border-teal-300/60 bg-gradient-to-b from-teal-50/40 to-white px-7 pt-5 pb-6">
|
||||
<p className="text-sm leading-relaxed text-gray-700 mb-4">
|
||||
{'You\'ve now worked through all of the questions we surfaced. Would you like to see an overview of what we understand so far?'}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleRequestOverview();
|
||||
}}
|
||||
disabled={overviewLoading}
|
||||
style={{ cursor: overviewLoading ? "wait" : "pointer" }}
|
||||
className="rounded-lg border border-teal-600 bg-white px-4 py-2 text-sm font-medium text-teal-700 hover:bg-teal-50 transition disabled:opacity-60"
|
||||
>
|
||||
{overviewLoading ? "Generating overview…" : "Review current understanding"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Investigation overview surface — transient, non-persistent */}
|
||||
{overviewState && (
|
||||
<div className="space-y-4" data-testid="investigation-overview">
|
||||
<div className="rounded-xl border-[2.5px] border-teal-300/70 bg-gradient-to-b from-teal-50/60 to-white px-8 pt-6 pb-7 shadow-sm">
|
||||
<h3 className="mb-3 text-[11px] font-bold tracking-[.18em] uppercase text-teal-700/70">
|
||||
What we understand
|
||||
</h3>
|
||||
<p className="text-base leading-relaxed text-gray-800">{overviewState.understanding}</p>
|
||||
</div>
|
||||
{overviewState.plausibleInterpretations && (
|
||||
<div className="rounded-xl border border-blue-200/70 bg-blue-50/40 px-8 pt-6 pb-7 shadow-sm">
|
||||
<h3 className="mb-3 text-[11px] font-bold tracking-[.18em] uppercase text-blue-600/70">
|
||||
What remains plausible
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed text-gray-700 italic">{overviewState.plausibleInterpretations}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -285,6 +285,10 @@ export default function ScenarioForm() {
|
||||
const [mockScenario, setMockScenario] = useState("");
|
||||
const [hideFacilitatorOnLanding, setHideFacilitatorOnLanding] = useState(false);
|
||||
|
||||
/* ── v0.54b — investigation overview transient state ────── */
|
||||
const [overviewState, setOverviewState] = useState(null);
|
||||
const [overviewLoading, setOverviewLoading] = useState(false);
|
||||
|
||||
/* ── in-flight gate for episode reconsideration on Done ──── */
|
||||
const doneInProgressRef = useRef(false);
|
||||
|
||||
@@ -413,6 +417,36 @@ export default function ScenarioForm() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v0.54b — request investigation overview via the established POST /api/cases/overview seam.
|
||||
* Transient state only: never persists to SituationGraph or currentUnderstanding.
|
||||
*/
|
||||
async function handleRequestOverview() {
|
||||
if (overviewLoading || !result?.situationGraph) return;
|
||||
|
||||
setOverviewLoading(true);
|
||||
setOverviewState(null); // clear any previous overview before new request
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/cases/overview", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
situationGraph: result.situationGraph,
|
||||
findings,
|
||||
plausibleInterpretations: (result.situationGraph?.reconstruction || {}).plausibleInterpretations ?? [],
|
||||
}),
|
||||
}).then((r) => r.json());
|
||||
|
||||
if (res?.success && res?.understanding != null) {
|
||||
setOverviewState(res);
|
||||
}
|
||||
// On failure: do not clear existing CU, do not block further attempts
|
||||
} finally {
|
||||
setOverviewLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function appendFocusedContribution(contribution) {
|
||||
// Derive a single stored contribution object and use it for BOTH
|
||||
// contribution storage AND Finding derivation so the same identity
|
||||
@@ -813,6 +847,11 @@ export default function ScenarioForm() {
|
||||
updateStatus={updateStatus}
|
||||
cuSynthesisLoading={cuSynthesisLoading}
|
||||
currentUnderstanding={currentUnderstanding}
|
||||
/* ── v0.54b — investigation overview transient state ─── */
|
||||
overviewState={overviewState}
|
||||
setOverviewState={setOverviewState}
|
||||
overviewLoading={overviewLoading}
|
||||
handleRequestOverview={handleRequestOverview}
|
||||
result={{
|
||||
...(result || {}),
|
||||
situationGraph: updateResult?.updatedSituationGraph ?? result?.situationGraph,
|
||||
|
||||
Reference in New Issue
Block a user