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;
|
||||
Reference in New Issue
Block a user