feat: hide signature ui when theres no signature field (#1676)

This commit is contained in:
Ephraim Duncan
2025-03-06 01:34:11 +00:00
committed by GitHub
parent a41ac632d0
commit ae6cc24317
5 changed files with 186 additions and 143 deletions

View File

@ -311,7 +311,11 @@ export const SigningForm = ({
<> <>
<form onSubmit={handleSubmit(onFormSubmit)}> <form onSubmit={handleSubmit(onFormSubmit)}>
<p className="text-muted-foreground mt-2 text-sm"> <p className="text-muted-foreground mt-2 text-sm">
<Trans>Please review the document before signing.</Trans> {recipient.role === RecipientRole.APPROVER && !hasSignatureField ? (
<Trans>Please review the document before approving.</Trans>
) : (
<Trans>Please review the document before signing.</Trans>
)}
</p> </p>
<hr className="border-border mb-8 mt-4" /> <hr className="border-border mb-8 mt-4" />
@ -335,38 +339,40 @@ export const SigningForm = ({
/> />
</div> </div>
<div> {hasSignatureField && (
<Label htmlFor="Signature"> <div>
<Trans>Signature</Trans> <Label htmlFor="Signature">
</Label> <Trans>Signature</Trans>
</Label>
<Card className="mt-2" gradient degrees={-120}> <Card className="mt-2" gradient degrees={-120}>
<CardContent className="p-0"> <CardContent className="p-0">
<SignaturePad <SignaturePad
className="h-44 w-full" className="h-44 w-full"
disabled={isSubmitting} disabled={isSubmitting}
defaultValue={signature ?? undefined} defaultValue={signature ?? undefined}
onValidityChange={(isValid) => { onValidityChange={(isValid) => {
setSignatureValid(isValid); setSignatureValid(isValid);
}} }}
onChange={(value) => { onChange={(value) => {
if (signatureValid) { if (signatureValid) {
setSignature(value); setSignature(value);
} }
}} }}
allowTypedSignature={document.documentMeta?.typedSignatureEnabled} allowTypedSignature={document.documentMeta?.typedSignatureEnabled}
/> />
</CardContent> </CardContent>
</Card> </Card>
{hasSignatureField && !signatureValid && ( {!signatureValid && (
<div className="text-destructive mt-2 text-sm"> <div className="text-destructive mt-2 text-sm">
<Trans> <Trans>
Signature is too small. Please provide a more complete signature. Signature is too small. Please provide a more complete signature.
</Trans> </Trans>
</div> </div>
)} )}
</div> </div>
)}
</div> </div>
<div className="flex flex-col gap-4 md:flex-row"> <div className="flex flex-col gap-4 md:flex-row">

View File

@ -1,6 +1,7 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { Trans } from '@lingui/macro'; import { Trans } from '@lingui/macro';
import { match } from 'ts-pattern';
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers'; import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
import type { Field } from '@documenso/prisma/client'; import type { Field } from '@documenso/prisma/client';
@ -58,62 +59,88 @@ export const SignDialog = ({
loading={isSubmitting} loading={isSubmitting}
disabled={disabled} disabled={disabled}
> >
{isComplete ? <Trans>Complete</Trans> : <Trans>Next field</Trans>} {match({ isComplete, role })
.with({ isComplete: false }, () => <Trans>Next field</Trans>)
.with({ isComplete: true, role: RecipientRole.APPROVER }, () => <Trans>Approve</Trans>)
.with({ isComplete: true, role: RecipientRole.VIEWER }, () => (
<Trans>Mark as viewed</Trans>
))
.with({ isComplete: true }, () => <Trans>Complete</Trans>)
.exhaustive()}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogTitle> <DialogTitle>
<div className="text-foreground text-xl font-semibold"> <div className="text-foreground text-xl font-semibold">
{role === RecipientRole.VIEWER && <Trans>Complete Viewing</Trans>} {match(role)
{role === RecipientRole.SIGNER && <Trans>Complete Signing</Trans>} .with(RecipientRole.VIEWER, () => <Trans>Complete Viewing</Trans>)
{role === RecipientRole.APPROVER && <Trans>Complete Approval</Trans>} .with(RecipientRole.SIGNER, () => <Trans>Complete Signing</Trans>)
.with(RecipientRole.APPROVER, () => <Trans>Complete Approval</Trans>)
.with(RecipientRole.CC, () => <Trans>Complete Viewing</Trans>)
.with(RecipientRole.ASSISTANT, () => <Trans>Complete Assisting</Trans>)
.exhaustive()}
</div> </div>
</DialogTitle> </DialogTitle>
<div className="text-muted-foreground max-w-[50ch]"> <div className="text-muted-foreground max-w-[50ch]">
{role === RecipientRole.VIEWER && ( {match(role)
<span> .with(RecipientRole.VIEWER, () => (
<Trans> <span>
<span className="inline-flex flex-wrap"> <Trans>
You are about to complete viewing " <span className="inline-flex flex-wrap">
<span className="inline-block max-w-[11rem] truncate align-baseline"> You are about to complete viewing "
{documentTitle} <span className="inline-block max-w-[11rem] truncate align-baseline">
{documentTitle}
</span>
".
</span> </span>
". <br /> Are you sure?
</span> </Trans>
<br /> Are you sure? </span>
</Trans> ))
</span> .with(RecipientRole.SIGNER, () => (
)} <span>
{role === RecipientRole.SIGNER && ( <Trans>
<span> <span className="inline-flex flex-wrap">
<Trans> You are about to complete signing "
<span className="inline-flex flex-wrap"> <span className="inline-block max-w-[11rem] truncate align-baseline">
You are about to complete signing " {documentTitle}
<span className="inline-block max-w-[11rem] truncate align-baseline"> </span>
{documentTitle} ".
</span> </span>
". <br /> Are you sure?
</span> </Trans>
<br /> Are you sure? </span>
</Trans> ))
</span> .with(RecipientRole.APPROVER, () => (
)} <span>
{role === RecipientRole.APPROVER && ( <Trans>
<span> <span className="inline-flex flex-wrap">
<Trans> You are about to complete approving{' '}
<span className="inline-flex flex-wrap"> <span className="inline-block max-w-[11rem] truncate align-baseline">
You are about to complete approving{' '} "{documentTitle}"
<span className="inline-block max-w-[11rem] truncate align-baseline"> </span>
"{documentTitle}" .
</span> </span>
. <br /> Are you sure?
</span> </Trans>
<br /> Are you sure? </span>
</Trans> ))
</span> .otherwise(() => (
)} <span>
<Trans>
<span className="inline-flex flex-wrap">
You are about to complete viewing "
<span className="inline-block max-w-[11rem] truncate align-baseline">
{documentTitle}
</span>
".
</span>
<br /> Are you sure?
</Trans>
</span>
))}
</div> </div>
<SigningDisclosure className="mt-4" /> <SigningDisclosure className="mt-4" />
@ -138,9 +165,13 @@ export const SignDialog = ({
loading={isSubmitting} loading={isSubmitting}
onClick={onSignatureComplete} onClick={onSignatureComplete}
> >
{role === RecipientRole.VIEWER && <Trans>Mark as Viewed</Trans>} {match(role)
{role === RecipientRole.SIGNER && <Trans>Sign</Trans>} .with(RecipientRole.VIEWER, () => <Trans>Mark as Viewed</Trans>)
{role === RecipientRole.APPROVER && <Trans>Approve</Trans>} .with(RecipientRole.SIGNER, () => <Trans>Sign</Trans>)
.with(RecipientRole.APPROVER, () => <Trans>Approve</Trans>)
.with(RecipientRole.CC, () => <Trans>Mark as Viewed</Trans>)
.with(RecipientRole.ASSISTANT, () => <Trans>Complete</Trans>)
.exhaustive()}
</Button> </Button>
</div> </div>
</DialogFooter> </DialogFooter>

View File

@ -432,40 +432,42 @@ export const EmbedDirectTemplateClientPage = ({
/> />
</div> </div>
<div> {hasSignatureField && (
<Label htmlFor="Signature"> <div>
<Trans>Signature</Trans> <Label htmlFor="Signature">
</Label> <Trans>Signature</Trans>
</Label>
<Card className="mt-2" gradient degrees={-120}> <Card className="mt-2" gradient degrees={-120}>
<CardContent className="p-0"> <CardContent className="p-0">
<SignaturePad <SignaturePad
className="h-44 w-full" className="h-44 w-full"
disabled={isThrottled || isSubmitting} disabled={isThrottled || isSubmitting}
defaultValue={signature ?? undefined} defaultValue={signature ?? undefined}
onChange={(value) => { onChange={(value) => {
setSignature(value); setSignature(value);
}} }}
onValidityChange={(isValid) => { onValidityChange={(isValid) => {
setSignatureValid(isValid); setSignatureValid(isValid);
}} }}
allowTypedSignature={Boolean( allowTypedSignature={Boolean(
metadata && metadata &&
'typedSignatureEnabled' in metadata && 'typedSignatureEnabled' in metadata &&
metadata.typedSignatureEnabled, metadata.typedSignatureEnabled,
)} )}
/> />
</CardContent> </CardContent>
</Card> </Card>
{hasSignatureField && !signatureValid && ( {hasSignatureField && !signatureValid && (
<div className="text-destructive mt-2 text-sm"> <div className="text-destructive mt-2 text-sm">
<Trans> <Trans>
Signature is too small. Please provide a more complete signature. Signature is too small. Please provide a more complete signature.
</Trans> </Trans>
</div> </div>
)} )}
</div> </div>
)}
</div> </div>
</div> </div>

View File

@ -436,40 +436,42 @@ export const EmbedSignDocumentClientPage = ({
/> />
</div> </div>
<div> {hasSignatureField && (
<Label htmlFor="Signature"> <div>
<Trans>Signature</Trans> <Label htmlFor="Signature">
</Label> <Trans>Signature</Trans>
</Label>
<Card className="mt-2" gradient degrees={-120}> <Card className="mt-2" gradient degrees={-120}>
<CardContent className="p-0"> <CardContent className="p-0">
<SignaturePad <SignaturePad
className="h-44 w-full" className="h-44 w-full"
disabled={isThrottled || isSubmitting} disabled={isThrottled || isSubmitting}
defaultValue={signature ?? undefined} defaultValue={signature ?? undefined}
onChange={(value) => { onChange={(value) => {
setSignature(value); setSignature(value);
}} }}
onValidityChange={(isValid) => { onValidityChange={(isValid) => {
setSignatureValid(isValid); setSignatureValid(isValid);
}} }}
allowTypedSignature={Boolean( allowTypedSignature={Boolean(
metadata && metadata &&
'typedSignatureEnabled' in metadata && 'typedSignatureEnabled' in metadata &&
metadata.typedSignatureEnabled, metadata.typedSignatureEnabled,
)} )}
/> />
</CardContent> </CardContent>
</Card> </Card>
{hasSignatureField && !signatureValid && ( {hasSignatureField && !signatureValid && (
<div className="text-destructive mt-2 text-sm"> <div className="text-destructive mt-2 text-sm">
<Trans> <Trans>
Signature is too small. Please provide a more complete signature. Signature is too small. Please provide a more complete signature.
</Trans> </Trans>
</div> </div>
)} )}
</div> </div>
)}
</> </>
)} )}
</div> </div>

View File

@ -384,7 +384,9 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) =
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true'); await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
} }
await page.getByRole('button', { name: 'Complete' }).click(); await page
.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Complete' : 'Approve' })
.click();
await page await page
.getByRole('button', { name: role === RecipientRole.SIGNER ? 'Sign' : 'Approve' }) .getByRole('button', { name: role === RecipientRole.SIGNER ? 'Sign' : 'Approve' })
.click(); .click();
@ -454,7 +456,7 @@ test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a
const { status } = await getDocumentByToken(token); const { status } = await getDocumentByToken(token);
expect(status).toBe(DocumentStatus.PENDING); expect(status).toBe(DocumentStatus.PENDING);
await page.getByRole('button', { name: 'Complete' }).click(); await page.getByRole('button', { name: 'Approve' }).click();
await expect(page.getByRole('dialog').getByText('Complete Approval').first()).toBeVisible(); await expect(page.getByRole('dialog').getByText('Complete Approval').first()).toBeVisible();
await page.getByRole('button', { name: 'Approve' }).click(); await page.getByRole('button', { name: 'Approve' }).click();