diff --git a/src/components/CareerSuggestions.js b/src/components/CareerSuggestions.js index 29b9ae1..354de33 100644 --- a/src/components/CareerSuggestions.js +++ b/src/components/CareerSuggestions.js @@ -6,46 +6,56 @@ const apiUrl = process.env.REACT_APP_API_URL || ''; // ✅ Load API URL directly export function CareerSuggestions({ careerSuggestions = [], userState, areaTitle, onCareerClick }) { const [updatedCareers, setUpdatedCareers] = useState([]); + const [loading, setLoading] = useState(true); + const [progress, setProgress] = useState(0); useEffect(() => { - if (!careerSuggestions || careerSuggestions.length === 0) return; + if (!careerSuggestions || careerSuggestions.length === 0) { + setLoading(false); + return; + } const token = localStorage.getItem('token'); // Get auth token const checkCareerDataAvailability = async () => { + setLoading(true); + setProgress(0); + const totalSteps = careerSuggestions.length * 4; // Each career has 4 API checks + let completedSteps = 0; + + const updateProgress = () => { + completedSteps += 1; + setProgress((completedSteps / totalSteps) * 100); + }; + const careerPromises = careerSuggestions.map(async (career) => { try { console.log(`Checking data for: ${career.title} (${career.code})`); - - console.log(`Fetching CIP Code from: ${apiUrl}/cip/${career.code}`); - console.log(`Fetching Job Description from: ${apiUrl}/onet/career-description/${career.code}`); - console.log(`Fetching Economic Projections from: ${apiUrl}/projections/${career.code.split('.')[0]}`); - console.log(`Fetching Salary Data from: ${apiUrl}/salary?socCode=${career.code.split('.')[0]}`); + const headers = { - Authorization: `Bearer ${token}`, // Match Dashboard.js headers - Accept: 'application/json', // Ensure JSON response + Authorization: `Bearer ${token}`, + Accept: 'application/json', }; - // Helper function to fetch and check JSON responses const fetchJSON = async (url) => { try { const response = await axios.get(url, { headers }); + updateProgress(); // ✅ Update progress on success return response.data; } catch (error) { - console.warn(`⚠️ Error fetching ${url}:`, error.response?.status, error.response?.data); - return null; // Return null if request fails + console.warn(`⚠️ Error fetching ${url}:`, error.response?.status); + updateProgress(); // ✅ Update progress even if failed + return null; } }; // Step 1: Fetch CIP Code const cipData = await fetchJSON(`${apiUrl}/cip/${career.code}`); const isCipMissing = !cipData || Object.keys(cipData).length === 0; - console.log(`CIP Code for ${career.code}:`, cipData); // Step 2: Fetch Job Description & Tasks const jobDetailsData = await fetchJSON(`${apiUrl}/onet/career-description/${career.code}`); const isJobDetailsMissing = !jobDetailsData || Object.keys(jobDetailsData).length === 0; - console.log(`Job Details for ${career.code}:`, jobDetailsData); // Step 3: Fetch Salary & Economic Projections in Parallel const [economicData, salaryResponse] = await Promise.all([ @@ -53,60 +63,59 @@ export function CareerSuggestions({ careerSuggestions = [], userState, areaTitle axios.get(`${apiUrl}/salary`, { params: { socCode: career.code.split('.')[0], area: areaTitle }, headers, - }).catch((error) => error.response), // Catch error for 404 handling + }).then((res) => { + updateProgress(); + return res.data; + }).catch((error) => { + updateProgress(); + return error.response?.status === 404 ? null : error.response; + }), ]); const isEconomicMissing = !economicData || Object.keys(economicData).length === 0; - console.log(`Economic Data for ${career.code}:`, economicData); + const isSalaryMissing = !salaryResponse; - // Salary check: If it fails with 404, we know it's missing - const isSalaryMissing = salaryResponse?.status === 404; - if (isSalaryMissing) { - console.log(`⚠️ Missing Salary Data for ${career.title} (${career.code})`); - } - - // Debugging: Log when a career is missing data - if (isCipMissing || isJobDetailsMissing || isEconomicMissing || isSalaryMissing) { - console.log(`⚠️ Limited Data for ${career.title} (${career.code})`); - } - - // Set `limitedData` to true if any required data is missing const isLimitedData = isCipMissing || isJobDetailsMissing || isEconomicMissing || isSalaryMissing; return { ...career, limitedData: isLimitedData }; } catch (error) { console.error(`Error checking API response for ${career.title}:`, error); - return { ...career, limitedData: true }; // Mark as limited if any request fails + return { ...career, limitedData: true }; } }); const updatedCareerList = await Promise.all(careerPromises); - - console.log("✅ Final Updated Careers with limitedData:", updatedCareerList); setUpdatedCareers(updatedCareerList); + setLoading(false); }; checkCareerDataAvailability(); }, [careerSuggestions, apiUrl, userState, areaTitle]); - if (updatedCareers.length === 0) { - return

Loading career suggestions...

; - } - return (

Career Suggestions

-
- {updatedCareers.map((career) => ( - - ))} -
+ + {loading ? ( +
+
+ {Math.round(progress)}% +
+

Loading Career Suggestions...

+
+ ) : ( +
+ {updatedCareers.map((career) => ( + + ))} +
+ )}
); } diff --git a/src/components/Dashboard.css b/src/components/Dashboard.css index 543e323..ea3bc8b 100644 --- a/src/components/Dashboard.css +++ b/src/components/Dashboard.css @@ -107,6 +107,35 @@ h2 { background-color: #0056b3; } +/* Progress Bar */ +.progress-container { + width: 100%; + background-color: #e0e0e0; + border-radius: 4px; + margin: 20px 0; + padding: 10px; + text-align: center; +} + +/* Striped Progress Bar */ +.progress-bar { + height: 12px; + background: repeating-linear-gradient( + -45deg, + #007bff, + #007bff 10px, + #5a9bff 10px, + #5a9bff 20px + ); + width: 0%; + transition: width 0.3s ease-in-out; + text-align: center; + font-size: 12px; + color: white; + border-radius: 4px; + line-height: 12px; + font-weight: bold; +} /* RIASEC Section */ .riasec-container { diff --git a/src/components/Dashboard.js b/src/components/Dashboard.js index 68c5067..fe5301f 100644 --- a/src/components/Dashboard.js +++ b/src/components/Dashboard.js @@ -34,6 +34,8 @@ function Dashboard() { const [selectedJobZone, setSelectedJobZone] = useState(''); const [careersWithJobZone, setCareersWithJobZone] = useState([]); // Store careers with job zone info + + const jobZoneLabels = { '1': 'Little or No Preparation', '2': 'Some Preparation Needed',