diff --git a/apps/trust/src/layouts/MainLayout.tsx b/apps/trust/src/layouts/MainLayout.tsx index ecf882feb..ae7c96ad3 100644 --- a/apps/trust/src/layouts/MainLayout.tsx +++ b/apps/trust/src/layouts/MainLayout.tsx @@ -1,6 +1,6 @@ import { type PreloadedQuery, usePreloadedQuery } from "react-relay"; -import type { TrustGraphQuery } from "/queries/__generated__/TrustGraphQuery.graphql.ts"; -import { trustGraphQuery } from "/queries/TrustGraph.ts"; +import type { TrustGraphCurrentQuery } from "/queries/__generated__/TrustGraphCurrentQuery.graphql.ts"; +import { currentTrustGraphQuery } from "/queries/TrustGraph.ts"; import { Logo, TabLink, Tabs } from "@probo/ui"; import { useTranslate } from "@probo/i18n"; import { OrganizationSidebar } from "/components/OrganizationSidebar"; @@ -10,21 +10,17 @@ import { AuthProvider } from "/providers/AuthProvider"; import { TrustCenterProvider } from "/providers/TrustCenterProvider"; type Props = { - queryRef: PreloadedQuery; + queryRef: PreloadedQuery; }; export function MainLayout(props: Props) { const { __ } = useTranslate(); - const trustCenter = usePreloadedQuery( - trustGraphQuery, - props.queryRef, - ).trustCenterBySlug; + const data = usePreloadedQuery(currentTrustGraphQuery, props.queryRef); + const trustCenter = data.currentTrustCenter; if (!trustCenter) { return null; } - - const baseTabUrl = `/trust/${trustCenter.slug}`; const showNDADialog = trustCenter.isUserAuthenticated && !trustCenter.hasAcceptedNonDisclosureAgreement && @@ -44,11 +40,11 @@ export function MainLayout(props: Props) {
- {__("Overview")} - + {__("Overview")} + {__("Documents")} - + {__("Subprocessors")} diff --git a/apps/trust/src/pages/AccessPage.tsx b/apps/trust/src/pages/AccessPage.tsx index 1f9d93d3b..21b457ef0 100644 --- a/apps/trust/src/pages/AccessPage.tsx +++ b/apps/trust/src/pages/AccessPage.tsx @@ -1,6 +1,6 @@ import { useTranslate } from "@probo/i18n"; import { useEffect, useState } from "react"; -import { useNavigate, useParams, useSearchParams } from "react-router"; +import { useNavigate, useSearchParams } from "react-router"; import { buildEndpoint } from "/providers/RelayProviders"; import { PageError } from "/components/PageError"; import { Spinner } from "@probo/ui"; @@ -10,16 +10,12 @@ import { Spinner } from "@probo/ui"; */ export function AccessPage() { const { __ } = useTranslate(); - const { slug } = useParams<{ slug: string }>(); const [searchParams] = useSearchParams(); const token = searchParams.get("token"); const navigate = useNavigate(); - const isValidRequest = !!(slug && token); + const isValidRequest = !!token; const [error, setError] = useState(() => { - if (!slug) { - return __("Invalid trust center"); - } if (!token) { return __("Invalid access token"); } @@ -57,7 +53,7 @@ export function AccessPage() { }) .then((data) => { if (data.success) { - navigate(`/trust/${slug}`); + navigate("/overview"); return; } throw new Error(data.message ?? __("Authentication failed")); @@ -65,7 +61,7 @@ export function AccessPage() { .catch((error) => { setError(error.message); }); - }, [isValidRequest, slug, token, __, navigate]); + }, [isValidRequest, token, __, navigate]); if (error) { return ; diff --git a/apps/trust/src/pages/DocumentsPage.tsx b/apps/trust/src/pages/DocumentsPage.tsx index efcdde216..94524c64a 100644 --- a/apps/trust/src/pages/DocumentsPage.tsx +++ b/apps/trust/src/pages/DocumentsPage.tsx @@ -1,6 +1,6 @@ import { type PreloadedQuery, usePreloadedQuery } from "react-relay"; -import { trustDocumentsQuery } from "/queries/TrustGraph"; -import type { TrustGraphDocumentsQuery } from "/queries/__generated__/TrustGraphDocumentsQuery.graphql.ts"; +import { currentTrustDocumentsQuery } from "/queries/TrustGraph"; +import type { TrustGraphCurrentDocumentsQuery } from "/queries/__generated__/TrustGraphCurrentDocumentsQuery.graphql.ts"; import { groupBy, objectEntries } from "@probo/helpers"; import { documentTypeLabel } from "/helpers/documents"; import { useTranslate } from "@probo/i18n"; @@ -10,16 +10,19 @@ import { Rows } from "/components/Rows.tsx"; import { RowHeader } from "/components/RowHeader.tsx"; type Props = { - queryRef: PreloadedQuery; + queryRef: PreloadedQuery; }; export function DocumentsPage({ queryRef }: Props) { const { __ } = useTranslate(); - const data = usePreloadedQuery(trustDocumentsQuery, queryRef); + const data = usePreloadedQuery( + currentTrustDocumentsQuery, + queryRef + ); const documents = - data.trustCenterBySlug?.documents.edges.map((edge) => edge.node) ?? []; + data.currentTrustCenter?.documents.edges.map((edge) => edge.node) ?? []; const documentsPerType = groupBy(documents, (document) => - documentTypeLabel(document.documentType, __), + documentTypeLabel(document.documentType, __) ); return (
diff --git a/apps/trust/src/pages/SubprocessorsPage.tsx b/apps/trust/src/pages/SubprocessorsPage.tsx index 4bbc6cfb6..1642642e8 100644 --- a/apps/trust/src/pages/SubprocessorsPage.tsx +++ b/apps/trust/src/pages/SubprocessorsPage.tsx @@ -1,27 +1,27 @@ import { type PreloadedQuery, usePreloadedQuery } from "react-relay"; -import { trustVendorsQuery } from "/queries/TrustGraph"; +import { currentTrustVendorsQuery } from "/queries/TrustGraph"; import { sprintf } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; -import type { TrustGraphVendorsQuery } from "/queries/__generated__/TrustGraphVendorsQuery.graphql"; +import type { TrustGraphCurrentVendorsQuery } from "/queries/__generated__/TrustGraphCurrentVendorsQuery.graphql"; import { VendorRow } from "/components/VendorRow"; import { Rows } from "/components/Rows.tsx"; type Props = { - queryRef: PreloadedQuery; + queryRef: PreloadedQuery; }; export function SubprocessorsPage({ queryRef }: Props) { const { __ } = useTranslate(); - const data = usePreloadedQuery(trustVendorsQuery, queryRef); + const data = usePreloadedQuery(currentTrustVendorsQuery, queryRef); const vendors = - data.trustCenterBySlug?.vendors.edges.map((edge) => edge.node) ?? []; + data.currentTrustCenter?.vendors.edges.map((edge) => edge.node) ?? []; return (

{__("Subprocessors")}

{sprintf( __("Third-party subprocessors %s work with:"), - data.trustCenterBySlug?.organization.name ?? "", + data.currentTrustCenter?.organization.name ?? "" )}

diff --git a/apps/trust/src/providers/RelayProviders.tsx b/apps/trust/src/providers/RelayProviders.tsx index 645593082..ea4ad1e63 100644 --- a/apps/trust/src/providers/RelayProviders.tsx +++ b/apps/trust/src/providers/RelayProviders.tsx @@ -86,10 +86,15 @@ const fetchRelay: FetchFunction = async ( requestInit.body = formData; } else { + // Extract slug from URL if present for slug-based routing + const slugMatch = window.location.pathname.match(/^\/trust\/([^/]+)/); + const slug = slugMatch ? slugMatch[1] : null; + requestInit.headers = { Accept: "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", "Content-Type": "application/json", + ...(slug ? { "X-Trust-Slug": slug } : {}), }; requestInit.body = JSON.stringify({ @@ -99,8 +104,11 @@ const fetchRelay: FetchFunction = async ( }); } + // Use relative API path to ensure it goes through the same routing context + // For /trust/slug/overview, this resolves to /trust/slug/api/trust/v1/graphql + // For custom domains at /overview, this resolves to /api/trust/v1/graphql const response = await fetch( - buildEndpoint("/api/trust/v1/graphql"), + buildEndpoint("./api/trust/v1/graphql"), requestInit, ); diff --git a/apps/trust/src/queries/TrustGraph.ts b/apps/trust/src/queries/TrustGraph.ts index 0bec4408b..65c6ab371 100644 --- a/apps/trust/src/queries/TrustGraph.ts +++ b/apps/trust/src/queries/TrustGraph.ts @@ -68,3 +68,73 @@ export const trustVendorsQuery = graphql` } } `; + +// Queries for custom domain (subdomain) approach +export const currentTrustGraphQuery = graphql` + query TrustGraphCurrentQuery { + currentTrustCenter { + id + slug + isUserAuthenticated + hasAcceptedNonDisclosureAgreement + ndaFileName + ndaFileUrl + organization { + name + description + websiteUrl + logoUrl + email + headquarterAddress + } + ...OverviewPageFragment + audits(first: 50) { + edges { + node { + id + ...AuditRowFragment + } + } + } + } + } +`; + +export const currentTrustDocumentsQuery = graphql` + query TrustGraphCurrentDocumentsQuery { + currentTrustCenter { + id + organization { + name + } + documents(first: 50) { + edges { + node { + id + documentType + ...DocumentRowFragment + } + } + } + } + } +`; + +export const currentTrustVendorsQuery = graphql` + query TrustGraphCurrentVendorsQuery { + currentTrustCenter { + id + organization { + name + } + vendors(first: 50) { + edges { + node { + id + ...VendorRowFragment + } + } + } + } + } +`; diff --git a/apps/trust/src/queries/__generated__/TrustGraphCurrentDocumentsQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphCurrentDocumentsQuery.graphql.ts new file mode 100644 index 000000000..52116ab3f --- /dev/null +++ b/apps/trust/src/queries/__generated__/TrustGraphCurrentDocumentsQuery.graphql.ts @@ -0,0 +1,229 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type DocumentType = "ISMS" | "OTHER" | "POLICY"; +export type TrustGraphCurrentDocumentsQuery$variables = Record; +export type TrustGraphCurrentDocumentsQuery$data = { + readonly currentTrustCenter: { + readonly documents: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly documentType: DocumentType; + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"DocumentRowFragment">; + }; + }>; + }; + readonly id: string; + readonly organization: { + readonly name: string; + }; + } | null | undefined; +}; +export type TrustGraphCurrentDocumentsQuery = { + response: TrustGraphCurrentDocumentsQuery$data; + variables: TrustGraphCurrentDocumentsQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v2 = [ + { + "kind": "Literal", + "name": "first", + "value": 50 + } +], +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "documentType", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "TrustGraphCurrentDocumentsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "currentTrustCenter", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Organization", + "kind": "LinkedField", + "name": "organization", + "plural": false, + "selections": [ + (v1/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "DocumentConnection", + "kind": "LinkedField", + "name": "documents", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Document", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + (v3/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "DocumentRowFragment" + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "documents(first:50)" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "TrustGraphCurrentDocumentsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "currentTrustCenter", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Organization", + "kind": "LinkedField", + "name": "organization", + "plural": false, + "selections": [ + (v1/*: any*/), + (v0/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "DocumentConnection", + "kind": "LinkedField", + "name": "documents", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Document", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "title", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "documents(first:50)" + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "f04b485bb7a2ff20ac409e123404a954", + "id": null, + "metadata": {}, + "name": "TrustGraphCurrentDocumentsQuery", + "operationKind": "query", + "text": "query TrustGraphCurrentDocumentsQuery {\n currentTrustCenter {\n id\n organization {\n name\n id\n }\n documents(first: 50) {\n edges {\n node {\n id\n documentType\n ...DocumentRowFragment\n }\n }\n }\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n}\n" + } +}; +})(); + +(node as any).hash = "73636da5c06a16813a57b92f30ab93d2"; + +export default node; diff --git a/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts new file mode 100644 index 000000000..d6711e4ba --- /dev/null +++ b/apps/trust/src/queries/__generated__/TrustGraphCurrentQuery.graphql.ts @@ -0,0 +1,509 @@ +/** + * @generated SignedSource<<1f034e5f6819d3db77f6aa02ec4e92c5>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type TrustGraphCurrentQuery$variables = Record; +export type TrustGraphCurrentQuery$data = { + readonly currentTrustCenter: { + readonly audits: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"AuditRowFragment">; + }; + }>; + }; + readonly hasAcceptedNonDisclosureAgreement: boolean; + readonly id: string; + readonly isUserAuthenticated: boolean; + readonly ndaFileName: string | null | undefined; + readonly ndaFileUrl: string | null | undefined; + readonly organization: { + readonly description: string | null | undefined; + readonly email: string | null | undefined; + readonly headquarterAddress: string | null | undefined; + readonly logoUrl: string | null | undefined; + readonly name: string; + readonly websiteUrl: string | null | undefined; + }; + readonly slug: string; + readonly " $fragmentSpreads": FragmentRefs<"OverviewPageFragment">; + } | null | undefined; +}; +export type TrustGraphCurrentQuery = { + response: TrustGraphCurrentQuery$data; + variables: TrustGraphCurrentQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "slug", + "storageKey": null +}, +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "isUserAuthenticated", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasAcceptedNonDisclosureAgreement", + "storageKey": null +}, +v4 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "ndaFileName", + "storageKey": null +}, +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "ndaFileUrl", + "storageKey": null +}, +v6 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v7 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null +}, +v8 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "websiteUrl", + "storageKey": null +}, +v9 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "logoUrl", + "storageKey": null +}, +v10 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "email", + "storageKey": null +}, +v11 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "headquarterAddress", + "storageKey": null +}, +v12 = [ + { + "kind": "Literal", + "name": "first", + "value": 50 + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "TrustGraphCurrentQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "currentTrustCenter", + "plural": false, + "selections": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/), + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Organization", + "kind": "LinkedField", + "name": "organization", + "plural": false, + "selections": [ + (v6/*: any*/), + (v7/*: any*/), + (v8/*: any*/), + (v9/*: any*/), + (v10/*: any*/), + (v11/*: any*/) + ], + "storageKey": null + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "OverviewPageFragment" + }, + { + "alias": null, + "args": (v12/*: any*/), + "concreteType": "AuditConnection", + "kind": "LinkedField", + "name": "audits", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "AuditEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Audit", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "AuditRowFragment" + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "audits(first:50)" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "TrustGraphCurrentQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "currentTrustCenter", + "plural": false, + "selections": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/), + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Organization", + "kind": "LinkedField", + "name": "organization", + "plural": false, + "selections": [ + (v6/*: any*/), + (v7/*: any*/), + (v8/*: any*/), + (v9/*: any*/), + (v10/*: any*/), + (v11/*: any*/), + (v0/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 14 + } + ], + "concreteType": "TrustCenterReferenceConnection", + "kind": "LinkedField", + "name": "references", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenterReferenceEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenterReference", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + (v6/*: any*/), + (v9/*: any*/), + (v8/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "references(first:14)" + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 3 + } + ], + "concreteType": "VendorConnection", + "kind": "LinkedField", + "name": "vendors", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "VendorEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Vendor", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + (v6/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "category", + "storageKey": null + }, + (v8/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "privacyPolicyUrl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "countries", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "vendors(first:3)" + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 5 + } + ], + "concreteType": "DocumentConnection", + "kind": "LinkedField", + "name": "documents", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Document", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "title", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "documentType", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "documents(first:5)" + }, + { + "alias": null, + "args": (v12/*: any*/), + "concreteType": "AuditConnection", + "kind": "LinkedField", + "name": "audits", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "AuditEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Audit", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Report", + "kind": "LinkedField", + "name": "report", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "filename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Framework", + "kind": "LinkedField", + "name": "framework", + "plural": false, + "selections": [ + (v0/*: any*/), + (v6/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "audits(first:50)" + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "a12f51feed01678d5a39df58ef95ad29", + "id": null, + "metadata": {}, + "name": "TrustGraphCurrentQuery", + "operationKind": "query", + "text": "query TrustGraphCurrentQuery {\n currentTrustCenter {\n id\n slug\n isUserAuthenticated\n hasAcceptedNonDisclosureAgreement\n ndaFileName\n ndaFileUrl\n organization {\n name\n description\n websiteUrl\n logoUrl\n email\n headquarterAddress\n id\n }\n ...OverviewPageFragment\n audits(first: 50) {\n edges {\n node {\n id\n ...AuditRowFragment\n }\n }\n }\n }\n}\n\nfragment AuditRowFragment on Audit {\n report {\n id\n filename\n }\n framework {\n id\n name\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n}\n\nfragment OverviewPageFragment on TrustCenter {\n references(first: 14) {\n edges {\n node {\n id\n name\n logoUrl\n websiteUrl\n }\n }\n }\n vendors(first: 3) {\n edges {\n node {\n id\n ...VendorRowFragment\n }\n }\n }\n documents(first: 5) {\n edges {\n node {\n id\n ...DocumentRowFragment\n documentType\n }\n }\n }\n}\n\nfragment VendorRowFragment on Vendor {\n id\n name\n category\n websiteUrl\n privacyPolicyUrl\n countries\n}\n" + } +}; +})(); + +(node as any).hash = "2fc7c45c2636a551c13d806f89e53c4a"; + +export default node; diff --git a/apps/trust/src/queries/__generated__/TrustGraphCurrentVendorsQuery.graphql.ts b/apps/trust/src/queries/__generated__/TrustGraphCurrentVendorsQuery.graphql.ts new file mode 100644 index 000000000..dbda366ad --- /dev/null +++ b/apps/trust/src/queries/__generated__/TrustGraphCurrentVendorsQuery.graphql.ts @@ -0,0 +1,240 @@ +/** + * @generated SignedSource<<66c7f989b05136848efd4ec2d918a0bd>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type TrustGraphCurrentVendorsQuery$variables = Record; +export type TrustGraphCurrentVendorsQuery$data = { + readonly currentTrustCenter: { + readonly id: string; + readonly organization: { + readonly name: string; + }; + readonly vendors: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"VendorRowFragment">; + }; + }>; + }; + } | null | undefined; +}; +export type TrustGraphCurrentVendorsQuery = { + response: TrustGraphCurrentVendorsQuery$data; + variables: TrustGraphCurrentVendorsQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v2 = [ + { + "kind": "Literal", + "name": "first", + "value": 50 + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "TrustGraphCurrentVendorsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "currentTrustCenter", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Organization", + "kind": "LinkedField", + "name": "organization", + "plural": false, + "selections": [ + (v1/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "VendorConnection", + "kind": "LinkedField", + "name": "vendors", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "VendorEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Vendor", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "VendorRowFragment" + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "vendors(first:50)" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "TrustGraphCurrentVendorsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "currentTrustCenter", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Organization", + "kind": "LinkedField", + "name": "organization", + "plural": false, + "selections": [ + (v1/*: any*/), + (v0/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "VendorConnection", + "kind": "LinkedField", + "name": "vendors", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "VendorEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Vendor", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "category", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "websiteUrl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "privacyPolicyUrl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "countries", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "vendors(first:50)" + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "1ae723b3588f13e1233ab6eb4c18215e", + "id": null, + "metadata": {}, + "name": "TrustGraphCurrentVendorsQuery", + "operationKind": "query", + "text": "query TrustGraphCurrentVendorsQuery {\n currentTrustCenter {\n id\n organization {\n name\n id\n }\n vendors(first: 50) {\n edges {\n node {\n id\n ...VendorRowFragment\n }\n }\n }\n }\n}\n\nfragment VendorRowFragment on Vendor {\n id\n name\n category\n websiteUrl\n privacyPolicyUrl\n countries\n}\n" + } +}; +})(); + +(node as any).hash = "a02b52855582163623c28763fbaeb568"; + +export default node; diff --git a/apps/trust/src/routes.tsx b/apps/trust/src/routes.tsx index ea167c27b..75f88a074 100644 --- a/apps/trust/src/routes.tsx +++ b/apps/trust/src/routes.tsx @@ -16,9 +16,9 @@ import { useCleanup } from "./hooks/useDelayedEffect"; import { PageError } from "./components/PageError"; import { MainLayout } from "/layouts/MainLayout"; import { - trustGraphQuery, - trustDocumentsQuery, - trustVendorsQuery, + currentTrustGraphQuery, + currentTrustDocumentsQuery, + currentTrustVendorsQuery, } from "/queries/TrustGraph"; import { OverviewPage } from "/pages/OverviewPage"; import { DocumentsPage } from "/pages/DocumentsPage"; @@ -50,49 +50,64 @@ function ErrorBoundary({ error: propsError }: { error?: string }) { const routes = [ { path: "/", + loader: async () => { + throw redirect("/overview"); + }, Component: Fragment, ErrorBoundary: ErrorBoundary, }, + // Custom domain routes (subdomain-based) { - path: "/trust/:slug/access", - Component: AccessPage, - ErrorBoundary: ErrorBoundary, - }, - { - path: "/trust/:slug", - queryLoader: ({ slug }) => - loadQuery(relayEnvironment, trustGraphQuery, { slug: slug }), + path: "/overview", + queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}), Component: MainLayout, fallback: MainSkeleton, ErrorBoundary: ErrorBoundary, children: [ { path: "", - loader: ({ params }) => { - throw redirect(`/trust/${params.slug}/overview`); - }, - }, - { - path: "overview", fallback: TabSkeleton, Component: OverviewPage, }, + ], + }, + { + path: "/documents", + queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}), + Component: MainLayout, + fallback: MainSkeleton, + ErrorBoundary: ErrorBoundary, + children: [ { - path: "documents", + path: "", + queryLoader: () => + loadQuery(relayEnvironment, currentTrustDocumentsQuery, {}), fallback: TabSkeleton, Component: DocumentsPage, - queryLoader: ({ slug }) => - loadQuery(relayEnvironment, trustDocumentsQuery, { slug: slug }), - }, - { - path: "subprocessors", - fallback: TabSkeleton, - Component: SubprocessorsPage, - queryLoader: ({ slug }) => - loadQuery(relayEnvironment, trustVendorsQuery, { slug: slug }), }, ], }, + { + path: "/subprocessors", + queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}), + Component: MainLayout, + fallback: MainSkeleton, + ErrorBoundary: ErrorBoundary, + children: [ + { + path: "", + queryLoader: () => + loadQuery(relayEnvironment, currentTrustVendorsQuery, {}), + fallback: TabSkeleton, + Component: SubprocessorsPage, + }, + ], + }, + { + path: "/access", + Component: AccessPage, + ErrorBoundary: ErrorBoundary, + }, // Fallback URL to the NotFound Page { path: "*", @@ -150,4 +165,15 @@ function routeTransformer({ } as RouteObject; } -export const router = createBrowserRouter(routes.map(routeTransformer)); +// Detect basename from current URL path +// If URL starts with /trust/{slug}, extract that as the basename +// Otherwise, use "/" for custom domains +function getBasename(): string { + const path = window.location.pathname; + const trustMatch = path.match(/^\/trust\/[^/]+/); + return trustMatch ? trustMatch[0] : "/"; +} + +export const router = createBrowserRouter(routes.map(routeTransformer), { + basename: getBasename(), +}); diff --git a/apps/trust/vite.config.ts b/apps/trust/vite.config.ts index abc29b15d..3b281c25e 100644 --- a/apps/trust/vite.config.ts +++ b/apps/trust/vite.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ build: { assetsDir: "assets", }, - base: "/trust/", + base: "./", server: { port: 5174, proxy: {