Added ability to save InterestInventory answers to User_Profile

This commit is contained in:
Josh 2025-03-31 17:51:20 +00:00
parent 6a6416499a
commit b5daa609ac
7 changed files with 126 additions and 48 deletions

View File

@ -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;

View File

@ -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 */}
<Route path="*" element={<Navigate to="/signin" />} />
</Routes>
<SessionExpiredHandler />
</div>
);
}

View File

@ -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 }),

View File

@ -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 (
<div className="modal-overlay">
<div className="modal">
<h3>Session Expired</h3>
<button
onClick={() => {
setShowModal(false); // clear modal before redirect
navigate('/signin');
}}
>
Go to Sign In
</button>
</div>
</div>
);
};
export default SessionExpiredHandler;

View File

@ -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);

View File

@ -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;

Binary file not shown.