← back to Nationalrealestate
States tab on rankings (51 states, raw metrics), national insight strip on map (median home value, % counties rising, median DOM), index favicon
ae4b420dfb1665bbd760c08c594354cdcce6e2e5 · 2026-07-21 15:44:22 -0700 · Steve Abrams
Files touched
M public/index.htmlM public/markets.htmlM src/server/index.ts
Diff
commit ae4b420dfb1665bbd760c08c594354cdcce6e2e5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 21 15:44:22 2026 -0700
States tab on rankings (51 states, raw metrics), national insight strip on map (median home value, % counties rising, median DOM), index favicon
---
public/index.html | 22 ++++++++++++++++++++++
public/markets.html | 1 +
src/server/index.ts | 2 +-
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/public/index.html b/public/index.html
index 39079c1..196cb74 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%230a0d13'/%3E%3Ctext x='8' y='12' font-size='10' text-anchor='middle' fill='%23c8a24b'%3EUS%3C/text%3E%3C/svg%3E">
<title>USRealEstate — National Market Map</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
@@ -56,6 +57,9 @@
<div id="map"></div>
<div id="legend"><div class="t">Loading…</div></div>
<div id="status">loading county data…</div>
+<div id="natstrip" style="position:fixed;left:50%;transform:translateX(-50%);bottom:18px;z-index:1000;
+ display:none;gap:22px;background:rgba(17,21,29,.94);border:1px solid var(--line);border-radius:10px;
+ padding:8px 18px;font-size:12px;color:var(--muted);white-space:nowrap;"></div>
<div id="topmkts" style="position:fixed;right:14px;top:64px;z-index:1000;background:rgba(17,21,29,.94);
border:1px solid var(--line);border-radius:10px;padding:10px 12px;max-width:250px;display:none;">
<div style="font-size:12px;color:var(--gold);font-weight:700;margin-bottom:6px;">Top Markets
@@ -168,6 +172,23 @@ function onEachFeature(f, layer){
layer.on('click', () => { location.href = `/market.html?key=county:${f.id}`; });
}
+async function loadNationalStrip(){
+ try {
+ const [z, y, d] = await Promise.all(['zhvi','zhvi_yoy','dom'].map(m =>
+ fetch('/api/choropleth?metric=' + m).then(r => r.json())));
+ const med = obj => { const v = Object.values(obj.values || {}).filter(Number.isFinite).sort((a,b)=>a-b);
+ return v.length ? v[Math.floor(v.length/2)] : null; };
+ const yv = Object.values(y.values || {}).filter(Number.isFinite);
+ const rising = yv.length ? Math.round(100 * yv.filter(v => v > 0).length / yv.length) : null;
+ const parts = [];
+ const mz = med(z); if (mz != null) parts.push(`median county home value <b style="color:var(--gold)">$${Math.round(mz).toLocaleString()}</b>`);
+ if (rising != null) parts.push(`<b style="color:${rising >= 50 ? '#3fb950' : '#f85149'}">${rising}%</b> of counties rising YoY`);
+ const md = med(d); if (md != null) parts.push(`median DOM <b style="color:var(--gold)">${Math.round(md)}d</b>`);
+ if (parts.length){ const el = document.getElementById('natstrip');
+ el.innerHTML = parts.map(p => `<span>${p}</span>`).join(''); el.style.display = 'flex'; }
+ } catch {}
+}
+
async function loadTopMarkets(){
try {
const d = await fetch('/api/markets?type=county').then(r => r.json());
@@ -193,6 +214,7 @@ async function loadTopMarkets(){
geojson = geo;
geoLayer = L.geoJSON(geo, { style: styleFeature, onEachFeature }).addTo(map);
loadTopMarkets();
+ loadNationalStrip();
})();
</script>
</body>
diff --git a/public/markets.html b/public/markets.html
index 206eeb4..9fcb9ae 100644
--- a/public/markets.html
+++ b/public/markets.html
@@ -45,6 +45,7 @@
<span id="typeSeg" style="margin-left:0">
<button data-type="county">Counties</button>
<button data-type="metro">Metros</button>
+ <button data-type="state">States</button>
</span>
</div>
<div class="cg-shell">
diff --git a/src/server/index.ts b/src/server/index.ts
index bcbf907..de4785c 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -125,7 +125,7 @@ const MARKETS_TTL_MS = 5 * 60_000;
app.get('/api/markets', async (req, res) => {
try {
const type = String(req.query.type || 'county');
- if (type !== 'county' && type !== 'metro') return res.status(400).json({ error: 'type must be county|metro' });
+ if (type !== 'county' && type !== 'metro' && type !== 'state') return res.status(400).json({ error: 'type must be county|metro|state' });
const hit = marketsCache.get(type);
if (hit && Date.now() - hit.at < MARKETS_TTL_MS) return res.json(hit.body);
const metricsQ = query<{ region_id: number; metric: string; value: string }>(
← fabf9ec auto-save: 2026-07-21T15:38:16 (1 files) — src/jobs/broker_r
·
back to Nationalrealestate
·
CSV export on rankings (/api/markets.csv, type-synced downlo 58c461c →