feat: gate dictation UI to single-recipient next steps

This commit is contained in:
David Nguyen
2026-08-04 17:35:24 +10:00
parent 19514b182b
commit 8d48c92a27
3 changed files with 15 additions and 65 deletions
@@ -11,6 +11,7 @@ import {
import type { TTemplate } from '@documenso/lib/types/template';
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
import { getDictatableNextRecipient } from '@documenso/lib/utils/recipient-groups';
import type {
TRemovedSignedFieldWithTokenMutationSchema,
TSignFieldWithTokenMutationSchema,
@@ -223,27 +224,12 @@ export const DirectTemplateSigningForm = ({
return undefined;
}
const sortedRecipients = template.recipients.sort((a, b) => {
// Sort by signingOrder first (nulls last), then by id
if (a.signingOrder === null && b.signingOrder === null) {
return a.id - b.id;
}
if (a.signingOrder === null) {
return 1;
}
if (b.signingOrder === null) {
return -1;
}
if (a.signingOrder === b.signingOrder) {
return a.id - b.id;
}
return a.signingOrder - b.signingOrder;
});
const currentIndex = sortedRecipients.findIndex((r) => r.id === directRecipient.id);
return currentIndex !== -1 && currentIndex < sortedRecipients.length - 1
? sortedRecipients[currentIndex + 1]
: undefined;
return (
getDictatableNextRecipient({
recipients: template.recipients,
currentRecipientId: directRecipient.id,
}) ?? undefined
);
}, [template.templateMeta?.signingOrder, template.recipients, directRecipient.id]);
return (
@@ -15,6 +15,7 @@ import type { CompletedField } from '@documenso/lib/types/fields';
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
import { getDocumentDataUrlForPdfViewer } from '@documenso/lib/utils/envelope-download';
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
import { getDictatableNextRecipient } from '@documenso/lib/utils/recipient-groups';
import type { FieldWithSignatureAndFieldMeta } from '@documenso/prisma/types/field-with-signature-and-fieldmeta';
import type { RecipientWithFields } from '@documenso/prisma/types/recipient-with-fields';
import { trpc } from '@documenso/trpc/react';
@@ -143,31 +144,11 @@ export const DocumentSigningPageViewV1 = ({
const targetSigner = recipient.role === RecipientRole.ASSISTANT && selectedSigner ? selectedSigner : null;
const nextRecipient = useMemo(() => {
if (!documentMeta?.signingOrder || documentMeta.signingOrder !== 'SEQUENTIAL') {
if (documentMeta?.signingOrder !== 'SEQUENTIAL') {
return undefined;
}
const sortedRecipients = [...allRecipients].sort((a, b) => {
// Sort by signingOrder first (nulls last), then by id
if (a.signingOrder === null && b.signingOrder === null) {
return a.id - b.id;
}
if (a.signingOrder === null) {
return 1;
}
if (b.signingOrder === null) {
return -1;
}
if (a.signingOrder === b.signingOrder) {
return a.id - b.id;
}
return a.signingOrder - b.signingOrder;
});
const currentIndex = sortedRecipients.findIndex((r) => r.id === recipient.id);
return currentIndex !== -1 && currentIndex < sortedRecipients.length - 1
? sortedRecipients[currentIndex + 1]
: undefined;
return getDictatableNextRecipient({ recipients: allRecipients, currentRecipientId: recipient.id }) ?? undefined;
}, [document.documentMeta?.signingOrder, allRecipients, recipient.id]);
const pendingFields = fieldsRequiringValidation.filter((field) => !field.inserted);
@@ -6,6 +6,7 @@ import type { EnvelopeForSigningResponse } from '@documenso/lib/server-only/enve
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import { isFieldUnsignedAndRequired, isRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
import { extractFieldInsertionValues } from '@documenso/lib/utils/envelope-signing';
import { getDictatableNextRecipient } from '@documenso/lib/utils/recipient-groups';
import { trpc } from '@documenso/trpc/react';
import type { TSignEnvelopeFieldValue } from '@documenso/trpc/server/envelope-router/sign-envelope-field.types';
import { EnvelopeType, type Field, FieldType, type Recipient, RecipientRole, SigningStatus } from '@prisma/client';
@@ -290,32 +291,14 @@ export const EnvelopeSigningProvider = ({
.filter((field) => field.inserted);
const nextRecipient = useMemo(() => {
if (!envelope.documentMeta.signingOrder || envelope.documentMeta.signingOrder !== 'SEQUENTIAL') {
if (envelope.documentMeta.signingOrder !== 'SEQUENTIAL') {
return null;
}
const sortedRecipients = [...envelope.recipients].sort((a, b) => {
// Sort by signingOrder first (nulls last), then by id
if (a.signingOrder === null && b.signingOrder === null) {
return a.id - b.id;
}
if (a.signingOrder === null) {
return 1;
}
if (b.signingOrder === null) {
return -1;
}
if (a.signingOrder === b.signingOrder) {
return a.id - b.id;
}
return a.signingOrder - b.signingOrder;
return getDictatableNextRecipient({
recipients: envelope.recipients,
currentRecipientId: recipient.id,
});
const currentIndex = sortedRecipients.findIndex((r) => r.id === recipient.id);
return currentIndex !== -1 && currentIndex < sortedRecipients.length - 1
? sortedRecipients[currentIndex + 1]
: null;
}, [envelope.documentMeta?.signingOrder, envelope.recipients, recipient.id]);
const signField = async (