From 3934e59369da4ad847dac44c684f007db73a71b2 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 5 Mar 2025 14:08:02 +0000 Subject: [PATCH] Optimized re-rendering of CareerSuggestions and Popoutpanel --- src/components/CareerSuggestions.js | 2 +- src/components/Dashboard.css | 19 +++- src/components/Dashboard.js | 163 +++++++++++++++++----------- src/components/PopoutPanel.css | 9 ++ src/components/PopoutPanel.js | 13 ++- 5 files changed, 135 insertions(+), 71 deletions(-) diff --git a/src/components/CareerSuggestions.js b/src/components/CareerSuggestions.js index 354de33..14368dd 100644 --- a/src/components/CareerSuggestions.js +++ b/src/components/CareerSuggestions.js @@ -30,7 +30,7 @@ export function CareerSuggestions({ careerSuggestions = [], userState, areaTitle const careerPromises = careerSuggestions.map(async (career) => { try { - console.log(`Checking data for: ${career.title} (${career.code})`); + const headers = { Authorization: `Bearer ${token}`, diff --git a/src/components/Dashboard.css b/src/components/Dashboard.css index ea3bc8b..c3cb7ae 100644 --- a/src/components/Dashboard.css +++ b/src/components/Dashboard.css @@ -154,31 +154,46 @@ h2 { .filter-container { width: 100%; max-width: 900px; /* Ensures alignment */ + min-width: 200px; /* Ensures dropdowns have enough space */ + gap: 20px; /* Add spacing between filters */ display: flex; align-items: center; + font-size: 14px; + flex-wrap: nowrap; /* Ensures responsiveness */ justify-content: flex-start; background: #ffffff; - padding: 10px 15px; + padding: 6px; border-radius: 8px; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); margin-bottom: 10px; } +.filter-group { + display: flex; + align-items: center; + gap: 10px; /* Space between label and dropdown */ +} + /* Style Dropdown */ .filter-container label { font-size: 14px; - font-weight: 600; + font-weight: bold; + white-space: nowrap; /* Prevents labels from wrapping */ color: #333; margin-right: 10px; } .filter-container select { padding: 8px; + font-weight: bold; font-size: 14px; border: 1px solid #ccc; border-radius: 4px; background-color: #fff; + margin-right: 8px; /* Space between label and dropdown */ + white-space: nowrap; /* Prevents label from wrapping */ cursor: pointer; + min-width: 220px; /* Ensures dropdowns have enough width */ } /* RIASEC Scores */ diff --git a/src/components/Dashboard.js b/src/components/Dashboard.js index fe5301f..de2ff60 100644 --- a/src/components/Dashboard.js +++ b/src/components/Dashboard.js @@ -1,6 +1,6 @@ // Dashboard.js import axios from 'axios'; -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useMemo, 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'; @@ -11,6 +11,7 @@ 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() { @@ -33,9 +34,8 @@ function Dashboard() { const [riaSecDescriptions, setRiaSecDescriptions] = useState([]); const [selectedJobZone, setSelectedJobZone] = useState(''); const [careersWithJobZone, setCareersWithJobZone] = useState([]); // Store careers with job zone info - - - + const [selectedFit, setSelectedFit] = useState(''); + const jobZoneLabels = { '1': 'Little or No Preparation', '2': 'Some Preparation Needed', @@ -44,6 +44,12 @@ function Dashboard() { '5': 'Extensive Preparation Needed' }; + const fitLabels = { + 'Best': 'Best - Very Strong Match', + 'Great': 'Great - Strong Match', + 'Good': 'Good - Less Strong Match' + }; + // Dynamic API URL const apiUrl = process.env.REACT_APP_API_URL || ''; @@ -71,19 +77,39 @@ function Dashboard() { fetchJobZones(); }, [careerSuggestions, apiUrl]); - const filteredCareers = selectedJobZone - ? careersWithJobZone.filter(career => { + const filteredCareers = useMemo(() => { + return careersWithJobZone.filter((career) => { + const jobZoneMatches = selectedJobZone + ? career.job_zone !== null && + career.job_zone !== undefined && + typeof career.job_zone === 'number' && + Number(career.job_zone) === Number(selectedJobZone) + : true; - return ( - career.job_zone !== null && - career.job_zone !== undefined && - typeof career.job_zone === 'number' && - Number(career.job_zone) === Number(selectedJobZone) - ); - }) - : careersWithJobZone; + const fitMatches = selectedFit ? career.fit === selectedFit : true; + return jobZoneMatches && fitMatches; + }); +}, [careersWithJobZone, selectedJobZone, selectedFit]); +const memoizedCareerSuggestions = useMemo(() => filteredCareers, [filteredCareers]); + + const memoizedPopoutPanel = useMemo(() => { + return ( + setSelectedCareer(null)} + loading={loading} + error={error} + userState={userState} + /> + ); + }, [selectedCareer, careerDetails, schools, salaryData, economicProjections, tuitionData, loading, error, userState]); useEffect(() => { let descriptions = []; // Declare outside for scope accessibility @@ -221,61 +247,66 @@ function Dashboard() { return (
-
- - -
+
+
+ + +
-
-
- -
- -
-
-

RIASEC Scores

- -
-
-

RIASEC Personality Descriptions

- {riaSecDescriptions.length > 0 ? ( -
    - {riaSecDescriptions.map((desc, index) => ( -
  • - {riaSecScores[index]?.area}: {desc} -
  • - ))} -
- ) : ( -

Loading descriptions...

- )} -
-
-
+
+ + +
+
- {selectedCareer && ( - setSelectedCareer(null)} - loading={loading} - error={error} - userState={userState} - /> - )} +
+
+ +
+ +
+
+

RIASEC Scores

+ +
+
+

RIASEC Personality Descriptions

+ {riaSecDescriptions.length > 0 ? ( +
    + {riaSecDescriptions.map((desc, index) => ( +
  • + {riaSecScores[index]?.area}: {desc} +
  • + ))} +
+ ) : ( +

Loading descriptions...

+ )} +
+
+
+ + {memoizedPopoutPanel} + {/* Pass context to Chatbot */}
diff --git a/src/components/PopoutPanel.css b/src/components/PopoutPanel.css index e60329f..7753cd1 100644 --- a/src/components/PopoutPanel.css +++ b/src/components/PopoutPanel.css @@ -13,6 +13,15 @@ z-index: 1000; /* Ensures it is above other elements */ } +.popout-panel.hidden { + display: none; +} + +.popout-panel.visible { + display: block; +} + + /* Mobile responsiveness */ @media (max-width: 768px) { .popout-panel { diff --git a/src/components/PopoutPanel.js b/src/components/PopoutPanel.js index 454f9d5..cb3c95a 100644 --- a/src/components/PopoutPanel.js +++ b/src/components/PopoutPanel.js @@ -4,25 +4,27 @@ import './PopoutPanel.css'; import { useState, useEffect } from 'react'; function PopoutPanel({ + isVisible, data = {}, userState = 'N/A', // Passed explicitly from Dashboard loading = false, error = null, closePanel, }) { - console.log('PopoutPanel Props:', { data, loading, error, userState }); const [isCalculated, setIsCalculated] = useState(false); const [results, setResults] = useState([]); // Store loan repayment calculation results const [loadingCalculation, setLoadingCalculation] = useState(false); - // ✅ Reset `results` when a new career is selected +// ✅ Reset `results` when a new career is selected useEffect(() => { console.log(`Career changed to: ${data?.title}, clearing previous loan results.`); setResults([]); // ✅ Clears results when a new career is selected setIsCalculated(false); // ✅ Reset calculation state }, [data]); // Runs whenever `data` changes (i.e., new career is selected) +if (!isVisible) return null; // ✅ Prevents rendering instead of hiding the whole dashboard + // Handle loading state if (loading) { return ( @@ -54,6 +56,13 @@ useEffect(() => { return 4; // Default to 4 years if unspecified }; + function handleClosePanel() { + setResults([]); // Clear only LoanRepayment results + setIsCalculated(false); // Reset calculation state + closePanel(); // Maintain existing close behavior + } + + return (