← back to Rentv 2026
auto-save: 2026-08-05T06:39:44 (5 files) — data/markets.json data/news.json data/ticker.json public/admin/pr-intelligence/pr-shared.js public/admin/pr-intelligence/content-drafts.html
ff6d1c63d2f03bcfaba656907d36c069e717e7c1 · 2026-08-05 06:39:45 -0700 · Steve Abrams
Files touched
M data/markets.jsonM data/news.jsonM data/ticker.jsonA public/admin/pr-intelligence/content-drafts.htmlM public/admin/pr-intelligence/pr-shared.js
Diff
commit ff6d1c63d2f03bcfaba656907d36c069e717e7c1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 5 06:39:45 2026 -0700
auto-save: 2026-08-05T06:39:44 (5 files) — data/markets.json data/news.json data/ticker.json public/admin/pr-intelligence/pr-shared.js public/admin/pr-intelligence/content-drafts.html
---
data/markets.json | 24 ++---
data/news.json | 2 +-
data/ticker.json | 12 +--
public/admin/pr-intelligence/content-drafts.html | 114 +++++++++++++++++++++++
public/admin/pr-intelligence/pr-shared.js | 1 +
5 files changed, 134 insertions(+), 19 deletions(-)
diff --git a/data/markets.json b/data/markets.json
index b5fa1c1d..a41faa5b 100644
--- a/data/markets.json
+++ b/data/markets.json
@@ -1,5 +1,5 @@
{
- "fetched_at": "2026-08-05T12:53:55.559Z",
+ "fetched_at": "2026-08-05T13:33:57.379Z",
"source": "Yahoo Finance — live (CBOE Treasury yields, Dow Jones/MSCI REIT indices)",
"curve": [
{
@@ -10,15 +10,15 @@
},
{
"label": "5-Year",
- "value": "4.33%",
- "change_bps": 0,
- "dir": "flat"
+ "value": "4.34%",
+ "change_bps": 1,
+ "dir": "up"
},
{
"label": "10-Year",
- "value": "4.62%",
- "change_bps": -1,
- "dir": "down"
+ "value": "4.63%",
+ "change_bps": 0,
+ "dir": "flat"
},
{
"label": "30-Year",
@@ -35,15 +35,15 @@
},
{
"label": "10Y – 3M (curve)",
- "value": "+89 bps",
+ "value": "+90 bps",
"dir": "up"
}
],
"reits": [
{
"label": "DJ U.S. Real Estate",
- "value": "396.09",
- "change_pct": -0.15,
+ "value": "394.06",
+ "change_pct": -0.51,
"dir": "down"
},
{
@@ -54,8 +54,8 @@
},
{
"label": "Vanguard REIT ETF (VNQ)",
- "value": "$98.92",
- "change_pct": -0.03,
+ "value": "$98.30",
+ "change_pct": -0.78,
"dir": "down"
}
],
diff --git a/data/news.json b/data/news.json
index f65c4ef2..c90bfdff 100644
--- a/data/news.json
+++ b/data/news.json
@@ -1,6 +1,6 @@
{
"source": "rentv.com — live",
- "fetched_at": "2026-08-05T12:53:55.312Z",
+ "fetched_at": "2026-08-05T13:33:57.170Z",
"count": 30,
"items": [
{
diff --git a/data/ticker.json b/data/ticker.json
index 9ea98b23..130c4e6b 100644
--- a/data/ticker.json
+++ b/data/ticker.json
@@ -1,5 +1,5 @@
{
- "fetched_at": "2026-08-05T12:53:55.530Z",
+ "fetched_at": "2026-08-05T13:33:57.351Z",
"la_office_vac": {
"label": "LA OFFICE VAC",
"value": "23.1%",
@@ -14,16 +14,16 @@
},
"cre_index": {
"label": "CRE INDEX",
- "value": "4,163",
- "change_pct": -0.15,
+ "value": "4,142",
+ "change_pct": -0.51,
"dir": "down",
"source": "Dow Jones U.S. Real Estate"
},
"ten_yr": {
"label": "10-YR",
- "value": "4.62%",
- "change_bps": -1,
- "dir": "down",
+ "value": "4.63%",
+ "change_bps": 0,
+ "dir": "flat",
"source": "CBOE 10-Yr (^TNX)"
}
}
\ No newline at end of file
diff --git a/public/admin/pr-intelligence/content-drafts.html b/public/admin/pr-intelligence/content-drafts.html
new file mode 100644
index 00000000..356ad55e
--- /dev/null
+++ b/public/admin/pr-intelligence/content-drafts.html
@@ -0,0 +1,114 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
+<title>CRE PR Intelligence — Drafts</title>
+<link rel="stylesheet" href="/admin/pr-intelligence/pr-shared.css">
+<style>
+ .dr-seg{display:inline-flex;border:1px solid var(--line,#d7dde3);border-radius:8px;overflow:hidden;margin:0 0 14px}
+ .dr-seg button{font:inherit;font-size:12.5px;padding:6px 14px;border:0;background:#fff;color:var(--ink,#222);cursor:pointer}
+ .dr-seg button.on{background:#0a66c2;color:#fff}
+ .dr-src{font-size:11px;font-weight:700;border-radius:6px;padding:2px 7px}
+ .dr-src.rentv{background:#e7f0ff;color:#0a66c2} .dr-src.wire{background:#fff2e0;color:#8a5a00}
+ .dr-when{font-size:12px;color:var(--muted,#5b636b);white-space:nowrap}
+ .dr-actions a{font-size:12px;font-weight:600;margin-right:12px;cursor:pointer;text-decoration:none}
+ .dr-actions a.pub{color:#1c7a3e} .dr-actions a.del{color:#b23a1e}
+ .dr-field{margin:0 0 10px} .dr-field label{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted,#5b636b);margin:0 0 4px}
+ .dr-field input,.dr-field select,.dr-field textarea{width:100%;box-sizing:border-box;font:inherit;font-size:13px;padding:8px 10px;border:1px solid var(--line,#d7dde3);border-radius:8px;background:#fff}
+ .dr-field textarea{min-height:220px;resize:vertical}
+</style>
+</head>
+<body>
+<script src="/admin/pr-intelligence/pr-shared.js"></script>
+<script>
+(async function () {
+ const c = PR.frame('content-drafts', 'Drafts',
+ 'Blog drafts awaiting review. Outside-wire stories (copyright Option 1) land here as drafts — check the provenance, edit if needed, then Publish (the deliberate human gate) or Delete. Only you turn a wire story live.');
+
+ const seg = document.createElement('div'); seg.className = 'dr-seg';
+ seg.innerHTML = `<button data-s="draft" class="on">📝 Drafts</button><button data-s="published">✅ Published</button>`;
+ c.appendChild(seg);
+ const wrap = document.createElement('div'); c.appendChild(wrap);
+
+ let status = 'draft';
+ const isWire = (s) => s && s !== 'RENTV';
+ const fmtWhen = (iso) => iso ? new Date(iso).toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : '—';
+
+ const tbl = PR.table([
+ { key: 'title', label: 'Headline', render: (r) => `<b>${PR.esc(r.title || '(untitled)')}</b>${r.dek ? `<br><span style="color:var(--muted,#5b636b);font-size:12px">${PR.esc(String(r.dek).slice(0, 90))}</span>` : ''}` },
+ { key: 'source', label: 'Source', render: (r) => `<span class="dr-src ${isWire(r.source) ? 'wire' : 'rentv'}" title="${isWire(r.source) ? 'Outside wire — publishing is your call' : 'RENTV original'}">${PR.esc(r.source || 'RENTV')}</span>` },
+ { key: 'cat', label: 'Category', render: (r) => PR.badge(r.cat || 'RENTV', 'gray') },
+ { key: 'created_at', label: 'Created', render: (r) => `<span class="dr-when" title="${PR.esc(r.created_at || '')}">🕓 ${fmtWhen(r.created_at)}</span>` },
+ { key: '_a', label: '', render: (r) => `<div class="dr-actions">
+ <a data-act="edit" data-id="${r.id}">Edit</a>
+ <a data-act="preview" data-id="${r.id}" href="/post/${r.id}" target="_blank" rel="noopener noreferrer">Preview</a>
+ ${status === 'draft'
+ ? `<a class="pub" data-act="publish" data-id="${r.id}">🚀 Publish</a>`
+ : `<a data-act="unpublish" data-id="${r.id}">Unpublish</a>`}
+ <a class="del" data-act="delete" data-id="${r.id}">Delete</a>
+ </div>` },
+ ]);
+ wrap.appendChild(tbl.el);
+
+ let rows = [];
+ async function load() {
+ try {
+ const j = await PR.api('/../posts?status=' + status);
+ rows = (j.items || []);
+ tbl.render(rows);
+ } catch (e) { PR.toast(e.message, true); }
+ }
+
+ seg.querySelectorAll('button').forEach((b) => b.addEventListener('click', () => {
+ seg.querySelectorAll('button').forEach((x) => x.classList.remove('on'));
+ b.classList.add('on'); status = b.dataset.s; load();
+ }));
+
+ // Delegated row actions
+ wrap.addEventListener('click', (e) => {
+ const a = e.target.closest('[data-act]'); if (!a) return;
+ const act = a.getAttribute('data-act'), id = a.getAttribute('data-id');
+ const row = rows.find((r) => r.id === id);
+ if (act === 'preview') return; // native link
+ e.preventDefault();
+ if (act === 'edit') return openEditor(row);
+ if (act === 'publish') {
+ if (!confirm(isWire(row.source) ? `Publish this OUTSIDE-WIRE story (${row.source}) live to /blog? This is your editorial call.` : 'Publish this entry live to /blog?')) return;
+ return PR.guard(async () => { await PR.api('/../posts/' + id, { method: 'PUT', body: { status: 'published' } }); load(); }, 'Published');
+ }
+ if (act === 'unpublish') return PR.guard(async () => { await PR.api('/../posts/' + id, { method: 'PUT', body: { status: 'draft' } }); load(); }, 'Moved to draft');
+ if (act === 'delete') {
+ if (!confirm('Delete this draft permanently?')) return;
+ return PR.guard(async () => { await PR.api('/../posts/' + id, { method: 'DELETE' }); load(); }, 'Deleted');
+ }
+ });
+
+ function openEditor(r) {
+ const d = PR.drawer();
+ const body = d.open(`Edit draft ${isWire(r.source) ? '· <span class="dr-src wire">' + PR.esc(r.source) + '</span>' : ''}`, `
+ <div class="dr-field"><label>Headline</label><input id="e_title" value="${PR.esc(r.title || '')}"></div>
+ <div class="dr-field"><label>Category</label>
+ <select id="e_cat">${['Sales','Leases','Financing','Development','Multifamily','Office','Retail','Industrial','People','Markets','RENTV'].map((x) => `<option ${x === (r.cat || 'RENTV') ? 'selected' : ''}>${x}</option>`).join('')}</select></div>
+ <div class="dr-field"><label>Standfirst (dek)</label><input id="e_dek" value="${PR.esc(r.dek || '')}"></div>
+ <div class="dr-field"><label>Image URL</label><input id="e_image" value="${PR.esc(r.image || '')}"></div>
+ <div class="dr-field"><label>Body</label><textarea id="e_body">${PR.esc(r.body || '')}</textarea></div>
+ <div class="dr-field"><button class="btn" id="e_save">💾 Save</button>
+ ${status === 'draft' ? '<button class="btn" id="e_savepub">💾 Save & Publish</button>' : ''}
+ <span class="dr-when" id="e_meta"></span></div>`);
+ const val = (id) => body.querySelector('#' + id).value;
+ const payload = () => ({ title: val('e_title').trim(), cat: val('e_cat'), dek: val('e_dek').trim(), image: val('e_image').trim(), body: val('e_body') });
+ body.querySelector('#e_save').addEventListener('click', () => PR.guard(async () => {
+ await PR.api('/../posts/' + r.id, { method: 'PUT', body: payload() }); d.close(); load();
+ }, 'Saved'));
+ const sp = body.querySelector('#e_savepub');
+ if (sp) sp.addEventListener('click', () => {
+ if (!confirm(isWire(r.source) ? `Publish this outside-wire story (${r.source}) live?` : 'Publish live?')) return;
+ PR.guard(async () => { await PR.api('/../posts/' + r.id, { method: 'PUT', body: { ...payload(), status: 'published' } }); d.close(); load(); }, 'Published');
+ });
+ }
+
+ load();
+})();
+</script>
+</body>
+</html>
diff --git a/public/admin/pr-intelligence/pr-shared.js b/public/admin/pr-intelligence/pr-shared.js
index ad3aa70c..dfdd0074 100644
--- a/public/admin/pr-intelligence/pr-shared.js
+++ b/public/admin/pr-intelligence/pr-shared.js
@@ -11,6 +11,7 @@
['review', '✅', 'Review Queue'],
['__sepC', '', 'Content'],
['content-studio', '📰', 'Content Studio'],
+ ['content-drafts', '📝', 'Drafts'],
['linkedin-posting', '📣', 'LinkedIn Posting'],
['__sep1', '', 'Outreach'],
['campaigns', '📮', 'Campaigns'],
← 6462c58f auto-save: 2026-08-05T06:09:34 (3 files) — data/markets.json
·
back to Rentv 2026
·
auto-save: 2026-08-05T07:09:54 (3 files) — data/markets.json f7026ca8 →