Fixed Priorities Modal Interests by adding new InterestsMeaningModal.js

This commit is contained in:
Josh 2025-06-09 17:04:56 +00:00
parent 112d6eec56
commit 8166a1119c
6 changed files with 185 additions and 54 deletions

View File

@ -161,12 +161,12 @@ app.post('/api/register', async (req, res) => {
// 1) Insert into user_profile // 1) Insert into user_profile
const profileQuery = ` const profileQuery = `
INSERT INTO user_profile INSERT INTO user_profile
(firstname, lastname, email, zipcode, state, area, career_situation) (username, firstname, lastname, email, zipcode, state, area, career_situation)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`; `;
pool.query( pool.query(
profileQuery, profileQuery,
[firstname, lastname, email, zipcode, state, area, career_situation], [username, firstname, lastname, email, zipcode, state, area, career_situation],
(errProfile, resultProfile) => { (errProfile, resultProfile) => {
if (errProfile) { if (errProfile) {
console.error('Error inserting user_profile:', errProfile.message); console.error('Error inserting user_profile:', errProfile.message);
@ -203,6 +203,7 @@ app.post('/api/register', async (req, res) => {
// Optionally fetch or build a user object. We already know: // Optionally fetch or build a user object. We already know:
// firstname, lastname, email, etc. from the request body. // firstname, lastname, email, etc. from the request body.
const userPayload = { const userPayload = {
username,
firstname, firstname,
lastname, lastname,
email, email,
@ -367,14 +368,14 @@ app.post('/api/user-profile', (req, res) => {
area, area,
careerSituation, careerSituation,
interest_inventory_answers, interest_inventory_answers,
riasec_scores, // new JSON with { R:15, I:22, A:..., S:..., E:..., C:... } riasec: riasec_scores, // NEW
career_priorities, career_priorities,
career_list, career_list,
} = req.body; } = req.body;
// Check if profile row exists // Check if profile row exists
pool.query( pool.query(
'SELECT * FROM user_profile WHERE id = ?', `SELECT * FROM user_profile WHERE id = ?`,
[profileId], [profileId],
(err, results) => { (err, results) => {
if (err) { if (err) {
@ -417,27 +418,18 @@ app.post('/api/user-profile', (req, res) => {
? userName ? userName
: existingRow?.username || null; : existingRow?.username || null;
// final RIASEC JSON // final RIASEC scores
// If you passed something in "riasec_scores", store as JSON const finalRiasec = req.body.riasec_scores
// If none passed, keep existing or null ? JSON.stringify(req.body.riasec_scores)
let finalRiasecScores; : existingRow.riasec_scores || null;
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) { if (existingRow) {
// Update existing row // Update existing row
const updateQuery = ` const updateQuery = `
UPDATE user_profile UPDATE user_profile
SET SET
username = ?, username = ?, -- NEW
firstname = ?, firstname = ?,
lastname = ?, lastname = ?,
email = ?, email = ?,
@ -446,7 +438,7 @@ app.post('/api/user-profile', (req, res) => {
area = ?, area = ?,
career_situation = ?, career_situation = ?,
interest_inventory_answers = ?, interest_inventory_answers = ?,
riasec_scores = ?, -- new field riasec_scores = ?, -- NEW
career_priorities = ?, career_priorities = ?,
career_list = ? career_list = ?
WHERE id = ? WHERE id = ?
@ -461,10 +453,10 @@ app.post('/api/user-profile', (req, res) => {
area || existingRow.area, area || existingRow.area,
careerSituation || existingRow.career_situation, careerSituation || existingRow.career_situation,
finalAnswers, finalAnswers,
finalRiasecScores, // JSON string finalRiasec,
finalCareerPriorities, finalCareerPriorities,
finalCareerList, finalCareerList,
profileId profileId,
]; ];
pool.query(updateQuery, params, (err2) => { pool.query(updateQuery, params, (err2) => {
@ -474,14 +466,16 @@ app.post('/api/user-profile', (req, res) => {
.status(500) .status(500)
.json({ error: 'Failed to update user profile' }); .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 { } else {
// Insert a new profile // Insert a new profile
const insertQuery = ` const insertQuery = `
INSERT INTO user_profile INSERT INTO user_profile
(id, username, firstname, lastname, email, zipcode, state, area, (id, username, firstname, lastname, email, zipcode, state, area,
career_situation, interest_inventory_answers, riasec_scores, career_situation, interest_inventory_answers, riasec,
career_priorities, career_list) career_priorities, career_list)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`; `;
@ -496,7 +490,7 @@ app.post('/api/user-profile', (req, res) => {
area, area,
careerSituation || null, careerSituation || null,
finalAnswers, finalAnswers,
finalRiasecScores, // JSON string or null finalRiasec,
finalCareerPriorities, finalCareerPriorities,
finalCareerList, finalCareerList,
]; ];
@ -508,10 +502,9 @@ app.post('/api/user-profile', (req, res) => {
.status(500) .status(500)
.json({ error: 'Failed to create user profile' }); .json({ error: 'Failed to create user profile' });
} }
return res.status(201).json({ return res
message: 'User profile created successfully', .status(201)
id: profileId .json({ message: 'User profile created successfully', id: profileId });
});
}); });
} }
} }

View File

@ -4,6 +4,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
import CareerSuggestions from './CareerSuggestions.js'; import CareerSuggestions from './CareerSuggestions.js';
import CareerPrioritiesModal from './CareerPrioritiesModal.js'; import CareerPrioritiesModal from './CareerPrioritiesModal.js';
import CareerModal from './CareerModal.js'; import CareerModal from './CareerModal.js';
import InterestMeaningModal from './InterestMeaningModal.js';
import CareerSearch from './CareerSearch.js'; import CareerSearch from './CareerSearch.js';
import { Button } from './ui/button.js'; import { Button } from './ui/button.js';
import axios from 'axios'; import axios from 'axios';
@ -28,8 +29,11 @@ const STATES = [
{ name: 'West Virginia', code: 'WV' }, { name: 'Wisconsin', code: 'WI' }, { name: 'Wyoming', code: 'WY' }, { name: 'West Virginia', code: 'WV' }, { name: 'Wisconsin', code: 'WI' }, { name: 'Wyoming', code: 'WY' },
]; ];
// -------------- CIP HELPER FUNCTIONS -------------- // -------------- CIP HELPER FUNCTIONS --------------
// 1) Insert leading zero if there's only 1 digit before the decimal // 1) Insert leading zero if there's only 1 digit before the decimal
function ensureTwoDigitsBeforeDecimal(cipStr) { function ensureTwoDigitsBeforeDecimal(cipStr) {
// e.g. "4.0201" => "04.0201" // e.g. "4.0201" => "04.0201"
@ -66,7 +70,21 @@ function CareerExplorer() {
const [userZipcode, setUserZipcode] = useState(null); const [userZipcode, setUserZipcode] = useState(null);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [pendingCareerForModal, setPendingCareerForModal] = useState(null); const [pendingCareerForModal, setPendingCareerForModal] = useState(null);
const [showInterestMeaningModal, setShowInterestMeaningModal] = useState(false);
const [modalData, setModalData] = useState({
career: null,
askForInterest: false,
defaultInterest: 3,
defaultMeaning: 3,
});
// ...
const fitRatingMap = {
Best: 5,
Great: 4,
Good: 3,
};
// This is where we'll hold ALL final suggestions (with job_zone merged) // This is where we'll hold ALL final suggestions (with job_zone merged)
const [careerSuggestions, setCareerSuggestions] = useState([]); const [careerSuggestions, setCareerSuggestions] = useState([]);
@ -560,30 +578,48 @@ function CareerExplorer() {
} }
}; };
// ------------------------------------------------------ // ------------------------------------------------------
// Add/Remove from comparison // Add/Remove from comparison
// ------------------------------------------------------ // ------------------------------------------------------
const addCareerToList = (career) => { const addCareerToList = (career) => {
// 1) get default (pre-calculated) ratings from your JSON
const masterRatings = getCareerRatingsBySocCode(career.code); const masterRatings = getCareerRatingsBySocCode(career.code);
const fitRatingMap = { // 2) figure out interest
Best: 5, const userHasInventory = priorities.interests !== "Im not sure yet";
Great: 4, const defaultInterestValue =
Good: 3, userHasInventory
}; ? // if user has done inventory, we rely on fit rating or fallback to .json
(fitRatingMap[career.fit] || masterRatings.interests || 3)
: // otherwise, just start them at 3 (we'll ask in the modal)
3;
const interestsRating = // 3) always ask for meaning, start at 3
priorities.interests === "Im not sure yet" const defaultMeaningValue = 3;
? parseInt(prompt("Rate your interest in this career (1-5):", "3"), 10)
: fitRatingMap[career.fit] || masterRatings.interests || 3; // 4) open the InterestMeaningModal instead of using prompt()
setModalData({
career,
masterRatings,
askForInterest: !userHasInventory,
defaultInterest: defaultInterestValue,
defaultMeaning: defaultMeaningValue,
});
setShowInterestMeaningModal(true);
};
const handleModalSave = ({ interest, meaning }) => {
const { career, masterRatings, askForInterest, defaultInterest } = modalData;
if (!career) return;
// If we asked for interest, use the user's input; otherwise keep the default
const finalInterest = askForInterest && interest !== null
? interest
: defaultInterest;
const finalMeaning = meaning;
const meaningRating = parseInt(
prompt(
"How important do you feel this job is to society or the world? (1-5):",
"3"
),
10
);
const stabilityRating = const stabilityRating =
career.ratings && career.ratings.stability !== undefined career.ratings && career.ratings.stability !== undefined
@ -597,8 +633,8 @@ function CareerExplorer() {
const careerWithUserRatings = { const careerWithUserRatings = {
...career, ...career,
ratings: { ratings: {
interests: interestsRating, interests: finalInterest,
meaning: meaningRating, meaning: finalMeaning,
stability: stabilityRating, stability: stabilityRating,
growth: growthRating, growth: growthRating,
balance: balanceRating, balance: balanceRating,
@ -925,6 +961,16 @@ function CareerExplorer() {
}} }}
/> />
<InterestMeaningModal
show={showInterestMeaningModal}
onClose={() => setShowInterestMeaningModal(false)}
onSave={handleModalSave}
careerTitle={modalData.career?.title || ""}
askForInterest={modalData.askForInterest}
defaultInterest={modalData.defaultInterest}
defaultMeaning={modalData.defaultMeaning}
/>
{selectedCareer && ( {selectedCareer && (
<CareerModal <CareerModal
career={selectedCareer} career={selectedCareer}

View File

@ -92,6 +92,7 @@ function CareerModal({ career, careerDetails, closeModal, addCareerToList }) {
stability: stabilityRating, stability: stabilityRating,
}, },
}); });
closeModal();
}} }}
className="text-white bg-green-500 hover:bg-green-600 rounded px-3 py-1" className="text-white bg-green-500 hover:bg-green-600 rounded px-3 py-1"
> >

View File

@ -10,11 +10,12 @@ const CareerPrioritiesModal = ({ userProfile, onClose }) => {
} }
}, [userProfile]); }, [userProfile]);
// Updated "interests" question:
const questions = [ const questions = [
{ {
id: 'interests', id: 'interests',
text: 'What kinds of activities do you naturally enjoy?', text: 'How important is it that your career aligns with your personal interests?',
options: ['I know my interests (completed inventory)', 'Im not sure yet'], options: ['Very important', 'Somewhat important', 'Not as important'],
}, },
{ {
id: 'meaning', id: 'meaning',
@ -61,13 +62,11 @@ const CareerPrioritiesModal = ({ userProfile, onClose }) => {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload), body: JSON.stringify(payload),
}); });
onClose(); onClose();
} catch (error) { } catch (error) {
console.error('Error saving priorities:', error); console.error('Error saving priorities:', error);
} }
}; };
const allAnswered = questions.every(q => responses[q.id]); const allAnswered = questions.every(q => responses[q.id]);
@ -100,7 +99,9 @@ const CareerPrioritiesModal = ({ userProfile, onClose }) => {
<button <button
onClick={handleSave} onClick={handleSave}
disabled={!allAnswered} disabled={!allAnswered}
className={`px-4 py-2 rounded ${allAnswered ? 'bg-blue-600 text-white' : 'bg-gray-300 cursor-not-allowed'}`} className={`px-4 py-2 rounded ${
allAnswered ? 'bg-blue-600 text-white' : 'bg-gray-300 cursor-not-allowed'
}`}
> >
Save Answers Save Answers
</button> </button>

View File

@ -0,0 +1,90 @@
import React, { useState, useEffect } from 'react';
import { Button } from './ui/button.js';
// This modal will ONLY ask for Interest & Meaning.
function InterestMeaningModal({
show,
onClose,
onSave,
careerTitle,
askForInterest, // boolean
defaultInterest, // number
defaultMeaning, // number
}) {
const [interestValue, setInterestValue] = useState(defaultInterest || 3);
const [meaningValue, setMeaningValue] = useState(defaultMeaning || 3);
// If `defaultInterest` or `defaultMeaning` change while open, reset local state
useEffect(() => {
setInterestValue(defaultInterest || 3);
}, [defaultInterest]);
useEffect(() => {
setMeaningValue(defaultMeaning || 3);
}, [defaultMeaning]);
if (!show) {
return null; // Do not render anything if show=false
}
const handleSave = () => {
onSave({
interest: askForInterest ? interestValue : null,
meaning: meaningValue,
});
onClose();
};
return (
<div className="fixed inset-0 z-60 bg-black bg-opacity-40 flex items-center justify-center">
<div className="bg-white p-4 rounded shadow-lg w-full max-w-sm">
<h2 className="text-xl font-semibold mb-4">
Add "{careerTitle}" to Comparison
</h2>
{/* If the user is "not sure yet," ask for interest rating */}
{askForInterest && (
<div className="mb-4">
<label className="block font-medium mb-1">
Your Interest in This Career (15)
</label>
<input
type="number"
min="1"
max="5"
value={interestValue}
onChange={(e) => setInterestValue(Number(e.target.value))}
className="w-full border rounded px-3 py-1"
/>
</div>
)}
{/* Always ask for meaning rating */}
<div className="mb-4">
<label className="block font-medium mb-1">
How Meaningful is This Career to You? (15)
</label>
<input
type="number"
min="1"
max="5"
value={meaningValue}
onChange={(e) => setMeaningValue(Number(e.target.value))}
className="w-full border rounded px-3 py-1"
/>
</div>
<div className="flex justify-end gap-2">
<Button onClick={onClose} className="bg-gray-300">
Cancel
</Button>
<Button onClick={handleSave} className="bg-blue-600 text-white">
Save
</Button>
</div>
</div>
</div>
);
}
export default InterestMeaningModal;

Binary file not shown.