Add nda to trust center
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -127,7 +127,9 @@ export default function TrustCenterAccessPage() {
|
||||
if (error) {
|
||||
const isTokenError = error.toLowerCase().includes('token') ||
|
||||
error.toLowerCase().includes('expired') ||
|
||||
error.toLowerCase().includes('invalid');
|
||||
error.toLowerCase().includes('invalid') ||
|
||||
error.toLowerCase().includes('401') ||
|
||||
error.toLowerCase().includes('unauthorized');
|
||||
|
||||
if (isTokenError) {
|
||||
return <TokenErrorPage error={error} />;
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
useDialogRef,
|
||||
IconTrashCan,
|
||||
IconPencil,
|
||||
IconCheckmark1,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useOutletContext } from "react-router";
|
||||
@@ -85,6 +86,7 @@ export default function TrustCenterAccessTab() {
|
||||
email: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
hasAcceptedNonDisclosureAgreement: boolean;
|
||||
createdAt: Date;
|
||||
};
|
||||
|
||||
@@ -95,6 +97,7 @@ export default function TrustCenterAccessTab() {
|
||||
email: edge.node.email,
|
||||
name: edge.node.name,
|
||||
active: edge.node.active,
|
||||
hasAcceptedNonDisclosureAgreement: edge.node.hasAcceptedNonDisclosureAgreement,
|
||||
createdAt: new Date(edge.node.createdAt)
|
||||
})) ?? [];
|
||||
|
||||
@@ -203,6 +206,7 @@ export default function TrustCenterAccessTab() {
|
||||
<Th>{__("Email")}</Th>
|
||||
<Th>{__("Date")}</Th>
|
||||
<Th>{__("Active")}</Th>
|
||||
<Th>{__("NDA")}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -220,6 +224,11 @@ export default function TrustCenterAccessTab() {
|
||||
onChange={(active) => handleToggleActive(access.id, active)}
|
||||
/>
|
||||
</Td>
|
||||
<Td>
|
||||
{access.hasAcceptedNonDisclosureAgreement && (
|
||||
<IconCheckmark1 size={16} className="text-txt-success" />
|
||||
)}
|
||||
</Td>
|
||||
<Td noLink width={160} className="text-end">
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Dropzone,
|
||||
Field,
|
||||
Input,
|
||||
PageHeader,
|
||||
@@ -12,9 +13,10 @@ import {
|
||||
Tabs,
|
||||
TabLink,
|
||||
TabItem,
|
||||
IconTrashCan,
|
||||
} from "@probo/ui";
|
||||
import { usePreloadedQuery, type PreloadedQuery } from "react-relay";
|
||||
import { trustCenterQuery, useUpdateTrustCenterMutation } from "/hooks/graph/TrustCenterGraph";
|
||||
import { trustCenterQuery, useUpdateTrustCenterMutation, useUploadTrustCenterNDAMutation, useDeleteTrustCenterNDAMutation } from "/hooks/graph/TrustCenterGraph";
|
||||
import type { TrustCenterGraphQuery } from "/hooks/graph/__generated__/TrustCenterGraphQuery.graphql";
|
||||
import { useState } from "react";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
@@ -32,6 +34,8 @@ export default function TrustCenterPage({ queryRef }: Props) {
|
||||
const { organization } = usePreloadedQuery(trustCenterQuery, queryRef);
|
||||
|
||||
const [updateTrustCenter, isUpdating] = useUpdateTrustCenterMutation();
|
||||
const [uploadNDA, isUploadingNDA] = useUploadTrustCenterNDAMutation();
|
||||
const [deleteNDA, isDeletingNDA] = useDeleteTrustCenterNDAMutation();
|
||||
const [isActive, setIsActive] = useState(organization.trustCenter?.active || false);
|
||||
const [slug, setSlug] = useState(organization.trustCenter?.slug || "");
|
||||
const [isUpdatingSlug, setIsUpdatingSlug] = useState(false);
|
||||
@@ -101,6 +105,57 @@ export default function TrustCenterPage({ queryRef }: Props) {
|
||||
});
|
||||
};
|
||||
|
||||
const handleNDAUpload = async (files: File[]) => {
|
||||
if (!organization.trustCenter?.id) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Trust center not found"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.length === 0) return;
|
||||
|
||||
const file = files[0];
|
||||
|
||||
await uploadNDA({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: organization.trustCenter.id,
|
||||
fileName: file.name,
|
||||
file: null,
|
||||
},
|
||||
},
|
||||
uploadables: {
|
||||
"input.file": file,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleNDADelete = async () => {
|
||||
if (!organization.trustCenter?.id) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("Trust center not found"),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(__("Are you sure you want to delete the NDA file?"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteNDA({
|
||||
variables: {
|
||||
input: {
|
||||
trustCenterId: organization.trustCenter.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const trustCenterUrl = organization.trustCenter?.slug
|
||||
? `${window.location.origin}/trust/${organization.trustCenter.slug}`
|
||||
: null;
|
||||
@@ -211,6 +266,68 @@ export default function TrustCenterPage({ queryRef }: Props) {
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-base font-medium">{__("Non-Disclosure Agreement")}</h2>
|
||||
{(isUploadingNDA || isDeletingNDA) && <Spinner />}
|
||||
</div>
|
||||
<Card padded className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
{!organization.trustCenter?.ndaFileName ? (
|
||||
<p className="text-sm text-txt-tertiary">
|
||||
{__("Upload a Non-Disclosure Agreement that visitors must accept before accessing your trust center")}
|
||||
</p>
|
||||
) : (<></>)}
|
||||
{organization.trustCenter?.ndaFileName ? (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-sm font-medium">
|
||||
{organization.trustCenter.ndaFileName || __("Non-Disclosure Agreement")}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs text-txt-tertiary">
|
||||
{__("Visitors will need to accept this NDA before accessing your trust center")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
if (organization.trustCenter?.ndaFileUrl) {
|
||||
window.open(organization.trustCenter.ndaFileUrl, '_blank');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{__("Download PDF")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="quaternary"
|
||||
icon={IconTrashCan}
|
||||
onClick={handleNDADelete}
|
||||
disabled={isDeletingNDA}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Dropzone
|
||||
description={__("Upload PDF files up to 10MB")}
|
||||
isUploading={isUploadingNDA}
|
||||
onDrop={handleNDAUpload}
|
||||
accept={{
|
||||
"application/pdf": [".pdf"],
|
||||
}}
|
||||
maxSize={10}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Tabs>
|
||||
<TabItem
|
||||
|
||||
Reference in New Issue
Block a user