β back to Ventura Corridor
iter 61: building prioritization 'walk here next' β /api/buildings/priority?max_miles=&min_unpitched=&limit= ranks unwalked-but-walkable buildings by priority_score = unpitched Γ (1/miles), proximity boost so close-by small buildings beat distant towers; /buildings.html gains a hero priority section above the main list with 15-card grid (top 15 by default, radius selector 0.25/0.5/1.0/2.0 mi), each card shows order # + address + city + miles + unpitched count + score + πΆ walk + π’ roster shortcuts; surfaces next-action queue (top 5 today: 15821=556, 15260=292, 15456=38, 15303=172, 15300=123 unpitched doors)
40a7c216f855e0dfac1e8f57e554633567bfc044 Β· 2026-05-06 14:11:52 -0700 Β· SteveStudio2
Files touched
M public/buildings.htmlM src/server/index.ts
Diff
commit 40a7c216f855e0dfac1e8f57e554633567bfc044
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Wed May 6 14:11:52 2026 -0700
iter 61: building prioritization 'walk here next' β /api/buildings/priority?max_miles=&min_unpitched=&limit= ranks unwalked-but-walkable buildings by priority_score = unpitched Γ (1/miles), proximity boost so close-by small buildings beat distant towers; /buildings.html gains a hero priority section above the main list with 15-card grid (top 15 by default, radius selector 0.25/0.5/1.0/2.0 mi), each card shows order # + address + city + miles + unpitched count + score + πΆ walk + π’ roster shortcuts; surfaces next-action queue (top 5 today: 15821=556, 15260=292, 15456=38, 15303=172, 15300=123 unpitched doors)
---
public/buildings.html | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/server/index.ts | 37 ++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+)
diff --git a/public/buildings.html b/public/buildings.html
index 380632a..6bb54be 100644
--- a/public/buildings.html
+++ b/public/buildings.html
@@ -174,6 +174,27 @@
</div>
</div>
+<section id="priority-section" style="padding:24px 32px;border-bottom:1px solid var(--rule);background:radial-gradient(ellipse at top right, rgba(212,182,131,0.06), transparent 70%)">
+ <div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px;flex-wrap:wrap;gap:12px">
+ <h2 style="font-family:var(--serif);font-style:italic;font-weight:400;font-size:24px;color:var(--metal-glow);margin:0">Walk here next</h2>
+ <div style="display:flex;gap:14px;align-items:baseline;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-mute)">
+ <span>radius
+ <select id="prio-radius" style="background:var(--noir);border:1px solid var(--rule);color:var(--ink);padding:3px 8px;font-size:11px">
+ <option value="0.25">0.25 mi</option>
+ <option value="0.5" selected>0.5 mi</option>
+ <option value="1.0">1.0 mi</option>
+ <option value="2.0">2.0 mi</option>
+ </select>
+ </span>
+ <span id="prio-summary">β</span>
+ </div>
+ </div>
+ <div id="priority-list" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:10px"></div>
+ <div style="font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-mute);margin-top:10px">
+ score = unpitched doors Γ (1 / miles from DW) Β· clears once Walked > 0
+ </div>
+</section>
+
<main id="list">
<div class="loading">Loading building rosterβ¦</div>
</main>
@@ -411,6 +432,48 @@ async function refreshDupes() {
loadDupeTotal();
+// βββ Priority "walk here next" section (iter 61) βββββββββββββββββββββ
+async function loadPriority() {
+ const radius = document.getElementById('prio-radius').value;
+ const list = document.getElementById('priority-list');
+ list.innerHTML = `<div style="color:var(--ink-mute);padding:14px;font-style:italic">scanning corridorβ¦</div>`;
+ try {
+ const data = await fetch(`/api/buildings/priority?max_miles=${radius}&min_unpitched=2&limit=15`).then(r => r.json());
+ const rows = data.rows || [];
+ document.getElementById('prio-summary').innerHTML =
+ `<b style="color:var(--metal-glow);font-family:var(--mono)">${data.count}</b> bldgs Β· <b style="color:var(--metal);font-family:var(--mono)">${data.total_unpitched_in_view}</b> unpitched doors`;
+ if (!rows.length) {
+ list.innerHTML = `<div style="color:var(--ink-mute);padding:14px;font-style:italic">No unwalked walkable buildings β all priority targets done. Widen the radius?</div>`;
+ return;
+ }
+ list.innerHTML = rows.map((b, i) => {
+ const isDW = (b.bldg_address || '').startsWith('15442 ');
+ return `
+ <div style="background:var(--noir-rise);border:1px solid var(--rule);${isDW ? 'border-color:var(--metal-glow);' : ''}padding:12px 14px;display:grid;grid-template-rows:auto auto auto;gap:6px">
+ <div style="display:flex;align-items:baseline;gap:10px">
+ <span style="font-family:var(--serif);font-style:italic;font-size:24px;color:var(--metal);min-width:30px">${String(i+1).padStart(2,'0')}</span>
+ <a href="/buildings.html?bldg=${encodeURIComponent(b.bldg_address)}" style="font-family:var(--serif);font-size:18px;color:var(--ink);text-decoration:none">${escapeHtml(b.bldg_address)}${isDW ? ' β‘' : ''}</a>
+ </div>
+ <div style="display:flex;gap:14px;font-size:10px;letter-spacing:.15em;color:var(--ink-mute);text-transform:uppercase">
+ <span>${b.city || ''}</span>
+ <span style="color:var(--metal-glow);font-family:var(--mono)">${b.dw_miles} mi</span>
+ <span><b style="color:var(--metal);font-family:var(--mono);font-size:13px">${b.unpitched}</b> unpitched</span>
+ <span style="color:var(--ink-mute)">/ ${b.tenants_total} total</span>
+ </div>
+ <div style="display:flex;gap:8px;font-size:9px;letter-spacing:.18em;text-transform:uppercase">
+ <span style="color:var(--metal-glow);font-family:var(--mono);background:rgba(212,182,131,0.06);padding:2px 8px">score ${b.priority_score}</span>
+ <a href="/walk-route.html?focus=${b.bldg_address.split(' ')[0]}" target="_blank" style="color:var(--metal);text-decoration:none;padding:2px 8px;border:1px solid var(--rule)">πΆ walk</a>
+ <a href="/buildings.html?bldg=${encodeURIComponent(b.bldg_address)}" target="_blank" style="color:var(--metal);text-decoration:none;padding:2px 8px;border:1px solid var(--rule)">π’ roster</a>
+ </div>
+ </div>`;
+ }).join('');
+ } catch (e) {
+ list.innerHTML = `<div style="color:var(--red);padding:14px">priority load failed: ${e.message}</div>`;
+ }
+}
+document.getElementById('prio-radius').addEventListener('change', loadPriority);
+loadPriority();
+
// Deep-link via ?bldg= URL param: pre-filter + auto-expand the matching card
(async () => {
const urlBldg = new URLSearchParams(location.search).get('bldg');
diff --git a/src/server/index.ts b/src/server/index.ts
index 72546b9..85ba2fe 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1143,6 +1143,43 @@ app.get('/api/buildings', async (req, res) => {
}
});
+// Priority list: buildings ranked by where Steve gets the most walkable doors per minute of walking time.
+// Score = unpitched_count Γ (1 / max(dw_miles, 0.05)) (proximity boost; closer = higher)
+// Filters: walking distance only, hasn't been walked yet, has unpitched doors.
+app.get('/api/buildings/priority', async (req, res) => {
+ try {
+ const limit = Math.min(parseInt(String(req.query.limit ?? '50'), 10) || 50, 200);
+ const maxMiles = parseFloat(String(req.query.max_miles ?? '1.0'));
+ const minUnpitched = parseInt(String(req.query.min_unpitched ?? '2'), 10) || 2;
+ const r = await query(
+ `SELECT bldg_address, city, zip, lat, lng,
+ tenants_total, pitched, walked, unpitched, replied, won, dw_miles, dominant_pitch_type,
+ ROUND(
+ (unpitched::numeric / GREATEST(dw_miles, 0.05)::numeric)::numeric,
+ 1
+ ) AS priority_score
+ FROM v_building_roster
+ WHERE dw_miles IS NOT NULL
+ AND dw_miles <= $1
+ AND walked = 0
+ AND unpitched >= $2
+ ORDER BY priority_score DESC, dw_miles ASC
+ LIMIT $3`,
+ [maxMiles, minUnpitched, limit]
+ );
+ const totalUnpitched = r.rows.reduce((a: number, x: any) => a + Number(x.unpitched || 0), 0);
+ res.json({
+ count: r.rowCount,
+ total_unpitched_in_view: totalUnpitched,
+ max_miles: maxMiles,
+ min_unpitched: minUnpitched,
+ rows: r.rows
+ });
+ } catch (e: any) {
+ res.status(500).json({ error: e.message });
+ }
+});
+
app.get('/api/buildings/dupe-stats', async (_req, res) => {
try {
const r = await query(`
β 6dbbf08 iter 60: recent activity feed on /today.html β /api/activity
Β·
back to Ventura Corridor
Β·
iter 62: walk-route priority-aware ordering β /api/pitches/w 31c4243 β