feat(confidence-engine): use server investigation persistence
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import { restartInvestigation } from "@/lib/storage/server-investigation-persistence.js";
|
||||
import { withAuthenticatedApi } from "@/lib/supabase/api-auth.js";
|
||||
|
||||
async function post(_request, { params }) {
|
||||
try {
|
||||
const snapshot = await restartInvestigation(params.id);
|
||||
if (!snapshot) return Response.json({ error: "Investigation not found" }, { status: 404 });
|
||||
return Response.json({ snapshot });
|
||||
} catch {
|
||||
return Response.json({ error: "Investigation persistence request failed" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = withAuthenticatedApi(post);
|
||||
@@ -11,10 +11,21 @@ export default function InvestigationPage({ params }) {
|
||||
const router = useRouter();
|
||||
const routeId = typeof params?.id === "string" ? params.id : "";
|
||||
const [existing, setExisting] = useState(null);
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!routeId) return;
|
||||
setExisting(loadInvestigation(routeId));
|
||||
let active = true;
|
||||
setHydrated(false);
|
||||
if (!routeId) { setHydrated(true); return; }
|
||||
(async () => {
|
||||
try {
|
||||
const snapshot = await loadInvestigation(routeId);
|
||||
if (active) setExisting(snapshot);
|
||||
} finally {
|
||||
if (active) setHydrated(true);
|
||||
}
|
||||
})();
|
||||
return () => { active = false; };
|
||||
}, [routeId]);
|
||||
|
||||
return (
|
||||
@@ -35,7 +46,9 @@ export default function InvestigationPage({ params }) {
|
||||
evidence-based structured reconstruction. This is a technical vertical
|
||||
slice — not a production system.
|
||||
</p>
|
||||
{existing ? (
|
||||
{!hydrated ? (
|
||||
<p className="text-sm text-gray-500">Loading investigation…</p>
|
||||
) : existing ? (
|
||||
<ScenarioForm
|
||||
investigationId={routeId}
|
||||
existingSnapshot={existing}
|
||||
|
||||
@@ -15,9 +15,20 @@ export default function ReportPage({ params }) {
|
||||
const generationAttempted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
setExisting(loadInvestigation(routeId));
|
||||
setHydrated(true);
|
||||
}, []);
|
||||
let active = true;
|
||||
setHydrated(false);
|
||||
(async () => {
|
||||
try {
|
||||
const snapshot = await loadInvestigation(routeId);
|
||||
if (active) setExisting(snapshot);
|
||||
} catch {
|
||||
if (active) setGenerationError(true);
|
||||
} finally {
|
||||
if (active) setHydrated(true);
|
||||
}
|
||||
})();
|
||||
return () => { active = false; };
|
||||
}, [routeId]);
|
||||
|
||||
// First-generation: create report when none persists (v0.58)
|
||||
useEffect(() => {
|
||||
@@ -54,7 +65,8 @@ export default function ReportPage({ params }) {
|
||||
const rev = existing?.investigationRevision ?? 0;
|
||||
const reportData = { understanding: data.understanding, plausibleInterpretations: data.plausibleInterpretations, hasPlausibleInterpretations: true, generatedFromRevision: rev };
|
||||
setExisting((p) => {
|
||||
saveInvestigation({ ...p, investigationReport: reportData });
|
||||
void saveInvestigation({ ...p, investigationReport: reportData })
|
||||
.catch((error) => console.error("Investigation report save failed", error));
|
||||
return { ...p, investigationReport: reportData };
|
||||
});
|
||||
} else {
|
||||
@@ -73,7 +85,7 @@ export default function ReportPage({ params }) {
|
||||
if (updateLoading) return;
|
||||
setUpdateLoading(true);
|
||||
|
||||
const snap = loadInvestigation(routeId);
|
||||
const snap = await loadInvestigation(routeId);
|
||||
const situationGraph = snap?.situationGraph;
|
||||
const findings = snap?.findings ?? [];
|
||||
const rev = snap?.investigationRevision ?? 0;
|
||||
@@ -99,7 +111,8 @@ export default function ReportPage({ params }) {
|
||||
if (data.success) {
|
||||
const reportData = { understanding: data.understanding, plausibleInterpretations: data.plausibleInterpretations, hasPlausibleInterpretations: true, generatedFromRevision: rev };
|
||||
setExisting((p) => {
|
||||
saveInvestigation({ ...p, investigationReport: reportData });
|
||||
void saveInvestigation({ ...p, investigationReport: reportData })
|
||||
.catch((error) => console.error("Investigation report save failed", error));
|
||||
return { ...p, investigationReport: reportData };
|
||||
});
|
||||
}
|
||||
|
||||
+28
-5
@@ -8,10 +8,23 @@ import { useRouter } from "next/navigation";
|
||||
function Portfolio() {
|
||||
const router = useRouter();
|
||||
const [summaries, setSummaries] = React.useState([]);
|
||||
const [hydrated, setHydrated] = React.useState(false);
|
||||
const [loadError, setLoadError] = React.useState(null);
|
||||
const [showRestartConfirm, setShowRestartConfirm] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setSummaries(listInvestigations());
|
||||
let active = true;
|
||||
(async () => {
|
||||
try {
|
||||
const investigations = await listInvestigations();
|
||||
if (active) setSummaries(investigations);
|
||||
} catch (error) {
|
||||
if (active) setLoadError(error);
|
||||
} finally {
|
||||
if (active) setHydrated(true);
|
||||
}
|
||||
})();
|
||||
return () => { active = false; };
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -96,10 +109,14 @@ function Portfolio() {
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
onClick={async () => {
|
||||
setShowRestartConfirm(null);
|
||||
try { restartInvestigation(summary.id); } catch (_) { /* storage must not crash caller */ }
|
||||
setSummaries(listInvestigations());
|
||||
try {
|
||||
await restartInvestigation(summary.id);
|
||||
setSummaries(await listInvestigations());
|
||||
} catch (error) {
|
||||
setLoadError(error);
|
||||
}
|
||||
}}
|
||||
className="rounded-lg border border-red-400 bg-white px-4 py-2 text-sm font-medium text-red-700 hover:bg-red-50 transition"
|
||||
>
|
||||
@@ -116,7 +133,13 @@ function Portfolio() {
|
||||
)}
|
||||
|
||||
{/* No investigations */}
|
||||
{summaries.length === 0 && (
|
||||
{!hydrated && (
|
||||
<section className="mb-10"><h2 className="mb-4 text-[13px] font-bold tracking-[.18em] uppercase text-teal-700/80">Investigations</h2><p className="text-sm text-gray-500 italic">Loading investigations…</p></section>
|
||||
)}
|
||||
{hydrated && loadError && (
|
||||
<section className="mb-10"><h2 className="mb-4 text-[13px] font-bold tracking-[.18em] uppercase text-teal-700/80">Investigations</h2><p className="text-sm text-red-600">Unable to load investigations.</p></section>
|
||||
)}
|
||||
{hydrated && !loadError && summaries.length === 0 && (
|
||||
<section className="mb-10">
|
||||
<h2 className="mb-4 text-[13px] font-bold tracking-[.18em] uppercase text-teal-700/80">
|
||||
Investigations
|
||||
|
||||
Reference in New Issue
Block a user