Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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
99 changes: 99 additions & 0 deletions Backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Routes, Route, useLocation } from "react-router-dom";

import Login from "./pages/Login";
import Register from "./pages/Register";
import ForgotPassword from "./pages/ForgotPassword";
import AuthCallback from "./pages/AuthCallback";
import Dashboard from "./pages/Dashboard";
import ProtectedRoute from "./components/ProtectedRoute";
Expand Down Expand Up @@ -43,7 +44,7 @@ import Lenis from "@studio-freight/lenis";
function App() {
const location = useLocation();

const hideFooterRoutes = ["/login", "/register"];
const hideFooterRoutes = ["/login", "/register", "/forgot-password"];
const hideFooter = hideFooterRoutes.includes(location.pathname);

// Initialize smooth scrolling with Lenis
Expand All @@ -70,7 +71,10 @@ function App() {
};
}, []);
Comment on lines 55 to 80

Copilot AI Feb 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Lenis smooth scrolling is being initialized globally in App.jsx and will run on every page, including pages where it might conflict with existing scroll behavior (e.g., dashboard, interview room, settings pages). The useEffect dependency array is empty, meaning it will only initialize once on mount and won't respond to route changes. Consider either:

  1. Only initializing Lenis on specific pages (like Landing, FAQ) where smooth scrolling is desired
  2. Adding route-specific logic to disable Lenis on certain pages
  3. Testing thoroughly on all pages to ensure no conflicts

The Landing and PricingPage already initialize their own Lenis instances, which could lead to duplicate initialization conflicts.

Copilot uses AI. Check for mistakes.



