Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions app/interactives/retirement-calculator/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,39 +34,46 @@ export default function RetirementCalculator() {
const [activeTab, setActiveTab] = useState<"balance" | "savings">("balance")
const [inputs, setInputs] = useState<CalculatorInputs>(defaultInputs)
const [isCalculated, setIsCalculated] = useState(false)
const [frozenRequiredBalance, setFrozenRequiredBalance] = useState<number>(0)

const results = useMemo(() => {
const realReturn = (inputs.expectedReturn) / 100
const realReturn = inputs.expectedReturn / 100

// Calculate Required Balance based on retirement spending needs
let calculatedRequiredBalance: number

if (realReturn <= 0) {
const requiredBalance = inputs.annualSpending * inputs.retirementLength
const FV_currentSavings = inputs.currentSavings * Math.pow(1 + realReturn, inputs.yearsToRetirement)
const targetBalance = Math.max(0, requiredBalance - FV_currentSavings)
return {
requiredBalance,
targetBalance,
annualSavings: inputs.yearsToRetirement > 0 ? targetBalance / inputs.yearsToRetirement : 0,
monthlySavings: inputs.yearsToRetirement > 0 ? targetBalance / inputs.yearsToRetirement / 12 : 0,
}
calculatedRequiredBalance = inputs.annualSpending * inputs.retirementLength
} else {
calculatedRequiredBalance =
inputs.annualSpending *
((1 - Math.pow(1 + realReturn, -inputs.retirementLength)) / realReturn)
}

const requiredBalance =
inputs.annualSpending *
((1 - Math.pow(1 + realReturn, -inputs.retirementLength)) / realReturn)
// 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 targetBalance = Math.max(0, requiredBalance - FV_currentSavings)
const annualSavings =
targetBalance *
(realReturn / (Math.pow(1 + realReturn, inputs.yearsToRetirement) - 1))

let annualSavings: number

if (realReturn <= 0 || inputs.yearsToRetirement <= 0) {
annualSavings = inputs.yearsToRetirement > 0 ? targetBalance / inputs.yearsToRetirement : 0
} else {
annualSavings =
targetBalance *
(realReturn / (Math.pow(1 + realReturn, inputs.yearsToRetirement) - 1))
}

return {
requiredBalance: Math.round(requiredBalance),
targetBalance: Math.round(targetBalance),
annualSavings: Math.round(annualSavings),
monthlySavings: Math.round(annualSavings / 12),
}
}, [inputs])
}, [inputs, activeTab, frozenRequiredBalance])

const updateInput = (key: keyof CalculatorInputs, value: string) => {
const numValue = parseFloat(value) || 0
Expand All @@ -83,12 +90,26 @@ export default function RetirementCalculator() {

const handleCalculate = () => {
setIsCalculated(true)
// FREEZE the required balance when Calculate is clicked
const realReturn = inputs.expectedReturn / 100
let calculatedRequiredBalance: number

if (realReturn <= 0) {
calculatedRequiredBalance = inputs.annualSpending * inputs.retirementLength
} else {
calculatedRequiredBalance =
inputs.annualSpending *
((1 - Math.pow(1 + realReturn, -inputs.retirementLength)) / realReturn)
}

setFrozenRequiredBalance(Math.round(calculatedRequiredBalance))
}

const handleReset = () => {
setInputs(defaultInputs)
setIsCalculated(false)
setActiveTab("balance")
setFrozenRequiredBalance(0)
}

return (
Expand All @@ -104,6 +125,10 @@ export default function RetirementCalculator() {
<Tabs value={activeTab} onValueChange={(v) => {
if (v === "savings" && !isCalculated) return
setActiveTab(v as "balance" | "savings")
if (v === "balance") {
setIsCalculated(false) // This allows recalculation on the balance tab
setFrozenRequiredBalance(0) // Clear frozen value when returning to balance tab
}
}} className="mb-10">
<TabsList className="grid w-full grid-rows-1 sm:grid-cols-2 p-0 gap-4">
<TabsTrigger
Expand Down Expand Up @@ -341,7 +366,7 @@ export default function RetirementCalculator() {
<BiSolidDownArrow size={24} />
</button>
</div>
</div>
</div>
<p className="text-xs">
How many years your retirement will last.
</p>
Expand Down