import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { ClipLoader } from "react-spinners"; import { v4 as uuidv4 } from "uuid"; import LoanRepayment from "./LoanRepayment.js"; import "./PopoutPanel.css"; // You can keep or remove depending on your needs function PopoutPanel({ isVisible, data = {}, userState = "N/A", loading = false, error = null, closePanel, updateChatbotContext, }) { // Original local states const [isCalculated, setIsCalculated] = useState(false); const [results, setResults] = useState([]); const [loadingCalculation, setLoadingCalculation] = useState(false); const [persistedROI, setPersistedROI] = useState({}); const [programLengths, setProgramLengths] = useState([]); const [sortBy, setSortBy] = useState("tuition"); const [maxTuition, setMaxTuition] = useState(50000); const [maxDistance, setMaxDistance] = useState(200); const token = localStorage.getItem("token"); const navigate = useNavigate(); // Destructure your data const { jobDescription = null, tasks = null, title = "Career Details", economicProjections = {}, salaryData = [], schools = [], } = data || {}; // Clear results if sorting or filters change useEffect(() => { setResults([]); setIsCalculated(false); }, [sortBy, maxTuition, maxDistance]); // Derive program lengths from school CREDDESC useEffect(() => { setProgramLengths( schools.map((school) => getProgramLength(school["CREDDESC"])) ); }, [schools]); // Update chatbot context if data is present useEffect(() => { if (data && Object.keys(data).length > 0) { updateChatbotContext({ careerDetails: data, schools, salaryData, economicProjections, results, persistedROI, }); } }, [ data, schools, salaryData, economicProjections, results, persistedROI, updateChatbotContext, ]); // If panel isn't visible, don't render if (!isVisible) return null; // If the panel or the loan calc is loading, show a spinner if (loading || loadingCalculation) { return (

Loading Career Details...

); } // Original helper function getProgramLength(degreeType) { if (degreeType?.includes("Associate")) return 2; if (degreeType?.includes("Bachelor")) return 4; if (degreeType?.includes("Master")) return 6; if (degreeType?.includes("Doctoral") || degreeType?.includes("First Professional")) return 8; if (degreeType?.includes("Certificate")) return 1; return 4; } // Original close logic function handleClosePanel() { setResults([]); setIsCalculated(false); closePanel(); } async function handlePlanMyPath() { if (!token) { alert("You need to be logged in to create a career path."); return; } try { // 1) Fetch existing career profiles (a.k.a. "careerPaths") const allPathsResponse = await fetch( `${process.env.REACT_APP_API_URL}/premium/career-profile/all`, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, } ); if (!allPathsResponse.ok) { throw new Error(`HTTP error ${allPathsResponse.status}`); } // The server returns { careerPaths: [...] } const { careerPaths } = await allPathsResponse.json(); // 2) Check if there's already a career path with the same name const match = careerPaths.find((cp) => cp.career_name === data.title); if (match) { // If a path already exists for this career, confirm with the user const decision = window.confirm( `A career path (scenario) for "${data.title}" already exists.\n\n` + `Click OK to RELOAD the existing path.\nClick Cancel to CREATE a new one.` ); if (decision) { // Reload existing path → go to Paywall navigate("/paywall", { state: { selectedCareer: { career_path_id: match.id, // 'id' is the primary key from the DB career_name: data.title, }, }, }); return; } } // 3) Otherwise, create a new career profile using POST /premium/career-profile const newResponse = await fetch( `${process.env.REACT_APP_API_URL}/premium/career-profile`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ // The server expects at least career_name career_name: data.title, // Optionally pass scenario_title, start_date, etc. }), } ); if (!newResponse.ok) { throw new Error("Failed to create new career path."); } // The server returns something like { message: 'Career profile upserted.', career_path_id: 'xxx-xxx' } const result = await newResponse.json(); const newlyCreatedId = result?.career_path_id; // 4) Navigate to /paywall, passing the newly created career_path_id navigate("/paywall", { state: { selectedCareer: { career_path_id: newlyCreatedId, career_name: data.title, }, }, }); } catch (error) { console.error("Error in Plan My Path:", error); } } // Filter & sort schools const filteredAndSortedSchools = [...schools] .filter((school) => { const inStateCost = parseFloat(school["In_state cost"]) || 0; const distance = parseFloat((school["distance"] || "0").replace(" mi", "")); return inStateCost <= maxTuition && distance <= maxDistance; }) .sort((a, b) => { if (sortBy === "tuition") return a["In_state cost"] - b["In_state cost"]; if (sortBy === "distance") { const distA = parseFloat((a["distance"] || "0").replace(" mi", "")); const distB = parseFloat((b["distance"] || "0").replace(" mi", "")); return distA - distB; } return 0; }); return (
{/* Header */}
{/* Main Content */}
{/* Title */}

