Adjusted financial simulation for taxes.
This commit is contained in:
parent
e3d804e01a
commit
126a17543c
244
src/components/ScenarioEditModal.js
Normal file
244
src/components/ScenarioEditModal.js
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// src/components/ScenarioEditModal.js
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import authFetch from '../utils/authFetch';
|
||||||
|
|
||||||
|
const ScenarioEditModal = ({
|
||||||
|
show,
|
||||||
|
onClose,
|
||||||
|
financialProfile,
|
||||||
|
setFinancialProfile,
|
||||||
|
collegeProfile,
|
||||||
|
setCollegeProfile,
|
||||||
|
apiURL,
|
||||||
|
authFetch,
|
||||||
|
}) => {
|
||||||
|
const [formData, setFormData] = useState({});
|
||||||
|
|
||||||
|
// Populate local formData whenever show=true
|
||||||
|
useEffect(() => {
|
||||||
|
if (!show) return;
|
||||||
|
|
||||||
|
setFormData({
|
||||||
|
// From financialProfile:
|
||||||
|
currentSalary: financialProfile?.current_salary ?? 0,
|
||||||
|
monthlyExpenses: financialProfile?.monthly_expenses ?? 0,
|
||||||
|
monthlyDebtPayments: financialProfile?.monthly_debt_payments ?? 0,
|
||||||
|
retirementSavings: financialProfile?.retirement_savings ?? 0,
|
||||||
|
emergencySavings: financialProfile?.emergency_fund ?? 0,
|
||||||
|
monthlyRetirementContribution: financialProfile?.retirement_contribution ?? 0,
|
||||||
|
monthlyEmergencyContribution: financialProfile?.emergency_contribution ?? 0,
|
||||||
|
surplusEmergencyAllocation: financialProfile?.extra_cash_emergency_pct ?? 50,
|
||||||
|
surplusRetirementAllocation: financialProfile?.extra_cash_retirement_pct ?? 50,
|
||||||
|
|
||||||
|
// From collegeProfile:
|
||||||
|
studentLoanAmount: collegeProfile?.existing_college_debt ?? 0,
|
||||||
|
interestRate: collegeProfile?.interest_rate ?? 5,
|
||||||
|
loanTerm: collegeProfile?.loan_term ?? 10,
|
||||||
|
loanDeferralUntilGraduation: !!collegeProfile?.loan_deferral_until_graduation,
|
||||||
|
academicCalendar: collegeProfile?.academic_calendar ?? 'monthly',
|
||||||
|
annualFinancialAid: collegeProfile?.annual_financial_aid ?? 0,
|
||||||
|
calculatedTuition: collegeProfile?.tuition ?? 0,
|
||||||
|
extraPayment: collegeProfile?.extra_payment ?? 0,
|
||||||
|
partTimeIncome: 0, // or fetch from DB if you store it
|
||||||
|
gradDate: collegeProfile?.expected_graduation ?? '',
|
||||||
|
programType: collegeProfile?.program_type ?? '',
|
||||||
|
creditHoursPerYear: collegeProfile?.credit_hours_per_year ?? 0,
|
||||||
|
hoursCompleted: collegeProfile?.hours_completed ?? 0,
|
||||||
|
programLength: collegeProfile?.program_length ?? 0,
|
||||||
|
inCollege:
|
||||||
|
collegeProfile?.college_enrollment_status === 'currently_enrolled' ||
|
||||||
|
collegeProfile?.college_enrollment_status === 'prospective_student',
|
||||||
|
expectedSalary: collegeProfile?.expected_salary ?? financialProfile?.current_salary ?? 0,
|
||||||
|
});
|
||||||
|
}, [show, financialProfile, collegeProfile]);
|
||||||
|
|
||||||
|
// Handle form changes in local state
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, type, value, checked } = e.target;
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]:
|
||||||
|
type === 'checkbox'
|
||||||
|
? checked
|
||||||
|
: type === 'number'
|
||||||
|
? parseFloat(value) || 0
|
||||||
|
: value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAVE: Update DB and local states, then close
|
||||||
|
const handleSave = async () => {
|
||||||
|
try {
|
||||||
|
// 1) Update the backend (financialProfile + collegeProfile):
|
||||||
|
// (Adjust endpoints/methods as needed in your codebase)
|
||||||
|
await authFetch(`${apiURL}/premium/financial-profile`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
current_salary: formData.currentSalary,
|
||||||
|
monthly_expenses: formData.monthlyExpenses,
|
||||||
|
monthly_debt_payments: formData.monthlyDebtPayments,
|
||||||
|
retirement_savings: formData.retirementSavings,
|
||||||
|
emergency_fund: formData.emergencySavings,
|
||||||
|
retirement_contribution: formData.monthlyRetirementContribution,
|
||||||
|
emergency_contribution: formData.monthlyEmergencyContribution,
|
||||||
|
extra_cash_emergency_pct: formData.surplusEmergencyAllocation,
|
||||||
|
extra_cash_retirement_pct: formData.surplusRetirementAllocation
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
await authFetch(`${apiURL}/premium/college-profile`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
existing_college_debt: formData.studentLoanAmount,
|
||||||
|
interest_rate: formData.interestRate,
|
||||||
|
loan_term: formData.loanTerm,
|
||||||
|
loan_deferral_until_graduation: formData.loanDeferralUntilGraduation,
|
||||||
|
academic_calendar: formData.academicCalendar,
|
||||||
|
annual_financial_aid: formData.annualFinancialAid,
|
||||||
|
tuition: formData.calculatedTuition,
|
||||||
|
extra_payment: formData.extraPayment,
|
||||||
|
expected_graduation: formData.gradDate,
|
||||||
|
program_type: formData.programType,
|
||||||
|
credit_hours_per_year: formData.creditHoursPerYear,
|
||||||
|
hours_completed: formData.hoursCompleted,
|
||||||
|
program_length: formData.programLength,
|
||||||
|
college_enrollment_status: formData.inCollege
|
||||||
|
? 'currently_enrolled'
|
||||||
|
: 'not_enrolled',
|
||||||
|
expected_salary: formData.expectedSalary
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2) Update local React state so your useEffect triggers re-simulation
|
||||||
|
setFinancialProfile((prev) => ({
|
||||||
|
...prev,
|
||||||
|
current_salary: formData.currentSalary,
|
||||||
|
monthly_expenses: formData.monthlyExpenses,
|
||||||
|
monthly_debt_payments: formData.monthlyDebtPayments,
|
||||||
|
retirement_savings: formData.retirementSavings,
|
||||||
|
emergency_fund: formData.emergencySavings,
|
||||||
|
retirement_contribution: formData.monthlyRetirementContribution,
|
||||||
|
emergency_contribution: formData.monthlyEmergencyContribution,
|
||||||
|
extra_cash_emergency_pct: formData.surplusEmergencyAllocation,
|
||||||
|
extra_cash_retirement_pct: formData.surplusRetirementAllocation
|
||||||
|
}));
|
||||||
|
|
||||||
|
setCollegeProfile((prev) => ({
|
||||||
|
...prev,
|
||||||
|
existing_college_debt: formData.studentLoanAmount,
|
||||||
|
interest_rate: formData.interestRate,
|
||||||
|
loan_term: formData.loanTerm,
|
||||||
|
loan_deferral_until_graduation: formData.loanDeferralUntilGraduation,
|
||||||
|
academic_calendar: formData.academicCalendar,
|
||||||
|
annual_financial_aid: formData.annualFinancialAid,
|
||||||
|
tuition: formData.calculatedTuition,
|
||||||
|
extra_payment: formData.extraPayment,
|
||||||
|
expected_graduation: formData.gradDate,
|
||||||
|
program_type: formData.programType,
|
||||||
|
credit_hours_per_year: formData.creditHoursPerYear,
|
||||||
|
hours_completed: formData.hoursCompleted,
|
||||||
|
program_length: formData.programLength,
|
||||||
|
college_enrollment_status: formData.inCollege
|
||||||
|
? 'currently_enrolled'
|
||||||
|
: 'not_enrolled',
|
||||||
|
expected_salary: formData.expectedSalary
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 3) Close the modal
|
||||||
|
onClose();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error saving scenario changes:', err);
|
||||||
|
// Optionally show a toast or error UI
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If show=false, don't render anything
|
||||||
|
if (!show) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal-backdrop">
|
||||||
|
<div className="modal-container">
|
||||||
|
<h2 className="text-xl font-bold mb-4">Edit Scenario Inputs</h2>
|
||||||
|
|
||||||
|
{/* EXAMPLE FIELDS: Add all the fields you actually want visible */}
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="block font-semibold">Current Salary</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="currentSalary"
|
||||||
|
value={formData.currentSalary}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="border px-2 py-1 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="block font-semibold">Monthly Expenses</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="monthlyExpenses"
|
||||||
|
value={formData.monthlyExpenses}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="border px-2 py-1 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="block font-semibold">Tuition</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="calculatedTuition"
|
||||||
|
value={formData.calculatedTuition}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="border px-2 py-1 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="block font-semibold">Annual Financial Aid</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="annualFinancialAid"
|
||||||
|
value={formData.annualFinancialAid}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="border px-2 py-1 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Example checkbox for loan deferral */}
|
||||||
|
<div className="mb-3 flex items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="loanDeferralUntilGraduation"
|
||||||
|
checked={formData.loanDeferralUntilGraduation}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
<label>Defer loan payments until graduation</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Add all other fields you want to expose... */}
|
||||||
|
|
||||||
|
{/* Modal Buttons */}
|
||||||
|
<div className="flex justify-end mt-6">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-4 py-2 mr-2 border rounded"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
className="bg-blue-600 text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScenarioEditModal;
|
@ -4,6 +4,64 @@ import moment from 'moment';
|
|||||||
* Single-filer federal tax calculation (2023).
|
* Single-filer federal tax calculation (2023).
|
||||||
* Includes standard deduction ($13,850).
|
* Includes standard deduction ($13,850).
|
||||||
*/
|
*/
|
||||||
|
const APPROX_STATE_TAX_RATES = {
|
||||||
|
AL: 0.05,
|
||||||
|
AK: 0.00,
|
||||||
|
AZ: 0.025,
|
||||||
|
AR: 0.05,
|
||||||
|
CA: 0.07,
|
||||||
|
CO: 0.045,
|
||||||
|
CT: 0.055,
|
||||||
|
DE: 0.05,
|
||||||
|
FL: 0.00,
|
||||||
|
GA: 0.05,
|
||||||
|
HI: 0.06,
|
||||||
|
ID: 0.058,
|
||||||
|
IL: 0.05,
|
||||||
|
IN: 0.035,
|
||||||
|
IA: 0.05,
|
||||||
|
KS: 0.05,
|
||||||
|
KY: 0.05,
|
||||||
|
LA: 0.04,
|
||||||
|
ME: 0.055,
|
||||||
|
MD: 0.05,
|
||||||
|
MA: 0.05,
|
||||||
|
MI: 0.0425,
|
||||||
|
MN: 0.06,
|
||||||
|
MS: 0.04,
|
||||||
|
MO: 0.05,
|
||||||
|
MT: 0.05,
|
||||||
|
NE: 0.05,
|
||||||
|
NV: 0.00,
|
||||||
|
NH: 0.00, // ignoring interest/dividend nuance
|
||||||
|
NJ: 0.057,
|
||||||
|
NM: 0.045,
|
||||||
|
NY: 0.06,
|
||||||
|
NC: 0.0475,
|
||||||
|
ND: 0.02,
|
||||||
|
OH: 0.04,
|
||||||
|
OK: 0.045,
|
||||||
|
OR: 0.07,
|
||||||
|
PA: 0.03,
|
||||||
|
RI: 0.045,
|
||||||
|
SC: 0.04,
|
||||||
|
SD: 0.00,
|
||||||
|
TN: 0.00,
|
||||||
|
TX: 0.00,
|
||||||
|
UT: 0.045,
|
||||||
|
VT: 0.055,
|
||||||
|
VA: 0.05,
|
||||||
|
WA: 0.00,
|
||||||
|
WV: 0.05,
|
||||||
|
WI: 0.05,
|
||||||
|
WY: 0.00,
|
||||||
|
DC: 0.05
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2) Single-filer federal tax calculation (2023).
|
||||||
|
* Includes standard deduction ($13,850).
|
||||||
|
*/
|
||||||
function calculateAnnualFederalTaxSingle(annualIncome) {
|
function calculateAnnualFederalTaxSingle(annualIncome) {
|
||||||
const STANDARD_DEDUCTION_SINGLE = 13850;
|
const STANDARD_DEDUCTION_SINGLE = 13850;
|
||||||
const taxableIncome = Math.max(0, annualIncome - STANDARD_DEDUCTION_SINGLE);
|
const taxableIncome = Math.max(0, annualIncome - STANDARD_DEDUCTION_SINGLE);
|
||||||
@ -35,20 +93,13 @@ function calculateAnnualFederalTaxSingle(annualIncome) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example state tax calculation.
|
* 3) Example approximate state tax calculation.
|
||||||
* Currently a simple flat rate based on `stateCode` from a small dictionary.
|
* Retrieves a single "effective" tax rate from the dictionary
|
||||||
* You can replace with bracket-based logic if desired.
|
* and returns a simple multiplication of annualIncome * rate.
|
||||||
*/
|
*/
|
||||||
function calculateAnnualStateTax(annualIncome, stateCode) {
|
function calculateAnnualStateTax(annualIncome, stateCode) {
|
||||||
// Example dictionary of flat rates (not real data!)
|
// Default to 5% if not found in dictionary
|
||||||
const stateTaxInfo = {
|
const rate = APPROX_STATE_TAX_RATES[stateCode] ?? 0.05;
|
||||||
CA: 0.08, // 8%
|
|
||||||
NY: 0.06, // 6%
|
|
||||||
TX: 0.00,
|
|
||||||
FL: 0.00,
|
|
||||||
GA: 0.05
|
|
||||||
};
|
|
||||||
const rate = stateTaxInfo[stateCode] ?? 0.05; // default 5% if not found
|
|
||||||
return annualIncome * rate;
|
return annualIncome * rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
user_profile.db
BIN
user_profile.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user