diff --git a/USAGE.md b/USAGE.md index e22c091..f2052cf 100755 --- a/USAGE.md +++ b/USAGE.md @@ -70,4 +70,436 @@ Now that the extension is configured, we can deploy the site again. Got to **Dep Once the build is complete, navigate to your production URL and you should see the **frameworks** that we just added to the database. - \ No newline at end of file + + +import React, { useEffect, useMemo, useState } from "react"; +import { Info, ChevronLeft, ChevronRight, Smartphone, Save, RefreshCcw, HelpCircle } from "lucide-react"; + +// Single-file wireframe for a mortgage calculator flow +// Focus: clarity, guidance, mobile-first, a11y, and resilience (autosave) +// Notes: This is a wireframe (no real finance maths). Replace placeholders with real logic later. + +const steps = [ + { key: "profile", label: "Your Details" }, + { key: "property", label: "Property" }, + { key: "loan", label: "Loan" }, + { key: "results", label: "Results" }, +]; + +const initialData = { + income: "", + deposit: "", + price: "", + rate: 5, + term: 25, + type: "repayment", + taxes: true, + insurance: false, +}; + +export default function MortgageCalculatorWireframe() { + const [stepIndex, setStepIndex] = useState(0); + const [data, setData] = useState(initialData); + const [errors, setErrors] = useState({}); + const [glossaryOpen, setGlossaryOpen] = useState(false); + const [saving, setSaving] = useState(false); + + // --- Autosave to localStorage (addresses: inputs lost on refresh) --- + useEffect(() => { + const raw = localStorage.getItem("mortgage-wireframe"); + if (raw) { + try { setData({ ...initialData, ...JSON.parse(raw) }); } catch {} + } + }, []); + + useEffect(() => { + setSaving(true); + const t = setTimeout(() => { + localStorage.setItem("mortgage-wireframe", JSON.stringify(data)); + setSaving(false); + }, 300); + return () => clearTimeout(t); + }, [data]); + + const canNext = useMemo(() => { + if (stepIndex === 0) return data.income && Number(data.income) > 0; + if (stepIndex === 1) return data.price && Number(data.price) > 0 && data.deposit !== ""; + if (stepIndex === 2) return data.rate >= 0 && data.term > 0; + return true; + }, [stepIndex, data]); + + const goNext = () => setStepIndex((i) => Math.min(i + 1, steps.length - 1)); + const goPrev = () => setStepIndex((i) => Math.max(i - 1, 0)); + + // --- Very rough placeholder maths for the wireframe UI --- + const monthly = useMemo(() => { + const price = Number(data.price) || 0; + const deposit = Number(data.deposit) || 0; + const principal = Math.max(price - deposit, 0); + const termMonths = (Number(data.term) || 0) * 12; + const r = (Number(data.rate) || 0) / 100 / 12; + if (!principal || !termMonths) return 0; + if (r === 0) return principal / termMonths; + return (principal * r) / (1 - Math.pow(1 + r, -termMonths)); + }, [data]); + + const affordabilityFlag = useMemo(() => { + const inc = Number(data.income) || 0; + if (!inc || !monthly) return null; + const ratio = monthly / (inc / 12); + if (ratio > 0.45) return "high"; + if (ratio > 0.30) return "medium"; + return "ok"; + }, [monthly, data.income]); + + // --- Accessible Field component --- + function Field({ id, label, hint, error, children }) { + return ( +
Estimated monthly payment
+£{monthly ? monthly.toLocaleString(undefined, { maximumFractionDigits: 0 }) : "—"}
+Interest rate
+{data.rate}%
+Term
+{data.term} years
+If rate −1%
+£{scenarioDelta(-1, data, monthly)}
+If rate +1%
+£{scenarioDelta(1, data, monthly)}
+If term +5y
+£{termDelta(5, data, monthly)}
+Your results are estimates. Fees, taxes and your credit profile can change the final offer.
+Warning: the payment may be high relative to your income.
} + {affordabilityFlag === "medium" &&Note: consider a longer term or larger deposit to reduce monthly cost.
} + {affordabilityFlag === "ok" &&This payment looks reasonable versus your income.
} +