diff --git a/app/components/OneTimePasswordInput.tsx b/app/components/OneTimePasswordInput.tsx index ab9a7506e9..db660f5a6c 100644 --- a/app/components/OneTimePasswordInput.tsx +++ b/app/components/OneTimePasswordInput.tsx @@ -6,15 +6,30 @@ import { s } from "@shared/styles"; type Props = React.ComponentProps & { /** The length of the OTP */ length?: number; + /** + * Whether to accept uppercase letters in addition to digits. Lowercase input + * is normalized to uppercase. Defaults to numeric only. + */ + alphanumeric?: boolean; }; +const sanitizeAlphanumeric = (value: string) => + value.replace(/[^a-zA-Z0-9]/g, "").toUpperCase(); + export const OneTimePasswordInput = React.forwardRef( function OneTimePasswordInput_( - { length = 6, ...rest }: Props, + { length = 6, alphanumeric, ...rest }: Props, ref: React.RefObject ) { + const alphanumericProps = alphanumeric + ? { + validationType: "none" as const, + sanitizeValue: sanitizeAlphanumeric, + } + : undefined; + return ( - + {Array.from({ length }, (_, i) => ( ))} diff --git a/app/scenes/TeamDelete.tsx b/app/scenes/TeamDelete.tsx index bbf87e3e67..8a038d6682 100644 --- a/app/scenes/TeamDelete.tsx +++ b/app/scenes/TeamDelete.tsx @@ -1,11 +1,11 @@ import { observer } from "mobx-react"; import * as React from "react"; -import { useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; import { useTranslation, Trans } from "react-i18next"; import { toast } from "sonner"; import Button from "~/components/Button"; import Flex from "~/components/Flex"; -import Input from "~/components/Input"; +import { OneTimePasswordInput } from "~/components/OneTimePasswordInput"; import Text from "~/components/Text"; import env from "~/env"; import useCurrentTeam from "~/hooks/useCurrentTeam"; @@ -25,7 +25,7 @@ function TeamDelete({ onSubmit }: Props) { const team = useCurrentTeam({ rejectOnEmpty: false }); const { t } = useTranslation(); const { - register, + control, handleSubmit: formHandleSubmit, formState, } = useForm(); @@ -62,9 +62,6 @@ function TeamDelete({ onSubmit }: Props) { [auth, onSubmit] ); - const inputProps = register("code", { - required: env.EMAIL_ENABLED, - }); const appName = env.APP_NAME; const workspaceName = team?.name; @@ -78,13 +75,27 @@ function TeamDelete({ onSubmit }: Props) { enter the code below to permanently destroy this workspace. - ( + + )} /> ) : ( diff --git a/app/scenes/UserDelete.tsx b/app/scenes/UserDelete.tsx index f9a5820af9..914e14e4b5 100644 --- a/app/scenes/UserDelete.tsx +++ b/app/scenes/UserDelete.tsx @@ -1,11 +1,11 @@ import { observer } from "mobx-react"; import * as React from "react"; -import { useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; import { useTranslation, Trans } from "react-i18next"; import { toast } from "sonner"; import Button from "~/components/Button"; import Flex from "~/components/Flex"; -import Input from "~/components/Input"; +import { OneTimePasswordInput } from "~/components/OneTimePasswordInput"; import Text from "~/components/Text"; import env from "~/env"; import useStores from "~/hooks/useStores"; @@ -24,7 +24,7 @@ function UserDelete({ onSubmit }: Props) { const { auth } = useStores(); const { t } = useTranslation(); const { - register, + control, handleSubmit: formHandleSubmit, formState, } = useForm(); @@ -61,9 +61,6 @@ function UserDelete({ onSubmit }: Props) { [auth, onSubmit] ); - const inputProps = register("code", { - required: env.EMAIL_ENABLED, - }); const appName = env.APP_NAME; return ( @@ -76,13 +73,27 @@ function UserDelete({ onSubmit }: Props) { enter the code below to permanently destroy your account. - ( + + )} /> ) : ( diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index aa8dc54192..483f33162b 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -1401,7 +1401,6 @@ "No templates have been created yet": "No templates have been created yet", "{{ teamName }} is using {{ appName }} to share documents, please login to continue.": "{{ teamName }} is using {{ appName }} to share documents, please login to continue.", "A confirmation code has been sent to your email address, please enter the code below to permanently destroy this workspace.": "A confirmation code has been sent to your email address, please enter the code below to permanently destroy this workspace.", - "Confirmation code": "Confirmation code", "Deleting the <1>{{workspaceName}} workspace will destroy all collections, documents, users, and associated data. You will be immediately logged out of {{appName}}.": "Deleting the <1>{{workspaceName}} workspace will destroy all collections, documents, users, and associated data. You will be immediately logged out of {{appName}}.", "Please note that workspaces are completely separated. They can have a different domain, settings, users, and billing.": "Please note that workspaces are completely separated. They can have a different domain, settings, users, and billing.", "You are creating a new workspace using your current account — {{email}}": "You are creating a new workspace using your current account — {{email}}",