diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index a2fd9454e31..3157bdd66ec 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -63,6 +63,7 @@ "flat": "^6.0.1", "framer-motion": "11.9.0", "fuse.js": "7.0.0", + "input-otp": "^1.2.4", "ioredis": "^5.4.1", "ipaddr.js": "^2.2.0", "lottie-react": "^2.4.0", @@ -87,7 +88,6 @@ "react-icons": "^5.2.1", "react-intersection-observer": "^9.10.3", "react-markdown": "^9.0.1", - "react-otp-input": "^3.1.1", "react-responsive-carousel": "^3.2.23", "react-table": "^7.8.0", "recharts": "^2.12.7", diff --git a/apps/dashboard/src/@/components/ui/input-otp.tsx b/apps/dashboard/src/@/components/ui/input-otp.tsx new file mode 100644 index 00000000000..832bc62a51d --- /dev/null +++ b/apps/dashboard/src/@/components/ui/input-otp.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { OTPInput, OTPInputContext } from "input-otp"; +import { Dot } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const InputOTP = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, containerClassName, ...props }, ref) => ( + +)); +InputOTP.displayName = "InputOTP"; + +const InputOTPGroup = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ className, ...props }, ref) => ( +
+)); +InputOTPGroup.displayName = "InputOTPGroup"; + +const InputOTPSlot = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> & { index: number } +>(({ index, className, ...props }, ref) => { + const inputOTPContext = React.useContext(OTPInputContext); + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]; + + return ( +
+ {char} + {hasFakeCaret && ( +
+
+
+ )} +
+ ); +}); +InputOTPSlot.displayName = "InputOTPSlot"; + +const InputOTPSeparator = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ ...props }, ref) => ( + // biome-ignore lint/a11y/useFocusableInteractive: +
+ +
+)); +InputOTPSeparator.displayName = "InputOTPSeparator"; + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }; diff --git a/apps/dashboard/src/components/onboarding/ConfirmEmail.tsx b/apps/dashboard/src/components/onboarding/ConfirmEmail.tsx index 8e8ee329a8a..f1dc51ec773 100644 --- a/apps/dashboard/src/components/onboarding/ConfirmEmail.tsx +++ b/apps/dashboard/src/components/onboarding/ConfirmEmail.tsx @@ -1,9 +1,15 @@ +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "@/components/ui/input-otp"; +import { cn } from "@/lib/utils"; import { useConfirmEmail, useResendEmailConfirmation, } from "@3rdweb-sdk/react/hooks/useApi"; import { useLoggedInUser } from "@3rdweb-sdk/react/hooks/useLoggedInUser"; -import { Flex, Input } from "@chakra-ui/react"; +import { Flex } from "@chakra-ui/react"; import { zodResolver } from "@hookform/resolvers/zod"; import { type EmailConfirmationValidationSchema, @@ -12,9 +18,9 @@ import { import { useErrorHandler } from "contexts/error-handler"; import { useTrack } from "hooks/analytics/useTrack"; import { useTxNotifications } from "hooks/useTxNotifications"; -import { type ClipboardEvent, useState } from "react"; +import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"; +import { useState } from "react"; import { useForm } from "react-hook-form"; -import OtpInput from "react-otp-input"; import { Button, Text } from "tw-components"; import { shortenString } from "utils/usedapp-external"; import { TitleAndDescription } from "./Title"; @@ -148,14 +154,6 @@ const OnboardingConfirmEmail: React.FC = ({ }); }; - const handlePaste = (e: ClipboardEvent) => { - const data = e.clipboardData.getData("text"); - if (data?.match(/^[A-Z]{6}$/)) { - form.setValue("confirmationToken", data); - handleSubmit(); - } - }; - return ( <> = ({ {!completed && (
- ( - - )} - /> + disabled={saving} + > + + {new Array(6).fill(0).map((_, idx) => ( + + key={idx} + index={idx} + className={cn("h-12 grow text-lg", { + "border-red-500": form.getFieldState( + "confirmationToken", + form.formState, + ).error, + })} + /> + ))} + +