← back to Marketing Command Center
IG activity panel: grid/list toggle + sort by all columns (dropdown + clickable headers)
1ec9940a2b5169fc7cf40ce819b9053a70fec2a5 · 2026-08-11 08:28:50 -0700 · Steve Abrams
Files touched
M public/ig-activity.htmlM public/ig-activity.json
Diff
commit 1ec9940a2b5169fc7cf40ce819b9053a70fec2a5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Aug 11 08:28:50 2026 -0700
IG activity panel: grid/list toggle + sort by all columns (dropdown + clickable headers)
---
public/ig-activity.html | 123 +++++++++++++++++++++++++++++++-----------------
public/ig-activity.json | 96 +++++++++++++++++--------------------
2 files changed, 123 insertions(+), 96 deletions(-)
diff --git a/public/ig-activity.html b/public/ig-activity.html
index 88ae127..50d7292 100644
--- a/public/ig-activity.html
+++ b/public/ig-activity.html
@@ -20,6 +20,11 @@
.toolbar label{color:var(--mut);font-size:12px;text-transform:uppercase;letter-spacing:.08em;margin-right:8px}
select,input[type=range]{accent-color:var(--accent)}
select{background:var(--card);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:13px}
+ .seg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
+ .seg button{background:var(--card);color:var(--mut);border:0;padding:7px 14px;font-size:13px;cursor:pointer}
+ .seg button.on{background:var(--accent);color:#000;font-weight:600}
+ #densityWrap.hide{display:none}
+ /* GRID */
.grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:16px;padding:22px 28px 60px}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden;display:flex;flex-direction:column;transition:border-color .15s,transform .15s}
.card:hover{border-color:var(--accent);transform:translateY(-2px)}
@@ -32,8 +37,16 @@
.foot{display:flex;justify-content:space-between;align-items:center;margin-top:9px;padding-top:9px;border-top:1px solid var(--line)}
.view{font-size:12px;color:var(--accent);text-decoration:none;border:1px solid var(--line);border-radius:6px;padding:4px 9px}
.view:hover{background:var(--accent);color:#000;border-color:var(--accent)}
+ /* LIST / TABLE */
+ table{width:calc(100% - 56px);margin:22px 28px 60px;border-collapse:collapse;font-size:13px}
+ th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:middle}
+ th{color:var(--mut);text-transform:uppercase;letter-spacing:.06em;font-size:11px;cursor:pointer;user-select:none;white-space:nowrap}
+ th:hover{color:var(--accent)}
+ th .arw{color:var(--accent);font-size:10px}
+ td img{width:44px;height:44px;object-fit:cover;border-radius:6px;display:block;background:#111}
+ td a.acct{color:var(--accent)}
+ tr:hover td{background:#161410}
.empty{color:var(--mut);text-align:center;padding:80px 20px}
- @media(max-width:640px){ .grid{--cols:2 !important} }
</style>
</head>
<body>
@@ -47,69 +60,95 @@
</div>
</header>
<div class="toolbar">
+ <div><label>View</label>
+ <span class="seg"><button id="v-grid" class="on">Grid</button><button id="v-list">List</button></span>
+ </div>
<div><label>Sort</label>
<select id="sort">
- <option value="newest">Newest</option>
- <option value="oldest">Oldest</option>
- <option value="account">Account A→Z</option>
- <option value="product">Product A→Z</option>
+ <option value="ts|desc">Newest</option>
+ <option value="ts|asc">Oldest</option>
+ <option value="handle|asc">Account A→Z</option>
+ <option value="handle|desc">Account Z→A</option>
+ <option value="product_title|asc">Product A→Z</option>
+ <option value="product_title|desc">Product Z→A</option>
+ <option value="kind|asc">Type A→Z</option>
</select>
</div>
- <div><label>Density</label>
+ <div id="densityWrap"><label>Density</label>
<input type="range" id="density" min="2" max="7" step="1">
</div>
<div class="sub" id="gen" style="margin-left:auto"></div>
</div>
<div id="grid" class="grid"></div>
+<table id="list" style="display:none"><thead><tr>
+ <th data-k="image_url">Image</th>
+ <th data-k="handle">Account <span class="arw"></span></th>
+ <th data-k="product_title">Product <span class="arw"></span></th>
+ <th data-k="ts">Posted <span class="arw"></span></th>
+ <th data-k="kind">Type <span class="arw"></span></th>
+ <th>Link</th>
+</tr></thead><tbody id="rows"></tbody></table>
<script>
const $=s=>document.querySelector(s);
const fmt=iso=>{try{return new Date(iso).toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'})}catch{return iso||''}};
-let DATA=[];
+let DATA=[], SORT=localStorage.getItem('ig_sort')||'ts|desc', MODE=localStorage.getItem('ig_mode')||'grid';
+function sorted(){
+ const [k,dir]=SORT.split('|'); const s=[...DATA];
+ s.sort((a,b)=>{ let x=a[k],y=b[k];
+ if(k==='ts'){x=new Date(x);y=new Date(y);}
+ else {x=(x||'').toString().toLowerCase();y=(y||'').toString().toLowerCase();}
+ return (x<y?-1:x>y?1:0)*(dir==='desc'?-1:1); });
+ return s;
+}
function render(){
- const sort=$('#sort').value;
- const p=[...DATA];
- if(sort==='newest')p.sort((a,b)=>new Date(b.ts)-new Date(a.ts));
- if(sort==='oldest')p.sort((a,b)=>new Date(a.ts)-new Date(b.ts));
- if(sort==='account')p.sort((a,b)=>(a.handle||'').localeCompare(b.handle||''));
- if(sort==='product')p.sort((a,b)=>(a.product_title||'').localeCompare(b.product_title||''));
- const g=$('#grid');
- if(!p.length){g.innerHTML='<div class="empty">No posts logged yet. As the network posts, they appear here.</div>';return;}
- g.innerHTML=p.map(x=>`
- <div class="card">
- <a href="${x.permalink||'#'}" target="_blank" rel="noopener noreferrer">
- <span class="thumb" style="background-image:url('${(x.image_url||'').replace(/'/g,'')}')"></span></a>
- <div class="body">
- <a class="acct" href="https://www.instagram.com/${x.handle}/" target="_blank" rel="noopener noreferrer">@${x.handle}</a>
- <div class="title" title="${(x.product_title||'').replace(/"/g,'"')}">${x.product_title||'—'}</div>
- <div class="when" title="${x.ts||''}">🕓 ${fmt(x.ts)}</div>
- <div class="foot">
- <span class="when">${x.kind||'IMAGE'}</span>
- <a class="view" href="${x.permalink||'#'}" target="_blank" rel="noopener noreferrer">View post ↗</a>
+ const p=sorted();
+ const grid=$('#grid'), list=$('#list');
+ if(MODE==='grid'){ grid.style.display='grid'; list.style.display='none'; $('#densityWrap').classList.remove('hide');
+ grid.innerHTML = p.length? p.map(x=>`
+ <div class="card">
+ <a href="${x.permalink||'#'}" target="_blank" rel="noopener noreferrer"><span class="thumb" style="background-image:url('${(x.image_url||'').replace(/'/g,'')}')"></span></a>
+ <div class="body">
+ <a class="acct" href="https://www.instagram.com/${x.handle}/" target="_blank" rel="noopener noreferrer">@${x.handle}</a>
+ <div class="title" title="${(x.product_title||'').replace(/"/g,'"')}">${x.product_title||'—'}</div>
+ <div class="when" title="${x.ts||''}">🕓 ${fmt(x.ts)}</div>
+ <div class="foot"><span class="when">${x.kind||'IMAGE'}</span>
+ <a class="view" href="${x.permalink||'#'}" target="_blank" rel="noopener noreferrer">View post ↗</a></div>
</div>
- </div>
- </div>`).join('');
+ </div>`).join('') : '<div class="empty">No posts logged yet.</div>';
+ } else { grid.style.display='none'; list.style.display='table'; $('#densityWrap').classList.add('hide');
+ $('#rows').innerHTML = p.length? p.map(x=>`
+ <tr>
+ <td><a href="${x.permalink||'#'}" target="_blank" rel="noopener noreferrer"><img src="${x.image_url||''}" alt=""></a></td>
+ <td><a class="acct" href="https://www.instagram.com/${x.handle}/" target="_blank" rel="noopener noreferrer">@${x.handle}</a></td>
+ <td>${x.product_title||'—'}</td>
+ <td title="${x.ts||''}">🕓 ${fmt(x.ts)}</td>
+ <td>${x.kind||'IMAGE'}</td>
+ <td><a class="view" href="${x.permalink||'#'}" target="_blank" rel="noopener noreferrer">View ↗</a></td>
+ </tr>`).join('') : '<tr><td colspan="6" class="empty">No posts logged yet.</td></tr>';
+ }
+ // header arrows
+ const [ck,cd]=SORT.split('|');
+ document.querySelectorAll('th[data-k]').forEach(th=>{const a=th.querySelector('.arw'); if(a) a.textContent = th.dataset.k===ck?(cd==='desc'?'▼':'▲'):'';});
+ $('#sort').value=SORT;
}
-function applyDensity(v){document.documentElement.style.setProperty('--cols',v);}
+function setMode(m){MODE=m;localStorage.setItem('ig_mode',m);$('#v-grid').classList.toggle('on',m==='grid');$('#v-list').classList.toggle('on',m==='list');render();}
+function setSort(s){SORT=s;localStorage.setItem('ig_sort',s);render();}
+$('#v-grid').onclick=()=>setMode('grid'); $('#v-list').onclick=()=>setMode('list');
+$('#sort').onchange=()=>setSort($('#sort').value);
+document.querySelectorAll('th[data-k]').forEach(th=>th.onclick=()=>{const k=th.dataset.k;const [ck,cd]=SORT.split('|');setSort(k+'|'+(ck===k&&cd==='asc'?'desc':'asc'));});
+const sDen=localStorage.getItem('ig_density')||'4'; $('#density').value=sDen; document.documentElement.style.setProperty('--cols',sDen);
+$('#density').oninput=()=>{const v=$('#density').value;localStorage.setItem('ig_density',v);document.documentElement.style.setProperty('--cols',v);};
async function load(){
- try{
- const r=await fetch('ig-activity.json?t='+Date.now());
- const j=await r.json();
- DATA=j.posts||[];
- $('#s-posts').textContent=j.total_posts??DATA.length;
+ try{ const r=await fetch('ig-activity.json?t='+Date.now()); const j=await r.json();
+ DATA=j.posts||[]; $('#s-posts').textContent=j.total_posts??DATA.length;
$('#s-accts').textContent=j.accounts_touched??new Set(DATA.map(x=>x.handle)).size;
$('#s-last').textContent=j.last_post_at?fmt(j.last_post_at):'–';
$('#gen').textContent='Updated '+(j.generated_at?fmt(j.generated_at):'');
}catch(e){DATA=[];}
- render();
+ setMode(MODE); // applies mode + renders
}
-// persisted controls
-const sSort=localStorage.getItem('ig_sort')||'newest';
-const sDen=localStorage.getItem('ig_density')||'4';
-$('#sort').value=sSort;$('#density').value=sDen;applyDensity(sDen);
-$('#sort').onchange=()=>{localStorage.setItem('ig_sort',$('#sort').value);render();};
-$('#density').oninput=()=>{const v=$('#density').value;localStorage.setItem('ig_density',v);applyDensity(v);};
-load(); setInterval(load,60000); // auto-refresh each minute
+load(); setInterval(load,60000);
</script>
</body>
</html>
diff --git a/public/ig-activity.json b/public/ig-activity.json
index 92f8dbd..80e3301 100644
--- a/public/ig-activity.json
+++ b/public/ig-activity.json
@@ -1,105 +1,93 @@
{
- "generated_at": "2026-08-11T15:18:04.900Z",
- "total_posts": 8,
- "accounts_touched": 8,
- "last_post_at": "2026-08-11T08:13:00-07:00",
+ "generated_at": "2026-08-11T15:26:27.480Z",
+ "total_posts": 7,
+ "accounts_touched": 7,
+ "last_post_at": "2026-08-11T15:26:27.447Z",
"by_account": {
"screenprintedwallpaper": 1,
"metallicwallpaper": 1,
- "goldleafwallpaper": 1,
- "linenwallpaper": 1,
"grassclothwallpaper": 1,
- "velvetwallpaper": 1,
+ "linenwallpaper": 1,
"suedewallpaper": 1,
+ "velvetwallpaper": 1,
"textilewallpaper": 1
},
"posts": [
{
- "ts": "2026-08-11T08:13:00-07:00",
+ "ts": "2026-08-11T15:26:27.447Z",
"handle": "screenprintedwallpaper",
"page_name": "Screen Printed Wallpaper",
"product_handle": "regal-lattice-screen-printed-wallpaper-tre-12907",
"product_title": "Regal Lattice - Screen Printed Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/3547fe79068d43fd86725b501b7c7f01.jpg",
- "permalink": "https://www.instagram.com/p/Db54ExfmxI5/",
- "media_id": null,
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/3547fe79068d43fd86725b501b7c7f01.jpg?v=1572309178",
+ "permalink": "https://www.instagram.com/p/Db56DKZG-Ur/",
+ "media_id": "18023951597891610",
"kind": "IMAGE"
},
{
- "ts": "2026-08-11T08:12:00-07:00",
+ "ts": "2026-08-11T15:26:14.155Z",
"handle": "metallicwallpaper",
"page_name": "Metallic Wallpaper",
"product_handle": "showgirls-wallpaper",
"product_title": "Showgirls Metallic Gold & Black Wallcovering | Graduate Collection",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/ScreenShot2023-09-11at1.53.19PM.png",
- "permalink": "https://www.instagram.com/p/Db54BKHG8Sv/",
- "media_id": null,
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/ScreenShot2023-09-11at1.53.19PM.png?v=1694465646",
+ "permalink": "https://www.instagram.com/p/Db56Bn6m_al/",
+ "media_id": "17891509140656955",
"kind": "IMAGE"
},
{
- "ts": "2026-08-11T08:12:00-07:00",
- "handle": "goldleafwallpaper",
- "page_name": "Gold Leaf Wallpaper",
- "product_handle": "dwtt-80525-designer-wallcoverings-los-angeles",
- "product_title": "Metal Leaf Metallic Gold - Gold Wallcovering | Thibaut",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/T41011_e0cd5049-136c-456e-b07d-a26bbb77e65b.jpg",
- "permalink": "https://www.instagram.com/p/Db54C7Cm-G_/",
- "media_id": null,
+ "ts": "2026-08-11T15:26:01.248Z",
+ "handle": "grassclothwallpaper",
+ "page_name": "Wallpaper Weekly",
+ "product_handle": "papalo-paper-grasscloth-prg-71096",
+ "product_title": "Papalo Paper Grasscloth | Phillipe Romano",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/cf484d7a1d1bd1eeba8b00c589a2453d.jpg?v=1743032177",
+ "permalink": "https://www.instagram.com/p/Db55_9RG1nJ/",
+ "media_id": "17904217761531860",
"kind": "IMAGE"
},
{
- "ts": "2026-08-11T07:56:00-07:00",
+ "ts": "2026-08-11T15:25:48.307Z",
"handle": "linenwallpaper",
"page_name": "Linen Wallpaper",
"product_handle": "pure-elements-sisal-linen",
"product_title": "Pure Elements Sisal Linen Wallcoverings",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0075624_sisal_650.jpg",
- "permalink": "https://www.instagram.com/p/Db50HyfG93k/",
- "media_id": null,
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0075624_sisal_650.jpg?v=1783366838",
+ "permalink": "https://www.instagram.com/p/Db55-UAGzTZ/",
+ "media_id": "18333064030260099",
"kind": "IMAGE"
},
{
- "ts": "2026-08-11T07:56:00-07:00",
- "handle": "grassclothwallpaper",
- "page_name": "Wallpaper Weekly",
- "product_handle": "papalo-paper-grasscloth-prg-71096",
- "product_title": "Papalo Paper Grasscloth | Phillipe Romano",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/cf484d7a1d1bd1eeba8b00c589a2453d.jpg",
- "permalink": "https://www.instagram.com/p/Db50Jmnm-7m/",
- "media_id": null,
+ "ts": "2026-08-11T15:25:34.527Z",
+ "handle": "suedewallpaper",
+ "page_name": "Suede Wallpaper",
+ "product_handle": "prw-920205-cts-07",
+ "product_title": "Ciudad Suede Natural | Phillipe Romano",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920205-cts-07.jpg?v=1781716927",
+ "permalink": "https://www.instagram.com/p/Db558rwm6s0/",
+ "media_id": "18141986509562672",
"kind": "IMAGE"
},
{
- "ts": "2026-08-11T07:55:00-07:00",
+ "ts": "2026-08-11T15:25:20.929Z",
"handle": "velvetwallpaper",
"page_name": "Velvet Wallpaper",
"product_handle": "veronicas-madison-flocked-velvet-wallpaper-4",
"product_title": "Veronica's Madison Flocked Velvet Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/VV218_madison_MAROON_1056x_8d36f6e3-f1df-410f-a0a6-f8c80c8ac535.webp",
- "permalink": "https://www.instagram.com/p/Db50Endm9JZ/",
- "media_id": null,
- "kind": "IMAGE"
- },
- {
- "ts": "2026-08-11T07:55:00-07:00",
- "handle": "suedewallpaper",
- "page_name": "Suede Wallpaper",
- "product_handle": "prw-920205-cts-07",
- "product_title": "Ciudad Suede Natural | Phillipe Romano",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920205-cts-07.jpg",
- "permalink": "https://www.instagram.com/p/Db50GKmG_k8/",
- "media_id": null,
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/VV218_madison_MAROON_1056x_8d36f6e3-f1df-410f-a0a6-f8c80c8ac535.webp?v=1734414936",
+ "permalink": "https://www.instagram.com/p/Db556-amys3/",
+ "media_id": "18136476730524343",
"kind": "IMAGE"
},
{
- "ts": "2026-08-11T07:52:00-07:00",
+ "ts": "2026-08-11T15:25:07.634Z",
"handle": "textilewallpaper",
"page_name": "Textile Wallpaper",
"product_handle": "wick-submerge-maharam",
"product_title": "Wick Submerge | Maharam",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/MH-WICK-021_0.jpg",
- "permalink": "https://www.instagram.com/p/Db5zlrhG1xF/",
- "media_id": null,
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/MH-WICK-021_0.jpg?v=1786455296",
+ "permalink": "https://www.instagram.com/p/Db555ZTG9KG/",
+ "media_id": "17978408325081647",
"kind": "IMAGE"
}
]
← 1ccbe05 Add IG Network Activity panel (what's posted + where, per-ca
·
back to Marketing Command Center
·
auto-data-snapshot: 2026-08-11T09:10:52 (1 data files) — pub fb98d9a →