useEffect(() => {

const hideNavbarRoutes = ["/dashboard", "/settings", "/pricing", "/career", "/terms", "/privacy", "/cookie-policy", "/interview-setup", "/auth/callback"];
const hideNavbar =
hideNavbarRoutes.includes(location.pathname) ||
Expand Down Expand Up @@ -129,6 +133,7 @@ useEffect(() => {
<Route path="/" element={<Landing />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
<Route path="/verify-email" element={<VerifyEmail />} />
<Route path="/auth/callback" element={<AuthCallback />} />
<Route path="/faq" element={<FAQ />} />
Expand Down
204 changes: 204 additions & 0 deletions Frontend/src/pages/ForgotPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import SEO from "../components/SEO";

export default function ForgotPassword() {
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState("");
const [error, setError] = useState("");
const [emailError, setEmailError] = useState("");
const navigate = useNavigate();

// Email validation regex
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

const validateEmail = (emailValue) => {
if (!emailValue) {
setEmailError("");
return false;
}
if (!emailRegex.test(emailValue)) {
setEmailError("Please enter a valid email address");
return false;
}
setEmailError("");
return true;
};

const handleEmailChange = (e) => {
const value = e.target.value;
setEmail(value);
if (value) {
validateEmail(value);
} else {
setEmailError("");
}
};

const handleEmailBlur = () => {
if (email) {
validateEmail(email);
}
};

const handleSendOTP = async (e) => {
e.preventDefault();
setError("");
setMessage("");

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

setLoading(true);

try {
// TODO: Implement actual OTP sending logic here
// Example API call:
// const response = await fetch('/api/auth/forgot-password', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({ email })
// });

// Simulated success for now
setTimeout(() => {
setLoading(false);
setMessage("OTP sent successfully! Please check your email.");
}, 1500);
} catch {

Copilot AI Feb 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch block doesn't capture the error parameter, making it impossible to provide specific error messages to the user. Change catch { to catch (err) { and consider logging the error or providing more specific error messages based on the error type. This is especially important for debugging and providing better user feedback when the actual API is implemented.

Suggested change
} catch {
} catch (err) {
console.error("Failed to send OTP:", err);

Copilot uses AI. Check for mistakes.
setLoading(false);
setError("Failed to send OTP. Please try again.");
}
};

return (
<div className="relative min-h-screen overflow-hidden bg-black text-white">
<SEO
title="Forgot Password – Intervyo"
description="Reset your Intervyo account password."
url="https://intervyo.xyz/forgot-password"
/>

{/* πŸ”³ TILE GRID BACKGROUND */}
<div className="absolute inset-0 grid grid-cols-[repeat(auto-fill,minmax(60px,1fr))] grid-rows-[repeat(auto-fill,minmax(60px,1fr))] pointer-events-auto">
{Array.from({ length: 800 }).map((_, i) => (
<div
key={i}
className="
border border-white/5
transition-colors duration-90 ease-out
hover:bg-[#10b981]
"
/>
))}
</div>
Comment on lines +87 to +98

Copilot AI Feb 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rendering 800 div elements for the grid background is inefficient and can impact performance. Consider using CSS gradients or pseudo-elements to create the grid pattern instead, which would be much more performant. For example, you could use background-image: repeating-linear-gradient() or a single div with a background pattern.

Suggested change
<div className="absolute inset-0 grid grid-cols-[repeat(auto-fill,minmax(60px,1fr))] grid-rows-[repeat(auto-fill,minmax(60px,1fr))] pointer-events-auto">
{Array.from({ length: 800 }).map((_, i) => (
<div
key={i}
className="
border border-white/5
transition-colors duration-90 ease-out
hover:bg-[#10b981]
"
/>
))}
</div>
<div
className="absolute inset-0 pointer-events-none"
style={{
backgroundImage:
"repeating-linear-gradient(to right, rgba(255,255,255,0.05) 0 1px, transparent 1px 60px), repeating-linear-gradient(to bottom, rgba(255,255,255,0.05) 0 1px, transparent 1px 60px)",
}}
/>

Copilot uses AI. Check for mistakes.

{/* Glow layer */}
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className="w-[420px] h-[420px] rounded-full bg-emerald-500 blur-[80px]" />
</div>

{/* CENTERED FORGOT PASSWORD CARD */}
<div className="relative z-10 flex flex-col items-center justify-center min-h-screen p-4 pt-32 pb-12 pointer-events-none">
<div
className="pointer-events-auto w-full max-w-md rounded-2xl border border-white/10
bg-gradient-to-br from-zinc-900/90 to-zinc-800/80
backdrop-blur-xl shadow-[0_0_60px_rgba(16,185,129,0.15)] p-8"
>
{/* Logo/Brand */}
<div className="text-center mb-8">
<div className="flex items-center justify-center mb-6">
<h1 className="text-4xl font-bold">
<span className="text-white">Interv</span>
<span className="text-emerald-500">yo</span>
</h1>
</div>
<h2 className="text-2xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent">
Forgot Password?
</h2>
<p className="text-gray-400 mt-2">
Enter your email to receive an OTP for password reset
</p>
</div>

{/* Success Message */}
{message && (
<div className="bg-emerald-500/10 border border-emerald-500/30 text-emerald-400 px-4 py-3 rounded-lg mb-4">
{message}
</div>
)}

{/* Error Message */}
{error && (
<div className="bg-red-500/10 border border-red-500/30 text-red-400 px-4 py-3 rounded-lg mb-4">
{error}
</div>
)}

<form onSubmit={handleSendOTP} className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Email Address
</label>
<input
type="email"
value={email}
onChange={handleEmailChange}
onBlur={handleEmailBlur}
placeholder="you@example.com"
pattern="[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
className={`w-full px-4 py-3 rounded-lg bg-zinc-900 border ${
emailError
? "border-red-500 focus:ring-2 focus:ring-red-500"
: "border-zinc-700 focus:ring-2 focus:ring-emerald-500"
}
text-white placeholder-gray-500
outline-none transition-all`}
required
/>
{emailError && (
<p className="mt-2 text-sm text-red-400">{emailError}</p>
)}
</div>

<button
type="submit"
disabled={loading || !email || emailError}
className="relative w-full overflow-hidden rounded-lg bg-emerald-500 py-3 font-semibold text-black
transition-all duration-300
hover:scale-[1.02] hover:shadow-[0_0_25px_rgba(16,185,129,0.8)]
active:scale-95 disabled:opacity-50 disabled:grayscale disabled:cursor-not-allowed"
>
<span className="relative z-10">
{loading ? "Sending OTP..." : "Send OTP"}
</span>
<span className="absolute inset-0 bg-gradient-to-r from-emerald-400 to-emerald-600 opacity-0 hover:opacity-100 transition-opacity" />
</button>
</form>

<div className="mt-6 text-center space-y-2">
<button
onClick={() => navigate("/login")}
className="text-emerald-400 font-semibold hover:underline transition-colors"
>
← Back to Login
</button>
<p className="text-gray-400 text-sm">
Don&apos;t have an account?{" "}
<a
href="/register"
className="text-emerald-400 font-semibold hover:underline"
>
Sign up
</a>
</p>
</div>
</div>
</div>
</div>
);
}
Loading