dev1/src/components/SignIn.js

143 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useRef, useState, useEffect, useContext } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { ProfileCtx } from '../App.js';
function SignIn({ setIsAuthenticated, setUser }) {
const navigate = useNavigate();
const { setFinancialProfile, setScenario } = useContext(ProfileCtx);
const usernameRef = useRef('');
const passwordRef = useRef('');
const [error, setError] = useState('');
const [showSessionExpiredMsg, setShowSessionExpiredMsg] = useState(false);
const location = useLocation();
const apiUrl = process.env.REACT_APP_API_URL;
useEffect(() => {
// Check if the URL query param has ?session=expired
const query = new URLSearchParams(location.search);
if (query.get('session') === 'expired') {
setShowSessionExpiredMsg(true);
}
}, [location.search]);
const handleSignIn = async (event) => {
event.preventDefault();
setError('');
// 0⃣ clear everything that belongs to the *previous* user
localStorage.removeItem('careerSuggestionsCache');
localStorage.removeItem('lastSelectedCareerProfileId');
localStorage.removeItem('aiClickCount');
localStorage.removeItem('aiClickDate');
localStorage.removeItem('aiRecommendations');
localStorage.removeItem('premiumOnboardingState');
localStorage.removeItem('financialProfile'); // if you cache it
localStorage.removeItem('selectedScenario');
const username = usernameRef.current.value;
const password = passwordRef.current.value;
if (!username || !password) {
setError('Please enter both username and password');
return;
}
try {
const resp = await fetch(`${apiUrl}/signin`, {
method : 'POST',
headers: { 'Content-Type': 'application/json' },
body : JSON.stringify(username, password),
});
const data = await resp.json(); // ← read ONCE
if (!resp.ok) throw new Error(data.error || 'Failed to sign in');
/* ---------------- success path ---------------- */
const { token, id, user } = data;
// fetch current user profile immediately
const profileRes = await fetch('/api/user-profile', {
headers: { Authorization: `Bearer ${token}` }
});
const profile = await profileRes.json();
setFinancialProfile(profile);
setScenario(null); // or fetch latest scenario separately
/* purge any leftovers from prior session */
['careerSuggestionsCache',
'lastSelectedCareerProfileId',
'aiClickCount',
'aiClickDate',
'aiRecommendations',
'premiumOnboardingState',
'financialProfile',
'selectedScenario'
].forEach(k => localStorage.removeItem(k));
/* store new session data */
localStorage.setItem('token', token);
localStorage.setItem('id', id);
setIsAuthenticated(true);
setUser(user);
navigate('/signin-landing');
} catch (err) {
setError(err.message);
}
};
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-100 p-4">
{showSessionExpiredMsg && (
<div className="mb-4 p-2 bg-red-100 border border-red-300 text-red-700 rounded">
Your session has expired. Please sign in again.
</div>
)}
<div className="w-full max-w-sm rounded-md bg-white p-6 shadow-md">
<h1 className="mb-6 text-center text-2xl font-semibold">Sign In</h1>
{error && (
<p className="mb-4 rounded bg-red-50 p-2 text-sm text-red-600">
{error}
</p>
)}
<form onSubmit={handleSignIn} className="flex flex-col space-y-4">
<input
type="text"
placeholder="Username"
ref={usernameRef}
className="w-full rounded border border-gray-300 p-2 focus:border-blue-500 focus:outline-none"
/>
<input
type="password"
placeholder="Password"
ref={passwordRef}
className="w-full rounded border border-gray-300 p-2 focus:border-blue-500 focus:outline-none"
/>
<button
type="submit"
className="mx-auto rounded bg-blue-600 px-6 py-2 text-center text-white transition-colors hover:bg-blue-700 focus:outline-none"
>
Sign In
</button>
</form>
<p className="mt-4 text-center text-sm text-gray-600">
Dont have an account?{' '}
<Link
to="/signup" // <- here
className="font-medium text-blue-600 hover:text-blue-500"
>
Sign Up
</Link>
</p>
</div>
</div>
);
}
export default SignIn;