From 32412e18080bd494fd6a4194a0430ab98f825bdc Mon Sep 17 00:00:00 2001 From: Sinan Date: Fri, 16 Aug 2024 23:19:54 +0530 Subject: [PATCH] otp design added --- client/app/patient/signin/page.tsx | 4 +- .../forms/{SiigninForm.tsx => SigninForm.tsx} | 0 .../modles/OtpVerificationModel.tsx | 79 ++++++++++ client/components/ui/alert-dialog.tsx | 141 ++++++++++++++++++ client/components/ui/input-otp.tsx | 71 +++++++++ client/package-lock.json | 73 +++++++++ client/package.json | 2 + 7 files changed, 369 insertions(+), 1 deletion(-) rename client/components/forms/{SiigninForm.tsx => SigninForm.tsx} (100%) create mode 100644 client/components/modles/OtpVerificationModel.tsx create mode 100644 client/components/ui/alert-dialog.tsx create mode 100644 client/components/ui/input-otp.tsx diff --git a/client/app/patient/signin/page.tsx b/client/app/patient/signin/page.tsx index 26ac57cc..d6aff359 100644 --- a/client/app/patient/signin/page.tsx +++ b/client/app/patient/signin/page.tsx @@ -1,10 +1,12 @@ -import SigninForm from "@/components/forms/SiigninForm"; +import SigninForm from "@/components/forms/SigninForm"; +import OtpVerificationModel from "@/components/modles/OtpVerificationModel"; import Image from "next/image"; import Link from "next/link"; const SignIn = ()=> { return (
+
{ + const router = useRouter(); + const path = usePathname(); + const [open, setOpen] = useState(true); + const [passkey, setPassKey] = useState(""); + const [error, setError] = useState(""); + + const closeModal = () => { + setOpen(false); + router.push(`/`); + } + + return ( + + + + +

Access Verification

+ close-icon closeModal()} + className="cursor-pointer" + /> +
+ + To access the admin page, please enter the passkey. + +
+
+ setPassKey(value)} + > + + + + + + + + + + {error &&

{error}

} +
+ + Verify Passkey + +
+
+ ); +} + +export default OtpVerificationModel; \ No newline at end of file diff --git a/client/components/ui/alert-dialog.tsx b/client/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..25e7b474 --- /dev/null +++ b/client/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/client/components/ui/input-otp.tsx b/client/components/ui/input-otp.tsx new file mode 100644 index 00000000..f66fcfa0 --- /dev/null +++ b/client/components/ui/input-otp.tsx @@ -0,0 +1,71 @@ +"use client" + +import * as React from "react" +import { OTPInput, OTPInputContext } from "input-otp" +import { Dot } from "lucide-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) => ( +
+ +
+)) +InputOTPSeparator.displayName = "InputOTPSeparator" + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } diff --git a/client/package-lock.json b/client/package-lock.json index 5718eb8d..116f9ec7 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^3.9.0", + "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-radio-group": "^1.2.0", @@ -17,6 +18,7 @@ "@reduxjs/toolkit": "^2.2.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "input-otp": "^1.2.4", "lucide-react": "^0.427.0", "next": "14.2.5", "next-themes": "^0.3.0", @@ -484,6 +486,33 @@ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.1.tgz", + "integrity": "sha512-wmCoJwj7byuVuiLKqDLlX7ClSUU0vd9sdCeM+2Ls+uf13+cpSJoMgwysHq1SGVVkJj5Xn0XWi1NoRCdkMpr6Mw==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dialog": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", @@ -588,6 +617,41 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz", + "integrity": "sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -3182,6 +3246,15 @@ "prop-types": "^15.8.1" } }, + "node_modules/input-otp": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.2.4.tgz", + "integrity": "sha512-md6rhmD+zmMnUh5crQNSQxq3keBRYvE3odbr4Qb9g2NWzQv9azi+t1a3X4TBTbh98fsGHgEEJlzbe1q860uGCA==", + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", diff --git a/client/package.json b/client/package.json index 292ae89a..843e0bdd 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.0", + "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-radio-group": "^1.2.0", @@ -19,6 +20,7 @@ "@reduxjs/toolkit": "^2.2.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "input-otp": "^1.2.4", "lucide-react": "^0.427.0", "next": "14.2.5", "next-themes": "^0.3.0",