← back to Marketing Command Center
Vendor IG Reporting panel: DW + 37 vendor Instagram accounts roster (33 found, ~4.5M combined reach), sortable, for social/competitive reporting
dc913ce0aceab3bc4d48606f1ca80a996ee3c7d9 · 2026-06-10 08:52:16 -0700 · Steve
Files touched
A data/vendor-instagram.jsonM modules/registry.jsA modules/vendors/index.jsA public/panels/vendors.htmlA public/panels/vendors.js
Diff
commit dc913ce0aceab3bc4d48606f1ca80a996ee3c7d9
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jun 10 08:52:16 2026 -0700
Vendor IG Reporting panel: DW + 37 vendor Instagram accounts roster (33 found, ~4.5M combined reach), sortable, for social/competitive reporting
---
data/vendor-instagram.json | 40 ++++++++++++++++++++++++++++++++++++++++
modules/registry.js | 1 +
modules/vendors/index.js | 39 +++++++++++++++++++++++++++++++++++++++
public/panels/vendors.html | 14 ++++++++++++++
public/panels/vendors.js | 35 +++++++++++++++++++++++++++++++++++
5 files changed, 129 insertions(+)
diff --git a/data/vendor-instagram.json b/data/vendor-instagram.json
new file mode 100644
index 0000000..9a984a7
--- /dev/null
+++ b/data/vendor-instagram.json
@@ -0,0 +1,40 @@
+[
+ { "brand": "Designer Wallcoverings", "vendorCode": "dw", "handle": "@designerwallcoverings", "url": "https://www.instagram.com/designerwallcoverings/", "followers": "15K", "verified": false, "note": "our account" },
+ { "brand": "1838 Wallcoverings", "vendorCode": "1838", "handle": "@1838_wallcoverings", "url": "https://www.instagram.com/1838_wallcoverings/", "followers": "20K", "verified": false, "note": "" },
+ { "brand": "Arteriors Home", "vendorCode": "arteriors", "handle": "@arteriorshome", "url": "https://www.instagram.com/arteriorshome/", "followers": "346K", "verified": false, "note": "" },
+ { "brand": "Atomic50 Ceilings", "vendorCode": "atomic50", "handle": "none found", "url": "", "followers": "", "verified": false, "note": "no official IG" },
+ { "brand": "Bespoke Wallcoverings", "vendorCode": "bespoke", "handle": "none found", "url": "", "followers": "", "verified": false, "note": "no official IG" },
+ { "brand": "BN Walls", "vendorCode": "bnwalls", "handle": "@bnwallsofficial", "url": "https://www.instagram.com/bnwallsofficial/", "followers": "12K", "verified": false, "note": "" },
+ { "brand": "Brewster Home Fashions", "vendorCode": "brewster", "handle": "@brewsterhome", "url": "https://www.instagram.com/brewsterhome/", "followers": "18K", "verified": false, "note": "" },
+ { "brand": "Colour & Design", "vendorCode": "colouranddesign", "handle": "@colour.design", "url": "https://www.instagram.com/colour.design/", "followers": "1.8K", "verified": false, "note": "" },
+ { "brand": "Contrado", "vendorCode": "contrado", "handle": "@contradouk", "url": "https://www.instagram.com/contradouk/", "followers": "47K", "verified": false, "note": "" },
+ { "brand": "Dedar", "vendorCode": "dedar", "handle": "@dedarmilano", "url": "https://www.instagram.com/dedarmilano/", "followers": "218K", "verified": false, "note": "" },
+ { "brand": "DeNovo Wall", "vendorCode": "denovowall", "handle": "@denovowall", "url": "https://www.instagram.com/denovowall/", "followers": "1.4K", "verified": false, "note": "" },
+ { "brand": "Designtex", "vendorCode": "designtex", "handle": "@designtex_inc", "url": "https://www.instagram.com/designtex_inc/", "followers": "30K", "verified": false, "note": "" },
+ { "brand": "Fabricut", "vendorCode": "fabricut", "handle": "@fabricut", "url": "https://www.instagram.com/fabricut/", "followers": "97K", "verified": false, "note": "" },
+ { "brand": "Folia Fabrics", "vendorCode": "folia", "handle": "none found", "url": "", "followers": "", "verified": false, "note": "no official IG" },
+ { "brand": "Hygge & West", "vendorCode": "hygge", "handle": "@hyggeandwest", "url": "https://www.instagram.com/hyggeandwest/", "followers": "219K", "verified": false, "note": "" },
+ { "brand": "Justin David Textiles", "vendorCode": "justindavid", "handle": "@justindavidhospitalitytextiles", "url": "https://www.instagram.com/justindavidhospitalitytextiles/", "followers": "1.9K", "verified": false, "note": "" },
+ { "brand": "Knoll Textiles", "vendorCode": "knoll", "handle": "@knolltextiles", "url": "https://www.instagram.com/knolltextiles/", "followers": "41K", "verified": false, "note": "" },
+ { "brand": "Maya Romanoff", "vendorCode": "maya-romanoff", "handle": "@mayaromanoff", "url": "https://www.instagram.com/mayaromanoff/", "followers": "26K", "verified": false, "note": "" },
+ { "brand": "MINDTHEGAP", "vendorCode": "mindthegap", "handle": "@mindtgap", "url": "https://www.instagram.com/mindtgap/", "followers": "83K", "verified": false, "note": "" },
+ { "brand": "Mural Source", "vendorCode": "muralsource", "handle": "@themuralsource", "url": "https://www.instagram.com/themuralsource/", "followers": "41K", "verified": false, "note": "" },
+ { "brand": "Naturale 54", "vendorCode": "naturale54", "handle": "none found", "url": "", "followers": "", "verified": false, "note": "no official IG" },
+ { "brand": "Osborne & Little", "vendorCode": "osborne", "handle": "@osborneandlittle", "url": "https://www.instagram.com/osborneandlittle/", "followers": "225K", "verified": false, "note": "" },
+ { "brand": "Rebel Walls", "vendorCode": "rebel-walls", "handle": "@rebelwalls", "url": "https://www.instagram.com/rebelwalls/", "followers": "357K", "verified": false, "note": "" },
+ { "brand": "Romo", "vendorCode": "romo", "handle": "@romo_fabrics", "url": "https://www.instagram.com/romo_fabrics/", "followers": "135K", "verified": false, "note": "" },
+ { "brand": "Stout Textiles", "vendorCode": "stout", "handle": "@stouttextiles", "url": "https://www.instagram.com/stouttextiles/", "followers": "23K", "verified": false, "note": "" },
+ { "brand": "Timorous Beasties", "vendorCode": "timorous", "handle": "@timorous_beasties", "url": "https://www.instagram.com/timorous_beasties/", "followers": "150K", "verified": false, "note": "" },
+ { "brand": "WallQuest", "vendorCode": "wallquest", "handle": "@wallquestofficial", "url": "https://www.instagram.com/wallquestofficial/", "followers": "10K", "verified": false, "note": "" },
+ { "brand": "York Wallcoverings", "vendorCode": "york", "handle": "@york_wallcoverings", "url": "https://www.instagram.com/york_wallcoverings/", "followers": "100K", "verified": false, "note": "" },
+ { "brand": "F. Schumacher", "vendorCode": "schumacher", "handle": "none found", "url": "", "followers": "", "verified": false, "note": "only personal accounts found — verify @schumacher1889" },
+ { "brand": "Thibaut", "vendorCode": "thibaut", "handle": "@thibaut_1886", "url": "https://www.instagram.com/thibaut_1886/", "followers": "294K", "verified": false, "note": "" },
+ { "brand": "Arte International", "vendorCode": "arte", "handle": "@artewalls", "url": "https://www.instagram.com/artewalls/", "followers": "363K", "verified": false, "note": "" },
+ { "brand": "Koroseal", "vendorCode": "koroseal", "handle": "@koroseal", "url": "https://www.instagram.com/koroseal/", "followers": "4.6K", "verified": false, "note": "" },
+ { "brand": "Wolf Gordon", "vendorCode": "wolf-gordon", "handle": "@wolfgordon", "url": "https://www.instagram.com/wolfgordon/", "followers": "11K", "verified": false, "note": "" },
+ { "brand": "Carnegie Fabrics", "vendorCode": "carnegie", "handle": "@carnegiefabrics", "url": "https://www.instagram.com/carnegiefabrics/", "followers": "13K", "verified": false, "note": "" },
+ { "brand": "Scalamandré", "vendorCode": "scalamandre", "handle": "@scalamandre", "url": "https://www.instagram.com/scalamandre/", "followers": "117K", "verified": false, "note": "" },
+ { "brand": "Sister Parish Design", "vendorCode": "sister-parish", "handle": "@sisterparishdesign1933", "url": "https://www.instagram.com/sisterparishdesign1933/", "followers": "125K", "verified": false, "note": "private-labeled by DW as Saybrook House" },
+ { "brand": "Cole & Son", "vendorCode": "cole-son", "handle": "@cole_and_son_wallpapers", "url": "https://www.instagram.com/cole_and_son_wallpapers/", "followers": "483K", "verified": false, "note": "" },
+ { "brand": "Phillip Jeffries", "vendorCode": "phillip-jeffries", "handle": "@phillipjeffriesltd", "url": "https://www.instagram.com/phillipjeffriesltd/", "followers": "524K", "verified": false, "note": "" }
+]
diff --git a/modules/registry.js b/modules/registry.js
index a06b19d..504c03a 100644
--- a/modules/registry.js
+++ b/modules/registry.js
@@ -15,4 +15,5 @@ module.exports = [
'performance', // performance dashboard: email + GA4 KPIs, charts, top campaigns
'social', // social scheduler: IG/TikTok queue board, gated publish
'segments', // audience segments: rule builder + live preview over contacts
+ 'vendors', // vendor IG reporting: DW + all vendor Instagram accounts roster
];
diff --git a/modules/vendors/index.js b/modules/vendors/index.js
new file mode 100644
index 0000000..2f3b525
--- /dev/null
+++ b/modules/vendors/index.js
@@ -0,0 +1,39 @@
+// Vendor IG Reporting — directory of DW's + all vendor Instagram accounts, for
+// competitive/social reporting. Reads data/vendor-instagram.json (curated; refresh
+// via research). Read-only. Pairs with the advertising-signals tooling for deeper
+// per-vendor ad/social intel later.
+const fs = require('fs');
+const path = require('path');
+
+const DATA = path.join(__dirname, '..', '..', 'data', 'vendor-instagram.json');
+function load() { try { return JSON.parse(fs.readFileSync(DATA, 'utf8')); } catch { return []; } }
+function followersToNum(f) {
+ if (!f) return 0;
+ const m = String(f).trim().match(/^([\d.]+)\s*([kKmM]?)/);
+ if (!m) return 0;
+ const n = parseFloat(m[1]); const u = m[2].toLowerCase();
+ return Math.round(n * (u === 'm' ? 1e6 : u === 'k' ? 1e3 : 1));
+}
+
+module.exports = {
+ id: 'vendors',
+ title: 'Vendor IG Reporting',
+ icon: '📷',
+ mount(router) {
+ router.get('/accounts', (_req, res) => {
+ const rows = load().map(r => ({ ...r, followersNum: followersToNum(r.followers), hasIG: r.handle && r.handle !== 'none found' }));
+ const withIG = rows.filter(r => r.hasIG);
+ const reach = withIG.reduce((s, r) => s + r.followersNum, 0);
+ res.json({
+ accounts: rows,
+ stats: {
+ total: rows.length,
+ withIG: withIG.length,
+ missing: rows.length - withIG.length,
+ totalReach: reach,
+ dw: rows.find(r => r.vendorCode === 'dw') || null,
+ },
+ });
+ });
+ },
+};
diff --git a/public/panels/vendors.html b/public/panels/vendors.html
new file mode 100644
index 0000000..ec73221
--- /dev/null
+++ b/public/panels/vendors.html
@@ -0,0 +1,14 @@
+<div id="vend-banner"></div>
+<div class="card" id="vend-stats" style="display:flex;gap:30px;flex-wrap:wrap;align-items:center">
+ <div><div style="font:600 26px/1 'Cormorant Garamond',serif" id="vs-total">…</div><small class="muted">vendor brands</small></div>
+ <div><div style="font:600 26px/1 'Cormorant Garamond',serif" id="vs-ig">…</div><small class="muted">with Instagram</small></div>
+ <div><div style="font:600 26px/1 'Cormorant Garamond',serif" id="vs-reach">…</div><small class="muted">combined reach</small></div>
+ <div><div style="font:600 26px/1 'Cormorant Garamond',serif" id="vs-missing">…</div><small class="muted">no IG found</small></div>
+ <div style="margin-left:auto"><label>Sort</label>
+ <select id="vs-sort"><option value="followers">Followers ↓</option><option value="brand">Brand A→Z</option><option value="code">Vendor code</option></select></div>
+</div>
+<div class="card">
+ <h2>DW + Vendor Instagram accounts</h2>
+ <div class="muted" style="margin-bottom:10px">Click any handle to open the account. Use this roster for competitive/social reporting.</div>
+ <div id="vend-rows"></div>
+</div>
diff --git a/public/panels/vendors.js b/public/panels/vendors.js
new file mode 100644
index 0000000..d52b858
--- /dev/null
+++ b/public/panels/vendors.js
@@ -0,0 +1,35 @@
+window.MCC_PANELS = window.MCC_PANELS || {};
+window.MCC_PANELS['vendors'] = {
+ async init(root) {
+ const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
+ const fmt = n => n >= 1e6 ? (n / 1e6).toFixed(1) + 'M' : n >= 1e3 ? Math.round(n / 1e3) + 'K' : (n || '—');
+ let data;
+ try { data = await (await fetch('/api/vendors/accounts')).json(); } catch { root.querySelector('#vend-rows').innerHTML = '<div class="muted">Failed to load.</div>'; return; }
+ const s = data.stats || {};
+ root.querySelector('#vs-total').textContent = s.total || 0;
+ root.querySelector('#vs-ig').textContent = s.withIG || 0;
+ root.querySelector('#vs-reach').textContent = fmt(s.totalReach || 0);
+ root.querySelector('#vs-missing').textContent = s.missing || 0;
+ const render = (sort) => {
+ let rows = [...data.accounts];
+ if (sort === 'brand') rows.sort((a, b) => a.brand.localeCompare(b.brand));
+ else if (sort === 'code') rows.sort((a, b) => (a.vendorCode || '').localeCompare(b.vendorCode || ''));
+ else rows.sort((a, b) => (b.followersNum || 0) - (a.followersNum || 0));
+ root.querySelector('#vend-rows').innerHTML = rows.map(r => {
+ const dw = r.vendorCode === 'dw';
+ const handle = r.hasIG
+ ? `<a class="lnk" href="${esc(r.url)}" target="_blank" rel="noopener noreferrer">${esc(r.handle)} ↗</a>`
+ : `<span class="muted">no IG</span>`;
+ return `<div class="row" style="justify-content:space-between;border-bottom:1px solid var(--line);padding:9px 2px;align-items:center${dw ? ';background:#faf6ee' : ''}">
+ <div style="min-width:200px"><b>${esc(r.brand)}</b>${dw ? ' <span class="pill">OURS</span>' : ''}${r.note && !dw ? ` <span class="muted" style="font-size:11px">${esc(r.note)}</span>` : ''}</div>
+ <div style="flex:1">${handle}</div>
+ <div style="min-width:70px;text-align:right;font-variant-numeric:tabular-nums">${r.hasIG ? fmt(r.followersNum) : ''}</div>
+ </div>`;
+ }).join('');
+ };
+ render('followers');
+ root.querySelector('#vs-sort').onchange = e => render(e.target.value);
+ root.querySelector('#vend-banner').innerHTML = s.missing
+ ? `<div class="muted-banner">${s.withIG} of ${s.total} brands have an official Instagram — ${s.missing} have none found (Atomic50, Bespoke, Folia, Naturale 54, Schumacher). Combined vendor reach ≈ ${fmt(s.totalReach)} followers.</div>` : '';
+ },
+};
← d54875f 3 more panels (parallel agents): Performance dashboard (KPIs
·
back to Marketing Command Center
·
Channels publishing engine: FB/IG/TikTok/YouTube adapters (r a85f5b2 →