feat: update recipient expiry handling

This commit is contained in:
Ephraim Atta-Duncan
2024-11-17 22:57:40 +00:00
parent ba627e22c5
commit 2d7988f484
6 changed files with 90 additions and 5 deletions

View File

@ -101,6 +101,8 @@ export const ResendDocumentActionItem = ({
setIsOpen(false); setIsOpen(false);
} catch (err) { } catch (err) {
console.log(err);
toast({ toast({
title: _(msg`Something went wrong`), title: _(msg`Something went wrong`),
description: _(msg`This document could not be re-sent at this time. Please try again.`), description: _(msg`This document could not be re-sent at this time. Please try again.`),

View File

@ -41,10 +41,8 @@ export const StackAvatar = ({ first, zIndex, fallbackText = '', type }: StackAva
classes = 'bg-documenso-200 text-documenso-800'; classes = 'bg-documenso-200 text-documenso-800';
break; break;
case RecipientStatusType.REJECTED: case RecipientStatusType.REJECTED:
classes = 'bg-red-200 text-red-800';
break;
case RecipientStatusType.EXPIRED: case RecipientStatusType.EXPIRED:
classes = 'bg-gray-200 text-gray-700'; classes = 'bg-red-200 text-red-800';
break; break;
default: default:

View File

@ -16,6 +16,7 @@ import type { TRecipientActionAuth } from '../../types/document-auth';
import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn'; import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { sendPendingEmail } from './send-pending-email'; import { sendPendingEmail } from './send-pending-email';
import { updateExpiredRecipients } from './update-expired-recipients';
export type CompleteDocumentWithTokenOptions = { export type CompleteDocumentWithTokenOptions = {
token: string; token: string;
@ -61,12 +62,22 @@ export const completeDocumentWithToken = async ({
throw new Error(`Document ${document.id} has no recipient with token ${token}`); throw new Error(`Document ${document.id} has no recipient with token ${token}`);
} }
await updateExpiredRecipients(documentId);
const [recipient] = document.Recipient; const [recipient] = document.Recipient;
if (recipient.expired && recipient.expired < new Date()) {
throw new Error(`Recipient ${recipient.id} signature period has expired`);
}
if (recipient.signingStatus === SigningStatus.SIGNED) { if (recipient.signingStatus === SigningStatus.SIGNED) {
throw new Error(`Recipient ${recipient.id} has already signed`); throw new Error(`Recipient ${recipient.id} has already signed`);
} }
if (recipient.signingStatus === SigningStatus.EXPIRED) {
throw new Error(`Recipient ${recipient.id} signature period has expired`);
}
if (document.documentMeta?.signingOrder === DocumentSigningOrder.SEQUENTIAL) { if (document.documentMeta?.signingOrder === DocumentSigningOrder.SEQUENTIAL) {
const isRecipientsTurn = await getIsRecipientsTurnToSign({ token: recipient.token }); const isRecipientsTurn = await getIsRecipientsTurnToSign({ token: recipient.token });

View File

@ -14,8 +14,8 @@ import type { RequestMetadata } from '@documenso/lib/universal/extract-request-m
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template'; import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import type { Prisma } from '@documenso/prisma/client'; import type { Prisma } from '@documenso/prisma/client';
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server'; import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
@ -106,6 +106,23 @@ export const resendDocument = async ({
return; 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 i18n = await getI18nInstance(document.documentMeta?.language);
const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role]; const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role];

View File

@ -0,0 +1,57 @@
import { prisma } from '@documenso/prisma';
import { SigningStatus } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
export const updateExpiredRecipients = async (documentId: number) => {
const now = new Date();
const expiredRecipients = await prisma.recipient.findMany({
where: {
documentId,
expired: {
lt: now,
},
signingStatus: {
not: SigningStatus.EXPIRED,
},
},
});
if (expiredRecipients.length > 0) {
await prisma.recipient.updateMany({
where: {
id: {
in: expiredRecipients.map((recipient) => recipient.id),
},
},
data: {
signingStatus: SigningStatus.EXPIRED,
},
});
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,
},
}),
}),
),
);
}
return expiredRecipients;
};

View File

@ -184,7 +184,7 @@ export function DocumentExpiryDialog({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[450px]"> <DialogContent className="sm:max-w-[450px]">
<DialogHeader> <DialogHeader>
<DialogTitle>Set Document Expiry</DialogTitle> <DialogTitle>Set Recipient Expiry</DialogTitle>
<DialogDescription> <DialogDescription>
Set the expiry date for the document signing recipient. The recipient will not be able Set the expiry date for the document signing recipient. The recipient will not be able
to sign the document after this date. to sign the document after this date.