From 0bf0b3de5172437379c1fd87bed954866d67dcf7 Mon Sep 17 00:00:00 2001 From: muhammedsirajudeen Date: Sun, 23 Feb 2025 19:55:08 +0530 Subject: [PATCH 01/11] refactor: - Introduced constants in refactor - Removed unwanted comment from user.model.ts --- .../implementation/auth.controller.ts | 4 ++-- backend/src/models/implementation/user.model.ts | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/backend/src/controllers/implementation/auth.controller.ts b/backend/src/controllers/implementation/auth.controller.ts index 0e01cf3..7efed11 100644 --- a/backend/src/controllers/implementation/auth.controller.ts +++ b/backend/src/controllers/implementation/auth.controller.ts @@ -10,7 +10,7 @@ export class AuthController implements IAuthController { try { const user = await this._authService.signup(req.body); - res.status(200).json({ + res.status(HttpStatus.OK).json({ email: user, }); } catch (err) { @@ -31,7 +31,7 @@ export class AuthController implements IAuthController { sameSite: "strict", }); - res.status(200).json({ accessToken: tokens.accessToken }); + res.status(HttpStatus.OK).json({ accessToken: tokens.accessToken }); } catch (err) { next(err); } diff --git a/backend/src/models/implementation/user.model.ts b/backend/src/models/implementation/user.model.ts index 7ee3618..27c5d52 100644 --- a/backend/src/models/implementation/user.model.ts +++ b/backend/src/models/implementation/user.model.ts @@ -1,23 +1,6 @@ import { model, Schema, Document } from "mongoose"; import { IUser } from "shared/types"; -// interface IUser { -// _id: string; -// username: string; -// name: string; -// email: string; -// password: string; -// status: "active" | "blocked"; -// role: "user" | "moderator"; -// bio: string; -// profile_picture?: string; -// social_links: { type: string; url: string }[]; -// resume?: string; -// date_of_birth: Date; -// created_at: Date; -// updated_at: Date; -// } - export interface IUserModel extends Document, Omit {} const userSchema = new Schema( From 411b74f3efa2a14d38e316c18b501c91cedd9036 Mon Sep 17 00:00:00 2001 From: muhammedsirajudeen Date: Mon, 24 Feb 2025 09:07:14 +0530 Subject: [PATCH 02/11] refactor: added comment about google svg --- frontend/src/components/user/auth/Login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/user/auth/Login.tsx b/frontend/src/components/user/auth/Login.tsx index 2b90020..aae9e2b 100644 --- a/frontend/src/components/user/auth/Login.tsx +++ b/frontend/src/components/user/auth/Login.tsx @@ -94,7 +94,7 @@ const Login: FC = ({ signin, setSignin }) => { - + {/* @muhammedsirajudeen: Replace this svg with icon from lucide-react */}
+ +
+ + + + ) +} diff --git a/frontend/src/components/user/common/ThemeToggle.tsx b/frontend/src/components/user/common/ThemeToggle.tsx index 0e4b012..69f1bcd 100644 --- a/frontend/src/components/user/common/ThemeToggle.tsx +++ b/frontend/src/components/user/common/ThemeToggle.tsx @@ -2,13 +2,13 @@ import { useState } from 'react' import { useTheme } from './theme-provider' const ThemeToggle = () => { - const { setTheme } = useTheme() + const { setTheme } = useTheme() const [theme, setLocalTheme] = useState(localStorage.getItem('vite-ui-theme')) const toggleTheme = () => { - setTheme(theme === "dark" ? 'light' : 'dark') - setLocalTheme(theme === "dark" ? 'light' : 'dark') + setTheme(theme === "dark" ? 'light' : 'dark') + setLocalTheme(theme === "dark" ? 'light' : 'dark') } diff --git a/frontend/src/pages/auth/Auth.tsx b/frontend/src/pages/auth/Auth.tsx index 3d44646..4cc9e70 100644 --- a/frontend/src/pages/auth/Auth.tsx +++ b/frontend/src/pages/auth/Auth.tsx @@ -1,14 +1,13 @@ -import AuthLeftPanel from "@/components/user/auth/AuthLeftPanel"; -import Login from "@/components/user/auth/Login"; -import { useState } from "react"; +import UserAuth from "@/components/user/auth"; export default function LoginPage() { - const [signin, setSignin] = useState<"register" | "login">("login"); - + // const [signin, setSignin] = useState<"register" | "login">("login"); + return (
- - + + {/* + */}
); } From 65981d444c30addf3fec348534584e961aeab65a Mon Sep 17 00:00:00 2001 From: MOHAMMED SADIK K P Date: Mon, 24 Feb 2025 13:32:49 +0530 Subject: [PATCH 05/11] feat(auth): restructure authentication layout by removing AuthLeftPanel and implementing SideLayout and FormLayout components --- .../components/user/auth/AuthLeftPanel.tsx | 13 --- .../user/auth/components/FormLayout.tsx | 74 ++++++++++++++ .../user/auth/components/SideLayout.tsx | 19 ++++ frontend/src/components/user/auth/index.tsx | 99 ++----------------- frontend/src/pages/auth/Auth.tsx | 4 +- 5 files changed, 102 insertions(+), 107 deletions(-) delete mode 100644 frontend/src/components/user/auth/AuthLeftPanel.tsx create mode 100644 frontend/src/components/user/auth/components/FormLayout.tsx create mode 100644 frontend/src/components/user/auth/components/SideLayout.tsx diff --git a/frontend/src/components/user/auth/AuthLeftPanel.tsx b/frontend/src/components/user/auth/AuthLeftPanel.tsx deleted file mode 100644 index c2de0c6..0000000 --- a/frontend/src/components/user/auth/AuthLeftPanel.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { FC } from 'react' -import Image from "../../../assets/AuthImage.jpg" - - -const AuthLeftPanel: FC = () => { - return ( -
- -
- ) -} - -export default AuthLeftPanel diff --git a/frontend/src/components/user/auth/components/FormLayout.tsx b/frontend/src/components/user/auth/components/FormLayout.tsx new file mode 100644 index 0000000..bf31ba1 --- /dev/null +++ b/frontend/src/components/user/auth/components/FormLayout.tsx @@ -0,0 +1,74 @@ +import { Separator } from "@/components/ui/separator" +import { UserAuthForm } from "./user-auth-form" +import Button from "@/components/ui/button" +import { Github } from "lucide-react" +import { useState } from "react" + + +export const FormLayout = () => { + + const [signin, setSignin] = useState<"register" | "login">("login"); + + return ( +
+
+
+

{signin === 'login' ? "Welcome Back!" : "Create an Account"}

+

+ {signin === 'login' ? "Please enter your email and password to log in." : "Please fill in the details to create your account."} +

+
+ +
+

+ {signin === "login" ? "Don't have an account?" : "Already have an account?"} +

+

setSignin(signin === "login" ? "register" : "login")} + > + {signin === "login" ? "Register now." : "Login now."} +

+
+
+
+ +
+
+ + Or continue with + +
+
+ +
+ + +
+
+
+ ) +} diff --git a/frontend/src/components/user/auth/components/SideLayout.tsx b/frontend/src/components/user/auth/components/SideLayout.tsx new file mode 100644 index 0000000..3dfc037 --- /dev/null +++ b/frontend/src/components/user/auth/components/SideLayout.tsx @@ -0,0 +1,19 @@ +import { useTheme } from "../../common/theme-provider" +import LightLogo from '@/assets/Logo Light.svg' +import DarkLogo from '@/assets/Logo Dark.svg' + +export const SideLayout = () => { + const { theme } = useTheme() + return ( +
+
+
+ Inker +
+
+ ) +} diff --git a/frontend/src/components/user/auth/index.tsx b/frontend/src/components/user/auth/index.tsx index e6a90d9..f59f3fa 100644 --- a/frontend/src/components/user/auth/index.tsx +++ b/frontend/src/components/user/auth/index.tsx @@ -1,95 +1,12 @@ -import LightLogo from '@/assets/Logo Light.svg' -import DarkLogo from '@/assets/Logo Dark.svg' -import { UserAuthForm } from './components/user-auth-form' -import { useTheme } from '../common/theme-provider' -import Button from '@/components/ui/button' -import { Github } from 'lucide-react' -import { Separator } from '@/components/ui/separator' +import { SideLayout } from './components/SideLayout' +import { FormLayout } from './components/FormLayout' -export default function SignIn2() { - const { theme } = useTheme() - return ( -
-
-
-
- Inker - Blog @Brototype -
- - Vite +export default function AuthForm() { -
-
-

- “This template has saved me countless hours of work and - helped me deliver stunning designs to my clients faster than ever - before.” -

-
John Doe
-
-
-
-
-
-
-

Login

-

- Enter your email and password below
- to log into your account -

-
- -
-
- -
-
- - Or continue with - -
-
- -
- - -
-
-
-
+ return ( + <> + + + ) } diff --git a/frontend/src/pages/auth/Auth.tsx b/frontend/src/pages/auth/Auth.tsx index 4cc9e70..3c29c2e 100644 --- a/frontend/src/pages/auth/Auth.tsx +++ b/frontend/src/pages/auth/Auth.tsx @@ -4,10 +4,8 @@ export default function LoginPage() { // const [signin, setSignin] = useState<"register" | "login">("login"); return ( -
+
- {/* - */}
); } From 5cac26d0ca19e49491d57cf09b1f9dd9bde5e8d1 Mon Sep 17 00:00:00 2001 From: MOHAMMED SADIK K P Date: Mon, 24 Feb 2025 13:38:31 +0530 Subject: [PATCH 06/11] feat(dependencies): add @hookform/resolvers and zod to package.json --- frontend/package.json | 4 +++- package-lock.json | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index f24b5ac..6090f83 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "@hookform/resolvers": "^4.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", @@ -28,7 +29,8 @@ "react-router-dom": "^7.1.5", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.6", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.19.0", diff --git a/package-lock.json b/package-lock.json index 00ff062..04c44b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@ "frontend": { "version": "1.0.0", "dependencies": { + "@hookform/resolvers": "^4.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", @@ -69,7 +70,8 @@ "react-router-dom": "^7.1.5", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.6", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.19.0", @@ -1042,6 +1044,17 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, + "node_modules/@hookform/resolvers": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-4.1.1.tgz", + "integrity": "sha512-S9YN1RgNWG+klUz5uQaV6rjE4pr6Py2tamj7ekshzLcMyg+/Pal1KZAYgGszV0+doiy41dUiQgXL3uRS9stndQ==", + "dependencies": { + "caniuse-lite": "^1.0.30001698" + }, + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -3259,7 +3272,6 @@ "version": "1.0.30001700", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5776,6 +5788,22 @@ "react": "^19.0.0" } }, + "node_modules/react-hook-form": { + "version": "7.54.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", + "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", + "peer": true, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -6838,6 +6866,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "shared": { "version": "1.0.0", "license": "ISC" From a1bfa5073e8d63bd6ff4ab809acc7480a52e9594 Mon Sep 17 00:00:00 2001 From: MOHAMMED SADIK K P Date: Mon, 24 Feb 2025 15:00:33 +0530 Subject: [PATCH 07/11] feat(ui): add Toaster component for notifications and integrate with authentication forms --- frontend/package.json | 3 + frontend/src/components/password-input.tsx | 39 ++++ frontend/src/components/ui/form.tsx | 175 ++++++++++++++++++ frontend/src/components/ui/sonner.tsx | 27 +++ .../user/auth/components/FormLayout.tsx | 16 +- .../user/auth/components/user-auth-form.tsx | 163 +++++++++++++++- frontend/src/main.tsx | 2 + package-lock.json | 24 ++- 8 files changed, 431 insertions(+), 18 deletions(-) create mode 100644 frontend/src/components/password-input.tsx create mode 100644 frontend/src/components/ui/form.tsx create mode 100644 frontend/src/components/ui/sonner.tsx diff --git a/frontend/package.json b/frontend/package.json index 6090f83..36044cd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,9 +24,12 @@ "embla-carousel-autoplay": "^8.5.2", "embla-carousel-react": "^8.5.2", "lucide-react": "^0.475.0", + "next-themes": "^0.4.4", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.54.2", "react-router-dom": "^7.1.5", + "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.6", "tailwindcss-animate": "^1.0.7", diff --git a/frontend/src/components/password-input.tsx b/frontend/src/components/password-input.tsx new file mode 100644 index 0000000..d4d4e06 --- /dev/null +++ b/frontend/src/components/password-input.tsx @@ -0,0 +1,39 @@ +import * as React from 'react' +import { Eye, EyeClosed } from 'lucide-react'; +import { cn } from '@/lib/utils' +import { Button } from './ui/button' + +type PasswordInputProps = Omit< + React.InputHTMLAttributes, + 'type' +> + +const PasswordInput = React.forwardRef( + ({ className, disabled, ...props }, ref) => { + const [showPassword, setShowPassword] = React.useState(false) + return ( +
+ + +
+ ) + } +) +PasswordInput.displayName = 'PasswordInput' + +export { PasswordInput } diff --git a/frontend/src/components/ui/form.tsx b/frontend/src/components/ui/form.tsx new file mode 100644 index 0000000..2bca54c --- /dev/null +++ b/frontend/src/components/ui/form.tsx @@ -0,0 +1,175 @@ +import * as React from 'react' +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from 'react-hook-form' +import * as LabelPrimitive from '@radix-ui/react-label' +import { Slot } from '@radix-ui/react-slot' +import { cn } from '@/lib/utils' +import { Label } from '@/components/ui/label' + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState, formState } = useFormContext() + + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error('useFormField should be used within ') + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId() + + return ( + +
+ + ) +}) +FormItem.displayName = 'FormItem' + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField() + + return ( +
- -
-

- {signin === "login" ? "Don't have an account?" : "Already have an account?"} -

-

setSignin(signin === "login" ? "register" : "login")} - > - {signin === "login" ? "Register now." : "Login now."} -

