Fixed Profile links, MultiScenarioView fixes.

This commit is contained in:
Josh 2025-05-30 14:35:29 +00:00
parent 569626d489
commit edeef42f5a
9 changed files with 761 additions and 381 deletions

View File

@ -359,7 +359,7 @@ app.post('/api/premium/ai/next-steps', authenticatePremiumUser, async (req, res)
const threeYearsFromNow = new Date(now); const threeYearsFromNow = new Date(now);
threeYearsFromNow.setFullYear(threeYearsFromNow.getFullYear() + 3); threeYearsFromNow.setFullYear(threeYearsFromNow.getFullYear() + 3);
const isoThreeYearsFromNow = threeYearsFromNow.toISOString().slice(0, 10); const isoThreeYearsFromNow = threeYearsFromNow.toISOString().slice(0, 10).slice(0, 10);
// 4) Construct ChatGPT messages // 4) Construct ChatGPT messages
const messages = [ const messages = [
@ -2146,7 +2146,7 @@ app.post(
SET resume_optimizations_used = 0, SET resume_optimizations_used = 0,
resume_limit_reset = ? resume_limit_reset = ?
WHERE id = ? WHERE id = ?
`, [resetDate.toISOString(), id]); `, [resetDate.toISOString().slice(0, 10), id]);
userProfile.resume_optimizations_used = 0; userProfile.resume_optimizations_used = 0;
} }
@ -2202,7 +2202,7 @@ app.post(
res.json({ res.json({
optimizedResume, optimizedResume,
remainingOptimizations, remainingOptimizations,
resetDate: resetDate.toISOString() resetDate: resetDate.toISOString().slice(0, 10)
}); });
} catch (err) { } catch (err) {
console.error('Error optimizing resume:', err); console.error('Error optimizing resume:', err);
@ -2246,7 +2246,7 @@ app.get('/api/premium/resume/remaining', authenticatePremiumUser, async (req, re
SET resume_optimizations_used = 0, SET resume_optimizations_used = 0,
resume_limit_reset = ? resume_limit_reset = ?
WHERE id = ? WHERE id = ?
`, [resetDate.toISOString(), id]); `, [resetDate.toISOString().slice(0, 10), id]);
userProfile.resume_optimizations_used = 0; userProfile.resume_optimizations_used = 0;
} }

View File

@ -243,28 +243,45 @@ function App() {
</div> </div>
{/* 5) Profile */} {/* 5) Profile */}
<div className="relative group"> <div className="relative group">
<Button <Button
style={{ color: '#1f2937' }} style={{ color: '#1f2937' }}
className={` className={`
bg-white bg-white
border border-gray-300 border border-gray-300
hover:bg-gray-100 hover:bg-gray-100
hover:text-blue-700 hover:text-blue-700
whitespace-nowrap whitespace-nowrap
text-xs sm:text-sm md:text-base text-xs sm:text-sm md:text-base
font-semibold font-semibold
min-w-0 min-w-0
max-w-[90px] max-w-[90px]
truncate truncate
`} `}
> >
Profile Profile
</Button> </Button>
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-48 z-50">
{/* Account Profile, Financial Profile links */} {/* DROPDOWN MENU FOR PROFILE */}
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-48 z-50">
{/* Account (Links to UserProfile.js) */}
<Link
to="/profile"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
>
Account
</Link>
{/* Financial Profile (Links to FinancialProfileForm.js) */}
<Link
to="/financial-profile"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
>
Financial Profile
</Link>
</div>
</div> </div>
</div>
</nav> </nav>
{/* LOGOUT + UPGRADE BUTTONS */} {/* LOGOUT + UPGRADE BUTTONS */}

View File

@ -117,8 +117,8 @@ const MilestoneAddModal = ({
end_month: impact.end_month !== null end_month: impact.end_month !== null
? parseInt(impact.end_month, 10) ? parseInt(impact.end_month, 10)
: null, : null,
created_at: new Date().toISOString(), created_at: new Date()..toISOString().slice(0, 10),
updated_at: new Date().toISOString() updated_at: new Date()..toISOString().slice(0, 10)
}) })
}); });
} }

View File

