new strategy human algo
This commit is contained in:
parent
5544c2f69f
commit
6a6557c3bf
@ -8,7 +8,6 @@ import * as z from "zod";
|
|||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -66,23 +65,27 @@ const formSchema = z.object({
|
|||||||
// Type for form values
|
// Type for form values
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
interface YearlyData {
|
||||||
|
age: number;
|
||||||
|
year: number;
|
||||||
|
balance: number;
|
||||||
|
phase: "accumulation" | "retirement";
|
||||||
|
monthlyAllowance: number;
|
||||||
|
fireNumber: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
interface CalculationResult {
|
interface CalculationResult {
|
||||||
fireNumber: number | null;
|
fireNumber: number | null;
|
||||||
retirementAge: number | null;
|
retirementAge: number | null;
|
||||||
inflationAdjustedAllowance: number | null;
|
inflationAdjustedAllowance: number | null;
|
||||||
retirementYears: number | null;
|
retirementYears: number | null;
|
||||||
error?: string;
|
error?: string;
|
||||||
yearlyData?: Array<{
|
yearlyData?: Record<string, YearlyData>;
|
||||||
age: number;
|
|
||||||
year: number;
|
|
||||||
balance: number;
|
|
||||||
phase: "accumulation" | "retirement";
|
|
||||||
}>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FireCalculatorForm() {
|
export default function FireCalculatorForm() {
|
||||||
const [result, setResult] = useState<CalculationResult | null>(null);
|
const [result, setResult] = useState<CalculationResult | null>(null);
|
||||||
const currentYear = new Date().getFullYear();
|
const irlYear = new Date().getFullYear();
|
||||||
|
|
||||||
// Initialize form with default values
|
// Initialize form with default values
|
||||||
const form = useForm<FormValues>({
|
const form = useForm<FormValues>({
|
||||||
@ -100,263 +103,103 @@ export default function FireCalculatorForm() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(values: FormValues) {
|
function onSubmit(values: FormValues) {
|
||||||
|
/*
|
||||||
|
PSEUDOCODE
|
||||||
|
1. calculate all balances if no retirement.
|
||||||
|
2. calculate all required FIRE numbers for each possible year of retirement for the selected strategy.
|
||||||
|
2.1 calculate the monthly allowance for each year of retirement for all years, fire number is these but cumulative.
|
||||||
|
3. binary search the crossover
|
||||||
|
4. calculate new balance for each year of retirement
|
||||||
|
5. graph balance, balance if no retirement, fire numbers, allowances
|
||||||
|
*/
|
||||||
setResult(null); // Reset previous results
|
setResult(null); // Reset previous results
|
||||||
|
|
||||||
const startingCapital = values.startingCapital;
|
const startingCapital = values.startingCapital;
|
||||||
const monthlySavings = values.monthlySavings;
|
const monthlySavings = values.monthlySavings;
|
||||||
const currentAge = values.currentAge;
|
const age = values.currentAge;
|
||||||
const annualGrowthRate = values.cagr / 100;
|
const annualGrowthRate = 1 + values.cagr / 100;
|
||||||
const initialMonthlyAllowance = values.desiredMonthlyAllowance;
|
const initialMonthlyAllowance = values.desiredMonthlyAllowance;
|
||||||
const annualInflation = values.inflationRate / 100;
|
const annualInflation = 1 + values.inflationRate / 100;
|
||||||
const lifeExpectancy = values.lifeExpectancy;
|
const ageOfDeath = values.lifeExpectancy;
|
||||||
const retirementStrategy = values.retirementStrategy;
|
const retirementStrategy = values.retirementStrategy;
|
||||||
|
|
||||||
const monthlyGrowthRate = Math.pow(1 + annualGrowthRate, 1 / 12) - 1;
|
|
||||||
const monthlyInflationRate = Math.pow(1 + annualInflation, 1 / 12) - 1;
|
|
||||||
const maxIterations = 100; // Adjusted max iterations for age limit
|
|
||||||
|
|
||||||
let requiredCapital: number | null = null;
|
let requiredCapital: number | null = null;
|
||||||
let retirementAge: number | null = null;
|
let retirementAge: number | null = null;
|
||||||
let finalInflationAdjustedAllowance: number | null = null;
|
let finalInflationAdjustedAllowance: number | null = null;
|
||||||
let canRetire = false;
|
|
||||||
let errorMessage: string | undefined = undefined;
|
|
||||||
|
|
||||||
// Array to store yearly data for the chart
|
// Array to store yearly data for the chart with initial value
|
||||||
const yearlyData: CalculationResult["yearlyData"] = [];
|
const yearlyData: Record<number, YearlyData> = {
|
||||||
yearlyData.push({
|
[irlYear]: {
|
||||||
age: currentAge,
|
age: age,
|
||||||
year: currentYear,
|
year: irlYear,
|
||||||
balance: startingCapital,
|
balance: startingCapital,
|
||||||
phase: "accumulation",
|
phase: "accumulation",
|
||||||
});
|
monthlyAllowance: initialMonthlyAllowance,
|
||||||
|
fireNumber: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// calculate all balances if no retirement
|
||||||
|
for (let year = irlYear + 1; year <= irlYear + ageOfDeath - age; year++) {
|
||||||
|
const previousYearData = yearlyData[year - 1];
|
||||||
|
if (!previousYearData) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
yearlyData[year] = {
|
||||||
|
age: age + year - irlYear,
|
||||||
|
year: year,
|
||||||
|
balance:
|
||||||
|
previousYearData.balance * annualGrowthRate + monthlySavings * 12,
|
||||||
|
phase: "accumulation",
|
||||||
|
monthlyAllowance: previousYearData.monthlyAllowance * annualInflation,
|
||||||
|
fireNumber: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// calculate FIRE numbers based on allowances
|
||||||
|
for (let year = irlYear + ageOfDeath - age; year >= irlYear; year--) {
|
||||||
|
const yearData = yearlyData[year];
|
||||||
|
if (!yearData) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
yearData.fireNumber =
|
||||||
|
(yearlyData[year + 1]?.fireNumber ?? 0) +
|
||||||
|
12 * yearData.monthlyAllowance;
|
||||||
|
}
|
||||||
|
|
||||||
let currentCapital = startingCapital;
|
// calculate new balance and retirement age
|
||||||
let age = currentAge;
|
for (let year = irlYear; year <= irlYear + ageOfDeath - age; year++) {
|
||||||
let monthlyAllowance = initialMonthlyAllowance;
|
const yearData = yearlyData[year];
|
||||||
|
const previousYearData = yearlyData[year - 1];
|
||||||
|
if (!yearData?.fireNumber) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!previousYearData) {
|
||||||
|
yearData.monthlyAllowance = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (yearData.balance > yearData.fireNumber) {
|
||||||
|
retirementAge ??= yearData.age;
|
||||||
|
requiredCapital ??= yearData.balance;
|
||||||
|
finalInflationAdjustedAllowance ??= yearData.monthlyAllowance;
|
||||||
|
yearData.phase = "retirement";
|
||||||
|
yearData.balance =
|
||||||
|
previousYearData.balance * annualGrowthRate -
|
||||||
|
yearData.monthlyAllowance * 12;
|
||||||
|
} else {
|
||||||
|
yearData.monthlyAllowance = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Calculation Logic based on Strategy ---
|
// --- Calculation Logic based on Strategy ---
|
||||||
|
|
||||||
if (retirementStrategy === "4% Rule") {
|
|
||||||
// --- 4% Rule Calculation ---
|
|
||||||
requiredCapital = (initialMonthlyAllowance * 12) / 0.04;
|
|
||||||
|
|
||||||
// Simulate accumulation until the 4% rule target is met
|
|
||||||
while (age < lifeExpectancy) {
|
|
||||||
if (currentCapital >= requiredCapital) {
|
|
||||||
canRetire = true;
|
|
||||||
retirementAge = age;
|
|
||||||
finalInflationAdjustedAllowance = monthlyAllowance;
|
|
||||||
break; // Found retirement age
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate one year of saving and growth
|
|
||||||
for (let month = 0; month < 12; month++) {
|
|
||||||
currentCapital += monthlySavings;
|
|
||||||
currentCapital *= 1 + monthlyGrowthRate;
|
|
||||||
monthlyAllowance *= 1 + monthlyInflationRate; // Keep track of inflation-adjusted allowance
|
|
||||||
}
|
|
||||||
age++;
|
|
||||||
|
|
||||||
yearlyData.push({
|
|
||||||
age: age,
|
|
||||||
year: currentYear + (age - currentAge),
|
|
||||||
balance: Math.round(currentCapital),
|
|
||||||
phase: "accumulation",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (age >= lifeExpectancy) break; // Stop if life expectancy is reached
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!canRetire) {
|
|
||||||
errorMessage =
|
|
||||||
"Cannot reach FIRE goal (4% Rule) before life expectancy.";
|
|
||||||
requiredCapital = null; // Cannot retire, so no specific FIRE number applies this way
|
|
||||||
} else if (retirementAge !== null) {
|
|
||||||
// Simulate retirement phase for chart data (using 4% withdrawal adjusted for inflation)
|
|
||||||
let simulationCapital = currentCapital;
|
|
||||||
let simulationAge = retirementAge;
|
|
||||||
|
|
||||||
// Mark retirement phase in existing data
|
|
||||||
yearlyData.forEach((data) => {
|
|
||||||
if (data.age >= retirementAge!) {
|
|
||||||
data.phase = "retirement";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
while (simulationAge < lifeExpectancy) {
|
|
||||||
let yearlyWithdrawal = requiredCapital * 0.04; // Initial 4%
|
|
||||||
// Adjust for inflation annually from retirement start
|
|
||||||
yearlyWithdrawal *= Math.pow(
|
|
||||||
1 + annualInflation,
|
|
||||||
simulationAge - retirementAge,
|
|
||||||
);
|
|
||||||
const monthlyWithdrawal = yearlyWithdrawal / 12;
|
|
||||||
|
|
||||||
for (let month = 0; month < 12; month++) {
|
|
||||||
simulationCapital -=
|
|
||||||
monthlyWithdrawal * Math.pow(1 + monthlyInflationRate, month); // Approximate intra-year inflation on withdrawal
|
|
||||||
simulationCapital *= 1 + monthlyGrowthRate;
|
|
||||||
}
|
|
||||||
simulationAge++;
|
|
||||||
|
|
||||||
yearlyData.push({
|
|
||||||
age: simulationAge,
|
|
||||||
year: currentYear + (simulationAge - currentAge),
|
|
||||||
balance: Math.round(simulationCapital),
|
|
||||||
phase: "retirement",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// --- Depletion and Maintenance Calculation (Simulation-based) ---
|
|
||||||
let iterations = 0;
|
|
||||||
|
|
||||||
while (age < lifeExpectancy && iterations < maxIterations) {
|
|
||||||
// Simulate one year of saving and growth
|
|
||||||
for (let month = 0; month < 12; month++) {
|
|
||||||
currentCapital += monthlySavings;
|
|
||||||
currentCapital *= 1 + monthlyGrowthRate;
|
|
||||||
monthlyAllowance *= 1 + monthlyInflationRate;
|
|
||||||
}
|
|
||||||
age++;
|
|
||||||
iterations++;
|
|
||||||
|
|
||||||
yearlyData.push({
|
|
||||||
age: age,
|
|
||||||
year: currentYear + (age - currentAge),
|
|
||||||
balance: Math.round(currentCapital),
|
|
||||||
phase: "accumulation",
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- Check if retirement is possible at this age ---
|
|
||||||
let testCapital = currentCapital;
|
|
||||||
let testAge = age;
|
|
||||||
let testAllowance = monthlyAllowance;
|
|
||||||
let isSufficient = true;
|
|
||||||
|
|
||||||
// Simulate retirement phase to check sufficiency
|
|
||||||
while (testAge < lifeExpectancy) {
|
|
||||||
const yearlyStartCapital = testCapital;
|
|
||||||
|
|
||||||
for (let month = 0; month < 12; month++) {
|
|
||||||
const withdrawal = testAllowance;
|
|
||||||
testCapital -= withdrawal;
|
|
||||||
const growth = testCapital * monthlyGrowthRate;
|
|
||||||
testCapital += growth; // Apply growth *after* withdrawal for the month
|
|
||||||
testAllowance *= 1 + monthlyInflationRate; // Inflate allowance for next month
|
|
||||||
}
|
|
||||||
testAge++;
|
|
||||||
|
|
||||||
if (testCapital <= 0) {
|
|
||||||
// Depleted capital before life expectancy
|
|
||||||
isSufficient = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retirementStrategy === "Maintenance") {
|
|
||||||
// Maintenance check: Withdrawal should not exceed growth for the year
|
|
||||||
// Use average capital for a slightly more stable check? Or end-of-year growth vs start-of-year withdrawal?
|
|
||||||
// Let's check if end-of-year capital is less than start-of-year capital
|
|
||||||
if (testCapital < yearlyStartCapital) {
|
|
||||||
isSufficient = false;
|
|
||||||
break; // Capital decreased, maintenance failed
|
|
||||||
}
|
|
||||||
// Alternative check: yearlyWithdrawal > yearlyGrowth
|
|
||||||
// if (yearlyWithdrawal > yearlyGrowth) {
|
|
||||||
// isSufficient = false;
|
|
||||||
// break; // Withdrawals exceed growth, maintenance failed
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
} // End retirement simulation check
|
|
||||||
|
|
||||||
if (isSufficient) {
|
|
||||||
canRetire = true;
|
|
||||||
retirementAge = age;
|
|
||||||
requiredCapital = currentCapital; // The capital needed at this point
|
|
||||||
finalInflationAdjustedAllowance = monthlyAllowance; // Allowance level at retirement
|
|
||||||
break; // Found retirement age
|
|
||||||
}
|
|
||||||
} // End accumulation simulation loop
|
|
||||||
|
|
||||||
if (!canRetire) {
|
|
||||||
errorMessage = `Cannot reach FIRE goal (${retirementStrategy}) before life expectancy or within ${maxIterations} years.`;
|
|
||||||
requiredCapital = null;
|
|
||||||
} else if (retirementAge !== null) {
|
|
||||||
// Simulate the actual retirement phase for chart data if retirement is possible
|
|
||||||
let simulationCapital = requiredCapital!;
|
|
||||||
let simulationAge = retirementAge;
|
|
||||||
let simulationAllowance = finalInflationAdjustedAllowance!;
|
|
||||||
|
|
||||||
// Mark retirement phase in existing data
|
|
||||||
yearlyData.forEach((data) => {
|
|
||||||
if (data.age >= retirementAge!) {
|
|
||||||
data.phase = "retirement";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simulate remaining years until life expectancy
|
|
||||||
while (simulationAge < lifeExpectancy) {
|
|
||||||
for (let month = 0; month < 12; month++) {
|
|
||||||
simulationCapital -= simulationAllowance;
|
|
||||||
simulationCapital *= 1 + monthlyGrowthRate;
|
|
||||||
simulationAllowance *= 1 + monthlyInflationRate;
|
|
||||||
}
|
|
||||||
simulationAge++;
|
|
||||||
|
|
||||||
// Ensure capital doesn't go below zero for chart visibility in Depletion
|
|
||||||
const displayBalance =
|
|
||||||
retirementStrategy === "Depletion"
|
|
||||||
? Math.max(0, simulationCapital)
|
|
||||||
: simulationCapital;
|
|
||||||
|
|
||||||
yearlyData.push({
|
|
||||||
age: simulationAge,
|
|
||||||
year: currentYear + (simulationAge - currentAge),
|
|
||||||
balance: Math.round(displayBalance),
|
|
||||||
phase: "retirement",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // End Depletion/Maintenance logic
|
|
||||||
|
|
||||||
// --- Set Final Result ---
|
// --- Set Final Result ---
|
||||||
if (
|
setResult({
|
||||||
canRetire &&
|
fireNumber: requiredCapital,
|
||||||
retirementAge !== null &&
|
retirementAge: retirementAge,
|
||||||
requiredCapital !== null &&
|
inflationAdjustedAllowance: finalInflationAdjustedAllowance,
|
||||||
finalInflationAdjustedAllowance !== null
|
retirementYears: ageOfDeath - retirementAge,
|
||||||
) {
|
yearlyData: Object.values(yearlyData),
|
||||||
// Ensure yearlyData covers up to lifeExpectancy if retirement happens early
|
error: undefined,
|
||||||
const lastDataYear =
|
});
|
||||||
yearlyData[yearlyData.length - 1]?.year ?? currentYear;
|
|
||||||
const expectedEndYear = currentYear + (lifeExpectancy - currentAge);
|
|
||||||
if (lastDataYear < expectedEndYear) {
|
|
||||||
// Need to continue simulation purely for charting if the main calc stopped early
|
|
||||||
// (This might already be covered by the post-retirement simulation loops added above)
|
|
||||||
console.warn(
|
|
||||||
"Chart data might not extend fully to life expectancy in some scenarios.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
setResult({
|
|
||||||
fireNumber: requiredCapital,
|
|
||||||
retirementAge: retirementAge,
|
|
||||||
inflationAdjustedAllowance: finalInflationAdjustedAllowance,
|
|
||||||
retirementYears: lifeExpectancy - retirementAge,
|
|
||||||
yearlyData: yearlyData,
|
|
||||||
error: undefined,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setResult({
|
|
||||||
fireNumber: null,
|
|
||||||
retirementAge: null,
|
|
||||||
inflationAdjustedAllowance: null,
|
|
||||||
retirementYears: null,
|
|
||||||
yearlyData: yearlyData, // Show accumulation data even if goal not reached
|
|
||||||
error:
|
|
||||||
errorMessage ?? "Calculation failed to find a retirement scenario.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to format currency without specific symbols
|
// Helper function to format currency without specific symbols
|
||||||
@ -615,7 +458,7 @@ export default function FireCalculatorForm() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{result?.yearlyData && result.yearlyData.length > 0 && (
|
{result?.yearlyData && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Financial Projection</CardTitle>
|
<CardTitle>Financial Projection</CardTitle>
|
||||||
@ -633,6 +476,10 @@ export default function FireCalculatorForm() {
|
|||||||
},
|
},
|
||||||
fireNumber: {
|
fireNumber: {
|
||||||
label: "FIRE Number",
|
label: "FIRE Number",
|
||||||
|
color: "var(--chart-2)",
|
||||||
|
},
|
||||||
|
realBalance: {
|
||||||
|
label: "Real Balance",
|
||||||
color: "var(--chart-3)",
|
color: "var(--chart-3)",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@ -669,7 +516,9 @@ export default function FireCalculatorForm() {
|
|||||||
return (
|
return (
|
||||||
<div className="bg-background border p-2 shadow-sm">
|
<div className="bg-background border p-2 shadow-sm">
|
||||||
<p className="font-medium">{`Year: ${data.year.toString()} (Age: ${data.age.toString()})`}</p>
|
<p className="font-medium">{`Year: ${data.year.toString()} (Age: ${data.age.toString()})`}</p>
|
||||||
<p className="text-primary">{`Balance: ${formatNumber(data.balance)}`}</p>
|
<p className="text-chart-1">{`Balance: ${formatNumber(data.balance)}`}</p>
|
||||||
|
<p className="text-chart-2">{`FIRE number: ${formatNumber(data.fireNumber)}`}</p>
|
||||||
|
<p className="text-chart-4">{`Monthly allowance: ${formatNumber(data.monthlyAllowance)}`}</p>
|
||||||
{result.fireNumber !== null && (
|
{result.fireNumber !== null && (
|
||||||
<p className="text-destructive">{`Target FIRE Number: ${formatNumber(result.fireNumber)}`}</p>
|
<p className="text-destructive">{`Target FIRE Number: ${formatNumber(result.fireNumber)}`}</p>
|
||||||
)}
|
)}
|
||||||
@ -693,6 +542,42 @@ export default function FireCalculatorForm() {
|
|||||||
stopOpacity={0.1}
|
stopOpacity={0.1}
|
||||||
/>
|
/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="fillFireNumber"
|
||||||
|
x1="0"
|
||||||
|
y1="0"
|
||||||
|
x2="0"
|
||||||
|
y2="1"
|
||||||
|
>
|
||||||
|
<stop
|
||||||
|
offset="5%"
|
||||||
|
stopColor="var(--chart-2)"
|
||||||
|
stopOpacity={0.8}
|
||||||
|
/>
|
||||||
|
<stop
|
||||||
|
offset="95%"
|
||||||
|
stopColor="var(--chart-2)"
|
||||||
|
stopOpacity={0.1}
|
||||||
|
/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="fillAllowance"
|
||||||
|
x1="0"
|
||||||
|
y1="0"
|
||||||
|
x2="0"
|
||||||
|
y2="1"
|
||||||
|
>
|
||||||
|
<stop
|
||||||
|
offset="5%"
|
||||||
|
stopColor="var(--chart-4)"
|
||||||
|
stopOpacity={0.8}
|
||||||
|
/>
|
||||||
|
<stop
|
||||||
|
offset="95%"
|
||||||
|
stopColor="var(--chart-4)"
|
||||||
|
stopOpacity={0.1}
|
||||||
|
/>
|
||||||
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<Area
|
<Area
|
||||||
type="monotone"
|
type="monotone"
|
||||||
@ -703,6 +588,24 @@ export default function FireCalculatorForm() {
|
|||||||
fillOpacity={0.4}
|
fillOpacity={0.4}
|
||||||
activeDot={{ r: 6 }}
|
activeDot={{ r: 6 }}
|
||||||
/>
|
/>
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="fireNumber"
|
||||||
|
name="fireNumber"
|
||||||
|
stroke="var(--chart-2)"
|
||||||
|
fill="url(#fillFireNumber)"
|
||||||
|
fillOpacity={0.4}
|
||||||
|
activeDot={{ r: 6 }}
|
||||||
|
/>
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="monthlyAllowance"
|
||||||
|
name="allowance"
|
||||||
|
stroke="var(--chart-4)"
|
||||||
|
fill="url(#fillAllowance)"
|
||||||
|
fillOpacity={0.4}
|
||||||
|
activeDot={{ r: 6 }}
|
||||||
|
/>
|
||||||
{result.fireNumber && (
|
{result.fireNumber && (
|
||||||
<ReferenceLine
|
<ReferenceLine
|
||||||
y={result.fireNumber}
|
y={result.fireNumber}
|
||||||
@ -718,7 +621,7 @@ export default function FireCalculatorForm() {
|
|||||||
{result.retirementAge && (
|
{result.retirementAge && (
|
||||||
<ReferenceLine
|
<ReferenceLine
|
||||||
x={
|
x={
|
||||||
currentYear +
|
irlYear +
|
||||||
(result.retirementAge - form.getValues().currentAge)
|
(result.retirementAge - form.getValues().currentAge)
|
||||||
}
|
}
|
||||||
stroke="var(--chart-2)"
|
stroke="var(--chart-2)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user