-
+
diff --git a/frontend/src/components/user/auth/components/user-auth-form.tsx b/frontend/src/components/user/auth/components/user-auth-form.tsx index 1299f72..369e256 100644 --- a/frontend/src/components/user/auth/components/user-auth-form.tsx +++ b/frontend/src/components/user/auth/components/user-auth-form.tsx @@ -1,6 +1,163 @@ +"use client" + +import { type FC, useState } from "react" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { PasswordInput } from "@/components/password-input" +import { Button } from "@/components/ui/button" +import { cn } from "@/lib/cn" +import { Loader2 } from "lucide-react" +import { toast } from "sonner" + +interface UserAuthFormProps { + authState: "login" | "register" + onStateChange: (state: "login" | "register") => void +} + +const loginSchema = z.object({ + email: z.string().min(1, { message: "Please enter your email" }).email({ message: "Invalid email address" }), + password: z + .string() + .min(1, { + message: "Please enter your password", + }) + .min(7, { + message: "Password must be at least 7 characters long", + }), +}) + +const registerSchema = z + .object({ + name: z.string().min(1, { message: "Please enter your name" }), + email: z.string().min(1, { message: "Please enter your email" }).email({ message: "Invalid email address" }), + password: z + .string() + .min(1, { + message: "Please enter your password", + }) + .min(7, { + message: "Password must be at least 7 characters long", + }), + confirmPassword: z.string(), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match.", + path: ["confirmPassword"], + }) + +export const UserAuthForm: FC = ({ authState, onStateChange }) => { + const [isLoading, setIsLoading] = useState(false) + const formSchema = authState === "login" ? loginSchema : registerSchema + const form = useForm>({ + resolver: zodResolver(formSchema), + }) + + async function onSubmit(data: z.infer) { + setIsLoading(true) + + try { + // Simulate API call + await new Promise((resolve) => setTimeout(resolve, 2000)) + + console.log(data) + toast.success(authState === "login" ? "Logged in successfully" : "Account created successfully") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + toast.error("An error occurred. Please try again.") + } finally { + setIsLoading(false) + } + } -export function UserAuthForm() { return ( -
UserAuthForm
+
+
+ +
+ {authState === "register" && ( + ( + + Name + + + + + + )} + /> + )} + ( + + Email + + + + + + )} + /> + ( + + Password + + + + + + )} + /> + {authState === "register" && ( + ( + + Confirm Password + + + + + + )} + /> + )} + +
+
+ +
+ {authState === "login" ? ( +

+ Don't have an account?{" "} + +

+ ) : ( +

+ Already have an account?{" "} + +

+ )} +
+
) -} \ No newline at end of file +} + diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..5fe9c37 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,9 +2,11 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' +import { Toaster } from 'sonner' createRoot(document.getElementById('root')!).render( + , ) diff --git a/package-lock.json b/package-lock.json index 04c44b1..8993803 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,9 +65,12 @@ "embla-carousel-autoplay": "^8.5.2", "embla-carousel-react": "^8.5.2", "lucide-react": "^0.475.0", + "next-themes": "^0.4.4", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.54.2", "react-router-dom": "^7.1.5", + "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.6", "tailwindcss-animate": "^1.0.7", @@ -1488,7 +1491,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", - "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, @@ -1708,7 +1710,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, @@ -5358,6 +5359,15 @@ "node": ">= 0.6" } }, + "node_modules/next-themes": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.4.tgz", + "integrity": "sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -5792,7 +5802,6 @@ "version": "7.54.2", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6282,6 +6291,15 @@ "node": ">=10" } }, + "node_modules/sonner": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.1.tgz", + "integrity": "sha512-FRBphaehZ5tLdLcQ8g2WOIRE+Y7BCfWi5Zyd8bCvBjiW8TxxAyoWZIxS661Yz6TGPqFQ4VLzOF89WEYhfynSFQ==", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", From 431f3be1dc8c33292f92e5eb5b8ca1c9848ef456 Mon Sep 17 00:00:00 2001 From: MOHAMMED SADIK K P Date: Mon, 24 Feb 2025 15:04:55 +0530 Subject: [PATCH 08/11] feat(auth): rename signin state to authState for clarity in FormLayout component --- .../src/components/user/auth/components/FormLayout.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/user/auth/components/FormLayout.tsx b/frontend/src/components/user/auth/components/FormLayout.tsx index fdcece3..fc2c3d6 100644 --- a/frontend/src/components/user/auth/components/FormLayout.tsx +++ b/frontend/src/components/user/auth/components/FormLayout.tsx @@ -7,20 +7,20 @@ import { useState } from "react" export const FormLayout = () => { - const [signin, setSignin] = useState<"register" | "login">("login"); + const [authState, setAuthState] = useState<'login' | 'register'>('login'); return (
-

