← back to Commercialrealestate
feat(index): saved searches (DTD verdict D, 2/3 Codex+Claude; Qwen dissent C=$/SF-vs-submarket OVERRIDDEN on honest-data grounds — comps.json submarket benchmarks are prose cap ranges, no joinable $/SF/median) — name + save the current filter/sort/view/heat state (reuses the shareable-URL serialization) into a rail 'Saved searches' list; one-click restore (full-URL, bulletproof) + ✕ delete; localStorage-persisted. Contrarian-gated, 0 errors.
45fe54b683ea17f80c5624acde46ca98ba0cf733 · 2026-07-02 15:45:19 -0700 · Steve Abrams
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
Diff
commit 45fe54b683ea17f80c5624acde46ca98ba0cf733
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 2 15:45:19 2026 -0700
feat(index): saved searches (DTD verdict D, 2/3 Codex+Claude; Qwen dissent C=$/SF-vs-submarket OVERRIDDEN on honest-data grounds — comps.json submarket benchmarks are prose cap ranges, no joinable $/SF/median) — name + save the current filter/sort/view/heat state (reuses the shareable-URL serialization) into a rail 'Saved searches' list; one-click restore (full-URL, bulletproof) + ✕ delete; localStorage-persisted. Contrarian-gated, 0 errors.
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
public/index.html | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/public/index.html b/public/index.html
index a51450c..734ae22 100644
--- a/public/index.html
+++ b/public/index.html
@@ -203,6 +203,13 @@
.quickstart { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; margin:16px 0; }
.qchip { background:var(--card); border:1px solid var(--line); color:var(--ink); border-radius:18px; padding:7px 14px; font-size:13px; cursor:pointer; }
.qchip:hover { border-color:var(--blue); color:var(--blue); }
+ .savedrow { display:flex; gap:6px; align-items:center; margin-bottom:5px; }
+ .savedname { flex:1; text-align:left; background:var(--card); border:1px solid var(--line); color:var(--ink); border-radius:8px; padding:6px 9px; font-size:12px; cursor:pointer; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
+ .savedname:hover { border-color:var(--blue); color:var(--blue); }
+ .saveddel { background:none; border:0; color:var(--mut); cursor:pointer; font-size:13px; }
+ .saveddel:hover { color:var(--bad); }
+ #savebtn { width:100%; background:transparent; border:1px dashed #284b7a; color:var(--blue); border-radius:8px; padding:7px; font-size:12px; cursor:pointer; margin-top:4px; }
+ .savedempty { color:var(--mut); font-size:11px; }
/* compare overlay */
#cmpov { display:none; position:fixed; inset:0; background:rgba(0,0,0,.6); z-index:60; align-items:flex-start; justify-content:center; padding:40px 14px; overflow:auto; }
#cmpov.on { display:flex; }
@@ -262,6 +269,7 @@
<aside id="drawer">
<div class="dhead"><b>Filters</b><button class="x" id="dclose" aria-label="Close">✕</button></div>
+ <div class="fsec"><h4>Saved searches</h4><div id="savedList"></div><button id="savebtn">💾 Save current view</button></div>
<div class="fsec"><h4>Asset type</h4><div class="chips" id="fType"></div></div>
<div class="fsec"><h4>Listing firm</h4><div class="chips" id="fFirm"></div></div>
<div class="fsec"><h4>Submarket / city</h4>
@@ -1127,6 +1135,14 @@ function paintPage(){
else { g.className='grid'; g.innerHTML=slice.map(card).join('')+moreBtn(); enrichAssessor(); }
applyHeat(slice);
}
+// ---- saved searches (name + restore the full shareable-URL state) ----
+const SAVED=(function(){ try{ return JSON.parse(localStorage.getItem('cre_saved')||'[]'); }catch(e){ return []; } })();
+function saveSaved(){ try{ localStorage.setItem('cre_saved', JSON.stringify(SAVED)); }catch(e){} }
+function buildSavedList(){ const el=$('#savedList'); if(!el) return;
+ el.innerHTML = SAVED.length
+ ? SAVED.map((s,i)=>`<div class="savedrow"><button class="savedname" data-qs="${(s.qs||'').replace(/"/g,'"')}" title="Restore this saved view">📁 ${esc(s.name)}</button><button class="saveddel" data-del="${i}" aria-label="Delete saved search">✕</button></div>`).join('')
+ : '<div class="savedempty">None yet — filter, then “Save current view”.</div>';
+}
function applyQuick(k){
if(k==='condo'){ F.types=new Set(['Condo']); }
else if(k==='mf'){ F.types=new Set(['Multifamily']); }
@@ -1384,6 +1400,10 @@ fetch('data/ranked.json').then(r=>r.json()).then(async d=>{
$$('#drawer .fsec').forEach(s=>s.classList.add('collapsed'));
// active-filter summary bar: remove one filter or reset all
$('#activebar').addEventListener('click',e=>{ if(e.target.closest('#afreset')){ resetAll(); return; } const c=e.target.closest('.afchip'); if(c) clearFilter(c.dataset.clear); });
+ // saved searches
+ buildSavedList();
+ $('#savedList').addEventListener('click',e=>{ const del=e.target.closest('[data-del]'); if(del){ SAVED.splice(+del.dataset.del,1); saveSaved(); buildSavedList(); return; } const n=e.target.closest('.savedname'); if(n){ location.href=location.pathname+(n.dataset.qs||''); } });
+ $('#savebtn').onclick=()=>{ const name=(prompt('Name this saved search:')||'').trim(); if(!name) return; SAVED.push({name, qs:location.search||''}); saveSaved(); buildSavedList(); };
$('#linkbtn').onclick=copyShareLink;
$('#csvbtn').onclick=exportCSV;
// facet chip handlers (multi-select toggles)
← 6c08b42 feat(index): Compare -> deliverable (DTD verdict A, 2/3 Code
·
back to Commercialrealestate
·
feat(index): per-card 📋 Copy summary (DTD verdict C, UNANIM a8fedb1 →