diff --git a/app/interactives/mortgate-calculator-suite/page.tsx b/app/interactives/mortgate-calculator-suite/page.tsx new file mode 100644 index 0000000..03dde8c --- /dev/null +++ b/app/interactives/mortgate-calculator-suite/page.tsx @@ -0,0 +1,802 @@ +"use client" + +import { useState } from "react" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/app/ui/components/card" +import { Label } from "@/app/ui/components/label" +import { Input } from "@/app/ui/components/input" +import { Button } from "@/app/ui/components/button" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/ui/components/tabs" +import ThemeToggle from "@/app/lib/theme-toggle"; +import { BiSolidUpArrow, BiSolidDownArrow } from "react-icons/bi"; + +const formatCurrency = (amount: number) => + amount.toLocaleString("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }) + +export default function MortgageCalculator() { + const [monthsRemaining, setMonthsRemaining] = useState("") + const [annualRate, setAnnualRate] = useState("") + const [monthlyPayment, setMonthlyPayment] = useState("") + const [currentBalance, setCurrentBalance] = useState(null) + + const [refCurrentBalance, setRefCurrentBalance] = useState("") + const [refCurrentMonthlyPayment, setRefCurrentMonthlyPayment] = useState("") + const [refCurrentMonths, setRefCurrentMonths] = useState("") + const [refCurrentRate, setRefCurrentRate] = useState("") // Added missing state for refinance current rate + const [refNewLoanAmount, setRefNewLoanAmount] = useState("") + const [refNewRate, setRefNewRate] = useState("") + const [refNewMonths, setRefNewMonths] = useState("") + const [refClosingCosts, setRefClosingCosts] = useState("") + const [refYearsIn, setRefYearsIn] = useState("") + const [refinanceResults, setRefinanceResults] = useState<{ + currentMonthlyPayment: number + newMonthlyPayment: number + monthlySavings: number + totalCurrentCost: number + totalNewCost: number + totalSavings: number + breakEvenMonths: number + breakEvenMessage: string + } | null>(null) + + const calculateBalance = () => { + const months = Number.parseFloat(monthsRemaining) + const rate = Number.parseFloat(annualRate) / 100 / 12 + const payment = Number.parseFloat(monthlyPayment) + + if (isNaN(months) || isNaN(rate) || isNaN(payment) || months <= 0 || rate < 0 || payment <= 0) { + alert("Please enter valid positive numbers") + return + } + + let balance: number + + if (rate === 0) { + balance = payment * months + } else { + balance = payment * ((1 - Math.pow(1 + rate, -months)) / rate) + } + + setCurrentBalance(balance) + } + + const calculateRefinance = () => { + const currentBal = Number.parseFloat(refCurrentBalance) + const currentR = Number.parseFloat(refCurrentRate) / 100 / 12 // Fixed: Use refCurrentRate instead of refCurrentMonthlyPayment + const currentM = Number.parseFloat(refCurrentMonths) + const newR = Number.parseFloat(refNewRate) / 100 / 12 + const newM = Number.parseFloat(refNewMonths) + const closingCosts = Number.parseFloat(refClosingCosts) + + if ( + isNaN(currentBal) || + isNaN(currentR) || + isNaN(currentM) || + isNaN(newR) || + isNaN(newM) || + isNaN(closingCosts) || + currentBal <= 0 || + currentR < 0 || + currentM <= 0 || + newR < 0 || + newM <= 0 || + closingCosts < 0 + ) { + alert("Please enter valid numbers") + return + } + + // Calculate current monthly payment: PMT = P × [r(1 + r)^n] / [(1 + r)^n - 1] + let currentMonthlyPayment: number + if (currentR === 0) { + currentMonthlyPayment = currentBal / currentM + } else { + currentMonthlyPayment = + (currentBal * (currentR * Math.pow(1 + currentR, currentM))) / (Math.pow(1 + currentR, currentM) - 1) + } + + // Calculate new monthly payment + const newLoanAmount = Number.parseFloat(refNewLoanAmount) + + // Validate newLoanAmount in your validation section + if ( + isNaN(currentBal) || + isNaN(currentR) || + isNaN(currentM) || + isNaN(newLoanAmount) || // ✅ Add this + isNaN(newR) || + isNaN(newM) || + isNaN(closingCosts) || + currentBal <= 0 || + currentR < 0 || + currentM <= 0 || + newLoanAmount <= 0 || // ✅ Add this + newR < 0 || + newM <= 0 || + closingCosts < 0 + ) { + alert("Please enter valid numbers") + return + } + + // Calculate new monthly payment + let newMonthlyPayment: number + if (newR === 0) { + newMonthlyPayment = newLoanAmount / newM // ✅ Use newLoanAmount + } else { + newMonthlyPayment = (newLoanAmount * (newR * Math.pow(1 + newR, newM))) / (Math.pow(1 + newR, newM) - 1) // ✅ Use newLoanAmount + } + + const monthlySavings = currentMonthlyPayment - newMonthlyPayment + const totalCurrentCost = currentMonthlyPayment * currentM + const totalNewCost = newMonthlyPayment * newM + closingCosts + const totalSavings = totalCurrentCost - totalNewCost + + let breakEvenMonths: number + let breakEvenMessage: string + + if (monthlySavings > 0) { + // Case 1: Lower monthly payment + breakEvenMonths = closingCosts / monthlySavings + breakEvenMessage = `You'll recover closing costs in ${breakEvenMonths.toFixed(1)} months` + } else if (monthlySavings < 0) { + // Case 2: Higher monthly payment + // Only makes sense if total savings over loan term is positive + if (totalSavings > 0) { + breakEvenMonths = closingCosts / (-monthlySavings) + breakEvenMessage = `Despite higher monthly payments, you'll save overall if you stay ${breakEvenMonths.toFixed(1)}+ months` + } else { + breakEvenMonths = Infinity + breakEvenMessage = "This refinance costs more overall - not recommended" + } + } else { + // Case 3: Same monthly payment + breakEvenMonths = totalSavings > 0 ? 0 : Infinity + breakEvenMessage = totalSavings > 0 + ? "Same monthly payment, but you save on total interest" + : "No financial benefit" + } + + setRefinanceResults({ + currentMonthlyPayment, + newMonthlyPayment, + monthlySavings, + totalCurrentCost, + totalNewCost, + totalSavings, + breakEvenMonths, + breakEvenMessage, // Add this to state type + }) + } + + return ( +
+
+ + {/* Header */} +

Mortgage Calculator Suite

+ {/* Mode Selection */} +
+ + + + Current Balance + Refinance Analysis + + + + + + Current Mortgage Balance Calculator + + Calculate the present value of your remaining monthly mortgage payments (your mortgage balance). + + + +
+ + setMonthsRemaining(e.target.value)} + min="0" + step="1" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+

