← back to Commercialrealestate
docs/CRCP-UI-STANDARD.md
96 lines
# CRCP Viewer — Canonical UI Standard (TK-10703)
Reference implementation: **`public/just-listed.html`**. Every data-grid page is brought to this bar.
This doc is the durable contract for the standardization ticket, written cycle 7 (2026-08-21).
## The standard (what a canonical CRCP grid page has)
1. **Day/night theme** — `/crcp-theme.css` + `/crcp-theme.js` (the `crcp-theme` localStorage key + a
☀️Day/🌙Night toggle in the header). Light mode must actually flip surfaces (no hardcoded dark
literals — use `var(--card/--bg2/--headbg/--line2)`; the Cycle-6 lesson).
2. **Corner nav** — `/corner-nav.js` (logo UL, hamburger UR).
3. **Grid controls** — a SORTABLE, field-toggleable, multi-view grid. TWO valid implementations exist
in this repo, both acceptable:
- `/three-view.{css,js}` — Grid / List / Table segmented view + all-field `#tvSort`
(used by just-listed, residential-brokers, licensed-agents, broker-grid, mls, condos…).
- `/nav-agent/nav-agent.js` (+ `/column-manager.js`, `/col-resize.js`, `/lending-grid.js`) — the
universal Nav Agent grid-controls drop-in (field-toggle panel, sort-every-field, resize)
(used by the whole lending family: lenders, lending, credit-unions, sba-lenders, mortgage-market).
Do **not** stack both on one page. Pick the one that matches the page's existing renderer.
4. **Full-spec modal / row-expand** — click a row → detail (property/agent history). Shared:
`/row-notes.js` (server-synced notes via `/api/agent-notes`, per POPUP-COLLISION-FIX doc).
5. **Always-on contact strip (HARD RULE, Steve, emphatic):** every page that lists **people**
(brokers / agents) ALWAYS shows their **phone AND email**, in every view. See non-fabrication rule.
6. Nice-to-have (reference has it, not required on every page): middle chat bar (`/api/claude-chat`),
new-only toggle + crawl-speed drip.
## HARD RULE — contacts, and the non-fabrication gate
The always-on phone/email rule applies **only where the data has an individual person**. It must be
satisfied **truthfully** — never by fabricating a join. This is the Cody-gate lesson (a name-only
join glued 38 Real Brokerage agents to the wrong person / published Larry Hernandez's phone for
Susan Stone). Rules:
- Join on **address** (authoritative) or **name + firm agreement** — never name alone.
- The authoritative address→agent contact source is **`GET /api/listing-brokers`** →
`rows[]{ address, city, name, phone, email, website }`. Join a listing to its agent by `na(address)`.
- Unconfirmed → DRE-lookup fallback contact strip, or a source link. Never invent phone/email.
## DATA REALITY (why most pages are NOT contact gaps)
Verified live against `scripts/serve.js` (:9911) with an app session:
| Endpoint | Shape | Individual contacts? |
|---|---|---|
| `/api/gov-agents` | `metros[]` (metro rollup, counts) | NO — aggregate |
| `/api/lending` (lenders/credit-unions/sba/mortgage-market) | `datasets[]` institutional stats | NO — institutions |
| `/api/closed-sales` (sales) | `rows[]` address/price/beds/sqft | NO — property comps |
| `/api/rent-rolls` | property underwriting | NO |
| `/api/direct-listings` | `listings[]` incl. `agent`,`firm` (names only) | PARTIAL — join for phone/email |
| `/api/contractors` | `results[]` incl. `phone` (CSLB; no email published) | phone-only (data limit, not a bug) |
| `/api/listing-brokers` | `rows[]` address,name,phone,email,website | YES — the join source |
## Page ledger (cycle 7)
**Compliant / done (prior cycles):** just-listed, mls, condos, licensed, licensed-agents,
residential-brokers, broker-grid, fha-leads, fha-loans, valley-pools, firms, crcp, agent, broker,
brokers, contractors, + lending family (lenders/lending/credit-unions/sba-lenders/mortgage-market/
loan-officers via nav-agent) + fleet theme/corner-nav.
**Legitimately EXEMPT (with reason):**
- `login.html` — auth gate.
- `agent.html`, `broker.html` — single-record DETAIL profiles (no grid; contacts already shown).
- `brokers.html` — Broker Mind-Map (graph viz, not a grid).
- `map.html` — Property Map (map, not a grid; contacts in popups).
- `graphics.html`, `admin.html` — charts / internal admin (no individual contacts).
- `cities-inline.html` — 34-line embed fragment (no chrome by design).
**Remaining cycle-7 work:**
- `direct-listings.html` — add grid-controls + ALWAYS-ON phone/email via `/api/listing-brokers`
address-join (agent+firm already shown). ← highest value
- `linkedin.html` — grid-controls + broker contact strip (firm-confirmed).
- `firecities.html`, `recities.html` — add corner-nav + day/night crcp-theme (preserve bespoke accent).
- `property.html` — ensure contact strip when a listing agent exists (address-join).
## Verification recipe (local, authed)
The whole site is session-gated (Steve 2026-08-20). To verify a page renders data:
```sh
# throwaway self-register → capture crcp_sid cookie
curl -s -c /tmp/crcp-sid.txt -X POST http://127.0.0.1:9911/auth/register \
-H 'Content-Type: application/json' \
-d '{"username":"tk10703verify","password":"Verify10703!","name":"TK10703 Verify"}'
# then load pages with the cookie (Playwright: add cookie crcp_sid; curl: -b /tmp/crcp-sid.txt)
```
Headless gate per page: HTTP 200, **0 swallowed JS errors**, view-toggle works, sort reorders,
tel/mailto counts > 0 on people pages, both themes flip surfaces. Shared assets (three-view.js,
nav-agent.js, crcp-theme.css, corner-nav.js, row-notes.js) are **linked, never edited** by page
fixers (avoids write races between parallel sessions — see the ticket's popup-collision note).
## Gate
Local file edits + commits are reversible (git). **Deploy to Kamatera (`/root/public-projects/
commercialrealestate`, :9911) is customer-facing and GATED** — drafts to pending-approval, never
auto-fired from this ticket.