chore: add pin input to all 2FA components

Adds the pin input to the currently used 2FA components sunsetting
the standard input that was previously used.
This commit is contained in:
Lucas Smith
2024-05-24 03:31:19 +00:00
parent 0985206088
commit 9cb80aa0bc
6 changed files with 56 additions and 14 deletions

View File

@ -18,7 +18,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { EnableAuthenticatorAppDialog } from '~/components/forms/2fa/enable-authenticator-app-dialog'; import { EnableAuthenticatorAppDialog } from '~/components/forms/2fa/enable-authenticator-app-dialog';
@ -138,7 +138,15 @@ export const DocumentActionAuth2FA = ({
<FormLabel required>2FA token</FormLabel> <FormLabel required>2FA token</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="Token" /> <PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />

View File

@ -28,7 +28,7 @@ import {
FormItem, FormItem,
FormMessage, FormMessage,
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZDisable2FAForm = z.object({ export const ZDisable2FAForm = z.object({
@ -107,7 +107,15 @@ export const DisableAuthenticatorAppDialog = () => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<Input {...field} placeholder="Token" /> <PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -30,7 +30,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { RecoveryCodeList } from './recovery-code-list'; import { RecoveryCodeList } from './recovery-code-list';
@ -212,7 +212,15 @@ export const EnableAuthenticatorAppDialog = ({ onSuccess }: EnableAuthenticatorA
<FormItem> <FormItem>
<FormLabel className="text-muted-foreground">Token</FormLabel> <FormLabel className="text-muted-foreground">Token</FormLabel>
<FormControl> <FormControl>
<Input {...field} type="text" value={field.value ?? ''} /> <PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -30,7 +30,7 @@ import {
FormItem, FormItem,
FormMessage, FormMessage,
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { RecoveryCodeList } from './recovery-code-list'; import { RecoveryCodeList } from './recovery-code-list';
@ -115,7 +115,15 @@ export const ViewRecoveryCodesDialog = () => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<Input {...field} placeholder="Token" /> <PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -38,6 +38,7 @@ 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 { PasswordInput } from '@documenso/ui/primitives/password-input'; import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
const ERROR_MESSAGES: Partial<Record<keyof typeof ErrorCode, string>> = { const ERROR_MESSAGES: Partial<Record<keyof typeof ErrorCode, string>> = {
@ -372,9 +373,17 @@ export const SignInForm = ({ className, initialEmail, isGoogleSSOEnabled }: Sign
name="totpCode" name="totpCode"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Authentication Token</FormLabel> <FormLabel>Token</FormLabel>
<FormControl> <FormControl>
<Input type="text" {...field} /> <PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -2,9 +2,10 @@
import * as React from 'react'; import * as React from 'react';
import { cn } from '@/lib/utils';
import { DashIcon } from '@radix-ui/react-icons';
import { OTPInput, OTPInputContext } from 'input-otp'; import { OTPInput, OTPInputContext } from 'input-otp';
import { Minus } from 'lucide-react';
import { cn } from '../lib/utils';
const PinInput = React.forwardRef< const PinInput = React.forwardRef<
React.ElementRef<typeof OTPInput>, React.ElementRef<typeof OTPInput>,
@ -43,7 +44,7 @@ const PinInputSlot = React.forwardRef<
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
'border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md', 'border-input relative flex h-10 w-10 items-center justify-center border-y border-r font-mono shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md',
isActive && 'ring-ring z-10 ring-1', isActive && 'ring-ring z-10 ring-1',
className, className,
)} )}
@ -66,7 +67,7 @@ const PinInputSeparator = React.forwardRef<
React.ComponentPropsWithoutRef<'div'> React.ComponentPropsWithoutRef<'div'>
>(({ ...props }, ref) => ( >(({ ...props }, ref) => (
<div ref={ref} role="separator" {...props}> <div ref={ref} role="separator" {...props}>
<DashIcon /> <Minus className="h-5 w-5" />
</div> </div>
)); ));