@ -1,13 +1,30 @@
// src/components/MilestoneCopyWizard.js // src/components/MilestoneCopyWizard.js
import React, { useState, useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { Button } from './ui/button.js';
import authFetch from '../utils/authFetch.js';
export default function MilestoneCopyWizard({ milestone, authFetch, onClose }) { export default function MilestoneCopyWizard({ milestone, onClose }) {
const [scenarios, setScenarios] = useState([]); const [scenarios, setScenarios] = useState([]);
const [selectedScenarios, setSelectedScenarios] = useState([]); const [selectedScenarios, setSelectedScenarios] = useState([]);
useEffect(() => { useEffect(() => {
// fetch /api/premium/career-profile/all => setScenarios if (!milestone) return;
}, [authFetch]); // 1) load all scenarios
async function loadAllScenarios() {
try {
const resp = await authFetch('/api/premium/career-profile/all');
if (!resp.ok) {
console.error('Failed to load all scenarios =>', resp.status);
return;
}
const data = await resp.json();
setScenarios(data.careerProfiles || []);
} catch (err) {
console.error('MilestoneCopyWizard => error loading scenarios:', err);
}
}
loadAllScenarios();
}, [milestone]);
function toggleScenario(id) { function toggleScenario(id) {
setSelectedScenarios((prev) => setSelectedScenarios((prev) =>
@ -16,29 +33,85 @@ export default function MilestoneCopyWizard({ milestone, authFetch, onClose }) {
} }
async function handleCopy() { async function handleCopy() {
// POST => /api/premium/milestone/copy if (!milestone || !selectedScenarios.length) {
// with { milestoneId: milestone.id, scenarioIds: selectedScenarios } onClose(false);
// Then onClose(true) return;
}
try {
// 2) call your copy endpoint
const payload = {
milestoneId: milestone.id,
scenarioIds: selectedScenarios
};
const res = await authFetch('/api/premium/milestone/copy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!res.ok) {
const txt = await res.text();
alert(txt || 'Failed to copy milestone');
onClose(false);
return;
}
onClose(true); // success
} catch (err) {
console.error('Error copying milestone =>', err);
alert('Error copying milestone');
onClose(false);
}
} }
if (!milestone) return null; if (!milestone) return null;
return ( return (
<div className="copy-wizard-backdrop"> <div
<div className="copy-wizard-content"> style={{
<h3>Copy: {milestone.title}</h3> position: 'fixed',
{scenarios.map((s) => ( top: 0,
<label key={s.id}> left: 0,
<input width: '100vw',
type="checkbox" height: '100vh',
checked={selectedScenarios.includes(s.id)} background: 'rgba(0,0,0,0.4)',
onChange={() => toggleScenario(s.id)} zIndex: 99999,
/> display: 'flex',
{s.career_name} justifyContent: 'center',
</label> alignItems: 'center'
))} }}
<br /> >
<button onClick={() => onClose(false)}>Cancel</button> <div
<button onClick={() => handleCopy()}>Copy</button> style={{
background: '#fff',
width: '400px',
padding: '1rem',
borderRadius: '4px'
}}
>
<h4>Copy Milestone:</h4>
<p style={{ fontWeight: 'bold' }}>{milestone.title}</p>
<div style={{ maxHeight: '250px', overflowY: 'auto', border: '1px solid #ccc', padding: '0.5rem' }}>
{scenarios.length === 0 && <p>No scenarios found.</p>}
{scenarios.map((s) => (
<div key={s.id} style={{ marginBottom: '0.25rem' }}>
<label>
<input
type="checkbox"
checked={selectedScenarios.includes(s.id)}
onChange={() => toggleScenario(s.id)}
/>
{s.scenario_title || s.career_name || '(Untitled)'}
</label>
</div>
))}
</div>
<div style={{ marginTop: '1rem', textAlign: 'right' }}>
<Button onClick={() => onClose(false)} style={{ marginRight: '0.5rem' }}>
Cancel
</Button>
<Button onClick={handleCopy}>Copy</Button>
</div>
</div> </div>
</div> </div>
); );

View File

@ -642,7 +642,7 @@ export default function MilestoneTracker({ selectedCareer: initialCareer }) {
programLength: collegeData.programLength, programLength: collegeData.programLength,
expectedSalary: collegeData.expectedSalary, expectedSalary: collegeData.expectedSalary,
startDate: new Date().toISOString(), startDate: new Date().toISOString().slice(0, 10),
simulationYears, simulationYears,
milestoneImpacts: allImpacts, milestoneImpacts: allImpacts,
@ -703,7 +703,7 @@ export default function MilestoneTracker({ selectedCareer: initialCareer }) {
const [clickCount, setClickCount] = useState(() => { const [clickCount, setClickCount] = useState(() => {
const storedCount = localStorage.getItem('aiClickCount'); const storedCount = localStorage.getItem('aiClickCount');
const storedDate = localStorage.getItem('aiClickDate'); const storedDate = localStorage.getItem('aiClickDate');
const today = new Date().toISOString().slice(0, 10); const today = new Date().toISOString().slice(0, 10).slice(0, 10);
if (storedDate !== today) { if (storedDate !== today) {
localStorage.setItem('aiClickDate', today); localStorage.setItem('aiClickDate', today);
localStorage.setItem('aiClickCount', '0'); localStorage.setItem('aiClickCount', '0');

View File

@ -7,31 +7,21 @@ import { Button } from './ui/button.js';
export default function MultiScenarioView() { export default function MultiScenarioView() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(null); const [error, setError] = useState(null);
// The users single overall financial profile
const [financialProfile, setFinancialProfile] = useState(null); const [financialProfile, setFinancialProfile] = useState(null);
// The list of scenario "headers" (rows from career_profiles)
const [scenarios, setScenarios] = useState([]); const [scenarios, setScenarios] = useState([]);
useEffect(() => { useEffect(() => {
loadScenariosAndFinancial(); loadScenariosAndFinancial();
}, []); }, []);
/**
* Fetch users financial profile + scenario list
*/
async function loadScenariosAndFinancial() { async function loadScenariosAndFinancial() {
setLoading(true); setLoading(true);
setError(null); setError(null);
try { try {
// 1) fetch users global financialProfile
const finRes = await authFetch('/api/premium/financial-profile'); const finRes = await authFetch('/api/premium/financial-profile');
if (!finRes.ok) throw new Error(`FinancialProfile error: ${finRes.status}`); if (!finRes.ok) throw new Error(`FinancialProfile error: ${finRes.status}`);
const finData = await finRes.json(); const finData = await finRes.json();
// 2) fetch scenario list
const scenRes = await authFetch('/api/premium/career-profile/all'); const scenRes = await authFetch('/api/premium/career-profile/all');
if (!scenRes.ok) throw new Error(`Scenarios error: ${scenRes.status}`); if (!scenRes.ok) throw new Error(`Scenarios error: ${scenRes.status}`);
const scenData = await scenRes.json(); const scenData = await scenRes.json();
@ -39,58 +29,57 @@ export default function MultiScenarioView() {
setFinancialProfile(finData); setFinancialProfile(finData);
setScenarios(scenData.careerProfiles || []); setScenarios(scenData.careerProfiles || []);
} catch (err) { } catch (err) {
console.error('MultiScenarioView load error:', err); console.error('MultiScenarioView =>', err);
setError(err.message || 'Failed to load multi-scenarios'); setError(err.message || 'Failed to load');
} finally { } finally {
setLoading(false); setLoading(false);
} }
} }
/**
* Create a brand-new scenario with minimal defaults
*/
async function handleAddScenario() { async function handleAddScenario() {
try { try {
const body = { const body = {
career_name: 'New Scenario ' + new Date().toLocaleDateString(), career_name: 'New Scenario ' + new Date().toLocaleDateString(),
status: 'planned', status: 'planned',
start_date: new Date().toISOString(), // slice(0,10) to avoid timestamps
start_date: new Date().toISOString().slice(0, 10),
college_enrollment_status: 'not_enrolled', college_enrollment_status: 'not_enrolled',
currently_working: 'no' currently_working: 'no'
}; };
const r = await authFetch('/api/premium/career-profile', {
const res = await authFetch('/api/premium/career-profile', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
if (!res.ok) throw new Error(`Add scenario error: ${res.status}`); if (!r.ok) throw new Error(`Add scenario error => ${r.status}`);
// reload
await loadScenariosAndFinancial(); await loadScenariosAndFinancial();
} catch (err) { } catch (err) {
alert(err.message); alert(err.message);
} }
} }
/**
* Clone a scenario:
* (A) create new scenario row from old scenario fields
* (B) also clone old scenarios college_profile
*/
async function handleCloneScenario(oldScenario) { async function handleCloneScenario(oldScenario) {
try { try {
// 1) create the new scenario row // convert oldScenario.start_date to just YYYY-MM-DD
const cloneStart = oldScenario.start_date
? oldScenario.start_date.slice(0, 10)
: new Date().toISOString().slice(0, 10);
const scenarioPayload = { const scenarioPayload = {
scenario_title: oldScenario.scenario_title scenario_title: oldScenario.scenario_title
? oldScenario.scenario_title + ' (Copy)' ? oldScenario.scenario_title + ' (Copy)'
: null, : 'Untitled (Copy)',
career_name: oldScenario.career_name career_name: oldScenario.career_name
? oldScenario.career_name + ' (Copy)' ? oldScenario.career_name + ' (Copy)'
: 'Untitled (Copy)', : 'Unknown Career',
status: oldScenario.status, status: oldScenario.status,
start_date: oldScenario.start_date, // also do the slice if projected_end_date is set
projected_end_date: oldScenario.projected_end_date, start_date: oldScenario.start_date
? oldScenario.start_date.slice(0, 10)
: '',
projected_end_date: oldScenario.projected_end_date
? oldScenario.projected_end_date.slice(0, 10)
: '',
college_enrollment_status: oldScenario.college_enrollment_status, college_enrollment_status: oldScenario.college_enrollment_status,
currently_working: oldScenario.currently_working || 'no', currently_working: oldScenario.currently_working || 'no',
@ -106,107 +95,195 @@ export default function MultiScenarioView() {
planned_additional_income: oldScenario.planned_additional_income planned_additional_income: oldScenario.planned_additional_income
}; };
const res = await authFetch('/api/premium/career-profile', { const createRes = await authFetch('/api/premium/career-profile', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(scenarioPayload) body: JSON.stringify(scenarioPayload)
}); });
if (!res.ok) throw new Error(`Clone scenario error: ${res.status}`); if (!createRes.ok) {
throw new Error(`Clone scenario error: ${createRes.status}`);
// parse the newly created scenario_id }
const newScenarioData = await res.json(); const newScenarioData = await createRes.json();
const newScenarioId = newScenarioData.career_profile_id; const newScenarioId = newScenarioData.career_profile_id;
// 2) Clone the old scenarios college_profile => new scenario // clone college
await cloneCollegeProfile(oldScenario.id, newScenarioId); await cloneCollegeProfile(oldScenario.id, newScenarioId);
// 3) reload // clone milestones
await cloneAllMilestones(oldScenario.id, newScenarioId);
await loadScenariosAndFinancial(); await loadScenariosAndFinancial();
} catch (err) { } catch (err) {
alert(`Clone scenario failed: ${err.message}`); alert('Failed to clone scenario => ' + err.message);
} }
} }
async function cloneCollegeProfile(oldScenarioId, newScenarioId) { async function cloneCollegeProfile(oldId, newId) {
try { try {
// fetch old scenarios college_profile const cRes = await authFetch(`/api/premium/college-profile?careerProfileId=${oldId}`);
const getRes = await authFetch( if (!cRes.ok) return;
`/api/premium/college-profile?careerProfileId=${oldScenarioId}` let oldC = await cRes.json();
); if (Array.isArray(oldC)) oldC = oldC[0] || null;
if (!getRes.ok) { if (!oldC || !oldC.id) return;
console.warn(
'Could not fetch old college profile for scenarioId=' + oldScenarioId
);
return;
}
let oldCollegeData = await getRes.json(); // you can do date-slice on expected_graduation if needed
if (Array.isArray(oldCollegeData)) { const pay = {
oldCollegeData = oldCollegeData[0] || null; career_profile_id: newId,
} selected_school: oldC.selected_school,
selected_program: oldC.selected_program,
if (!oldCollegeData || !oldCollegeData.id) { program_type: oldC.program_type,
// no old college profile => nothing to clone academic_calendar: oldC.academic_calendar,
return; is_in_state: oldC.is_in_state,
} is_in_district: oldC.is_in_district,
is_online: oldC.is_online,
// build new payload college_enrollment_status: oldC.college_enrollment_status,
const clonePayload = { annual_financial_aid: oldC.annual_financial_aid,
career_profile_id: newScenarioId, existing_college_debt: oldC.existing_college_debt,
tuition_paid: oldC.tuition_paid,
selected_school: oldCollegeData.selected_school, tuition: oldC.tuition,
selected_program: oldCollegeData.selected_program, loan_deferral_until_graduation: oldC.loan_deferral_until_graduation,
program_type: oldCollegeData.program_type, loan_term: oldC.loan_term,
academic_calendar: oldCollegeData.academic_calendar, interest_rate: oldC.interest_rate,
extra_payment: oldC.extra_payment,
is_in_state: oldCollegeData.is_in_state, credit_hours_per_year: oldC.credit_hours_per_year,
is_in_district: oldCollegeData.is_in_district, hours_completed: oldC.hours_completed,
is_online: oldCollegeData.is_online, program_length: oldC.program_length,
college_enrollment_status: oldCollegeData.college_enrollment_status, credit_hours_required: oldC.credit_hours_required,
expected_graduation: oldC.expected_graduation
annual_financial_aid: oldCollegeData.annual_financial_aid, ? oldC.expected_graduation.slice(0, 10)
existing_college_debt: oldCollegeData.existing_college_debt, : '',
tuition_paid: oldCollegeData.tuition_paid, expected_salary: oldC.expected_salary
tuition: oldCollegeData.tuition,
loan_deferral_until_graduation: oldCollegeData.loan_deferral_until_graduation,
loan_term: oldCollegeData.loan_term,
interest_rate: oldCollegeData.interest_rate,
extra_payment: oldCollegeData.extra_payment,
credit_hours_per_year: oldCollegeData.credit_hours_per_year,
hours_completed: oldCollegeData.hours_completed,
program_length: oldCollegeData.program_length,
credit_hours_required: oldCollegeData.credit_hours_required,
expected_graduation: oldCollegeData.expected_graduation,
expected_salary: oldCollegeData.expected_salary
}; };
const pRes = await authFetch('/api/premium/college-profile', {
// insert new row in college_profiles
const postRes = await authFetch('/api/premium/college-profile', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(clonePayload) body: JSON.stringify(pay)
}); });
if (!postRes.ok) { if (!pRes.ok) {
console.warn( console.warn('Clone college failed =>', pRes.status);
'Could not clone old collegeProfile => new scenario',
postRes.status
);
} }
} catch (err) { } catch (err) {
console.error('Error cloning college profile:', err); console.error('cloneCollegeProfile =>', err);
}
}
async function cloneAllMilestones(oldId, newId) {
try {
const mRes = await authFetch(
`/api/premium/milestones?careerProfileId=${oldId}`
);
if (!mRes.ok) {
console.warn('No old milestones => skip');
return;
}
const d = await mRes.json();
const oldList = d.milestones || [];
for (const m of oldList) {
// create new milestone
const newMileId = await cloneSingleMilestone(m, newId);
// tasks
await cloneTasks(m.id, newMileId);
}
} catch (err) {
console.error('cloneAllMilestones =>', err);
}
}
async function cloneSingleMilestone(oldM, newScenarioId) {
try {
// remove timestamps from oldM.date
const justDate = oldM.date ? oldM.date.slice(0, 10) : '';
const pay = {
title: oldM.title,
description: oldM.description,
date: justDate,
career_profile_id: newScenarioId,
progress: oldM.progress,
status: oldM.status,
is_universal: oldM.is_universal
};
const r = await authFetch('/api/premium/milestone', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(pay)
});
if (!r.ok) {
console.warn('cloneSingleMilestone =>', r.status);
return null;
}
const j = await r.json();
let mid = null;
if (Array.isArray(j)) {
mid = j[0]?.id || null;
} else if (j?.id) {
mid = j.id;
}
// impacts
if (mid) {
await cloneMilestoneImpacts(oldM.id, mid);
}
return mid;
} catch (err) {
console.error('cloneSingleMilestone =>', err);
return null;
}
}
async function cloneMilestoneImpacts(oldMId, newMId) {
try {
const iRes = await authFetch(`/api/premium/milestone-impacts?milestone_id=${oldMId}`);
if (!iRes.ok) return;
const d = await iRes.json();
const arr = d.impacts || [];
for (const imp of arr) {
const justStart = imp.start_date ? imp.start_date.slice(0, 10) : null;
const justEnd = imp.end_date ? imp.end_date.slice(0, 10) : null;
const pay = {
milestone_id: newMId,
impact_type: imp.impact_type,
direction: imp.direction,
amount: imp.amount,
start_date: justStart,
end_date: justEnd
};
await authFetch('/api/premium/milestone-impacts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(pay)
});
}
} catch (err) {
console.error('cloneMilestoneImpacts =>', err);
}
}
async function cloneTasks(oldMId, newMId) {
try {
const tRes = await authFetch(`/api/premium/tasks?milestone_id=${oldMId}`);
if (!tRes.ok) return;
const d = await tRes.json();
const arr = d.tasks || [];
for (const tk of arr) {
const pay = {
milestone_id: newMId,
title: tk.title,
description: tk.description,
due_date: tk.due_date ? tk.due_date.slice(0, 10) : ''
};
await authFetch('/api/premium/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(pay)
});
}
} catch (err) {
console.error('cloneTasks =>', err);
} }
} }
async function handleRemoveScenario(id) { async function handleRemoveScenario(id) {
const confirmDel = window.confirm('Delete this scenario?'); const c = window.confirm('Delete scenario?');
if (!confirmDel) return; if (!c) return;
try { try {
const res = await authFetch(`/api/premium/career-profile/${id}`, { const r = await authFetch(`/api/premium/career-profile/${id}`, { method: 'DELETE' });
method: 'DELETE' if (!r.ok) throw new Error(`Delete scenario => ${r.status}`);
});
if (!res.ok) throw new Error(`Delete scenario error: ${res.status}`);
await loadScenariosAndFinancial(); await loadScenariosAndFinancial();
} catch (err) { } catch (err) {
alert(err.message); alert(err.message);
@ -216,30 +293,22 @@ export default function MultiScenarioView() {
if (loading) return <p>Loading scenarios...</p>; if (loading) return <p>Loading scenarios...</p>;
if (error) return <p style={{ color: 'red' }}>{error}</p>; if (error) return <p style={{ color: 'red' }}>{error}</p>;
// show only first 2 scenarios const visible = scenarios.slice(0, 2);
const visibleScenarios = scenarios.slice(0, 2);
return ( return (
<div style={{ margin: '1rem' }}> <div style={{ margin: '1rem' }}>
{/* Add Scenario button */} <Button onClick={handleAddScenario} style={{ marginBottom: '1rem' }}>
<div style={{ marginBottom: '1rem' }}> + Add Scenario
<Button onClick={handleAddScenario}>+ Add Scenario</Button> </Button>
</div>
{/* Display 1 or 2 scenarios side by side */}
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
{visibleScenarios.map((sc) => ( {visible.map(sc => (
<ScenarioContainer <ScenarioContainer
key={sc.id} key={sc.id}
scenario={sc} scenario={sc}
financialProfile={financialProfile} financialProfile={financialProfile}
onClone={handleCloneScenario} // <--- pass down onClone={handleCloneScenario}
onRemove={handleRemoveScenario} // <--- pass down onRemove={handleRemoveScenario}
onEdit={(sc) => {
console.log('Edit scenario clicked:', sc);
// or open a modal if you prefer
}}
hideMilestones // if you want to hide milestone details
/> />
))} ))}
</div> </div>

View File

@ -56,7 +56,7 @@ const CareerOnboarding = ({ nextStep, prevStep, data, setData }) => {
inCollege: isInCollege, inCollege: isInCollege,
// fallback defaults, or use user-provided // fallback defaults, or use user-provided
status: prevData.status || 'planned', status: prevData.status || 'planned',
start_date: prevData.start_date || new Date().toISOString().slice(0, 10), start_date: prevData.start_date || new Date().toISOString().slice(0, 10).slice(0, 10),
projected_end_date: prevData.projected_end_date || null projected_end_date: prevData.projected_end_date || null
})); }));

File diff suppressed because it is too large Load Diff

View File

@ -424,7 +424,7 @@ export default function ScenarioEditModal({
expectedSalary: expectedSalary:
collegeRow.expected_salary || financialData.current_salary || 0, collegeRow.expected_salary || financialData.current_salary || 0,
startDate: scenarioRow.start_date || new Date().toISOString(), startDate: scenarioRow.start_date || new Date().toISOString().slice(0, 10),
simulationYears: 20, simulationYears: 20,
milestoneImpacts: [] milestoneImpacts: []
}; };