From 112d6eec560f389043441ee6eb2eb6ba8ee875b8 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 9 Jun 2025 14:17:53 +0000 Subject: [PATCH] Added RIASEC to user_profile --- backend/server.js | 77 ++++++++++++++++++++++------- backend/server2.js | 37 ++++++++++++++ src/components/InterestInventory.js | 22 +++++++++ 3 files changed, 118 insertions(+), 18 deletions(-) diff --git a/backend/server.js b/backend/server.js index d0f5e35..6dcf9e1 100755 --- a/backend/server.js +++ b/backend/server.js @@ -337,7 +337,7 @@ app.get('/api/check-username/:username', (req, res) => { /** * POST /api/user-profile * Headers: { Authorization: Bearer } - * Body: { firstName, lastName, email, zipCode, state, area, ... } + * Body: { userName, firstName, lastName, email, zipCode, state, area, ... } * * If user_profile row exists (id = token.id), update * else insert @@ -358,6 +358,7 @@ app.post('/api/user-profile', (req, res) => { } const { + userName, firstName, lastName, email, @@ -366,13 +367,14 @@ app.post('/api/user-profile', (req, res) => { area, careerSituation, interest_inventory_answers, + riasec_scores, // new JSON with { R:15, I:22, A:..., S:..., E:..., C:... } career_priorities, career_list, } = req.body; // Check if profile row exists pool.query( - `SELECT * FROM user_profile WHERE id = ?`, + 'SELECT * FROM user_profile WHERE id = ?', [profileId], (err, results) => { if (err) { @@ -391,31 +393,66 @@ app.post('/api/user-profile', (req, res) => { .json({ error: 'All fields are required for initial profile creation.' }); } + // Final handling of interest inventory answers const finalAnswers = interest_inventory_answers !== undefined ? interest_inventory_answers : existingRow?.interest_inventory_answers || null; + // final career priorities const finalCareerPriorities = career_priorities !== undefined ? career_priorities : existingRow?.career_priorities || null; + // final career list const finalCareerList = career_list !== undefined ? career_list : existingRow?.career_list || null; + // final userName + const finalUserName = + userName !== undefined + ? userName + : existingRow?.username || null; + + // final RIASEC JSON + // If you passed something in "riasec_scores", store as JSON + // If none passed, keep existing or null + let finalRiasecScores; + if (riasec_scores !== undefined) { + if (riasec_scores) { + finalRiasecScores = JSON.stringify(riasec_scores); + } else { + // if it's empty or falsey, set to null or keep existing + finalRiasecScores = null; + } + } else { + finalRiasecScores = existingRow?.riasec_scores || null; + } + if (existingRow) { - // Update the existing user_profile + // Update existing row const updateQuery = ` UPDATE user_profile - SET firstname = ?, lastname = ?, email = ?, zipcode = ?, state = ?, area = ?, - career_situation = ?, interest_inventory_answers = ?, career_priorities = ?, - career_list = ? + SET + username = ?, + firstname = ?, + lastname = ?, + email = ?, + zipcode = ?, + state = ?, + area = ?, + career_situation = ?, + interest_inventory_answers = ?, + riasec_scores = ?, -- new field + career_priorities = ?, + career_list = ? WHERE id = ? `; const params = [ + finalUserName, firstName || existingRow.firstname, lastName || existingRow.lastname, email || existingRow.email, @@ -424,9 +461,10 @@ app.post('/api/user-profile', (req, res) => { area || existingRow.area, careerSituation || existingRow.career_situation, finalAnswers, + finalRiasecScores, // JSON string finalCareerPriorities, finalCareerList, - profileId, + profileId ]; pool.query(updateQuery, params, (err2) => { @@ -436,20 +474,20 @@ app.post('/api/user-profile', (req, res) => { .status(500) .json({ error: 'Failed to update user profile' }); } - return res - .status(200) - .json({ message: 'User profile updated successfully' }); + return res.status(200).json({ message: 'User profile updated successfully' }); }); } else { - // Insert a new profile (the user_auth record exists, but the user_profile row is missing) + // Insert a new profile const insertQuery = ` INSERT INTO user_profile - (id, firstname, lastname, email, zipcode, state, area, career_situation, - interest_inventory_answers, career_priorities, career_list) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + (id, username, firstname, lastname, email, zipcode, state, area, + career_situation, interest_inventory_answers, riasec_scores, + career_priorities, career_list) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `; const params = [ - profileId, // Force the row's primary key to match the existing user ID + profileId, + finalUserName, firstName, lastName, email, @@ -458,6 +496,7 @@ app.post('/api/user-profile', (req, res) => { area, careerSituation || null, finalAnswers, + finalRiasecScores, // JSON string or null finalCareerPriorities, finalCareerList, ]; @@ -469,15 +508,17 @@ app.post('/api/user-profile', (req, res) => { .status(500) .json({ error: 'Failed to create user profile' }); } - return res - .status(201) - .json({ message: 'User profile created successfully', id: profileId }); + return res.status(201).json({ + message: 'User profile created successfully', + id: profileId + }); }); } } ); }); + /* ------------------------------------------------------------------ FETCH USER PROFILE (MySQL) ------------------------------------------------------------------ */ diff --git a/backend/server2.js b/backend/server2.js index 6f35820..c02d699 100755 --- a/backend/server2.js +++ b/backend/server2.js @@ -349,6 +349,24 @@ app.post('/api/onet/submit_answers', async (req, res) => { // filter out lower ed const filtered = filterHigherEducationCareers(careerSuggestions); + const riasecCode = convertToRiasecCode(riaSecScores); + + const token = req.headers.authorization?.split(' ')[1]; + if (token) { + try { + await axios.post(`${process.env.MAIN_API_URL}/api/user-profile`, + { + interest_inventory_answers: answers, + riasec: riasecCode + }, + { headers: { Authorization: `Bearer ${token}` } } + ); + } catch (err) { + console.error('Error storing RIASEC in user_profile =>', err.response?.data || err.message); + // fallback if needed + } + } + res.status(200).json({ careers: filtered, riaSecScores, @@ -379,6 +397,25 @@ function filterHigherEducationCareers(careers) { .filter(Boolean); } +function convertToRiasecCode(riaSecScores) { + // We assume each item has { area, score, description } + // Sort them by area in R, I, A, S, E, C order or by highest score, whichever you prefer: + + // Sort by standard R -> I -> A -> S -> E -> C ordering: + const order = { Realistic: 0, Investigative: 1, Artistic: 2, Social: 3, Enterprising: 4, Conventional: 5 }; + // or you can sort by descending score: + // const sorted = [...riaSecScores].sort((a, b) => b.score - a.score); + + // For this example, let's do the standard R -> I -> A -> S -> E -> C + const sorted = [...riaSecScores].sort((a, b) => order[a.area] - order[b.area]); + + // Now build the 6-letter code + // e.g. "RI" + "A" + ... + // If you want to show tie-breaking or real logic, you can do so + return sorted.map(item => item.area[0].toUpperCase()).join(''); + // e.g. "RIASEC" +} + // ONet career details app.get('/api/onet/career-details/:socCode', async (req, res) => { const { socCode } = req.params; diff --git a/src/components/InterestInventory.js b/src/components/InterestInventory.js index d40c561..82f1276 100644 --- a/src/components/InterestInventory.js +++ b/src/components/InterestInventory.js @@ -3,6 +3,18 @@ import { useNavigate } from 'react-router-dom'; import { ClipLoader } from 'react-spinners'; import authFetch from '../utils/authFetch.js'; + +function mapScores(riaSecScores) { + const map = {}; + // e.g. area = "Realistic" => letter "R" + riaSecScores.forEach(obj => { + const letter = obj.area[0].toUpperCase(); // 'R', 'I', 'A', 'S', 'E', 'C' + map[letter] = obj.score; + }); + return map; // e.g. { R:15, I:22, A:20, S:30, E:25, C:24 } +} + + const InterestInventory = () => { const [questions, setQuestions] = useState([]); const [responses, setResponses] = useState({}); @@ -156,6 +168,16 @@ const InterestInventory = () => { const { careers: careerSuggestions, riaSecScores } = data; if (Array.isArray(careerSuggestions) && Array.isArray(riaSecScores)) { + // 4) Convert those scores to a short code + const scoresMap = mapScores(riaSecScores); // { R:15, I:22, ... } + await authFetch('/api/user-profile', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + riasec_scores: scoresMap // store in DB as a JSON string + }), + }); + navigate('/career-explorer', { state: { careerSuggestions, riaSecScores, fromInterestInventory: true } }); } else { throw new Error('Invalid data format from the server.');