From 30c784f1438db93ebeacb6035b0c8d35f43fe1ac Mon Sep 17 00:00:00 2001 From: Yasir Akhlaque Date: Fri, 17 Oct 2025 12:11:56 +0530 Subject: [PATCH 1/5] Improved the Contact UI --- frontend/src/components/PasswordInput.jsx | 12 +- frontend/src/pages/LoginPage.jsx | 133 ++++++++++--- frontend/src/pages/RegisterPage.jsx | 223 ++++++++++++++++++---- 3 files changed, 307 insertions(+), 61 deletions(-) diff --git a/frontend/src/components/PasswordInput.jsx b/frontend/src/components/PasswordInput.jsx index bfeaeeb..6d2b825 100644 --- a/frontend/src/components/PasswordInput.jsx +++ b/frontend/src/components/PasswordInput.jsx @@ -15,19 +15,24 @@ const EyeSlashedIcon = () => ( ); -const PasswordInput = ({ value, onChange, placeholder = "Password", id = "password", error }) => { +const PasswordInput = ({ value, onChange, placeholder = "Password", id = "password", error, className }) => { const [isPasswordVisible, setIsPasswordVisible] = useState(false); const togglePasswordVisibility = () => { setIsPasswordVisible(prevState => !prevState); }; + // Default classes if no className is provided + const defaultClasses = "w-full px-4 py-2 pr-10 mt-2 border rounded-md focus:outline-none focus:ring-1"; + const errorClasses = error ? 'border-red-500 focus:ring-red-500' : 'focus:ring-blue-600 dark:border-gray-600'; + const inputClasses = className || `${defaultClasses} ${errorClasses}`; + return (
{isPasswordVisible ? : } diff --git a/frontend/src/pages/LoginPage.jsx b/frontend/src/pages/LoginPage.jsx index e211bee..50ce20b 100644 --- a/frontend/src/pages/LoginPage.jsx +++ b/frontend/src/pages/LoginPage.jsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import useAuth from '../hooks/useAuth'; import PasswordInput from '../components/PasswordInput'; +import { HiMail, HiArrowRight } from 'react-icons/hi'; /* // Demo user credentials const DEMO_EMAIL = 'test@example.com'; @@ -11,17 +12,21 @@ export default function LoginPage() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [serverError, setServerError] = useState(''); + const [isLoading, setIsLoading] = useState(false); const { login } = useAuth(); const handleSubmit = async (e) => { e.preventDefault(); setServerError(''); // Clear previous server errors + setIsLoading(true); try { await login(email, password); // Toast handled globally in AuthContext } catch (error) { setServerError(error.message); // Toast handled globally in AuthContext + } finally { + setIsLoading(false); } }; @@ -31,41 +36,117 @@ export default function LoginPage() { }; */ return ( -
- - +
+ {/* Logo/Brand */} + Paisable -
-

Login to your account

- {serverError &&

{serverError}

} -
-
-
- - setEmail(e.target.value)} required /> -
-
- - setPassword(e.target.value)} /> + {/* Login Card */} +
+ {/* Header */} +
+

Welcome Back

+

Sign in to continue to your account

+
+ + {/* Error Message */} + {serverError && ( +
+

{serverError}

+
+ )} + + {/* Form */} + + {/* Email Field */} +
+ +
+
+ +
+ setEmail(e.target.value)} + required + />
-
- - {/**/} +
+ + {/* Password Field */} +
+ + setPassword(e.target.value)} + className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700/50 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200" + /> +
+ + {/* Submit Button */} + + + {/* Divider */} +
+
+
-
- Don't have an account? Register +
+ + New to Paisable? +
+ + {/* Register Link */} +
+ + Create an account + + +
+ + {/* Footer */} +

