Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions frontend/src/pages/Auth/ForgotPAssword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import React, { useState } from "react";
import Input from "../../components/Inputs/Input";
import Button from "../../components/Button/Button";
import { validateEmail } from "../../utils/helper";
import { API_PATHS } from "../../utils/apiPaths";
import axiosInstance from "../../utils/axiosinstance";
import { LuArrowRight, LuArrowLeft } from "react-icons/lu";

const ForgotPassword = ({ setCurrentPage }) => {
const [email, setEmail] = useState("");
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
const [loading, setLoading] = useState(false);

const handleSubmit = async (e) => {
e.preventDefault();

if (!validateEmail(email)) {
setError("Please enter a valid email address.");
return;
}

setError(null);
setLoading(true);

try {
await axiosInstance.post(API_PATHS.AUTH.FORGOT_PASSWORD, { email });

setSuccess(true);
setError(null);
} catch (err) {
if (err.response?.data?.message) {
setError(err.response.data.message);
} else {
setError("Failed to send reset link. Please try again.");
}
} finally {
setLoading(false);
}
};

return (
<div className="w-full relative">
<div className="relative z-10">
{/* Header */}
<div className="mb-8 text-center sm:text-left">
<div className="flex items-center justify-center sm:justify-start gap-2 mb-4">
<img
src="/PrepPilot-Logo.png"
alt="PrepPilot Logo"
className="w-8 h-8 object-contain"
/>
<span className="font-semibold text-gray-200">PrepPilot</span>
</div>

<h2 className="text-2xl sm:text-3xl font-bold text-white mb-2 tracking-tight">
Reset Password
</h2>
<p className="text-sm text-gray-400">
Enter your email and we'll send you a link to reset your password
</p>
</div>

{!success ? (
<form onSubmit={handleSubmit} className="space-y-5">
<div className="w-full">
<Input
value={email}
onChange={({ target }) => setEmail(target.value)}
label="Email Address"
placeholder="your@email.com"
type="email"
autoFocus
/>
</div>

{error && (
<div className="p-3 bg-red-500/10 border border-red-500/30 rounded-lg">
<p className="text-red-400 text-sm font-medium">{error}</p>
</div>
)}

<Button
type="submit"
loading={loading}
loadingText="Sending reset link..."
icon={
<LuArrowRight className="group-hover:translate-x-1 transition-transform" />
}
className="mt-6 w-full flex justify-center py-2.5 text-sm font-semibold shadow-lg shadow-violet-500/20 bg-gradient-to-r from-violet-600 to-blue-600 hover:from-violet-500 hover:to-blue-500 text-white rounded-lg transition-all"
>
Send Reset Link
</Button>

<div className="mt-6 text-center">
<button
type="button"
onClick={() => {
setCurrentPage("login");
setError(null);
}}
className="flex items-center justify-center gap-2 mx-auto text-sm text-gray-400 hover:text-gray-300 transition-colors"
>
<LuArrowLeft className="w-4 h-4" />
Back to Login
</button>
</div>
</form>
) : (
/* Success State */
<div className="text-center py-8">
<div className="mx-auto w-16 h-16 bg-green-500/10 rounded-full flex items-center justify-center mb-6">
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-9 h-9 text-green-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2.5}
d="M5 13l4 4L19 7"
/>
</svg>
</div>

<h3 className="text-2xl font-semibold text-white mb-3">
Check Your Email
</h3>
<p className="text-gray-400 mb-8">
We've sent a password reset link to <br />
<span className="font-medium text-white">{email}</span>
</p>

<Button
type="button"
onClick={() => {
setCurrentPage("login");
setSuccess(false);
setEmail("");
}}
className="w-full py-2.5 text-sm font-semibold bg-white/10 hover:bg-white/20 text-white rounded-lg transition-all"
>
Back to Login
</Button>
</div>
)}
</div>
</div>
);
};

