197 lines
6.0 KiB
JavaScript
197 lines
6.0 KiB
JavaScript
import React, { useState } from 'react';
|
|
|
|
function LoanRepayment({ schools, salaryData, earningHorizon }) {
|
|
const [tuitionType, setTuitionType] = useState('inState'); // 'inState' or 'outOfState'
|
|
const [interestRate, setInterestRate] = useState(5.5); // Default federal loan interest rate
|
|
const [loanTerm, setLoanTerm] = useState(10); // Default loan term (10 years)
|
|
const [extraPayment, setExtraPayment] = useState(0); // Extra monthly payment
|
|
const [currentSalary, setCurrentSalary] = useState(0); // Current salary input
|
|
const [results, setResults] = useState([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState(null);
|
|
|
|
// Validation function
|
|
const validateInputs = () => {
|
|
if (!schools || schools.length === 0) {
|
|
setError('School data is missing. Loan calculations cannot proceed.');
|
|
return false;
|
|
}
|
|
|
|
if (interestRate <= 0) {
|
|
setError('Interest rate must be greater than 0.');
|
|
return false;
|
|
}
|
|
|
|
if (loanTerm <= 0) {
|
|
setError('Loan term must be greater than 0.');
|
|
return false;
|
|
}
|
|
|
|
if (extraPayment < 0) {
|
|
setError('Extra monthly payment cannot be negative.');
|
|
return false;
|
|
}
|
|
|
|
if (currentSalary < 0) {
|
|
setError('Current salary cannot be negative.');
|
|
return false;
|
|
}
|
|
|
|
setError(null); // Clear errors if valid
|
|
return true;
|
|
};
|
|
|
|
// Loan calculation function for all schools
|
|
const calculateLoanDetails = () => {
|
|
if (!validateInputs()) return; // Validate inputs before calculation
|
|
|
|
const schoolResults = schools.map((school) => {
|
|
const tuition = tuitionType === 'inState' ? school.inState : school.outOfState;
|
|
const monthlyRate = interestRate / 12 / 100;
|
|
const loanTermMonths = loanTerm * 12;
|
|
|
|
// Calculate minimum monthly payment
|
|
const minimumMonthlyPayment = tuition * (monthlyRate * Math.pow(1 + monthlyRate, loanTermMonths)) /
|
|
(Math.pow(1 + monthlyRate, loanTermMonths) - 1);
|
|
|
|
// Total loan cost with extra payments
|
|
const extraMonthlyPayment = minimumMonthlyPayment + extraPayment;
|
|
let remainingBalance = tuition;
|
|
let monthsWithExtra = 0;
|
|
|
|
while (remainingBalance > 0) {
|
|
monthsWithExtra++;
|
|
const interest = remainingBalance * monthlyRate;
|
|
const principal = extraMonthlyPayment - interest;
|
|
remainingBalance -= principal;
|
|
}
|
|
|
|
const totalLoanCost = extraMonthlyPayment * monthsWithExtra;
|
|
|
|
// Handle missing salary data
|
|
let salary = salaryData && salaryData[0]?.value ? salaryData[0].value : null;
|
|
let netGain = 'N/A';
|
|
let monthlySalary = 'N/A';
|
|
|
|
if (salary) {
|
|
// Calculate net gain
|
|
const totalSalary = salary * earningHorizon;
|
|
const currentSalaryEarnings = currentSalary * earningHorizon * Math.pow(1.03, earningHorizon); // 3% growth
|
|
netGain = (totalSalary - totalLoanCost - currentSalaryEarnings).toFixed(2);
|
|
|
|
// Monthly salary
|
|
monthlySalary = (salary / 12).toFixed(2);
|
|
}
|
|
|
|
return {
|
|
...school,
|
|
tuition,
|
|
monthlyPayment: minimumMonthlyPayment.toFixed(2),
|
|
totalMonthlyPayment: extraMonthlyPayment.toFixed(2), // Add total payment including extra
|
|
totalLoanCost: totalLoanCost.toFixed(2),
|
|
netGain,
|
|
monthlySalary,
|
|
};
|
|
});
|
|
|
|
setResults(schoolResults);
|
|
};
|
|
|
|
|
|
return (
|
|
<div>
|
|
<h2>Loan Repayment and ROI Analysis</h2>
|
|
|
|
<div>
|
|
<label>
|
|
Tuition Type:
|
|
<select
|
|
value={tuitionType}
|
|
onChange={(e) => setTuitionType(e.target.value)}
|
|
>
|
|
<option value="inState">In-State</option>
|
|
<option value="outOfState">Out-of-State</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label>
|
|
Interest Rate (%):
|
|
<input
|
|
type="number"
|
|
value={interestRate}
|
|
onChange={(e) => setInterestRate(Number(e.target.value))}
|
|
onFocus={(e) => e.target.select()}
|
|
min="0"
|
|
/>
|
|
</label>
|
|
|
|
<label>
|
|
Loan Term (Years):
|
|
<input
|
|
type="number"
|
|
value={loanTerm}
|
|
onChange={(e) => setLoanTerm(Number(e.target.value))}
|
|
onFocus={(e) => e.target.select()}
|
|
min="0"
|
|
/>
|
|
</label>
|
|
|
|
<label>
|
|
Extra Monthly Payment ($):
|
|
<input
|
|
type="number"
|
|
value={extraPayment}
|
|
onChange={(e) => setExtraPayment(Number(e.target.value))}
|
|
onFocus={(e) => e.target.select()}
|
|
min="0"
|
|
/>
|
|
</label>
|
|
|
|
<label>
|
|
Current Salary (Gross Annual $):
|
|
<input
|
|
type="number"
|
|
value={currentSalary}
|
|
onChange={(e) => setCurrentSalary(e.target.value)}
|
|
onFocus={(e) => e.target.select()}
|
|
min="0"
|
|
/>
|
|
</label>
|
|
|
|
<button onClick={calculateLoanDetails} disabled={loading}>
|
|
{loading ? 'Calculating...' : 'Calculate'}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Error Message */}
|
|
{error && <p style={{ color: 'red' }}>{error}</p>}
|
|
|
|
{/* Results Display */}
|
|
{results.length > 0 && (
|
|
<div>
|
|
<h3>Comparison by School</h3>
|
|
{results.map((result, index) => (
|
|
<div key={index}>
|
|
<h4>{result.schoolName}</h4>
|
|
<p>Total Tuition: ${result.tuition}</p>
|
|
<p>Monthly Payment: ${result.monthlyPayment}</p>
|
|
<p>Total Monthly Payment (with extra): ${result.totalMonthlyPayment}</p>
|
|
<p>Total Loan Cost: ${result.totalLoanCost}</p>
|
|
<p>Net Gain: {result.netGain}</p>
|
|
<p>Monthly Salary (Gross): {result.monthlySalary}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Salary Warning */}
|
|
{!salaryData || salaryData.length === 0 ? (
|
|
<p style={{ color: 'red' }}>Salary data is not available for this profession. Loan calculations are limited.</p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
|
|
}
|
|
|
|
export default LoanRepayment;
|