diff --git a/src/components/PremiumOnboarding/CareerOnboarding.js b/src/components/PremiumOnboarding/CareerOnboarding.js index c21f639..29f6b65 100644 --- a/src/components/PremiumOnboarding/CareerOnboarding.js +++ b/src/components/PremiumOnboarding/CareerOnboarding.js @@ -1,4 +1,4 @@ -// CareerOnboarding.js (inline implementation of career search) +// CareerOnboarding.js import React, { useState, useEffect } from 'react'; import axios from 'axios'; import { Input } from '../ui/input.js'; // Ensure path matches your structure @@ -25,24 +25,23 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData }) => { } }, []); - // Fetch careers exactly once on mount useEffect(() => { const fetchCareerTitles = async () => { try { const response = await fetch('/career_clusters.json'); const data = await response.json(); - + const careerTitlesSet = new Set(); - + const clusters = Object.keys(data); for (let i = 0; i < clusters.length; i++) { const cluster = clusters[i]; const subdivisions = Object.keys(data[cluster]); - + for (let j = 0; j < subdivisions.length; j++) { const subdivision = subdivisions[j]; const careersArray = data[cluster][subdivision]; - + for (let k = 0; k < careersArray.length; k++) { const careerObj = careersArray[k]; if (careerObj.title) { @@ -51,18 +50,17 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData }) => { } } } - + setCareers([...careerTitlesSet]); - + } catch (error) { console.error("Error fetching or processing career_clusters.json:", error); } }; - + fetchCareerTitles(); }, []); - - // Update career selection automatically whenever the searchInput matches a valid career explicitly + useEffect(() => { if (careers.includes(searchInput)) { setSelectedCareer(searchInput); @@ -77,14 +75,13 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData }) => { const handleCareerInputChange = (e) => { const inputValue = e.target.value; setSearchInput(inputValue); - - // only set explicitly when an exact match occurs + if (careers.includes(inputValue)) { setSelectedCareer(inputValue); setData(prev => ({ ...prev, career_name: inputValue })); } }; - + const handleSubmit = () => { if (!selectedCareer || !currentlyWorking || !collegeEnrollmentStatus) { alert("Please complete all required fields before continuing."); @@ -110,31 +107,33 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData }) => { nextStep(); }; - return ( -
-

Career Details

+
+

Career Details

- - +
+ + +
-
-

Search for Career

+
+

Search for Career

