diff --git a/apps/web/src/app/(dashboard)/documents/_action-items/resend-document.tsx b/apps/web/src/app/(dashboard)/documents/_action-items/resend-document.tsx index 2a9e8ffa3..d5b2b7dc2 100644 --- a/apps/web/src/app/(dashboard)/documents/_action-items/resend-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/_action-items/resend-document.tsx @@ -100,9 +100,7 @@ export const ResendDocumentActionItem = ({ }); setIsOpen(false); - } catch (err) { - console.log(err); - + } catch { toast({ title: _(msg`Something went wrong`), description: _(msg`This document could not be re-sent at this time. Please try again.`), diff --git a/apps/web/src/components/document/document-expiry-settings.tsx b/apps/web/src/components/document/document-expiry-settings.tsx index 95e20837a..a310d6431 100644 --- a/apps/web/src/components/document/document-expiry-settings.tsx +++ b/apps/web/src/components/document/document-expiry-settings.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; -import { msg } from '@lingui/macro'; +import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { Input } from '@documenso/ui/primitives/input'; @@ -25,12 +25,14 @@ export const DocumentExpirySettings = ({ onChange }: DocumentExpirySettingsProps const handleExpiryValueChange = (value: string) => { const parsedValue = parseInt(value, 10); - if (isNaN(parsedValue)) { + + if (Number.isNaN(parsedValue) || parsedValue <= 0) { setExpiryValue(undefined); + return; } else { setExpiryValue(parsedValue); + onChange(parsedValue, expiryUnit); } - onChange(parsedValue, expiryUnit); }; const handleExpiryUnitChange = (value: 'day' | 'week' | 'month') => { @@ -50,12 +52,18 @@ export const DocumentExpirySettings = ({ onChange }: DocumentExpirySettingsProps /> diff --git a/packages/lib/server-only/document/resend-document.tsx b/packages/lib/server-only/document/resend-document.tsx index 1843a3d68..81d70149d 100644 --- a/packages/lib/server-only/document/resend-document.tsx +++ b/packages/lib/server-only/document/resend-document.tsx @@ -106,23 +106,6 @@ export const resendDocument = async ({ return; } - let newExpiryDate: Date | null = null; - if (recipient.expired) { - const durationInMs = recipient.expired.getTime() - document.updatedAt.getTime(); - newExpiryDate = new Date(Date.now() + durationInMs); - - await prisma.recipient.update({ - where: { id: recipient.id }, - data: { - expired: newExpiryDate, - signingStatus: - recipient.signingStatus === SigningStatus.EXPIRED - ? SigningStatus.NOT_SIGNED - : recipient.signingStatus, - }, - }); - } - const i18n = await getI18nInstance(document.documentMeta?.language); const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role]; @@ -183,6 +166,22 @@ export const resendDocument = async ({ await prisma.$transaction( async (tx) => { + if (recipient.expired) { + const durationInMs = recipient.expired.getTime() - document.updatedAt.getTime(); + const newExpiryDate = new Date(Date.now() + durationInMs); + + await tx.recipient.update({ + where: { id: recipient.id }, + data: { + expired: newExpiryDate, + signingStatus: + recipient.signingStatus === SigningStatus.EXPIRED + ? SigningStatus.NOT_SIGNED + : recipient.signingStatus, + }, + }); + } + const [html, text] = await Promise.all([ renderEmailWithI18N(template, { lang: document.documentMeta?.language, diff --git a/packages/lib/server-only/document/update-expired-recipients.ts b/packages/lib/server-only/document/update-expired-recipients.ts index 85543e13a..039e351ba 100644 --- a/packages/lib/server-only/document/update-expired-recipients.ts +++ b/packages/lib/server-only/document/update-expired-recipients.ts @@ -31,26 +31,24 @@ export const updateExpiredRecipients = async (documentId: number) => { }, }); - await Promise.all( - expiredRecipients.map(async (recipient) => - prisma.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_EXPIRED, - documentId, - user: { - name: recipient.name, - email: recipient.email, - }, - data: { - recipientEmail: recipient.email, - recipientName: recipient.name, - recipientId: recipient.id, - recipientRole: recipient.role, - }, - }), + await prisma.documentAuditLog.createMany({ + data: expiredRecipients.map((recipient) => + createDocumentAuditLogData({ + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_EXPIRED, + documentId, + user: { + name: recipient.name, + email: recipient.email, + }, + data: { + recipientName: recipient.name, + recipientRole: recipient.role, + recipientId: recipient.id, + recipientEmail: recipient.email, + }, }), ), - ); + }); } return expiredRecipients; diff --git a/packages/lib/server-only/recipient/is-recipient-expired.ts b/packages/lib/server-only/recipient/is-recipient-expired.ts index 617a72a83..f1642cb4f 100644 --- a/packages/lib/server-only/recipient/is-recipient-expired.ts +++ b/packages/lib/server-only/recipient/is-recipient-expired.ts @@ -1,3 +1,5 @@ +import { DateTime } from 'luxon'; + import { prisma } from '@documenso/prisma'; import { SigningStatus } from '@documenso/prisma/client'; @@ -16,8 +18,8 @@ export const isRecipientExpired = async ({ token }: IsRecipientExpiredOptions) = throw new Error('Recipient not found'); } - const now = new Date(); - const hasExpired = recipient.expired && new Date(recipient.expired) <= now; + const now = DateTime.now(); + const hasExpired = recipient.expired && DateTime.fromJSDate(recipient.expired) <= now; if (hasExpired && recipient.signingStatus !== SigningStatus.EXPIRED) { await prisma.recipient.update({ diff --git a/packages/lib/server-only/recipient/set-recipient-expiry.ts b/packages/lib/server-only/recipient/set-recipient-expiry.ts index a7d4cf9a6..a246fd6d5 100644 --- a/packages/lib/server-only/recipient/set-recipient-expiry.ts +++ b/packages/lib/server-only/recipient/set-recipient-expiry.ts @@ -103,9 +103,9 @@ export const setRecipientExpiry = async ({ }, }), }); - - return persisted; } + + return persisted; }); return updatedRecipient; diff --git a/packages/lib/utils/document-audit-logs.ts b/packages/lib/utils/document-audit-logs.ts index 1987e21d9..f40fe2689 100644 --- a/packages/lib/utils/document-audit-logs.ts +++ b/packages/lib/utils/document-audit-logs.ts @@ -375,7 +375,7 @@ export const formatDocumentAuditLogAction = ( .with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_EXPIRED }, () => { const userName = prefix || _(msg`Recipient`); - const result = msg`${userName} expired`; + const result = msg`${userName}'s signing period has expired`; return { anonymous: result, diff --git a/packages/trpc/server/recipient-router/schema.ts b/packages/trpc/server/recipient-router/schema.ts index f0f16be38..8d590812e 100644 --- a/packages/trpc/server/recipient-router/schema.ts +++ b/packages/trpc/server/recipient-router/schema.ts @@ -84,7 +84,7 @@ export type TRejectDocumentWithTokenMutationSchema = z.infer< export const ZSetSignerExpirySchema = z.object({ documentId: z.number(), signerId: z.number(), - expiry: z.date(), + expiry: z.date().min(new Date(), { message: 'Expiry date must be in the future' }), teamId: z.number().optional(), }); diff --git a/packages/ui/primitives/document-flow/add-signers.types.ts b/packages/ui/primitives/document-flow/add-signers.types.ts index 29d2648ee..357b46374 100644 --- a/packages/ui/primitives/document-flow/add-signers.types.ts +++ b/packages/ui/primitives/document-flow/add-signers.types.ts @@ -15,7 +15,7 @@ export const ZAddSignerSchema = z.object({ .min(1), name: z.string(), role: z.nativeEnum(RecipientRole), - expiry: z.date().optional(), + expiry: z.date().min(new Date(), { message: 'Expiry date must be in the future' }).optional(), signingOrder: z.number().optional(), actionAuth: ZMapNegativeOneToUndefinedSchema.pipe(ZRecipientActionAuthTypesSchema.optional()), }); diff --git a/packages/ui/primitives/document-flow/document-expiry-dialog.tsx b/packages/ui/primitives/document-flow/document-expiry-dialog.tsx index 91c17a2ab..3c8984ffe 100644 --- a/packages/ui/primitives/document-flow/document-expiry-dialog.tsx +++ b/packages/ui/primitives/document-flow/document-expiry-dialog.tsx @@ -164,6 +164,8 @@ export function DocumentExpiryDialog({ case 'months': expiryDate = addMonths(now, values.amount); break; + default: + throw new Error(`Invalid unit: ${values.unit}`); } } @@ -173,10 +175,10 @@ export function DocumentExpiryDialog({ expiry: expiryDate, }); - // TODO: Implement logic to update expiry when resending document + // TODO: Duncan => Implement logic to update expiry when resending document // This should be handled on the server-side when a document is resent - // TODO: Implement logic to mark recipients as expired + // TODO: Duncan => Implement logic to mark recipients as expired // This should be a scheduled task or part of the completion process on the server };