From 48a107685ade245f321fcf1a42fcac9cfe6d87ec Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Thu, 4 Jun 2026 14:45:37 +0300 Subject: [PATCH] chore: recipient helpers --- .../envelope-editor-recipient-form.tsx | 13 ++-- packages/lib/utils/recipients.ts | 59 +++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx index 8467db273..96b810af8 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx @@ -158,11 +158,8 @@ export const EnvelopeEditorRecipientForm = () => { return watchedSigners.some((signer) => signer.role === RecipientRole.ASSISTANT); }, [watchedSigners]); - const normalizeSigningOrders = (signers: typeof watchedSigners, options: { preserveOrder?: boolean } = {}) => { - return normalizeRecipientSigningOrders(signers, { - ...options, - canUpdateRecipient: (signer) => canRecipientBeModified(signer.id), - }); + const normalizeSigningOrders = (signers: typeof watchedSigners) => { + return normalizeRecipientSigningOrders(signers, (signer) => canRecipientBeModified(signer.id)); }; const activeRecipientCount = watchedSigners.filter((signer) => !isCcRecipient(signer)).length; @@ -393,7 +390,7 @@ export const EnvelopeEditorRecipientForm = () => { items.splice(insertIndex, 0, reorderedSigner); - const updatedSigners = normalizeSigningOrders(items, { preserveOrder: true }); + const updatedSigners = normalizeSigningOrders(items); form.setValue('signers', updatedSigners, { shouldValidate: true, @@ -484,9 +481,7 @@ export const EnvelopeEditorRecipientForm = () => { const newPosition = Math.min(Math.max(0, newOrder - 1), signersWithSigningOrder.length); signersWithSigningOrder.splice(newPosition, 0, reorderedSigner); - const updatedSigners = normalizeSigningOrders([...signersWithSigningOrder, ...signersWithoutSigningOrder], { - preserveOrder: true, - }); + const updatedSigners = normalizeSigningOrders([...signersWithSigningOrder, ...signersWithoutSigningOrder]); form.setValue('signers', updatedSigners, { shouldValidate: true, diff --git a/packages/lib/utils/recipients.ts b/packages/lib/utils/recipients.ts index 6da161568..e2d402fa3 100644 --- a/packages/lib/utils/recipients.ts +++ b/packages/lib/utils/recipients.ts @@ -8,6 +8,15 @@ import type { TRecipientLite } from '../types/recipient'; import { extractLegacyIds } from '../universal/id'; import { zEmail } from './zod'; +type RecipientWithSigningOrder = { + role: RecipientRole; + signingOrder?: number | null; +}; + +type CanUpdateRecipient = (recipient: T) => boolean; + +const canUpdateAnyRecipient = () => true; + /** * Roles that require fields to be assigned before a document can be distributed. * @@ -15,6 +24,56 @@ import { zEmail } from './zod'; */ export const RECIPIENT_ROLES_THAT_REQUIRE_FIELDS = [RecipientRole.SIGNER] as const; +export const isCcRecipient = (recipient: Pick) => { + return recipient.role === RecipientRole.CC; +}; + +export const getRecipientSigningOrder = (recipient: Pick) => { + if (isCcRecipient(recipient)) { + return null; + } + + return recipient.signingOrder ?? null; +}; + +export const sortRecipientsForSigningOrder = (recipients: T[]): T[] => { + return [...recipients].sort((r1, r2) => { + const r1IsCcRecipient = isCcRecipient(r1); + const r2IsCcRecipient = isCcRecipient(r2); + + // CC recipients always sort after non-CC recipients. + if (r1IsCcRecipient !== r2IsCcRecipient) { + return r1IsCcRecipient ? 1 : -1; + } + + // Order by signing order; missing orders sort last. + const r1SigningOrder = r1.signingOrder ?? Number.MAX_SAFE_INTEGER; + const r2SigningOrder = r2.signingOrder ?? Number.MAX_SAFE_INTEGER; + + return r1SigningOrder - r2SigningOrder; + }); +}; + +export const normalizeRecipientSigningOrders = ( + recipients: T[], + canUpdateRecipient: CanUpdateRecipient = canUpdateAnyRecipient, +): Array => { + const recipientsWithSigningOrder = recipients.filter((recipient) => !isCcRecipient(recipient)); + const ccRecipients = recipients.filter((recipient) => isCcRecipient(recipient)); + + const normalizedRecipients = recipientsWithSigningOrder.map((recipient, index) => ({ + ...recipient, + signingOrder: canUpdateRecipient(recipient) ? index + 1 : (recipient.signingOrder ?? index + 1), + })); + + const normalizedCcRecipients = ccRecipients.map((recipient) => ({ + ...recipient, + signingOrder: undefined, + })); + + return [...normalizedRecipients, ...normalizedCcRecipients]; +}; + /** * Returns recipients who are missing required fields for their role. *