diff --git a/apps/remix/app/components/embed/embed-document-rejected.tsx b/apps/remix/app/components/embed/embed-document-rejected.tsx new file mode 100644 index 000000000..911df8729 --- /dev/null +++ b/apps/remix/app/components/embed/embed-document-rejected.tsx @@ -0,0 +1,33 @@ +import { Trans } from '@lingui/react/macro'; +import { XCircle } from 'lucide-react'; + +export const EmbedDocumentRejected = () => { + return ( +
+
+
+ + +

+ Document Rejected +

+
+ +
+ You have rejected this document +
+ +

+ + The document owner has been notified of your decision. They may contact you with further + instructions if necessary. + +

+ +

+ No further action is required from you at this time. +

+
+
+ ); +}; diff --git a/apps/remix/app/components/embed/embed-document-signing-page.tsx b/apps/remix/app/components/embed/embed-document-signing-page.tsx index 980e80f7a..9f501e592 100644 --- a/apps/remix/app/components/embed/embed-document-signing-page.tsx +++ b/apps/remix/app/components/embed/embed-document-signing-page.tsx @@ -4,7 +4,13 @@ import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; import type { DocumentMeta, TemplateMeta } from '@prisma/client'; -import { type DocumentData, type Field, FieldType, RecipientRole } from '@prisma/client'; +import { + type DocumentData, + type Field, + FieldType, + RecipientRole, + SigningStatus, +} from '@prisma/client'; import { LucideChevronDown, LucideChevronUp } from 'lucide-react'; import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn'; @@ -29,9 +35,11 @@ import { injectCss } from '~/utils/css-vars'; import { ZSignDocumentEmbedDataSchema } from '../../types/embed-document-sign-schema'; import { useRequiredDocumentSigningContext } from '../general/document-signing/document-signing-provider'; import { DocumentSigningRecipientProvider } from '../general/document-signing/document-signing-recipient-provider'; +import { DocumentSigningRejectDialog } from '../general/document-signing/document-signing-reject-dialog'; import { EmbedClientLoading } from './embed-client-loading'; import { EmbedDocumentCompleted } from './embed-document-completed'; import { EmbedDocumentFields } from './embed-document-fields'; +import { EmbedDocumentRejected } from './embed-document-rejected'; export type EmbedSignDocumentClientPageProps = { token: string; @@ -74,6 +82,9 @@ export const EmbedSignDocumentClientPage = ({ const [hasFinishedInit, setHasFinishedInit] = useState(false); const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false); const [hasCompletedDocument, setHasCompletedDocument] = useState(isCompleted); + const [hasRejectedDocument, setHasRejectedDocument] = useState( + recipient.signingStatus === SigningStatus.REJECTED, + ); const [selectedSignerId, setSelectedSignerId] = useState( allRecipients.length > 0 ? allRecipients[0].id : null, ); @@ -82,6 +93,8 @@ export const EmbedSignDocumentClientPage = ({ const [isNameLocked, setIsNameLocked] = useState(false); const [showPendingFieldTooltip, setShowPendingFieldTooltip] = useState(false); + const [allowDocumentRejection, setAllowDocumentRejection] = useState(false); + const selectedSigner = allRecipients.find((r) => r.id === selectedSignerId); const isAssistantMode = recipient.role === RecipientRole.ASSISTANT; @@ -160,6 +173,25 @@ export const EmbedSignDocumentClientPage = ({ } }; + const onDocumentRejected = (reason: string) => { + if (window.parent) { + window.parent.postMessage( + { + action: 'document-rejected', + data: { + token, + documentId, + recipientId: recipient.id, + reason, + }, + }, + '*', + ); + } + + setHasRejectedDocument(true); + }; + useLayoutEffect(() => { const hash = window.location.hash.slice(1); @@ -173,6 +205,7 @@ export const EmbedSignDocumentClientPage = ({ // Since a recipient can be provided a name we can lock it without requiring // a to be provided by the parent application, unlike direct templates. setIsNameLocked(!!data.lockName); + setAllowDocumentRejection(!!data.allowDocumentRejection); if (data.darkModeDisabled) { document.documentElement.classList.add('dark-mode-disabled'); @@ -207,6 +240,10 @@ export const EmbedSignDocumentClientPage = ({ } }, [hasFinishedInit, hasDocumentLoaded]); + if (hasRejectedDocument) { + return ; + } + if (hasCompletedDocument) { return ( {(!hasFinishedInit || !hasDocumentLoaded) && } + {allowDocumentRejection && ( +
+ +
+ )} +
{/* Viewer */}
@@ -419,7 +466,7 @@ export const EmbedSignDocumentClientPage = ({ ) : (