'use strict'; SM.injectLayout('nav-upload'); const uploadZone = document.getElementById('uploadZone'); const csvInput = document.getElementById('csvInput'); let selectedFiles = []; // ── Drag & Drop ── uploadZone.addEventListener('dragover', e => { e.preventDefault(); uploadZone.classList.add('drag'); }); uploadZone.addEventListener('dragleave', () => uploadZone.classList.remove('drag')); uploadZone.addEventListener('drop', e => { e.preventDefault(); uploadZone.classList.remove('drag'); const allFiles = Array.from(e.dataTransfer.files); if (!allFiles.length) return; const files = allFiles.filter(f => f.name.endsWith('.csv')); if (files.length) { showFilePreview(files); } else { SM.showModal({ title: 'Format Tidak Sesuai', message: 'Hanya menerima file .csv', type: 'error' }); } }); uploadZone.addEventListener('click', e => { if (e.target.tagName === 'BUTTON') return; csvInput.click(); }); csvInput.addEventListener('change', e => { const allFiles = Array.from(e.target.files); if (!allFiles.length) return; const files = allFiles.filter(f => f.name.endsWith('.csv')); if (files.length) { showFilePreview(files); } else { SM.showModal({ title: 'Format Tidak Sesuai', message: 'Hanya menerima file dataset dalam format .csv.', type: 'error' }); } }); function showFilePreview(files) { selectedFiles = files; const totalSize = files.reduce((acc, f) => acc + f.size, 0); if (files.length === 1) { window._uploadedFilename = files[0].name; document.getElementById('fileName').textContent = files[0].name; } else { window._uploadedFilename = `${files.length}_File_CSV_Gabungan.csv`; document.getElementById('fileName').textContent = `${files.length} File CSV Terpilih`; } document.getElementById('filePreview').style.display = 'flex'; document.getElementById('fileMeta').textContent = `${(totalSize/1024).toFixed(1)} KB`; } document.getElementById('btnCancel').addEventListener('click', () => { selectedFiles = []; csvInput.value = ''; document.getElementById('filePreview').style.display = 'none'; }); document.getElementById('btnAnalyze').addEventListener('click', async () => { if (!selectedFiles || selectedFiles.length === 0) return; document.getElementById('filePreview').style.display = 'none'; document.getElementById('progressWrap').style.display = 'block'; const steps = ['Membaca file...','Parsing CSV...','Menjalankan Text Cleaning...', 'Tokenisasi IndoBERT...','Klasifikasi Sentimen...','Menyimpan Hasil...','Selesai!']; let combinedText = ''; for (let i = 0; i < selectedFiles.length; i++) { let t = await selectedFiles[i].text(); // Untuk file kedua dan seterusnya, hapus baris pertama (header csv) agar tidak ikut terproses if (i > 0) { const firstNewline = t.indexOf('\\n'); if (firstNewline !== -1) t = t.substring(firstNewline + 1); } // Gabungkan text, pastikan dipisah enter combinedText += t + (t.endsWith('\\n') ? '' : '\\n'); } const text = combinedText; // Update progress UI step document.getElementById('progressStep').textContent = steps[0]; // Run in next tick so UI updates first await new Promise(r => setTimeout(r, 30)); let stepIdx = 0; function progress(done, total) { const pct = Math.round((done/total)*100); document.getElementById('progressBar').style.width = pct + '%'; document.getElementById('progressPct').textContent = pct + '%'; const si = Math.min(Math.floor((pct/100)*steps.length), steps.length-1); if (si !== stepIdx) { stepIdx = si; document.getElementById('progressStep').textContent = steps[si]; } } try { // processCSV is now async — calls IndoBERT API in batches const result = await SM.processCSV(text, progress); if (!result || !result.rows || !result.rows.length) { SM.showModal({ title: 'Dataset Kosong', message: 'Tidak ada data teks yang ditemukan dalam file CSV tersebut.', type: 'error' }); document.getElementById('progressWrap').style.display = 'none'; document.getElementById('filePreview').style.display = 'flex'; return; } const { rows, meta } = result; // Animate to 100% document.getElementById('progressBar').style.width = '100%'; document.getElementById('progressPct').textContent = '100%'; document.getElementById('progressStep').textContent = 'Selesai! Menyiapkan dashboard...'; SM.saveData(rows, meta); // Show center success modal setTimeout(() => { SM.showModal({ title: 'Analisis Selesai', message: ` Berhasil memproses ${SM.fmt(rows.length)} teks dengan model machine learning IndoBERT. Hasil analisis telah siap untuk ditinjau.

Peringatan Penting: Seluruh data sentimen dan riwayat file CSV Anda disimpan secara permanen di memori lokal (Local Storage) peramban web ini untuk menjamin privasi. Tolong jangan menghapus riwayat peramban atau Cache / Clear Data, karena akan menyebabkan semua data di Riwayat Analisis hilang secara permanen. `, type: 'success', onConfirm: () => { window.location.href = 'dashboard'; } }); }, 400); } catch (err) { // Show error popup when IndoBERT model / API fails document.getElementById('progressWrap').style.display = 'none'; document.getElementById('filePreview').style.display = 'flex'; // Reset progress bar document.getElementById('progressBar').style.width = '0%'; document.getElementById('progressPct').textContent = '0%'; document.getElementById('progressStep').textContent = ''; SM.showModal({ title: 'Gagal Memproses Sentimen', message: ` Model IndoBERT tidak dapat diakses.

${SM.esc(err.message || 'Terjadi kesalahan yang tidak diketahui.')}

Solusi:
1. Pastikan server Python sudah berjalan (python app.py)
2. Pastikan model IndoBERT sudah terunduh dengan benar
3. Pastikan dependensi Python sudah terinstall (pip install -r requirements.txt)
`, type: 'error' }); } }); // ── Demo Data Trigger ── const btnDemo = document.getElementById('btnDemoData'); if (btnDemo) { btnDemo.addEventListener('click', async () => { if (typeof window.DEMO_CSV === 'undefined') { SM.showToast('Data demo tidak tersedia.', 'error'); return; } // Set demo filename for meta window._uploadedFilename = 'demo_300_tweets.csv'; document.getElementById('filePreview').style.display = 'none'; document.getElementById('progressWrap').style.display = 'block'; const steps = ['Membaca data demo...', 'Parsing CSV...', 'Menjalankan Text Cleaning...', 'Tokenisasi IndoBERT...', 'Klasifikasi Sentimen...', 'Menyimpan Hasil...', 'Selesai!']; document.getElementById('progressStep').textContent = steps[0]; async function progress(done, total) { const pct = Math.round((done / total) * 100); document.getElementById('progressBar').style.width = pct + '%'; document.getElementById('progressPct').textContent = pct + '%'; const si = Math.min(Math.floor((pct / 100) * steps.length), steps.length - 1); document.getElementById('progressStep').textContent = steps[si]; } try { const result = await SM.processCSV(window.DEMO_CSV, progress); const { rows, meta } = result; document.getElementById('progressBar').style.width = '100%'; document.getElementById('progressPct').textContent = '100%'; document.getElementById('progressStep').textContent = 'Selesai! Menyiapkan dashboard...'; SM.saveData(rows, meta); setTimeout(() => { SM.showModal({ title: 'Demo Selesai', message: `Berhasil memproses ${rows.length} tweet demo dengan model IndoBERT. Lihat hasilnya di dashboard!`, type: 'success', onConfirm: () => { window.location.href = 'dashboard'; } }); }, 400); } catch (err) { SM.showToast('Gagal memproses demo: ' + err.message, 'error'); document.getElementById('progressWrap').style.display = 'none'; } }); }