feat: complete document 2fa (wip)

This commit is contained in:
Ephraim Atta-Duncan
2025-04-23 08:26:52 +00:00
parent b94645a451
commit 35db8182f0
9 changed files with 171 additions and 84 deletions

View File

@ -27,7 +27,7 @@ export type DocumentSigningAuthDialogProps = {
actionTarget: FieldType | 'DOCUMENT'; actionTarget: FieldType | 'DOCUMENT';
open: boolean; open: boolean;
onOpenChange: (value: boolean) => void; onOpenChange: (value: boolean) => void;
isEnterprise: boolean;
/** /**
* The callback to run when the reauth form is filled out. * The callback to run when the reauth form is filled out.
*/ */
@ -41,6 +41,7 @@ export const DocumentSigningAuthDialog = ({
open, open,
onOpenChange, onOpenChange,
onReauthFormSubmit, onReauthFormSubmit,
isEnterprise,
}: DocumentSigningAuthDialogProps) => { }: DocumentSigningAuthDialogProps) => {
const { recipient, user, isCurrentlyAuthenticating } = useRequiredDocumentSigningAuthContext(); const { recipient, user, isCurrentlyAuthenticating } = useRequiredDocumentSigningAuthContext();

View File

@ -66,6 +66,7 @@ export interface DocumentSigningAuthProviderProps {
recipient: Recipient; recipient: Recipient;
user?: SessionUser | null; user?: SessionUser | null;
children: React.ReactNode; children: React.ReactNode;
isEnterprise: boolean;
} }
export const DocumentSigningAuthProvider = ({ export const DocumentSigningAuthProvider = ({
@ -73,6 +74,7 @@ export const DocumentSigningAuthProvider = ({
recipient: initialRecipient, recipient: initialRecipient,
user, user,
children, children,
isEnterprise,
}: DocumentSigningAuthProviderProps) => { }: DocumentSigningAuthProviderProps) => {
const [documentAuthOptions, setDocumentAuthOptions] = useState(initialDocumentAuthOptions); const [documentAuthOptions, setDocumentAuthOptions] = useState(initialDocumentAuthOptions);
const [recipient, setRecipient] = useState(initialRecipient); const [recipient, setRecipient] = useState(initialRecipient);
@ -138,8 +140,13 @@ export const DocumentSigningAuthProvider = ({
.exhaustive(); .exhaustive();
const executeActionAuthProcedure = async (options: ExecuteActionAuthProcedureOptions) => { const executeActionAuthProcedure = async (options: ExecuteActionAuthProcedureOptions) => {
// Directly run callback if no auth required. // Determine if authentication is required based on enterprise status and action target.
if (!derivedRecipientActionAuth || options.actionTarget !== FieldType.SIGNATURE) { const requiresAuthTrigger = isEnterprise
? derivedRecipientActionAuth && options.actionTarget === FieldType.SIGNATURE
: derivedRecipientActionAuth && options.actionTarget === 'DOCUMENT';
// Directly run callback if no auth trigger is needed.
if (!requiresAuthTrigger) {
await options.onReauthFormSubmit(); await options.onReauthFormSubmit();
return; return;
} }
@ -209,6 +216,7 @@ export const DocumentSigningAuthProvider = ({
onReauthFormSubmit={documentAuthDialogPayload.onReauthFormSubmit} onReauthFormSubmit={documentAuthDialogPayload.onReauthFormSubmit}
actionTarget={documentAuthDialogPayload.actionTarget} actionTarget={documentAuthDialogPayload.actionTarget}
documentAuthType={derivedRecipientActionAuth} documentAuthType={derivedRecipientActionAuth}
isEnterprise={isEnterprise}
/> />
)} )}
</DocumentSigningAuthContext.Provider> </DocumentSigningAuthContext.Provider>
@ -218,6 +226,8 @@ export const DocumentSigningAuthProvider = ({
type ExecuteActionAuthProcedureOptions = Omit< type ExecuteActionAuthProcedureOptions = Omit<
DocumentSigningAuthDialogProps, DocumentSigningAuthDialogProps,
'open' | 'onOpenChange' | 'documentAuthType' | 'recipientRole' 'open' | 'onOpenChange' | 'documentAuthType' | 'recipientRole'
>; > & {
actionTarget: FieldType | 'DOCUMENT';
};
DocumentSigningAuthProvider.displayName = 'DocumentSigningAuthProvider'; DocumentSigningAuthProvider.displayName = 'DocumentSigningAuthProvider';

View File

@ -28,6 +28,7 @@ import {
AssistantConfirmationDialog, AssistantConfirmationDialog,
type NextSigner, type NextSigner,
} from '../../dialogs/assistant-confirmation-dialog'; } from '../../dialogs/assistant-confirmation-dialog';
import { useRequiredDocumentSigningAuthContext } from './document-signing-auth-provider';
import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog'; import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog';
import { useRequiredDocumentSigningContext } from './document-signing-provider'; import { useRequiredDocumentSigningContext } from './document-signing-provider';
@ -39,6 +40,7 @@ export type DocumentSigningFormProps = {
isRecipientsTurn: boolean; isRecipientsTurn: boolean;
allRecipients?: RecipientWithFields[]; allRecipients?: RecipientWithFields[];
setSelectedSignerId?: (id: number | null) => void; setSelectedSignerId?: (id: number | null) => void;
isEnterprise: boolean;
}; };
export const DocumentSigningForm = ({ export const DocumentSigningForm = ({
@ -49,6 +51,7 @@ export const DocumentSigningForm = ({
isRecipientsTurn, isRecipientsTurn,
allRecipients = [], allRecipients = [],
setSelectedSignerId, setSelectedSignerId,
isEnterprise,
}: DocumentSigningFormProps) => { }: DocumentSigningFormProps) => {
const { sessionData } = useOptionalSession(); const { sessionData } = useOptionalSession();
const user = sessionData?.user; const user = sessionData?.user;
@ -62,6 +65,7 @@ export const DocumentSigningForm = ({
const assistantSignersId = useId(); const assistantSignersId = useId();
const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext(); const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext();
const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext();
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false); const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false);
@ -114,11 +118,17 @@ export const DocumentSigningForm = ({
setIsAssistantSubmitting(true); setIsAssistantSubmitting(true);
try { try {
await completeDocument(undefined, nextSigner); await executeActionAuthProcedure({
actionTarget: 'DOCUMENT',
isEnterprise,
onReauthFormSubmit: async (authOptions) => {
await completeDocument(authOptions, nextSigner);
},
});
} catch (err) { } catch (err) {
toast({ toast({
title: 'Error', title: _(msg`Error`),
description: 'An error occurred while completing the document. Please try again.', description: _(msg`An error occurred while completing the document. Please try again.`),
variant: 'destructive', variant: 'destructive',
}); });
@ -229,7 +239,13 @@ export const DocumentSigningForm = ({
fields={fields} fields={fields}
fieldsValidated={fieldsValidated} fieldsValidated={fieldsValidated}
onSignatureComplete={async (nextSigner) => { onSignatureComplete={async (nextSigner) => {
await completeDocument(undefined, nextSigner); await executeActionAuthProcedure({
actionTarget: 'DOCUMENT',
isEnterprise,
onReauthFormSubmit: async (authOptions) => {
await completeDocument(authOptions, nextSigner);
},
});
}} }}
role={recipient.role} role={recipient.role}
allowDictateNextSigner={document.documentMeta?.allowDictateNextSigner} allowDictateNextSigner={document.documentMeta?.allowDictateNextSigner}
@ -409,7 +425,13 @@ export const DocumentSigningForm = ({
fieldsValidated={fieldsValidated} fieldsValidated={fieldsValidated}
disabled={!isRecipientsTurn} disabled={!isRecipientsTurn}
onSignatureComplete={async (nextSigner) => { onSignatureComplete={async (nextSigner) => {
await completeDocument(undefined, nextSigner); await executeActionAuthProcedure({
actionTarget: 'DOCUMENT',
isEnterprise,
onReauthFormSubmit: async (authOptions) => {
await completeDocument(authOptions, nextSigner);
},
});
}} }}
role={recipient.role} role={recipient.role}
allowDictateNextSigner={ allowDictateNextSigner={

View File

@ -6,6 +6,7 @@ import { getOptionalLoaderContext } from 'server/utils/get-loader-session';
import signingCelebration from '@documenso/assets/images/signing-celebration.png'; import signingCelebration from '@documenso/assets/images/signing-celebration.png';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session'; import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { useOptionalSession } from '@documenso/lib/client-only/providers/session'; import { useOptionalSession } from '@documenso/lib/client-only/providers/session';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token'; import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { isRecipientAuthorized } from '@documenso/lib/server-only/document/is-recipient-authorized'; import { isRecipientAuthorized } from '@documenso/lib/server-only/document/is-recipient-authorized';
@ -60,6 +61,10 @@ export async function loader({ params, request }: Route.LoaderArgs) {
throw new Response('Not Found', { status: 404 }); throw new Response('Not Found', { status: 404 });
} }
const isEnterprise = user?.id
? await isUserEnterprise({ userId: user.id }).catch(() => false)
: false;
const recipientWithFields = { ...recipient, fields }; const recipientWithFields = { ...recipient, fields };
const isRecipientsTurn = await getIsRecipientsTurnToSign({ token }); const isRecipientsTurn = await getIsRecipientsTurnToSign({ token });
@ -115,6 +120,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
isDocumentAccessValid: false, isDocumentAccessValid: false,
recipientEmail: recipient.email, recipientEmail: recipient.email,
recipientHasAccount, recipientHasAccount,
isEnterprise,
} as const); } as const);
} }
@ -149,6 +155,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
completedFields, completedFields,
recipientSignature, recipientSignature,
isRecipientsTurn, isRecipientsTurn,
isEnterprise,
} as const); } as const);
} }
@ -176,6 +183,7 @@ export default function SigningPage() {
isRecipientsTurn, isRecipientsTurn,
allRecipients, allRecipients,
recipientWithFields, recipientWithFields,
isEnterprise,
} = data; } = data;
if (document.deletedAt || document.status === DocumentStatus.REJECTED) { if (document.deletedAt || document.status === DocumentStatus.REJECTED) {
@ -241,6 +249,7 @@ export default function SigningPage() {
documentAuthOptions={document.authOptions} documentAuthOptions={document.authOptions}
recipient={recipient} recipient={recipient}
user={user} user={user}
isEnterprise={isEnterprise}
> >
<DocumentSigningPageView <DocumentSigningPageView
recipient={recipientWithFields} recipient={recipientWithFields}

View File

@ -1,8 +1,6 @@
import { DocumentVisibility } from '@prisma/client'; import { DocumentStatus, DocumentVisibility, TeamMemberRole } from '@prisma/client';
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { CreateDocumentAuditLogDataResponse } from '@documenso/lib/utils/document-audit-logs'; import type { CreateDocumentAuditLogDataResponse } from '@documenso/lib/utils/document-audit-logs';
@ -135,18 +133,18 @@ export const updateDocument = async ({
data?.globalActionAuth === undefined ? documentGlobalActionAuth : data.globalActionAuth; data?.globalActionAuth === undefined ? documentGlobalActionAuth : data.globalActionAuth;
// Check if user has permission to set the global action auth. // Check if user has permission to set the global action auth.
if (newGlobalActionAuth) { // if (newGlobalActionAuth) {
const isDocumentEnterprise = await isUserEnterprise({ // const isDocumentEnterprise = await isUserEnterprise({
userId, // userId,
teamId, // teamId,
}); // });
if (!isDocumentEnterprise) { // if (!isDocumentEnterprise) {
throw new AppError(AppErrorCode.UNAUTHORIZED, { // throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'You do not have permission to set the action auth', // message: 'You do not have permission to set the action auth',
}); // });
} // }
} // }
const isTitleSame = data.title === undefined || data.title === document.title; const isTitleSame = data.title === undefined || data.title === document.title;
const isExternalIdSame = data.externalId === undefined || data.externalId === document.externalId; const isExternalIdSame = data.externalId === undefined || data.externalId === document.externalId;

View File

@ -2,6 +2,7 @@ import { DocumentStatus, FieldType, RecipientRole, SigningStatus } from '@prisma
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox'; import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown'; import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number'; import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
@ -13,7 +14,7 @@ import { prisma } from '@documenso/prisma';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../../constants/date-formats'; import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../../constants/date-formats';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '../../constants/time-zones'; import { DEFAULT_DOCUMENT_TIME_ZONE } from '../../constants/time-zones';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { TRecipientActionAuth } from '../../types/document-auth'; import type { TRecipientActionAuth, TRecipientActionAuthTypes } from '../../types/document-auth';
import { import {
ZCheckboxFieldMeta, ZCheckboxFieldMeta,
ZDropdownFieldMeta, ZDropdownFieldMeta,
@ -23,6 +24,7 @@ import {
} from '../../types/field-meta'; } from '../../types/field-meta';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { extractDocumentAuthMethods } from '../../utils/document-auth';
import { validateFieldAuth } from '../document/validate-field-auth'; import { validateFieldAuth } from '../document/validate-field-auth';
export type SignFieldWithTokenOptions = { export type SignFieldWithTokenOptions = {
@ -169,13 +171,24 @@ export const signFieldWithToken = async ({
} }
} }
const derivedRecipientActionAuth = await validateFieldAuth({ const isEnterprise = userId ? await isUserEnterprise({ userId }) : false;
documentAuthOptions: document.authOptions, let requiredAuthType: TRecipientActionAuthTypes | null = null;
recipient,
field, if (isEnterprise) {
userId, requiredAuthType = await validateFieldAuth({
authOptions, documentAuthOptions: document.authOptions,
}); recipient,
field,
userId,
authOptions,
});
} else {
const { derivedRecipientActionAuth } = extractDocumentAuthMethods({
documentAuth: document.authOptions,
recipientAuth: recipient.authOptions,
});
requiredAuthType = derivedRecipientActionAuth;
}
const documentMeta = await prisma.documentMeta.findFirst({ const documentMeta = await prisma.documentMeta.findFirst({
where: { where: {
@ -286,9 +299,9 @@ export const signFieldWithToken = async ({
}), }),
) )
.exhaustive(), .exhaustive(),
fieldSecurity: derivedRecipientActionAuth fieldSecurity: requiredAuthType
? { ? {
type: derivedRecipientActionAuth, type: requiredAuthType,
} }
: undefined, : undefined,
}, },

View File

@ -68,6 +68,16 @@ export const ZDocumentActionAuthTypesSchema = z
'The type of authentication required for the recipient to sign the document. This field is restricted to Enterprise plan users only.', 'The type of authentication required for the recipient to sign the document. This field is restricted to Enterprise plan users only.',
); );
/**
* The non-enterprise document action auth methods.
*
* Only includes options available to non-enterprise users.
*/
export const ZNonEnterpriseDocumentActionAuthTypesSchema = z.enum([
DocumentAuth.TWO_FACTOR_AUTH,
DocumentAuth.EXPLICIT_NONE,
]);
/** /**
* The recipient access auth methods. * The recipient access auth methods.
* *
@ -102,6 +112,7 @@ export const ZRecipientActionAuthTypesSchema = z
export const DocumentAccessAuth = ZDocumentAccessAuthTypesSchema.Enum; export const DocumentAccessAuth = ZDocumentAccessAuthTypesSchema.Enum;
export const DocumentActionAuth = ZDocumentActionAuthTypesSchema.Enum; export const DocumentActionAuth = ZDocumentActionAuthTypesSchema.Enum;
export const NonEnterpriseDocumentActionAuth = ZNonEnterpriseDocumentActionAuthTypesSchema.Enum;
export const RecipientAccessAuth = ZRecipientAccessAuthTypesSchema.Enum; export const RecipientAccessAuth = ZRecipientAccessAuthTypesSchema.Enum;
export const RecipientActionAuth = ZRecipientActionAuthTypesSchema.Enum; export const RecipientActionAuth = ZRecipientActionAuthTypesSchema.Enum;
@ -152,6 +163,9 @@ export type TDocumentAccessAuth = z.infer<typeof ZDocumentAccessAuthSchema>;
export type TDocumentAccessAuthTypes = z.infer<typeof ZDocumentAccessAuthTypesSchema>; export type TDocumentAccessAuthTypes = z.infer<typeof ZDocumentAccessAuthTypesSchema>;
export type TDocumentActionAuth = z.infer<typeof ZDocumentActionAuthSchema>; export type TDocumentActionAuth = z.infer<typeof ZDocumentActionAuthSchema>;
export type TDocumentActionAuthTypes = z.infer<typeof ZDocumentActionAuthTypesSchema>; export type TDocumentActionAuthTypes = z.infer<typeof ZDocumentActionAuthTypesSchema>;
export type TNonEnterpriseDocumentActionAuthTypes = z.infer<
typeof ZNonEnterpriseDocumentActionAuthTypesSchema
>;
export type TRecipientAccessAuth = z.infer<typeof ZRecipientAccessAuthSchema>; export type TRecipientAccessAuth = z.infer<typeof ZRecipientAccessAuthSchema>;
export type TRecipientAccessAuthTypes = z.infer<typeof ZRecipientAccessAuthTypesSchema>; export type TRecipientAccessAuthTypes = z.infer<typeof ZRecipientAccessAuthTypesSchema>;
export type TRecipientActionAuth = z.infer<typeof ZRecipientActionAuthSchema>; export type TRecipientActionAuth = z.infer<typeof ZRecipientActionAuthSchema>;

View File

@ -7,7 +7,11 @@ import type { SelectProps } from '@radix-ui/react-select';
import { InfoIcon } from 'lucide-react'; import { InfoIcon } from 'lucide-react';
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth'; import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
import { DocumentActionAuth, DocumentAuth } from '@documenso/lib/types/document-auth'; import {
DocumentActionAuth,
DocumentAuth,
NonEnterpriseDocumentActionAuth,
} from '@documenso/lib/types/document-auth';
import { import {
Select, Select,
SelectContent, SelectContent,
@ -17,38 +21,51 @@ import {
} from '@documenso/ui/primitives/select'; } from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
export const DocumentGlobalAuthActionSelect = forwardRef<HTMLButtonElement, SelectProps>( interface DocumentGlobalAuthActionSelectProps extends SelectProps {
(props, ref) => { isDocumentEnterprise?: boolean;
const { _ } = useLingui(); }
return ( export const DocumentGlobalAuthActionSelect = forwardRef<
<Select {...props}> HTMLButtonElement,
<SelectTrigger className="bg-background text-muted-foreground"> DocumentGlobalAuthActionSelectProps
<SelectValue >(({ isDocumentEnterprise, ...props }, ref) => {
ref={ref} const { _ } = useLingui();
data-testid="documentActionSelectValue"
placeholder={_(msg`No restrictions`)}
/>
</SelectTrigger>
<SelectContent position="popper"> return (
{/* Note: -1 is remapped in the Zod schema to the required value. */} <Select {...props}>
<SelectItem value={'-1'}> <SelectTrigger className="bg-background text-muted-foreground">
<Trans>No restrictions</Trans> <SelectValue
</SelectItem> ref={ref}
data-testid="documentActionSelectValue"
placeholder={_(msg`No restrictions`)}
/>
</SelectTrigger>
{Object.values(DocumentActionAuth) <SelectContent position="popper">
.filter((auth) => auth !== DocumentAuth.ACCOUNT) {/* Note: -1 is remapped in the Zod schema to the required value. */}
.map((authType) => ( <SelectItem value={'-1'}>
<SelectItem key={authType} value={authType}> <Trans>No restrictions</Trans>
{DOCUMENT_AUTH_TYPES[authType].value} </SelectItem>
</SelectItem>
))} {isDocumentEnterprise
</SelectContent> ? Object.values(DocumentActionAuth)
</Select> .filter((auth) => auth !== DocumentAuth.ACCOUNT)
); .map((authType) => (
}, <SelectItem key={authType} value={authType}>
); {DOCUMENT_AUTH_TYPES[authType].value}
</SelectItem>
))
: Object.values(NonEnterpriseDocumentActionAuth)
.filter((auth) => auth !== DocumentAuth.EXPLICIT_NONE)
.map((authType) => (
<SelectItem key={authType} value={authType}>
{DOCUMENT_AUTH_TYPES[authType].value}
</SelectItem>
))}
</SelectContent>
</Select>
);
});
DocumentGlobalAuthActionSelect.displayName = 'DocumentGlobalAuthActionSelect'; DocumentGlobalAuthActionSelect.displayName = 'DocumentGlobalAuthActionSelect';

View File

@ -1,10 +1,15 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro'; import { Trans, useLingui } from '@lingui/react/macro';
import { useLingui } from '@lingui/react/macro'; import {
import { DocumentVisibility, TeamMemberRole } from '@prisma/client'; DocumentStatus,
import { DocumentStatus, type Field, type Recipient, SendStatus } from '@prisma/client'; DocumentVisibility,
type Field,
type Recipient,
SendStatus,
TeamMemberRole,
} from '@prisma/client';
import { InfoIcon } from 'lucide-react'; import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
@ -268,24 +273,22 @@ export const AddSettingsFormPartial = ({
/> />
)} )}
{isDocumentEnterprise && ( <FormField
<FormField control={form.control}
control={form.control} name="globalActionAuth"
name="globalActionAuth" render={({ field }) => (
render={({ field }) => ( <FormItem>
<FormItem> <FormLabel className="flex flex-row items-center">
<FormLabel className="flex flex-row items-center"> <Trans>Recipient action authentication</Trans>
<Trans>Recipient action authentication</Trans> <DocumentGlobalAuthActionTooltip />
<DocumentGlobalAuthActionTooltip /> </FormLabel>
</FormLabel>
<FormControl> <FormControl>
<DocumentGlobalAuthActionSelect {...field} onValueChange={field.onChange} /> <DocumentGlobalAuthActionSelect {...field} onValueChange={field.onChange} />
</FormControl> </FormControl>
</FormItem> </FormItem>
)} )}
/> />
)}
<Accordion type="multiple" className="mt-6"> <Accordion type="multiple" className="mt-6">
<AccordionItem value="advanced-options" className="border-none"> <AccordionItem value="advanced-options" className="border-none">