'use strict'; SM.injectLayout('nav-data'); const store = SM.loadData(); if (!store) { window.location.replace('upload'); throw new Error('No data — redirecting to upload'); } const { rows, meta } = store; document.getElementById('topbarMeta').textContent = `${meta.filename} — ${rows.length} tweets`; // ── Column definitions ── const COLS = [ { key:'id', label:'No', visible:true, sortable:true }, { key:'raw', label:'Teks Asli', visible:true, sortable:false }, { key:'cleaned', label:'Teks Bersih', visible:false, sortable:false }, { key:'username', label:'Username', visible:true, sortable:true }, { key:'location', label:'Lokasi', visible:true, sortable:true }, { key:'date', label:'Waktu', visible:true, sortable:true }, { key:'sentiment', label:'Sentimen', visible:true, sortable:true }, { key:'confidence', label:'Kepercayaan', visible:true, sortable:true }, { key:'fav', label:'Like', visible:true, sortable:true }, { key:'rt', label:'Retweet', visible:true, sortable:true }, { key:'rep', label:'Reply', visible:false, sortable:true }, { key:'qot', label:'Quote', visible:false, sortable:true }, { key:'engagement', label:'Engagement', visible:true, sortable:true }, ]; // Populate location & user dropdowns const locs = [...new Set(rows.map(r=>(r.location||'').trim()||'—'))].sort(); const users = [...new Set(rows.map(r=>r.username))].sort(); const fLoc = document.getElementById('fLocation'); const fUser = document.getElementById('fUser'); locs.forEach(l => { const o=document.createElement('option'); o.value=l; o.textContent=l; fLoc.appendChild(o); }); users.forEach(u => { const o=document.createElement('option'); o.value=u; o.textContent='@'+u; fUser.appendChild(o); }); // Column toggle buttons document.getElementById('colToggle').innerHTML = COLS.map((c,i) => `
${c.label}
` ).join(''); document.querySelectorAll('.col-pill').forEach(pill => { pill.addEventListener('click', () => { const i = +pill.dataset.colidx; COLS[i].visible = !COLS[i].visible; pill.classList.toggle('on', COLS[i].visible); renderTable(); }); }); // ── State ── let filtered = [...rows]; let currentPage = 1; let pageSize = 20; let sortKey = 'id', sortDir = 1; let expandedRows = new Set(); // ── Filters ── function applyFilters() { const s = document.getElementById('fSentiment').value; const loc = document.getElementById('fLocation').value; const usr = document.getElementById('fUser').value; const q = document.getElementById('fSearch').value.toLowerCase().trim(); const minE= parseInt(document.getElementById('fMinEngage').value)||0; const minC= (parseFloat(document.getElementById('fMinConf').value)||0)/100; filtered = rows.filter(r => (s==='all' || r.sentiment===s) && (loc==='all' || (r.location||'—')===loc) && (usr==='all' || r.username===usr) && (r.engagement >= minE) && (r.confidence >= minC) && (!q || r.raw.toLowerCase().includes(q) || r.username.toLowerCase().includes(q) || r.cleaned.toLowerCase().includes(q)) ); // Sort filtered.sort((a,b)=>{ const va=a[sortKey], vb=b[sortKey]; if(typeof va==='number') return (va-vb)*sortDir; return String(va).localeCompare(String(vb))*sortDir; }); currentPage = 1; renderTable(); } ['fSentiment','fLocation','fUser'].forEach(id => document.getElementById(id).addEventListener('change', applyFilters)); document.getElementById('fSearch').addEventListener('input', applyFilters); document.getElementById('fMinEngage').addEventListener('input', applyFilters); document.getElementById('fMinConf').addEventListener('input', applyFilters); document.getElementById('pageSize').addEventListener('change', e => { pageSize=+e.target.value; currentPage=1; renderTable(); }); document.getElementById('btnReset').addEventListener('click', () => { document.getElementById('fSentiment').value='all'; document.getElementById('fLocation').value='all'; document.getElementById('fUser').value='all'; document.getElementById('fSearch').value=''; document.getElementById('fMinEngage').value=''; document.getElementById('fMinConf').value=''; // Refresh custom select labels after reset ['fSentiment','fLocation','fUser','pageSize'].forEach(id => { const el = document.getElementById(id); if (el) el.dispatchEvent(new Event('_csdRefresh')); }); applyFilters(); }); // ── Table HEAD ── function renderHead() { const visCols = COLS.filter(c=>c.visible); document.getElementById('tableHead').innerHTML = ` ${visCols.map(c => { const isSorted = sortKey === c.key; const sortIcon = isSorted ? (sortDir === 1 ? '' : '') : ''; return `
${c.label} ${c.sortable ? sortIcon : ''}
`; }).join('')} `; document.querySelectorAll('th[data-sort]').forEach(th => { th.addEventListener('click', () => { if(sortKey===th.dataset.sort) sortDir*=-1; else { sortKey=th.dataset.sort; sortDir=-1; } applyFilters(); // Use applyFilters to trigger sorting before re-render }); }); } // ── Table BODY ── function renderTable() { renderHead(); const visCols = COLS.filter(c=>c.visible); const start = (currentPage-1)*pageSize; const page = filtered.slice(start, start+pageSize); document.getElementById('tableBody').innerHTML = page.map(r => { const cells = visCols.map(c => { const v = r[c.key]; if(c.key==='sentiment') return `${r.sentiment}`; if(c.key==='confidence') return `
${(v*100).toFixed(1)}%
`; if(c.key==='raw') return `${SM.esc(v.slice(0,80))}${v.length>80?'…':''}`; if(c.key==='cleaned') return `${SM.esc(v.slice(0,60))}${v.length>60?'…':''}`; if(c.key==='date') { const d=new Date(v); return `${isNaN(d)?v:d.toLocaleTimeString('id-ID',{hour:'2-digit',minute:'2-digit'})}`; } if(c.key==='username') return `@${SM.esc(v)}`; if(c.key==='id') return `${v}`; return `${SM.esc(String(v))}`; }).join(''); return ` ${cells} `; }).join(''); renderPagination(); document.getElementById('tableInfo').textContent = `Menampilkan ${Math.min(start+1,filtered.length)}–${Math.min(start+pageSize,filtered.length)} dari ${filtered.length} data (total ${rows.length})`; } function renderPagination() { const total = Math.ceil(filtered.length/pageSize); const pg = document.getElementById('pagination'); if(total<=1) { pg.innerHTML=''; return; } const range=[1]; if(currentPage>3) range.push('…'); for(let i=Math.max(2,currentPage-1);i<=Math.min(total-1,currentPage+1);i++) range.push(i); if(currentPage1) range.push(total); pg.innerHTML = ` ${range.map(p=>p==='…'?`` :``).join('')} `; } window.goPage = function(p) { currentPage=Math.max(1,Math.min(p,Math.ceil(filtered.length/pageSize))); expandedRows.clear(); renderTable(); document.getElementById('dataTable').scrollIntoView({behavior:'smooth'}); }; // Initial render applyFilters(); // ── Custom Dropdowns ── SM.initCustomSelect(document.getElementById('fSentiment'), { showDots: { 'all': null, 'Positif': '#34d399', 'Negatif': '#f87171', 'Netral': '#fbbf24', } }); SM.initCustomSelect(document.getElementById('fLocation')); SM.initCustomSelect(document.getElementById('fUser')); SM.initCustomSelect(document.getElementById('pageSize'), { compact: true }); SM.initCustomNumber(document.getElementById('fMinEngage')); SM.initCustomNumber(document.getElementById('fMinConf'));