Spaces:
Running
Running
| ; | |
| 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 <strong>.csv</strong>.', 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 <strong>${SM.fmt(rows.length)}</strong> teks dengan model machine learning IndoBERT. Hasil analisis telah siap untuk ditinjau. | |
| <br><br> | |
| <span style="font-size:13.5px;color:var(--tx2);display:block;background:var(--bg-card2);padding:16px;border-radius:12px;text-align:left;line-height:1.5;"> | |
| <strong>Peringatan Penting:</strong> Seluruh data sentimen dan riwayat file CSV Anda disimpan secara permanen di memori lokal (<em>Local Storage</em>) peramban web ini untuk menjamin privasi. <strong>Tolong jangan menghapus riwayat peramban atau <em>Cache / Clear Data</em></strong>, karena akan menyebabkan semua data di Riwayat Analisis hilang secara permanen. | |
| </span> | |
| `, | |
| 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: ` | |
| <strong>Model IndoBERT tidak dapat diakses.</strong><br><br> | |
| ${SM.esc(err.message || 'Terjadi kesalahan yang tidak diketahui.')} | |
| <br><br> | |
| <span style="font-size:13px;color:var(--tx2);display:block;background:var(--bg-card2);padding:14px;border-radius:10px;text-align:left;line-height:1.5;"> | |
| <strong>Solusi:</strong><br> | |
| 1. Pastikan server Python sudah berjalan (<code>python app.py</code>)<br> | |
| 2. Pastikan model IndoBERT sudah terunduh dengan benar<br> | |
| 3. Pastikan dependensi Python sudah terinstall (<code>pip install -r requirements.txt</code>) | |
| </span> | |
| `, | |
| 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 <strong>${rows.length}</strong> 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'; | |
| } | |
| }); | |
| } | |