diff --git a/src/components/MultiScenarioView.js b/src/components/MultiScenarioView.js index 83f33ef..84c74c2 100644 --- a/src/components/MultiScenarioView.js +++ b/src/components/MultiScenarioView.js @@ -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 -// 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 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,112 +54,214 @@ 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

Loading scenarios...

; - if (error) return

{error}

; + if (error) return

{error}

; return ( -
+
{scenarios.map(sc => ( 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 */} +
+ +
); } diff --git a/user_profile.db b/user_profile.db index ecc73c3..f9ca5b5 100644 Binary files a/user_profile.db and b/user_profile.db differ