export default ForgotPassword;
168 changes: 104 additions & 64 deletions frontend/src/pages/Auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,23 @@ const Login = ({ setCurrentPage, onLoginSuccess }) => {

const handleLogin = async (e) => {
e.preventDefault();
if (!validateEmail(email)) { setError("Please enter a valid email address."); return; }
if (!password) { setError("Please enter your password"); return; }
if (!validateEmail(email)) {
setError("Please enter a valid email address.");
return;
}
if (!password) {
setError("Please enter your password");
return;
}

setError("");
setLoading(true);

try {
const response = await axiosInstance.post(API_PATHS.AUTH.LOGIN, { email, password });
const response = await axiosInstance.post(API_PATHS.AUTH.LOGIN, {
email,
password,
});
const { token } = response.data;

if (token) {
Expand Down Expand Up @@ -56,87 +65,118 @@ const Login = ({ setCurrentPage, onLoginSuccess }) => {

return (
<div className="w-full relative">

<div className="relative z-10">
{/* Header */}
<div className="mb-8 text-center sm:text-left">
<div className="flex items-center justify-center sm:justify-start gap-2 mb-4">
<img src="/PrepPilot-Logo.png" alt="PrepPilot Logo" className="w-8 h-8 object-contain" />
<span className="font-semibold text-gray-200">PrepPilot</span>
</div>
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-2 tracking-tight">
Welcome Back
</h2>
<p className="text-sm text-gray-400">Sign in to continue your interview preparation journey</p>
{/* Header */}
<div className="mb-8 text-center sm:text-left">
<div className="flex items-center justify-center sm:justify-start gap-2 mb-4">
<img
src="/PrepPilot-Logo.png"
alt="PrepPilot Logo"
className="w-8 h-8 object-contain"
/>
<span className="font-semibold text-gray-200">PrepPilot</span>
</div>
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-2 tracking-tight">
Welcome Back
</h2>
<p className="text-sm text-gray-400">
Sign in to continue your interview preparation journey
</p>
</div>

<form onSubmit={handleLogin} className="space-y-5">
<div className="w-full">
<Input
value={email}
onChange={({ target }) => setEmail(target.value)}
label="Email Address"
placeholder="your@email.com"
type="text"
autoFocus
/>
</div>
<form onSubmit={handleLogin} className="space-y-5">
<div className="w-full">
<Input
value={email}
onChange={({ target }) => setEmail(target.value)}
label="Email Address"
placeholder="your@email.com"
type="text"
autoFocus
/>
</div>

<div className="w-full">
<Input
value={password}
onChange={({ target }) => setPassword(target.value)}
label="Password"
placeholder="Min 8 characters"
type="password"
/>
</div>
<div className="w-full">
<Input
value={password}
onChange={({ target }) => setPassword(target.value)}
label="Password"
placeholder="Min 8 characters"
type="password"
/>
</div>

{/* Remember Me */}
<div className="flex items-center gap-2 mt-2">
{/* Remember Me + Forgot Password */}
<div className="flex items-center justify-between mt-2">
<div className="flex items-center gap-2">
<input
id="rememberMe"
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="cursor-pointer w-4 h-4 rounded border-gray-600 bg-white"
/>
<label htmlFor="rememberMe" className="text-sm text-gray-400 cursor-pointer hover:text-gray-300 transition-colors">
<label
htmlFor="rememberMe"
className="text-sm text-gray-400 cursor-pointer hover:text-gray-300 transition-colors"
>
Remember Me
</label>
</div>

{error && (
<div id="login-error" role="alert" aria-live="polite" className="p-3 mt-4 bg-red-500/10 border border-red-500/30 rounded-lg">
<p className="text-red-400 text-sm font-medium">{error}</p>
</div>
)}

<Button
type="submit"
loading={loading}
loadingText="Signing in..."
icon={<LuArrowRight className="group-hover:translate-x-1 transition-transform" />}
className="mt-6 w-full flex justify-center py-2.5 text-sm font-semibold shadow-lg shadow-violet-500/20 bg-gradient-to-r from-violet-600 to-blue-600 hover:from-violet-500 hover:to-blue-500 text-white rounded-lg transition-all"
<button
type="button"
onClick={() => {
setCurrentPage("forgot-password"); // or "forgotPassword" depending on your page name
setError(null);
}}
className="text-sm font-medium text-blue-400 hover:text-blue-300 transition-colors cursor-pointer"
>
Sign In
</Button>
Forgot Password?
</button>
</div>

<div className="mt-8 pt-6 border-t border-white/10">
<p className="text-sm text-gray-400 text-center">
Don't have an account?{" "}
<button
type="button"
className="font-semibold text-blue-400 hover:text-blue-300 transition-colors cursor-pointer ml-1"
onClick={() => { setCurrentPage("signup"); setError(null); }}
>
Create account
</button>
</p>
{error && (
<div
id="login-error"
role="alert"
aria-live="polite"
className="p-3 mt-4 bg-red-500/10 border border-red-500/30 rounded-lg"
>
<p className="text-red-400 text-sm font-medium">{error}</p>
</div>
</form>
</div>
)}

<Button
type="submit"
loading={loading}
loadingText="Signing in..."
icon={
<LuArrowRight className="group-hover:translate-x-1 transition-transform" />
}
className="mt-6 w-full flex justify-center py-2.5 text-sm font-semibold shadow-lg shadow-violet-500/20 bg-gradient-to-r from-violet-600 to-blue-600 hover:from-violet-500 hover:to-blue-500 text-white rounded-lg transition-all"
>
Sign In
</Button>

<div className="mt-8 pt-6 border-t border-white/10">
<p className="text-sm text-gray-400 text-center">
Don't have an account?{" "}
<button
type="button"
className="font-semibold text-blue-400 hover:text-blue-300 transition-colors cursor-pointer ml-1"
onClick={() => {
setCurrentPage("signup");
setError(null);
}}
>
Create account
</button>
</p>
</div>
</form>
</div>
</div>
);
};

Expand Down