Added ability to save InterestInventory answers to User_Profile
This commit is contained in:
parent
6a6416499a
commit
b5daa609ac
@ -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;
|
||||
|
@ -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>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 }),
|
||||
|
34
src/components/SessionExpiredHandler.js
Normal file
34
src/components/SessionExpiredHandler.js
Normal 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;
|
@ -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);
|
||||
|
@ -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;
|
||||
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user