Able to pass selected career from Explorer to EducationalProgramsPage.

This commit is contained in:
Josh 2025-05-16 17:00:15 +00:00
parent 77cd3b6845
commit 3b4c44c088
2 changed files with 437 additions and 392 deletions

View File

@ -17,7 +17,6 @@ import SignIn from './components/SignIn.js';
import SignUp from './components/SignUp.js'; import SignUp from './components/SignUp.js';
import PlanningLanding from './components/PlanningLanding.js'; import PlanningLanding from './components/PlanningLanding.js';
import CareerExplorer from './components/CareerExplorer.js'; import CareerExplorer from './components/CareerExplorer.js';
import EducationalPrograms from './components/EducationalPrograms.js';
import PreparingLanding from './components/PreparingLanding.js'; import PreparingLanding from './components/PreparingLanding.js';
import EducationalProgramsPage from './components/EducationalProgramsPage.js'; import EducationalProgramsPage from './components/EducationalProgramsPage.js';
import EnhancingLanding from './components/EnhancingLanding.js'; import EnhancingLanding from './components/EnhancingLanding.js';
@ -126,6 +125,11 @@ function App() {
Career Explorer Career Explorer
</Link> </Link>
</li> </li>
<li>
<Link className="text-blue-600 hover:text-blue-800" to="/educational-programs">
Skills/Educational Planner
</Link>
</li>
<li> <li>
<Link className="text-blue-600 hover:text-blue-800" to="/interest-inventory"> <Link className="text-blue-600 hover:text-blue-800" to="/interest-inventory">
Interest Inventory Interest Inventory

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo, useCallback } from 'react'; import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { useLocation } from 'react-router-dom'; 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';
@ -9,65 +9,49 @@ import {Button} from './ui/button.js';
import axios from 'axios'; import axios from 'axios';
const STATES = [ const STATES = [
{ name: 'Alabama', code: 'AL' }, { name: 'Alabama', code: 'AL' }, { name: 'Alaska', code: 'AK' }, { name: 'Arizona', code: 'AZ' },
{ name: 'Alaska', code: 'AK' }, { name: 'Arkansas', code: 'AR' }, { name: 'California', code: 'CA' }, { name: 'Colorado', code: 'CO' },
{ name: 'Arizona', code: 'AZ' }, { name: 'Connecticut', code: 'CT' }, { name: 'Delaware', code: 'DE' }, { name: 'District of Columbia', code: 'DC' },
{ name: 'Arkansas', code: 'AR' }, { name: 'Florida', code: 'FL' }, { name: 'Georgia', code: 'GA' }, { name: 'Hawaii', code: 'HI' },
{ name: 'California', code: 'CA' }, { name: 'Idaho', code: 'ID' }, { name: 'Illinois', code: 'IL' }, { name: 'Indiana', code: 'IN' },
{ name: 'Colorado', code: 'CO' }, { name: 'Iowa', code: 'IA' }, { name: 'Kansas', code: 'KS' }, { name: 'Kentucky', code: 'KY' },
{ name: 'Connecticut', code: 'CT' }, { name: 'Louisiana', code: 'LA' }, { name: 'Maine', code: 'ME' }, { name: 'Maryland', code: 'MD' },
{ name: 'Delaware', code: 'DE' }, { name: 'Massachusetts', code: 'MA' }, { name: 'Michigan', code: 'MI' }, { name: 'Minnesota', code: 'MN' },
{ name: 'District of Columbia', code: 'DC' }, { name: 'Mississippi', code: 'MS' }, { name: 'Missouri', code: 'MO' }, { name: 'Montana', code: 'MT' },
{ name: 'Florida', code: 'FL' }, { name: 'Nebraska', code: 'NE' }, { name: 'Nevada', code: 'NV' }, { name: 'New Hampshire', code: 'NH' },
{ name: 'Georgia', code: 'GA' }, { name: 'New Jersey', code: 'NJ' }, { name: 'New Mexico', code: 'NM' }, { name: 'New York', code: 'NY' },
{ name: 'Hawaii', code: 'HI' }, { name: 'North Carolina', code: 'NC' }, { name: 'North Dakota', code: 'ND' }, { name: 'Ohio', code: 'OH' },
{ name: 'Idaho', code: 'ID' }, { name: 'Oklahoma', code: 'OK' }, { name: 'Oregon', code: 'OR' }, { name: 'Pennsylvania', code: 'PA' },
{ name: 'Illinois', code: 'IL' }, { name: 'Rhode Island', code: 'RI' }, { name: 'South Carolina', code: 'SC' }, { name: 'South Dakota', code: 'SD' },
{ name: 'Indiana', code: 'IN' }, { name: 'Tennessee', code: 'TN' }, { name: 'Texas', code: 'TX' }, { name: 'Utah', code: 'UT' },
{ name: 'Iowa', code: 'IA' }, { name: 'Vermont', code: 'VT' }, { name: 'Virginia', code: 'VA' }, { name: 'Washington', code: 'WA' },
{ name: 'Kansas', code: 'KS' }, { name: 'West Virginia', code: 'WV' }, { name: 'Wisconsin', code: 'WI' }, { name: 'Wyoming', code: 'WY' },
{ name: 'Kentucky', code: 'KY' },
{ name: 'Louisiana', code: 'LA' },
{ name: 'Maine', code: 'ME' },
{ name: 'Maryland', code: 'MD' },
{ name: 'Massachusetts', code: 'MA' },
{ name: 'Michigan', code: 'MI' },
{ name: 'Minnesota', code: 'MN' },
{ name: 'Mississippi', code: 'MS' },
{ name: 'Missouri', code: 'MO' },
{ name: 'Montana', code: 'MT' },
{ name: 'Nebraska', code: 'NE' },
{ name: 'Nevada', code: 'NV' },
{ name: 'New Hampshire', code: 'NH' },
{ name: 'New Jersey', code: 'NJ' },
{ name: 'New Mexico', code: 'NM' },
{ name: 'New York', code: 'NY' },
{ name: 'North Carolina', code: 'NC' },
{ name: 'North Dakota', code: 'ND' },
{ name: 'Ohio', code: 'OH' },
{ name: 'Oklahoma', code: 'OK' },
{ name: 'Oregon', code: 'OR' },
{ name: 'Pennsylvania', code: 'PA' },
{ name: 'Rhode Island', code: 'RI' },
{ name: 'South Carolina', code: 'SC' },
{ name: 'South Dakota', code: 'SD' },
{ name: 'Tennessee', code: 'TN' },
{ name: 'Texas', code: 'TX' },
{ name: 'Utah', code: 'UT' },
{ name: 'Vermont', code: 'VT' },
{ name: 'Virginia', code: 'VA' },
{ name: 'Washington', code: 'WA' },
{ 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"
return cipStr.replace(/^(\d)\./, '0$1.');
}
// 2) Clean an array of CIP codes, e.g. ["4.0201", "14.0901"] => ["0402", "1409"]
function cleanCipCodes(cipArray) {
return cipArray.map((code) => {
let codeStr = code.toString();
codeStr = ensureTwoDigitsBeforeDecimal(codeStr); // ensure "04.0201"
return codeStr.replace('.', '').slice(0, 4); // => "040201" => "0402"
});
}
function getFullStateName(code) { function getFullStateName(code) {
const found = STATES.find((s) => s.code === code?.toUpperCase()); const found = STATES.find((s) => s.code === code?.toUpperCase());
return found ? found.name : ''; return found ? found.name : '';
} }
function CareerExplorer({ }) { function CareerExplorer() {
const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const apiUrl = process.env.REACT_APP_API_URL || ''; const apiUrl = process.env.REACT_APP_API_URL || '';
@ -105,8 +89,10 @@ function CareerExplorer({ }) {
Good: 'Good - Less Strong Match', Good: 'Good - Less Strong Match',
}; };
// ===================== Load user profile =====================
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
const fetchUserProfile = async () => { const fetchUserProfile = async () => {
try { try {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
@ -118,18 +104,17 @@ function CareerExplorer({ }) {
const profileData = res.data; const profileData = res.data;
console.log('[fetchUserProfile] loaded profileData =>', profileData); console.log('[fetchUserProfile] loaded profileData =>', profileData);
// 1) Set userProfile and all relevant states using `profileData`:
setUserProfile(profileData); setUserProfile(profileData);
setUserState(profileData.state); setUserState(profileData.state);
setAreaTitle(profileData.area); setAreaTitle(profileData.area);
setUserZipcode(profileData.zipcode); setUserZipcode(profileData.zipcode);
// 2) Load saved career list if it exists // If they have a saved career list
if (profileData.career_list) { if (profileData.career_list) {
setCareerList(JSON.parse(profileData.career_list)); setCareerList(JSON.parse(profileData.career_list));
} }
// 3) If user has interest inventory answers, fetch suggestions // If they have interest inventory, fetch suggestions
if (profileData.interest_inventory_answers) { if (profileData.interest_inventory_answers) {
const answers = profileData.interest_inventory_answers; const answers = profileData.interest_inventory_answers;
const careerSuggestionsRes = await axios.post(`${apiUrl}/onet/submit_answers`, { const careerSuggestionsRes = await axios.post(`${apiUrl}/onet/submit_answers`, {
@ -137,15 +122,13 @@ function CareerExplorer({ }) {
state: profileData.state, state: profileData.state,
area: profileData.area, area: profileData.area,
}); });
const { careers = [] } = careerSuggestionsRes.data || {}; const { careers = [] } = careerSuggestionsRes.data || {};
setCareerSuggestions(careers.flat()); setCareerSuggestions(careers.flat());
} else { } else {
// No inventory => no suggestions (or do something else here)
setCareerSuggestions([]); setCareerSuggestions([]);
} }
// 4) Check if all priorities are answered // Check if priorities answered
const priorities = profileData.career_priorities const priorities = profileData.career_priorities
? JSON.parse(profileData.career_priorities) ? JSON.parse(profileData.career_priorities)
: {}; : {};
@ -154,16 +137,14 @@ function CareerExplorer({ }) {
.every((key) => priorities[key]); .every((key) => priorities[key]);
if (!allAnswered) { if (!allAnswered) {
// If user hasn't answered them all, show the priorities modal
setShowModal(true); setShowModal(true);
} }
} else { } else {
// Not a 200 response => fallback
setShowModal(true); setShowModal(true);
} }
} catch (err) { } catch (err) {
console.error('Error fetching user profile:', err); console.error('Error fetching user profile:', err);
setShowModal(true); // fallback if error setShowModal(true);
setLoading(false); setLoading(false);
} }
}; };
@ -171,13 +152,14 @@ function CareerExplorer({ }) {
fetchUserProfile(); fetchUserProfile();
}, [apiUrl]); }, [apiUrl]);
// ===================== If location.state has careerSuggestions =====================
useEffect(() => { useEffect(() => {
if (location.state?.careerSuggestions) { if (location.state?.careerSuggestions) {
setCareerSuggestions(location.state.careerSuggestions); setCareerSuggestions(location.state.careerSuggestions);
} }
}, [location.state]); }, [location.state]);
// Fetch Job Zones if suggestions are provided // ===================== Fetch job zones for suggestions =====================
useEffect(() => { useEffect(() => {
const fetchJobZones = async () => { const fetchJobZones = async () => {
if (!careerSuggestions.length) return; if (!careerSuggestions.length) return;
@ -193,7 +175,6 @@ function CareerExplorer({ }) {
job_zone: jobZoneData[career.code.slice(0, -3)]?.job_zone || null, job_zone: jobZoneData[career.code.slice(0, -3)]?.job_zone || null,
})); }));
// IMPORTANT: Ensure this actually sets a new array
setCareersWithJobZone([...updatedCareers]); setCareersWithJobZone([...updatedCareers]);
} catch (error) { } catch (error) {
console.error('Error fetching job zone information:', error); console.error('Error fetching job zone information:', error);
@ -203,6 +184,7 @@ function CareerExplorer({ }) {
fetchJobZones(); fetchJobZones();
}, [careerSuggestions, apiUrl]); }, [careerSuggestions, apiUrl]);
// ===================== handleCareerClick (detail fetch) =====================
const handleCareerClick = useCallback( const handleCareerClick = useCallback(
async (career) => { async (career) => {
console.log('[handleCareerClick] career =>', career); console.log('[handleCareerClick] career =>', career);
@ -213,7 +195,6 @@ function CareerExplorer({ }) {
setSalaryData([]); setSalaryData([]);
setEconomicProjections({}); setEconomicProjections({});
// We can set selectedCareer immediately so that our Modal condition is met.
setSelectedCareer(career); setSelectedCareer(career);
if (!socCode) { if (!socCode) {
@ -226,7 +207,10 @@ function CareerExplorer({ }) {
try { try {
// CIP fetch // CIP fetch
const cipResponse = await fetch(`${apiUrl}/cip/${socCode}`); const cipResponse = await fetch(`${apiUrl}/cip/${socCode}`);
if (!cipResponse.ok) {setError(`We're sorry, but specific details for "${career.title}" are not available at this time.`); if (!cipResponse.ok) {
setError(
`We're sorry, but specific details for "${career.title}" are not available at this time.`
);
setCareerDetails({ error: `We're sorry, but detailed info for "${career.title}" isn't available right now.`}); setCareerDetails({ error: `We're sorry, but detailed info for "${career.title}" isn't available right now.`});
setLoading(false); setLoading(false);
return; return;
@ -236,7 +220,8 @@ function CareerExplorer({ }) {
// Job details // Job details
const jobDetailsResponse = await fetch(`${apiUrl}/onet/career-description/${socCode}`); const jobDetailsResponse = await fetch(`${apiUrl}/onet/career-description/${socCode}`);
if (!jobDetailsResponse.ok){setCareerDetails({ error: `We're sorry, but detailed info for "${career.title}" isn't available right now.`}); if (!jobDetailsResponse.ok){
setCareerDetails({ error: `We're sorry, but detailed info for "${career.title}" isn't available right now.`});
setLoading(false); setLoading(false);
return; return;
} }
@ -254,7 +239,8 @@ function CareerExplorer({ }) {
// Build salary array // Build salary array
const sData = salaryResponse.data || {}; const sData = salaryResponse.data || {};
const salaryDataPoints = sData && Object.keys(sData).length > 0 const salaryDataPoints =
sData && Object.keys(sData).length > 0
? [ ? [
{ {
percentile: '10th Percentile', percentile: '10th Percentile',
@ -304,13 +290,11 @@ function CareerExplorer({ }) {
economicProjections: economicResponse.data || {}, economicProjections: economicResponse.data || {},
}; };
// Now set the fully fetched data
setCareerDetails(updatedCareerDetails); setCareerDetails(updatedCareerDetails);
} catch (error) { } catch (error) {
console.error('Error processing career click:', error.message); console.error('Error processing career click:', error.message);
setCareerDetails({ setCareerDetails({
error: `We're sorry, but detailed info for "${career.title}" isn't available right now.` error: `We're sorry, but detailed info for "${career.title}" isn't available right now.`,
}); });
} finally { } finally {
setLoading(false); setLoading(false);
@ -319,7 +303,7 @@ function CareerExplorer({ }) {
[userState, apiUrl, areaTitle, userZipcode] [userState, apiUrl, areaTitle, userZipcode]
); );
// ============= Let typed careers open PopoutPanel ============= // ===================== handleCareerFromSearch =====================
const handleCareerFromSearch = useCallback( const handleCareerFromSearch = useCallback(
(obj) => { (obj) => {
const adapted = { const adapted = {
@ -341,6 +325,7 @@ function CareerExplorer({ }) {
} }
}, [pendingCareerForModal, handleCareerFromSearch]); }, [pendingCareerForModal, handleCareerFromSearch]);
// ===================== Load careers_with_ratings for CIP arrays =====================
useEffect(() => { useEffect(() => {
fetch('/careers_with_ratings.json') fetch('/careers_with_ratings.json')
.then((res) => { .then((res) => {
@ -358,14 +343,16 @@ function CareerExplorer({ }) {
const priorityKeys = ['interests', 'meaning', 'stability', 'growth', 'balance', 'recognition']; const priorityKeys = ['interests', 'meaning', 'stability', 'growth', 'balance', 'recognition'];
const getCareerRatingsBySocCode = (socCode) => { const getCareerRatingsBySocCode = (socCode) => {
return masterCareerRatings.find(c => c.soc_code === socCode)?.ratings || {}; return masterCareerRatings.find((c) => c.soc_code === socCode)?.ratings || {};
}; };
// ===================== Save comparison list to backend =====================
const saveCareerListToBackend = async (newCareerList) => { const saveCareerListToBackend = async (newCareerList) => {
try { try {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
await axios.post(`${apiUrl}/user-profile`, { await axios.post(
`${apiUrl}/user-profile`,
{
firstName: userProfile?.firstname, firstName: userProfile?.firstname,
lastName: userProfile?.lastname, lastName: userProfile?.lastname,
email: userProfile?.email, email: userProfile?.email,
@ -373,22 +360,20 @@ function CareerExplorer({ }) {
state: userProfile?.state, state: userProfile?.state,
area: userProfile?.area, area: userProfile?.area,
careerSituation: userProfile?.career_situation, careerSituation: userProfile?.career_situation,
// For the rest, ensure we're not overwriting if not needed
interest_inventory_answers: userProfile?.interest_inventory_answers, interest_inventory_answers: userProfile?.interest_inventory_answers,
career_priorities: userProfile?.career_priorities, career_priorities: userProfile?.career_priorities,
// IMPORTANT: We convert the newCareerList to JSON if your DB expects text
career_list: JSON.stringify(newCareerList), career_list: JSON.stringify(newCareerList),
}, { },
{
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
}); }
);
} catch (err) { } catch (err) {
console.error('Error saving career_list:', err); console.error('Error saving career_list:', err);
// optional: show a user-friendly error
} }
}; };
// ===================== Add/Remove from comparison =====================
const addCareerToList = (career) => { const addCareerToList = (career) => {
const masterRatings = getCareerRatingsBySocCode(career.code); const masterRatings = getCareerRatingsBySocCode(career.code);
@ -434,27 +419,53 @@ function CareerExplorer({ }) {
alert("Career already in comparison list."); alert("Career already in comparison list.");
return prevList; return prevList;
} }
const newList = [...prevList, careerWithUserRatings]; const newList = [...prevList, careerWithUserRatings];
// Call the API to save
saveCareerListToBackend(newList); saveCareerListToBackend(newList);
return newList; return newList;
}); });
}; };
const removeCareerFromList = (careerCode) => { const removeCareerFromList = (careerCode) => {
setCareerList((prevList) => { setCareerList((prevList) => {
const newList = prevList.filter((c) => c.code !== careerCode); const newList = prevList.filter((c) => c.code !== careerCode);
// Call the API to save
saveCareerListToBackend(newList); saveCareerListToBackend(newList);
return newList; return newList;
}); });
}; };
// ===================== Let user pick a career from comparison => "Select for Education" =====================
const handleSelectForEducation = (career) => {
// 1) Confirm
const confirmed = window.confirm(
`Are you sure you want to move on to Educational Programs for ${career.title}?`
);
if (!confirmed) return;
// 2) Look up CIP codes from masterCareerRatings by SOC code
const matching = masterCareerRatings.find((r) => r.soc_code === career.code);
if (!matching) {
alert(`No CIP codes found for ${career.title}.`);
return;
}
// 3) Clean CIP codes
const rawCips = matching.cip_codes || [];
const cleanedCips = cleanCipCodes(rawCips); // from top-level function
console.log('cleanedCips =>', cleanedCips);
// 4) Navigate
navigate('/educational-programs', {
state: {
cipCodes: cleanedCips,
careerTitle: career.title,
userZip: userZipcode,
userState: userState,
},
});
};
// Filtering logic (Job Zone and Fit) // ===================== Filter logic for jobZone, Fit =====================
const filteredCareers = useMemo(() => { const filteredCareers = useMemo(() => {
return careersWithJobZone.filter((career) => { return careersWithJobZone.filter((career) => {
const jobZoneMatches = selectedJobZone const jobZoneMatches = selectedJobZone
@ -468,6 +479,7 @@ function CareerExplorer({ }) {
}); });
}, [careersWithJobZone, selectedJobZone, selectedFit]); }, [careersWithJobZone, selectedJobZone, selectedFit]);
// Weighted “match score” logic. (unchanged)
const priorityWeight = (priority, response) => { const priorityWeight = (priority, response) => {
const weightMap = { const weightMap = {
interests: { interests: {
@ -523,7 +535,6 @@ function CareerExplorer({ }) {
}; };
return ( return (
<div className="career-explorer-container bg-white p-6 rounded shadow"> <div className="career-explorer-container bg-white p-6 rounded shadow">
{renderLoadingOverlay()} {renderLoadingOverlay()}
{showModal && ( {showModal && (
@ -533,11 +544,12 @@ function CareerExplorer({ }) {
/> />
)} )}
<div className="flex justify-between items-center mb-4"> <div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">Explore Careers - use the tools in this area to find your perfect career</h2> <h2 className="text-xl font-semibold">
Explore Careers - use the tools below to find your perfect career
</h2>
<CareerSearch <CareerSearch
onCareerSelected={(careerObj) => { onCareerSelected={(careerObj) => {
console.log('[Dashboard] onCareerSelected =>', careerObj); console.log('[Dashboard] onCareerSelected =>', careerObj);
// Set the "pendingCareerForModal" so our useEffect fires
setPendingCareerForModal(careerObj); setPendingCareerForModal(careerObj);
}} }}
/> />
@ -550,7 +562,9 @@ function CareerExplorer({ }) {
<tr> <tr>
<th className="border p-2">Career</th> <th className="border p-2">Career</th>
{priorityKeys.map((priority) => ( {priorityKeys.map((priority) => (
<th key={priority} className="border p-2 capitalize">{priority}</th> <th key={priority} className="border p-2 capitalize">
{priority}
</th>
))} ))}
<th className="border p-2">Match</th> <th className="border p-2">Match</th>
<th className="border p-2">Actions</th> <th className="border p-2">Actions</th>
@ -559,7 +573,7 @@ function CareerExplorer({ }) {
<tbody> <tbody>
{careerList.map((career) => { {careerList.map((career) => {
const ratings = career.ratings || {}; const ratings = career.ratings || {};
const interestsRating = ratings.interests || 3; // default to 3 if not rated via InterestInventory const interestsRating = ratings.interests || 3;
const meaningRating = ratings.meaning || 3; const meaningRating = ratings.meaning || 3;
const stabilityRating = ratings.stability || 3; const stabilityRating = ratings.stability || 3;
const growthRating = ratings.growth || 3; const growthRating = ratings.growth || 3;
@ -601,17 +615,30 @@ function CareerExplorer({ }) {
<td className="border p-2">{balanceRating}</td> <td className="border p-2">{balanceRating}</td>
<td className="border p-2">{recognitionRating}</td> <td className="border p-2">{recognitionRating}</td>
<td className="border p-2 font-bold">{matchScore.toFixed(1)}%</td> <td className="border p-2 font-bold">{matchScore.toFixed(1)}%</td>
<td className="border p-2"> <td className="border p-2 space-x-2">
<Button className="bg-red-600 text-black-500" onClick={() => removeCareerFromList(career.code)}>Remove</Button> <Button
className="bg-red-600 text-black-500"
onClick={() => removeCareerFromList(career.code)}
>
Remove
</Button>
{/* New Button -> "Select for Education" */}
<Button
className="bg-green-600 text-white"
onClick={() => handleSelectForEducation(career)}
>
Search for Education
</Button>
</td> </td>
</tr> </tr>
); );
})} })}
</tbody> </tbody>
</table> </table>
) : <p>No careers added to comparison.</p>} ) : (
<p>No careers added to comparison.</p>
)}
<div className="flex gap-4 mb-4"> <div className="flex gap-4 mb-4">
<select <select
@ -621,7 +648,9 @@ function CareerExplorer({ }) {
> >
<option value="">All Preparation Levels</option> <option value="">All Preparation Levels</option>
{Object.entries(jobZoneLabels).map(([zone, label]) => ( {Object.entries(jobZoneLabels).map(([zone, label]) => (
<option key={zone} value={zone}>{label}</option> <option key={zone} value={zone}>
{label}
</option>
))} ))}
</select> </select>
@ -632,7 +661,9 @@ function CareerExplorer({ }) {
> >
<option value="">All Fit Levels</option> <option value="">All Fit Levels</option>
{Object.entries(fitLabels).map(([key, label]) => ( {Object.entries(fitLabels).map(([key, label]) => (
<option key={key} value={key}>{label}</option> <option key={key} value={key}>
{label}
</option>
))} ))}
</select> </select>
</div> </div>
@ -663,11 +694,21 @@ function CareerExplorer({ }) {
<div className="mt-6 text-xs text-gray-500 border-t pt-2"> <div className="mt-6 text-xs text-gray-500 border-t pt-2">
Career results and details provided by Career results and details provided by
<a href="https://www.onetcenter.org" target="_blank" rel="noopener noreferrer"> O*Net</a>, <a href="https://www.onetcenter.org" target="_blank" rel="noopener noreferrer">
in partnership with {' '}
<a href="https://www.bls.gov" target="_blank" rel="noopener noreferrer"> Bureau of Labor Statistics</a> O*Net
</a>
, in partnership with
<a href="https://www.bls.gov" target="_blank" rel="noopener noreferrer">
{' '}
Bureau of Labor Statistics
</a>
and and
<a href="https://nces.ed.gov" target="_blank" rel="noopener noreferrer"> NCES</a>. <a href="https://nces.ed.gov" target="_blank" rel="noopener noreferrer">
{' '}
NCES
</a>
.
</div> </div>
</div> </div>
); );