mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
feat(documents): the vault surface
The page becomes one wide, calm filing-cabinet view: PageHeader with an Upload action, a drop-target upload zone whose accept list and per-file cap come from the usage endpoint (quota bar only above 80 %), a sticky filter rail (200 ms debounced search focused by '/', nine type chips, condition chips from linked episodes, a year segmenter, one-tap clear), and the virtualized timeline below. Filter state lives in the URL so every view is shareable and back-button-safe; uploads filtered to an episode arrive pre-linked. Hovering a card prefetches detail metadata — never the blob. Designed states: a skeleton grid that matches the loaded layout, a teaching empty state, a distinct no-matches state, and a retryable error card. /documents joins the shell's wide-container list; the shell stays the single owner of the page frame. Locale bundles gain the vault vocabulary in all six languages and drop the retired extraction-review strings. Plan-Task: W2-T1, W2-T4 (zone), W2-T5 (prefetch), W2-T6
This commit is contained in:
1 parent
4014d77a21
commit
b8009d2e73
12 files changed
+1268
-754
No files matched your search
+12
-39
@@ -1,45 +1,18 @@
|
||||
"use client";
|
||||
import { Suspense } from "react";
|
||||
|
||||
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";
|
||||
import { DocumentsView } from "@/components/documents/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.
|
||||
* The Dokumente vault — server shell only. The client surface owns the
|
||||
* module gate, the URL-backed filter state (which needs `useSearchParams`,
|
||||
* hence the Suspense boundary), the virtualized timeline, and the upload
|
||||
* queue. The route rides the AuthShell's wide-container flag: the shell
|
||||
* owns the page frame, this file never re-implements paddings.
|
||||
*/
|
||||
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 />;
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<DocumentsView />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user