[object Object]

← back to Zendesk Chat Analyzer

Add CSV export of current filtered/arranged grid view (client-side, respects columns+filters)

590979c4c5406894cb5e68f1eb278256fabf29ba · 2026-08-11 09:32:26 -0700 · Steve

Files touched

Diff

commit 590979c4c5406894cb5e68f1eb278256fabf29ba
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Aug 11 09:32:26 2026 -0700

    Add CSV export of current filtered/arranged grid view (client-side, respects columns+filters)
---
 public/app.js     | 12 ++++++++++++
 public/index.html |  1 +
 2 files changed, 13 insertions(+)

diff --git a/public/app.js b/public/app.js
index 35126ed..c6b8b70 100644
--- a/public/app.js
+++ b/public/app.js
@@ -240,6 +240,18 @@ document.getElementById('colBtn').onclick=e=>{ e.stopPropagation(); buildColMenu
 document.addEventListener('click',()=>document.getElementById('colPop').classList.remove('on'));
 document.getElementById('colPop').onclick=e=>e.stopPropagation();
 document.getElementById('resetCols').onclick=()=>{ colOrder=DEF_ORDER.slice(); colHidden=new Set(DEF_HIDE); sortKey='ts';sortDir=-1; localStorage.removeItem('zca_cols');localStorage.removeItem('zca_sort'); table(); };
+document.getElementById('exportCsv').onclick=()=>{
+  let r=rows().slice();
+  const q=document.getElementById('search').value.toLowerCase().split(/\s+/).filter(Boolean);
+  if(q.length) r=r.filter(c=>{ const blob=COLDEFS.map(d=>d.p(c)).join(' ').toLowerCase(); return q.every(w=>blob.includes(w)); });
+  const sd=CMAP[sortKey]||CMAP.ts;
+  r.sort((a,b)=>{ let x=sd.p(a),y=sd.p(b); if(typeof x==='string')x=x.toLowerCase(); if(typeof y==='string')y=y.toLowerCase(); return (x>y?1:x<y?-1:0)*sortDir; });
+  const cols=visCols(), esc=v=>{ v=String(v==null?'':v); return /[",\n]/.test(v)?'"'+v.replace(/"/g,'""')+'"':v; };
+  const csv=[cols.map(k=>esc(CMAP[k].l)).join(',')].concat(r.map(c=>cols.map(k=>esc(CMAP[k].p(c))).join(','))).join('\r\n');
+  const a=document.createElement('a'); a.href=URL.createObjectURL(new Blob([csv],{type:'text/csv'}));
+  const f=[FILTER.theme,FILTER.brand,FILTER.country,FILTER.missed?'missed':''].filter(Boolean).join('-')||'all';
+  a.download=`dw-chats-${f}-${r.length}rows.csv`; a.click(); URL.revokeObjectURL(a.href);
+};
 document.getElementById('search').oninput=table;
 document.getElementById('density').oninput=e=>{ document.querySelectorAll('#tbl td,#tbl th').forEach(td=>td.style.padding=`${e.target.value*.7}px 9px`); };
 
diff --git a/public/index.html b/public/index.html
index 08dd4da..09be5b4 100644
--- a/public/index.html
+++ b/public/index.html
@@ -155,6 +155,7 @@ a{color:var(--acc);text-decoration:none}a:hover{text-decoration:underline}
           <div class="colpop" id="colPop"></div>
         </div>
         <button id="resetCols">↺ Reset</button>
+        <button id="exportCsv">⭳ Export CSV</button>
         <span class="mut" id="tableCount"></span>
       </div>
       <div class="mut" style="font-size:11px;margin:-4px 0 8px">Tip: click a header to sort · <b>drag a header to reorder</b> · use ▦ Columns to show/hide · choices are saved.</div>

← 0631d5f Clamp /api/recent HOURS to 1h..7d (abuse guard, Cody)  ·  back to Zendesk Chat Analyzer  ·  Add rule-based sentiment + urgency scoring (/bin/zsh local): d73376a →