← back to Marketing Command Center
Show last 6 posts per owned account in the #vendors Owned·DW Fleet section
c12730bbc565f60eb94c9b2f4f88231d9e69c390 · 2026-08-25 10:14:13 -0700 · Steve
The owned-fleet section only showed name + link. Now each of the 35 owned accounts
renders its last 6 Instagram posts inline (thumb + title + date · type), sourced from
the fleet post ledger (/api/ig-activity/posts — same data as the IG Activity panel),
reusing the existing .ig-li styling the external-vendor rows use. Best-effort: rows
still render if the ledger is unreachable; the 6 no-creds accounts show 'No posts yet'.
Verified: 35 blocks, 170 post rows, 6 empty-states, thumbnails + styling applied, no
page errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M public/panels/vendors.js
Diff
commit c12730bbc565f60eb94c9b2f4f88231d9e69c390
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Aug 25 10:14:13 2026 -0700
Show last 6 posts per owned account in the #vendors Owned·DW Fleet section
The owned-fleet section only showed name + link. Now each of the 35 owned accounts
renders its last 6 Instagram posts inline (thumb + title + date · type), sourced from
the fleet post ledger (/api/ig-activity/posts — same data as the IG Activity panel),
reusing the existing .ig-li styling the external-vendor rows use. Best-effort: rows
still render if the ledger is unreachable; the 6 no-creds accounts show 'No posts yet'.
Verified: 35 blocks, 170 post rows, 6 empty-states, thumbnails + styling applied, no
page errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/panels/vendors.js | 40 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/public/panels/vendors.js b/public/panels/vendors.js
index 4b7dd5a..aee619f 100644
--- a/public/panels/vendors.js
+++ b/public/panels/vendors.js
@@ -18,8 +18,11 @@ window.MCC_PANELS['vendors'] = {
setStats();
// Owned · DW Fleet — all 35 DW-OWNED Instagram accounts from the canonical
- // source (window.MCC_ACCOUNTS). Rendered as a native vend-block list, clearly
- // separated ABOVE the external vendor roster. Additive; never touches vendors.
+ // source (window.MCC_ACCOUNTS), each showing its LAST 6 posts inline (thumb +
+ // title + date · type), mirroring the external-vendor rows. The owned posts come
+ // from the fleet post ledger (/api/ig-activity/posts — the same data the IG
+ // Activity panel renders), NOT the vendor Business-Discovery cache. Additive;
+ // never touches the external vendor roster.
(async () => {
const host = root.querySelector('#vend-owned-rows');
if (!host) return;
@@ -27,14 +30,47 @@ window.MCC_PANELS['vendors'] = {
try { owned = (window.MCC_ACCOUNTS && await window.MCC_ACCOUNTS.load()) || []; }
catch { host.innerHTML = '<div class="muted">Failed to load owned accounts.</div>'; return; }
if (!owned.length) { host.innerHTML = '<div class="muted">No owned accounts found.</div>'; return; }
+
+ // Group the fleet ledger by handle → last 6 each (newest first). Best-effort:
+ // if the ledger is unreachable, the rows still render (just without posts).
+ const byHandle = {};
+ try {
+ const j = await (await fetch(location.origin + '/api/ig-activity/posts', { credentials: 'same-origin' })).json();
+ for (const p of (j.posts || [])) { (byHandle[p.handle] = byHandle[p.handle] || []).push(p); }
+ for (const k of Object.keys(byHandle)) byHandle[k].sort((a, b) => new Date(b.ts) - new Date(a.ts));
+ } catch { /* ledger unreachable — rows render without posts */ }
+
+ const ownedPosts = (handle) => {
+ const list = (byHandle[handle] || []).slice(0, 6);
+ if (!list.length) return '<div class="muted" style="font-size:11px;padding:2px 2px 4px">No posts yet.</div>';
+ const items = list.map(p => {
+ const cap = esc((p.product_title || '').replace(/\s+/g, ' ').slice(0, 120)) || '(no title)';
+ const when = p.ts ? String(p.ts).slice(0, 10) : '';
+ const kind = esc(p.kind || 'IMAGE');
+ const thumb = p.image_url
+ ? `<img class="ig-li-thumb" loading="lazy" src="${esc(p.image_url)}" alt="" onerror="this.style.visibility='hidden'">`
+ : `<span class="ig-li-thumb"></span>`;
+ return `<a class="ig-li" href="${esc(p.permalink || '#')}" target="_blank" rel="noopener noreferrer">
+ ${thumb}
+ <span class="ig-li-body">
+ <span class="ig-li-cap">${cap} <span class="ig-li-ext">↗</span></span>
+ <span class="ig-li-meta">${when ? when + ' · ' : ''}${kind}</span>
+ </span></a>`;
+ }).join('');
+ return `<div class="ig-list">${items}</div>`;
+ };
+
const rows = [...owned].sort((a, b) => String(a.name || a.handle).localeCompare(String(b.name || b.handle)));
host.innerHTML = rows.map(a => {
const h = esc(a.handle);
+ const n = (byHandle[a.handle] || []).length;
return `<div class="vend-block" style="border-bottom:1px solid var(--line);padding:10px 2px;background:#faf6ee">
<div class="row" style="justify-content:space-between;align-items:center">
<div style="min-width:200px"><b>${esc(a.name || a.handle)}</b> <span class="pill">OURS</span></div>
<div style="flex:1"><a class="lnk" href="https://www.instagram.com/${h}/" target="_blank" rel="noopener noreferrer">@${h} ↗</a></div>
+ <div style="min-width:70px;text-align:right;font-variant-numeric:tabular-nums" title="posts in the fleet ledger">${n ? n + ' posts' : ''}</div>
</div>
+ ${ownedPosts(a.handle)}
</div>`;
}).join('');
})();
← 37ff9a8 mcc: credential-safe fetch guard in shell (inoculate root-re
·
back to Marketing Command Center
·
Add "Make it ours" DW-original creative system to #vendors a 6b6da55 →