Fixed Priorities Modal Interests by adding new InterestsMeaningModal.js
This commit is contained in:
parent
112d6eec56
commit
8166a1119c
@ -161,12 +161,12 @@ app.post('/api/register', async (req, res) => {
|
||||
// 1) Insert into user_profile
|
||||
const profileQuery = `
|
||||
INSERT INTO user_profile
|
||||
(firstname, lastname, email, zipcode, state, area, career_situation)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
(username, firstname, lastname, email, zipcode, state, area, career_situation)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
pool.query(
|
||||
profileQuery,
|
||||
[firstname, lastname, email, zipcode, state, area, career_situation],
|
||||
[username, firstname, lastname, email, zipcode, state, area, career_situation],
|
||||
(errProfile, resultProfile) => {
|
||||
if (errProfile) {
|
||||
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:
|
||||
// firstname, lastname, email, etc. from the request body.
|
||||
const userPayload = {
|
||||
username,
|
||||
firstname,
|
||||
lastname,
|
||||
email,
|
||||
@ -367,14 +368,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:... }
|
||||
riasec: riasec_scores, // NEW
|
||||
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) {
|
||||
@ -417,27 +418,18 @@ app.post('/api/user-profile', (req, res) => {
|
||||
? 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;
|
||||
}
|
||||
// final RIASEC scores
|
||||
const finalRiasec = req.body.riasec_scores
|
||||
? JSON.stringify(req.body.riasec_scores)
|
||||
: existingRow.riasec_scores || null;
|
||||
|
||||
|
||||
if (existingRow) {
|
||||
// Update existing row
|
||||
const updateQuery = `
|
||||
UPDATE user_profile
|
||||
SET
|
||||
username = ?,
|
||||
SET
|
||||
username = ?, -- NEW
|
||||
firstname = ?,
|
||||
lastname = ?,
|
||||
email = ?,
|
||||
@ -446,7 +438,7 @@ app.post('/api/user-profile', (req, res) => {
|
||||
area = ?,
|
||||
career_situation = ?,
|
||||
interest_inventory_answers = ?,
|
||||
riasec_scores = ?, -- new field
|
||||
riasec_scores = ?, -- NEW
|
||||
career_priorities = ?,
|
||||
career_list = ?
|
||||
WHERE id = ?
|
||||
@ -461,10 +453,10 @@ app.post('/api/user-profile', (req, res) => {
|
||||
area || existingRow.area,
|
||||
careerSituation || existingRow.career_situation,
|
||||
finalAnswers,
|
||||
finalRiasecScores, // JSON string
|
||||
finalRiasec,
|
||||
finalCareerPriorities,
|
||||
finalCareerList,
|
||||
profileId
|
||||
profileId,
|
||||
];
|
||||
|
||||
pool.query(updateQuery, params, (err2) => {
|
||||
@ -474,14 +466,16 @@ 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
|
||||
const insertQuery = `
|
||||
INSERT INTO user_profile
|
||||
(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)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
@ -496,7 +490,7 @@ app.post('/api/user-profile', (req, res) => {
|
||||
area,
|
||||
careerSituation || null,
|
||||
finalAnswers,
|
||||
finalRiasecScores, // JSON string or null
|
||||
finalRiasec,
|
||||
finalCareerPriorities,
|
||||
finalCareerList,
|
||||
];
|
||||
@ -508,10 +502,9 @@ 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 });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import CareerSuggestions from './CareerSuggestions.js';
|
||||
import CareerPrioritiesModal from './CareerPrioritiesModal.js';
|
||||
import CareerModal from './CareerModal.js';
|
||||
import InterestMeaningModal from './InterestMeaningModal.js';
|
||||
import CareerSearch from './CareerSearch.js';
|
||||
import { Button } from './ui/button.js';
|
||||
import axios from 'axios';
|
||||
@ -28,8 +29,11 @@ const STATES = [
|
||||
{ name: 'West Virginia', code: 'WV' }, { name: 'Wisconsin', code: 'WI' }, { name: 'Wyoming', code: 'WY' },
|
||||
];
|
||||
|
||||
|
||||
|
||||
// -------------- CIP HELPER FUNCTIONS --------------
|
||||
|
||||
|
||||
// 1) Insert leading zero if there's only 1 digit before the decimal
|
||||
function ensureTwoDigitsBeforeDecimal(cipStr) {
|
||||
// e.g. "4.0201" => "04.0201"
|
||||
@ -66,7 +70,21 @@ function CareerExplorer() {
|
||||
const [userZipcode, setUserZipcode] = useState(null);
|
||||
const [error, setError] = 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)
|
||||
const [careerSuggestions, setCareerSuggestions] = useState([]);
|
||||
|
||||
@ -560,30 +578,48 @@ function CareerExplorer() {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Add/Remove from comparison
|
||||
// ------------------------------------------------------
|
||||
const addCareerToList = (career) => {
|
||||
// 1) get default (pre-calculated) ratings from your JSON
|
||||
const masterRatings = getCareerRatingsBySocCode(career.code);
|
||||
|
||||
const fitRatingMap = {
|
||||
Best: 5,
|
||||
Great: 4,
|
||||
Good: 3,
|
||||
};
|
||||
// 2) figure out interest
|
||||
const userHasInventory = priorities.interests !== "I’m not sure yet";
|
||||
const defaultInterestValue =
|
||||
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 =
|
||||
priorities.interests === "I’m not sure yet"
|
||||
? parseInt(prompt("Rate your interest in this career (1-5):", "3"), 10)
|
||||
: fitRatingMap[career.fit] || masterRatings.interests || 3;
|
||||
// 3) always ask for meaning, start at 3
|
||||
const defaultMeaningValue = 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 =
|
||||
career.ratings && career.ratings.stability !== undefined
|
||||
@ -597,8 +633,8 @@ function CareerExplorer() {
|
||||
const careerWithUserRatings = {
|
||||
...career,
|
||||
ratings: {
|
||||
interests: interestsRating,
|
||||
meaning: meaningRating,
|
||||
interests: finalInterest,
|
||||
meaning: finalMeaning,
|
||||
stability: stabilityRating,
|
||||
growth: growthRating,
|
||||
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 && (
|
||||
<CareerModal
|
||||
career={selectedCareer}
|
||||
|
@ -92,6 +92,7 @@ function CareerModal({ career, careerDetails, closeModal, addCareerToList }) {
|
||||
stability: stabilityRating,
|
||||
},
|
||||
});
|
||||
closeModal();
|
||||
}}
|
||||
className="text-white bg-green-500 hover:bg-green-600 rounded px-3 py-1"
|
||||
>
|
||||
|
@ -10,11 +10,12 @@ const CareerPrioritiesModal = ({ userProfile, onClose }) => {
|
||||
}
|
||||
}, [userProfile]);
|
||||
|
||||
// Updated "interests" question:
|
||||
const questions = [
|
||||
{
|
||||
id: 'interests',
|
||||
text: 'What kinds of activities do you naturally enjoy?',
|
||||
options: ['I know my interests (completed inventory)', 'I’m not sure yet'],
|
||||
text: 'How important is it that your career aligns with your personal interests?',
|
||||
options: ['Very important', 'Somewhat important', 'Not as important'],
|
||||
},
|
||||
{
|
||||
id: 'meaning',
|
||||
@ -61,13 +62,11 @@ const CareerPrioritiesModal = ({ userProfile, onClose }) => {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Error saving priorities:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const allAnswered = questions.every(q => responses[q.id]);
|
||||
|
||||
@ -100,7 +99,9 @@ const CareerPrioritiesModal = ({ userProfile, onClose }) => {
|
||||
<button
|
||||
onClick={handleSave}
|
||||
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
|
||||
</button>
|
||||
|
90
src/components/InterestMeaningModal.js
Normal file
90
src/components/InterestMeaningModal.js
Normal 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 (1–5)
|
||||
</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? (1–5)
|
||||
</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;
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user