diff --git a/src/app/(auth)/(signin)/page.tsx b/src/app/(auth)/(signin)/page.tsx new file mode 100644 index 0000000..e4cc001 --- /dev/null +++ b/src/app/(auth)/(signin)/page.tsx @@ -0,0 +1,86 @@ +import { Metadata } from "next"; +import Link from "next/link"; +import UserAuthForm from "@/components/forms/user-auth-form"; +import { buttonVariants } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; + +export const metadata: Metadata = { + title: "Sign In", + description: "Sign In to start your journey with skilled ai.", +}; + +export default function AuthenticationPage() { + return ( +
+ + Login + +
+
+
+ + + + SkilledAi +
+
+
+

+ “Empower your coding journey at SkilledAi. Access tutorials, + challenges, and expert-led courses to master programming languages + and tools. Join a supportive community for discussions and code + reviews. Elevate your skills and stay ahead in the tech world with + us!.” +

+ {/*
Ak
*/} +
+
+
+
+
+
+

+ 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/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..5677414 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,31 @@ +import Providers from "@/components/layout/providers"; +import { Toaster } from "@/components/ui/toaster"; +import "@uploadthing/react/styles.css"; +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Home | Skilled Ai", + description: + "Empower your coding journey at Skilled Ai. Access tutorials, challenges, and expert-led courses to master programming languages and tools. Join a supportive community for discussions and code reviews. Elevate your skills and stay ahead in the tech world with us!", +}; + +export default async function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + + {children} + + + + ); +} diff --git a/src/app/loading.tsx b/src/app/loading.tsx new file mode 100644 index 0000000..2b4ee58 --- /dev/null +++ b/src/app/loading.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +const Loading = () => { + return ( +
+ ) +} + +export default Loading \ No newline at end of file diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 0000000..4b336c1 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { useRouter } from "next/navigation"; + +import { Button } from "@/components/ui/button"; + +export default function NotFound() { + const router = useRouter(); + + return ( +
+ + 404 + +

+ Something's missing +

+

+ Sorry, the page you are looking for doesn't exist or has been + moved. +

+
+ + +
+
+ ); +} diff --git a/src/components/forms/user-auth-form.tsx b/src/components/forms/user-auth-form.tsx new file mode 100644 index 0000000..accb9ae --- /dev/null +++ b/src/components/forms/user-auth-form.tsx @@ -0,0 +1,126 @@ +"use client"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import GoogleSignInButton from "../google-auth-button"; +import { createClient } from "@/utils/supabase/client"; + +const formSchema = z.object({ + email: z.string().email({ message: "Enter a valid email address" }), + password: z + .string() + .min(6, { message: "Password should be minimum 6 characters" }), +}); + +type UserFormValue = z.infer; + +export default function UserAuthForm() { + const router = useRouter(); + const searchParams = useSearchParams(); + const callbackUrl = searchParams.get("callbackUrl"); + const [loading, setLoading] = useState(false); + // const defaultValues = { + // email: "demo@gmail.com", + // }; + const form = useForm({ + resolver: zodResolver(formSchema), + // defaultValues, + }); + + const onSubmit = async ({ email, password }: UserFormValue) => { + console.log(email, password); + const supabase = createClient(); + const { data, error } = await supabase.auth.signUp({ + email, + password, + options: { + emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`, + }, + }); + console.log(data); + console.log(error); + // signIn("credentials", { + // email: data.email, + // callbackUrl: callbackUrl ?? "/dashboard", + // }); + + if (!error) { + router.push("/dashboard"); + } + }; + + return ( + <> +
+ + ( + + Email + + + + + + )} + /> + ( + + Password + + + + + + )} + /> + + + + +
+
+ +
+
+ + Or continue with + +
+
+ + + ); +} diff --git a/src/components/forms/user-profile-stepper/create-profile.tsx b/src/components/forms/user-profile-stepper/create-profile.tsx new file mode 100644 index 0000000..760e744 --- /dev/null +++ b/src/components/forms/user-profile-stepper/create-profile.tsx @@ -0,0 +1,653 @@ +"use client"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Heading } from "@/components/ui/heading"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { profileSchema, type ProfileFormValues } from "@/lib/form-schema"; +import { cn } from "@/lib/utils"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { AlertTriangleIcon, Trash, Trash2Icon } from "lucide-react"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import { SubmitHandler, useFieldArray, useForm } from "react-hook-form"; + +interface ProfileFormType { + initialData: any | null; + categories: any; +} + +export const CreateProfileOne: React.FC = ({ + initialData, + categories, +}) => { + const params = useParams(); + const router = useRouter(); + const [open, setOpen] = useState(false); + const [loading, setLoading] = useState(false); + const [imgLoading, setImgLoading] = useState(false); + const title = initialData ? "Edit account" : "Create Your Profile"; + const description = initialData + ? "Edit a account." + : "To create your account, we first need some basic information about you."; + const toastMessage = initialData ? "Account updated." : "Account created."; + const action = initialData ? "Save changes" : "Create"; + const [previousStep, setPreviousStep] = useState(0); + const [currentStep, setCurrentStep] = useState(0); + const [data, setData] = useState({}); + const delta = currentStep - previousStep; + + const defaultValues = { + jobs: [ + { + jobtitle: "", + employer: "", + startdate: "", + enddate: "", + jobcountry: "", + jobcity: "", + }, + ], + }; + + const form = useForm({ + resolver: zodResolver(profileSchema), + defaultValues, + mode: "onChange", + }); + + const { + control, + formState: { errors }, + } = form; + + const { append, remove, fields } = useFieldArray({ + control, + name: "jobs", + }); + + const onSubmit = async (data: ProfileFormValues) => { + try { + setLoading(true); + if (initialData) { + // await axios.post(`/api/products/edit-product/${initialData._id}`, data); + } else { + // const res = await axios.post(`/api/products/create-product`, data); + // console.log("product", res); + } + router.refresh(); + router.push(`/dashboard/products`); + } catch (error: any) { + } finally { + setLoading(false); + } + }; + + const onDelete = async () => { + try { + setLoading(true); + // await axios.delete(`/api/${params.storeId}/products/${params.productId}`); + router.refresh(); + router.push(`/${params.storeId}/products`); + } catch (error: any) { + } finally { + setLoading(false); + setOpen(false); + } + }; + + const processForm: SubmitHandler = (data) => { + console.log("data ==>", data); + setData(data); + // api call and reset + // form.reset(); + }; + + type FieldName = keyof ProfileFormValues; + + const steps = [ + { + id: "Step 1", + name: "Personal Information", + fields: [ + "firstname", + "lastname", + "email", + "contactno", + "country", + "city", + ], + }, + { + id: "Step 2", + name: "Professional Informations", + // fields are mapping and flattening for the error to be trigger for the dynamic fields + fields: fields + ?.map((_, index) => [ + `jobs.${index}.jobtitle`, + `jobs.${index}.employer`, + `jobs.${index}.startdate`, + `jobs.${index}.enddate`, + `jobs.${index}.jobcountry`, + `jobs.${index}.jobcity`, + // Add other field names as needed + ]) + .flat(), + }, + { id: "Step 3", name: "Complete" }, + ]; + + const next = async () => { + const fields = steps[currentStep].fields; + + const output = await form.trigger(fields as FieldName[], { + shouldFocus: true, + }); + + if (!output) return; + + if (currentStep < steps.length - 1) { + if (currentStep === steps.length - 2) { + await form.handleSubmit(processForm)(); + } + setPreviousStep(currentStep); + setCurrentStep((step) => step + 1); + } + }; + + const prev = () => { + if (currentStep > 0) { + setPreviousStep(currentStep); + setCurrentStep((step) => step - 1); + } + }; + + const countries = [{ id: "wow", name: "india" }]; + const cities = [{ id: "2", name: "kerala" }]; + + return ( + <> +
+ + {initialData && ( + + )} +
+ +
+
    + {steps.map((step, index) => ( +
  • + {currentStep > index ? ( +
    + + {step.id} + + {step.name} +
    + ) : currentStep === index ? ( +
    + + {step.id} + + {step.name} +
    + ) : ( +
    + + {step.id} + + {step.name} +
    + )} +
  • + ))} +
+
+ +
+ +
+ {currentStep === 0 && ( + <> + ( + + First Name + + + + + + )} + /> + ( + + Last Name + + + + + + )} + /> + ( + + Email + + + + + + )} + /> + ( + + Contact Number + + + + + + )} + /> + ( + + Country + + + + )} + /> + ( + + City + + + + )} + /> + + )} + {currentStep === 1 && ( + <> + {fields?.map((field, index) => ( + + + button]:hidden [&[data-state=open]>.alert]:hidden relative !no-underline", + errors?.jobs?.[index] && "text-red-700", + )} + > + {`Work Experience ${index + 1}`} + + + {errors?.jobs?.[index] && ( + + + + )} + + +
+ ( + + Job title + + + + + + )} + /> + ( + + Employer + + + + + + )} + /> + ( + + Start date + + + + + + )} + /> + ( + + End date + + + + + + )} + /> + ( + + Job country + + + + )} + /> + ( + + Job city + + + + )} + /> +
+
+
+
+ ))} + +
+ +
+ + )} + {currentStep === 2 && ( +
+

Completed

+
+                  {JSON.stringify(data)}
+                
+
+ )} +
+ + {/* */} +
+ + {/* Navigation */} +
+
+ + +
+
+ + ); +};