Added CareerProfileForm and CollegeProfileForm, changed FinancialProfileForm for consistency, RetirementLanding button removal for FinancialProfileForm. Altered CareerOnboarding and ReviewPage

This commit is contained in:
Josh 2025-07-22 18:07:06 +00:00
parent 613f79f6ee
commit 465a7d686c
12 changed files with 1157 additions and 233 deletions

View File

@ -193,8 +193,7 @@ app.get('/api/premium/career-profile/latest', authenticatePremiumUser, async (re
const sql = ` const sql = `
SELECT SELECT
*, *,
DATE_FORMAT(start_date, '%Y-%m-%d') AS start_date, DATE_FORMAT(start_date, '%Y-%m-%d') AS start_date
DATE_FORMAT(projected_end_date, '%Y-%m-%d') AS projected_end_date
FROM career_profiles FROM career_profiles
WHERE user_id = ? WHERE user_id = ?
ORDER BY start_date DESC ORDER BY start_date DESC
@ -214,8 +213,7 @@ app.get('/api/premium/career-profile/all', authenticatePremiumUser, async (req,
const sql = ` const sql = `
SELECT SELECT
*, *,
DATE_FORMAT(start_date, '%Y-%m-%d') AS start_date, DATE_FORMAT(start_date, '%Y-%m-%d') AS start_date
DATE_FORMAT(projected_end_date, '%Y-%m-%d') AS projected_end_date
FROM career_profiles FROM career_profiles
WHERE user_id = ? WHERE user_id = ?
ORDER BY start_date ASC ORDER BY start_date ASC
@ -235,8 +233,7 @@ app.get('/api/premium/career-profile/:careerProfileId', authenticatePremiumUser,
const sql = ` const sql = `
SELECT SELECT
*, *,
DATE_FORMAT(start_date, '%Y-%m-%d') AS start_date, DATE_FORMAT(start_date, '%Y-%m-%d') AS start_date
DATE_FORMAT(projected_end_date, '%Y-%m-%d') AS projected_end_date
FROM career_profiles FROM career_profiles
WHERE id = ? WHERE id = ?
AND user_id = ? AND user_id = ?
@ -262,7 +259,6 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
career_name, career_name,
status, status,
start_date, start_date,
projected_end_date,
college_enrollment_status, college_enrollment_status,
currently_working, currently_working,
career_goals, career_goals,
@ -295,7 +291,6 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
career_name, career_name,
status, status,
start_date, start_date,
projected_end_date,
college_enrollment_status, college_enrollment_status,
currently_working, currently_working,
career_goals, career_goals,
@ -309,11 +304,10 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
planned_surplus_retirement_pct, planned_surplus_retirement_pct,
planned_additional_income planned_additional_income
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
status = VALUES(status), status = VALUES(status),
start_date = VALUES(start_date), start_date = VALUES(start_date),
projected_end_date = VALUES(projected_end_date),
college_enrollment_status = VALUES(college_enrollment_status), college_enrollment_status = VALUES(college_enrollment_status),
currently_working = VALUES(currently_working), currently_working = VALUES(currently_working),
career_goals = VALUES(career_goals), career_goals = VALUES(career_goals),
@ -336,7 +330,6 @@ app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res
career_name, career_name,
status || 'planned', status || 'planned',
start_date || null, start_date || null,
projected_end_date || null,
college_enrollment_status || null, college_enrollment_status || null,
currently_working || null, currently_working || null,
career_goals || null, career_goals || null,
@ -2453,29 +2446,32 @@ app.delete('/api/premium/milestones/:milestoneId', authenticatePremiumUser, asyn
------------------------------------------------------------------ */ ------------------------------------------------------------------ */
// GET /api/premium/financial-profile // GET /api/premium/financial-profile
app.get('/api/premium/financial-profile', auth, (req, res) => { app.get('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
const uid = req.userId; try {
db.query('SELECT * FROM financial_profile WHERE user_id=?', [uid], const [rows] = await pool.query(
(err, rows) => { 'SELECT * FROM financial_profiles WHERE user_id=? LIMIT 1',
if (err) return res.status(500).json({ error:'DB error' }); [req.id]
);
if (!rows.length) { if (!rows.length) {
// ←———— send a benign default instead of 404 return res.json({
return res.json({ current_salary: 0,
current_salary: 0, additional_income: 0,
additional_income: 0, monthly_expenses: 0,
monthly_expenses: 0, monthly_debt_payments: 0,
monthly_debt_payments: 0, retirement_savings: 0,
retirement_savings: 0, emergency_fund: 0,
emergency_fund: 0, retirement_contribution: 0,
retirement_contribution: 0, emergency_contribution: 0,
emergency_contribution: 0, extra_cash_emergency_pct: 50,
extra_cash_emergency_pct: 50, extra_cash_retirement_pct: 50
extra_cash_retirement_pct: 50 });
}); }
} res.json(rows[0]);
res.json(rows[0]); } catch (err) {
}); console.error('financialprofile GET error:', err);
res.status(500).json({ error: 'DB error' });
}
}); });
app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => { app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
@ -2725,6 +2721,21 @@ app.get('/api/premium/college-profile', authenticatePremiumUser, async (req, res
} }
}); });
// GET every college profile for the loggedin user
app.get('/api/premium/college-profile/all', authenticatePremiumUser, async (req,res)=>{
const sql = `
SELECT cp.*,
DATE_FORMAT(cp.created_at,'%Y-%m-%d') AS created_at,
IFNULL(cpr.scenario_title, cpr.career_name) AS career_title
FROM college_profiles cp
JOIN career_profiles cpr ON cpr.id = cp.career_profile_id
WHERE cp.user_id = ?
ORDER BY cp.created_at DESC
`;
const [rows] = await pool.query(sql,[req.id]);
res.json({ collegeProfiles: rows });
});
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------
AI-SUGGESTED MILESTONES AI-SUGGESTED MILESTONES
------------------------------------------------------------------ */ ------------------------------------------------------------------ */

View File

