← back to Nationalrealestate
deals/contractors/feed: make every displayed table column sortable
e36e0ecca1cd19b8afd592129dd55716ef8b876d · 2026-08-13 08:44:32 -0700 · Steve
deals.html: surface doc_number as a Table column + sort-select options
(doc_number:asc/desc) + click-to-sort header; server whitelist already had
pe.doc_number (injection-safe fixed ORDER BY fragment).
contractors.html: Phone was a displayed-but-unsortable column (bare <th> with
no data-k/indicator). Added phone to server SORT_COLS (c.phone), moved Phone
into TBL_COLS so its header is click-sortable + arrow-indicated, and added
Phone sort-select options.
commercial-feed.html: use_desc (Use) column was click-sortable but missing
from the sort <select> — added Use A→Z option (server SORT_COLS already had it).
Every column rendered in each Table view is now sortable (select + click) or,
for the action-only detail/open cell, correctly carries no sort affordance.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M public/commercial-feed.htmlM public/contractors.htmlM public/deals.htmlM src/server/contractors.ts
Diff
commit e36e0ecca1cd19b8afd592129dd55716ef8b876d
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Aug 13 08:44:32 2026 -0700
deals/contractors/feed: make every displayed table column sortable
deals.html: surface doc_number as a Table column + sort-select options
(doc_number:asc/desc) + click-to-sort header; server whitelist already had
pe.doc_number (injection-safe fixed ORDER BY fragment).
contractors.html: Phone was a displayed-but-unsortable column (bare <th> with
no data-k/indicator). Added phone to server SORT_COLS (c.phone), moved Phone
into TBL_COLS so its header is click-sortable + arrow-indicated, and added
Phone sort-select options.
commercial-feed.html: use_desc (Use) column was click-sortable but missing
from the sort <select> — added Use A→Z option (server SORT_COLS already had it).
Every column rendered in each Table view is now sortable (select + click) or,
for the action-only detail/open cell, correctly carries no sort affordance.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/commercial-feed.html | 1 +
public/contractors.html | 6 ++++--
public/deals.html | 5 ++++-
src/server/contractors.ts | 1 +
4 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/public/commercial-feed.html b/public/commercial-feed.html
index 98fe8c6..5db4980 100644
--- a/public/commercial-feed.html
+++ b/public/commercial-feed.html
@@ -83,6 +83,7 @@
<option value="address:desc">Address Z→A</option>
<option value="city:asc">City A→Z</option>
<option value="type:asc">Type A→Z</option>
+ <option value="use_desc:asc">Use A→Z</option>
<option value="sqft:desc">SqFt ↓</option>
<option value="sqft:asc">SqFt ↑</option>
<option value="year_built:desc">Built newest</option>
diff --git a/public/contractors.html b/public/contractors.html
index 5b77c30..fa8d63b 100644
--- a/public/contractors.html
+++ b/public/contractors.html
@@ -120,6 +120,8 @@
<option value="expire_date:desc">Expires latest</option>
<option value="expire_date:asc">Expires soonest</option>
<option value="primary_class:asc">Primary class A→Z</option>
+ <option value="phone:asc">Phone ↑</option>
+ <option value="phone:desc">Phone ↓</option>
</select>
<span class="cg-seg" id="viewseg">
<button data-view="grid" class="active" title="Grid">▦ Grid</button>
@@ -288,14 +290,14 @@ function card(r){
const TBL_COLS = [
['business_name','Business'],['license_no','License #'],['license_status','Status'],
['city','City'],['county','County'],['zip','ZIP'],['primary_class','Class'],
- ['issue_date','Issued'],['expire_date','Expires'],
+ ['issue_date','Issued'],['expire_date','Expires'],['phone','Phone'],
];
function renderTable(){
const [sk,sd]=SORT.split(':');
const arrow=k=> k===sk ? (sd==='desc'?' ▾':' ▴') : '';
let h='<table class="ctrtbl"><thead><tr>'+
TBL_COLS.map(([k,l])=>`<th data-k="${k}" class="${k===sk?'sorted':''}">${esc(l)}${arrow(k)}</th>`).join('')+
- '<th>Phone</th></tr></thead><tbody>';
+ '</tr></thead><tbody>';
for(const r of PAGE){
const cls=(r.classifications||[]).join(', ');
h+='<tr>'+
diff --git a/public/deals.html b/public/deals.html
index c2981d2..e25088d 100644
--- a/public/deals.html
+++ b/public/deals.html
@@ -119,6 +119,8 @@
<option value="year_built:asc">Built oldest</option>
<option value="grantor:asc">Seller A→Z</option>
<option value="grantee:asc">Buyer A→Z</option>
+ <option value="doc_number:asc">Doc # A→Z</option>
+ <option value="doc_number:desc">Doc # Z→A</option>
</select>
<span class="cg-seg" id="viewseg">
<button data-view="grid" title="Grid">▦ Grid</button>
@@ -271,7 +273,7 @@ function headSort(k){ const c=curSort(); const nd=(c.k===k)?(c.d==='asc'?'desc':
// ── TABLE view: dense all-columns spreadsheet, EVERY header click-to-sort ──
const TBL=[['sale_date','Sale date'],['address','Address'],['city','City'],['ctype','Type'],
['sale_price','Price','num'],['sqft','SqFt','num'],['year_built','Built','num'],
- ['grantor','Seller'],['grantee','Buyer']];
+ ['grantor','Seller'],['grantee','Buyer'],['doc_number','Doc #']];
function renderTable(rows){
const c=curSort(); const arr=k=> k===c.k?(c.d==='desc'?' ▾':' ▴'):'';
let h='<table><thead><tr>'+
@@ -287,6 +289,7 @@ function renderTable(rows){
h+=`<tr><td>${esc(r.sale_date)}</td><td>${addrOpen}</td><td>${cityDrill}</td><td>${typeDrill}</td>`+
`<td class="num">${priceDrill}</td><td class="num">${r.sqft!=null?fmt(r.sqft):''}</td><td class="num">${yearDrill}</td>`+
`<td>${esc(r.grantor)||'<span class="lbl">—</span>'}</td><td>${esc(r.grantee)||'<span class="lbl">—</span>'}</td>`+
+ `<td>${esc(r.doc_number)||'<span class="lbl">—</span>'}</td>`+
`<td><a class="open" href="${drillUrl({id:r.id})}" data-id="${r.id}" title="Open deal detail">detail ›</a></td></tr>`;
}
h+='</tbody></table>';
diff --git a/src/server/contractors.ts b/src/server/contractors.ts
index a9fdb5b..0c981a9 100644
--- a/src/server/contractors.ts
+++ b/src/server/contractors.ts
@@ -58,6 +58,7 @@ const SORT_COLS: Record<string, string> = {
issue_date: 'c.issue_date',
expire_date: 'c.expire_date',
primary_class: 'c.primary_class',
+ phone: 'c.phone',
};
/** Resolve ORDER BY from ?sort=&order= (whitelist); defaults to business_name ASC. */
← 27b936f nav-agent: v1.1 merge-mode asset refresh
·
back to Nationalrealestate
·
chore: gitignore *.bak (prevent tooling backup cruft) [TK-10 5a2f624 →