// Dashboard.js import axios from 'axios'; import React, { useState, useCallback, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js'; import { CareerSuggestions } from './CareerSuggestions.js'; import PopoutPanel from './PopoutPanel.js'; import { Bar } from 'react-chartjs-2'; import { fetchSchools } from '../utils/apiUtils.js'; import './Dashboard.css'; ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend); function Dashboard() { const location = useLocation() const navigate = useNavigate(); const [careerSuggestions, setCareerSuggestions] = useState([]); const [careerDetails, setCareerDetails] = useState(null); const [riaSecScores, setRiaSecScores] = useState([]); const [selectedCareer, setSelectedCareer] = useState(null); const [schools, setSchools] = useState([]); const [salaryData, setSalaryData] = useState([]); const [economicProjections, setEconomicProjections] = useState(null); const [tuitionData, setTuitionData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [userState, setUserState] = useState(null); const [areaTitle, setAreaTitle] = useState(null); const [riaSecDescriptions, setRiaSecDescriptions] = useState([]); // Dynamic API URL const apiUrl = process.env.REACT_APP_API_URL || '/api'; useEffect(() => { let descriptions = []; // Declare outside for scope accessibility if (location.state) { const { careerSuggestions: suggestions, riaSecScores: scores } = location.state || {}; descriptions = scores.map((score) => score.description || "No description available."); setCareerSuggestions(suggestions || []); setRiaSecScores(scores || []); setRiaSecDescriptions(descriptions); // Set descriptions } else { console.warn('No data found, redirecting to Interest Inventory'); navigate('/interest-inventory'); } }, [location.state, navigate]); useEffect(() => { const fetchUserProfile = async () => { try { const token = localStorage.getItem('token'); const profileResponse = await fetch(`${apiUrl}/user-profile`, { headers: { Authorization: `Bearer ${token}` }, }); if (profileResponse.ok) { const profileData = await profileResponse.json(); console.log('Fetched User Profile:', profileData); const { state, area } = profileData; // Use 'area' instead of 'AREA_TITLE' setUserState(state); setAreaTitle(area && area.trim() ? area.trim() : ''); // Ensure 'area' is set correctly console.log('Profile Data Set:', { state, area }); } else { console.error('Failed to fetch user profile'); } } catch (error) { console.error('Error fetching user profile:', error); } }; fetchUserProfile(); }, [apiUrl]); const handleCareerClick = useCallback( async (career) => { const socCode = career.code; // Extract SOC code from career object setSelectedCareer(career); // Set career first to trigger loading panel setLoading(true); // Enable loading state only when career is clicked setError(null); // Clear previous errors setCareerDetails({}); // Reset career details to avoid undefined errors setSchools([]); setSalaryData([]); setEconomicProjections({}); setTuitionData([]); if (!socCode) { console.error('SOC Code is missing'); setError('SOC Code is missing'); return; } try { // Step 1: Fetch CIP Code const cipResponse = await fetch(`${apiUrl}/cip/${socCode}`); if (!cipResponse.ok) throw new Error('Failed to fetch CIP Code'); const { cipCode } = await cipResponse.json(); const cleanedCipCode = cipCode.replace('.', '').slice(0, 4); // Step 2: Fetch Data in Parallel const [filteredSchools, economicResponse, tuitionResponse, salaryResponse] = await Promise.all([ fetchSchools(cleanedCipCode, userState), axios.get(`${apiUrl}/projections/${socCode.split('.')[0]}`), axios.get(`${apiUrl}/tuition/${cleanedCipCode}`, { params: { state: userState }, }), axios.get(`${apiUrl}/salary`, { params: { socCode: socCode.split('.')[0], area: areaTitle }, }), ]); // Step 3: Format Salary Data const salaryDataPoints = [ { percentile: '10th Percentile', value: salaryResponse.data.A_PCT10 || 0 }, { percentile: '25th Percentile', value: salaryResponse.data.A_PCT25 || 0 }, { percentile: 'Median', value: salaryResponse.data.A_MEDIAN || 0 }, { percentile: '75th Percentile', value: salaryResponse.data.A_PCT75 || 0 }, { percentile: '90th Percentile', value: salaryResponse.data.A_PCT90 || 0 }, ]; // Step 4: Consolidate Career Details setCareerDetails({ ...career, economicProjections: economicResponse.data, salaryData: salaryDataPoints, schools: filteredSchools, tuitionData: tuitionResponse.data, }); } catch (error) { console.error('Error processing career click:', error.message); setError('Failed to load data'); } finally { setLoading(false); } }, [userState, apiUrl, areaTitle] ); const chartData = { labels: riaSecScores.map((score) => score.area), datasets: [ { label: 'RIASEC Scores', data: riaSecScores.map((score) => score.score), backgroundColor: 'rgba(75, 192, 192, 0.2)', borderColor: 'rgba(75, 192, 192, 1)', borderWidth: 1, }, ], }; return (
Loading descriptions...
)}