diff --git a/scruter-nextjs/actions/seller/signup-action.tsx b/scruter-nextjs/actions/seller/signup-action.tsx new file mode 100644 index 00000000..133e0553 --- /dev/null +++ b/scruter-nextjs/actions/seller/signup-action.tsx @@ -0,0 +1,52 @@ +"use server" +import { generateAndSendOTP } from '@/lib/auth'; +import prismadb from '@/lib/prismadb'; +import { Prisma, Seller } from '@prisma/client'; + +export async function SellerCreate({ + name,email +}:{ + name:string, + email:string +}): Promise<{ success: boolean; error?: string ; data?:Seller}> { + + const exitingSeller = await prismadb.seller.findUnique({ + where: { + email: email, + }, + }); + + if (exitingSeller) { + return { + success: false, + error: 'seller already exists', + }; + } + + try { + const res = await prismadb.seller.create({ + data: { + name: name, + email: email, + }, + }); + + if(!res){ + return {success:false, error:"Error occured in seller creation"}; + } + + const resp=await generateAndSendOTP(res.email,"seller"); + + if(!resp){ + return {success:false, error:"Error occured in sending otp"}; + } + + return { success: true , data:res}; + + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + console.log(err.message); + } + return { success: false, error: 'An unexpected error occurred.' }; + } +} diff --git a/scruter-nextjs/app/(routes)/auth/components/otp-form.tsx b/scruter-nextjs/app/(routes)/auth/components/otp-form.tsx new file mode 100644 index 00000000..0ef1d44d --- /dev/null +++ b/scruter-nextjs/app/(routes)/auth/components/otp-form.tsx @@ -0,0 +1,135 @@ +'use client'; + +import { + InputOTP, + InputOTPGroup, + InputOTPSeparator, + InputOTPSlot, +} from '@/components/ui/input-otp'; + +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, + } from "@/components/ui/form"; + +import { z } from "zod"; + +import * as React from 'react'; + +import { cn } from '@/lib/utils'; +import { Icons } from '@/components/ui/icons'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { Toaster, toast } from 'react-hot-toast'; +import { SellerCreate } from '@/actions/seller/signup-action'; +import { zodResolver } from "@hookform/resolvers/zod"; + +import { signIn } from "next-auth/react"; +import { useForm } from 'react-hook-form'; +import { ChevronLeftCircleIcon } from 'lucide-react'; + +interface UserAuthFormProps extends React.HTMLAttributes { + roleType: 'user' | 'seller'; + email:string; + setOtpOpen:(otp:boolean)=>void +} + +const FormSchema = z.object({ + pin: z.string().min(6, { + message: "Your one-time password must be 6 characters.", + }), + }); + + +export function OtpForm({ + className, + roleType, + email, + setOtpOpen, + ...props +}: UserAuthFormProps) { + const [isLoading, setIsLoading] = React.useState(false); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + pin: "", + }, + }); + + async function onOTPSubmit(data: z.infer) { + // console.log(data.pin+email); + setIsLoading(true); + + // toast.success(data.pin) + const result = await signIn("credentials", { + email, + otp: data.pin, + role: "seller", + redirect: false, + }); + console.log(result); + if (!result?.ok) { + toast.error("Invalid email or otp"); + } else { + toast.success(`Welcome!`); + + // setTimeout(() => { + // window.location.href = `"/`; // Redirect on success + // }, 2000); + } + setIsLoading(false); + } + + + return ( +
+ +
+ + ( + + + {setOtpOpen(false)}} className="h-5 w-5"/> + One-Time Password + + + + + + + + + + + + + + + Please enter the one-time password sent to your email. + + + + )} + /> + + + + +
+ ); +} diff --git a/scruter-nextjs/app/(routes)/auth/components/signup-form.tsx b/scruter-nextjs/app/(routes)/auth/components/signup-form.tsx new file mode 100644 index 00000000..7763bd91 --- /dev/null +++ b/scruter-nextjs/app/(routes)/auth/components/signup-form.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { + InputOTP, + InputOTPGroup, + InputOTPSeparator, + InputOTPSlot, +} from '@/components/ui/input-otp'; + +import * as React from 'react'; + +import { cn } from '@/lib/utils'; +import { Icons } from '@/components/ui/icons'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { Toaster, toast } from 'react-hot-toast'; +import { SellerCreate } from '@/actions/seller/signup-action'; +import { OtpForm } from './otp-form'; + +interface UserAuthFormProps extends React.HTMLAttributes { + authType: 'signup' | 'login'; +} + +export function SellerSignupForm({ + className, + authType, + ...props +}: UserAuthFormProps) { + const [isLoading, setIsLoading] = React.useState(false); + + const [name, setName] = React.useState(''); + const [email, setEmail] = React.useState(''); + const [otpOpen, setOtpOpen] = React.useState(false); + + async function onSubmit(event: React.SyntheticEvent) { + event.preventDefault(); + setIsLoading(true); + + if (!name || !email) { + toast.error('missing details'); + return; + } + // toast.success(name+email); + const res = await SellerCreate({ + name: name, + email: email, + }); + + if (!res.success && res.error) { + toast.error(res.error); + return; + } + + toast.success('user created successfully, please enter OTP'); + + setOtpOpen(true); + setIsLoading(false); + } + + return ( +
+ + {!otpOpen && ( +
+
+
+ + {/* : {name} */} + { + setName(e.target.value); + }} + /> + + {/* : {email} */} + { + setEmail(e.target.value); + }} + /> +
+ +
+
+ )} + + {otpOpen && } +
+ ); +} diff --git a/scruter-nextjs/app/(routes)/auth/seller/signup/page.tsx b/scruter-nextjs/app/(routes)/auth/seller/signup/page.tsx new file mode 100644 index 00000000..a3cc7517 --- /dev/null +++ b/scruter-nextjs/app/(routes)/auth/seller/signup/page.tsx @@ -0,0 +1,79 @@ +import { Metadata } from 'next'; +import Image from 'next/image'; +import Link from 'next/link'; + +import { cn } from '@/lib/utils'; +import { buttonVariants } from '@/components/ui/button'; +import { SellerSignupForm} from '../../components/signup-form'; + +export const metadata: Metadata = { + title: 'Authentication', + description: 'Authentication forms built using the components.', +}; + +export default function AuthenticationPage() { + return ( + <> +
+ Authentication + Authentication +
+
+ + Login + +
+ +
+
+
+
+

+ Create an account +

+

+ Enter your email below to create your account +

+
+ +

+ By clicking continue, you agree to our{' '} + + Terms of Service + {' '} + and{' '} + + Privacy Policy + + . +

+
+
+
+ + ); +} diff --git a/scruter-nextjs/app/(routes)/auth/user/signup/page.tsx b/scruter-nextjs/app/(routes)/auth/user/signup/page.tsx new file mode 100644 index 00000000..396f5254 --- /dev/null +++ b/scruter-nextjs/app/(routes)/auth/user/signup/page.tsx @@ -0,0 +1,79 @@ +import { Metadata } from 'next'; +import Image from 'next/image'; +import Link from 'next/link'; + +import { cn } from '@/lib/utils'; +import { buttonVariants } from '@/components/ui/button'; +import { SellerSignupForm} from '../../components/signup-form'; + +export const metadata: Metadata = { + title: 'Authentication', + description: 'Authentication forms built using the components.', +}; + +export default function AuthenticationPage() { + return ( + <> +
+ Authentication + Authentication +
+
+ + Login + +
+ +
+
+
+
+

+ Create an account +

+

+ Enter your email below to create your account +

+
+ +

+ By clicking continue, you agree to our{' '} + + Terms of Service + {' '} + and{' '} + + Privacy Policy + + . +

+
+
+
+ + ); +} diff --git a/scruter-nextjs/app/layout.tsx b/scruter-nextjs/app/(routes)/layout.tsx similarity index 53% rename from scruter-nextjs/app/layout.tsx rename to scruter-nextjs/app/(routes)/layout.tsx index 98f7616e..0ddf9338 100644 --- a/scruter-nextjs/app/layout.tsx +++ b/scruter-nextjs/app/(routes)/layout.tsx @@ -1,19 +1,20 @@ import type { Metadata } from 'next'; -import localFont from 'next/font/local'; -import './globals.css'; +// import localFont from 'next/font/local'; +import '../globals.css'; import Navbar from '@/components/NavBars&Footers/navbar'; import Footer from '@/components/NavBars&Footers/footer'; +import CustomCursor from '@/components/ui/CustomCursor'; -const geistSans = localFont({ - src: './fonts/GeistVF.woff', - variable: '--font-geist-sans', - weight: '100 900', -}); -const geistMono = localFont({ - src: './fonts/GeistMonoVF.woff', - variable: '--font-geist-mono', - weight: '100 900', -}); +// const geistSans = localFont({ +// src: './fonts/GeistVF.woff', +// variable: '--font-geist-sans', +// weight: '100 900', +// }); +// const geistMono = localFont({ +// src: './fonts/GeistMonoVF.woff', +// variable: '--font-geist-mono', +// weight: '100 900', +// }); export const metadata: Metadata = { title: 'Create Next App', @@ -28,8 +29,9 @@ export default function RootLayout({ return ( + {children}