@ -25,6 +25,10 @@ import InterestInventory from './components/InterestInventory.js';
import Dashboard from './components/Dashboard.js'; import Dashboard from './components/Dashboard.js';
import UserProfile from './components/UserProfile.js'; import UserProfile from './components/UserProfile.js';
import FinancialProfileForm from './components/FinancialProfileForm.js'; import FinancialProfileForm from './components/FinancialProfileForm.js';
import CareerProfileList from './components/CareerProfileList.js';
import CareerProfileForm from './components/CareerProfileForm.js';
import CollegeProfileList from './components/CollegeProfileList.js';
import CollegeProfileForm from './components/CollegeProfileForm.js';
import CareerRoadmap from './components/CareerRoadmap.js'; import CareerRoadmap from './components/CareerRoadmap.js';
import Paywall from './components/Paywall.js'; import Paywall from './components/Paywall.js';
import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js'; import OnboardingContainer from './components/PremiumOnboarding/OnboardingContainer.js';
@ -239,7 +243,7 @@ const uiToolHandlers = useMemo(() => {
{/* Header */} {/* Header */}
<header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm relative"> <header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm relative">
<h1 className="text-lg font-semibold"> <h1 className="text-lg font-semibold">
AptivaAI - Career Guidance Platform (beta) AptivaAI - Career Guidance Platform
</h1> </h1>
{isAuthenticated && ( {isAuthenticated && (
@ -360,7 +364,7 @@ const uiToolHandlers = useMemo(() => {
)} )}
onClick={() => navigate('/retirement')} onClick={() => navigate('/retirement')}
> >
Retirement Planning Retirement Planning (beta)
{!canAccessPremium && ( {!canAccessPremium && (
<span className="text-xs ml-1 text-gray-600"> <span className="text-xs ml-1 text-gray-600">
(Premium) (Premium)
@ -406,20 +410,32 @@ const uiToolHandlers = useMemo(() => {
</Link> </Link>
{canAccessPremium ? ( {canAccessPremium ? (
/* Premium users go straight to the wizard */ /* Premium users go straight to the wizard */
<Link <Link
to="/premium-onboarding" to="/profile/careers"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700" className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
> >
Premium Onboarding Career Profiles
</Link> </Link>
) : ( ) : (
/* Free users are nudged to upgrade */ <span
className="block px-4 py-2 text-sm text-gray-400 cursor-not-allowed"
>
Career Profiles (Premium)
</span>
)}
{/* College Profiles (go straight to list) */}
{canAccessPremium ? (
<Link <Link
to="/paywall" to="/profile/college"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700" className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
> >
College Planning&nbsp;<span className="text-xs">(Premium)</span> College Profiles
</Link> </Link>
) : (
<span className="block px-4 py-2 text-sm text-gray-400 cursor-not-allowed">
College Profiles (Premium)
</span>
)} )}
</div> </div>
</div> </div>
@ -533,6 +549,11 @@ const uiToolHandlers = useMemo(() => {
</PremiumRoute> </PremiumRoute>
} }
/> />
<Route path="/profile/careers" element={<CareerProfileList />} />
<Route path="/profile/careers/:id/edit" element={<CareerProfileForm />} />
<Route path="/profile/college/" element={<CollegeProfileList />} />
<Route path="/profile/college/:careerId/:id?" element={<CollegeProfileForm />} />
<Route <Route
path="/financial-profile" path="/financial-profile"
element={ element={

View File

@ -0,0 +1,232 @@
// CareerProfileForm.js
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import authFetch from '../utils/authFetch.js';
import CareerSearch from './CareerSearch.js'; // ← same component as onboarding
export default function CareerProfileForm() {
const { id } = useParams(); // "new" or an existing uuid
const nav = useNavigate();
/* ---------- 1. local state ---------- */
const [form, setForm] = useState({
scenario_title : '',
career_name : '',
soc_code : '',
status : 'current',
start_date : '',
retirement_start_date : '',
college_enrollment_status : '',
career_goals : '',
desired_retirement_income_monthly : ''
});
const [careerLocked, setCareerLocked] = useState(id !== 'new'); // lock unless new
/* ---------- 2. helpers ---------- */
const handleChange = e =>
setForm(prev => ({ ...prev, [e.target.name]: e.target.value }));
const handleCareerSelected = obj => {
// obj = { title, soc_code, … }
setForm(prev => ({
...prev,
career_name : obj.title,
soc_code : obj.soc_code
}));
setCareerLocked(true);
};
const unlockCareer = () => {
// allow user to repick
setCareerLocked(false);
setForm(prev => ({ ...prev, career_name: '', soc_code: '' }));
};
/* ---------- 3. load an existing row (edit mode) ---------- */
useEffect(() => {
if (id === 'new') return;
(async () => {
const res = await authFetch(`/api/premium/career-profile/${id}`);
if (!res.ok) return;
const d = await res.json();
setForm(prev => ({
...prev,
scenario_title : d.scenario_title ?? '',
career_name : d.career_name ?? '',
soc_code : d.soc_code ?? '',
status : d.status ?? 'current',
start_date : d.start_date ?? '',
retirement_start_date : d.retirement_start_date ?? '',
college_enrollment_status : d.college_enrollment_status ?? '',
career_goals : d.career_goals ?? '',
desired_retirement_income_monthly :
d.desired_retirement_income_monthly ?? ''
}));
})();
}, [id]);
/* ---------- 4. save ---------- */
async function save() {
if (!form.soc_code) {
alert('Please pick a valid career from the list first.');
return;
}
try {
const res = await authFetch('/api/premium/career-profile', {
method : 'POST',
headers : { 'Content-Type': 'application/json' },
body : JSON.stringify({
...form,
id: id === 'new' ? undefined : id // upsert
})
});
if (!res.ok) throw new Error(await res.text());
nav(-1);
} catch (err) {
console.error(err);
alert(err.message);
}
}
/* ---------- 5. render ---------- */
return (
<div className="max-w-lg mx-auto space-y-4">
<h2 className="text-2xl font-semibold">
{id === 'new' ? 'New' : 'Edit'} Career Profile
</h2>
{/* Scenario title */}
<label className="block">
<span className="font-medium">Scenario Title</span>
<input
name="scenario_title"
className="mt-1 w-full border rounded p-2"
placeholder="e.g. DataScientist Plan"
value={form.scenario_title}
onChange={handleChange}
/>
</label>
{/* Career picker (locked vs editable) */}
<label className="block font-medium">Career *</label>
{careerLocked ? (
<div className="flex items-center space-x-2">
<input
className="flex-1 border rounded p-2 bg-gray-100"
value={form.career_name}
disabled
/>
<button
type="button"
className="text-blue-600 underline text-sm"
onClick={unlockCareer}
>
Change
</button>
</div>
) : (
<CareerSearch onCareerSelected={handleCareerSelected} required />
)}
{/* Status */}
<label className="block">
<span className="font-medium">Status</span>
<select
name="status"
className="mt-1 w-full border rounded p-2"
value={form.status}
onChange={handleChange}
>
<option value="current">current</option>
<option value="future">future</option>
<option value="retired">retired</option>
</select>
</label>
{/* Dates */}
<label className="block">
<span className="font-medium">Start Date</span>
<input
type="date"
name="start_date"
className="mt-1 w-full border rounded p-2"
value={form.start_date}
onChange={handleChange}
/>
</label>
<label className="block">
<span className="font-medium">Retirement Start Date</span>
<input
type="date"
name="retirement_start_date"
className="mt-1 w-full border rounded p-2"
value={form.retirement_start_date}
onChange={handleChange}
/>
</label>
{/* College status */}
<label className="block">
<span className="font-medium">College Enrollment Status</span>
<select
name="college_enrollment_status"
className="mt-1 w-full border rounded p-2"
value={form.college_enrollment_status}
onChange={handleChange}
>
<option value="">-- select --</option>
<option value="not_applicable">Not Applicable</option>
<option value="prospective_student">Prospective Student</option>
<option value="currently_enrolled">Currently Enrolled</option>
<option value="completed">Completed</option>
</select>
</label>
{/* Career goals */}
<label className="block">
<span className="font-medium">Career Goals</span>
<textarea
rows={3}
name="career_goals"
className="mt-1 w-full border rounded p-2"
placeholder="e.g. Become a senior datascientist in five years…"
value={form.career_goals}
onChange={handleChange}
/>
</label>
{/* Desired retirement income */}
<label className="block">
<span className="font-medium">Desired Retirement Income / Month ($)</span>
<input
type="number"
name="desired_retirement_income_monthly"
className="mt-1 w-full border rounded p-2"
placeholder="e.g. 6000"
value={form.desired_retirement_income_monthly}
onChange={handleChange}
/>
</label>
{/* Action buttons */}
<div className="pt-4 flex justify-between">
<button
type="button"
onClick={() => nav(-1)}
className="bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold py-2 px-4 rounded"
>
Back
</button>
<button
type="button"
onClick={save}
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded"
>
Save
</button>
</div>
</div>
);
}

View File

@ -0,0 +1,79 @@
import React, { useEffect, useState } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
export default function CareerProfileList() {
const [rows, setRows] = useState([]);
const nav = useNavigate();
const token = localStorage.getItem('token');
useEffect(() => {
fetch('/api/premium/career-profile/all', {
headers: { Authorization: `Bearer ${token}` }
})
.then(r => r.json())
.then(d => setRows(d.careerProfiles || []));
}, [token]);
async function remove(id) {
if (!window.confirm('Delete this career profile?')) return;
await fetch(`/api/premium/career-profile/${id}`, {
method : 'DELETE',
headers: { Authorization: `Bearer ${token}` }
});
setRows(rows.filter(r => r.id !== id));
}
return (
<div className="max-w-4xl mx-auto space-y-4">
<h2 className="text-2xl font-semibold">Career Profiles</h2>
<button
onClick={() => nav('/profile/careers/new/edit')}
className="px-3 py-2 bg-blue-600 text-white rounded"
>
+ New Career Profile
</button>
<table className="w-full border mt-4 text-sm">
<thead className="bg-gray-100">
<tr>
<th className="p-2 text-left">Title</th>
<th className="p-2 text-left">Status</th>
<th className="p-2">Start</th>
<th className="p-2"></th>
</tr>
</thead>
<tbody>
{rows.map(r => (
<tr key={r.id} className="border-t">
<td className="p-2">{r.scenario_title || r.career_name}</td>
<td className="p-2">{r.status}</td>
<td className="p-2">{r.start_date}</td>
<td className="p-2 space-x-2">
<Link
to={`/profile/careers/${r.id}/edit`}
className="underline text-blue-600"
>
edit
</Link>
<button
onClick={() => remove(r.id)}
className="text-red-600 underline"
>
delete
</button>
</td>
</tr>
))}
{rows.length === 0 && (
<tr>
<td colSpan={5} className="p-4 text-center text-gray-500">
No career profiles yet
</td>
</tr>
)}
</tbody>
</table>
</div>
);
}

View File

@ -0,0 +1,522 @@
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import authFetch from '../utils/authFetch.js';
/** -----------------------------------------------------------
* Ensure numerics are sent as numbers and booleans as 0 / 1
* mirrors the logic you use in OnboardingContainer
* ---------------------------------------------------------- */
const parseFloatOrNull = v => {
if (v === '' || v == null) return null;
const n = parseFloat(v);
return Number.isFinite(n) ? n : null;
};
function normalisePayload(draft) {
const bools = [
'is_in_state','is_in_district','is_online',
'loan_deferral_until_graduation'
];
const nums = [
'annual_financial_aid','existing_college_debt','interest_rate','loan_term',
'extra_payment','expected_salary','credit_hours_per_year','hours_completed',
'credit_hours_required','program_length','tuition','tuition_paid'
];
const dates = ['enrollment_date', 'expected_graduation'];
const out = { ...draft };
bools.forEach(k => { out[k] = draft[k] ? 1 : 0; });
nums .forEach(k => { out[k] = parseFloatOrNull(draft[k]) ?? 0; });
dates.forEach(k => { out[k] = toMySqlDate(draft[k]); });
delete out.created_at;
delete out.updated_at;
return out;
}
const toMySqlDate = iso => {
if (!iso) return null;
return iso.replace('T', ' ').slice(0, 19);
};
export default function CollegeProfileForm() {
const { careerId, id } = useParams(); // id optional
const nav = useNavigate();
const token = localStorage.getItem('token');
const [cipRows, setCipRows] = useState([]);
const [schoolSug, setSchoolSug] = useState([]);
const [progSug, setProgSug] = useState([]);
const [types, setTypes] = useState([]);
const [ipeds, setIpeds] = useState([]);
const [schoolValid, setSchoolValid] = useState(true);
const [programValid, setProgramValid] = useState(true);
const schoolData = cipRows;
const [form, setForm] = useState({
career_profile_id : careerId,
selected_school : '',
selected_program : '',
program_type : '',
annual_financial_aid : 0,
tuition : 0,
interest_rate : 5.5,
loan_term : 10
});
const [manualTuition, setManualTuition] = useState(
form.tuition ? String(form.tuition) : ''
);
const [autoTuition, setAutoTuition] = useState(0);
// ---------- handlers (inside component) ----------
const handleFieldChange = (e) => {
const { name, value, type, checked } = e.target;
setForm((prev) => {
const draft = { ...prev };
if (type === 'checkbox') {
draft[name] = checked;
} else if (
[
'interest_rate','loan_term','extra_payment','expected_salary',
'annual_financial_aid','existing_college_debt','credit_hours_per_year',
'hours_completed','credit_hours_required','tuition','tuition_paid',
'program_length'
].includes(name)
) {
draft[name] = value === '' ? '' : parseFloat(value);
} else {
draft[name] = value;
}
return draft;
});
};
const onSchoolInput = (e) => {
handleFieldChange(e);
const v = e.target.value.toLowerCase();
const suggestions = cipRows
.filter((r) => r.INSTNM.toLowerCase().includes(v))
.map((r) => r.INSTNM);
setSchoolSug([...new Set(suggestions)].slice(0, 10));
};
const onProgramInput = (e) => {
handleFieldChange(e);
if (!form.selected_school) return;
const v = e.target.value.toLowerCase();
const sug = cipRows
.filter(
(r) =>
r.INSTNM.toLowerCase() === form.selected_school.toLowerCase() &&
r.CIPDESC.toLowerCase().includes(v)
)
.map((r) => r.CIPDESC);
setProgSug([...new Set(sug)].slice(0, 10));
};
useEffect(() => {
if (id && id !== 'new') {
fetch(`/api/premium/college-profile?careerProfileId=${careerId}`, {
headers: { Authorization: `Bearer ${token}` }
})
.then(r => r.json())
.then(setForm);
}
}, [careerId, id, token]);
async function handleSave(){
try{
const body = normalisePayload({ ...form, tuition: chosenTuition, career_profile_id: careerId });
const res = await authFetch('/api/premium/college-profile',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify(body)
});
if(!res.ok) throw new Error(await res.text());
alert('Saved!');
setForm(p => ({ ...p, tuition: chosenTuition }));
setManualTuition(String(chosenTuition));
nav(-1);
}catch(err){ console.error(err); alert(err.message);}
}
/* LOAD iPEDS ----------------------------- */
useEffect(() => {
fetch('/ic2023_ay.csv')
.then(r => r.text())
.then(text => {
const rows = text.split('\n').map(l => l.split(','));
const headers = rows[0];
const parsed = rows.slice(1).map(r =>
Object.fromEntries(r.map((v,i)=>[headers[i], v]))
);
setIpeds(parsed); // you already declared setIpeds
})
.catch(err => console.error('iPEDS load failed', err));
}, []);
useEffect(() => { fetch('/cip_institution_mapping_new.json')
.then(r=>r.text()).then(t => setCipRows(
t.split('\n').map(l=>{try{return JSON.parse(l)}catch{ return null }})
.filter(Boolean)
));
fetch('/ic2023_ay.csv')
.then(r=>r.text()).then(csv=>{/* identical to CollegeOnboarding */});
},[]);
useEffect(()=>{
if(!form.selected_school || !form.selected_program) { setTypes([]); return; }
const t = cipRows.filter(r =>
r.INSTNM.toLowerCase()===form.selected_school.toLowerCase() &&
r.CIPDESC===form.selected_program)
.map(r=>r.CREDDESC);
setTypes([...new Set(t)]);
},[form.selected_school, form.selected_program, cipRows]);
useEffect(() => {
if (!ipeds.length) return;
if (!form.selected_school ||
!form.program_type ||
!form.credit_hours_per_year) return;
/* 1 ─ locate UNITID */
const sch = cipRows.find(
r => r.INSTNM.toLowerCase() === form.selected_school.toLowerCase()
);
if (!sch) return;
const unitId = sch.UNITID;
const row = ipeds.find(r => r.UNITID === unitId);
if (!row) return;
/* 2 ─ decide instate / district buckets */
const grad = [
"Master's Degree","Doctoral Degree",
"Graduate/Professional Certificate","First Professional Degree"
].includes(form.program_type);
const pick = (codeInDist, codeInState, codeOut) => {
if (form.is_in_district) return row[codeInDist];
else if (form.is_in_state) return row[codeInState];
else return row[codeOut];
};
const partTime = grad
? pick('HRCHG5','HRCHG6','HRCHG7')
: pick('HRCHG1','HRCHG2','HRCHG3');
const fullTime = grad
? pick('TUITION5','TUITION6','TUITION7')
: pick('TUITION1','TUITION2','TUITION3');
const chpy = parseFloat(form.credit_hours_per_year) || 0;
const est = chpy && chpy < 24
? parseFloat(partTime || 0) * chpy
: parseFloat(fullTime || 0);
setAutoTuition(Math.round(est));
}, [
ipeds,
cipRows,
form.selected_school,
form.program_type,
form.credit_hours_per_year,
form.is_in_state,
form.is_in_district
]);
const handleManualTuitionChange = e => setManualTuition(e.target.value);
const chosenTuition = manualTuition.trim() === ''
? autoTuition
: parseFloat(manualTuition);
return (
<div className="max-w-md mx-auto p-6 space-y-4">
<h2 className="text-2xl font-semibold">
{id === 'new' ? 'New' : 'Edit'} College Plan
</h2>
{(form.college_enrollment_status === 'currently_enrolled' ||
form.college_enrollment_status === 'prospective_student') ? (
/* ───────────────────────────────────────────────────────── */
<div className="space-y-4">
{/* 1 │ Location / modality checkboxes */}
{[
{ n:'is_in_district', l:'In District?' },
{ n:'is_in_state', l:'InState Tuition?' },
{ n:'is_online', l:'Program is Fully Online' },
{ n:'loan_deferral_until_graduation',
l:'Defer Loan Payments untilGraduation?' }
].map(({n,l}) => (
<div key={n} className="flex items-center space-x-2">
<input type="checkbox" name={n}
className="h-4 w-4"
checked={!!form[n]}
onChange={handleFieldChange}/>
<label className="font-medium">{l}</label>
</div>
))}
{/* 2 │ School picker */}
<div className="space-y-1">
<label className="block font-medium">
School Name * (choose from list)
</label>
<input
name="selected_school"
value={form.selected_school}
onChange={onSchoolInput}
onBlur={() => {
const ok = cipRows.some(
r => r.INSTNM.toLowerCase() === form.selected_school.toLowerCase()
);
setSchoolValid(ok);
if (!ok) alert('Please pick a school from the list.');
}}
list="school-suggestions"
placeholder="Start typing and choose…"
className={`w-full border rounded p-2 ${schoolValid ? '' : 'border-red-500'}`}
required
/>
<datalist id="school-suggestions">
{schoolSug.map((s,i)=>(
<option key={i} value={s} />
))}
</datalist>
</div>
{/* 3 │ Program picker */}
<div className="space-y-1">
<label className="block font-medium">
Major / Program * (choose from list)
</label>
<input
name="selected_program"
value={form.selected_program}
onChange={onProgramInput}
onBlur={() => {
const ok =
form.selected_school && // need a school first
cipRows.some(
r =>
r.INSTNM.toLowerCase() === form.selected_school.toLowerCase() &&
r.CIPDESC.toLowerCase() === form.selected_program.toLowerCase()
);
setProgramValid(ok);
if (!ok) alert('Please pick a program from the list.');
}}
list="program-suggestions"
placeholder="Start typing and choose…"
className={`w-full border rounded p-2 ${programValid ? '' : 'border-red-500'}`}
required
/>
<datalist id="program-suggestions">
{progSug.map((p,i)=>(
<option key={i} value={p} />
))}
</datalist>
</div>
{/* 4 │ Programtype */}
<div className="space-y-1">
<label className="block font-medium">Degree Type *</label>
<select
name="program_type"
value={form.program_type}
onChange={handleFieldChange}
className="w-full border rounded p-2"
required
>
<option value="">Select Program Type</option>
{types.map((t,i)=><option key={i} value={t}>{t}</option>)}
</select>
</div>
{/* 5 │ Academic calendar */}
<div className="space-y-1">
<label className="block font-medium">Academic Calendar</label>
<select
name="academic_calendar"
value={form.academic_calendar || 'semester'}
onChange={handleFieldChange}
className="w-full border rounded p-2"
>
<option value="semester">Semester</option>
<option value="quarter">Quarter</option>
<option value="trimester">Trimester</option>
<option value="other">Other</option>
</select>
</div>
{/* 6 │ Credithour fields (conditionally rendered) */}
{(form.program_type === 'Graduate/Professional Certificate' ||
form.program_type === 'First Professional Degree' ||
form.program_type === 'Doctoral Degree' ||
form.program_type === 'Undergraduate Certificate or Diploma') && (
<div className="space-y-1">
<label className="block font-medium">Credit Hours Required</label>
<input
type="number"
name="credit_hours_required"
value={form.credit_hours_required}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
)}
<div className="space-y-1">
<label className="block font-medium">Credit Hours Per Year</label>
<input
type="number"
name="credit_hours_per_year"
value={form.credit_hours_per_year}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
{/* 7 │ Tuition & aid */}
<div className="space-y-1">
<label className="block font-medium">Yearly Tuition</label>
<input
type="number"
value={
manualTuition.trim() === '' ? autoTuition : manualTuition
}
onChange={handleManualTuitionChange}
placeholder="Blank = autocalculated"
className="w-full border rounded p-2"
/>
</div>
<span className="font-medium">Annual Aid</span>
<input
type="number"
name="annual_financial_aid"
value={form.annual_financial_aid}
onChange={handleFieldChange}
className="mt-1 w-full border rounded p-2"
/>
{/* 8 │ Existing debt */}
<div className="space-y-1">
<label className="block font-medium">Existing College Debt</label>
<input
type="number"
name="existing_college_debt"
value={form.existing_college_debt}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
{/* 9 │ Programlength & hourscompleted */}
{(form.college_enrollment_status === 'currently_enrolled' ||
form.college_enrollment_status === 'prospective_student') && (
<>
<div className="space-y-1">
<label className="block font-medium">Program Length (years)</label>
<input
type="number"
name="program_length"
value={form.program_length}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
{form.college_enrollment_status === 'currently_enrolled' && (
<div className="space-y-1">
<label className="block font-medium">Hours Completed</label>
<input
type="number"
name="hours_completed"
value={form.hours_completed}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
)}
</>
)}
{/* 10 │ Interest, term, extra payment, salary */}
<div className="flex space-x-4">
<label className="block flex-1">
<span className="font-medium">Interest %</span>
<input
type="number"
name="interest_rate"
value={form.interest_rate}
onChange={handleFieldChange}
className="mt-1 w-full border rounded p-2"
/>
</label>
<label className="block flex-1">
<span className="font-medium">Loan Term (years)</span>
<input
type="number"
name="loan_term"
value={form.loan_term}
onChange={handleFieldChange}
className="mt-1 w-full border rounded p-2"
/>
</label>
</div>
<div className="space-y-1">
<label className="block font-medium">Extra Monthly Payment</label>
<input
type="number"
name="extra_payment"
value={form.extra_payment}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
<div className="space-y-1">
<label className="block font-medium">Expected Salary After Graduation</label>
<input
type="number"
name="expected_salary"
value={form.expected_salary}
onChange={handleFieldChange}
className="w-full border rounded p-2"
/>
</div>
</div>
) : (
<p>
User is neither currently enrolled nor a prospective student nothing to
edit.
</p>
)}
{/* 11 │ Action buttons */}
<div className="pt-4">
<button
onClick={() => nav(-1)}
className="bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold py-2 px-4 rounded mr-3"
>
 Back
</button>
<button
onClick={handleSave}
disabled={!schoolValid || !programValid}
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded"
>
Save
</button>
</div>
</div>
);
}

View File

@ -0,0 +1,57 @@
import React, { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
export default function CollegeProfileList() {
const [rows, setRows] = useState([]);
const nav = useNavigate();
const token = localStorage.getItem('token');
useEffect(() => {
fetch('/api/premium/college-profile/all', {
headers: { Authorization: `Bearer ${token}` }
})
.then(r => r.json())
.then(d => setRows(d.collegeProfiles || []));
}, [token]);
return (
<div className="max-w-5xl mx-auto space-y-4">
<h2 className="text-2xl font-semibold">College Profiles</h2>
<table className="w-full border text-sm">
<thead className="bg-gray-100">
<tr>
<th className="p-2 text-left">Career</th>
<th className="p-2 text-left">School</th>
<th className="p-2 text-left">Program</th>
<th className="p-2">Created</th>
<th className="p-2"></th>
</tr>
</thead>
<tbody>
{rows.map(r => (
<tr key={r.id} className="border-t">
<td className="p-2">{r.career_title}</td>
<td className="p-2">{r.selected_school}</td>
<td className="p-2">{r.selected_program}</td>
<td className="p-2">{r.created_at?.slice(0,10)}</td>
<td className="p-2">
<Link
to={`/profile/college/${r.career_profile_id}/${r.id}`}
className="underline text-blue-600"
>
edit
</Link>
</td>
</tr>
))}
{rows.length === 0 && (
<tr><td colSpan={5} className="p-4 text-center text-gray-500">
No college profiles yet
</td></tr>
)}
</tbody>
</table>
</div>
);
}

View File

@ -1,181 +1,219 @@
// FinancialProfileForm.js // FinancialProfileForm.js
import React, { useState, useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import authFetch from '../utils/authFetch.js'; import { useNavigate } from 'react-router-dom';
function FinancialProfileForm() { import authFetch from '../utils/authFetch.js';
// We'll store the fields in local state import Modal from './ui/modal.js';
const [currentSalary, setCurrentSalary] = useState(''); import ExpensesWizard from './ExpensesWizard.js'; // same wizard you use in onboarding
const [additionalIncome, setAdditionalIncome] = useState(''); import { Button } from './ui/button.js'; // Tailwindbased button (optional)
const [monthlyExpenses, setMonthlyExpenses] = useState('');
const [monthlyDebtPayments, setMonthlyDebtPayments] = useState('');
const [retirementSavings, setRetirementSavings] = useState('');
const [emergencyFund, setEmergencyFund] = useState('');
const [retirementContribution, setRetirementContribution] = useState('');
const [monthlyEmergencyContribution, setMonthlyEmergencyContribution] = useState('');
const [extraCashEmergencyPct, setExtraCashEmergencyPct] = useState('');
const [extraCashRetirementPct, setExtraCashRetirementPct] = useState('');
/* helper clamp 0100 */
const pct = v => Math.min(Math.max(parseFloat(v) || 0, 0), 100);
export default function FinancialProfileForm() {
const nav = useNavigate();
/* ─────────────── local state ─────────────── */
const [currentSalary, setCurrentSalary] = useState('');
const [additionalIncome, setAdditionalIncome] = useState('');
const [monthlyExpenses, setMonthlyExpenses] = useState('');
const [monthlyDebtPayments, setMonthlyDebtPayments] = useState('');
const [retirementSavings, setRetirementSavings] = useState('');
const [emergencyFund, setEmergencyFund] = useState('');
const [retirementContribution, setRetirementContribution] = useState('');
const [emergencyContribution, setEmergencyContribution] = useState('');
const [extraCashEmergencyPct, setExtraCashEmergencyPct] = useState('50');
const [extraCashRetirementPct, setExtraCashRetirementPct] = useState('50');
/* wizard modal */
const [showExpensesWizard, setShowExpensesWizard] = useState(false);
const openWizard = () => setShowExpensesWizard(true);
const closeWizard = () => setShowExpensesWizard(false);
/* ───────────── preload existing row ───────── */
useEffect(() => { useEffect(() => {
// On mount, fetch the user's existing profile from the new financial_profiles table (async () => {
async function fetchProfile() {
try { try {
const res = await authFetch('/api/premium/financial-profile', { const res = await authFetch('/api/premium/financial-profile');
method: 'GET' if (!res.ok) return;
}); const d = await res.json();
if (res.ok) {
const data = await res.json(); setCurrentSalary (d.current_salary ?? '');
// data might be an empty object if no row yet setAdditionalIncome (d.additional_income ?? '');
setCurrentSalary(data.current_salary || ''); setMonthlyExpenses (d.monthly_expenses ?? '');
setAdditionalIncome(data.additional_income || ''); setMonthlyDebtPayments (d.monthly_debt_payments ?? '');
setMonthlyExpenses(data.monthly_expenses || ''); setRetirementSavings (d.retirement_savings ?? '');
setMonthlyDebtPayments(data.monthly_debt_payments || ''); setEmergencyFund (d.emergency_fund ?? '');
setRetirementSavings(data.retirement_savings || ''); setRetirementContribution (d.retirement_contribution ?? '');
setEmergencyFund(data.emergency_fund || ''); setEmergencyContribution (d.emergency_contribution ?? '');
setRetirementContribution(data.retirement_contribution || ''); setExtraCashEmergencyPct (d.extra_cash_emergency_pct ?? '');
setMonthlyEmergencyContribution(data.monthly_emergency_contribution || ''); setExtraCashRetirementPct (d.extra_cash_retirement_pct ?? '');
setExtraCashEmergencyPct(data.extra_cash_emergency_pct || ''); } catch (err) { console.error(err); }
setExtraCashRetirementPct(data.extra_cash_retirement_pct || ''); })();
}
} catch (err) {
console.error("Failed to load financial profile:", err);
}
}
fetchProfile();
}, []); }, []);
// Submit form updates => POST to the same endpoint /* -----------------------------------------------------------
async function handleSubmit(e) { * keep the two % inputs complementary (must add to 100)
e.preventDefault(); * --------------------------------------------------------- */
try { function handleChange(e) {
const body = { const { name, value } = e.target;
current_salary: parseFloat(currentSalary) || 0, const pct = Math.max(0, Math.min(100, Number(value) || 0)); // clamp 0100
additional_income: parseFloat(additionalIncome) || 0,
monthly_expenses: parseFloat(monthlyExpenses) || 0,
monthly_debt_payments: parseFloat(monthlyDebtPayments) || 0,
retirement_savings: parseFloat(retirementSavings) || 0,
emergency_fund: parseFloat(emergencyFund) || 0,
retirement_contribution: parseFloat(retirementContribution) || 0,
monthly_emergency_contribution: parseFloat(monthlyEmergencyContribution) || 0,
extra_cash_emergency_pct: parseFloat(extraCashEmergencyPct) || 0,
extra_cash_retirement_pct: parseFloat(extraCashRetirementPct) || 0
};
const res = await authFetch('/api/premium/financial-profile', { if (name === 'extraCashEmergencyPct') {
method: 'POST', setExtraCashEmergencyPct(String(pct));
headers: { 'Content-Type': 'application/json' }, setExtraCashRetirementPct(String(100 - pct));
body: JSON.stringify(body) } else if (name === 'extraCashRetirementPct') {
}); setExtraCashRetirementPct(String(pct));
setExtraCashEmergencyPct(String(100 - pct));
if (res.ok) { } else {
// show success or redirect // all other numeric fields:
console.log("Profile updated"); // allow empty string so users can clear then retype
} else { const update = valSetter => valSetter(value === '' ? '' : Number(value));
console.error("Failed to update profile:", await res.text()); switch (name) {
case 'currentSalary': update(setCurrentSalary); break;
case 'additionalIncome': update(setAdditionalIncome); break;
case 'monthlyExpenses': update(setMonthlyExpenses); break;
case 'monthlyDebtPayments': update(setMonthlyDebtPayments); break;
case 'retirementSavings': update(setRetirementSavings); break;
case 'emergencyFund': update(setEmergencyFund); break;
case 'retirementContribution': update(setRetirementContribution); break;
case 'emergencyContribution': update(setEmergencyContribution); break;
default: break;
} }
} catch (err) {
console.error("Error submitting financial profile:", err);
} }
} }
/* ───────────── submit ─────────────────────── */
async function handleSubmit(e) {
e.preventDefault();
const body = {
current_salary: parseFloat(currentSalary) || 0,
additional_income: parseFloat(additionalIncome) || 0,
monthly_expenses: parseFloat(monthlyExpenses) || 0,
monthly_debt_payments: parseFloat(monthlyDebtPayments) || 0,
retirement_savings: parseFloat(retirementSavings) || 0,
emergency_fund: parseFloat(emergencyFund) || 0,
retirement_contribution: parseFloat(retirementContribution) || 0,
emergency_contribution: parseFloat(emergencyContribution) || 0,
extra_cash_emergency_pct: pct(extraCashEmergencyPct),
extra_cash_retirement_pct: pct(extraCashRetirementPct)
};
try {
const res = await authFetch('/api/premium/financial-profile', {
method : 'POST',
headers: { 'Content-Type':'application/json' },
body : JSON.stringify(body)
});
if (!res.ok) throw new Error(await res.text());
alert('Financial profile saved.');
nav(-1);
} catch (err) {
console.error(err);
alert('Failed to save financial profile.');
}
}
/* ───────────── view ───────────────────────── */
return ( return (
<form onSubmit={handleSubmit} className="max-w-2xl mx-auto p-4 space-y-4 bg-white shadow rounded"> <>
<h2 className="text-xl font-semibold">Edit Your Financial Profile</h2> <form
onSubmit={handleSubmit}
className="max-w-2xl mx-auto p-6 space-y-4 bg-white shadow rounded"
>
<h2 className="text-xl font-semibold">EditYourFinancialProfile</h2>
<label className="block font-medium">Current Salary</label> {/* salary / income */}
<input <label className="block font-medium">CurrentAnnualSalary</label>
type="number" <input type="number" className="w-full border rounded p-2"
value={currentSalary} name="currentSalary" value={currentSalary} onChange={handleChange} />
onChange={(e) => setCurrentSalary(e.target.value)}
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Additional Monthly Income</label> <label className="block font-medium">AdditionalAnnualIncome</label>
<input <input type="number" className="w-full border rounded p-2"
type="number" name="additionalIncome" value={additionalIncome} onChange={handleChange} />
value={additionalIncome}
onChange={(e) => setAdditionalIncome(e.target.value)}
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Monthly Living Expenses</label> {/* expenses with wizard */}
<input <label className="block font-medium">MonthlyLivingExpenses</label>
type="number" <div className="flex space-x-2 items-center">
value={monthlyExpenses} <input type="number" className="w-full border rounded p-2"
onChange={(e) => setMonthlyExpenses(e.target.value)} value={monthlyExpenses}
className="w-full border rounded p-2" onChange={e=>setMonthlyExpenses(e.target.value)} />
placeholder="$" <Button className="bg-blue-600 text-white px-3 py-2 rounded"
/> type="button" onClick={openWizard}>
Need Help?
</Button>
</div>
<label className="block font-medium">Monthly Debt Payments</label> {/* rest of the numeric fields */}
<input <label className="block font-medium">MonthlyDebtPayments</label>
type="number" <input type="number" className="w-full border rounded p-2"
value={monthlyDebtPayments} name="monthlyDebtPayments" value={monthlyDebtPayments} onChange={handleChange} />
onChange={(e) => setMonthlyDebtPayments(e.target.value)}
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Retirement Savings</label> <label className="block font-medium">RetirementSavings</label>
<input <input type="number" className="w-full border rounded p-2"
type="number" name="retirementSavings" value={retirementSavings} onChange={handleChange} />
value={retirementSavings}
onChange={(e) => setRetirementSavings(e.target.value)}
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Emergency Fund</label> <label className="block font-medium">EmergencyFund</label>
<input <input type="number" className="w-full border rounded p-2"
type="number" name="emergencyFund" value={emergencyFund} onChange={handleChange} />
value={emergencyFund}
onChange={(e) => setEmergencyFund(e.target.value)}
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Monthly Retirement Contribution</label> <label className="block font-medium">MonthlyRetirementContribution</label>
<input <input type="number" className="w-full border rounded p-2"
type="number" name="retirementContribution" value={retirementContribution} onChange={handleChange} />
value={retirementContribution}
onChange={(e) => setRetirementContribution(e.target.value)}
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Monthly Emergency Contribution</label> <label className="block font-medium">MonthlyEmergencyContribution</label>
<input <input type="number" className="w-full border rounded p-2"
type="number" name="emergencyContribution"
value={monthlyEmergencyContribution} value={emergencyContribution}
onChange={(e) => setMonthlyEmergencyContribution(e.target.value)} onChange={handleChange} />
className="w-full border rounded p-2"
placeholder="$"
/>
<label className="block font-medium">Extra Cash to Emergency (%)</label> {/* allocation kept in sync */}
<input <h3 className="text-lg font-medium pt-2">ExtraMonthlyCashAllocation (must total100%)</h3>
type="number"
value={extraCashEmergencyPct}
onChange={(e) => setExtraCashEmergencyPct(e.target.value)}
className="w-full border rounded p-2"
placeholder="e.g. 30"
/>
<label className="block font-medium">Extra Cash to Retirement (%)</label> <label className="block font-medium">ToEmergencyFund(%)</label>
<input <input type="number" className="w-full border rounded p-2"
type="number" name="extraCashEmergencyPct"
value={extraCashRetirementPct} value={extraCashEmergencyPct}
onChange={(e) => setExtraCashRetirementPct(e.target.value)} onChange={handleChange} />
className="w-full border rounded p-2"
placeholder="e.g. 70"
/>
<button type="submit" className="bg-blue-600 text-white px-4 py-2 rounded"> <label className="block font-medium">ToRetirement(%)</label>
Save and Continue <input type="number" className="w-full border rounded p-2"
</button> name="extraCashRetirementPct"
</form> value={extraCashRetirementPct}
onChange={handleChange} />
{/* action buttons */}
<div className="pt-4 flex justify-between">
<button
type="button"
onClick={()=>nav(-1)}
className="bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold py-2 px-4 rounded"
>
Back
</button>
<button
type="submit"
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded"
>
Save
</button>
</div>
</form>
{/* wizard modal */}
{showExpensesWizard && (
<Modal onClose={closeWizard}>
<ExpensesWizard
onClose={closeWizard}
onExpensesCalculated={total => {
setMonthlyExpenses(total);
closeWizard();
}}
/>
</Modal>
)}
</>
); );
} }
export default FinancialProfileForm;

View File

@ -33,14 +33,6 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData, finishNow }) => {
const skipFin = !!data.skipFinancialStep; const skipFin = !!data.skipFinancialStep;
// 1) Grab the location state values, if any // 1) Grab the location state values, if any
const {
socCode,
cipCodes,
careerTitle, // <--- we passed this from handleSelectForEducation
userZip,
userState,
} = location.state || {};
/* ── 3. sideeffects when route brings a new career object ── */ /* ── 3. sideeffects when route brings a new career object ── */
useEffect(() => { useEffect(() => {
if (!navCareerObj?.title) return; if (!navCareerObj?.title) return;
@ -166,17 +158,6 @@ function handleSubmit() {
/> />
</div> </div>
<div className="space-y-2">
<label className="block font-medium">Projected End Date (optional):</label>
<input
name="projected_end_date"
type="date"
onChange={handleChange}
value={data.projected_end_date || ''}
className="w-full border rounded p-2"
/>
</div>
<div className="space-y-2"> <div className="space-y-2">
<label className="block font-medium"> <label className="block font-medium">
Are you currently enrolled in college or planning to enroll? <Req /> Are you currently enrolled in college or planning to enroll? <Req />

View File

@ -48,7 +48,6 @@ function ReviewPage({
<div><strong>College enrollment Status:</strong> {careerData.college_enrollment_status || 'N/A'}</div> <div><strong>College enrollment Status:</strong> {careerData.college_enrollment_status || 'N/A'}</div>
<div><strong>Status:</strong> {careerData.status || 'N/A'}</div> <div><strong>Status:</strong> {careerData.status || 'N/A'}</div>
<div><strong>Start Date:</strong> {careerData.start_date || 'N/A'}</div> <div><strong>Start Date:</strong> {careerData.start_date || 'N/A'}</div>
<div><strong>Projected End Date:</strong> {careerData.projected_end_date || 'N/A'}</div>
<div><strong>Career Goals:</strong> {careerData.career_goals || 'N/A'}</div> <div><strong>Career Goals:</strong> {careerData.career_goals || 'N/A'}</div>
</div> </div>

View File

@ -15,8 +15,7 @@ function RetirementLanding() {
Plan strategically and financially for retirement. AptivaAI provides you with clear financial projections, milestone tracking, and scenario analysis for a secure future. Plan strategically and financially for retirement. AptivaAI provides you with clear financial projections, milestone tracking, and scenario analysis for a secure future.
</p> </p>
<div className="grid grid-cols-1 gap-4"> <div className="grid grid-cols-1 gap-4">
<Button onClick={() => navigate('/financial-profile')}>Update Financial Profile</Button> <Button onClick={() => navigate('/retirement-planner')}>Compare different retirement scenarios and get AI help with planning</Button>
<Button onClick={() => navigate('/retirement-planner')}>Set Retirement Milestones and get AI help with planning</Button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -159,7 +159,6 @@ export default function ScenarioEditModal({
career_name : safe(s.career_name), career_name : safe(s.career_name),
status : safe(s.status || 'planned'), status : safe(s.status || 'planned'),
start_date : safe(s.start_date), start_date : safe(s.start_date),
projected_end_date : safe(s.projected_end_date),
retirement_start_date: safe(s.retirement_start_date), retirement_start_date: safe(s.retirement_start_date),
desired_retirement_income_monthly : safe( desired_retirement_income_monthly : safe(
s.desired_retirement_income_monthly s.desired_retirement_income_monthly
@ -498,7 +497,6 @@ async function handleSave() {
currently_working : formData.currently_working || "no", currently_working : formData.currently_working || "no",
status : s(formData.status), status : s(formData.status),
start_date : s(formData.start_date), start_date : s(formData.start_date),
projected_end_date : s(formData.projected_end_date),
retirement_start_date : s(formData.retirement_start_date), retirement_start_date : s(formData.retirement_start_date),
desired_retirement_income_monthly : n(formData.desired_retirement_income_monthly), desired_retirement_income_monthly : n(formData.desired_retirement_income_monthly),
@ -687,18 +685,6 @@ if (formData.retirement_start_date) {
className="border border-gray-300 rounded p-2 w-full" className="border border-gray-300 rounded p-2 w-full"
/> />
</div> </div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Projected End Date
</label>
<input
type="date"
name="projected_end_date"
value={formData.projected_end_date || ''}
onChange={handleFormChange}
className="border border-gray-300 rounded p-2 w-full"
/>
</div>
</div> </div>
{/* Retirement date */} {/* Retirement date */}

View File

@ -51,7 +51,6 @@ export default function ScenarioEditWizard({
currently_working: scenData.currently_working, currently_working: scenData.currently_working,
status: scenData.status, status: scenData.status,
start_date: scenData.start_date, start_date: scenData.start_date,
projected_end_date: scenData.projected_end_date,
planned_monthly_expenses: scenData.planned_monthly_expenses, planned_monthly_expenses: scenData.planned_monthly_expenses,
planned_monthly_debt_payments: scenData.planned_monthly_debt_payments, planned_monthly_debt_payments: scenData.planned_monthly_debt_payments,
planned_monthly_retirement_contribution: scenData.planned_monthly_retirement_contribution, planned_monthly_retirement_contribution: scenData.planned_monthly_retirement_contribution,