diff --git a/app/interactives/retirement-calculator/page.tsx b/app/interactives/retirement-calculator/page.tsx index 8670c16..878d8df 100644 --- a/app/interactives/retirement-calculator/page.tsx +++ b/app/interactives/retirement-calculator/page.tsx @@ -3,12 +3,13 @@ import { useState, useMemo } from "react" import ThemeToggle from "@/app/lib/theme-toggle"; import { Tabs, TabsList, TabsTrigger } from "@/app/ui/components/tabs"; -import { BiSolidError, BiSolidDownArrow, BiSolidUpArrow } from "react-icons/bi"; +import { BiSolidError } from "react-icons/bi"; interface CalculatorInputs { annualSpending: number retirementLength: number - expectedReturn: number + expectedReturnDuringRetirement: number // Used for Required Balance calculation + expectedReturnBeforeRetirement: number // Used ONLY for Annual Savings calculation currentSavings: number yearsToRetirement: number } @@ -25,9 +26,10 @@ function formatCurrency(value: number): string { const defaultInputs: CalculatorInputs = { annualSpending: 60000, retirementLength: 25, - expectedReturn: 5, + expectedReturnDuringRetirement: 5, + expectedReturnBeforeRetirement: 0, currentSavings: 0, - yearsToRetirement: 30, + yearsToRetirement: 0, } export default function RetirementCalculator() { @@ -37,34 +39,35 @@ export default function RetirementCalculator() { const [frozenRequiredBalance, setFrozenRequiredBalance] = useState(0) const results = useMemo(() => { - const realReturn = inputs.expectedReturn / 100 + const realReturnDuringRetirement = inputs.expectedReturnDuringRetirement / 100 // ← CHANGED + const realReturnBeforeRetirement = inputs.expectedReturnBeforeRetirement / 100 // ← ADDED // Calculate Required Balance based on retirement spending needs let calculatedRequiredBalance: number - if (realReturn <= 0) { + if (realReturnDuringRetirement <= 0) { // ← CHANGED calculatedRequiredBalance = inputs.annualSpending * inputs.retirementLength } else { calculatedRequiredBalance = inputs.annualSpending * - ((1 - Math.pow(1 + realReturn, -inputs.retirementLength)) / realReturn) + ((1 - Math.pow(1 + realReturnDuringRetirement, -inputs.retirementLength)) / realReturnDuringRetirement) // ← CHANGED } // Use frozen value for Annual Savings tab, fresh calculation for Required Balance tab const requiredBalance = activeTab === "savings" ? frozenRequiredBalance : calculatedRequiredBalance // Now calculate target balance and annual savings based on the appropriate required balance - const FV_currentSavings = inputs.currentSavings * Math.pow(1 + realReturn, inputs.yearsToRetirement) + const FV_currentSavings = inputs.currentSavings * Math.pow(1 + realReturnBeforeRetirement, inputs.yearsToRetirement) // ← CHANGED const targetBalance = Math.max(0, requiredBalance - FV_currentSavings) let annualSavings: number - if (realReturn <= 0 || inputs.yearsToRetirement <= 0) { + if (realReturnBeforeRetirement <= 0 || inputs.yearsToRetirement <= 0) { // ← CHANGED annualSavings = inputs.yearsToRetirement > 0 ? targetBalance / inputs.yearsToRetirement : 0 } else { annualSavings = targetBalance * - (realReturn / (Math.pow(1 + realReturn, inputs.yearsToRetirement) - 1)) + (realReturnBeforeRetirement / (Math.pow(1 + realReturnBeforeRetirement, inputs.yearsToRetirement) - 1)) // ← CHANGED } return { @@ -78,20 +81,34 @@ export default function RetirementCalculator() { const updateInput = (key: keyof CalculatorInputs, value: string) => { const numValue = parseFloat(value) || 0 setInputs((prev) => ({ ...prev, [key]: numValue })) - // Only invalidate the calculation when balance-tab-only inputs change - // while on the balance tab. Shared inputs (expectedReturn, retirementLength) - // and savings-tab inputs should not reset when on the savings tab. - const savingsTabKeys: Array = ["currentSavings", "yearsToRetirement"] - const sharedKeys: Array = ["expectedReturn", "retirementLength"] - if (savingsTabKeys.includes(key)) return - if (sharedKeys.includes(key) && activeTab === "savings") return + + // Inputs that ONLY affect the savings calculation (don't reset isCalculated) + const savingsOnlyInputs: Array = [ + "currentSavings", + "yearsToRetirement", + "expectedReturnBeforeRetirement" // ← ADDED + ] + + // Inputs that affect the Required Balance calculation + const balanceInputs: Array = [ + "annualSpending", + "retirementLength", + "expectedReturnDuringRetirement" // ← ADDED + ] + + // Don't reset if changing savings-only inputs + if (savingsOnlyInputs.includes(key)) return + + // Don't reset if on savings tab and changing balance inputs + if (activeTab === "savings" && balanceInputs.includes(key)) return + setIsCalculated(false) } const handleCalculate = () => { setIsCalculated(true) // FREEZE the required balance when Calculate is clicked - const realReturn = inputs.expectedReturn / 100 + const realReturn = inputs.expectedReturnDuringRetirement / 100 // ← CHANGED let calculatedRequiredBalance: number if (realReturn <= 0) { @@ -160,13 +177,6 @@ export default function RetirementCalculator() { )} - {isCalculated && ( -
-

Target Retirement Savings

-

{isCalculated ? formatCurrency(results.targetBalance) : "—"}

-
- )} - {activeTab === "balance" ? ( <> {/* Required Balance Result */} @@ -183,36 +193,6 @@ export default function RetirementCalculator() { onChange={(e) => updateInput("annualSpending", e.target.value)} className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -
- - -

How much you plan to withdraw each year. @@ -235,36 +215,6 @@ export default function RetirementCalculator() { onChange={(e) => updateInput("currentSavings", e.target.value)} className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -

- - -

How much you have already saved for retirement. @@ -285,36 +235,6 @@ export default function RetirementCalculator() { onChange={(e) => updateInput("yearsToRetirement", e.target.value)} className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -

- - -

How many years until you plan to retire. @@ -336,94 +256,55 @@ export default function RetirementCalculator() { onChange={(e) => updateInput("retirementLength", e.target.value)} className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" /> -

- - -

How many years your retirement will last.

} - {/* Expected Return Input */} -
- -
- updateInput("expectedReturn", e.target.value)} - className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" - /> -
- - + {/* Expected Return Input - BALANCE TAB */} + {activeTab === "balance" && ( +
+ +
+ updateInput("expectedReturnDuringRetirement", e.target.value)} + className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + />
+

+ Annual investment return rate during retirement. +

-

- {activeTab === "balance" - ? "Annual investment return rate during retirement." - : "Annual investment return rate before retirement." - } -

-
+ )} + +{/* Expected Return Input - SAVINGS TAB */} +{activeTab === "savings" && ( +
+ +
+ updateInput("expectedReturnBeforeRetirement", e.target.value)} + className="w-full pl-4 pr-16 py-3 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none transition [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+

+ Annual investment return rate before retirement. +

+
+)} {/* Calculate and Reset Buttons */} @@ -450,9 +331,6 @@ export default function RetirementCalculator() { {/* Right Column - Results */}
-

Results

- -

Your calculated retirement figures

{activeTab === "balance" ? ( <> {/* Required Balance Result */} @@ -463,7 +341,9 @@ export default function RetirementCalculator() {

{isCalculated ? formatCurrency(results.requiredBalance) : "—"}

-

This estimates the lump sum needed at retirement to fund your annual spending for {inputs.retirementLength} years, assuming a {inputs.expectedReturn}% annual return during retirement.

+

+ This estimates the lump sum needed at retirement to fund your annual spending for {inputs.retirementLength} years, assuming a {inputs.expectedReturnDuringRetirement}% annual return during retirement. +

) : ( @@ -471,15 +351,25 @@ export default function RetirementCalculator() { {/* Annual Savings Result */}

Required Retirement Balance

-

{isCalculated ? formatCurrency(results.requiredBalance) : "—"}

-

This estimates the lump sum needed at retirement to fund your annual spending for {inputs.retirementLength} years, assuming a {inputs.expectedReturn}% annual return during retirement.

+

+ {isCalculated ? formatCurrency(results.requiredBalance) : "—"} +

+

+ This estimates the lump sum needed at retirement to fund your annual spending + for {inputs.retirementLength} years, assuming a {inputs.expectedReturnDuringRetirement}% + annual return during retirement. +

Required Annual Savings

{formatCurrency(results.annualSavings)}

-

Amount to save each year over {inputs.yearsToRetirement} years to reach your target balance, assuming a {inputs.expectedReturn}% annual return before retirement.

+

+ Amount to save each year over {inputs.yearsToRetirement} years to reach your + target balance, assuming a {inputs.expectedReturnBeforeRetirement}% annual + return before retirement. +

)}