From 8166a1119cfb9d5d75c3d557229ad23779c5ad05 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 9 Jun 2025 17:04:56 +0000 Subject: [PATCH] Fixed Priorities Modal Interests by adding new InterestsMeaningModal.js --- backend/server.js | 55 +++++++-------- src/components/CareerExplorer.js | 82 ++++++++++++++++----- src/components/CareerModal.js | 1 + src/components/CareerPrioritiesModal.js | 11 +-- src/components/InterestMeaningModal.js | 90 ++++++++++++++++++++++++ user_profile.db | Bin 151552 -> 155648 bytes 6 files changed, 185 insertions(+), 54 deletions(-) create mode 100644 src/components/InterestMeaningModal.js diff --git a/backend/server.js b/backend/server.js index 6dcf9e1..2a46a49 100755 --- a/backend/server.js +++ b/backend/server.js @@ -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 }); }); } } diff --git a/src/components/CareerExplorer.js b/src/components/CareerExplorer.js index c71ead3..22f0d43 100644 --- a/src/components/CareerExplorer.js +++ b/src/components/CareerExplorer.js @@ -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() { }} /> + setShowInterestMeaningModal(false)} + onSave={handleModalSave} + careerTitle={modalData.career?.title || ""} + askForInterest={modalData.askForInterest} + defaultInterest={modalData.defaultInterest} + defaultMeaning={modalData.defaultMeaning} + /> + {selectedCareer && ( diff --git a/src/components/CareerPrioritiesModal.js b/src/components/CareerPrioritiesModal.js index d6a781b..34e4eac 100644 --- a/src/components/CareerPrioritiesModal.js +++ b/src/components/CareerPrioritiesModal.js @@ -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 }) => { diff --git a/src/components/InterestMeaningModal.js b/src/components/InterestMeaningModal.js new file mode 100644 index 0000000..9958d61 --- /dev/null +++ b/src/components/InterestMeaningModal.js @@ -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 ( +
+
+

+ Add "{careerTitle}" to Comparison +

+ + {/* If the user is "not sure yet," ask for interest rating */} + {askForInterest && ( +
+ + setInterestValue(Number(e.target.value))} + className="w-full border rounded px-3 py-1" + /> +
+ )} + + {/* Always ask for meaning rating */} +
+ + setMeaningValue(Number(e.target.value))} + className="w-full border rounded px-3 py-1" + /> +
+ +
+ + +
+
+
+ ); +} + +export default InterestMeaningModal; diff --git a/user_profile.db b/user_profile.db index 89093384075a01dbbf034e0d190cc6bd2488f70e..cab669b6bd62817750de78395efc830e835acafc 100644 GIT binary patch delta 1830 zcmbW2&u<$=6vu71A&m*y{8D>BMdPFj5_POyM^4lTC`c)Y14Tke+)$>o&-Re@j59Oq zbPwSENUkV=kgq-P4{)e@>47_c1sAULLPP}z-kV)JZjM~6%N%`jW$HCV5`**q04n$Y! zM0YZFM|GqEFj95c7vpVdon;&OS_;XP{iN4Vpa;sazR__ioYac`6ksh_##PL89}IWE zgzojgh@s@krWHJa=__!U%D#Wc>0%%1%z<@vrVZ=qShh3P!*>kcw{;f7Kw=R8)n?W~ z&obR+sgj2&;Q0hZnsDPUb7AykXGn?kwUv}=gMC(H$KwH40%F#ac-jix&)6$T^|+FK zd=e{BC{-N6$SL#S+rSn8MnwOmWX(Nh4=|hU?G?skfS~qz&fdn2`--n#ZD7X zhR_NH#TvQLST73eioQ$)TTUjnrMqhVXd+@oGzqz z3(srcGKiiZk^>YGCN>eFezLG5Z0E77UD@egXxOl|8%2+UrE0DAXci}Wjf^V(34Dls zDRHb+oc-~;JYjMfKWoh02(nMUJR*kkT0xtaw|3HKlOY^ ijlMlAUmZQUQCX={bNJTHM#M~Q