{signin === 'login' ? "Welcome Back!" : "Create an Account"}

+

{authState === 'login' ? "Welcome Back!" : "Create an Account"}

- {signin === 'login' ? "Please enter your email and password to log in." : "Please fill in the details to create your account."} + {authState === 'login' ? "Please enter your email and password to log in." : "Please fill in the details to create your account."}

From dca2ede5169a8d41faecf2f85a034e7467ecae60 Mon Sep 17 00:00:00 2001 From: MOHAMMED SADIK K P Date: Mon, 24 Feb 2025 16:10:26 +0530 Subject: [PATCH 09/11] feat(auth): implement authentication services with axios and update UserAuthForm for API integration --- frontend/package.json | 1 + frontend/src/api/axios.ts | 9 ++ frontend/src/components/password-input.tsx | 4 +- .../user/auth/components/user-auth-form.tsx | 25 +++-- frontend/src/pages/auth/Auth.tsx | 1 - frontend/src/services/authServices.ts | 18 ++++ package-lock.json | 101 ++++++++++++++++++ 7 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 frontend/src/api/axios.ts diff --git a/frontend/package.json b/frontend/package.json index 36044cd..6b1a3b1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/vite": "^4.0.6", + "axios": "^1.7.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "embla-carousel-autoplay": "^8.5.2", diff --git a/frontend/src/api/axios.ts b/frontend/src/api/axios.ts new file mode 100644 index 0000000..5312064 --- /dev/null +++ b/frontend/src/api/axios.ts @@ -0,0 +1,9 @@ +import axios from 'axios' + +export const axiosInstance = axios.create({ + baseURL: 'http://localhost:3000', + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, +}); \ No newline at end of file diff --git a/frontend/src/components/password-input.tsx b/frontend/src/components/password-input.tsx index d4d4e06..7fec070 100644 --- a/frontend/src/components/password-input.tsx +++ b/frontend/src/components/password-input.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Eye, EyeClosed } from 'lucide-react'; +import { Eye, EyeOff } from 'lucide-react'; import { cn } from '@/lib/utils' import { Button } from './ui/button' @@ -28,7 +28,7 @@ const PasswordInput = React.forwardRef( className='absolute right-1 top-1/2 h-6 w-6 -translate-y-1/2 rounded-md text-muted-foreground' onClick={() => setShowPassword((prev) => !prev)} > - {showPassword ? : } + {showPassword ? : }
) diff --git a/frontend/src/components/user/auth/components/user-auth-form.tsx b/frontend/src/components/user/auth/components/user-auth-form.tsx index 369e256..d5c2b7f 100644 --- a/frontend/src/components/user/auth/components/user-auth-form.tsx +++ b/frontend/src/components/user/auth/components/user-auth-form.tsx @@ -4,13 +4,15 @@ import { type FC, useState } from "react" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { z } from "zod" -import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { Form, FormControl, FormField, FormItem, FormMessage } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { PasswordInput } from "@/components/password-input" import { Button } from "@/components/ui/button" import { cn } from "@/lib/cn" import { Loader2 } from "lucide-react" import { toast } from "sonner" +import { AuthService } from "@/services/authServices" +import { useNavigate } from "react-router-dom" interface UserAuthFormProps { authState: "login" | "register" @@ -50,6 +52,7 @@ const registerSchema = z export const UserAuthForm: FC = ({ authState, onStateChange }) => { const [isLoading, setIsLoading] = useState(false) + const navigate = useNavigate() const formSchema = authState === "login" ? loginSchema : registerSchema const form = useForm>({ resolver: zodResolver(formSchema), @@ -61,9 +64,13 @@ export const UserAuthForm: FC = ({ authState, onStateChange } try { // Simulate API call await new Promise((resolve) => setTimeout(resolve, 2000)) - - console.log(data) + if (authState == 'login') { + await AuthService.loginService(data) + } else { + await AuthService.registerService(data as { email: string; password: string; name: string; confirmPassword: string }) + } toast.success(authState === "login" ? "Logged in successfully" : "Account created successfully") + navigate('/home') // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { toast.error("An error occurred. Please try again.") @@ -83,9 +90,8 @@ export const UserAuthForm: FC = ({ authState, onStateChange } name="name" render={({ field }) => ( - Name - + @@ -97,9 +103,8 @@ export const UserAuthForm: FC = ({ authState, onStateChange } name="email" render={({ field }) => ( - Email - + @@ -110,9 +115,8 @@ export const UserAuthForm: FC = ({ authState, onStateChange } name="password" render={({ field }) => ( - Password - + @@ -124,9 +128,8 @@ export const UserAuthForm: FC = ({ authState, onStateChange } name="confirmPassword" render={({ field }) => ( - Confirm Password - + diff --git a/frontend/src/pages/auth/Auth.tsx b/frontend/src/pages/auth/Auth.tsx index 3c29c2e..9633dba 100644 --- a/frontend/src/pages/auth/Auth.tsx +++ b/frontend/src/pages/auth/Auth.tsx @@ -1,7 +1,6 @@ import UserAuth from "@/components/user/auth"; export default function LoginPage() { - // const [signin, setSignin] = useState<"register" | "login">("login"); return (
diff --git a/frontend/src/services/authServices.ts b/frontend/src/services/authServices.ts index e69de29..26a5b3c 100644 --- a/frontend/src/services/authServices.ts +++ b/frontend/src/services/authServices.ts @@ -0,0 +1,18 @@ +import { axiosInstance } from "@/api/axios" + +export const AuthService = { + loginService: async (data: { email: string, password: string }) => { + try { + return await axiosInstance.post('/login', data) + } catch (error) { + console.log(error) + } + }, + registerService: async (data: { email: string; password: string; name: string; confirmPassword: string; }) => { + try { + return await axiosInstance.post('/register', data) + } catch (error) { + console.log(error) + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8993803..d07a593 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,7 @@ "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/vite": "^4.0.6", + "axios": "^1.7.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "embla-carousel-autoplay": "^8.5.2", @@ -3078,6 +3079,21 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/backend": { "resolved": "backend", "link": true @@ -3394,6 +3410,17 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3523,6 +3550,14 @@ "dev": true, "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3707,6 +3742,20 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", @@ -4291,6 +4340,39 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4474,6 +4556,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -5701,6 +5797,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", From 66970a04b525c03d7948067219fbce047986cf67 Mon Sep 17 00:00:00 2001 From: JasimIhsan Date: Tue, 25 Feb 2025 17:07:27 +0530 Subject: [PATCH 10/11] feat: sidebar navigation done --- .../src/components/user/common/AppSidebar.tsx | 153 +++++++++--------- frontend/src/router/AppRoutes.tsx | 4 +- 2 files changed, 77 insertions(+), 80 deletions(-) diff --git a/frontend/src/components/user/common/AppSidebar.tsx b/frontend/src/components/user/common/AppSidebar.tsx index 2093db2..4c2ab76 100644 --- a/frontend/src/components/user/common/AppSidebar.tsx +++ b/frontend/src/components/user/common/AppSidebar.tsx @@ -1,86 +1,81 @@ -import { Calendar, Home, Inbox, Search, Settings } from "lucide-react"; -import LightLogo from "../../../assets/Logo Light.svg" -import DarkLogo from "../../../assets/Logo Dark.svg" - -import { - Sidebar, - SidebarContent, - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, - SidebarHeader, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, -} from "@/components/ui/sidebar"; +import { UserRound, Newspaper, Search, SquareActivity, Plus } from "lucide-react"; +import LightLogo from "../../../assets/Logo Light.svg"; +import DarkLogo from "../../../assets/Logo Dark.svg"; +import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar"; import { DropdownMenu, DropdownMenuTrigger } from "../../ui/dropdown-menu"; +import Button from "@/components/ui/button"; +import { useNavigate, Link } from "react-router-dom"; const items = [ - { - title: "Home", - url: "#", - icon: Home, - }, - { - title: "Inbox", - url: "#", - icon: Inbox, - }, - { - title: "Calendar", - url: "#", - icon: Calendar, - }, - { - title: "Search", - url: "#", - icon: Search, - }, - { - title: "Settings", - url: "#", - icon: Settings, - }, + { + title: "My feed", + url: "/home", + icon: Newspaper, + }, + { + title: "Explore", + url: "/explore", + icon: Search, + }, + { + title: "Activity", + url: "/activity", + icon: SquareActivity, + }, + { + title: "Profile", + url: "/profile", + icon: UserRound, + }, ]; export function AppSidebar() { - return ( - - - - - - - -
- - -
-
-
-
-
-
-
- - - Application - - - {items.map((item) => ( - - - - - {item.title} - - - - ))} - - - - -
- ); + const navigate = useNavigate(); + + return ( + + + + + + + +
+ + +
+
+
+
+
+
+
+ + + + + + + + {items.map((item) => ( + + + + + + + + ))} + + + + +
+ ); } diff --git a/frontend/src/router/AppRoutes.tsx b/frontend/src/router/AppRoutes.tsx index c771d58..3a3828a 100644 --- a/frontend/src/router/AppRoutes.tsx +++ b/frontend/src/router/AppRoutes.tsx @@ -11,7 +11,9 @@ export const router = createBrowserRouter([ path: "/", element:, children: [ - { path: "home" , element:
home
}, + { path: "home" , element:
Feeds
}, + { path: "explore" , element:
Explore
}, + { path: "activity" , element:
Activity
}, { path: "profile", element: }, ], }, From 8f4ec153ec84ebc49f58ccf3a47bc68ad7be481a Mon Sep 17 00:00:00 2001 From: JasimIhsan Date: Tue, 25 Feb 2025 17:13:18 +0530 Subject: [PATCH 11/11] fix: resolve lint error --- frontend/src/components/user/common/AppSidebar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/user/common/AppSidebar.tsx b/frontend/src/components/user/common/AppSidebar.tsx index 4c2ab76..fb715ee 100644 --- a/frontend/src/components/user/common/AppSidebar.tsx +++ b/frontend/src/components/user/common/AppSidebar.tsx @@ -4,7 +4,7 @@ import DarkLogo from "../../../assets/Logo Dark.svg"; import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar"; import { DropdownMenu, DropdownMenuTrigger } from "../../ui/dropdown-menu"; import Button from "@/components/ui/button"; -import { useNavigate, Link } from "react-router-dom"; +import { Link } from "react-router-dom"; const items = [ { @@ -30,7 +30,6 @@ const items = [ ]; export function AppSidebar() { - const navigate = useNavigate(); return (