mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
fix: polish
This commit is contained in:
@ -1,7 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@ -22,6 +20,8 @@ import {
|
|||||||
} from '@documenso/ui/primitives/form/form';
|
} from '@documenso/ui/primitives/form/form';
|
||||||
import { Input } from '@documenso/ui/primitives/input';
|
import { Input } from '@documenso/ui/primitives/input';
|
||||||
|
|
||||||
|
import { EnableAuthenticatorAppDialog } from '~/components/forms/2fa/enable-authenticator-app-dialog';
|
||||||
|
|
||||||
import { useRequiredDocumentAuthContext } from './document-auth-provider';
|
import { useRequiredDocumentAuthContext } from './document-auth-provider';
|
||||||
|
|
||||||
export type DocumentActionAuth2FAProps = {
|
export type DocumentActionAuth2FAProps = {
|
||||||
@ -58,6 +58,7 @@ export const DocumentActionAuth2FA = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [is2FASetupSuccessful, setIs2FASetupSuccessful] = useState(false);
|
||||||
const [formErrorCode, setFormErrorCode] = useState<string | null>(null);
|
const [formErrorCode, setFormErrorCode] = useState<string | null>(null);
|
||||||
|
|
||||||
const onFormSubmit = async ({ token }: T2FAAuthFormSchema) => {
|
const onFormSubmit = async ({ token }: T2FAAuthFormSchema) => {
|
||||||
@ -87,17 +88,29 @@ export const DocumentActionAuth2FA = ({
|
|||||||
token: '',
|
token: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setIs2FASetupSuccessful(false);
|
||||||
setFormErrorCode(null);
|
setFormErrorCode(null);
|
||||||
}, [open, form]);
|
|
||||||
|
|
||||||
if (!user?.twoFactorEnabled) {
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
if (!user?.twoFactorEnabled && !is2FASetupSuccessful) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Alert variant="warning">
|
<Alert variant="warning">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{recipient.role === RecipientRole.VIEWER && actionTarget === 'DOCUMENT'
|
<p>
|
||||||
? 'You need to setup 2FA to mark this document as viewed.'
|
{recipient.role === RecipientRole.VIEWER && actionTarget === 'DOCUMENT'
|
||||||
: `You need to setup 2FA to ${actionVerb.toLowerCase()} this ${actionTarget.toLowerCase()}.`}
|
? 'You need to setup 2FA to mark this document as viewed.'
|
||||||
|
: `You need to setup 2FA to ${actionVerb.toLowerCase()} this ${actionTarget.toLowerCase()}.`}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{user?.identityProvider === 'DOCUMENSO' && (
|
||||||
|
<p className="mt-2">
|
||||||
|
By enabling 2FA, you will be required to enter a code from your authenticator app
|
||||||
|
every time you sign in.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
@ -106,9 +119,7 @@ export const DocumentActionAuth2FA = ({
|
|||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button type="button" asChild>
|
<EnableAuthenticatorAppDialog onSuccess={() => setIs2FASetupSuccessful(true)} />
|
||||||
<Link href="/settings/security">Setup 2FA</Link>
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export const DocumentActionAuthAccount = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset disabled={isSigningOut} className="space-y-4">
|
<fieldset disabled={isSigningOut} className="space-y-4">
|
||||||
<Alert>
|
<Alert variant="warning">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{actionTarget === 'DOCUMENT' && recipient.role === RecipientRole.VIEWER ? (
|
{actionTarget === 'DOCUMENT' && recipient.role === RecipientRole.VIEWER ? (
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@ -137,10 +137,7 @@ export const DocumentActionAuthPasskey = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (passkeyData.isInitialLoading || (passkeyData.isError && passkeyData.passkeys.length === 0)) {
|
||||||
passkeyData.isInitialLoading ||
|
|
||||||
(passkeyData.isRefetching && passkeyData.passkeys.length === 0)
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-28 items-center justify-center">
|
<div className="flex h-28 items-center justify-center">
|
||||||
<Loader className="text-muted-foreground h-6 w-6 animate-spin" />
|
<Loader className="text-muted-foreground h-6 w-6 animate-spin" />
|
||||||
@ -171,7 +168,7 @@ export const DocumentActionAuthPasskey = ({
|
|||||||
if (passkeyData.passkeys.length === 0) {
|
if (passkeyData.passkeys.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Alert>
|
<Alert variant="warning">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{recipient.role === RecipientRole.VIEWER && actionTarget === 'DOCUMENT'
|
{recipient.role === RecipientRole.VIEWER && actionTarget === 'DOCUMENT'
|
||||||
? 'You need to setup a passkey to mark this document as viewed.'
|
? 'You need to setup a passkey to mark this document as viewed.'
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { MAXIMUM_PASSKEYS } from '@documenso/lib/constants/auth';
|
import { MAXIMUM_PASSKEYS } from '@documenso/lib/constants/auth';
|
||||||
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
|
|
||||||
import type {
|
import type {
|
||||||
TDocumentAuthOptions,
|
TDocumentAuthOptions,
|
||||||
TRecipientAccessAuthTypes,
|
TRecipientAccessAuthTypes,
|
||||||
@ -175,9 +174,11 @@ export const DocumentAuthProvider = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [passkeyData.passkeys]);
|
}, [passkeyData.passkeys]);
|
||||||
|
|
||||||
|
// Assume that a user must be logged in for any auth requirements.
|
||||||
const isAuthRedirectRequired = Boolean(
|
const isAuthRedirectRequired = Boolean(
|
||||||
DOCUMENT_AUTH_TYPES[derivedRecipientActionAuth || '']?.isAuthRedirectRequired &&
|
derivedRecipientActionAuth &&
|
||||||
!preCalculatedActionAuthOptions,
|
derivedRecipientActionAuth !== DocumentAuth.EXPLICIT_NONE &&
|
||||||
|
user?.email !== recipient.email,
|
||||||
);
|
);
|
||||||
|
|
||||||
const refetchPasskeys = async () => {
|
const refetchPasskeys = async () => {
|
||||||
|
|||||||
@ -42,10 +42,10 @@ export const SigningForm = ({ document, recipient, fields, redirectUrl }: Signin
|
|||||||
const { mutateAsync: completeDocumentWithToken } =
|
const { mutateAsync: completeDocumentWithToken } =
|
||||||
trpc.recipient.completeDocumentWithToken.useMutation();
|
trpc.recipient.completeDocumentWithToken.useMutation();
|
||||||
|
|
||||||
const {
|
const { handleSubmit, formState } = useForm();
|
||||||
handleSubmit,
|
|
||||||
formState: { isSubmitting },
|
// Keep the loading state going if successful since the redirect may take some time.
|
||||||
} = useForm();
|
const isSubmitting = formState.isSubmitting || formState.isSubmitSuccessful;
|
||||||
|
|
||||||
const uninsertedFields = useMemo(() => {
|
const uninsertedFields = useMemo(() => {
|
||||||
return sortFieldsByPosition(fields.filter((field) => !field.inserted));
|
return sortFieldsByPosition(fields.filter((field) => !field.inserted));
|
||||||
|
|||||||
@ -41,8 +41,13 @@ export const ZEnable2FAForm = z.object({
|
|||||||
|
|
||||||
export type TEnable2FAForm = z.infer<typeof ZEnable2FAForm>;
|
export type TEnable2FAForm = z.infer<typeof ZEnable2FAForm>;
|
||||||
|
|
||||||
export const EnableAuthenticatorAppDialog = () => {
|
export type EnableAuthenticatorAppDialogProps = {
|
||||||
|
onSuccess?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EnableAuthenticatorAppDialog = ({ onSuccess }: EnableAuthenticatorAppDialogProps) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@ -79,6 +84,7 @@ export const EnableAuthenticatorAppDialog = () => {
|
|||||||
const data = await enable2FA({ code: token });
|
const data = await enable2FA({ code: token });
|
||||||
|
|
||||||
setRecoveryCodes(data.recoveryCodes);
|
setRecoveryCodes(data.recoveryCodes);
|
||||||
|
onSuccess?.();
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: 'Two-factor authentication enabled',
|
title: 'Two-factor authentication enabled',
|
||||||
@ -89,7 +95,7 @@ export const EnableAuthenticatorAppDialog = () => {
|
|||||||
toast({
|
toast({
|
||||||
title: 'Unable to setup two-factor authentication',
|
title: 'Unable to setup two-factor authentication',
|
||||||
description:
|
description:
|
||||||
'We were unable to setup two-factor authentication for your account. Please ensure that you have entered your password correctly and try again.',
|
'We were unable to setup two-factor authentication for your account. Please ensure that you have entered your code correctly and try again.',
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,21 +4,12 @@ import { DocumentAuth } from '../types/document-auth';
|
|||||||
type DocumentAuthTypeData = {
|
type DocumentAuthTypeData = {
|
||||||
key: TDocumentAuth;
|
key: TDocumentAuth;
|
||||||
value: string;
|
value: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this authentication event will require the user to halt and
|
|
||||||
* redirect.
|
|
||||||
*
|
|
||||||
* Defaults to false.
|
|
||||||
*/
|
|
||||||
isAuthRedirectRequired?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DOCUMENT_AUTH_TYPES: Record<string, DocumentAuthTypeData> = {
|
export const DOCUMENT_AUTH_TYPES: Record<string, DocumentAuthTypeData> = {
|
||||||
[DocumentAuth.ACCOUNT]: {
|
[DocumentAuth.ACCOUNT]: {
|
||||||
key: DocumentAuth.ACCOUNT,
|
key: DocumentAuth.ACCOUNT,
|
||||||
value: 'Require account',
|
value: 'Require account',
|
||||||
isAuthRedirectRequired: true,
|
|
||||||
},
|
},
|
||||||
[DocumentAuth.PASSKEY]: {
|
[DocumentAuth.PASSKEY]: {
|
||||||
key: DocumentAuth.PASSKEY,
|
key: DocumentAuth.PASSKEY,
|
||||||
|
|||||||
Reference in New Issue
Block a user