Compliance portal document pages#1491
Conversation
Build the Trust Center documents page: a unified list of published documents, uploaded files, and audit reports, grouped into category sections. An All/Public/Private tab bar filters the list by trust center visibility. Expose that filter over the trust v1 API by adding a TrustCenterVisibility enum and a shared TrustCenterVisibilityFilter input, wiring it through the documents, audits, and trustCenterFiles connections down to the existing coredata SQL filters. "All" keeps the default public+private slice; the other tabs pin a single visibility. Access controls are display-only for now (auth is handled separately): authorized or public entries open their exported PDF via the export mutations, requested entries show a pending state, and everything else shows an inert Get Access affordance. Add the v2 Tabs and Toaster kit components (Base UI headless) needed by the page and mount a toast provider at the app root for mutation feedback. Signed-off-by: Émile Ré <emile@probo.com>
Replace the minimal placeholder Toaster with the richer implementation already built on compliance-portal-auth (per-type icons plus warning and info variants, and its stories). Both branches now share the exact same component and app-root wiring, so they converge instead of colliding when they merge. Align App.tsx provider ordering to match. Signed-off-by: Émile Ré <emile@probo.com>
The results container hand-wrote its layout classes and toggled the dimmed state with a string-interpolated conditional. Move both the page shell and the busy/dimmed state into a documentsLayout tv variant, and reuse it in the skeleton so the loading and loaded layouts share one source of truth. Signed-off-by: Émile Ré <emile@probo.com>
8d69040 to
bce7535
Compare
Give the toolbar a 64px min-height so its controls are vertically centered, and make the header band flush at the bottom. This widens the gap above the toolbar and trims the oversized space below it to match the Figma hero. Mirror the header in the skeleton so it does not jump when data loads. Signed-off-by: Émile Ré <emile@probo.com>
There was a problem hiding this comment.
2 issues found across 46 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/compliance-portal/src/pages/documents/DocumentsPageLoader.tsx">
<violation number="1" location="apps/compliance-portal/src/pages/documents/DocumentsPageLoader.tsx:36">
P2: The first result can be displayed under the wrong tab when the URL changes while this preload is pending: `initialVariables` remains on the old tab, and `DocumentsPage` treats its first render as already synchronized and skips refetching. Passing the loaded tab through to the page or coordinating the initial refetch would keep the URL filter and displayed slice consistent.</violation>
</file>
<file name="apps/compliance-portal/src/pages/documents/_lib/openExportedFile.ts">
<violation number="1" location="apps/compliance-portal/src/pages/documents/_lib/openExportedFile.ts:28">
P1: Viewing a Trust Center file with an active MIME type can execute its uploaded HTML/SVG content because the returned MIME is used directly for a navigated Blob URL. Restrict inline navigation to an explicit safe allowlist such as PDF, or force untrusted/non-previewable types to download with `application/octet-stream`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| const commaIndex = dataUri.indexOf(","); | ||
| const base64 = commaIndex === -1 ? dataUri : dataUri.slice(commaIndex + 1); | ||
| const mimeMatch = dataUri.match(/^data:([^;]+);/); | ||
| const mimeType = mimeMatch?.[1] ?? "application/octet-stream"; |
There was a problem hiding this comment.
P1: Viewing a Trust Center file with an active MIME type can execute its uploaded HTML/SVG content because the returned MIME is used directly for a navigated Blob URL. Restrict inline navigation to an explicit safe allowlist such as PDF, or force untrusted/non-previewable types to download with application/octet-stream.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/compliance-portal/src/pages/documents/_lib/openExportedFile.ts, line 28:
<comment>Viewing a Trust Center file with an active MIME type can execute its uploaded HTML/SVG content because the returned MIME is used directly for a navigated Blob URL. Restrict inline navigation to an explicit safe allowlist such as PDF, or force untrusted/non-previewable types to download with `application/octet-stream`.</comment>
<file context>
@@ -0,0 +1,42 @@
+ const commaIndex = dataUri.indexOf(",");
+ const base64 = commaIndex === -1 ? dataUri : dataUri.slice(commaIndex + 1);
+ const mimeMatch = dataUri.match(/^data:([^;]+);/);
+ const mimeType = mimeMatch?.[1] ?? "application/octet-stream";
+
+ const binary = atob(base64);
</file context>
|
|
||
| // Seed the first fetch with the URL's tab; later changes are handled by the | ||
| // page's refetch. | ||
| const initialVariables = useRef(toQueryVariables(tab)); |
There was a problem hiding this comment.
P2: The first result can be displayed under the wrong tab when the URL changes while this preload is pending: initialVariables remains on the old tab, and DocumentsPage treats its first render as already synchronized and skips refetching. Passing the loaded tab through to the page or coordinating the initial refetch would keep the URL filter and displayed slice consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/compliance-portal/src/pages/documents/DocumentsPageLoader.tsx, line 36:
<comment>The first result can be displayed under the wrong tab when the URL changes while this preload is pending: `initialVariables` remains on the old tab, and `DocumentsPage` treats its first render as already synchronized and skips refetching. Passing the loaded tab through to the page or coordinating the initial refetch would keep the URL filter and displayed slice consistent.</comment>
<file context>
@@ -0,0 +1,47 @@
+
+ // Seed the first fetch with the URL's tab; later changes are handled by the
+ // page's refetch.
+ const initialVariables = useRef(toQueryVariables(tab));
+
+ useEffect(() => {
</file context>
Fold the review feedback from the documents page work into the shared components and helpers: - Toaster: give the close control the kit's focus-visible ring, and wrap the title/description in Toast.Content so stacked toasts get Base UI's height measurement and overflow handling. - TabsSkeleton: spread rest before the fixed aria-hidden so the decorative subtree can't be exposed to assistive tech. - DocumentsPage: reconcile the active tab against the tab the preloaded query actually loaded with, so a tab change during the initial preload no longer shows the wrong slice. - Grouping: drop the duplicated bucketing helpers and reuse the shared groupBy from @probo/helpers, keeping field-specific sorting/labeling in the callers. - Documents list items: extract a shared DocumentEntry row and a useExportAndOpen hook so the three item components stop repeating the export/access behavior while keeping their own fragments. - Subprocessors skeleton: reuse SelectSkeleton/TextFieldSkeleton instead of hand-rolled placeholders. - useDocumentTab: derive DocumentTab and URL validation from a single DOCUMENT_TABS source of truth. Signed-off-by: Émile Ré <emile@probo.com>
Summary by cubic
Build the Trust Center Documents page with All/Public/Private tabs, grouped sections, and inline access actions. Adds a visibility filter to the trust v1 GraphQL API and ships
v2Tabs and Toaster (mounted app‑wide), plus follow‑ups for preload/tab sync, shared export helpers, and small UI polish.Coredata
+10-0WithTrustCenterVisibilities(...)toDocumentFilterandAuditFilterto request specific visibility slices.GraphQL API
+41-11TrustCenterVisibilityenum andTrustCenterVisibilityFilterinput.filtertoTrustCenter.documents,audits, andtrustCenterFiles; default to public+private when omitted.Service
+14-8Documents.ListForOrganizationIdandAudits.ListForOrganizationIdto accept an optional filter (defaulting to trust‑center filters when nil).compliance_page_servicecall sites to passnilfor unchanged behavior.Package: ui
+527-7v2Tabs on@base-ui/react/tabs(Tabs,TabsList,TabsTab,TabsIndicator,TabsSkeleton) andv2Toaster on@base-ui/react/toastwith per‑type icons and stories.Other
+1228-35/documents) with loader and skeleton; localize page/UI strings (en/fr).documentsLayouttv variant for page and skeleton.useExportAndOpenhook; show “View”, “Access requested”, or inert “Get Access”.Toast.Provider+<Toaster />) in the app root; addcommon.errori18n keys.flushBottomSpaceto align tab bars; wire newdocumentRoutesand remove the old placeholder route.SelectSkeleton/TextFieldSkeleton; reuse sharedgroupByhelper.Written for commit 99990d6. Summary will update on new commits.