feat(documents): module gate, nav, and review-then-confirm UI

Add the opt-in inboundDocuments module (off by default, like the MCP
endpoint): ingesting a clinical document egresses it to the OCR/vision
provider, so the surface ships dark and the user turns it on from the
Modules hub. The born-gated /documents page carries the prominent
non-diagnostic disclaimer (we read, we don't interpret; nothing is saved
until you approve it; only your clinician can interpret these results),
uploads a document, and walks each extracted fact: source provenance and
confidence are shown, low-confidence facts are blocked from approval until
edited, and the confirm step commits the approved facts.
This commit is contained in:
Marc-André Bombeck committed 2026-06-28 16:57:16 +02:00
1 parent 98ca0b9d61
commit 422778f679
10 files changed
+707 -4

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { Loader2 } from "lucide-react";
import { useAuth } from "@/hooks/use-auth";
import { InboundDocumentsView } from "@/components/documents/inbound-documents-view";
/**
* v1.25.0 (W-DOCS-IN) — inbound clinical documents entry.
*
* Born-gated on the resolved `modules.inboundDocuments` flag from
* `GET /api/auth/me` (opt-in / default-off — the per-user opt-in AND the
* operator server-wide kill-switch). An unauthenticated visitor is bounced to
* login; an authenticated account without the module opted in is bounced home
* (the nav entry is already hidden for them, so this only catches a direct URL
* hit). Every `/api/documents/inbound/*` route also enforces the gate
* server-side, so this is a UX redirect, not the security boundary.
*/
export default function DocumentsPage() {
const { user, isLoading, isAuthenticated } = useAuth();
const router = useRouter();
const enabled = user?.modules?.inboundDocuments === true;
useEffect(() => {
if (isLoading) return;
if (!isAuthenticated) {
router.push("/auth/login");
} else if (!enabled) {
router.push("/");
}
}, [isLoading, isAuthenticated, enabled, router]);
if (isLoading || !isAuthenticated || !enabled) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="text-primary h-8 w-8 animate-spin motion-reduce:animate-none" />
</div>
);
}
return <InboundDocumentsView />;
}