diff --git a/src/components/MilestoneTracker.js b/src/components/MilestoneTracker.js index fe19cb0..6121bd3 100644 --- a/src/components/MilestoneTracker.js +++ b/src/components/MilestoneTracker.js @@ -276,6 +276,9 @@ export default function MilestoneTracker({ selectedCareer: initialCareer }) { const [selectedCareer, setSelectedCareer] = useState(initialCareer || null); const [careerProfileId, setCareerProfileId] = useState(null); const [scenarioRow, setScenarioRow] = useState(null); + const [aiRisk, setAiRisk] = useState(null); + const [aiRiskLoading, setAiRiskLoading] = useState(false); + const [aiRiskError, setAiRiskError] = useState(null); const [collegeProfile, setCollegeProfile] = useState(null); const [strippedSocCode, setStrippedSocCode] = useState(null); @@ -455,6 +458,81 @@ export default function MilestoneTracker({ selectedCareer: initialCareer }) { setStrippedSocCode(stripSocCode(found.soc_code)); }, [scenarioRow, masterCareerRatings]); + useEffect(() => { + // If we have no SOC code or scenarioRow is missing, reset + if (!strippedSocCode || !scenarioRow?.career_name) { + setAiRisk(null); + return; + } + + async function fetchAiRisk() { + setAiRiskLoading(true); + setAiRiskError(null); + + try { + // 1) Attempt local DB first + const localRes = await fetch(`${apiURL}/ai-risk/${strippedSocCode}`); + if (localRes.ok) { + const localData = await localRes.json(); + setAiRisk(localData); + } else if (localRes.status === 404) { + // 2) If not found => call GPT route + // We'll pass minimal data if we don't have job description or tasks + const chatPayload = { + socCode: strippedSocCode, + careerName: scenarioRow.career_name, + jobDescription: '', // or optionally fetch from O*NET / tasks + tasks: [] + }; + + // POST to your server3 public route + const gptRes = await fetch(`${apiURL}/public/ai-risk-analysis`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(chatPayload), + }); + if (!gptRes.ok) { + throw new Error('GPT call failed'); + } + const gptData = await gptRes.json(); + + // 3) Store in server2 so we skip GPT next time + await fetch(`${apiURL}/ai-risk`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + socCode: strippedSocCode, + careerName: gptData.careerName, + jobDescription: gptData.jobDescription, + tasks: gptData.tasks, + riskLevel: gptData.riskLevel, + reasoning: gptData.reasoning, + }) + }); + + // 4) Set it in state + setAiRisk({ + socCode: strippedSocCode, + careerName: scenarioRow.career_name, + riskLevel: gptData.riskLevel, + reasoning: gptData.reasoning + }); + } else { + // Some other error code + throw new Error(`AI Risk fetch error: ${localRes.status}`); + } + } catch (err) { + console.error('Error fetching AI risk =>', err); + setAiRiskError('Failed to load AI risk data.'); + setAiRisk(null); + } finally { + setAiRiskLoading(false); + } + } + + fetchAiRisk(); +}, [strippedSocCode, scenarioRow?.career_name, apiURL]); + // 6) Salary useEffect(() => { if (!strippedSocCode) { @@ -887,18 +965,34 @@ if (aiLoading || clickCount >= DAILY_CLICK_LIMIT) {

Where Am I Now?

{/* 1) Career */} -
-

- Current Career:{' '} - {scenarioRow?.career_name || '(Select a career)'} -

- {yearsInCareer && ( +

- Time in this career: {yearsInCareer}{' '} - {yearsInCareer === '<1' ? 'year' : 'years'} + Current Career:{' '} + {scenarioRow?.career_name || '(Select a career)'}

- )} -
+ {yearsInCareer && ( +

+ Time in this career: {yearsInCareer}{' '} + {yearsInCareer === '<1' ? 'year' : 'years'} +

+ )} + +
+ {aiRiskLoading ? ( +

Loading AI risk...

+ ) : aiRiskError ? ( +

{aiRiskError}

+ ) : aiRisk ? ( +
+ AI Risk Level: {aiRisk.riskLevel} +
+ {aiRisk.reasoning} +
+ ) : ( +

No AI risk data available

+ )} +
+
{/* 2) Salary Benchmarks */}
diff --git a/user_profile.db b/user_profile.db index abb6722..7fb4ada 100644 Binary files a/user_profile.db and b/user_profile.db differ