Add NDA view on console

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-02-20 12:12:04 +01:00
parent 33747c46eb
commit 069b47d240
12 changed files with 422 additions and 29 deletions

View File

@@ -1,6 +1,6 @@
import { formatDate } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { ActionDropdown, DropdownItem, IconArchive, IconCheckmark1, IconPencil, IconRotateCw, Td, Tr } from "@probo/ui";
import { ActionDropdown, DropdownItem, IconArchive, IconPencil, IconRotateCw, Td, Tr } from "@probo/ui";
import { useCallback, useState } from "react";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";
@@ -11,6 +11,8 @@ import { updateTrustCenterAccessMutation } from "#/hooks/graph/TrustCenterAccess
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { TrustCenterAccessEditDialog } from "#/pages/organizations/trustCenter/TrustCenterAccessTab/TrustCenterAccessEditDialog";
import { NdaSignatureBadge } from "./NdaSignatureBadge";
const fragment = graphql`
fragment CompliancePageAccessListItemFragment on TrustCenterAccess {
id
@@ -20,7 +22,9 @@ const fragment = graphql`
state
activeCount
pendingRequestCount
hasAcceptedNonDisclosureAgreement
ndaSignature {
status
}
canUpdate: permission(action: "core:trust-center-access:update")
}
`;
@@ -78,9 +82,13 @@ export function CompliancePageAccessListItem(props: {
</Td>
<Td>
<div className="flex justify-center">
{access.hasAcceptedNonDisclosureAgreement && (
<IconCheckmark1 size={16} className="text-txt-success" />
)}
{access.ndaSignature
? (
<NdaSignatureBadge status={access.ndaSignature.status} />
)
: (
<span className="text-txt-tertiary">-</span>
)}
</div>
</Td>
<Td noLink width={160} className="text-end">

View File

@@ -0,0 +1,94 @@
import { formatDatetime } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";
import type { ElectronicSignatureSectionFragment$key } from "#/__generated__/core/ElectronicSignatureSectionFragment.graphql";
import { EventTypeLabel } from "./EventTypeLabel";
import { NdaSignatureBadge } from "./NdaSignatureBadge";
const fragment = graphql`
fragment ElectronicSignatureSectionFragment on ElectronicSignature {
status
signedAt
certificateFileUrl
events {
id
eventType
actorEmail
occurredAt
}
}
`;
export function ElectronicSignatureSection({
fragmentRef,
}: {
fragmentRef: ElectronicSignatureSectionFragment$key;
}) {
const { __ } = useTranslate();
const signature = useFragment(fragment, fragmentRef);
return (
<div>
<h3 className="text-sm font-medium text-txt-primary mb-3">
{__("Electronic Signature")}
</h3>
<div className="rounded-lg border border-border-solid bg-bg-secondary p-4 space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm text-txt-secondary">{__("Status")}</span>
<NdaSignatureBadge status={signature.status} />
</div>
{signature.signedAt && (
<div className="flex items-center justify-between">
<span className="text-sm text-txt-secondary">{__("Signed at")}</span>
<span className="text-sm text-txt-primary">
{formatDatetime(signature.signedAt)}
</span>
</div>
)}
{signature.certificateFileUrl && (
<div className="flex items-center justify-between">
<span className="text-sm text-txt-secondary">{__("Certificate")}</span>
<a
href={signature.certificateFileUrl}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-txt-accent hover:underline"
>
{__("Download")}
</a>
</div>
)}
{signature.events.length > 0 && (
<div className="pt-2 border-t border-border-solid">
<span className="text-xs font-medium text-txt-secondary uppercase tracking-wider">
{__("Activity")}
</span>
<div className="mt-2 space-y-2">
{signature.events.map(event => (
<div
key={event.id}
className="flex items-start justify-between text-xs"
>
<div>
<span className="text-txt-primary">
<EventTypeLabel eventType={event.eventType} />
</span>
<span className="text-txt-tertiary ml-1">
{event.actorEmail}
</span>
</div>
<span className="text-txt-tertiary shrink-0 ml-2">
{formatDatetime(event.occurredAt)}
</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,28 @@
import { useTranslate } from "@probo/i18n";
export function EventTypeLabel({ eventType }: { eventType: string }) {
const { __ } = useTranslate();
switch (eventType) {
case "DOCUMENT_VIEWED":
return __("Document viewed");
case "CONSENT_GIVEN":
return __("Consent given");
case "FULL_NAME_TYPED":
return __("Full name typed");
case "SIGNATURE_ACCEPTED":
return __("Signature accepted");
case "SIGNATURE_COMPLETED":
return __("Signature completed");
case "SEAL_COMPUTED":
return __("Seal computed");
case "TIMESTAMP_REQUESTED":
return __("Timestamp requested");
case "CERTIFICATE_GENERATED":
return __("Certificate generated");
case "PROCESSING_ERROR":
return __("Processing error");
default:
return eventType;
}
}

View File

@@ -0,0 +1,20 @@
import { useTranslate } from "@probo/i18n";
import { Badge } from "@probo/ui";
type ElectronicSignatureStatus = "PENDING" | "ACCEPTED" | "PROCESSING" | "COMPLETED" | "FAILED";
export function NdaSignatureBadge({ status }: { status: ElectronicSignatureStatus }) {
const { __ } = useTranslate();
switch (status) {
case "COMPLETED":
return <Badge variant="success">{__("Signed")}</Badge>;
case "ACCEPTED":
case "PROCESSING":
return <Badge variant="info">{__("Processing")}</Badge>;
case "PENDING":
return <Badge variant="warning">{__("Pending")}</Badge>;
case "FAILED":
return <Badge variant="danger">{__("Failed")}</Badge>;
}
}

View File

@@ -29,6 +29,7 @@ import {
import { useFormWithSchema } from "#/hooks/useFormWithSchema";
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { ElectronicSignatureSection } from "#/pages/organizations/compliance-page/access/_components/ElectronicSignatureSection";
import { TrustCenterDocumentAccessList } from "./TrustCenterDocumentAccessList";
interface TrustCenterAccessEditDialogProps {
@@ -223,6 +224,10 @@ export function TrustCenterAccessEditForm(
</div>
{data.node.ndaSignature && (
<ElectronicSignatureSection fragmentRef={data.node.ndaSignature} />
)}
<TrustCenterDocumentAccessList
documentAccesses={documentAccesses}
initialStatusByID={initialStatusByID}