{monthsRemaining || "0"} months = {(Number.parseFloat(monthsRemaining || "0") / 12).toFixed(1)} years

+
+ +
+ + setAnnualRate(e.target.value)} + min="0" + step="0.1" + className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ + setMonthlyPayment(e.target.value)} + min="0" + step="0.01" + className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ {currentBalance !== null && ( +
+

Current Mortgage Balance

+

+ ${currentBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} +

+
+ )} + +
+
+
+ + + + + Refinance Analysis + Analyze if refinancing makes financial sense for your situation. + + + +
+
+

Current Loan Term

+ +
+ + setRefCurrentBalance(e.target.value)} + min="0" + step="0.01" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ + setRefCurrentMonths(e.target.value)} + min="0" + step="1" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ + setRefCurrentRate(e.target.value)} + min="0" + step="0.01" + className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ + setRefCurrentMonthlyPayment(e.target.value)} + min="0" + step="0.01" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+
+
+

New Loan Terms

+ +
+ + setRefNewLoanAmount(e.target.value)} + min="0" + step="0.01" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ + setRefNewMonths(e.target.value)} + min="0" + step="1" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+

{refNewMonths || "0"} months = {(Number.parseFloat(refNewMonths || "0") / 12).toFixed(1)} years

+
+ +
+ + setRefNewRate(e.target.value)} + min="0" + step="0.1" + className="text-lagunita font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ +
+ + setRefClosingCosts(e.target.value)} + min="0" + step="0.01" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+ +
+ + setRefYearsIn(e.target.value)} + min="0" + step="1" + className="text-black font-bold [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" + /> +
+ + +
+
+
+
+ + {refinanceResults && ( +
+ +
+

New monthly payment

+

+ ${formatCurrency(refinanceResults.newMonthlyPayment)} +

+
+ +
+

+ {refinanceResults.monthlySavings >= 0 ? "Monthly savings" : "Monthly increase"} +

+

= 0 + ? "bg-lagunita-lighter text-black border-1 border-lagunita" + : "bg-berry-light border-berry text-black" + }`}> + {refinanceResults.monthlySavings >= 0 ? "+" : "-"}$ + {formatCurrency(Math.abs(refinanceResults.monthlySavings))} +

+
+ + {/*
+

Break-even analysis

+

+ {refinanceResults.breakEvenMessage} +

+ {refinanceResults.breakEvenMonths > 0 && refinanceResults.breakEvenMonths !== Infinity && ( +

+ ({refinanceResults.breakEvenMonths.toFixed(1)} months = {(refinanceResults.breakEvenMonths / 12).toFixed(1)} years) +

+ )} +
*/} + +
+

+ {refinanceResults.totalSavings >= 0 ? "Refinancing saves you" : "Refinancing costs you"} +

+

= 0 ? "text-lagunita" : "text-berry" + }`}> + {refinanceResults.totalSavings >= 0 ? "" : "-"}$ + {formatCurrency(Math.abs(refinanceResults.totalSavings))} +

+
+
+ )} + + {refinanceResults ? ( +
+ + +
+ ) : ( + + )} + + + + + +
+
+
+ ) +} \ No newline at end of file