import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { CurrentHome, NewHome, Debt, CalculationResult } from './types'; import NumberInput from './components/NumberInput'; import { Icons, COLORS } from './constants'; import { getAIAnalysis } from './services/geminiService'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; const App: React.FC = () => { // --- State --- const [currentHome, setCurrentHome] = useState({ value: 400000, mortgageBalance: 250000, interestRate: 3.0, termYears: 30, taxInsuranceEstimate: 450, }); const [newHome, setNewHome] = useState({ purchasePrice: 550000, interestRate: 7.0, termYears: 30, taxInsuranceEstimate: 625, }); const [debts, setDebts] = useState([ { id: '1', name: 'Car Loan', balance: 25000, monthlyPayment: 550, payOff: true }, { id: '2', name: 'Credit Card A', balance: 12000, monthlyPayment: 360, payOff: true }, { id: '3', name: 'Student Loan', balance: 35000, monthlyPayment: 400, payOff: false }, ]); const [sellingCostsPercent, setSellingCostsPercent] = useState(6); const [aiAnalysis, setAiAnalysis] = useState(''); const [isAnalyzing, setIsAnalyzing] = useState(false); const [showShareModal, setShowShareModal] = useState(false); const [toast, setToast] = useState(null); // --- URL Hydration --- useEffect(() => { const params = new URLSearchParams(window.location.search); const data = params.get('data'); if (data) { try { const decoded = JSON.parse(atob(data)); if (decoded.currentHome) setCurrentHome(decoded.currentHome); if (decoded.newHome) setNewHome(decoded.newHome); if (decoded.debts) setDebts(decoded.debts); if (decoded.sellingCostsPercent) setSellingCostsPercent(decoded.sellingCostsPercent); } catch (e) { console.error("Failed to hydrate state from URL", e); } } }, []); // --- Toast logic --- useEffect(() => { if (toast) { const timer = setTimeout(() => setToast(null), 3000); return () => clearTimeout(timer); } }, [toast]); // --- Calculations --- const calculateMortgage = (principal: number, annualRate: number, years: number) => { if (annualRate === 0) return principal / (years * 12); const monthlyRate = annualRate / 100 / 12; const numberOfPayments = years * 12; return ( (principal * monthlyRate * Math.pow(1 + monthlyRate, numberOfPayments)) / (Math.pow(1 + monthlyRate, numberOfPayments) - 1) ); }; const results = useMemo((): CalculationResult => { const sellingCosts = currentHome.value * (sellingCostsPercent / 100); const netProceeds = currentHome.value - currentHome.mortgageBalance - sellingCosts; const debtsToPayOff = debts.filter((d) => d.payOff); const totalDebtBalanceEliminated = debtsToPayOff.reduce((acc, d) => acc + d.balance, 0); const downPayment = Math.max(0, netProceeds - totalDebtBalanceEliminated); const newMortgageAmount = Math.max(0, newHome.purchasePrice - downPayment); const currentPI = calculateMortgage(currentHome.mortgageBalance, currentHome.interestRate, currentHome.termYears); const currentDebtPayments = debts.reduce((acc, d) => acc + d.monthlyPayment, 0); const currentTotal = currentPI + currentDebtPayments + currentHome.taxInsuranceEstimate; const newPI = calculateMortgage(newMortgageAmount, newHome.interestRate, newHome.termYears); const remainingDebts = debts.filter((d) => !d.payOff); const remainingDebtPayments = remainingDebts.reduce((acc, d) => acc + d.monthlyPayment, 0); const newTotal = newPI + newHome.taxInsuranceEstimate + remainingDebtPayments; return { currentMonthlyTotal: currentTotal, newMonthlyTotal: newTotal, monthlySavings: currentTotal - newTotal, equityGained: currentHome.value - currentHome.mortgageBalance, netProceeds, downPayment, newMortgageAmount, debtsPaidOff: totalDebtBalanceEliminated, }; }, [currentHome, newHome, debts, sellingCostsPercent]); // --- Handlers --- const handleDebtToggle = (id: string) => { setDebts((prev) => prev.map((d) => (d.id === id ? { ...d, payOff: !d.payOff } : d)) ); }; const addDebt = () => { const newDebt: Debt = { id: Date.now().toString(), name: 'New Debt', balance: 0, monthlyPayment: 0, payOff: false, }; setDebts([...debts, newDebt]); }; const removeDebt = (id: string) => { setDebts(debts.filter((d) => d.id !== id)); }; const updateDebt = (id: string, field: keyof Debt, value: any) => { setDebts((prev) => prev.map((d) => (d.id === id ? { ...d, [field]: value } : d)) ); }; const runAI = useCallback(async () => { setIsAnalyzing(true); const analysis = await getAIAnalysis(currentHome, newHome, debts, results); setAiAnalysis(analysis); setIsAnalyzing(false); }, [currentHome, newHome, debts, results]); const generateShareLink = () => { const state = { currentHome, newHome, debts, sellingCostsPercent }; const serialized = btoa(JSON.stringify(state)); const url = new URL(window.location.href); url.searchParams.set('data', serialized); return url.toString(); }; const copyToClipboard = (text: string, msg: string) => { navigator.clipboard.writeText(text); setToast(msg); }; const shareSummary = () => { const savings = Math.round(results.monthlySavings); const debtPaid = Math.round(results.debtsPaidOff); const summary = `Check out this Louisville Home Strategy I put together for you! By using your existing home equity, we can: - Eliminate $${debtPaid.toLocaleString()} in high-interest debt - Get you into your new $${newHome.purchasePrice.toLocaleString()} home - ${savings >= 0 ? `SAVE you $${Math.abs(savings).toLocaleString()} per month` : `Adjust your monthly outlay to $${Math.round(results.newMonthlyTotal).toLocaleString()}`} See the full breakdown here: ${generateShareLink()}`; copyToClipboard(summary, "Summary copied! Paste in text or DM."); }; // --- Chart Data --- const chartData = [ { name: 'Current Outlay', total: Math.round(results.currentMonthlyTotal), mortgage: Math.round(calculateMortgage(currentHome.mortgageBalance, currentHome.interestRate, currentHome.termYears) + currentHome.taxInsuranceEstimate), debt: Math.round(debts.reduce((acc, d) => acc + d.monthlyPayment, 0)), }, { name: 'Proposed Plan', total: Math.round(results.newMonthlyTotal), mortgage: Math.round(calculateMortgage(results.newMortgageAmount, newHome.interestRate, newHome.termYears) + newHome.taxInsuranceEstimate), debt: Math.round(debts.filter(d => !d.payOff).reduce((acc, d) => acc + d.monthlyPayment, 0)), }, ]; return (
{/* Toast Notification */} {toast && (
{toast}
)} {/* Header */}

Louisville Equity Unlocker

Debt Consolidation & Home Upgrade Strategy

Potential Monthly Change

= 0 ? 'text-green-400' : 'text-red-400'}`}> {results.monthlySavings >= 0 ? '+' : ''}${Math.abs(Math.round(results.monthlySavings)).toLocaleString()} /mo

{/* Share Modal */} {showShareModal && (

Share This Plan

Send these specific calculations to your client via text, DM, or email.

)}
{/* Left Column: Inputs */}
{/* Current Situation */}

Current Situation

setCurrentHome({ ...currentHome, value: v })} prefix="$" /> setCurrentHome({ ...currentHome, mortgageBalance: v })} prefix="$" /> setCurrentHome({ ...currentHome, interestRate: v })} suffix="%" step={0.1} /> setCurrentHome({ ...currentHome, taxInsuranceEstimate: v })} prefix="$" /> setSellingCostsPercent(v)} suffix="%" step={0.5} />
{/* New Home Target */}

New Home Purchase

setNewHome({ ...newHome, purchasePrice: v })} prefix="$" /> setNewHome({ ...newHome, interestRate: v })} suffix="%" step={0.125} /> setNewHome({ ...newHome, taxInsuranceEstimate: v })} prefix="$" />
{/* Center/Right Column: Debt Management & Results */}
{/* Debt Console */}

Existing Debts to Consolidate

{debts.map((debt) => (
updateDebt(debt.id, 'name', e.target.value)} />
updateDebt(debt.id, 'balance', parseFloat(e.target.value))} />
updateDebt(debt.id, 'monthlyPayment', parseFloat(e.target.value))} />
))} {debts.length === 0 &&

No debts added. Add them to see the consolidation impact!

}
{/* Results Visuals */}
{/* Visual Chart */}

Monthly Expense Comparison

`$${value.toLocaleString()}`} />
{/* Quick Stats Grid */}
Net Proceeds ${Math.round(results.netProceeds).toLocaleString()}
Debt Paid Off ${Math.round(results.debtsPaidOff).toLocaleString()}
New Down Payment ${Math.round(results.downPayment).toLocaleString()}
Loan-to-Value {Math.round((results.newMortgageAmount / newHome.purchasePrice) * 100)}%
{/* AI Strategy Summary */} {(aiAnalysis || isAnalyzing) && (

Louisville Realtor AI Analysis

{isAnalyzing ? (
) : (
{aiAnalysis}
)}
)} {/* Detailed Breakdown Table */}

Full Monthly Budget Breakdown

Expense Type Current ({currentHome.interestRate}%) Proposed ({newHome.interestRate}%) Change
Mortgage (P&I) ${Math.round(calculateMortgage(currentHome.mortgageBalance, currentHome.interestRate, currentHome.termYears)).toLocaleString()} ${Math.round(calculateMortgage(results.newMortgageAmount, newHome.interestRate, newHome.termYears)).toLocaleString()} +${Math.round(calculateMortgage(results.newMortgageAmount, newHome.interestRate, newHome.termYears) - calculateMortgage(currentHome.mortgageBalance, currentHome.interestRate, currentHome.termYears)).toLocaleString()}
Taxes & Insurance ${currentHome.taxInsuranceEstimate.toLocaleString()} ${newHome.taxInsuranceEstimate.toLocaleString()} = currentHome.taxInsuranceEstimate ? 'text-red-500' : 'text-green-500'}`}> {newHome.taxInsuranceEstimate >= currentHome.taxInsuranceEstimate ? '+' : '-'}${Math.abs(newHome.taxInsuranceEstimate - currentHome.taxInsuranceEstimate).toLocaleString()}
Non-Mortgage Debt ${debts.reduce((acc, d) => acc + d.monthlyPayment, 0).toLocaleString()} ${debts.filter(d => !d.payOff).reduce((acc, d) => acc + d.monthlyPayment, 0).toLocaleString()} -${(debts.filter(d => d.payOff).reduce((acc, d) => acc + d.monthlyPayment, 0)).toLocaleString()}
Total Monthly Outlay ${Math.round(results.currentMonthlyTotal).toLocaleString()} ${Math.round(results.newMonthlyTotal).toLocaleString()} = 0 ? 'text-green-600' : 'text-red-600'}`}> {results.monthlySavings >= 0 ? 'Savings: ' : 'Increase: '} ${Math.abs(Math.round(results.monthlySavings)).toLocaleString()}

© {new Date().getFullYear()} Louisville Luxury Real Estate Group. Licensed in Kentucky.

Disclaimer: This calculator is for illustrative purposes only. Interest rates, taxes, and insurance are estimates and not a guarantee of credit. Consult with a professional financial advisor or mortgage lender for exact figures. Equity calculations based on user inputs.

); }; export default App;
import React from 'react'; import { createRoot } from 'react-dom/client'; import App from './App'; const container = document.getElementById('root'); if (container) { const root = createRoot(container); root.render( ); }