{title}

{/* Error */} {error && (
{error}
)} {/* Job Description */}

Job Description

{jobDescription || "No description available"}

{/* Tasks */}

Expected Tasks

{tasks && tasks.length > 0 ? (
    {tasks.map((task, idx) => (
  • {task}
  • ))}
) : (

No tasks available.

)}
{/* Economic Projections */}

Economic Projections for {userState}

{economicProjections && typeof economicProjections === "object" ? (
  • 2022 Employment: {economicProjections["2022 Employment"] || "N/A"}
  • 2032 Employment: {economicProjections["2032 Employment"] || "N/A"}
  • Total Change: {economicProjections["Total Change"] || "N/A"}
) : (

No economic projections available.

)}
{/* Salary Data */}

Salary Data

{salaryData.length > 0 ? ( {salaryData.map((point, idx) => ( ))}
Percentile Regional Salary US Salary
{point.percentile} {point.regionalSalary > 0 ? `$${parseInt(point.regionalSalary, 10).toLocaleString()}` : "N/A"} {point.nationalSalary > 0 ? `$${parseInt(point.nationalSalary, 10).toLocaleString()}` : "N/A"}
) : (

Salary data is not available.

)}
{/* Schools Offering Programs */}

Schools Offering Programs

{/* Filter Bar */}
{filteredAndSortedSchools.length > 0 ? (
{filteredAndSortedSchools.map((school, idx) => (
{school["INSTNM"] || "Unnamed School"}

Degree Type: {school["CREDDESC"] || "N/A"}

In-State Tuition: ${school["In_state cost"] || "N/A"}

Out-of-State Tuition: ${school["Out_state cost"] || "N/A"}

Distance: {school["distance"] || "N/A"}

Website:{" "} {school["Website"]}

))}
) : (

No schools matching your filters.

)}
{/* Loan Repayment Analysis */}

Loan Repayment Analysis

({ schoolName: school["INSTNM"], inState: parseFloat(school["In_state cost"]) || 0, outOfState: parseFloat(school["Out_state cost"]) || 0, inStateGraduate: parseFloat(school["In State Graduate"]) || parseFloat(school["In_state cost"]) || 0, outStateGraduate: parseFloat(school["Out State Graduate"]) || parseFloat(school["Out_state cost"]) || 0, degreeType: school["CREDDESC"], programLength: programLengths[i], }))} salaryData={salaryData} setResults={setResults} setLoading={setLoadingCalculation} setPersistedROI={setPersistedROI} />
{/* Results Display */} {results.length > 0 && (

Comparisons by School over the life of the loan

{/* ========================================= Here's the key part: a grid for results. ========================================= */}
{results.map((result, idx) => (

{result.schoolName} - {result.degreeType || "N/A"}

Total Tuition: ${result.totalTuition}

Monthly Payment: ${result.monthlyPayment}

Total Monthly Payment (with extra): $ {result.totalMonthlyPayment}

Total Loan Cost: ${result.totalLoanCost}

Net Gain: ${result.netGain}

Monthly Salary (Gross): ${result.monthlySalary}

))}
)}
); } export default PopoutPanel;