mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 01:15:49 +10:00
fix: sort CC recipients last
This commit is contained in:
+83
-45
@@ -7,7 +7,11 @@ import { useOptionalSession } from '@documenso/lib/client-only/providers/session
|
|||||||
import type { TDetectedRecipientSchema } from '@documenso/lib/server-only/ai/envelope/detect-recipients/schema';
|
import type { TDetectedRecipientSchema } from '@documenso/lib/server-only/ai/envelope/detect-recipients/schema';
|
||||||
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
|
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
|
||||||
import { nanoid } from '@documenso/lib/universal/id';
|
import { nanoid } from '@documenso/lib/universal/id';
|
||||||
import { canRecipientBeModified as utilCanRecipientBeModified } from '@documenso/lib/utils/recipients';
|
import {
|
||||||
|
isCcRecipient,
|
||||||
|
normalizeRecipientSigningOrders,
|
||||||
|
canRecipientBeModified as utilCanRecipientBeModified,
|
||||||
|
} from '@documenso/lib/utils/recipients';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
|
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
|
||||||
import {
|
import {
|
||||||
@@ -154,17 +158,23 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
return watchedSigners.some((signer) => signer.role === RecipientRole.ASSISTANT);
|
return watchedSigners.some((signer) => signer.role === RecipientRole.ASSISTANT);
|
||||||
}, [watchedSigners]);
|
}, [watchedSigners]);
|
||||||
|
|
||||||
const normalizeSigningOrders = (signers: typeof watchedSigners) => {
|
const normalizeSigningOrders = (signers: typeof watchedSigners, options: { preserveOrder?: boolean } = {}) => {
|
||||||
return signers
|
return normalizeRecipientSigningOrders(signers, {
|
||||||
.sort((a, b) => (a.signingOrder ?? 0) - (b.signingOrder ?? 0))
|
...options,
|
||||||
.map((signer, index) => ({ ...signer, signingOrder: index + 1 }));
|
canUpdateRecipient: (signer) => canRecipientBeModified(signer.id),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const activeRecipientCount = watchedSigners.filter((signer) => !isCcRecipient(signer)).length;
|
||||||
append: appendSigner,
|
|
||||||
fields: signers,
|
const isAssistantLastActiveRecipient = (signers: typeof watchedSigners) => {
|
||||||
remove: removeSigner,
|
const activeRecipients = signers.filter((signer) => !isCcRecipient(signer));
|
||||||
} = useFieldArray({
|
const lastActiveRecipient = activeRecipients[activeRecipients.length - 1];
|
||||||
|
|
||||||
|
return lastActiveRecipient?.role === RecipientRole.ASSISTANT;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { fields: signers, remove: removeSigner } = useFieldArray({
|
||||||
control,
|
control,
|
||||||
name: 'signers',
|
name: 'signers',
|
||||||
keyName: 'nativeId',
|
keyName: 'nativeId',
|
||||||
@@ -207,14 +217,31 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
return utilCanRecipientBeModified(recipient, fields);
|
return utilCanRecipientBeModified(recipient, fields);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const appendNormalizedSigner = (signer: (typeof watchedSigners)[number], shouldFocus = false) => {
|
||||||
|
const updatedSigners = normalizeSigningOrders([...form.getValues('signers'), signer]);
|
||||||
|
|
||||||
|
form.setValue('signers', updatedSigners, {
|
||||||
|
shouldValidate: true,
|
||||||
|
shouldDirty: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldFocus) {
|
||||||
|
const signerIndex = updatedSigners.findIndex((updatedSigner) => updatedSigner.formId === signer.formId);
|
||||||
|
|
||||||
|
if (signerIndex !== -1) {
|
||||||
|
requestAnimationFrame(() => form.setFocus(`signers.${signerIndex}.email`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onAddSigner = () => {
|
const onAddSigner = () => {
|
||||||
appendSigner({
|
appendNormalizedSigner({
|
||||||
formId: nanoid(12),
|
formId: nanoid(12),
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
role: RecipientRole.SIGNER,
|
role: RecipientRole.SIGNER,
|
||||||
actionAuth: [],
|
actionAuth: [],
|
||||||
signingOrder: signers.length > 0 ? (signers[signers.length - 1]?.signingOrder ?? 0) + 1 : 1,
|
signingOrder: activeRecipientCount + 1,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -322,18 +349,16 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
|
|
||||||
form.setFocus(`signers.${emptySignerIndex}.email`);
|
form.setFocus(`signers.${emptySignerIndex}.email`);
|
||||||
} else {
|
} else {
|
||||||
appendSigner(
|
appendNormalizedSigner(
|
||||||
{
|
{
|
||||||
formId: nanoid(12),
|
formId: nanoid(12),
|
||||||
name: currentEditorName ?? '',
|
name: currentEditorName ?? '',
|
||||||
email: currentEditorEmail ?? '',
|
email: currentEditorEmail ?? '',
|
||||||
role: RecipientRole.SIGNER,
|
role: RecipientRole.SIGNER,
|
||||||
actionAuth: [],
|
actionAuth: [],
|
||||||
signingOrder: signers.length > 0 ? (signers[signers.length - 1]?.signingOrder ?? 0) + 1 : 1,
|
signingOrder: activeRecipientCount + 1,
|
||||||
},
|
|
||||||
{
|
|
||||||
shouldFocus: true,
|
|
||||||
},
|
},
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
void form.trigger('signers');
|
void form.trigger('signers');
|
||||||
@@ -368,18 +393,14 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
|
|
||||||
items.splice(insertIndex, 0, reorderedSigner);
|
items.splice(insertIndex, 0, reorderedSigner);
|
||||||
|
|
||||||
const updatedSigners = items.map((signer, index) => ({
|
const updatedSigners = normalizeSigningOrders(items, { preserveOrder: true });
|
||||||
...signer,
|
|
||||||
signingOrder: !canRecipientBeModified(signer.id) ? signer.signingOrder : index + 1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
form.setValue('signers', updatedSigners, {
|
form.setValue('signers', updatedSigners, {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
shouldDirty: true,
|
shouldDirty: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lastSigner = updatedSigners[updatedSigners.length - 1];
|
if (isAssistantLastActiveRecipient(updatedSigners)) {
|
||||||
if (lastSigner.role === RecipientRole.ASSISTANT) {
|
|
||||||
toast({
|
toast({
|
||||||
title: t`Warning: Assistant as last signer`,
|
title: t`Warning: Assistant as last signer`,
|
||||||
description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
|
description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
|
||||||
@@ -410,18 +431,19 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedSigners = currentSigners.map((signer, idx) => ({
|
const updatedSigners = normalizeSigningOrders(
|
||||||
...signer,
|
currentSigners.map((signer, idx) => ({
|
||||||
role: idx === index ? role : signer.role,
|
...signer,
|
||||||
signingOrder: !canRecipientBeModified(signer.id) ? signer.signingOrder : idx + 1,
|
role: idx === index ? role : signer.role,
|
||||||
}));
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
form.setValue('signers', updatedSigners, {
|
form.setValue('signers', updatedSigners, {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
shouldDirty: true,
|
shouldDirty: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (role === RecipientRole.ASSISTANT && index === updatedSigners.length - 1) {
|
if (role === RecipientRole.ASSISTANT && isAssistantLastActiveRecipient(updatedSigners)) {
|
||||||
toast({
|
toast({
|
||||||
title: t`Warning: Assistant as last signer`,
|
title: t`Warning: Assistant as last signer`,
|
||||||
description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
|
description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
|
||||||
@@ -446,22 +468,32 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
const currentSigners = form.getValues('signers');
|
const currentSigners = form.getValues('signers');
|
||||||
const signer = currentSigners[index];
|
const signer = currentSigners[index];
|
||||||
|
|
||||||
// Remove signer from current position and insert at new position
|
if (isCcRecipient(signer)) {
|
||||||
const remainingSigners = currentSigners.filter((_, idx) => idx !== index);
|
return;
|
||||||
const newPosition = Math.min(Math.max(0, newOrder - 1), currentSigners.length - 1);
|
}
|
||||||
remainingSigners.splice(newPosition, 0, signer);
|
|
||||||
|
|
||||||
const updatedSigners = remainingSigners.map((s, idx) => ({
|
const signersWithSigningOrder = currentSigners.filter((s) => !isCcRecipient(s));
|
||||||
...s,
|
const signersWithoutSigningOrder = currentSigners.filter((s) => isCcRecipient(s));
|
||||||
signingOrder: !canRecipientBeModified(s.id) ? s.signingOrder : idx + 1,
|
const currentSigningOrderIndex = signersWithSigningOrder.findIndex((s) => s.formId === signer.formId);
|
||||||
}));
|
|
||||||
|
if (currentSigningOrderIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [reorderedSigner] = signersWithSigningOrder.splice(currentSigningOrderIndex, 1);
|
||||||
|
const newPosition = Math.min(Math.max(0, newOrder - 1), signersWithSigningOrder.length);
|
||||||
|
signersWithSigningOrder.splice(newPosition, 0, reorderedSigner);
|
||||||
|
|
||||||
|
const updatedSigners = normalizeSigningOrders([...signersWithSigningOrder, ...signersWithoutSigningOrder], {
|
||||||
|
preserveOrder: true,
|
||||||
|
});
|
||||||
|
|
||||||
form.setValue('signers', updatedSigners, {
|
form.setValue('signers', updatedSigners, {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
shouldDirty: true,
|
shouldDirty: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (signer.role === RecipientRole.ASSISTANT && newPosition === remainingSigners.length - 1) {
|
if (signer.role === RecipientRole.ASSISTANT && isAssistantLastActiveRecipient(updatedSigners)) {
|
||||||
toast({
|
toast({
|
||||||
title: t`Warning: Assistant as last signer`,
|
title: t`Warning: Assistant as last signer`,
|
||||||
description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
|
description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
|
||||||
@@ -475,10 +507,12 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
setShowSigningOrderConfirmation(false);
|
setShowSigningOrderConfirmation(false);
|
||||||
|
|
||||||
const currentSigners = form.getValues('signers');
|
const currentSigners = form.getValues('signers');
|
||||||
const updatedSigners = currentSigners.map((signer) => ({
|
const updatedSigners = normalizeSigningOrders(
|
||||||
...signer,
|
currentSigners.map((signer) => ({
|
||||||
role: signer.role === RecipientRole.ASSISTANT ? RecipientRole.SIGNER : signer.role,
|
...signer,
|
||||||
}));
|
role: signer.role === RecipientRole.ASSISTANT ? RecipientRole.SIGNER : signer.role,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
form.setValue('signers', updatedSigners, {
|
form.setValue('signers', updatedSigners, {
|
||||||
shouldValidate: true,
|
shouldValidate: true,
|
||||||
@@ -781,6 +815,7 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
isDragDisabled={
|
isDragDisabled={
|
||||||
!isSigningOrderSequential ||
|
!isSigningOrderSequential ||
|
||||||
isSubmitting ||
|
isSubmitting ||
|
||||||
|
isCcRecipient(signer) ||
|
||||||
!canRecipientBeModified(signer.id) ||
|
!canRecipientBeModified(signer.id) ||
|
||||||
!signer.signingOrder
|
!signer.signingOrder
|
||||||
}
|
}
|
||||||
@@ -804,7 +839,11 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center gap-x-2">
|
<div className="flex flex-row items-center gap-x-2">
|
||||||
{isSigningOrderSequential && (
|
{isSigningOrderSequential && isCcRecipient(signer) && (
|
||||||
|
<div className="mt-auto h-10 w-[4.25rem] flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isSigningOrderSequential && !isCcRecipient(signer) && (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`signers.${index}.signingOrder`}
|
name={`signers.${index}.signingOrder`}
|
||||||
@@ -820,7 +859,7 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
max={signers.length}
|
max={activeRecipientCount}
|
||||||
data-testid="signing-order-input"
|
data-testid="signing-order-input"
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-10 text-center',
|
'w-10 text-center',
|
||||||
@@ -961,7 +1000,6 @@ export const EnvelopeEditorRecipientForm = () => {
|
|||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
handleRoleChange(index, value as RecipientRole);
|
handleRoleChange(index, value as RecipientRole);
|
||||||
field.onChange(value);
|
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
snapshot.isDragging || isSubmitting || !canRecipientBeModified(signer.id)
|
snapshot.isDragging || isSubmitting || !canRecipientBeModified(signer.id)
|
||||||
|
|||||||
Reference in New Issue
Block a user