Add trust center front
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import {
|
||||
Card,
|
||||
Tr,
|
||||
Td,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Th,
|
||||
Button,
|
||||
IconArrowDown,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { FrameworkLogo } from "/components/FrameworkLogo";
|
||||
|
||||
type Audit = {
|
||||
id: string;
|
||||
framework: {
|
||||
name: string;
|
||||
};
|
||||
validFrom: string;
|
||||
validUntil: string | null;
|
||||
state: string;
|
||||
createdAt: string;
|
||||
report: {
|
||||
id: string;
|
||||
filename: string;
|
||||
downloadUrl: string | null;
|
||||
} | null;
|
||||
reportUrl: string | null;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
audits: Audit[];
|
||||
organizationName: string;
|
||||
isAuthenticated: boolean;
|
||||
};
|
||||
|
||||
export function PublicTrustCenterAudits({ audits, organizationName, isAuthenticated }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
|
||||
if (audits.length === 0) {
|
||||
return (
|
||||
<Card padded>
|
||||
<div className="text-center py-8">
|
||||
<h2 className="text-xl font-semibold text-txt-primary mb-2">
|
||||
{__("Compliance")}
|
||||
</h2>
|
||||
<p className="text-txt-secondary">
|
||||
{__("No compliance reports are currently available.")}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card padded className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-txt-primary">
|
||||
{__("Compliance")}
|
||||
</h2>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{sprintf(__("%s is compliant with the following frameworks"), organizationName)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Framework")}</Th>
|
||||
<Th>{__("Report")}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{audits.map((audit) => {
|
||||
const hasReport = audit.report || audit.reportUrl;
|
||||
const downloadUrl = audit.report?.downloadUrl || audit.reportUrl;
|
||||
const reportName = audit.report?.filename || __("Compliance Report");
|
||||
|
||||
return (
|
||||
<Tr key={audit.id}>
|
||||
<Td>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="w-8 h-8 [&>img]:w-8 [&>img]:h-8 [&>div]:w-8 [&>div]:h-8">
|
||||
<FrameworkLogo name={audit.framework.name} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="font-medium">
|
||||
{audit.framework.name}
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{!hasReport ? (
|
||||
<span className="text-txt-tertiary text-sm">
|
||||
{__("No report")}
|
||||
</span>
|
||||
) : !isAuthenticated ? (
|
||||
<span className="text-txt-tertiary text-sm">
|
||||
{__("Not available")}
|
||||
</span>
|
||||
) : downloadUrl ? (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={IconArrowDown}
|
||||
onClick={() => {
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = reportName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
) : (
|
||||
<span className="text-txt-tertiary text-sm">
|
||||
{__("Not available")}
|
||||
</span>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
Card,
|
||||
Tr,
|
||||
Td,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Th,
|
||||
DocumentTypeBadge,
|
||||
Button,
|
||||
IconArrowDown,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutation } from "react-relay";
|
||||
import type { PublicTrustCenterDocumentsExportPDFMutation } from "./__generated__/PublicTrustCenterDocumentsExportPDFMutation.graphql";
|
||||
|
||||
const exportDocumentVersionPDFMutation = graphql`
|
||||
mutation PublicTrustCenterDocumentsExportPDFMutation(
|
||||
$input: ExportDocumentVersionPDFInput!
|
||||
) {
|
||||
exportDocumentVersionPDF(input: $input) {
|
||||
data
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Document = {
|
||||
id: string;
|
||||
title: string;
|
||||
documentType: string;
|
||||
versions: {
|
||||
edges: Array<{
|
||||
node: {
|
||||
id: string;
|
||||
status: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
|
||||
type Props = {
|
||||
documents: Document[];
|
||||
isAuthenticated: boolean;
|
||||
};
|
||||
|
||||
export function PublicTrustCenterDocuments({ documents, isAuthenticated }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const [exportDocumentVersionPDF] = useMutation<PublicTrustCenterDocumentsExportPDFMutation>(exportDocumentVersionPDFMutation);
|
||||
|
||||
const handleDownload = (document: Document) => {
|
||||
const latestVersion = document.versions.edges[0]?.node;
|
||||
if (!latestVersion) return;
|
||||
|
||||
exportDocumentVersionPDF({
|
||||
variables: {
|
||||
input: { documentVersionId: latestVersion.id },
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
if (data.exportDocumentVersionPDF?.data) {
|
||||
const link = window.document.createElement("a");
|
||||
link.href = data.exportDocumentVersionPDF.data;
|
||||
link.download = `${document.title}.pdf`;
|
||||
window.document.body.appendChild(link);
|
||||
link.click();
|
||||
window.document.body.removeChild(link);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (documents.length === 0) {
|
||||
return (
|
||||
<Card padded>
|
||||
<div className="text-center py-8">
|
||||
<h2 className="text-xl font-semibold text-txt-primary mb-2">
|
||||
{__("Documents")}
|
||||
</h2>
|
||||
<p className="text-txt-secondary">
|
||||
{__("No documents are currently available.")}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card padded className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-txt-primary">
|
||||
{__("Documents")}
|
||||
</h2>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{__("Security and compliance documentation")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th className="w-1/2">{__("Document")}</Th>
|
||||
<Th className="w-1/4">{__("Type")}</Th>
|
||||
<Th className="w-1/4">{__("Download")}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{documents.map((document) => {
|
||||
const latestVersion = document.versions.edges[0]?.node;
|
||||
|
||||
return (
|
||||
<Tr key={document.id}>
|
||||
<Td>
|
||||
<div className="font-medium">
|
||||
{document.title}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<DocumentTypeBadge type={document.documentType} />
|
||||
</Td>
|
||||
<Td>
|
||||
{!latestVersion ? (
|
||||
<span className="text-txt-tertiary text-sm">
|
||||
{__("No version available")}
|
||||
</span>
|
||||
) : !isAuthenticated ? (
|
||||
<span className="text-txt-tertiary text-sm">
|
||||
{__("Not available")}
|
||||
</span>
|
||||
) : (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={IconArrowDown}
|
||||
onClick={() => handleDownload(document)}
|
||||
>
|
||||
{__("Download")}
|
||||
</Button>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
Card,
|
||||
Tr,
|
||||
Td,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Th,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { faviconUrl, sprintf } from "@probo/helpers";
|
||||
|
||||
type Vendor = {
|
||||
id: string;
|
||||
name: string;
|
||||
category: string;
|
||||
description: string | null;
|
||||
createdAt: string;
|
||||
privacyPolicyUrl?: string | null;
|
||||
websiteUrl?: string | null;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
vendors: Vendor[];
|
||||
organizationName: string;
|
||||
};
|
||||
|
||||
export function PublicTrustCenterVendors({ vendors, organizationName }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
|
||||
if (vendors.length === 0) {
|
||||
return (
|
||||
<Card padded>
|
||||
<div className="text-center py-8">
|
||||
<h2 className="text-xl font-semibold text-txt-primary mb-2">
|
||||
{__("Vendors")}
|
||||
</h2>
|
||||
<p className="text-txt-secondary">
|
||||
{__("No vendor information is currently available.")}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card padded className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-txt-primary">
|
||||
{__("Vendors")}
|
||||
</h2>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{sprintf(__("Third-party vendors and service providers %s work with"), organizationName)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Company")}</Th>
|
||||
<Th>{__("Website")}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{vendors.map((vendor) => {
|
||||
const url = vendor.privacyPolicyUrl || vendor.websiteUrl;
|
||||
const logo = faviconUrl(vendor.websiteUrl);
|
||||
|
||||
const getCleanUrl = (url: string) => {
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
return parsedUrl.hostname + parsedUrl.pathname + parsedUrl.search;
|
||||
} catch {
|
||||
return url.replace(/^https?:\/\//, '');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tr key={vendor.id}>
|
||||
<Td>
|
||||
<div className="flex items-center space-x-3">
|
||||
{logo && (
|
||||
<img
|
||||
src={logo}
|
||||
alt={`${vendor.name} logo`}
|
||||
className="w-8 h-8 object-contain rounded-full"
|
||||
/>
|
||||
)}
|
||||
<div className="font-medium">
|
||||
{vendor.name}
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{url ? (
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:text-blue-800 underline"
|
||||
>
|
||||
{getCleanUrl(url)}
|
||||
</a>
|
||||
) : (
|
||||
<span className="text-txt-secondary text-sm">
|
||||
{__("No website available")}
|
||||
</span>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @generated SignedSource<<24fd0dcf15c98ad3d20762e4ccc83a1b>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type ExportDocumentVersionPDFInput = {
|
||||
documentVersionId: string;
|
||||
};
|
||||
export type PublicTrustCenterDocumentsExportPDFMutation$variables = {
|
||||
input: ExportDocumentVersionPDFInput;
|
||||
};
|
||||
export type PublicTrustCenterDocumentsExportPDFMutation$data = {
|
||||
readonly exportDocumentVersionPDF: {
|
||||
readonly data: string;
|
||||
};
|
||||
};
|
||||
export type PublicTrustCenterDocumentsExportPDFMutation = {
|
||||
response: PublicTrustCenterDocumentsExportPDFMutation$data;
|
||||
variables: PublicTrustCenterDocumentsExportPDFMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "ExportDocumentVersionPDFPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "exportDocumentVersionPDF",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "data",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PublicTrustCenterDocumentsExportPDFMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PublicTrustCenterDocumentsExportPDFMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "b5d33d4c301cfa811bb74d52f61f52e8",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PublicTrustCenterDocumentsExportPDFMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PublicTrustCenterDocumentsExportPDFMutation(\n $input: ExportDocumentVersionPDFInput!\n) {\n exportDocumentVersionPDF(input: $input) {\n data\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "b6a173895aea2450ad4ac1a4ec6aeb4e";
|
||||
|
||||
export default node;
|
||||
64
apps/console/src/hooks/graph/PublicTrustCenterGraph.ts
Normal file
64
apps/console/src/hooks/graph/PublicTrustCenterGraph.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
// Manual query definition for trust API (not processed by relay compiler)
|
||||
export const publicTrustCenterQuery = {
|
||||
params: {
|
||||
name: "PublicTrustCenterGraphQuery",
|
||||
operationKind: "query",
|
||||
text: `
|
||||
query PublicTrustCenterGraphQuery($slug: String!) {
|
||||
trustCenterBySlug(slug: $slug) {
|
||||
id
|
||||
active
|
||||
slug
|
||||
organization {
|
||||
id
|
||||
name
|
||||
logoUrl
|
||||
}
|
||||
documents(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
documentType
|
||||
versions(first: 1) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
audits(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
framework {
|
||||
name
|
||||
}
|
||||
report {
|
||||
id
|
||||
filename
|
||||
downloadUrl
|
||||
}
|
||||
reportUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
vendors(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
category
|
||||
websiteUrl
|
||||
privacyPolicyUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
};
|
||||
95
apps/console/src/hooks/graph/TrustCenterAccessGraph.ts
Normal file
95
apps/console/src/hooks/graph/TrustCenterAccessGraph.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { graphql } from 'react-relay';
|
||||
import { useLazyLoadQuery } from 'react-relay';
|
||||
|
||||
export const trustCenterAccessesQuery = graphql`
|
||||
query TrustCenterAccessGraphQuery($trustCenterId: ID!) {
|
||||
node(id: $trustCenterId) {
|
||||
... on TrustCenter {
|
||||
id
|
||||
accesses(first: 100, orderBy: { field: CREATED_AT, direction: DESC })
|
||||
@connection(key: "TrustCenterAccessTab_accesses") {
|
||||
__id
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
email
|
||||
name
|
||||
active
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const revokeTrustCenterAccessMutation = graphql`
|
||||
mutation TrustCenterAccessGraphRevokeMutation($input: RevokeTrustCenterAccessInput!) {
|
||||
revokeTrustCenterAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
email
|
||||
name
|
||||
active
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const createTrustCenterAccessMutation = graphql`
|
||||
mutation TrustCenterAccessGraphCreateMutation(
|
||||
$input: CreateTrustCenterAccessInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createTrustCenterAccess(input: $input) {
|
||||
trustCenterAccessEdge @prependEdge(connections: $connections) {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
email
|
||||
name
|
||||
active
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const updateTrustCenterAccessMutation = graphql`
|
||||
mutation TrustCenterAccessGraphUpdateMutation($input: UpdateTrustCenterAccessInput!) {
|
||||
updateTrustCenterAccess(input: $input) {
|
||||
trustCenterAccess {
|
||||
id
|
||||
email
|
||||
name
|
||||
active
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const deleteTrustCenterAccessMutation = graphql`
|
||||
mutation TrustCenterAccessGraphDeleteMutation(
|
||||
$input: DeleteTrustCenterAccessInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
deleteTrustCenterAccess(input: $input) {
|
||||
deletedTrustCenterAccessId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function useTrustCenterAccesses(trustCenterId: string) {
|
||||
return useLazyLoadQuery(trustCenterAccessesQuery, { trustCenterId });
|
||||
}
|
||||
17
apps/console/src/hooks/graph/TrustCenterAccessTokenGraph.ts
Normal file
17
apps/console/src/hooks/graph/TrustCenterAccessTokenGraph.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { graphql } from 'react-relay';
|
||||
|
||||
export const trustCenterByIdQuery = graphql`
|
||||
query TrustCenterAccessTokenGraphQuery($trustCenterId: ID!) {
|
||||
node(id: $trustCenterId) {
|
||||
... on TrustCenter {
|
||||
id
|
||||
slug
|
||||
active
|
||||
organization {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
201
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphCreateMutation.graphql.ts
generated
Normal file
201
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphCreateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* @generated SignedSource<<79b53f51663ada6e9899523400d54996>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type CreateTrustCenterAccessInput = {
|
||||
email: string;
|
||||
name: string;
|
||||
sendEmail?: boolean;
|
||||
trustCenterId: string;
|
||||
};
|
||||
export type TrustCenterAccessGraphCreateMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateTrustCenterAccessInput;
|
||||
};
|
||||
export type TrustCenterAccessGraphCreateMutation$data = {
|
||||
readonly createTrustCenterAccess: {
|
||||
readonly trustCenterAccessEdge: {
|
||||
readonly cursor: any;
|
||||
readonly node: {
|
||||
readonly active: boolean;
|
||||
readonly createdAt: any;
|
||||
readonly email: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type TrustCenterAccessGraphCreateMutation = {
|
||||
response: TrustCenterAccessGraphCreateMutation$data;
|
||||
variables: TrustCenterAccessGraphCreateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccessEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccessEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "active",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterAccessGraphCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateTrustCenterAccessPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createTrustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterAccessGraphCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateTrustCenterAccessPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createTrustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "trustCenterAccessEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "eafddcd0263963235d3249c22eb50593",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TrustCenterAccessGraphCreateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TrustCenterAccessGraphCreateMutation(\n $input: CreateTrustCenterAccessInput!\n) {\n createTrustCenterAccess(input: $input) {\n trustCenterAccessEdge {\n cursor\n node {\n id\n email\n name\n active\n createdAt\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "99676fee0b2de06a92cdad66c577eee7";
|
||||
|
||||
export default node;
|
||||
132
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphDeleteMutation.graphql.ts
generated
Normal file
132
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphDeleteMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @generated SignedSource<<509cd7c9db3cbf15931c386b13a13987>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeleteTrustCenterAccessInput = {
|
||||
accessId: string;
|
||||
};
|
||||
export type TrustCenterAccessGraphDeleteMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: DeleteTrustCenterAccessInput;
|
||||
};
|
||||
export type TrustCenterAccessGraphDeleteMutation$data = {
|
||||
readonly deleteTrustCenterAccess: {
|
||||
readonly deletedTrustCenterAccessId: string;
|
||||
};
|
||||
};
|
||||
export type TrustCenterAccessGraphDeleteMutation = {
|
||||
response: TrustCenterAccessGraphDeleteMutation$data;
|
||||
variables: TrustCenterAccessGraphDeleteMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deletedTrustCenterAccessId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterAccessGraphDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteTrustCenterAccessPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteTrustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterAccessGraphDeleteMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteTrustCenterAccessPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteTrustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "deletedTrustCenterAccessId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "3f55c9ce5cac2874b769ec994977367a",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TrustCenterAccessGraphDeleteMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TrustCenterAccessGraphDeleteMutation(\n $input: DeleteTrustCenterAccessInput!\n) {\n deleteTrustCenterAccess(input: $input) {\n deletedTrustCenterAccessId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "0d0a44eb6db912eeb533d24718c49b35";
|
||||
|
||||
export default node;
|
||||
317
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphQuery.graphql.ts
generated
Normal file
317
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* @generated SignedSource<<2333a6a7d5415f1a08a5612dcaceee8f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type TrustCenterAccessGraphQuery$variables = {
|
||||
trustCenterId: string;
|
||||
};
|
||||
export type TrustCenterAccessGraphQuery$data = {
|
||||
readonly node: {
|
||||
readonly accesses?: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly cursor: any;
|
||||
readonly node: {
|
||||
readonly active: boolean;
|
||||
readonly createdAt: any;
|
||||
readonly email: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
}>;
|
||||
readonly pageInfo: {
|
||||
readonly endCursor: any | null | undefined;
|
||||
readonly hasNextPage: boolean;
|
||||
readonly hasPreviousPage: boolean;
|
||||
readonly startCursor: any | null | undefined;
|
||||
};
|
||||
};
|
||||
readonly id?: string;
|
||||
};
|
||||
};
|
||||
export type TrustCenterAccessGraphQuery = {
|
||||
response: TrustCenterAccessGraphQuery$data;
|
||||
variables: TrustCenterAccessGraphQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "trustCenterId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "trustCenterId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"kind": "Literal",
|
||||
"name": "orderBy",
|
||||
"value": {
|
||||
"direction": "DESC",
|
||||
"field": "CREATED_AT"
|
||||
}
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccessEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "active",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
v6 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
},
|
||||
(v3/*: any*/)
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterAccessGraphQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": "accesses",
|
||||
"args": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"concreteType": "TrustCenterAccessConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__TrustCenterAccessTab_accesses_connection",
|
||||
"plural": false,
|
||||
"selections": (v5/*: any*/),
|
||||
"storageKey": "__TrustCenterAccessTab_accesses_connection(orderBy:{\"direction\":\"DESC\",\"field\":\"CREATED_AT\"})"
|
||||
}
|
||||
],
|
||||
"type": "TrustCenter",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterAccessGraphQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v4/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": "TrustCenterAccessConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "accesses",
|
||||
"plural": false,
|
||||
"selections": (v5/*: any*/),
|
||||
"storageKey": "accesses(first:100,orderBy:{\"direction\":\"DESC\",\"field\":\"CREATED_AT\"})"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v6/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "TrustCenterAccessTab_accesses",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "accesses"
|
||||
}
|
||||
],
|
||||
"type": "TrustCenter",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "5b83cd4ae2434ce2e00de7230d264432",
|
||||
"id": null,
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "forward",
|
||||
"path": [
|
||||
"node",
|
||||
"accesses"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "TrustCenterAccessGraphQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query TrustCenterAccessGraphQuery(\n $trustCenterId: ID!\n) {\n node(id: $trustCenterId) {\n __typename\n ... on TrustCenter {\n id\n accesses(first: 100, orderBy: {field: CREATED_AT, direction: DESC}) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n edges {\n cursor\n node {\n id\n email\n name\n active\n createdAt\n __typename\n }\n }\n }\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "af598fd2af198e63ed84fd618857a985";
|
||||
|
||||
export default node;
|
||||
137
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphRevokeMutation.graphql.ts
generated
Normal file
137
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphRevokeMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @generated SignedSource<<a234c36ae13e75cd1b0d893c4e232fc4>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RevokeTrustCenterAccessInput = {
|
||||
accessId: string;
|
||||
};
|
||||
export type TrustCenterAccessGraphRevokeMutation$variables = {
|
||||
input: RevokeTrustCenterAccessInput;
|
||||
};
|
||||
export type TrustCenterAccessGraphRevokeMutation$data = {
|
||||
readonly revokeTrustCenterAccess: {
|
||||
readonly trustCenterAccess: {
|
||||
readonly active: boolean;
|
||||
readonly createdAt: any;
|
||||
readonly email: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type TrustCenterAccessGraphRevokeMutation = {
|
||||
response: TrustCenterAccessGraphRevokeMutation$data;
|
||||
variables: TrustCenterAccessGraphRevokeMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RevokeTrustCenterAccessPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "revokeTrustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "active",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterAccessGraphRevokeMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterAccessGraphRevokeMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "b26927a2b8d02eb2e3754850b0a44d8b",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TrustCenterAccessGraphRevokeMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TrustCenterAccessGraphRevokeMutation(\n $input: RevokeTrustCenterAccessInput!\n) {\n revokeTrustCenterAccess(input: $input) {\n trustCenterAccess {\n id\n email\n name\n active\n createdAt\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "269cce2fe60fac04d807f1004ef0777f";
|
||||
|
||||
export default node;
|
||||
141
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphUpdateMutation.graphql.ts
generated
Normal file
141
apps/console/src/hooks/graph/__generated__/TrustCenterAccessGraphUpdateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @generated SignedSource<<54ac79c4d292eb19f73bbf2363015dd9>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type UpdateTrustCenterAccessInput = {
|
||||
accessId: string;
|
||||
active?: boolean | null | undefined;
|
||||
email?: string | null | undefined;
|
||||
name?: string | null | undefined;
|
||||
sendEmail?: boolean;
|
||||
};
|
||||
export type TrustCenterAccessGraphUpdateMutation$variables = {
|
||||
input: UpdateTrustCenterAccessInput;
|
||||
};
|
||||
export type TrustCenterAccessGraphUpdateMutation$data = {
|
||||
readonly updateTrustCenterAccess: {
|
||||
readonly trustCenterAccess: {
|
||||
readonly active: boolean;
|
||||
readonly createdAt: any;
|
||||
readonly email: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type TrustCenterAccessGraphUpdateMutation = {
|
||||
response: TrustCenterAccessGraphUpdateMutation$data;
|
||||
variables: TrustCenterAccessGraphUpdateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "UpdateTrustCenterAccessPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updateTrustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenterAccess",
|
||||
"kind": "LinkedField",
|
||||
"name": "trustCenterAccess",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "active",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterAccessGraphUpdateMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterAccessGraphUpdateMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "8e284c8129de02d689385cae38cc3876",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TrustCenterAccessGraphUpdateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation TrustCenterAccessGraphUpdateMutation(\n $input: UpdateTrustCenterAccessInput!\n) {\n updateTrustCenterAccess(input: $input) {\n trustCenterAccess {\n id\n email\n name\n active\n createdAt\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "235754d40be56785bd0e7a36ac8c4ec2";
|
||||
|
||||
export default node;
|
||||
169
apps/console/src/hooks/graph/__generated__/TrustCenterAccessTokenGraphQuery.graphql.ts
generated
Normal file
169
apps/console/src/hooks/graph/__generated__/TrustCenterAccessTokenGraphQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @generated SignedSource<<6e43a07222fde7acc3d588bac24a8077>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type TrustCenterAccessTokenGraphQuery$variables = {
|
||||
trustCenterId: string;
|
||||
};
|
||||
export type TrustCenterAccessTokenGraphQuery$data = {
|
||||
readonly node: {
|
||||
readonly active?: boolean;
|
||||
readonly id?: string;
|
||||
readonly organization?: {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
readonly slug?: string;
|
||||
};
|
||||
};
|
||||
export type TrustCenterAccessTokenGraphQuery = {
|
||||
response: TrustCenterAccessTokenGraphQuery$data;
|
||||
variables: TrustCenterAccessTokenGraphQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "trustCenterId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "trustCenterId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "slug",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "active",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Organization",
|
||||
"kind": "LinkedField",
|
||||
"name": "organization",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "TrustCenterAccessTokenGraphQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"type": "TrustCenter",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "TrustCenterAccessTokenGraphQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"type": "TrustCenter",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "7d3d891944391d31a722364f8b1f1cbb",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "TrustCenterAccessTokenGraphQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query TrustCenterAccessTokenGraphQuery(\n $trustCenterId: ID!\n) {\n node(id: $trustCenterId) {\n __typename\n ... on TrustCenter {\n id\n slug\n active\n organization {\n id\n name\n }\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "e273d4e8c8b756a5aba5271520df8602";
|
||||
|
||||
export default node;
|
||||
87
apps/console/src/layouts/PublicTrustCenterLayout.tsx
Normal file
87
apps/console/src/layouts/PublicTrustCenterLayout.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Outlet } from "react-router";
|
||||
import { Logo, Button, IconArrowBoxLeft } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { buildEndpoint } from "/providers/RelayProviders";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
organizationName: string;
|
||||
organizationLogo?: string | null;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export function PublicTrustCenterLayout({ organizationName, organizationLogo, children }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await fetch(buildEndpoint('/api/trust/v1/trust-center-access/logout'), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
} finally {
|
||||
window.location.href = "/";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-tertiary">
|
||||
<header className="bg-surface border-b border-border-solid">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
<div className="flex items-center space-x-4">
|
||||
{organizationLogo ? (
|
||||
<img
|
||||
src={organizationLogo}
|
||||
alt={organizationName}
|
||||
className="h-8 w-8 rounded"
|
||||
/>
|
||||
) : (
|
||||
<Logo className="h-8 w-8" />
|
||||
)}
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold text-txt-primary">
|
||||
{organizationName}
|
||||
</h1>
|
||||
<p className="text-sm text-txt-secondary">Trust Center</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="text-sm text-txt-tertiary">
|
||||
<a
|
||||
href="https://www.getprobo.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-txt-secondary transition-colors flex items-center space-x-1"
|
||||
>
|
||||
<img
|
||||
src="/favicons/favicon.ico"
|
||||
alt="Probo"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span>Powered by Probo</span>
|
||||
</a>
|
||||
</div>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={IconArrowBoxLeft}
|
||||
onClick={handleLogout}
|
||||
title={__("Logout")}
|
||||
className="text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{children || <Outlet />}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
218
apps/console/src/pages/PublicTrustCenterPage.tsx
Normal file
218
apps/console/src/pages/PublicTrustCenterPage.tsx
Normal file
@@ -0,0 +1,218 @@
|
||||
import { useParams, Navigate } from "react-router";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { publicTrustCenterQuery } from "/hooks/graph/PublicTrustCenterGraph";
|
||||
import { PublicTrustCenterLayout } from "/layouts/PublicTrustCenterLayout";
|
||||
import { PublicTrustCenterAudits } from "../components/trustCenter/PublicTrustCenterAudits";
|
||||
import { PublicTrustCenterVendors } from "../components/trustCenter/PublicTrustCenterVendors";
|
||||
import { PublicTrustCenterDocuments } from "../components/trustCenter/PublicTrustCenterDocuments";
|
||||
import { PageError } from "/components/PageError";
|
||||
import { TrustRelayProvider } from "/providers/TrustRelayProvider";
|
||||
import { useState, useEffect } from "react";
|
||||
import { buildEndpoint } from "/providers/RelayProviders";
|
||||
|
||||
interface GraphQLError {
|
||||
message: string;
|
||||
path?: string[];
|
||||
locations?: Array<{ line: number; column: number }>;
|
||||
}
|
||||
|
||||
interface GraphQLResponse<T> {
|
||||
data?: T;
|
||||
errors?: GraphQLError[];
|
||||
}
|
||||
|
||||
interface Organization {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl?: string;
|
||||
}
|
||||
|
||||
interface Framework {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface DocumentVersion {
|
||||
id: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface DocumentVersionConnection {
|
||||
edges: Array<{
|
||||
node: DocumentVersion;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface Document {
|
||||
id: string;
|
||||
title: string;
|
||||
documentType: string;
|
||||
versions: DocumentVersionConnection;
|
||||
}
|
||||
|
||||
interface Audit {
|
||||
id: string;
|
||||
framework: Framework;
|
||||
validFrom: string;
|
||||
validUntil: string | null;
|
||||
state: string;
|
||||
createdAt: string;
|
||||
report: {
|
||||
id: string;
|
||||
filename: string;
|
||||
downloadUrl: string | null;
|
||||
} | null;
|
||||
reportUrl: string | null;
|
||||
}
|
||||
|
||||
interface Vendor {
|
||||
id: string;
|
||||
name: string;
|
||||
category: string;
|
||||
description: string | null;
|
||||
createdAt: string;
|
||||
websiteUrl?: string | null;
|
||||
privacyPolicyUrl?: string | null;
|
||||
}
|
||||
|
||||
interface Connection<T> {
|
||||
edges: Array<{
|
||||
node: T;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface TrustCenter {
|
||||
id: string;
|
||||
active: boolean;
|
||||
slug: string;
|
||||
organization: Organization;
|
||||
documents: Connection<Document>;
|
||||
audits: Connection<Audit>;
|
||||
vendors: Connection<Vendor>;
|
||||
}
|
||||
|
||||
interface PublicTrustCenterData {
|
||||
trustCenterBySlug?: TrustCenter;
|
||||
}
|
||||
|
||||
function PublicTrustCenterContent() {
|
||||
const { __ } = useTranslate();
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const [data, setData] = useState<PublicTrustCenterData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
const organizationName = data?.trustCenterBySlug?.organization?.name;
|
||||
usePageTitle(organizationName ? `${organizationName} - Trust Center` : "Trust Center");
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
fetch(buildEndpoint("/api/trust/v1/graphql"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
operationName: publicTrustCenterQuery.params.name,
|
||||
query: publicTrustCenterQuery.params.text,
|
||||
variables: { slug },
|
||||
}),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((result: GraphQLResponse<PublicTrustCenterData>) => {
|
||||
if (result.errors && result.errors.some((error: GraphQLError) =>
|
||||
!error.message.includes('access denied: authentication required')
|
||||
)) {
|
||||
throw new Error(result.errors[0].message);
|
||||
}
|
||||
setData(result.data || null);
|
||||
})
|
||||
.catch(setError)
|
||||
.finally(() => setLoading(false));
|
||||
}, [slug]);
|
||||
|
||||
if (!slug) {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <PageError />;
|
||||
}
|
||||
|
||||
const { trustCenterBySlug } = data || {};
|
||||
|
||||
if (!trustCenterBySlug) {
|
||||
return <PageError />;
|
||||
}
|
||||
|
||||
if (!trustCenterBySlug.active) {
|
||||
return <PageError />;
|
||||
}
|
||||
|
||||
const { organization } = trustCenterBySlug;
|
||||
|
||||
const documents = trustCenterBySlug.documents.edges
|
||||
.map(edge => edge.node);
|
||||
|
||||
const audits = trustCenterBySlug.audits.edges
|
||||
.map(edge => edge.node);
|
||||
|
||||
const vendors = trustCenterBySlug.vendors.edges
|
||||
.map(edge => edge.node);
|
||||
|
||||
const isAuthenticated = audits.some((audit: Audit) =>
|
||||
audit.report?.downloadUrl || audit.reportUrl
|
||||
);
|
||||
|
||||
return (
|
||||
<PublicTrustCenterLayout
|
||||
organizationName={organization.name}
|
||||
organizationLogo={organization.logoUrl}
|
||||
>
|
||||
<div className="space-y-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold text-txt-primary mb-4">
|
||||
{sprintf(__("%s Trust Center"), organization.name)}
|
||||
</h1>
|
||||
<p className="text-lg text-txt-secondary max-w-2xl mx-auto">
|
||||
{__("Explore our security practices, compliance certifications, and transparency reports.")}
|
||||
</p>
|
||||
</div>
|
||||
<PublicTrustCenterAudits
|
||||
audits={audits}
|
||||
organizationName={organization.name}
|
||||
isAuthenticated={isAuthenticated}
|
||||
/>
|
||||
<PublicTrustCenterDocuments
|
||||
documents={documents}
|
||||
isAuthenticated={isAuthenticated}
|
||||
/>
|
||||
<PublicTrustCenterVendors
|
||||
vendors={vendors}
|
||||
organizationName={organization.name}
|
||||
/>
|
||||
</div>
|
||||
</PublicTrustCenterLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PublicTrustCenterPage() {
|
||||
return (
|
||||
<TrustRelayProvider>
|
||||
<PublicTrustCenterContent />
|
||||
</TrustRelayProvider>
|
||||
);
|
||||
}
|
||||
130
apps/console/src/pages/TrustCenterAccessPage.tsx
Normal file
130
apps/console/src/pages/TrustCenterAccessPage.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useParams, useNavigate, useSearchParams } from "react-router";
|
||||
import { useState, useEffect } from "react";
|
||||
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
|
||||
import { PageError } from "/components/PageError";
|
||||
import { buildEndpoint } from "/providers/RelayProviders";
|
||||
import { IconClock, IconWarning } from "@probo/ui";
|
||||
|
||||
function TokenErrorPage({ error }: { error: string }) {
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const isExpiredToken = error.toLowerCase().includes('expired');
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-level-0 flex items-center justify-center p-4">
|
||||
<div className="max-w-md w-full text-center space-y-6">
|
||||
<div className="space-y-4">
|
||||
{isExpiredToken ? (
|
||||
<div className="space-y-3">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-amber-100 rounded-full">
|
||||
<IconClock size={32} className="text-amber-600" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-semibold text-txt-primary">
|
||||
{__("Access Link Expired")}
|
||||
</h1>
|
||||
<p className="text-txt-secondary">
|
||||
{__("This access link has expired. Trust center access links are valid for 7 days for security reasons.")}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-red-100 rounded-full">
|
||||
<IconWarning size={32} className="text-red-600" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-semibold text-txt-primary">
|
||||
{__("Invalid Access Link")}
|
||||
</h1>
|
||||
<p className="text-txt-secondary">
|
||||
{__("This access link is not valid. It may have been revoked or the link might be incorrect.")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-level-1 border border-border-low rounded-lg p-4 space-y-3">
|
||||
<h3 className="font-medium text-txt-primary">{__("What can you do?")}</h3>
|
||||
<ul className="text-sm text-txt-secondary space-y-2 text-left">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-primary-600 mt-1">•</span>
|
||||
<span>{__("Contact the person who sent you this link to request a new access invitation")}</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-primary-600 mt-1">•</span>
|
||||
<span>{__("Check if you received a newer email with an updated access link")}</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-primary-600 mt-1">•</span>
|
||||
<span>{__("Verify that you copied the entire link correctly from the email")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TrustCenterAccessPage() {
|
||||
const { __ } = useTranslate();
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const [searchParams] = useSearchParams();
|
||||
const token = searchParams.get('token');
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slug) {
|
||||
setError(__("Invalid trust center"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
setError(__("Invalid or missing access token"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(buildEndpoint('/api/trust/v1/trust-center-access/authenticate'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ token }),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
navigate(`/trust/${slug}`);
|
||||
} else {
|
||||
setError(data.message || __("Authentication failed"));
|
||||
setLoading(false);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setError(__("Authentication failed"));
|
||||
setLoading(false);
|
||||
});
|
||||
}, [slug, token, __, navigate]);
|
||||
|
||||
if (loading) {
|
||||
return <PageSkeleton />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
const isTokenError = error.toLowerCase().includes('token') ||
|
||||
error.toLowerCase().includes('expired') ||
|
||||
error.toLowerCase().includes('invalid');
|
||||
|
||||
if (isTokenError) {
|
||||
return <TokenErrorPage error={error} />;
|
||||
}
|
||||
|
||||
return <PageError error={error} />;
|
||||
}
|
||||
|
||||
return <div>{__("Redirecting to trust center...")}</div>;
|
||||
}
|
||||
@@ -212,8 +212,7 @@ export default function TrustCenterPage({ queryRef }: Props) {
|
||||
</Card>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-base font-medium">{__("Content")}</h2>
|
||||
<Tabs>
|
||||
<Tabs>
|
||||
<TabItem
|
||||
asChild
|
||||
active={
|
||||
@@ -231,6 +230,9 @@ export default function TrustCenterPage({ queryRef }: Props) {
|
||||
<TabLink to={`/organizations/${organizationId}/trust-center/documents`}>
|
||||
{__("Documents")}
|
||||
</TabLink>
|
||||
<TabLink to={`/organizations/${organizationId}/trust-center/access`}>
|
||||
{__("Access")}
|
||||
</TabLink>
|
||||
</Tabs>
|
||||
|
||||
<Outlet context={{ organization }} />
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
import { Badge, Button, Card, Dialog, DialogContent, DialogFooter, Field, Input, Spinner, Table, Tbody, Td, Th, Thead, Tr, useDialogRef, useToast, IconCheckmark1, IconCrossLargeX, IconTrashCan } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useOutletContext } from "react-router";
|
||||
import { useState, useCallback } from "react";
|
||||
import {
|
||||
useTrustCenterAccesses,
|
||||
createTrustCenterAccessMutation,
|
||||
updateTrustCenterAccessMutation,
|
||||
deleteTrustCenterAccessMutation
|
||||
} from "/hooks/graph/TrustCenterAccessGraph";
|
||||
import { useMutation } from "react-relay";
|
||||
import type { TrustCenterAccessGraphQuery$data } from "/hooks/graph/__generated__/TrustCenterAccessGraphQuery.graphql";
|
||||
|
||||
type ContextType = {
|
||||
organization: {
|
||||
id: string;
|
||||
trustCenter?: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export default function TrustCenterAccessTab() {
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const { organization } = useOutletContext<ContextType>();
|
||||
|
||||
const [createInvitation, isCreating] = useMutation(createTrustCenterAccessMutation);
|
||||
const [updateInvitation, isUpdating] = useMutation(updateTrustCenterAccessMutation);
|
||||
const [deleteInvitation, isDeleting] = useMutation(deleteTrustCenterAccessMutation);
|
||||
|
||||
const dialogRef = useDialogRef();
|
||||
const [email, setEmail] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
|
||||
type AccessType = {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
createdAt: Date;
|
||||
};
|
||||
|
||||
const data = useTrustCenterAccesses(organization.trustCenter?.id || "");
|
||||
|
||||
if (!organization.trustCenter?.id) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-base font-medium">{__("External Access")}</h3>
|
||||
<p className="text-sm text-txt-tertiary">
|
||||
{__("Manage who can access your trust center with time-limited tokens")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Card padded>
|
||||
<div className="text-center text-txt-tertiary py-8">
|
||||
<Spinner />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const trustCenterData = data as TrustCenterAccessGraphQuery$data | null;
|
||||
const accesses: AccessType[] = trustCenterData?.node?.accesses?.edges ?
|
||||
trustCenterData.node.accesses.edges.map(edge => ({
|
||||
id: edge.node.id,
|
||||
email: edge.node.email,
|
||||
name: edge.node.name,
|
||||
active: edge.node.active,
|
||||
createdAt: new Date(edge.node.createdAt)
|
||||
})) : [];
|
||||
|
||||
const handleInvite = useCallback(async () => {
|
||||
if (!organization.trustCenter?.id) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Trust center not found"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email.trim() || !name.trim()) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Email and name are required"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const connectionId = trustCenterData?.node?.accesses?.__id;
|
||||
|
||||
try {
|
||||
createInvitation({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: organization.trustCenter.id,
|
||||
email: email.trim(),
|
||||
name: name.trim(),
|
||||
sendEmail: true,
|
||||
},
|
||||
connections: connectionId ? [connectionId] : [],
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors && errors.length > 0) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: errors[0]?.message || __("Failed to send invitation"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (dialogRef.current) {
|
||||
dialogRef.current.close();
|
||||
}
|
||||
setEmail("");
|
||||
setName("");
|
||||
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access invitation sent successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message || __("Failed to send invitation. Please try again."),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("An unexpected error occurred. Please try again."),
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
}, [organization.trustCenter?.id, email, name, trustCenterData, createInvitation, toast, __, dialogRef]);
|
||||
|
||||
const handleRevoke = useCallback(async (accessId: string) => {
|
||||
updateInvitation({
|
||||
variables: {
|
||||
input: {
|
||||
accessId,
|
||||
active: false,
|
||||
sendEmail: false,
|
||||
},
|
||||
},
|
||||
onCompleted: () => {
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access revoked successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message,
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
}, [updateInvitation, toast, __]);
|
||||
|
||||
const handleReinvite = useCallback(async (access: AccessType) => {
|
||||
if (!organization.trustCenter?.id) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Trust center not found"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateInvitation({
|
||||
variables: {
|
||||
input: {
|
||||
accessId: access.id,
|
||||
active: true,
|
||||
sendEmail: true,
|
||||
},
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors && errors.length > 0) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: errors[0]?.message || __("Failed to send reinvitation"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Reinvitation sent successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message || __("Failed to send reinvitation. Please try again."),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("An unexpected error occurred. Please try again."),
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
}, [organization.trustCenter?.id, updateInvitation, toast, __]);
|
||||
|
||||
const handleDelete = useCallback(async (accessId: string) => {
|
||||
const connectionId = trustCenterData?.node?.accesses?.__id;
|
||||
|
||||
deleteInvitation({
|
||||
variables: {
|
||||
input: {
|
||||
accessId,
|
||||
},
|
||||
connections: connectionId ? [connectionId] : [],
|
||||
},
|
||||
onCompleted: () => {
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("Access deleted successfully"),
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: error.message,
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
}, [deleteInvitation, toast, __, trustCenterData]);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-base font-medium">{__("External Access")}</h3>
|
||||
<p className="text-sm text-txt-tertiary">
|
||||
{__("Manage who can access your trust center with time-limited tokens")}
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={() => dialogRef.current?.open()}>
|
||||
{__("Invite")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card padded>
|
||||
{accesses.length === 0 ? (
|
||||
<div className="text-center text-txt-tertiary py-8">
|
||||
{__("No external access granted yet")}
|
||||
</div>
|
||||
) : (
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Name")}</Th>
|
||||
<Th>{__("Email")}</Th>
|
||||
<Th>{__("Status")}</Th>
|
||||
<Th>{__("Date")}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{accesses.map((access) => (
|
||||
<Tr key={access.id}>
|
||||
<Td className="font-medium">{access.name}</Td>
|
||||
<Td>{access.email}</Td>
|
||||
<Td>
|
||||
<Badge variant={access.active ? "success" : "neutral"}>
|
||||
{access.active ? __("Active") : __("Revoked")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
{access.createdAt.toLocaleDateString()}
|
||||
</Td>
|
||||
<Td noLink width={120} className="text-end">
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => access.active ? handleRevoke(access.id) : handleReinvite(access)}
|
||||
disabled={isUpdating}
|
||||
icon={access.active ? IconCrossLargeX : IconCheckmark1}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => handleDelete(access.id)}
|
||||
disabled={isDeleting}
|
||||
icon={IconTrashCan}
|
||||
/>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
title={__("Invite External Access")}
|
||||
>
|
||||
<DialogContent padded className="space-y-4">
|
||||
<p className="text-txt-secondary text-sm">
|
||||
{__("Send a 7-day access token to an external person to view your trust center")}
|
||||
</p>
|
||||
|
||||
<Field label={__("Full Name")} required>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder={__("John Doe")}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label={__("Email Address")} required>
|
||||
<Input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder={__("john@example.com")}
|
||||
/>
|
||||
</Field>
|
||||
</DialogContent>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => dialogRef.current?.close()}
|
||||
disabled={isCreating}
|
||||
>
|
||||
{__("Cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleInvite} disabled={isCreating}>
|
||||
{isCreating && <Spinner />}
|
||||
{__("Send Invitation")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
80
apps/console/src/providers/TrustRelayProvider.tsx
Normal file
80
apps/console/src/providers/TrustRelayProvider.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
Environment,
|
||||
type FetchFunction,
|
||||
Network,
|
||||
RecordSource,
|
||||
Store,
|
||||
} from "relay-runtime";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { RelayEnvironmentProvider } from "react-relay";
|
||||
import { buildEndpoint } from "./RelayProviders";
|
||||
|
||||
export class TrustCenterError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "TrustCenterError";
|
||||
}
|
||||
}
|
||||
|
||||
const fetchTrustRelay: FetchFunction = async (request, variables) => {
|
||||
const requestInit: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept:
|
||||
"application/graphql-response+json; charset=utf-8, application/json; charset=utf-8",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "include", // Include cookies for authentication
|
||||
body: JSON.stringify({
|
||||
operationName: request.name,
|
||||
query: request.text,
|
||||
variables,
|
||||
}),
|
||||
};
|
||||
|
||||
const response = await fetch(
|
||||
buildEndpoint("/api/trust/v1/graphql"),
|
||||
requestInit
|
||||
);
|
||||
|
||||
if (response.status === 500) {
|
||||
throw new TrustCenterError("Internal server error");
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
|
||||
if (json.errors) {
|
||||
throw new TrustCenterError(
|
||||
`Error fetching GraphQL query '${
|
||||
request.name
|
||||
}' with variables '${JSON.stringify(variables)}': ${JSON.stringify(
|
||||
json.errors
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
const trustSource = new RecordSource();
|
||||
const trustStore = new Store(trustSource, {
|
||||
queryCacheExpirationTime: 5 * 60 * 1000, // 5 minutes for trust center content
|
||||
gcReleaseBufferSize: 10,
|
||||
});
|
||||
|
||||
export const trustRelayEnvironment = new Environment({
|
||||
network: Network.create(fetchTrustRelay),
|
||||
store: trustStore,
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider for trust center Relay environment (public API)
|
||||
*/
|
||||
export function TrustRelayProvider({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<RelayEnvironmentProvider environment={trustRelayEnvironment}>
|
||||
{children}
|
||||
</RelayEnvironmentProvider>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "react-router";
|
||||
import { MainLayout } from "./layouts/MainLayout";
|
||||
import { AuthLayout, CenteredLayout, CenteredLayoutSkeleton } from "@probo/ui";
|
||||
import { Fragment, Suspense, type FC, type LazyExoticComponent } from "react";
|
||||
import { Fragment, Suspense } from "react";
|
||||
import {
|
||||
relayEnvironment,
|
||||
UnAuthenticatedError,
|
||||
@@ -31,6 +31,13 @@ import { auditRoutes } from "./routes/auditRoutes.ts";
|
||||
import { trustCenterRoutes } from "./routes/trustCenterRoutes.ts";
|
||||
import { lazy } from "@probo/react-lazy";
|
||||
|
||||
export type AppRoute = Omit<RouteObject, "Component" | "children"> & {
|
||||
Component?: React.ComponentType<any>;
|
||||
children?: AppRoute[];
|
||||
fallback?: React.ComponentType;
|
||||
queryLoader?: (params: any) => PreloadedQuery<any>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Top level error boundary
|
||||
*/
|
||||
@@ -44,13 +51,6 @@ function ErrorBoundary({ error: propsError }: { error?: string }) {
|
||||
return <PageError error={error?.toString()} />;
|
||||
}
|
||||
|
||||
export type AppRoute = {
|
||||
Component: FC<any> | LazyExoticComponent<FC<any>>;
|
||||
children?: AppRoute[];
|
||||
fallback?: FC;
|
||||
queryLoader?: (params: Record<string, string>) => PreloadedQuery<any>;
|
||||
} & Omit<RouteObject, "Component" | "children">;
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/auth",
|
||||
@@ -106,6 +106,18 @@ const routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/trust/:slug",
|
||||
ErrorBoundary: ErrorBoundary,
|
||||
fallback: PageSkeleton,
|
||||
Component: lazy(() => import("./pages/PublicTrustCenterPage")),
|
||||
},
|
||||
{
|
||||
path: "/trust/:slug/access",
|
||||
ErrorBoundary: ErrorBoundary,
|
||||
fallback: PageSkeleton,
|
||||
Component: lazy(() => import("./pages/TrustCenterAccessPage")),
|
||||
},
|
||||
{
|
||||
path: "/organizations/:organizationId",
|
||||
Component: MainLayout,
|
||||
@@ -160,17 +172,19 @@ function routeTransformer({
|
||||
...route
|
||||
}: AppRoute): RouteObject {
|
||||
let result = { ...route };
|
||||
if (FallbackComponent) {
|
||||
if (FallbackComponent && route.Component) {
|
||||
const OriginalComponent = route.Component;
|
||||
result = {
|
||||
...result,
|
||||
Component: (props) => (
|
||||
<Suspense fallback={<FallbackComponent />}>
|
||||
<route.Component {...props} />
|
||||
<OriginalComponent {...props} />
|
||||
</Suspense>
|
||||
),
|
||||
};
|
||||
}
|
||||
if (queryLoader) {
|
||||
if (queryLoader && route.Component) {
|
||||
const OriginalComponent = route.Component;
|
||||
result = {
|
||||
...result,
|
||||
loader: ({ params }) => {
|
||||
@@ -187,7 +201,7 @@ function routeTransformer({
|
||||
|
||||
return (
|
||||
<Suspense fallback={FallbackComponent ? <FallbackComponent /> : null}>
|
||||
<route.Component queryRef={queryRef} />
|
||||
<OriginalComponent queryRef={queryRef} />
|
||||
</Suspense>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -44,6 +44,13 @@ export const trustCenterRoutes = [
|
||||
() => import("/pages/organizations/trustCenter/TrustCenterDocumentsTab")
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "access",
|
||||
fallback: LinkCardSkeleton,
|
||||
Component: lazy(
|
||||
() => import("/pages/organizations/trustCenter/TrustCenterAccessTab")
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
|
||||
Reference in New Issue
Block a user