Fixed UserProfile values for career_situation
This commit is contained in:
parent
48e68d47b7
commit
146061a9b9
@ -392,9 +392,9 @@ app.get('/api/cip/:socCode', (req, res) => {
|
|||||||
**************************************************/
|
**************************************************/
|
||||||
app.get('/api/schools', (req, res) => {
|
app.get('/api/schools', (req, res) => {
|
||||||
const { cipCode, state } = req.query;
|
const { cipCode, state } = req.query;
|
||||||
console.log('Query Params:', { cipCode, state });
|
console.log('Query Params:', { cipCode });
|
||||||
if (!cipCode || !state) {
|
if (!cipCode || !state) {
|
||||||
return res.status(400).json({ error: 'CIP Code and State are required.' });
|
return res.status(400).json({ error: 'CIP Code is required' });
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const matchedCIP = cipCode.replace('.', '').slice(0, 4);
|
const matchedCIP = cipCode.replace('.', '').slice(0, 4);
|
||||||
|
@ -19,6 +19,7 @@ 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 EducationalPrograms from './components/EducationalPrograms.js';
|
||||||
import PreparingLanding from './components/PreparingLanding.js';
|
import PreparingLanding from './components/PreparingLanding.js';
|
||||||
|
import EducationalProgramsPage from './components/EducationalProgramsPage.js';
|
||||||
import EnhancingLanding from './components/EnhancingLanding.js';
|
import EnhancingLanding from './components/EnhancingLanding.js';
|
||||||
import RetirementLanding from './components/RetirementLanding.js';
|
import RetirementLanding from './components/RetirementLanding.js';
|
||||||
import InterestInventory from './components/InterestInventory.js';
|
import InterestInventory from './components/InterestInventory.js';
|
||||||
@ -244,7 +245,7 @@ function App() {
|
|||||||
<Route path="/profile" element={<UserProfile />} />
|
<Route path="/profile" element={<UserProfile />} />
|
||||||
<Route path="/planning" element={<PlanningLanding />} />
|
<Route path="/planning" element={<PlanningLanding />} />
|
||||||
<Route path="/career-explorer" element={<CareerExplorer />} />
|
<Route path="/career-explorer" element={<CareerExplorer />} />
|
||||||
<Route path="/educational-programs" element={<EducationalPrograms />} />
|
<Route path="/educational-programs" element={<EducationalProgramsPage/>}/>
|
||||||
<Route path="/preparing" element={<PreparingLanding />} />
|
<Route path="/preparing" element={<PreparingLanding />} />
|
||||||
<Route path="/enhancing" element={<EnhancingLanding />} />
|
<Route path="/enhancing" element={<EnhancingLanding />} />
|
||||||
<Route path="/retirement" element={<RetirementLanding />} />
|
<Route path="/retirement" element={<RetirementLanding />} />
|
||||||
|
@ -404,7 +404,7 @@ function CareerExplorer({ }) {
|
|||||||
: fitRatingMap[career.fit] || masterRatings.interests || 3;
|
: fitRatingMap[career.fit] || masterRatings.interests || 3;
|
||||||
|
|
||||||
const meaningRating = parseInt(
|
const meaningRating = parseInt(
|
||||||
prompt("How meaningful is this career to you? (1-5):", "3"),
|
prompt("How important do you feel this job is to society or the world? (1-5):", "3"),
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
|
284
src/components/EducationalProgramsPage.js
Normal file
284
src/components/EducationalProgramsPage.js
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
// Your existing search component
|
||||||
|
import CareerSearch from './CareerSearch.js';
|
||||||
|
|
||||||
|
// The existing utility calls
|
||||||
|
import { fetchSchools, clientGeocodeZip, haversineDistance } from '../utils/apiUtils.js';
|
||||||
|
|
||||||
|
// A simple Button component (if you don’t already import from elsewhere)
|
||||||
|
function Button({ onClick, children, ...props }) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-500"
|
||||||
|
onClick={onClick}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EducationalProgramsPage
|
||||||
|
* - If we have a CIP code (from location.state or otherwise), we fetch + display schools.
|
||||||
|
* - If no CIP code is provided, user sees a CareerSearch to pick a career => sets CIP code.
|
||||||
|
* - Then the user can filter & sort (tuition, distance, optional in-state only).
|
||||||
|
*/
|
||||||
|
function EducationalProgramsPage() {
|
||||||
|
// 1) Get CIP code from React Router location.state (if available)
|
||||||
|
// If no CIP code in route state, default to an empty string
|
||||||
|
const location = useLocation();
|
||||||
|
const [cipCode, setCipCode] = useState(location.state?.cipCode || '');
|
||||||
|
|
||||||
|
// Optionally, you can also read userState / userZip from location.state or from user’s profile
|
||||||
|
const [userState, setUserState] = useState(location.state?.userState || '');
|
||||||
|
const [userZip, setUserZip] = useState(location.state?.userZip || '');
|
||||||
|
|
||||||
|
// ============ Data + UI state ============
|
||||||
|
const [schools, setSchools] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
// Filter states
|
||||||
|
const [sortBy, setSortBy] = useState('tuition'); // 'tuition' or 'distance'
|
||||||
|
const [maxTuition, setMaxTuition] = useState(99999);
|
||||||
|
const [maxDistance, setMaxDistance] = useState(99999);
|
||||||
|
// Optional “in-state only” toggle
|
||||||
|
const [inStateOnly, setInStateOnly] = useState(false);
|
||||||
|
|
||||||
|
// ============ Handle Career Search -> CIP code ============
|
||||||
|
const handleCareerSelected = (foundObj) => {
|
||||||
|
// foundObj = { title, soc_code, cip_code } from CareerSearch
|
||||||
|
if (foundObj?.cip_code) {
|
||||||
|
setCipCode(foundObj.cip_code);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============ Fetch + Compute Distance once we have a CIP code ============
|
||||||
|
useEffect(() => {
|
||||||
|
// If no CIP code is set yet, do nothing.
|
||||||
|
if (!cipCode) return;
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1) Fetch schools by CIP code (and userState if your API still uses it)
|
||||||
|
const fetchedSchools = await fetchSchools(cipCode, userState);
|
||||||
|
|
||||||
|
// 2) Optionally geocode user ZIP to compute distances
|
||||||
|
let userLat = null;
|
||||||
|
let userLng = null;
|
||||||
|
if (userZip) {
|
||||||
|
try {
|
||||||
|
const geoResult = await clientGeocodeZip(userZip);
|
||||||
|
userLat = geoResult.lat;
|
||||||
|
userLng = geoResult.lng;
|
||||||
|
} catch (geoErr) {
|
||||||
|
console.warn('Unable to geocode user ZIP:', geoErr.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Compute distance for each school (if lat/lng is available)
|
||||||
|
const schoolsWithDistance = fetchedSchools.map((sch) => {
|
||||||
|
const lat2 = sch.LATITUDE ? parseFloat(sch.LATITUDE) : null;
|
||||||
|
const lon2 = sch.LONGITUD ? parseFloat(sch.LONGITUD) : null;
|
||||||
|
|
||||||
|
if (userLat && userLng && lat2 && lon2) {
|
||||||
|
const distMiles = haversineDistance(userLat, userLng, lat2, lon2);
|
||||||
|
return { ...sch, distance: distMiles.toFixed(1) };
|
||||||
|
} else {
|
||||||
|
return { ...sch, distance: null };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setSchools(schoolsWithDistance);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching/processing schools:', err);
|
||||||
|
setError('Failed to load schools.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
}, [cipCode, userState, userZip]);
|
||||||
|
|
||||||
|
// ============ Filter + Sort ============
|
||||||
|
const filteredAndSortedSchools = useMemo(() => {
|
||||||
|
if (!schools) return [];
|
||||||
|
let result = [...schools];
|
||||||
|
|
||||||
|
// 1) (Optional) In-state only
|
||||||
|
if (inStateOnly && userState) {
|
||||||
|
result = result.filter((sch) => sch.STABBR === userState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Filter by max tuition
|
||||||
|
// We’ll use “In_state cost” if your data references that, or you can adapt.
|
||||||
|
result = result.filter((sch) => {
|
||||||
|
const inStateCost = sch['In_state cost']
|
||||||
|
? parseFloat(sch['In_state cost'])
|
||||||
|
: 999999;
|
||||||
|
return inStateCost <= maxTuition;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3) Filter by max distance
|
||||||
|
result = result.filter((sch) => {
|
||||||
|
if (sch.distance === null) {
|
||||||
|
// If distance is unknown, decide if you want to include or exclude it
|
||||||
|
return true; // let’s include unknown
|
||||||
|
}
|
||||||
|
return parseFloat(sch.distance) <= maxDistance;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4) Sort
|
||||||
|
if (sortBy === 'distance') {
|
||||||
|
result.sort((a, b) => {
|
||||||
|
const distA = a.distance !== null ? parseFloat(a.distance) : Infinity;
|
||||||
|
const distB = b.distance !== null ? parseFloat(b.distance) : Infinity;
|
||||||
|
return distA - distB;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// sort by tuition
|
||||||
|
result.sort((a, b) => {
|
||||||
|
const tA = a['In_state cost'] ? parseFloat(a['In_state cost']) : Infinity;
|
||||||
|
const tB = b['In_state cost'] ? parseFloat(b['In_state cost']) : Infinity;
|
||||||
|
return tA - tB;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [schools, sortBy, maxTuition, maxDistance, inStateOnly, userState]);
|
||||||
|
|
||||||
|
// ============ Render UI ============
|
||||||
|
|
||||||
|
// 1) If we have NO CIP code yet, show the fallback “CareerSearch”
|
||||||
|
if (!cipCode) {
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">Educational Programs</h2>
|
||||||
|
<p className="mb-4 text-gray-600">
|
||||||
|
You have not selected a career yet. Please search for one below:
|
||||||
|
</p>
|
||||||
|
<CareerSearch onCareerSelected={handleCareerSelected} />
|
||||||
|
<p className="mt-2 text-sm text-gray-500">
|
||||||
|
After you pick a career, we’ll display matching educational programs.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) If we DO have a CIP code, show the filterable school list
|
||||||
|
if (loading) {
|
||||||
|
return <div className="p-4">Loading schools...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="p-4 text-red-600">
|
||||||
|
<p>Error: {error}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">
|
||||||
|
Schools Offering Programs for CIP: {cipCode}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Filter Bar */}
|
||||||
|
<div className="mb-4 flex flex-wrap items-center space-x-4">
|
||||||
|
{/* Sort */}
|
||||||
|
<label className="text-sm text-gray-600">
|
||||||
|
Sort:
|
||||||
|
<select
|
||||||
|
className="ml-2 rounded border px-2 py-1 text-sm"
|
||||||
|
value={sortBy}
|
||||||
|
onChange={(e) => setSortBy(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="tuition">Tuition</option>
|
||||||
|
<option value="distance">Distance</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Tuition */}
|
||||||
|
<label className="text-sm text-gray-600">
|
||||||
|
Tuition (max):
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="ml-2 w-20 rounded border px-2 py-1 text-sm"
|
||||||
|
value={maxTuition}
|
||||||
|
onChange={(e) => setMaxTuition(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Distance */}
|
||||||
|
<label className="text-sm text-gray-600">
|
||||||
|
Distance (max):
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="ml-2 w-20 rounded border px-2 py-1 text-sm"
|
||||||
|
value={maxDistance}
|
||||||
|
onChange={(e) => setMaxDistance(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Optional: In-State Only Toggle */}
|
||||||
|
{userState && (
|
||||||
|
<label className="inline-flex items-center space-x-2 text-sm text-gray-600">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={inStateOnly}
|
||||||
|
onChange={(e) => setInStateOnly(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span>In-State Only</span>
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* School List */}
|
||||||
|
{filteredAndSortedSchools.length > 0 ? (
|
||||||
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
{filteredAndSortedSchools.map((school, idx) => (
|
||||||
|
<div key={idx} className="rounded border p-3 text-sm">
|
||||||
|
<strong>{school['INSTNM'] || 'Unnamed School'}</strong>
|
||||||
|
<p>Degree Type: {school['CREDDESC'] || 'N/A'}</p>
|
||||||
|
<p>In-State Tuition: ${school['In_state cost'] || 'N/A'}</p>
|
||||||
|
<p>Out-of-State Tuition: ${school['Out_state cost'] || 'N/A'}</p>
|
||||||
|
<p>
|
||||||
|
Distance:{' '}
|
||||||
|
{school.distance !== null ? `${school.distance} mi` : 'N/A'}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Website:{' '}
|
||||||
|
{school['Website'] ? (
|
||||||
|
<a
|
||||||
|
href={school['Website']}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
{school['Website']}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
'N/A'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
No schools matching your filters.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EducationalProgramsPage;
|
@ -252,7 +252,7 @@ function SignUp() {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<h2 className="mb-4 text-xl font-semibold text-center">Choose Your Career Stage</h2>
|
<h2 className="mb-4 text-xl font-semibold text-center">Where are you in your career journey right now?</h2>
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
{careerSituations.map((situation) => (
|
{careerSituations.map((situation) => (
|
||||||
<SituationCard
|
<SituationCard
|
||||||
|
@ -15,6 +15,7 @@ function UserProfile() {
|
|||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Helper to do authorized fetch
|
||||||
const authFetch = async (url, options = {}) => {
|
const authFetch = async (url, options = {}) => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@ -48,10 +49,6 @@ function UserProfile() {
|
|||||||
|
|
||||||
const res = await authFetch('/api/user-profile', {
|
const res = await authFetch('/api/user-profile', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res || !res.ok) return;
|
if (!res || !res.ok) return;
|
||||||
@ -70,6 +67,7 @@ function UserProfile() {
|
|||||||
setIsPremiumUser(true);
|
setIsPremiumUser(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have a state, load its areas
|
||||||
if (data.state) {
|
if (data.state) {
|
||||||
setLoadingAreas(true);
|
setLoadingAreas(true);
|
||||||
try {
|
try {
|
||||||
@ -92,8 +90,10 @@ function UserProfile() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchProfileAndAreas();
|
fetchProfileAndAreas();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []); // only runs once
|
}, []); // only runs once
|
||||||
|
|
||||||
|
// Whenever user changes "selectedState", re-fetch areas
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAreasByState = async () => {
|
const fetchAreasByState = async () => {
|
||||||
if (!selectedState) {
|
if (!selectedState) {
|
||||||
@ -144,9 +144,6 @@ function UserProfile() {
|
|||||||
try {
|
try {
|
||||||
const response = await authFetch('/api/user-profile', {
|
const response = await authFetch('/api/user-profile', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(profileData),
|
body: JSON.stringify(profileData),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,59 +156,45 @@ function UserProfile() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FULL list of states, including all 50 states (+ DC if desired)
|
// FULL list of states for your dropdown
|
||||||
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' },
|
// The updated career situations (same as in SignUp.js)
|
||||||
{ name: 'Maryland', code: 'MD' },
|
const careerSituations = [
|
||||||
{ name: 'Massachusetts', code: 'MA' },
|
{
|
||||||
{ name: 'Michigan', code: 'MI' },
|
id: 'planning',
|
||||||
{ name: 'Minnesota', code: 'MN' },
|
title: 'Planning Your Career',
|
||||||
{ name: 'Mississippi', code: 'MS' },
|
},
|
||||||
{ name: 'Missouri', code: 'MO' },
|
{
|
||||||
{ name: 'Montana', code: 'MT' },
|
id: 'preparing',
|
||||||
{ name: 'Nebraska', code: 'NE' },
|
title: 'Preparing for Your (Next) Career',
|
||||||
{ name: 'Nevada', code: 'NV' },
|
},
|
||||||
{ name: 'New Hampshire', code: 'NH' },
|
{
|
||||||
{ name: 'New Jersey', code: 'NJ' },
|
id: 'enhancing',
|
||||||
{ name: 'New Mexico', code: 'NM' },
|
title: 'Enhancing Your Career',
|
||||||
{ name: 'New York', code: 'NY' },
|
},
|
||||||
{ name: 'North Carolina', code: 'NC' },
|
{
|
||||||
{ name: 'North Dakota', code: 'ND' },
|
id: 'retirement',
|
||||||
{ name: 'Ohio', code: 'OH' },
|
title: 'Retirement Planning',
|
||||||
{ 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' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -296,13 +279,13 @@ function UserProfile() {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loadingAreas && <p className="text-sm text-gray-500">Loading areas...</p>}
|
{/* Loading indicator for areas */}
|
||||||
|
{loadingAreas && (
|
||||||
|
<p className="text-sm text-gray-500">Loading areas...</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Areas Dropdown */}
|
{/* Areas Dropdown */}
|
||||||
{loadingAreas ? (
|
{!loadingAreas && areas.length > 0 && (
|
||||||
<p className="text-sm text-gray-500">Loading areas...</p>
|
|
||||||
) : (
|
|
||||||
areas.length > 0 && (
|
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-sm font-medium text-gray-700">
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
||||||
Area:
|
Area:
|
||||||
@ -321,11 +304,9 @@ function UserProfile() {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Premium-Only Field */}
|
{/* Career Situation */}
|
||||||
{isPremiumUser && (
|
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-sm font-medium text-gray-700">
|
<label className="mb-1 block text-sm font-medium text-gray-700">
|
||||||
What best describes your current career situation?
|
What best describes your current career situation?
|
||||||
@ -336,13 +317,13 @@ function UserProfile() {
|
|||||||
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-blue-600 focus:outline-none"
|
||||||
>
|
>
|
||||||
<option value="">Select One</option>
|
<option value="">Select One</option>
|
||||||
<option value="highSchool">High School Student</option>
|
{careerSituations.map((cs) => (
|
||||||
<option value="collegeStudent">College Student</option>
|
<option key={cs.id} value={cs.id}>
|
||||||
<option value="transitioningPro">Transitioning Professional</option>
|
{cs.title}
|
||||||
<option value="advancingPro">Advancing Professional</option>
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Form Buttons */}
|
{/* Form Buttons */}
|
||||||
<div className="mt-6 flex items-center justify-end space-x-3">
|
<div className="mt-6 flex items-center justify-end space-x-3">
|
||||||
|
@ -48,13 +48,12 @@ export function haversineDistance(lat1, lon1, lat2, lon2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch schools
|
// Fetch schools
|
||||||
export const fetchSchools = async (cipCode, state = '', level = '', type = '') => {
|
export const fetchSchools = async (cipCode) => {
|
||||||
try {
|
try {
|
||||||
const apiUrl = process.env.REACT_APP_API_URL || '';
|
const apiUrl = process.env.REACT_APP_API_URL || '';
|
||||||
const response = await axios.get(`${apiUrl}/schools`, {
|
const response = await axios.get(`${apiUrl}/schools`, {
|
||||||
params: {
|
params: {
|
||||||
cipCode,
|
cipCode
|
||||||
state,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"soc_code": "19-4071.00",
|
|
||||||
"title": "Forest and Conservation Technicians",
|
|
||||||
"ratings": {
|
|
||||||
"growth": 1,
|
|
||||||
"balance": 3,
|
|
||||||
"recognition": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"soc_code": "15-1241.00",
|
|
||||||
"title": "Computer Network Architects",
|
|
||||||
"ratings": {
|
|
||||||
"growth": 5,
|
|
||||||
"balance": 4,
|
|
||||||
"recognition": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user