Added RIASEC to user_profile

This commit is contained in:
Josh 2025-06-09 14:17:53 +00:00
parent 87effa1a86
commit 112d6eec56
3 changed files with 118 additions and 18 deletions

View File

@ -337,7 +337,7 @@ app.get('/api/check-username/:username', (req, res) => {
/**
* POST /api/user-profile
* Headers: { Authorization: Bearer <token> }
* 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 = ?,
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)
------------------------------------------------------------------ */

View File

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

View File

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