← back to Rentv 2026
public/admin/pr-intelligence/content-drafts.html
115 lines
<!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="${PR.esc(r.id)}">Edit</a>
<a data-act="preview" data-id="${PR.esc(r.id)}" href="/post/${encodeURIComponent(r.id)}" target="_blank" rel="noopener noreferrer">Preview</a>
${status === 'draft'
? `<a class="pub" data-act="publish" data-id="${PR.esc(r.id)}">🚀 Publish</a>`
: `<a data-act="unpublish" data-id="${PR.esc(r.id)}">Unpublish</a>`}
<a class="del" data-act="delete" data-id="${PR.esc(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) => String(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) ? ' — ' + PR.esc(r.source) : ''), `
<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>