+ Protected by industry-standard security +

); } \ No newline at end of file diff --git a/frontend/src/pages/RegisterPage.jsx b/frontend/src/pages/RegisterPage.jsx index fec46f5..468eaec 100644 --- a/frontend/src/pages/RegisterPage.jsx +++ b/frontend/src/pages/RegisterPage.jsx @@ -2,15 +2,38 @@ import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import useAuth from '../hooks/useAuth'; import PasswordInput from '../components/PasswordInput'; +import { HiMail, HiArrowRight, HiCheckCircle, HiXCircle } from 'react-icons/hi'; export default function RegisterPage() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [errors, setErrors] = useState({}); - // State for server-side errors const [serverError, setServerError] = useState(''); + const [isLoading, setIsLoading] = useState(false); const { signup } = useAuth(); + // Password strength indicator + const getPasswordStrength = () => { + if (!password) return null; + const hasLength = password.length >= 8 && password.length <= 16; + const hasLetter = /[a-zA-Z]/.test(password); + const hasDigit = /\d/.test(password); + const hasSymbol = /[\W_]/.test(password); + + const criteria = [hasLength, hasLetter, hasDigit, hasSymbol]; + const metCriteria = criteria.filter(Boolean).length; + + return { + hasLength, + hasLetter, + hasDigit, + hasSymbol, + strength: metCriteria + }; + }; + + const passwordStrength = getPasswordStrength(); + const validate = () => { const newErrors = {}; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; @@ -39,68 +62,204 @@ export default function RegisterPage() { const handleSubmit = async (e) => { e.preventDefault(); - setServerError(''); // Clear previous server errors + setServerError(''); const validationErrors = validate(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setErrors({}); + setIsLoading(true); - // The signup function in AuthContext needs to be updated to handle errors try { await signup(email, password); } catch (error) { setServerError(error.message); + } finally { + setIsLoading(false); } }; + const PasswordCriterion = ({ met, text }) => ( +
+ {met ? ( + + ) : ( + + )} + + {text} + +
+ ); + return ( -
- +
+ {/* Logo/Brand */} + Paisable -
-

Create an account

- {serverError &&

{serverError}

} -
-
-
- + + {/* Register Card */} +
+ {/* Header */} +
+

Create Account

+

Start managing your finances today

+
+ + {/* Error Message */} + {serverError && ( +
+

{serverError}

+
+ )} + + {/* Form */} + + {/* Email Field */} +
+ +
+
+ +
setEmail(e.target.value)} required /> - {errors.email &&

{errors.email}

}
-
- - setPassword(e.target.value)} - error={errors.password} - /> - {errors.password &&

{errors.password}

} -
-
-
+ + {/* Password Field */} +
+ + setPassword(e.target.value)} + error={errors.password} + className={`w-full px-4 py-3 border rounded-lg bg-gray-50 dark:bg-gray-700/50 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 transition-all duration-200 ${ + errors.password + ? 'border-red-500 focus:ring-red-500' + : 'border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-transparent' + }`} + /> + {errors.password && ( +

+ + {errors.password} +

+ )} + + {/* Password Strength Indicator */} + {password && passwordStrength && ( +
+

+ Password Requirements: +

+
+ + + + +
+ {/* Strength Bar */} +
+
+ {[1, 2, 3, 4].map((level) => ( +
= level + ? passwordStrength.strength === 4 + ? 'bg-green-500' + : passwordStrength.strength === 3 + ? 'bg-yellow-500' + : 'bg-red-500' + : 'bg-gray-200 dark:bg-gray-600' + }`} + /> + ))} +
+
+
+ )} +
+ + {/* Submit Button */} + + + + )} + + + {/* Divider */} +
+
+
-
- Already have an account? - - Log in - +
+ + Already have an account? +
+ + {/* Login Link */} +
+ + Sign in instead + + +
+ + {/* Footer */} +

+ By signing up, you agree to our Terms & Privacy Policy +

); } \ No newline at end of file From 289022bf6015bb954cc261268ea747f847d189f6 Mon Sep 17 00:00:00 2001 From: Yasir Akhlaque <148755054+yasirakhlaque@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:59:03 +0530 Subject: [PATCH 2/5] Update frontend/src/components/PasswordInput.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/src/components/PasswordInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/PasswordInput.jsx b/frontend/src/components/PasswordInput.jsx index 6d2b825..3dc2037 100644 --- a/frontend/src/components/PasswordInput.jsx +++ b/frontend/src/components/PasswordInput.jsx @@ -25,7 +25,7 @@ const PasswordInput = ({ value, onChange, placeholder = "Password", id = "passwo // Default classes if no className is provided const defaultClasses = "w-full px-4 py-2 pr-10 mt-2 border rounded-md focus:outline-none focus:ring-1"; const errorClasses = error ? 'border-red-500 focus:ring-red-500' : 'focus:ring-blue-600 dark:border-gray-600'; - const inputClasses = className || `${defaultClasses} ${errorClasses}`; + const inputClasses = `${defaultClasses} ${errorClasses} ${className ?? ''}`; return (
From d7a92dca076bf270ba92d79245e2bfc18fa75282 Mon Sep 17 00:00:00 2001 From: Yasir Akhlaque <148755054+yasirakhlaque@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:59:26 +0530 Subject: [PATCH 3/5] Update frontend/src/components/PasswordInput.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/src/components/PasswordInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/PasswordInput.jsx b/frontend/src/components/PasswordInput.jsx index 3dc2037..99e5cb3 100644 --- a/frontend/src/components/PasswordInput.jsx +++ b/frontend/src/components/PasswordInput.jsx @@ -23,7 +23,7 @@ const PasswordInput = ({ value, onChange, placeholder = "Password", id = "passwo }; // Default classes if no className is provided - const defaultClasses = "w-full px-4 py-2 pr-10 mt-2 border rounded-md focus:outline-none focus:ring-1"; + const defaultClasses = "w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring-1"; const errorClasses = error ? 'border-red-500 focus:ring-red-500' : 'focus:ring-blue-600 dark:border-gray-600'; const inputClasses = `${defaultClasses} ${errorClasses} ${className ?? ''}`; From f9848eebb822b45fd96ec775966827ae283ea7a7 Mon Sep 17 00:00:00 2001 From: Yasir Akhlaque <148755054+yasirakhlaque@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:59:37 +0530 Subject: [PATCH 4/5] Update frontend/src/pages/RegisterPage.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/src/pages/RegisterPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/RegisterPage.jsx b/frontend/src/pages/RegisterPage.jsx index 468eaec..781a520 100644 --- a/frontend/src/pages/RegisterPage.jsx +++ b/frontend/src/pages/RegisterPage.jsx @@ -128,7 +128,7 @@ export default function RegisterPage() {
- +
Date: Mon, 20 Oct 2025 10:59:55 +0530 Subject: [PATCH 5/5] Update frontend/src/pages/LoginPage.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/src/pages/LoginPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/LoginPage.jsx b/frontend/src/pages/LoginPage.jsx index 50ce20b..d3acd3b 100644 --- a/frontend/src/pages/LoginPage.jsx +++ b/frontend/src/pages/LoginPage.jsx @@ -70,7 +70,7 @@ export default function LoginPage() {
- +