diff --git a/src/components/Chatbot.js b/src/components/Chatbot.js index 4650eec..30de1aa 100644 --- a/src/components/Chatbot.js +++ b/src/components/Chatbot.js @@ -25,11 +25,39 @@ const Chatbot = ({ context }) => { Use the following user-specific data: - Career Suggestions: ${context.careerSuggestions?.map((c) => c.title).join(", ") || "No suggestions available."} - Selected Career: ${context.selectedCareer?.title || "None"} - - Schools: ${context.schools?.map((s) => s["INSTNM"]).join(", ") || "No schools available."} - - Median Salary: ${ - context.salaryData?.find((s) => s.percentile === "Median")?.value || "Unavailable" + - Job Description: ${context.careerDetails?.jobDescription || "No job description available."} + - Expected Tasks: ${context.careerDetails?.tasks?.join(", ") || "No tasks available."} + - Salary Data: + ${context.salaryData?.length > 0 + ? context.salaryData.map(sd => `${sd.percentile}: $${sd.regionalSalary || "N/A"} (Regional), $${sd.nationalSalary || "N/A"} (National)`).join(", ") + : "Not available"} + - Schools Available: ${ + context.schools?.length > 0 + ? context.schools + .map(school => `${school.INSTNM} (Distance: ${school.distance || "N/A"}, Tuition: $${school["In_state cost"] || "N/A"})`) + .join("; ") + : "No schools available." + } + + - Economic Projections: ${ + context.economicProjections && Object.keys(context.economicProjections).length > 0 + ? `2022 Employment: ${context.economicProjections["2022 Employment"] || "N/A"}, ` + + `2032 Employment: ${context.economicProjections["2032 Employment"] || "N/A"}, ` + + `Total Change: ${context.economicProjections["Total Change"] || "N/A"}` + : "Not available" } - - ROI (Return on Investment): If available, use education costs vs. salary potential to guide users. + + - ROI Analysis: ${ + context.persistedROI && Array.isArray(context.persistedROI) && context.persistedROI.length > 0 + ? context.persistedROI + .map(roi => `${roi.schoolName}: Net Gain: $${roi.netGain || "N/A"}, Monthly Payment: $${roi.monthlyPayment || "N/A"}`) + .join("; ") + : "No ROI data available." + } + + - User State: ${context.userState || "Not provided"} + - User Area: ${context.areaTitle || "Not provided"} + - User Zipcode: ${context.userZipcode || "Not provided"} **Your response should ALWAYS provide analysis, not just list careers.** Example responses: diff --git a/src/components/Dashboard.js b/src/components/Dashboard.js index ac5a71f..ba1dab0 100644 --- a/src/components/Dashboard.js +++ b/src/components/Dashboard.js @@ -36,6 +36,8 @@ function Dashboard() { const [careersWithJobZone, setCareersWithJobZone] = useState([]); // Store careers with job zone info const [selectedFit, setSelectedFit] = useState(''); const [results, setResults] = useState([]); // Add results state + const [chatbotContext, setChatbotContext] = useState({}); + const jobZoneLabels = { '1': 'Little or No Preparation', @@ -93,9 +95,29 @@ function Dashboard() { }); }, [careersWithJobZone, selectedJobZone, selectedFit]); +const updateChatbotContext = (updatedData) => { + setChatbotContext((prevContext) => { + const mergedContext = { + ...prevContext, // ✅ Preserve existing context (Dashboard Data) + ...Object.keys(updatedData).reduce((acc, key) => { + if (updatedData[key] !== undefined && updatedData[key] !== null) { + acc[key] = updatedData[key]; // ✅ Only update fields with actual data + } + return acc; + }, {}), + }; + + console.log("🔄 Updated Chatbot Context (Merged Dashboard + PopoutPanel):", mergedContext); + return mergedContext; + }); +}; + + const memoizedCareerSuggestions = useMemo(() => filteredCareers, [filteredCareers]); const memoizedPopoutPanel = useMemo(() => { + console.log("Passing careerDetails to PopoutPanel:", careerDetails); + return ( filteredCareers, [filteredCareer error={error} userState={userState} results={results} + updateChatbotContext={updateChatbotContext} /> ); }, [selectedCareer, careerDetails, schools, salaryData, economicProjections, tuitionData, loading, error, userState]); @@ -153,6 +176,33 @@ const memoizedCareerSuggestions = useMemo(() => filteredCareers, [filteredCareer fetchUserProfile(); }, [apiUrl]); + useEffect(() => { + if ( + careerSuggestions.length > 0 && + riaSecScores.length > 0 && + userState !== null && + areaTitle !== null && + userZipcode !== null + ) { + console.log("✅ All data ready, forcing chatbotContext update..."); + + // ✅ Create a completely new object so React detects the change + const newChatbotContext = { + careerSuggestions: [...careersWithJobZone], // Ensure fresh array reference + riaSecScores: [...riaSecScores], // Ensure fresh array reference + userState: userState || "", + areaTitle: areaTitle || "", + userZipcode: userZipcode || "", + }; + + setChatbotContext(newChatbotContext); + } else { + console.log("⏳ Skipping chatbotContext update because data is not ready yet."); + } + }, [careerSuggestions, riaSecScores, userState, areaTitle, userZipcode, careersWithJobZone]); + + + const handleCareerClick = useCallback( async (career) => { const socCode = career.code; // Extract SOC code from career object @@ -239,15 +289,19 @@ async (career) => { ] : []; - setCareerDetails({ - ...career, - jobDescription: description, - tasks: tasks, - economicProjections: economicResponse.data || {}, - salaryData: salaryDataPoints, - schools: schoolsWithDistance, - tuitionData: tuitionResponse.data || [], - }); + const updatedCareerDetails = { + ...career, + jobDescription: description, + tasks: tasks, + economicProjections: economicResponse.data || {}, + salaryData: salaryDataPoints, + schools: schoolsWithDistance, + tuitionData: tuitionResponse.data || [], + }; + + // Ensure `careerDetails` is fully updated before passing to chatbot + setCareerDetails(updatedCareerDetails); + updateChatbotContext({ careerDetails: updatedCareerDetails }); } catch (error) { console.error('Error processing career click:', error.message); @@ -276,14 +330,9 @@ async (career) => { careerSuggestions, riaSecScores, selectedCareer, - schools, - salaryData, - economicProjections, - tuitionData, userState, areaTitle, userZipcode, - results, }); return ( @@ -362,21 +411,7 @@ async (career) => { {/* Pass context to Chatbot */}
{careerSuggestions.length > 0 ? ( - + ) : (

Loading Chatbot...

)} diff --git a/src/components/PopoutPanel.js b/src/components/PopoutPanel.js index 0ece026..7c680f9 100644 --- a/src/components/PopoutPanel.js +++ b/src/components/PopoutPanel.js @@ -11,6 +11,7 @@ function PopoutPanel({ loading = false, error = null, closePanel, + updateChatbotContext, }) { const [isCalculated, setIsCalculated] = useState(false); const [results, setResults] = useState([]); // Store loan repayment calculation results @@ -39,6 +40,24 @@ function PopoutPanel({ setProgramLengths(schools.map(school => getProgramLength(school['CREDDESC']))); }, [schools]); + useEffect(() => { + console.log("📩 Updating Chatbot Context from PopoutPanel:", data); + + if (data && Object.keys(data).length > 0) { + updateChatbotContext({ + careerDetails: data, + schools, + salaryData, + economicProjections, + results, + persistedROI, // ✅ Make sure ROI is included! + }); + } else { + console.log("⚠️ No valid PopoutPanel data to update chatbot context."); + } + }, [data, schools, salaryData, economicProjections, results, persistedROI, updateChatbotContext]); + + if (!isVisible) return null; if (loading || loadingCalculation) { @@ -51,6 +70,7 @@ function PopoutPanel({ ); } + // Get program length for calculating tuition const getProgramLength = (degreeType) => { if (degreeType?.includes("Associate")) return 2; @@ -94,7 +114,7 @@ function PopoutPanel({

{title}

- + {/* Job Description and Tasks */}

Job Description