diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d134963..f7ae83e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@radix-ui/react-avatar": "^1.1.2", + "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-scroll-area": "^1.2.2", @@ -22194,6 +22195,92 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "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-checkbox/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "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-checkbox/node_modules/@radix-ui/react-slot": { + "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" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", @@ -22467,6 +22554,24 @@ } } }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", @@ -22481,6 +22586,39 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@remix-run/router": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", @@ -49902,6 +50040,45 @@ } } }, + "@radix-ui/react-checkbox": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", + "requires": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "dependencies": { + "@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "requires": {} + }, + "@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "requires": { + "@radix-ui/react-slot": "1.1.2" + } + }, + "@radix-ui/react-slot": { + "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==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.1" + } + } + } + }, "@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", @@ -50021,12 +50198,34 @@ "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", "requires": {} }, + "@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "requires": { + "@radix-ui/react-use-callback-ref": "1.1.0" + } + }, "@radix-ui/react-use-layout-effect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", "requires": {} }, + "@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "requires": {} + }, + "@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "requires": { + "@radix-ui/react-use-layout-effect": "1.1.0" + } + }, "@remix-run/router": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 102f792..62bf6b9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@radix-ui/react-avatar": "^1.1.2", + "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-scroll-area": "^1.2.2", diff --git a/frontend/src/Pages/Authentication.tsx b/frontend/src/Pages/Authentication.tsx index 5651d5d..168e90d 100644 --- a/frontend/src/Pages/Authentication.tsx +++ b/frontend/src/Pages/Authentication.tsx @@ -2,30 +2,40 @@ import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; import { LoginForm, SignUpForm, OTPVerificationForm, ForgotPasswordForm, ResetPasswordForm } from './Authentication/forms.tsx'; import { Link } from 'react-router-dom'; +import loginSvg from '@/assets/login.svg'; +import signupSvg from '@/assets/signup.svg'; -const LeftSection = () => ( -
-
- +interface LeftSectionProps { + authMode: 'login' | 'signup' | 'otpVerification' | 'forgotPassword' | 'resetPassword'; +} + +const LeftSection: React.FC = ({ authMode }) => ( +
+
+ - {/* SVG Content */} + {/* Arguehub Logo */} Arguehub
-
+ +
+ {`${authMode
-

+

"We cannot solve our problems with the same thinking we used when we created them."

-
Albert Einstein
+
- Albert Einstein
); - - interface RightSectionProps { authMode: 'login' | 'signup' | 'otpVerification' | 'forgotPassword' | 'resetPassword'; toggleAuthMode: () => void; @@ -90,9 +100,7 @@ const RightSection: React.FC = ({
); - const Authentication = () => { - // Extend authMode to include 'resetPassword' const [authMode, setAuthMode] = useState< 'login' | 'signup' | 'otpVerification' | 'forgotPassword' | 'resetPassword' >('login'); @@ -105,29 +113,24 @@ const Authentication = () => { setAuthMode((prevMode) => (prevMode === 'login' ? 'signup' : 'login')); }; - // Start OTP verification process const startOtpVerification = (email: string) => { setEmailForOTP(email); setAuthMode('otpVerification'); }; - // Handle successful OTP verification const handleOtpVerified = () => { setAuthMode('login'); }; - // Start forgot password process const startForgotPassword = () => { setAuthMode('forgotPassword'); }; - // Start reset password process const startResetPassword = (email: string) => { setEmailForPasswordReset(email); setAuthMode('resetPassword'); }; - // Handle successful password reset const handlePasswordReset = () => { setInfoMessage('Your password was successfully reset. You can now log in.'); setAuthMode('login'); @@ -135,7 +138,7 @@ const Authentication = () => { return (
- + void; @@ -13,14 +14,14 @@ interface LoginFormProps { export const LoginForm: React.FC = ({ startForgotPassword, infoMessage }) => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const [passwordVisible, setPasswordVisible] = useState(false) const authContext = useContext(AuthContext); + const [checked, setChecked] = useState(false); if (!authContext) { throw new Error('LoginForm must be used within an AuthProvider'); } - const { login, error, loading } = authContext; + const { login, loading } = authContext; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -37,30 +38,33 @@ export const LoginForm: React.FC = ({ startForgotPassword, infoM onChange={(e) => setEmail(e.target.value)} className="mb-2" /> - setPassword(e.target.value)} - className="mb-1" - /> -
-
- setPasswordVisible(e.target.checked)} +
+ setPassword(e.target.value)} + /> +
+
+
+ setChecked(checked === true)} + className='w-3 h-3 border border-primary rounded-full focus:ring-2 focus:ring-primary' /> + +
+ +
+

Forgot Password?

-
show password
+
- {error &&

{error}

} -

- Forgot your password?{' '} - - Reset Password - -

+ @@ -76,7 +80,6 @@ interface SignUpFormProps { export const SignUpForm: React.FC = ({ startOtpVerification }) => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const [passwordVisible, setPasswordVisible] = useState(false) const [confirmPassword, setConfirmPassword] = useState(''); const authContext = useContext(AuthContext); @@ -84,7 +87,7 @@ export const SignUpForm: React.FC = ({ startOtpVerification }) throw new Error('SignUpForm must be used within an AuthProvider'); } - const { signup, error, loading } = authContext; + const { signup, loading } = authContext; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -107,31 +110,22 @@ export const SignUpForm: React.FC = ({ startOtpVerification }) onChange={(e) => setEmail(e.target.value)} className="mb-2" /> - setPassword(e.target.value)} - className="mb-2" - /> - setConfirmPassword(e.target.value)} - className="mb-4" - /> -
-
- setPasswordVisible(e.target.checked)} - /> -
-
show password
+
+ setPassword(e.target.value)} + /> +
+
+ setConfirmPassword(e.target.value)} + />
- {error &&

{error}

} @@ -239,11 +233,6 @@ export const ForgotPasswordForm: React.FC = ({ }; -interface ResetPasswordFormProps { - email: string; - handlePasswordReset: () => void; -} - interface ResetPasswordFormProps { email: string; handlePasswordReset: () => void; @@ -253,14 +242,13 @@ export const ResetPasswordForm: React.FC = ({ email, han const [code, setCode] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmNewPassword, setConfirmNewPassword] = useState(''); - const [passwordVisible, setPasswordVisible] = useState(false) const authContext = useContext(AuthContext); if (!authContext) { throw new Error('ResetPasswordForm must be used within an AuthProvider'); } - const { confirmForgotPassword, login, error, loading } = authContext; + const { confirmForgotPassword, login, loading } = authContext; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -286,31 +274,23 @@ export const ResetPasswordForm: React.FC = ({ email, han placeholder="Enter Code" className="w-full mb-2" /> - setNewPassword(e.target.value)} - placeholder="New Password" - className="w-full mb-2" - /> - setConfirmNewPassword(e.target.value)} - placeholder="Confirm New Password" - className="w-full mb-4" - /> -
-
- setPasswordVisible(e.target.checked)} +
+ setNewPassword(e.target.value)} + + /> +
+
+ setConfirmNewPassword(e.target.value)} />
-
show password
-
- {error &&

{error}

} diff --git a/frontend/src/assets/login.svg b/frontend/src/assets/login.svg new file mode 100644 index 0000000..4f01c76 --- /dev/null +++ b/frontend/src/assets/login.svg @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/signup.svg b/frontend/src/assets/signup.svg new file mode 100644 index 0000000..0540ed3 --- /dev/null +++ b/frontend/src/assets/signup.svg @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/components/PasswordInput.tsx b/frontend/src/components/PasswordInput.tsx new file mode 100644 index 0000000..0b4fe05 --- /dev/null +++ b/frontend/src/components/PasswordInput.tsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import { Input } from '@/components/ui/input'; +import { Eye, EyeOff } from 'lucide-react'; + +interface PasswordInputProps extends React.InputHTMLAttributes { + error?: string; +} + +const PasswordInput: React.FC = ({ error, ...props }) => { + const [showPassword, setShowPassword] = useState(false); + + return ( +
+ + + {error &&

{error}

} +
+ ); +}; + +export default PasswordInput; \ No newline at end of file diff --git a/frontend/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..5ff7a4d --- /dev/null +++ b/frontend/src/components/ui/checkbox.tsx @@ -0,0 +1,28 @@ +// components/ui/checkbox.tsx +import * as React from "react"; +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import { Check } from "lucide-react"; +import { cn } from "@/lib/utils"; // Ensure you have a utility for class merging + +export interface CheckboxProps extends React.ComponentPropsWithoutRef {} + +const Checkbox = React.forwardRef, CheckboxProps>( + ({ className, ...props }, ref) => ( + + + + + + ) +); + +Checkbox.displayName = "Checkbox"; + +export { Checkbox };