import { Button } from '@documenso/ui/primitives/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@documenso/ui/primitives/dialog'; import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input'; import { Trans } from '@lingui/react/macro'; import type React from 'react'; import { useState } from 'react'; import { type FieldValues, type Path, useFormContext } from 'react-hook-form'; import { z } from 'zod'; /** * Schema for forms that accept a two factor code. Compose with `.extend()` or `.merge()`. */ export const ZTwoFactorCodeFieldSchema = z.object({ totpCode: z.string().trim().optional(), backupCode: z.string().trim().optional(), }); export type TTwoFactorCodeFieldSchema = z.infer; export const hasTwoFactorCode = (data: TTwoFactorCodeFieldSchema) => !!data.totpCode || !!data.backupCode; type TwoFactorMethod = 'totp' | 'backup'; export type TwoFactorCodeDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; isSubmitting?: boolean; submitLabel: React.ReactNode; /** * Called when the user submits the code. Typically the parent form's submit handler. */ onSubmit: () => void; }; /** * Collects a TOTP or backup code on top of an existing form, mirroring the * sign in and disable 2FA dialogs. * * Must be rendered inside a `
` whose values include `totpCode` and `backupCode`. */ export const TwoFactorCodeDialog = ({ open, onOpenChange, isSubmitting, submitLabel, onSubmit, }: TwoFactorCodeDialogProps) => { const form = useFormContext(); const [method, setMethod] = useState('totp'); const totpCodeName = 'totpCode' as Path; const backupCodeName = 'backupCode' as Path; const onToggleMethod = () => { form.resetField(totpCodeName); form.resetField(backupCodeName); setMethod((current) => (current === 'totp' ? 'backup' : 'totp')); }; const handleOpenChange = (value: boolean) => { if (isSubmitting) { return; } if (!value) { form.resetField(totpCodeName); form.resetField(backupCodeName); setMethod('totp'); } onOpenChange(value); }; return ( Two-Factor Authentication {method === 'totp' ? ( Enter the code from your authenticator app to continue. ) : ( Enter one of your backup codes to continue. )}
{method === 'totp' && ( ( {Array(6) .fill(null) .map((_, i) => ( ))} )} /> )} {method === 'backup' && ( ( Backup Code )} /> )}
); };