{careers.map((career, index) => ( @@ -143,38 +142,79 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData }) => {
- {selectedCareer &&

Selected Career: {selectedCareer}

} + {selectedCareer && ( +

+ Selected Career: {selectedCareer} +

+ )} - - +
+ + +
- - +
+ + +
- - +
+ + +
- - +
+ + +
- - +
+ + +
); }; diff --git a/src/components/PremiumOnboarding/CollegeOnboarding.js b/src/components/PremiumOnboarding/CollegeOnboarding.js index c1a015a..9575068 100644 --- a/src/components/PremiumOnboarding/CollegeOnboarding.js +++ b/src/components/PremiumOnboarding/CollegeOnboarding.js @@ -1,7 +1,7 @@ +// CollegeOnboarding.js import React, { useState, useEffect } from 'react'; import authFetch from '../../utils/authFetch.js'; - function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) { // CIP / iPEDS local states (purely for CIP data and suggestions) const [schoolData, setSchoolData] = useState([]); @@ -11,8 +11,6 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) const [availableProgramTypes, setAvailableProgramTypes] = useState([]); // ---- DESCTRUCTURE PARENT DATA FOR ALL FIELDS EXCEPT TUITION/PROGRAM_LENGTH ---- - // We'll store user "typed" values for tuition/program_length in local states, - // but everything else comes directly from `data`. const { college_enrollment_status = '', selected_school = '', @@ -34,59 +32,42 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) hours_completed = '', credit_hours_required = '', tuition_paid = '', - // We do NOT consume data.tuition or data.program_length directly here - // because we store them in local states (manualTuition, manualProgramLength). } = data; // ---- 1. LOCAL STATES for auto/manual logic on TWO fields ---- - // manualTuition: user typed override - // autoTuition: iPEDS calculation - const [manualTuition, setManualTuition] = useState(''); // '' means no manual override + const [manualTuition, setManualTuition] = useState(''); const [autoTuition, setAutoTuition] = useState(0); - // same approach for program_length const [manualProgramLength, setManualProgramLength] = useState(''); const [autoProgramLength, setAutoProgramLength] = useState('0.00'); - // ------------------------------------------ - // Universal handleChange for all parent fields - // ------------------------------------------ + // -- universal handleChange for all parent fields except tuition/program_length const handleParentFieldChange = (e) => { const { name, value, type, checked } = e.target; let val = value; if (type === 'checkbox') { val = checked; } - // parse numeric fields that are NOT tuition or program_length if (['interest_rate','loan_term','extra_payment','expected_salary'].includes(name)) { val = parseFloat(val) || 0; } else if ( ['annual_financial_aid','existing_college_debt','credit_hours_per_year', - 'hours_completed','credit_hours_required','tuition_paid'] - .includes(name) + 'hours_completed','credit_hours_required','tuition_paid'].includes(name) ) { val = val === '' ? '' : parseFloat(val); } - setData(prev => ({ ...prev, [name]: val })); }; - // ------------------------------------------ - // handleManualTuition, handleManualProgramLength - // for local fields - // ------------------------------------------ const handleManualTuitionChange = (e) => { - // user typed something => override - setManualTuition(e.target.value); // store as string for partial typing + setManualTuition(e.target.value); }; const handleManualProgramLengthChange = (e) => { setManualProgramLength(e.target.value); }; - // ------------------------------------------ - // CIP Data fetch once - // ------------------------------------------ + // CIP fetch useEffect(() => { async function fetchCipData() { try { @@ -104,9 +85,7 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) fetchCipData(); }, []); - // ------------------------------------------ - // iPEDS Data fetch once - // ------------------------------------------ + // iPEDS fetch useEffect(() => { async function fetchIpedsData() { try { @@ -125,9 +104,7 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) fetchIpedsData(); }, []); - // ------------------------------------------ - // handleSchoolChange, handleProgramChange, etc. => update parent fields - // ------------------------------------------ + // handleSchoolChange const handleSchoolChange = (e) => { const value = e.target.value; setData(prev => ({ @@ -137,7 +114,6 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) program_type: '', credit_hours_required: '', })); - // CIP suggestions const filtered = schoolData.filter(s => s.INSTNM.toLowerCase().includes(value.toLowerCase()) ); @@ -163,7 +139,6 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) const handleProgramChange = (e) => { const value = e.target.value; setData(prev => ({ ...prev, selected_program: value })); - if (!value) { setProgramSuggestions([]); return; @@ -188,11 +163,11 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) program_type: val, credit_hours_required: '', })); - setManualProgramLength(''); // reset manual override + setManualProgramLength(''); setAutoProgramLength('0.00'); }; - // Once we have school+program, load possible program types + // once we have school+program, load possible program types useEffect(() => { if (!selected_program || !selected_school || !schoolData.length) return; const possibleTypes = schoolData @@ -204,15 +179,11 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) setAvailableProgramTypes([...new Set(possibleTypes)]); }, [selected_program, selected_school, schoolData]); - // ------------------------------------------ - // Auto-calc Tuition => store in local autoTuition - // ------------------------------------------ + // auto-calc tuition useEffect(() => { - // do we have enough to calc? if (!icTuitionData.length) return; if (!selected_school || !program_type || !credit_hours_per_year) return; - // find row const found = schoolData.find(s => s.INSTNM.toLowerCase() === selected_school.toLowerCase()); if (!found) return; const unitId = found.UNITID; @@ -221,7 +192,6 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) const match = icTuitionData.find(row => row.UNITID === unitId); if (!match) return; - // grad or undergrad const isGradOrProf = [ "Master's Degree", "Doctoral Degree", @@ -258,7 +228,6 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) const chpy = parseFloat(credit_hours_per_year) || 0; let estimate = 0; - // threshold if (chpy < 24 && partTimeRate) { estimate = partTimeRate * chpy; } else { @@ -266,15 +235,12 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) } setAutoTuition(Math.round(estimate)); - }, [ icTuitionData, selected_school, program_type, credit_hours_per_year, is_in_state, is_in_district, schoolData ]); - // ------------------------------------------ - // Auto-calc Program Length => store in local autoProgramLength - // ------------------------------------------ + // auto-calc program length useEffect(() => { if (!program_type) return; if (!hours_completed || !credit_hours_per_year) return; @@ -299,264 +265,315 @@ function CollegeOnboarding({ nextStep, prevStep, data, setData, careerPathId }) setAutoProgramLength(calcLength); }, [program_type, hours_completed, credit_hours_per_year, credit_hours_required]); - // ------------------------------------------ - // handleSubmit => merges final chosen values - // ------------------------------------------ + // final handleSubmit const handleSubmit = () => { - const chosenTuition = manualTuition.trim() === '' - ? autoTuition + const chosenTuition = manualTuition.trim() === '' + ? autoTuition : parseFloat(manualTuition); const chosenProgramLength = manualProgramLength.trim() === '' ? autoProgramLength : manualProgramLength; - // Update parent’s data (collegeData) setData(prev => ({ ...prev, - tuition: chosenTuition, // match name used by parent or server - program_length: chosenProgramLength // match name used by parent + tuition: chosenTuition, + program_length: chosenProgramLength })); - // Then go to the next step in the parent’s wizard nextStep(); }; - // The displayed tuition => (manualTuition !== '' ? manualTuition : autoTuition) const displayedTuition = (manualTuition.trim() === '' ? autoTuition : manualTuition); - - // The displayed program length => (manualProgramLength !== '' ? manualProgramLength : autoProgramLength) const displayedProgramLength = (manualProgramLength.trim() === '' ? autoProgramLength : manualProgramLength); - return ( -
-

