← back to Boomer Calculator

components/MortgageCalculator.tsx

147 lines

'use client'

import { useState } from 'react'
import { monthlyPayment, readNumber } from '@/lib/calculations'

export default function MortgageCalculator() {
  const [homePrice, setHomePrice] = useState('')
  const [downPayment, setDownPayment] = useState('')
  const [loanTerm, setLoanTerm] = useState('30')
  const [interestRate, setInterestRate] = useState('')
  const [result, setResult] = useState<string | null>(null)
  const [error, setError] = useState('')

  const calculateMortgage = () => {
    setError('')
    setResult(null)
    try {
      const payment = monthlyPayment(readNumber(homePrice, 'home price'), readNumber(downPayment, 'down payment (use 0 for none)'), readNumber(loanTerm, 'loan term'), readNumber(interestRate, 'interest rate (use 0 for no interest)'))
      setResult(payment.toFixed(2))
    } catch (err) {
      setError((err as Error).message)
    }
  }

  const reset = () => {
    setHomePrice('')
    setDownPayment('')
    setLoanTerm('30')
    setInterestRate('')
    setResult(null)
    setError('')
  }

  return (
    <div className="bg-white rounded-xl shadow-2xl p-3 sm:p-4 max-w-2xl mx-auto">
      <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-800 mb-3 text-center">
        🏠 Mortgage
      </h2>

      <div className="grid grid-cols-2 gap-2 sm:gap-3 mb-3">
        <div>
          <label htmlFor="homePrice" className="block text-sm sm:text-base font-bold text-gray-700 mb-1">
            Home Price ($)
          </label>
          <input
            type="number"
            id="homePrice" value={homePrice}
            onChange={(e) => { setHomePrice(e.target.value); setResult(null); setError('') }}
            placeholder="500000"
            className="w-full p-2 sm:p-3 border-3 border-gray-300 rounded-lg focus:border-purple-500 focus:outline-none text-lg sm:text-xl font-bold"
          />
        </div>

        <div>
          <label htmlFor="downPayment" className="block text-sm sm:text-base font-bold text-gray-700 mb-1">
            Down Payment ($)
          </label>
          <input
            type="number"
            id="downPayment" value={downPayment}
            onChange={(e) => { setDownPayment(e.target.value); setResult(null); setError('') }}
            placeholder="100000"
            className="w-full p-2 sm:p-3 border-3 border-gray-300 rounded-lg focus:border-purple-500 focus:outline-none text-lg sm:text-xl font-bold"
          />
        </div>

        <div>
          <label htmlFor="loanTerm" className="block text-sm sm:text-base font-bold text-gray-700 mb-1">
            Loan Term
          </label>
          <select
            id="loanTerm" value={loanTerm}
            onChange={(e) => { setLoanTerm(e.target.value); setResult(null); setError('') }}
            className="w-full p-2 sm:p-3 border-3 border-gray-300 rounded-lg focus:border-purple-500 focus:outline-none text-lg sm:text-xl font-bold"
          >
            <option value="15">15 years</option>
            <option value="20">20 years</option>
            <option value="30">30 years</option>
          </select>
        </div>

        <div>
          <label htmlFor="interestRate" className="block text-sm sm:text-base font-bold text-gray-700 mb-1">
            Rate (%)
          </label>
          <input
            type="number"
            step="0.01"
            id="interestRate" value={interestRate}
            onChange={(e) => { setInterestRate(e.target.value); setResult(null); setError('') }}
            placeholder="6.5"
            className="w-full p-2 sm:p-3 border-3 border-gray-300 rounded-lg focus:border-purple-500 focus:outline-none text-lg sm:text-xl font-bold"
          />
        </div>
      </div>

      {error && <p role="alert" className="mb-3 text-center text-red-700">{error}</p>}

      {result !== null && (
        <div className="mb-3 p-3 sm:p-4 bg-gradient-to-r from-green-50 to-blue-50 rounded-lg border-3 border-green-200">
          <p className="text-center text-gray-700 mb-1 text-sm sm:text-base font-bold">
            Estimated Monthly Payment:
          </p>
          <p className="text-center text-3xl sm:text-4xl md:text-5xl font-bold text-green-600">
            ${result}
          </p>
          <p className="text-center text-sm sm:text-base text-gray-600 mt-1 font-semibold">
            Principal and interest only. Taxes, insurance and fees excluded.
          </p>
        </div>
      )}

      <div className="flex gap-2 sm:gap-3 mb-2">
        <button
          onClick={(e) => {
            e.preventDefault();
            calculateMortgage();
          }}
          className="flex-1 bg-gradient-to-r from-purple-500 to-pink-500 text-white py-3 sm:py-4 rounded-lg font-bold text-lg sm:text-xl shadow-lg select-none cursor-pointer"
          style={{ WebkitTapHighlightColor: 'transparent' }}
        >
          Calculate
        </button>
        <button
          onClick={(e) => {
            e.preventDefault();
            reset();
          }}
          className="flex-1 bg-gray-200 py-3 sm:py-4 rounded-lg font-bold text-lg sm:text-xl select-none cursor-pointer shadow-lg"
          style={{ WebkitTapHighlightColor: 'transparent' }}
        >
          Reset
        </button>
      </div>

      <div className="text-center space-y-1">
        <p className="text-xs sm:text-sm text-gray-600 font-semibold">
          Fixed-rate loan estimate
        </p>
        <p className="text-xs text-gray-500">
          Enter your own price, down payment and rate.
        </p>
      </div>
    </div>
  )
}