feat: add reminder interval to document settings

This commit is contained in:
Ephraim Atta-Duncan
2025-04-15 06:27:56 +00:00
parent a6de9b3e3d
commit 651f5bbb6d
10 changed files with 114 additions and 10 deletions

View File

@ -175,7 +175,8 @@ export const DocumentEditForm = ({
const onAddSettingsFormSubmit = async (data: TAddSettingsFormSchema) => { const onAddSettingsFormSubmit = async (data: TAddSettingsFormSchema) => {
try { try {
const { timezone, dateFormat, redirectUrl, language, signatureTypes } = data.meta; const { timezone, dateFormat, redirectUrl, language, signatureTypes, reminderInterval } =
data.meta;
await updateDocument({ await updateDocument({
documentId: document.id, documentId: document.id,
@ -194,6 +195,7 @@ export const DocumentEditForm = ({
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE), typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD), uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW), drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
reminderInterval,
}, },
}); });

View File

@ -1,4 +1,8 @@
import type { DocumentDistributionMethod, DocumentSigningOrder } from '@prisma/client'; import type {
DocumentDistributionMethod,
DocumentReminderInterval,
DocumentSigningOrder,
} from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@ -30,6 +34,7 @@ export type CreateDocumentMetaOptions = {
uploadSignatureEnabled?: boolean; uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean; drawSignatureEnabled?: boolean;
language?: SupportedLanguageCodes; language?: SupportedLanguageCodes;
reminderInterval?: DocumentReminderInterval;
requestMetadata: ApiRequestMetadata; requestMetadata: ApiRequestMetadata;
}; };
@ -51,6 +56,7 @@ export const upsertDocumentMeta = async ({
uploadSignatureEnabled, uploadSignatureEnabled,
drawSignatureEnabled, drawSignatureEnabled,
language, language,
reminderInterval,
requestMetadata, requestMetadata,
}: CreateDocumentMetaOptions) => { }: CreateDocumentMetaOptions) => {
const document = await prisma.document.findFirst({ const document = await prisma.document.findFirst({
@ -106,6 +112,7 @@ export const upsertDocumentMeta = async ({
uploadSignatureEnabled, uploadSignatureEnabled,
drawSignatureEnabled, drawSignatureEnabled,
language, language,
reminderInterval,
}, },
update: { update: {
subject, subject,
@ -122,6 +129,7 @@ export const upsertDocumentMeta = async ({
uploadSignatureEnabled, uploadSignatureEnabled,
drawSignatureEnabled, drawSignatureEnabled,
language, language,
reminderInterval,
}, },
}); });

View File

@ -56,6 +56,8 @@ export const ZDocumentSchema = DocumentSchema.pick({
allowDictateNextSigner: true, allowDictateNextSigner: true,
language: true, language: true,
emailSettings: true, emailSettings: true,
reminderInterval: true,
lastReminderSentAt: true,
}).nullable(), }).nullable(),
recipients: ZRecipientLiteSchema.array(), recipients: ZRecipientLiteSchema.array(),
fields: ZFieldSchema.array(), fields: ZFieldSchema.array(),

View File

@ -0,0 +1,6 @@
-- CreateEnum
CREATE TYPE "DocumentReminderInterval" AS ENUM ('NONE', 'EVERY_1_HOUR', 'EVERY_6_HOURS', 'EVERY_12_HOURS', 'DAILY', 'EVERY_3_DAYS', 'WEEKLY', 'EVERY_2_WEEKS', 'MONTHLY');
-- AlterTable
ALTER TABLE "DocumentMeta" ADD COLUMN "lastReminderSentAt" TIMESTAMP(3),
ADD COLUMN "reminderInterval" "DocumentReminderInterval" NOT NULL DEFAULT 'NONE';

View File

@ -36,4 +36,4 @@
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "5.6.2" "typescript": "5.6.2"
} }
} }

View File

