dev1/src/components/CareerModal.js
Josh d5d3c6d63b
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Fixed limited data behavior, All Others in CareerExplorer
2025-08-05 15:18:54 +00:00

303 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { AlertTriangle } from 'lucide-react';
import isAllOther from '../utils/isAllOther.js';
function CareerModal({ career, careerDetails, closeModal, addCareerToList }) {
const [error, setError] = useState(null);
const [loadingRisk, setLoadingRisk] = useState(false);
const aiRisk = careerDetails?.aiRisk || null;
if (!careerDetails) {
return (
<div className="fixed inset-0 bg-gray-900 bg-opacity-70 flex justify-center items-center z-50">
<div className="bg-white rounded-lg shadow-lg p-6">
<p className="text-lg text-gray-700">Loading career details</p>
</div>
</div>
);
}
// Handle your normal careerDetails loading logic
if (careerDetails?.error) {
return (
<div className="fixed inset-0 bg-gray-900 bg-opacity-70 flex justify-center items-center z-50">
<div className="bg-white rounded-lg shadow-lg p-6 max-w-xl">
<p className="text-lg text-gray-700 mb-4">
{careerDetails.error}
</p>
<button
onClick={closeModal}
className="bg-red-500 text-white p-2 rounded"
>
Close
</button>
</div>
</div>
);
}
if (!careerDetails?.salaryData === undefined) {
return (
<div className="fixed inset-0 bg-gray-900 bg-opacity-70 flex justify-center items-center z-50">
<div className="bg-white rounded-lg shadow-lg p-6">
<p className="text-lg text-gray-700">Loading career details...</p>
</div>
</div>
);
}
if (error) return <div>{error}</div>;
// Helper for "stability" rating
const calculateStabilityRating = (salaryData) => {
const medianSalaryObj = salaryData.find((s) => s.percentile === 'Median');
const medianSalary =
medianSalaryObj?.regionalSalary || medianSalaryObj?.nationalSalary || 0;
if (medianSalary >= 90000) return 5;
if (medianSalary >= 70000) return 4;
if (medianSalary >= 50000) return 3;
if (medianSalary >= 30000) return 2;
return 1;
};
return (
<div className="fixed inset-0 bg-gray-900 bg-opacity-70 flex justify-center items-center overflow-auto z-50">
<div className="bg-white rounded-lg shadow-lg w-full max-w-5xl p-6 m-4 max-h-[90vh] overflow-y-auto">
{isAllOther(career) && (
<div className="mb-4 flex items-start rounded-md border-l-4 border-yellow-500 bg-yellow-50 p-3">
<AlertTriangle className="mt-[2px] mr-2 h-5 w-5 text-yellow-600" />
<p className="text-sm text-yellow-800">
You've selected an "umbrella" field that covers a wide range of careers—many
people begin a career journey with a broad interest area and we don't want to discourage
anyone from taking this approach. It's just difficult to display detailed career data
and daytoday tasks for this “allother” occupation. Use it as a starting point,
keep exploring specializations, and we can show you richer insights as soon as you are able
to narrow it down to a more specific role. If you know this is the field for you, go ahead to
add it to your comparison list or move straight into Preparing & Upskilling for Your Career!
</p>
</div>
)}
{/* Title row */}
<div className="flex justify-between items-center mb-4 pb-2 border-b">
<div>
<h2 className="text-2xl font-bold text-blue-600">
{careerDetails.title}
</h2>
{/* AI RISK SECTION */}
{loadingRisk && (
<p className="text-sm text-gray-500 mt-1">Loading AI risk…</p>
)}
{!loadingRisk && aiRisk && aiRisk.riskLevel && aiRisk.reasoning && (
<div className="text-sm text-gray-500 mt-1">
<strong>AI Risk Level:</strong> {aiRisk.riskLevel}
<br />
<span>{aiRisk.reasoning}</span>
</div>
)}
{!loadingRisk && !aiRisk && (
<p className="text-sm text-gray-500 mt-1">No AI risk data available</p>
)}
</div>
{/* Buttons */}
<div className="flex gap-2">
<button
onClick={() => {
const stabilityRating = calculateStabilityRating(
careerDetails.salaryData
);
addCareerToList({
...careerDetails,
ratings: {
stability: stabilityRating,
},
});
closeModal();
}}
className="text-white bg-green-500 hover:bg-green-600 rounded px-3 py-1"
>
Add to Comparison
</button>
<button
onClick={closeModal}
className="text-white bg-red-500 hover:bg-red-600 rounded px-3 py-1"
>
Close
</button>
</div>
</div>
{/* Job Description */}
{careerDetails.jobDescription && (
<div className="mb-4">
<h3 className="text-lg font-semibold mb-1">Job Description:</h3>
<p className="text-gray-700">{careerDetails.jobDescription}</p>
</div>
)}
{/* Tasks */}
{careerDetails.tasks?.length > 0 && (
<div className="mb-4 border-t pt-3">
<h3 className="text-lg font-semibold mb-2">Tasks:</h3>
<ul className="list-disc pl-5 space-y-1">
{careerDetails.tasks.map((task, i) => (
<li key={i}>{task}</li>
))}
</ul>
</div>
)}
{(careerDetails.salaryData?.length > 0 ||
(careerDetails.economicProjections &&
(careerDetails.economicProjections.state ||
careerDetails.economicProjections.national))) && (
<div className="flex flex-col md:flex-row gap-4 border-t pt-3">
{/* ── Salary table ───────────────────────── */}
{careerDetails.salaryData?.length > 0 && (
<div className="md:w-1/2 overflow-x-auto">
<h3 className="text-lg font-semibold mb-2">Salary Data</h3>
<table className="w-full text-left border border-gray-300 rounded">
<thead className="bg-gray-100">
<tr>
<th className="px-3 py-2 border-b">Percentile</th>
<th className="px-3 py-2 border-b">Regional Salary</th>
<th className="px-3 py-2 border-b">National Salary</th>
</tr>
</thead>
<tbody>
{careerDetails.salaryData.map((row, i) => (
<tr key={i}>
<td className="px-3 py-2 border-b">{row.percentile}</td>
<td className="px-3 py-2 border-b">
${row.regionalSalary.toLocaleString()}
</td>
<td className="px-3 py-2 border-b">
${row.nationalSalary.toLocaleString()}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{/* ── Economic projections ───────────────── */}
{(careerDetails.economicProjections?.state ||
careerDetails.economicProjections?.national) && (
<div className="md:w-1/2 overflow-x-auto">
<h3 className="text-lg font-semibold mb-2">Economic Projections</h3>
<table className="w-full text-left border border-gray-300 rounded">
<thead className="bg-gray-100">
<tr>
<th className="px-3 py-2 border-b"></th>
{careerDetails.economicProjections.state && (
<th className="px-3 py-2 border-b">
{careerDetails.economicProjections.state.area}
</th>
)}
{careerDetails.economicProjections.national && (
<th className="px-3 py-2 border-b">National</th>
)}
</tr>
</thead>
<tbody>
<tr>
<td className="px-3 py-2 border-b font-semibold">
Current Jobs
</td>
{careerDetails.economicProjections.state && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.state.base.toLocaleString()}
</td>
)}
{careerDetails.economicProjections.national && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.national.base.toLocaleString()}
</td>
)}
</tr>
<tr>
<td className="px-3 py-2 border-b font-semibold">
Jobs in&nbsp;10&nbsp;yrs
</td>
{careerDetails.economicProjections.state && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.state.projection.toLocaleString()}
</td>
)}
{careerDetails.economicProjections.national && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.national.projection.toLocaleString()}
</td>
)}
</tr>
<tr>
<td className="px-3 py-2 border-b font-semibold">Growth%</td>
{careerDetails.economicProjections.state && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.state.percentChange}%
</td>
)}
{careerDetails.economicProjections.national && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.national.percentChange}%
</td>
)}
</tr>
<tr>
<td className="px-3 py-2 border-b font-semibold">
Annual Openings
</td>
{careerDetails.economicProjections.state && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.state.annualOpenings.toLocaleString()}
</td>
)}
{careerDetails.economicProjections.national && (
<td className="px-3 py-2 border-b">
{careerDetails.economicProjections.national.annualOpenings.toLocaleString()}
</td>
)}
</tr>
</tbody>
</table>
{/* Conditional disclaimer when AI risk is Moderate or High */}
{aiRisk?.riskLevel &&
(aiRisk.riskLevel === 'Moderate' || aiRisk.riskLevel === 'High') && (
<p className="text-sm text-red-600 mt-2">
Note: These 10year projections may change if AIdriven tools
significantly affect {careerDetails.title} tasks. With a&nbsp;
<strong>{aiRisk.riskLevel.toLowerCase()}</strong> AI risk, its possible
some responsibilities could be automated over time.
</p>
)}
</div>
)}
</div>
)}
</div>
</div>
);
}
export default CareerModal;