College Details

+
+

College Details

{(college_enrollment_status === 'currently_enrolled' || college_enrollment_status === 'prospective_student') ? ( - <> -
); } diff --git a/src/components/PremiumOnboarding/FinancialOnboarding.js b/src/components/PremiumOnboarding/FinancialOnboarding.js index 6d6da21..295f8cf 100644 --- a/src/components/PremiumOnboarding/FinancialOnboarding.js +++ b/src/components/PremiumOnboarding/FinancialOnboarding.js @@ -1,3 +1,4 @@ +// FinancialOnboarding.js import React from 'react'; const FinancialOnboarding = ({ nextStep, prevStep, data, setData, isEditMode = false }) => { @@ -13,7 +14,6 @@ const FinancialOnboarding = ({ nextStep, prevStep, data, setData, isEditMode = f emergency_contribution = 0, extra_cash_emergency_pct = "", extra_cash_retirement_pct = "", - planned_monthly_expenses = '', planned_monthly_debt_payments = '', planned_monthly_retirement_contribution = '', @@ -22,7 +22,7 @@ const FinancialOnboarding = ({ nextStep, prevStep, data, setData, isEditMode = f planned_surplus_retirement_pct = '', planned_additional_income = '' } = data; - + const handleChange = (e) => { const { name, value } = e.target; let val = parseFloat(value) || 0; @@ -42,172 +42,250 @@ const FinancialOnboarding = ({ nextStep, prevStep, data, setData, isEditMode = f extra_cash_emergency_pct: 100 - val })); } else { - setData(prevData => ({ - ...prevData, - [name]: val - })); + setData(prevData => ({ ...prevData, [name]: val })); } }; return ( -
-

Financial Details

+
+

Financial Details

{currently_working === 'yes' && ( - <> - +
+
+ + +
- - +
+ + +
+
)} - - - - - - - - - - - - -

Extra Monthly Cash Allocation

-

If you have extra money left each month after expenses, how would you like to allocate it? (Must add to 100%)

- - - - - - - -{/* Only show the planned overrides if isEditMode is true */} -{isEditMode && ( - <> -
-

Planned Scenario Overrides

-

These fields let you override your real finances for this scenario.

- - +
+
+ +
- +
+ +
- +
+ +
- +
+ +
- +
+ +
- +
+ +
+
- +
+

Extra Monthly Cash Allocation

+

+ If you have extra money left each month after expenses, how would you like to allocate it? + (Must add to 100%) +

+ +
+ - +
+ +
+ + +
+
+ + {/* Only show the planned overrides if isEditMode is true */} + {isEditMode && ( +
+
+

Planned Scenario Overrides

+

+ These fields let you override your real finances for this scenario. +

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
)} - - + +
+ + +
); }; diff --git a/src/components/PremiumOnboarding/PremiumWelcome.js b/src/components/PremiumOnboarding/PremiumWelcome.js index c5fdbc6..98a7f8d 100644 --- a/src/components/PremiumOnboarding/PremiumWelcome.js +++ b/src/components/PremiumOnboarding/PremiumWelcome.js @@ -2,12 +2,17 @@ import React from 'react'; const PremiumWelcome = ({ nextStep }) => ( -
-

Welcome to AptivaAI Premium!

-

+

+

Welcome to AptivaAI Premium!

+

Let's get started by gathering some quick information to personalize your experience.

- +
);