mirror of
https://github.com/documenso/documenso.git
synced 2025-11-19 03:01:59 +10:00
feat: update recipient expiry handling
This commit is contained in:
@ -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.`),
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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 });
|
||||||
|
|
||||||
|
|||||||
@ -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];
|
||||||
|
|||||||
@ -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;
|
||||||
|
};
|
||||||
@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user