{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "auth-two-factor-challenge-page", "dependencies": [ "@inertiajs/react" ], "registryDependencies": [ "@launch/auth-layout", "@launch/form-errors", "@launch/input-error", "@launch/use-two-factor-auth", "button", "input", "label", "spinner" ], "files": [ { "path": "registry/new-york-v4/pages/auth/two-factor-challenge.tsx", "content": "import { Form, Head, setLayoutProps } from \"@inertiajs/react\";\nimport { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from \"input-otp\";\nimport { useMemo, useState } from \"react\";\nimport InputError from \"@/components/input-error\";\nimport { Button } from \"@/components/ui/button\";\nimport { toastFirstFormError } from \"@/lib/form-errors\";\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from \"@/components/ui/input-otp\";\nimport { Spinner } from \"@/components/ui/spinner\";\nimport { OTP_MAX_LENGTH } from \"@/hooks/use-two-factor-auth\";\nimport { store } from \"@/routes/two-factor/login\";\n\nconst RECOVERY_CODE_LENGTH = 20;\nconst formatRecoveryCode = (value: string): string =>\n value.length > 10 ? `${value.slice(0, 10)}-${value.slice(10)}` : value;\n\nconst normalizeAuthenticationCode = (value: string): string => value.replace(/\\D/g, \"\");\nconst normalizeRecoveryCode = (value: string): string => value.replace(/[^a-zA-Z0-9]/g, \"\");\n\nexport default function TwoFactorChallenge() {\n const [showRecoveryInput, setShowRecoveryInput] = useState(false);\n const [code, setCode] = useState(\"\");\n const [recoveryCode, setRecoveryCode] = useState(\"\");\n\n const authConfigContent = useMemo<{\n title: string;\n description: string;\n toggleText: string;\n }>(() => {\n if (showRecoveryInput) {\n return {\n title: \"Recovery code\",\n description:\n \"Please confirm access to your account by entering one of your emergency recovery codes.\",\n toggleText: \"login using an authentication code\",\n };\n }\n\n return {\n title: \"Authentication code\",\n description: \"Enter the authentication code provided by your authenticator application.\",\n toggleText: \"login using a recovery code\",\n };\n }, [showRecoveryInput]);\n\n setLayoutProps({\n title: authConfigContent.title,\n description: authConfigContent.description,\n });\n\n const toggleRecoveryMode = (clearErrors: () => void): void => {\n setShowRecoveryInput(!showRecoveryInput);\n clearErrors();\n setCode(\"\");\n setRecoveryCode(\"\");\n };\n\n const formattedRecoveryCode = formatRecoveryCode(recoveryCode);\n\n const renderRecoverySlots = (startIndex: number, length: number) =>\n Array.from({ length }, (_, index) => (\n \n ));\n\n return (\n <>\n \n\n
\n {\n toastFirstFormError(errors);\n\n if (showRecoveryInput) {\n setRecoveryCode(\"\");\n\n return;\n }\n\n setCode(\"\");\n }}\n className=\"flex flex-col gap-5\"\n >\n {({ processing, errors, clearErrors }) => (\n <>\n {showRecoveryInput ? (\n
\n \n
\n setRecoveryCode(normalizeRecoveryCode(value))}\n disabled={processing}\n pattern={REGEXP_ONLY_DIGITS_AND_CHARS}\n pasteTransformer={normalizeRecoveryCode}\n autoFocus\n containerClassName=\"flex flex-wrap justify-center gap-2\"\n >\n \n {renderRecoverySlots(0, RECOVERY_CODE_LENGTH / 2)}\n \n \n {renderRecoverySlots(RECOVERY_CODE_LENGTH / 2, RECOVERY_CODE_LENGTH / 2)}\n \n \n
\n \n
\n ) : (\n
\n
\n setCode(normalizeAuthenticationCode(value))}\n disabled={processing}\n pattern={REGEXP_ONLY_DIGITS}\n pasteTransformer={normalizeAuthenticationCode}\n autoFocus\n >\n \n {Array.from({ length: OTP_MAX_LENGTH }, (_, index) => (\n \n ))}\n \n \n
\n \n
\n )}\n\n \n\n
\n or you can \n toggleRecoveryMode(clearErrors)}\n >\n {authConfigContent.toggleText}\n \n
\n \n )}\n \n
\n \n );\n}\n\nTwoFactorChallenge.layout = {\n title: \"Authentication code\",\n description: \"Enter the authentication code provided by your authenticator application.\",\n};\n", "type": "registry:file", "target": "resources/js/pages/auth/two-factor-challenge.tsx" } ], "type": "registry:block" }