'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 `
| `;
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'));