mirror of
https://github.com/documenso/documenso.git
synced 2026-10-04 02:33:54 +10:00
feat: add allow document rejection setting to document preferences
Adds an allowDocumentRejection column to organisation settings (default true), team settings (nullable, inherits from the organisation) and document meta (default true), with the migration. Adds the "Allow Document Rejection" field to the organisation and team document preferences form, wires it through the settings pages, and lists it in the reset-to-defaults dialog and the admin settings section. Adds a per-document Yes/No field to the envelope editor settings dialog. Exposes the field on the document meta create/update schemas and the document, envelope, template and editor envelope response schemas, and hides the reject dialog on the V1 signing page when the document disallows rejection.
This commit is contained in:
@@ -96,6 +96,9 @@ export const DocumentPreferencesResetDialog = ({
|
||||
<li>
|
||||
<Trans>Delegate document ownership</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<Trans>Allow document rejection</Trans>
|
||||
</li>
|
||||
{showAiFeatures && (
|
||||
<li>
|
||||
<Trans>AI features</Trans>
|
||||
|
||||
@@ -43,6 +43,7 @@ export type TDocumentPreferencesFormSchema = {
|
||||
signatureTypes: DocumentSignatureType[];
|
||||
defaultRecipients: TDefaultRecipients | null;
|
||||
delegateDocumentOwnership: boolean | null;
|
||||
allowDocumentRejection: boolean | null;
|
||||
aiFeaturesEnabled: boolean | null;
|
||||
};
|
||||
|
||||
@@ -57,6 +58,7 @@ type SettingsSubset = Pick<
|
||||
| 'drawSignatureEnabled'
|
||||
| 'defaultRecipients'
|
||||
| 'delegateDocumentOwnership'
|
||||
| 'allowDocumentRejection'
|
||||
| 'aiFeaturesEnabled'
|
||||
>;
|
||||
|
||||
@@ -77,6 +79,7 @@ const getDocumentPreferencesFormValues = (settings: SettingsSubset): TDocumentPr
|
||||
signatureTypes: extractTeamSignatureSettings({ ...settings }),
|
||||
defaultRecipients: settings.defaultRecipients ? ZDefaultRecipientsSchema.parse(settings.defaultRecipients) : null,
|
||||
delegateDocumentOwnership: settings.delegateDocumentOwnership,
|
||||
allowDocumentRejection: settings.allowDocumentRejection,
|
||||
aiFeaturesEnabled: settings.aiFeaturesEnabled,
|
||||
};
|
||||
};
|
||||
@@ -101,6 +104,7 @@ export const DocumentPreferencesForm = ({ settings, onFormSubmit, canInherit }:
|
||||
}),
|
||||
defaultRecipients: ZDefaultRecipientsSchema.nullable(),
|
||||
delegateDocumentOwnership: z.boolean().nullable(),
|
||||
allowDocumentRejection: z.boolean().nullable(),
|
||||
aiFeaturesEnabled: z.boolean().nullable(),
|
||||
});
|
||||
|
||||
@@ -486,6 +490,60 @@ export const DocumentPreferencesForm = ({ settings, onFormSubmit, canInherit }:
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="allowDocumentRejection"
|
||||
render={({ field }) => (
|
||||
<InheritableField
|
||||
className="flex-1"
|
||||
canInherit={canInherit}
|
||||
isInherited={field.value === null}
|
||||
label={<Trans>Allow Document Rejection</Trans>}
|
||||
testId="allow-document-rejection"
|
||||
>
|
||||
<FormControl>
|
||||
<Select
|
||||
{...field}
|
||||
value={field.value === null ? '-1' : field.value.toString()}
|
||||
onValueChange={(value) =>
|
||||
field.onChange(value === 'true' ? true : value === 'false' ? false : null)
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="bg-background text-muted-foreground"
|
||||
data-testid="allow-document-rejection-trigger"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
<SelectItem value="true">
|
||||
<Trans>Yes</Trans>
|
||||
</SelectItem>
|
||||
|
||||
<SelectItem value="false">
|
||||
<Trans>No</Trans>
|
||||
</SelectItem>
|
||||
|
||||
{canInherit && (
|
||||
<SelectItem value={'-1'}>
|
||||
<Trans>Inherit from organisation</Trans>
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
<Trans>
|
||||
Controls whether recipients can reject newly created documents from the signing page. Existing
|
||||
documents keep their current setting. You can change it per document in the document settings.
|
||||
</Trans>
|
||||
</FormDescription>
|
||||
</InheritableField>
|
||||
)}
|
||||
/>
|
||||
|
||||
{isAiFeaturesConfigured && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
|
||||
@@ -138,6 +138,12 @@ export const AdminGlobalSettingsSection = ({
|
||||
</DetailsValue>
|
||||
</DetailsCard>
|
||||
|
||||
<DetailsCard label={<Trans>Allow document rejection</Trans>}>
|
||||
<DetailsValue>
|
||||
{booleanValue(settings.allowDocumentRejection, inheritedSettings?.allowDocumentRejection)}
|
||||
</DetailsValue>
|
||||
</DetailsCard>
|
||||
|
||||
<DetailsCard label={<Trans>Typed signature</Trans>}>
|
||||
<DetailsValue>
|
||||
{booleanValue(settings.typedSignatureEnabled, inheritedSettings?.typedSignatureEnabled)}
|
||||
|
||||
+3
-1
@@ -261,7 +261,9 @@ export const DocumentSigningPageViewV1 = ({
|
||||
|
||||
<div className="flex items-center gap-x-4">
|
||||
<DocumentSigningAttachmentsPopover envelopeId={document.envelopeId} token={recipient.token} />
|
||||
<DocumentSigningRejectDialog documentId={document.id} token={recipient.token} />
|
||||
{documentMeta.allowDocumentRejection && (
|
||||
<DocumentSigningRejectDialog documentId={document.id} token={recipient.token} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ export const ZAddSettingsFormSchema = z.object({
|
||||
signatureTypes: z.array(z.nativeEnum(DocumentSignatureType)).min(1, {
|
||||
message: msg`At least one signature type must be enabled`.id,
|
||||
}),
|
||||
allowDocumentRejection: z.boolean(),
|
||||
envelopeExpirationPeriod: ZEnvelopeExpirationPeriod.nullish(),
|
||||
reminderSettings: ZEnvelopeReminderSettings.nullish(),
|
||||
}),
|
||||
@@ -200,6 +201,7 @@ export const EnvelopeEditorSettingsDialog = ({ trigger, ...props }: EnvelopeEdit
|
||||
emailReplyTo: envelope.documentMeta.emailReplyTo ?? undefined,
|
||||
emailSettings: ZDocumentEmailSettingsSchema.parse(envelope.documentMeta.emailSettings),
|
||||
signatureTypes: extractTeamSignatureSettings(envelope.documentMeta),
|
||||
allowDocumentRejection: envelope.documentMeta.allowDocumentRejection,
|
||||
envelopeExpirationPeriod: envelope.documentMeta?.envelopeExpirationPeriod ?? null,
|
||||
reminderSettings: envelope.documentMeta?.reminderSettings ?? null,
|
||||
},
|
||||
@@ -249,6 +251,7 @@ export const EnvelopeEditorSettingsDialog = ({ trigger, ...props }: EnvelopeEdit
|
||||
message,
|
||||
subject,
|
||||
emailReplyTo,
|
||||
allowDocumentRejection,
|
||||
envelopeExpirationPeriod,
|
||||
reminderSettings,
|
||||
} = data.meta;
|
||||
@@ -278,6 +281,7 @@ export const EnvelopeEditorSettingsDialog = ({ trigger, ...props }: EnvelopeEdit
|
||||
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
|
||||
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
|
||||
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
|
||||
allowDocumentRejection,
|
||||
envelopeExpirationPeriod,
|
||||
reminderSettings,
|
||||
},
|
||||
@@ -465,6 +469,52 @@ export const EnvelopeEditorSettingsDialog = ({ trigger, ...props }: EnvelopeEdit
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="meta.allowDocumentRejection"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex flex-row items-center">
|
||||
<Trans>Allow Document Rejection</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-xs text-muted-foreground">
|
||||
<Trans>
|
||||
Controls whether recipients can reject the document from the signing page.
|
||||
</Trans>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value ? 'true' : 'false'}
|
||||
onValueChange={(value) => field.onChange(value === 'true')}
|
||||
>
|
||||
<SelectTrigger className="bg-background" data-testid="allow-document-rejection-trigger">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
<SelectItem value="true">
|
||||
<Trans>Yes</Trans>
|
||||
</SelectItem>
|
||||
|
||||
<SelectItem value="false">
|
||||
<Trans>No</Trans>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{settings.allowConfigureDateFormat && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
|
||||
@@ -39,6 +39,7 @@ export default function OrganisationSettingsDocumentPage() {
|
||||
signatureTypes,
|
||||
defaultRecipients,
|
||||
delegateDocumentOwnership,
|
||||
allowDocumentRejection,
|
||||
aiFeaturesEnabled,
|
||||
} = data;
|
||||
|
||||
@@ -46,6 +47,7 @@ export default function OrganisationSettingsDocumentPage() {
|
||||
documentVisibility === null ||
|
||||
documentLanguage === null ||
|
||||
documentDateFormat === null ||
|
||||
allowDocumentRejection === null ||
|
||||
aiFeaturesEnabled === null
|
||||
) {
|
||||
throw new Error('Should not be possible.');
|
||||
@@ -63,6 +65,7 @@ export default function OrganisationSettingsDocumentPage() {
|
||||
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
|
||||
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
|
||||
delegateDocumentOwnership,
|
||||
allowDocumentRejection,
|
||||
aiFeaturesEnabled,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ export default function TeamsSettingsPage() {
|
||||
signatureTypes,
|
||||
defaultRecipients,
|
||||
delegateDocumentOwnership,
|
||||
allowDocumentRejection,
|
||||
aiFeaturesEnabled,
|
||||
} = data;
|
||||
|
||||
@@ -57,6 +58,7 @@ export default function TeamsSettingsPage() {
|
||||
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
|
||||
}),
|
||||
delegateDocumentOwnership,
|
||||
allowDocumentRejection,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ export const ZDocumentMetaSchema = DocumentMetaSchema.pick({
|
||||
typedSignatureEnabled: true,
|
||||
uploadSignatureEnabled: true,
|
||||
drawSignatureEnabled: true,
|
||||
allowDocumentRejection: true,
|
||||
language: true,
|
||||
emailSettings: true,
|
||||
});
|
||||
@@ -105,6 +106,10 @@ export const ZDocumentMetaUploadSignatureEnabledSchema = z
|
||||
.boolean()
|
||||
.describe('Whether to allow recipients to sign using an uploaded signature.');
|
||||
|
||||
export const ZDocumentMetaAllowDocumentRejectionSchema = z
|
||||
.boolean()
|
||||
.describe('Whether to allow recipients to reject the document from the signing page.');
|
||||
|
||||
/**
|
||||
* Note: Any updates to this will cause public API changes. You will need to update
|
||||
* all corresponding areas where this is used (some places that use this needs to pass
|
||||
@@ -123,6 +128,7 @@ export const ZDocumentMetaCreateSchema = z.object({
|
||||
typedSignatureEnabled: ZDocumentMetaTypedSignatureEnabledSchema.optional(),
|
||||
uploadSignatureEnabled: ZDocumentMetaUploadSignatureEnabledSchema.optional(),
|
||||
drawSignatureEnabled: ZDocumentMetaDrawSignatureEnabledSchema.optional(),
|
||||
allowDocumentRejection: ZDocumentMetaAllowDocumentRejectionSchema.optional(),
|
||||
emailId: z.string().nullish(),
|
||||
emailReplyTo: zEmail().nullish(),
|
||||
emailSettings: ZDocumentEmailSettingsSchema.nullish(),
|
||||
|
||||
@@ -63,6 +63,7 @@ export const ZDocumentSchema = LegacyDocumentSchema.pick({
|
||||
uploadSignatureEnabled: true,
|
||||
drawSignatureEnabled: true,
|
||||
allowDictateNextSigner: true,
|
||||
allowDocumentRejection: true,
|
||||
language: true,
|
||||
emailSettings: true,
|
||||
emailId: true,
|
||||
|
||||
@@ -280,6 +280,7 @@ export const ZEditorEnvelopeSchema = EnvelopeSchema.pick({
|
||||
uploadSignatureEnabled: true,
|
||||
drawSignatureEnabled: true,
|
||||
allowDictateNextSigner: true,
|
||||
allowDocumentRejection: true,
|
||||
language: true,
|
||||
emailSettings: true,
|
||||
emailId: true,
|
||||
|
||||
@@ -50,6 +50,7 @@ export const ZEnvelopeSchema = EnvelopeSchema.pick({
|
||||
uploadSignatureEnabled: true,
|
||||
drawSignatureEnabled: true,
|
||||
allowDictateNextSigner: true,
|
||||
allowDocumentRejection: true,
|
||||
language: true,
|
||||
emailSettings: true,
|
||||
emailId: true,
|
||||
|
||||
@@ -55,6 +55,7 @@ export const ZTemplateSchema = TemplateSchema.pick({
|
||||
uploadSignatureEnabled: true,
|
||||
drawSignatureEnabled: true,
|
||||
allowDictateNextSigner: true,
|
||||
allowDocumentRejection: true,
|
||||
distributionMethod: true,
|
||||
redirectUrl: true,
|
||||
language: true,
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "DocumentMeta" ADD COLUMN "allowDocumentRejection" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "OrganisationGlobalSettings" ADD COLUMN "allowDocumentRejection" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "TeamGlobalSettings" ADD COLUMN "allowDocumentRejection" BOOLEAN;
|
||||
@@ -583,6 +583,8 @@ model DocumentMeta {
|
||||
|
||||
reminderSettings Json? /// [EnvelopeReminderSettings] @zod.custom.use(ZEnvelopeReminderSettingsSchema)
|
||||
|
||||
allowDocumentRejection Boolean @default(true)
|
||||
|
||||
envelope Envelope?
|
||||
}
|
||||
|
||||
@@ -966,6 +968,7 @@ model OrganisationGlobalSettings {
|
||||
documentTimezone String? // Nullable to allow using local timezones if not set.
|
||||
documentDateFormat String @default("yyyy-MM-dd hh:mm a")
|
||||
delegateDocumentOwnership Boolean @default(false)
|
||||
allowDocumentRejection Boolean @default(true)
|
||||
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
uploadSignatureEnabled Boolean @default(true)
|
||||
@@ -1005,6 +1008,7 @@ model TeamGlobalSettings {
|
||||
documentTimezone String?
|
||||
documentDateFormat String?
|
||||
delegateDocumentOwnership Boolean?
|
||||
allowDocumentRejection Boolean?
|
||||
|
||||
includeSenderDetails Boolean?
|
||||
includeSigningCertificate Boolean?
|
||||
|
||||
Reference in New Issue
Block a user