← back to Rentv
desk UX streamline: scope-contextual views (kill dead Map/Command combos), remove redundant Command + fake-geo Map, add live result count
e17a5f0d7a3a64843f525d6ca77cf06f5c2589f0 · 2026-07-22 20:14:14 -0700 · Steve
Files touched
Diff
commit e17a5f0d7a3a64843f525d6ca77cf06f5c2589f0
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 20:14:14 2026 -0700
desk UX streamline: scope-contextual views (kill dead Map/Command combos), remove redundant Command + fake-geo Map, add live result count
---
public/desk.html | 43 ++++++++++++++++++-------------------------
1 file changed, 18 insertions(+), 25 deletions(-)
diff --git a/public/desk.html b/public/desk.html
index f72b7794..1703f7a9 100644
--- a/public/desk.html
+++ b/public/desk.html
@@ -74,9 +74,7 @@
<nav class="rail" id="rail">
<div class="h">View</div>
<button data-v="directory" class="on"><span class="i">▤</span>Directory</button>
- <button data-v="map"><span class="i">◈</span>Map</button>
<button data-v="analytics"><span class="i">▮</span>Analytics</button>
- <button data-v="command"><span class="i">⌘</span>Command</button>
<div class="h" style="margin-top:10px">Scope</div>
<button data-s="brokers" class="on scope"><span class="i">👤</span>Brokers <span class="c" id="cB">0</span></button>
<button data-s="firms" class="scope"><span class="i">🏢</span>Firms <span class="c" id="cF">0</span></button>
@@ -86,9 +84,7 @@
</nav>
<div class="content">
<div class="view on" id="v-directory"></div>
- <div class="view" id="v-map"></div>
<div class="view" id="v-analytics"></div>
- <div class="view" id="v-command"></div>
</div>
</div>
<script>
@@ -149,15 +145,25 @@ async function liveRows(scope,term){
return scopeData().filter(x=>JSON.stringify(x).toLowerCase().includes(term.toLowerCase()));
}
// rail
-$('rail').addEventListener('click',e=>{const btn=e.target.closest('button');if(!btn)return;
+// Views are contextual to the scope — a scope only exposes the views that actually render ITS data,
+// so you can never land on a mismatched combo (e.g. Sublease + a firm-only chart).
+const SCOPE_VIEWS={brokers:['directory','analytics'],firms:['directory','analytics'],owners:['directory'],sublease:['directory']};
+function applyScopeViews(){
+ const ok=SCOPE_VIEWS[D.scope]||['directory'];
+ document.querySelectorAll('#rail [data-v]').forEach(b=>{const good=ok.includes(b.dataset.v);b.disabled=!good;b.style.opacity=good?'':'.32';b.style.cursor=good?'':'not-allowed';});
+ if(!ok.includes(D.view)){D.view='directory';
+ document.querySelectorAll('#rail [data-v]').forEach(x=>x.classList.toggle('on',x.dataset.v==='directory'));
+ document.querySelectorAll('.view').forEach(v=>v.classList.toggle('on',v.id==='v-directory'));}
+}
+$('rail').addEventListener('click',e=>{const btn=e.target.closest('button');if(!btn||btn.disabled)return;
if(btn.dataset.v){document.querySelectorAll('#rail [data-v]').forEach(x=>x.classList.remove('on'));btn.classList.add('on');D.view=btn.dataset.v;document.querySelectorAll('.view').forEach(v=>v.classList.remove('on'));$('v-'+D.view).classList.add('on');}
if(btn.dataset.s){document.querySelectorAll('#rail .scope').forEach(x=>x.classList.remove('on'));btn.classList.add('on');D.scope=btn.dataset.s;D.selDir=0;renderAll();}
});
-function renderAll(){directory();map();analytics();command();}
+function renderAll(){directory();analytics();applyScopeViews();}
// ── Directory (master-detail) ──
function directory(){
const rows=scopeData();
- $('v-directory').innerHTML=`<div class="md"><div class="list"><div class="search"><input id="dq" placeholder="Search ${D.scope}…"></div><div class="rows" id="drows"></div></div><div class="detail" id="ddetail"></div></div>`;
+ $('v-directory').innerHTML=`<div class="md"><div class="list"><div class="search"><input id="dq" placeholder="Search ${D.scope}…"><div class="fresh" id="dcount" style="padding:7px 2px 0"></div></div><div class="rows" id="drows"></div></div><div class="detail" id="ddetail"></div></div>`;
const q='';
function list(){let r=rows.slice();const v=($('dq').value||'').toLowerCase();if(v)r=r.filter(x=>JSON.stringify(x).toLowerCase().includes(v));return r;}
function detail(x){if(!x){$('ddetail').innerHTML='';return;}
@@ -168,20 +174,15 @@ function directory(){
const emptyMsg=D.scope==='sublease'
? '<div class="fresh" style="padding:20px 16px;line-height:1.6">No sublease listings yet.<br>Inventory is entered by the RENTV desk from live broker deals — <a class="field-link" href="/desk-admin">add a listing →</a></div>'
: '<div class="fresh">No matches in the registry.</div>';
- async function draw(){const r=await liveRows(D.scope,$('dq').value);$('drows').innerHTML=r.map((x,i)=>`<div class="row${i===D.selDir?' sel':''}" data-i="${i}"><div class="nm">${esc(nm(x))}</div><div class="f">${esc(sub(D.scope,x))}</div></div>`).join('')||emptyMsg;
+ async function draw(){const r=await liveRows(D.scope,$('dq').value);
+ const tot=D.scope==='brokers'?D.totalB:D.scope==='firms'?D.totalF:D.scope==='sublease'?D.totalS:D.owners.length;
+ const term=($('dq').value||'').trim();
+ $('dcount').textContent=r.length?(term?`${r.length} match${r.length===1?'':'es'}`:`Showing ${r.length.toLocaleString()} of ${Number(tot).toLocaleString()}`):'';
+ $('drows').innerHTML=r.map((x,i)=>`<div class="row${i===D.selDir?' sel':''}" data-i="${i}"><div class="nm">${esc(nm(x))}</div><div class="f">${esc(sub(D.scope,x))}</div></div>`).join('')||emptyMsg;
if(!r.length&&D.scope==='sublease'){$('ddetail').innerHTML='<div class="fresh" style="padding:30px 36px;line-height:1.7;max-width:520px">This desk surfaces <b style="color:#e7ecf1">real sublease space</b> — sqft, asking rate, term, the sublandlord, and the listing broker, each with click-outs to the listing, map, and broker. It fills as the RENTV team logs deals from their broker network.</div>';return;}
$('drows').querySelectorAll('.row').forEach(row=>row.onclick=()=>{D.selDir=+row.dataset.i;$('drows').querySelectorAll('.row').forEach(x=>x.classList.remove('sel'));row.classList.add('sel');detail(r[D.selDir]);});detail(r[D.selDir]||r[0]);}
$('dq').oninput=debounce(()=>{D.selDir=0;draw();},300);draw();
}
-// ── Map ──
-function map(){
- const mq=D.owners.slice(0,10);
- $('v-map').innerHTML=`<div class="mapv"><div class="map" id="mapc"><div class="g"></div></div><div class="mside" id="mside"></div></div>`;
- mq.forEach((m,i)=>{const x=12+((i*41)%76),y=18+((i*31)%62);const p=document.createElement('div');p.className='pin'+(i===0?' sel':'');p.style.left=x+'%';p.style.top=y+'%';p.dataset.i=i;p.innerHTML=`<div class="d"></div><div class="l">${shortUsd(m.assessed_total)}</div>`;$('mapc').appendChild(p);});
- function show(m){const ad=[m.address,m.city].filter(Boolean).join(', ');$('mside').innerHTML=`<div class="k">${esc(m.type_label||'Asset')} · ${esc(m.use_desc||'')}</div><h2>${esc(m.address||m.ain)}</h2><div style="color:#9aa4ad;margin-bottom:12px">${esc(ad)}</div><div class="acts">${actions('owners',m)}</div><div style="margin-top:16px"><div class="r"><span>Assessed</span><b>${shortUsd(m.assessed_total)}</b></div><div class="r"><span>SqFt</span><b>${(m.sqft||0).toLocaleString()}</b></div><div class="r"><span>Built</span><b>${m.year_built||'—'}</b></div><div class="r"><span>Recorded</span><b>${dstr(m.recording_date)}</b></div></div>`;}
- if(mq[0])show(mq[0]);
- $('mapc').querySelectorAll('.pin').forEach(p=>p.onclick=()=>{$('mapc').querySelectorAll('.pin').forEach(x=>x.classList.remove('sel'));p.classList.add('sel');show(mq[+p.dataset.i]);});
-}
// ── Analytics ──
function analytics(){
const top=D.firms.slice().sort((a,b)=>(b.agent_count||0)-(a.agent_count||0)).slice(0,9);const max=top[0]?.agent_count||1;
@@ -191,13 +192,5 @@ function analytics(){
}</div></div></div>`;
$('bars').querySelectorAll('.barwrap').forEach(w=>w.querySelector('.bar').onclick=e=>{if(e.target.closest('a'))return;w.classList.toggle('open');});
}
-// ── Command ──
-function command(){
- $('v-command').innerHTML=`<div class="cmd"><div class="box"><div class="in"><span style="color:#8b939b;font-size:18px">⌘</span><input id="cq" placeholder="Search ${D.scope}…"></div><div class="results" id="cres"></div></div></div>`;
- async function draw(){let r=(await liveRows(D.scope,$('cq').value)).slice(0,40);
- $('cres').innerHTML=r.map((x,i)=>`<div class="res${i===0?' hot':''}"><div class="nm">${esc(nm(x))}</div><div class="f">${esc(sub(D.scope,x))}${loc(x)?' · '+esc(loc(x)):''}</div><div class="exp"><div class="acts">${actions(D.scope,x)}</div></div></div>`).join('')||'<div class="res"><div class="f">No matches.</div></div>';
- $('cres').querySelectorAll('.res').forEach(el=>el.onclick=e=>{if(e.target.closest('a'))return;el.classList.toggle('open');});}
- $('cq').oninput=debounce(draw,300);draw();
-}
</script>
</body></html>
← 9e29c2bb auto-save: 2026-07-22T19:46:22 (3 files) — data/markets.json
·
back to Rentv
·
auto-save: 2026-07-22T20:16:30 (3 files) — data/markets.json 85dbfc62 →