[object Object]

← back to Rentv 2026

PR intel: correspondence timeline on the contact drawer

ca159afbcb6df847cb7a73b011f3659689a836c8 · 2026-08-06 09:24:03 -0700 · Steve

Contact detail now shows a Correspondence section: every Gmail-synced message
to/from the contact, direction (▲out/▼in), subject, and real date+time
(toLocaleString — date AND time per the admin-card rule). Empty state prompts
to connect Gmail on the Credentials page. Reads GET /people/:id/timeline
(tenant-scoped). Verified against the live endpoint.

Files touched

Diff

commit ca159afbcb6df847cb7a73b011f3659689a836c8
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 09:24:03 2026 -0700

    PR intel: correspondence timeline on the contact drawer
    
    Contact detail now shows a Correspondence section: every Gmail-synced message
    to/from the contact, direction (▲out/▼in), subject, and real date+time
    (toLocaleString — date AND time per the admin-card rule). Empty state prompts
    to connect Gmail on the Credentials page. Reads GET /people/:id/timeline
    (tenant-scoped). Verified against the live endpoint.
---
 public/admin/pr-intelligence/people.html | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/public/admin/pr-intelligence/people.html b/public/admin/pr-intelligence/people.html
index 5828ddf6..790cfd18 100644
--- a/public/admin/pr-intelligence/people.html
+++ b/public/admin/pr-intelligence/people.html
@@ -113,6 +113,8 @@
       <h3 style="margin:14px 0 8px">Outreach</h3>
       ${p.outreach.map((m) => `<div class="gatechk">${PR.statusBadge(m.status)} <span>${PR.esc(m.subject || '(no subject)')}</span>
         <span style="color:var(--muted);margin-left:auto">${PR.dateShort(m.sent_at || m.drafted_at)}</span></div>`).join('') || '<div class="empty">No outreach yet</div>'}
+      <h3 style="margin:14px 0 8px">Correspondence <span style="font-weight:400;color:var(--muted);font-size:12px">(email dates &amp; times, synced from Gmail)</span></h3>
+      <div id="e-timeline"><div class="empty">Loading…</div></div>
       <h3 style="margin:14px 0 8px">Audit trail</h3>
       ${p.recent_activity.slice(0, 12).map((a) => `<div class="gatechk"><span style="color:var(--muted)">${PR.dateShort(a.created_at)}</span>
         <span>${PR.esc(a.activity)}</span><span style="color:var(--muted)">${PR.esc(a.actor || '')}</span></div>`).join('')}`);
@@ -122,6 +124,16 @@
         ? '<div class="b green" style="margin-top:8px">✅ Outreach-ready</div>'
         : '<div class="evi" style="border-color:var(--warn)"><b>Not outreach-ready:</b> ' + r.reasons.map(PR.esc).join('; ') + '</div>';
     });
+    // Correspondence timeline (Gmail-synced messages, with real date + time)
+    PR.api('/people/' + id + '/timeline').then((r) => {
+      const el = body.querySelector('#e-timeline'); if (!el) return;
+      const rows = (r && r.rows) || [];
+      if (!rows.length) { el.innerHTML = '<div class="empty">No correspondence yet — connect Gmail on the Credentials page and run a sync.</div>'; return; }
+      const arrow = (d) => d === 'inbound' ? '<span title="received" style="color:#0a8a4a">▼ in</span>' : '<span title="sent" style="color:#0b6cff">▲ out</span>';
+      const when = (m) => { const t = m.corresponded_at || m.sent_at || m.replied_at; return t ? new Date(t).toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : '—'; };
+      el.innerHTML = rows.map((m) => `<div class="gatechk">${arrow(m.direction)} <span>${PR.esc(m.subject || '(no subject)')}</span>
+        <span style="color:var(--muted);margin-left:auto" title="${PR.esc(m.corresponded_at || '')}">🕓 ${PR.esc(when(m))}</span></div>`).join('');
+    }).catch(() => { const el = body.querySelector('#e-timeline'); if (el) el.innerHTML = '<div class="empty">Timeline unavailable.</div>'; });
     body.querySelector('#e-save').addEventListener('click', async () => {
       await PR.guard(() => PR.api('/people/' + id, { method: 'PUT', body: {
         exact_title: body.querySelector('#e-title').value,

← df653c37 auto-save: 2026-08-06T09:20:28 (10 files) — .deploy.conf dat  ·  back to Rentv 2026  ·  News map: market-activity stats panel (count + deal volume + 27f2cd04 →