From 90732d913db5302baf30adac2034305dc10a5920 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 31 Mar 2025 17:51:20 +0000 Subject: [PATCH] Added ability to save InterestInventory answers to User_Profile --- backend/server.js | 12 ++--- src/App.js | 5 +- src/components/InterestInventory.js | 56 ++++++++++++++++++-- src/components/SessionExpiredHandler.js | 34 ++++++++++++ src/components/UserProfile.js | 1 - src/utils/authFetch.js | 66 +++++++++++------------- user_profile.db | Bin 57344 -> 57344 bytes 7 files changed, 126 insertions(+), 48 deletions(-) create mode 100644 src/components/SessionExpiredHandler.js diff --git a/backend/server.js b/backend/server.js index 6128d9b..82273d2 100755 --- a/backend/server.js +++ b/backend/server.js @@ -134,7 +134,7 @@ app.post('/api/user-profile', (req, res) => { return res.status(401).json({ error: 'Invalid or expired token' }); } - const { firstName, lastName, email, zipCode, state, area, careerSituation } = req.body; + const { firstName, lastName, email, zipCode, state, area, careerSituation, interest_inventory_answers } = req.body; if (!firstName || !lastName || !email || !zipCode || !state || !area) { return res.status(400).json({ error: 'All fields are required' }); @@ -147,13 +147,13 @@ app.post('/api/user-profile', (req, res) => { return res.status(500).json({ error: 'Database error' }); } - const params = [firstName, lastName, email, zipCode, state, area, careerSituation || null, userId]; + const params = [firstName, lastName, email, zipCode, state, area, careerSituation || null, interest_inventory_answers, userId]; if (row) { // Profile exists → UPDATE const updateQuery = ` UPDATE user_profile - SET firstname = ?, lastname = ?, email = ?, zipcode = ?, state = ?, area = ?, career_situation = ? + SET firstname = ?, lastname = ?, email = ?, zipcode = ?, state = ?, area = ?, career_situation = ?, interest_inventory_answers = ? WHERE user_id = ? `; db.run(updateQuery, params, function (err) { @@ -166,8 +166,8 @@ app.post('/api/user-profile', (req, res) => { } else { // Profile doesn't exist → INSERT const insertQuery = ` - INSERT INTO user_profile (firstname, lastname, email, zipcode, state, area, career_situation, user_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + INSERT INTO user_profile (firstname, lastname, email, zipcode, state, area, career_situation, interest_inventory_answers, user_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `; db.run(insertQuery, params, function (err) { if (err) { @@ -180,8 +180,6 @@ app.post('/api/user-profile', (req, res) => { }); }); - - // Route for login app.post('/api/login', (req, res) => { const { username, password } = req.body; diff --git a/src/App.js b/src/App.js index 3184757..b2eaef0 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,6 @@ import React, { useState} from 'react'; import { Routes, Route, Navigate } from 'react-router-dom'; +import SessionExpiredHandler from './components/SessionExpiredHandler.js'; import GettingStarted from './components/GettingStarted.js'; import SignIn from './components/SignIn.js'; import SignUp from './components/SignUp.js'; @@ -10,7 +11,7 @@ import MilestoneTracker from "./components/MilestoneTracker.js"; import './App.css'; function App() { - console.log("App.js is rendering!"); + const [isAuthenticated, setIsAuthenticated] = useState(() => { return !!localStorage.getItem('token'); // Check localStorage }); @@ -50,7 +51,9 @@ function App() { {/* Catch-all for unknown routes */} } /> + + ); } diff --git a/src/components/InterestInventory.js b/src/components/InterestInventory.js index 773151a..5e04892 100644 --- a/src/components/InterestInventory.js +++ b/src/components/InterestInventory.js @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { ClipLoader } from 'react-spinners'; +import authFetch from '../utils/authFetch.js'; import './InterestInventory.css'; const InterestInventory = () => { @@ -12,7 +13,10 @@ const InterestInventory = () => { const navigate = useNavigate(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); + const [userProfile, setUserProfile] = useState(null); + const userId = localStorage.getItem('userId'); + const apiUrl = process.env.REACT_APP_API_URL || ''; const fetchQuestions = async () => { setLoading(true); // Start loading @@ -20,7 +24,7 @@ const InterestInventory = () => { const url = '/api/onet/questions?start=1&end=60'; try { - const response = await fetch(url, { + const response = await authFetch(url, { method: 'GET', headers: { Accept: 'application/json' }, }); @@ -47,8 +51,38 @@ const InterestInventory = () => { useEffect(() => { fetchQuestions(); + fetchUserProfile(); }, []); + const fetchUserProfile = async () => { + try { + const res = await authFetch('/api/user-profile', { + method: 'GET', + }); + + if (!res || !res.ok) throw new Error('Failed to fetch user profile'); + + const data = await res.json(); + setUserProfile(data); + } catch (err) { + console.error('Error fetching user profile:', err.message); + } + }; + + + + useEffect(() => { + const storedAnswers = userProfile?.interest_inventory_answers; + if (questions.length === 60 && storedAnswers && storedAnswers.length === 60) { + const restored = {}; + storedAnswers.split('').forEach((val, index) => { + restored[index + 1] = val; + }); + setResponses(restored); + } + }, [questions, userProfile]); + + const handleResponseChange = (questionIndex, value) => { setResponses((prevResponses) => ({ ...prevResponses, @@ -95,12 +129,28 @@ const InterestInventory = () => { if (!validateCurrentPage()) return; const answers = Array.from({ length: 60 }, (_, i) => responses[i + 1] || '0').join(''); - + + await authFetch(`${apiUrl}/user-profile`, { + method: 'POST', + body: JSON.stringify({ + firstName: userProfile?.firstname, + lastName: userProfile?.lastname, + email: userProfile?.email, + zipCode: userProfile?.zipcode, + state: userProfile?.state, + area: userProfile?.area, + careerSituation: userProfile?.career_situation || null, + interest_inventory_answers: answers, + }), + }); + + + try { setIsSubmitting(true); setError(null); // Clear previous errors const url = `${process.env.REACT_APP_API_URL}/onet/submit_answers`; - const response = await fetch(url, { + const response = await authFetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ answers }), diff --git a/src/components/SessionExpiredHandler.js b/src/components/SessionExpiredHandler.js new file mode 100644 index 0000000..9a3dfc6 --- /dev/null +++ b/src/components/SessionExpiredHandler.js @@ -0,0 +1,34 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { setSessionExpiredCallback } from '../utils/authFetch.js'; + +const SessionExpiredHandler = () => { + const [showModal, setShowModal] = useState(false); + const navigate = useNavigate(); + + useEffect(() => { + setSessionExpiredCallback(() => { + setShowModal(true); + }); + }, []); + + if (!showModal) return null; + + return ( +
+
+

Session Expired

+ +
+
+ ); +}; + +export default SessionExpiredHandler; diff --git a/src/components/UserProfile.js b/src/components/UserProfile.js index 2e1e3cc..39a3893 100644 --- a/src/components/UserProfile.js +++ b/src/components/UserProfile.js @@ -119,7 +119,6 @@ function UserProfile() { if (!response.ok) { throw new Error('Failed to save profile'); } - console.warn('Profile saved successfully'); console.log('Profile saved successfully'); } catch (error) { console.error('Error saving profile:', error); diff --git a/src/utils/authFetch.js b/src/utils/authFetch.js index 505b5f4..1b2a658 100644 --- a/src/utils/authFetch.js +++ b/src/utils/authFetch.js @@ -1,38 +1,32 @@ -// src/utils/authFetch.js +let onSessionExpiredCallback = null; -export const authFetch = async (url, options = {}, onUnauthorized) => { - const token = localStorage.getItem("token"); - - console.log("Token:", token); // Log token value - - if (!token) { - console.log("Token is missing, triggering onUnauthorized"); - if (typeof onUnauthorized === 'function') onUnauthorized(); - return null; - } - - const finalOptions = { - ...options, - headers: { - ...(options.headers || {}), - Authorization: `Bearer ${token}`, - }, - }; - - try { - const res = await fetch(url, finalOptions); - - console.log("Response Status:", res.status); // Log response status - - if (res.status === 401 || res.status === 403) { - console.log("Unauthorized response received, triggering onUnauthorized"); - if (typeof onUnauthorized === 'function') onUnauthorized(); - return null; - } - - return res; - } catch (err) { - console.error("Fetch error:", err); - return null; - } +export const setSessionExpiredCallback = (callback) => { + onSessionExpiredCallback = callback; }; + +const authFetch = async (url, options = {}) => { + const token = localStorage.getItem('token'); + + if (!token) { + onSessionExpiredCallback?.(); + return null; + } + + const res = await fetch(url, { + ...options, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + ...options.headers, + }, + }); + + if ([401, 403].includes(res.status)) { + onSessionExpiredCallback?.(); + return null; + } + + return res; +}; + +export default authFetch; diff --git a/user_profile.db b/user_profile.db index ec9e0adc720511a2f0824477bfe38743e7c98339..0148c9d0f9c9a5f2d9bd4bb7de385b4c7075cb6f 100644 GIT binary patch delta 202 zcmZoTz}#?vd4jZHE&~Gt9}x2ZF%uArP1G^w&t=dnTExp5!obax$iS4iG4C@YS5p}y zySS<}l>jd(bs8Th05 zYxu1;3o7XIPqvkp6KGUob-#MH#Z)Y#C} r)Y#P6$k52x*wnH-(=S1;h4q1Ka0PH-)ggQ2s