Fixed Clone Scenario
This commit is contained in:
parent
e58507411a
commit
2a268a0c6e
@ -1,11 +1,17 @@
|
||||
// src/components/MultiScenarioView.js
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import authFetch from '../utils/authFetch.js';
|
||||
import ScenarioContainer from './ScenarioContainer.js';
|
||||
|
||||
// This component loads the user's global financial profile
|
||||
// plus a list of all scenarios, and renders one <ScenarioContainer>
|
||||
// for each. It also has the "Add Scenario" and "Clone/Remove" logic.
|
||||
|
||||
/**
|
||||
* MultiScenarioView
|
||||
* -----------------
|
||||
* - Loads the user’s global financialProfile
|
||||
* - Loads all scenarios from `career_paths`
|
||||
* - Renders a <ScenarioContainer> for each scenario
|
||||
* - Handles "Add Scenario", "Clone Scenario" (including college_profile), "Remove Scenario"
|
||||
*/
|
||||
export default function MultiScenarioView() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
@ -13,16 +19,20 @@ export default function MultiScenarioView() {
|
||||
// The user’s single overall financial profile
|
||||
const [financialProfile, setFinancialProfile] = useState(null);
|
||||
|
||||
// The list of scenario "headers" (career_paths)
|
||||
// The list of scenario "headers" (rows from career_paths)
|
||||
const [scenarios, setScenarios] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
loadScenariosAndFinancial();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Fetch user’s financial profile + scenario list
|
||||
*/
|
||||
async function loadScenariosAndFinancial() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// 1) fetch user’s global financialProfile
|
||||
const finRes = await authFetch('/api/premium/financial-profile');
|
||||
@ -44,78 +54,179 @@ export default function MultiScenarioView() {
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new scenario => then reload
|
||||
/**
|
||||
* Create a brand-new scenario with minimal defaults
|
||||
*/
|
||||
async function handleAddScenario() {
|
||||
try {
|
||||
const body = {
|
||||
// minimal fields so the scenario is valid
|
||||
career_name: 'New Scenario ' + new Date().toLocaleDateString(),
|
||||
status: 'planned',
|
||||
start_date: new Date().toISOString(),
|
||||
college_enrollment_status: 'not_enrolled',
|
||||
currently_working: 'no'
|
||||
};
|
||||
|
||||
const res = await authFetch('/api/premium/career-profile', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
if (!res.ok) throw new Error(`Add scenario error: ${res.status}`);
|
||||
|
||||
// reload
|
||||
await loadScenariosAndFinancial();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Clone a scenario => then reload
|
||||
async function handleCloneScenario(s) {
|
||||
/**
|
||||
* CLONE a scenario: (A) create new scenario row, (B) also clone old scenario’s college_profile
|
||||
*/
|
||||
async function handleCloneScenario(oldScenario) {
|
||||
try {
|
||||
const body = {
|
||||
scenario_title: s.scenario_title ? s.scenario_title + ' (Copy)' : null,
|
||||
career_name: s.career_name ? s.career_name + ' (Copy)' : 'Untitled (Copy)',
|
||||
status: s.status,
|
||||
start_date: s.start_date,
|
||||
projected_end_date: s.projected_end_date,
|
||||
college_enrollment_status: s.college_enrollment_status,
|
||||
currently_working: s.currently_working,
|
||||
planned_monthly_expenses: s.planned_monthly_expenses,
|
||||
planned_monthly_debt_payments: s.planned_monthly_debt_payments,
|
||||
planned_monthly_retirement_contribution: s.planned_monthly_retirement_contribution,
|
||||
planned_monthly_emergency_contribution: s.planned_monthly_emergency_contribution,
|
||||
planned_surplus_emergency_pct: s.planned_surplus_emergency_pct,
|
||||
planned_surplus_retirement_pct: s.planned_surplus_retirement_pct,
|
||||
planned_additional_income: s.planned_additional_income
|
||||
// 1) create the new scenario row
|
||||
const scenarioPayload = {
|
||||
scenario_title: oldScenario.scenario_title
|
||||
? oldScenario.scenario_title + ' (Copy)'
|
||||
: null,
|
||||
career_name: oldScenario.career_name
|
||||
? oldScenario.career_name + ' (Copy)'
|
||||
: 'Untitled (Copy)',
|
||||
status: oldScenario.status,
|
||||
start_date: oldScenario.start_date,
|
||||
projected_end_date: oldScenario.projected_end_date,
|
||||
college_enrollment_status: oldScenario.college_enrollment_status,
|
||||
currently_working: oldScenario.currently_working,
|
||||
|
||||
planned_monthly_expenses: oldScenario.planned_monthly_expenses,
|
||||
planned_monthly_debt_payments: oldScenario.planned_monthly_debt_payments,
|
||||
planned_monthly_retirement_contribution:
|
||||
oldScenario.planned_monthly_retirement_contribution,
|
||||
planned_monthly_emergency_contribution:
|
||||
oldScenario.planned_monthly_emergency_contribution,
|
||||
planned_surplus_emergency_pct: oldScenario.planned_surplus_emergency_pct,
|
||||
planned_surplus_retirement_pct: oldScenario.planned_surplus_retirement_pct,
|
||||
planned_additional_income: oldScenario.planned_additional_income
|
||||
};
|
||||
|
||||
const res = await authFetch('/api/premium/career-profile', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
body: JSON.stringify(scenarioPayload)
|
||||
});
|
||||
if (!res.ok) throw new Error(`Clone scenario error: ${res.status}`);
|
||||
|
||||
// parse the newly created scenario_id
|
||||
const newScenarioData = await res.json();
|
||||
const newScenarioId = newScenarioData.career_path_id;
|
||||
|
||||
// 2) Clone the old scenario’s college_profile => new scenario
|
||||
await cloneCollegeProfile(oldScenario.id, newScenarioId);
|
||||
|
||||
// 3) reload
|
||||
await loadScenariosAndFinancial();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove => reload
|
||||
/**
|
||||
* Helper to clone old scenario’s college_profile => new scenario
|
||||
*/
|
||||
async function cloneCollegeProfile(oldScenarioId, newScenarioId) {
|
||||
try {
|
||||
// fetch old scenario’s college_profile
|
||||
const getRes = await authFetch(
|
||||
`/api/premium/college-profile?careerPathId=${oldScenarioId}`
|
||||
);
|
||||
if (!getRes.ok) {
|
||||
console.warn(
|
||||
'Could not fetch old college profile for scenarioId=' + oldScenarioId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let oldCollegeData = await getRes.json();
|
||||
if (Array.isArray(oldCollegeData)) {
|
||||
oldCollegeData = oldCollegeData[0] || null;
|
||||
}
|
||||
|
||||
if (!oldCollegeData || !oldCollegeData.id) {
|
||||
// no old college profile => nothing to clone
|
||||
return;
|
||||
}
|
||||
|
||||
// build new payload
|
||||
const clonePayload = {
|
||||
career_path_id: newScenarioId,
|
||||
|
||||
selected_school: oldCollegeData.selected_school,
|
||||
selected_program: oldCollegeData.selected_program,
|
||||
program_type: oldCollegeData.program_type,
|
||||
academic_calendar: oldCollegeData.academic_calendar,
|
||||
|
||||
is_in_state: oldCollegeData.is_in_state,
|
||||
is_in_district: oldCollegeData.is_in_district,
|
||||
is_online: oldCollegeData.is_online,
|
||||
college_enrollment_status: oldCollegeData.college_enrollment_status,
|
||||
|
||||
annual_financial_aid: oldCollegeData.annual_financial_aid,
|
||||
existing_college_debt: oldCollegeData.existing_college_debt,
|
||||
tuition_paid: oldCollegeData.tuition_paid,
|
||||
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
|
||||
};
|
||||
|
||||
// insert new row in college_profiles
|
||||
const postRes = await authFetch('/api/premium/college-profile', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(clonePayload)
|
||||
});
|
||||
if (!postRes.ok) {
|
||||
console.warn(
|
||||
'Could not clone old collegeProfile => new scenario',
|
||||
postRes.status
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error cloning college profile:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove scenario => server also deletes its college_profile => reload
|
||||
*/
|
||||
async function handleRemoveScenario(id) {
|
||||
const confirmDel = window.confirm('Delete this scenario?');
|
||||
if (!confirmDel) return;
|
||||
|
||||
try {
|
||||
const res = await authFetch(`/api/premium/career-profile/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
if (!res.ok) throw new Error(`Delete scenario error: ${res.status}`);
|
||||
|
||||
// reload
|
||||
await loadScenariosAndFinancial();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// If user wants to "edit" a scenario, we'll pass it down to the container's "onEdit"
|
||||
// or you can open a modal at this level. For now, we rely on the ScenarioContainer
|
||||
// "onEdit" prop if needed.
|
||||
|
||||
if (loading) return <p>Loading scenarios...</p>;
|
||||
if (error) return <p style={{ color: 'red' }}>{error}</p>;
|
||||
|
||||
@ -124,24 +235,25 @@ export default function MultiScenarioView() {
|
||||
{scenarios.map(sc => (
|
||||
<ScenarioContainer
|
||||
key={sc.id}
|
||||
scenario={sc} // pass the scenario row
|
||||
scenario={sc}
|
||||
financialProfile={financialProfile}
|
||||
onClone={(s) => handleCloneScenario(s)}
|
||||
onRemove={(id) => handleRemoveScenario(id)}
|
||||
onClone={handleCloneScenario}
|
||||
onRemove={handleRemoveScenario}
|
||||
onEdit={(sc) => {
|
||||
// Example: open an edit modal or navigate to a scenario editor.
|
||||
// Example: open an edit modal or navigate to a scenario editor
|
||||
console.log('Edit scenario clicked:', sc);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Add Scenario button */}
|
||||
<div style={{ alignSelf: 'flex-start' }}>
|
||||
<button
|
||||
onClick={handleAddScenario}
|
||||
style={{
|
||||
padding: '0.5rem 1rem',
|
||||
height: 'auto',
|
||||
backgroundColor: '#76b900', // or whatever
|
||||
backgroundColor: '#76b900',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer'
|
||||
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user