@ -388,6 +388,18 @@ enum DocumentDistributionMethod {
NONE NONE
} }
enum DocumentReminderInterval {
NONE
EVERY_1_HOUR
EVERY_6_HOURS
EVERY_12_HOURS
DAILY
EVERY_3_DAYS
WEEKLY
EVERY_2_WEEKS
MONTHLY
}
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';"]) /// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';"])
model DocumentMeta { model DocumentMeta {
id String @id @default(cuid()) id String @id @default(cuid())
@ -409,6 +421,9 @@ model DocumentMeta {
language String @default("en") language String @default("en")
distributionMethod DocumentDistributionMethod @default(EMAIL) distributionMethod DocumentDistributionMethod @default(EMAIL)
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema) emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
reminderInterval DocumentReminderInterval @default(NONE)
lastReminderSentAt DateTime?
} }
enum ReadStatus { enum ReadStatus {

View File

@ -5,8 +5,8 @@ import { authenticatedProcedure } from '../trpc';
import { import {
ZUpdateDocumentRequestSchema, ZUpdateDocumentRequestSchema,
ZUpdateDocumentResponseSchema, ZUpdateDocumentResponseSchema,
updateDocumentMeta,
} from './update-document.types'; } from './update-document.types';
import { updateDocumentMeta } from './update-document.types';
/** /**
* Public route. * Public route.
@ -39,6 +39,7 @@ export const updateDocumentRoute = authenticatedProcedure
signingOrder: meta.signingOrder, signingOrder: meta.signingOrder,
allowDictateNextSigner: meta.allowDictateNextSigner, allowDictateNextSigner: meta.allowDictateNextSigner,
emailSettings: meta.emailSettings, emailSettings: meta.emailSettings,
reminderInterval: meta.reminderInterval,
requestMetadata: ctx.metadata, requestMetadata: ctx.metadata,
}); });
} }

View File

@ -1,4 +1,4 @@
import { DocumentSigningOrder } from '@prisma/client'; import { DocumentReminderInterval, DocumentSigningOrder } from '@prisma/client';
// import type { OpenApiMeta } from 'trpc-to-openapi'; // import type { OpenApiMeta } from 'trpc-to-openapi';
import { z } from 'zod'; import { z } from 'zod';
@ -61,6 +61,7 @@ export const ZUpdateDocumentRequestSchema = z.object({
uploadSignatureEnabled: ZDocumentMetaUploadSignatureEnabledSchema.optional(), uploadSignatureEnabled: ZDocumentMetaUploadSignatureEnabledSchema.optional(),
drawSignatureEnabled: ZDocumentMetaDrawSignatureEnabledSchema.optional(), drawSignatureEnabled: ZDocumentMetaDrawSignatureEnabledSchema.optional(),
emailSettings: ZDocumentEmailSettingsSchema.optional(), emailSettings: ZDocumentEmailSettingsSchema.optional(),
reminderInterval: z.nativeEnum(DocumentReminderInterval).optional(),
}) })
.optional(), .optional(),
}); });

View File

@ -1,10 +1,16 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro'; import { Trans, useLingui } from '@lingui/react/macro';
import { useLingui } from '@lingui/react/macro'; import {
import { DocumentVisibility, TeamMemberRole } from '@prisma/client'; DocumentReminderInterval,
import { DocumentStatus, type Field, type Recipient, SendStatus } from '@prisma/client'; DocumentStatus,
DocumentVisibility,
type Field,
type Recipient,
SendStatus,
TeamMemberRole,
} from '@prisma/client';
import { InfoIcon } from 'lucide-react'; import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
@ -108,6 +114,7 @@ export const AddSettingsFormPartial = ({
redirectUrl: document.documentMeta?.redirectUrl ?? '', redirectUrl: document.documentMeta?.redirectUrl ?? '',
language: document.documentMeta?.language ?? 'en', language: document.documentMeta?.language ?? 'en',
signatureTypes: extractTeamSignatureSettings(document.documentMeta), signatureTypes: extractTeamSignatureSettings(document.documentMeta),
reminderInterval: document.documentMeta?.reminderInterval ?? DocumentReminderInterval.NONE,
}, },
}, },
}); });
@ -353,6 +360,67 @@ export const AddSettingsFormPartial = ({
)} )}
/> />
<FormField
control={form.control}
name="meta.reminderInterval"
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
<Trans>Reminder Interval</Trans>{' '}
<Tooltip>
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground max-w-xs">
<Trans>Set the interval between reminders for this document.</Trans>
</TooltipContent>
</Tooltip>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value={DocumentReminderInterval.NONE}>
No reminders
</SelectItem>
<SelectItem value={DocumentReminderInterval.EVERY_1_HOUR}>
Every hour
</SelectItem>
<SelectItem value={DocumentReminderInterval.EVERY_6_HOURS}>
Every 6 hours
</SelectItem>
<SelectItem value={DocumentReminderInterval.EVERY_12_HOURS}>
Every 12 hours
</SelectItem>
<SelectItem value={DocumentReminderInterval.DAILY}>
Daily
</SelectItem>
<SelectItem value={DocumentReminderInterval.EVERY_3_DAYS}>
Every 3 days
</SelectItem>
<SelectItem value={DocumentReminderInterval.WEEKLY}>
Weekly
</SelectItem>
<SelectItem value={DocumentReminderInterval.EVERY_2_WEEKS}>
Every 2 weeks
</SelectItem>
<SelectItem value={DocumentReminderInterval.MONTHLY}>
Monthly
</SelectItem>
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="meta.dateFormat" name="meta.dateFormat"

View File

@ -1,5 +1,5 @@
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { DocumentVisibility } from '@prisma/client'; import { DocumentReminderInterval, DocumentVisibility } from '@prisma/client';
import { z } from 'zod'; import { z } from 'zod';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats'; import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
@ -57,6 +57,7 @@ export const ZAddSettingsFormSchema = z.object({
signatureTypes: z.array(z.nativeEnum(DocumentSignatureType)).min(1, { signatureTypes: z.array(z.nativeEnum(DocumentSignatureType)).min(1, {
message: msg`At least one signature type must be enabled`.id, message: msg`At least one signature type must be enabled`.id,
}), }),
reminderInterval: z.nativeEnum(DocumentReminderInterval).optional(),
}), }),
}); });