Spaces:
Sleeping
Sleeping
Update templates/result.html
Browse files- templates/result.html +184 -0
templates/result.html
CHANGED
|
@@ -442,6 +442,42 @@ footer {
|
|
| 442 |
<p class="insight-meta" id="insight-meta"></p>
|
| 443 |
</div>
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
<!-- FOOTER -->
|
| 446 |
<footer>
|
| 447 |
<div class="ft-kw">Kata kunci: <strong id="ft-kw">—</strong></div>
|
|
@@ -662,6 +698,9 @@ function render(d) {
|
|
| 662 |
loadImg('static/wordcloud.png','wc-box');
|
| 663 |
loadImg('static/heatmap.png','hm-box');
|
| 664 |
|
|
|
|
|
|
|
|
|
|
| 665 |
// ── CROSS-PLATFORM ANALYSIS ──
|
| 666 |
const platforms = crossData.platforms || {};
|
| 667 |
if (Object.keys(platforms).length >= 2) {
|
|
@@ -719,6 +758,151 @@ function loadImg(src, boxId) {
|
|
| 719 |
};
|
| 720 |
img.src = '/' + src + '?t=' + Date.now();
|
| 721 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 722 |
</script>
|
| 723 |
</body>
|
| 724 |
</html>
|
|
|
|
| 442 |
<p class="insight-meta" id="insight-meta"></p>
|
| 443 |
</div>
|
| 444 |
|
| 445 |
+
<!-- ABSA + NER -->
|
| 446 |
+
<div class="main-grid" style="border-bottom:1px solid var(--b1);position:relative;z-index:1">
|
| 447 |
+
<div class="panel">
|
| 448 |
+
<div class="panel-tag">13 — Aspect-Based Sentiment Analysis (ABSA)</div>
|
| 449 |
+
<div id="absa-grid" class="empty">Memuat…</div>
|
| 450 |
+
</div>
|
| 451 |
+
<div class="panel">
|
| 452 |
+
<div class="panel-tag">14 — Named Entity Recognition (NER)</div>
|
| 453 |
+
<div id="ner-grid" class="empty">Memuat…</div>
|
| 454 |
+
</div>
|
| 455 |
+
</div>
|
| 456 |
+
|
| 457 |
+
<!-- STANCE + EMOTION -->
|
| 458 |
+
<div class="main-grid" style="border-bottom:1px solid var(--b1);position:relative;z-index:1">
|
| 459 |
+
<div class="panel">
|
| 460 |
+
<div class="panel-tag">15 — Stance Detection</div>
|
| 461 |
+
<div id="stance-grid" class="empty">Memuat…</div>
|
| 462 |
+
</div>
|
| 463 |
+
<div class="panel">
|
| 464 |
+
<div class="panel-tag">16 — Emotion Detection</div>
|
| 465 |
+
<div id="emotion-grid" class="empty">Memuat…</div>
|
| 466 |
+
</div>
|
| 467 |
+
</div>
|
| 468 |
+
|
| 469 |
+
<!-- KEYWORDS + SUMMARY -->
|
| 470 |
+
<div class="main-grid" style="border-bottom:1px solid var(--b1);position:relative;z-index:1">
|
| 471 |
+
<div class="panel">
|
| 472 |
+
<div class="panel-tag">17 — Keyword & Phrase Extraction</div>
|
| 473 |
+
<div id="kw-grid" class="empty">Memuat…</div>
|
| 474 |
+
</div>
|
| 475 |
+
<div class="panel">
|
| 476 |
+
<div class="panel-tag">18 — Ringkasan Otomatis per Platform</div>
|
| 477 |
+
<div id="sum-grid" class="empty">Memuat…</div>
|
| 478 |
+
</div>
|
| 479 |
+
</div>
|
| 480 |
+
|
| 481 |
<!-- FOOTER -->
|
| 482 |
<footer>
|
| 483 |
<div class="ft-kw">Kata kunci: <strong id="ft-kw">—</strong></div>
|
|
|
|
| 698 |
loadImg('static/wordcloud.png','wc-box');
|
| 699 |
loadImg('static/heatmap.png','hm-box');
|
| 700 |
|
| 701 |
+
// ── NEW FEATURES ──
|
| 702 |
+
renderNewFeatures(d);
|
| 703 |
+
|
| 704 |
// ── CROSS-PLATFORM ANALYSIS ──
|
| 705 |
const platforms = crossData.platforms || {};
|
| 706 |
if (Object.keys(platforms).length >= 2) {
|
|
|
|
| 758 |
};
|
| 759 |
img.src = '/' + src + '?t=' + Date.now();
|
| 760 |
}
|
| 761 |
+
|
| 762 |
+
// ── ABSA ──
|
| 763 |
+
function renderABSA(absa) {
|
| 764 |
+
const el = $('absa-grid');
|
| 765 |
+
const top = absa.top_aspects || [];
|
| 766 |
+
if (!top.length) { el.innerHTML = '<span class="empty">Tidak ada aspek terdeteksi.</span>'; return; }
|
| 767 |
+
const COLORS = {Positive:'var(--pos)',Negative:'var(--neg)',Neutral:'var(--neu)',Uncertain:'var(--gold)'};
|
| 768 |
+
el.innerHTML = `
|
| 769 |
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.5rem">
|
| 770 |
+
${top.map(a => `
|
| 771 |
+
<div style="background:var(--s2);border:1px solid var(--b2);border-radius:7px;padding:0.75rem 1rem">
|
| 772 |
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.4rem">
|
| 773 |
+
<span style="font-weight:700;font-size:0.78rem;text-transform:capitalize">${esc(a.aspect)}</span>
|
| 774 |
+
<span style="font-family:var(--mono);font-size:0.55rem;padding:0.1rem 0.4rem;border-radius:3px;border:1px solid;color:${COLORS[a.dominant]||'var(--neu)'};border-color:${COLORS[a.dominant]||'var(--neu)'}">${a.dominant}</span>
|
| 775 |
+
</div>
|
| 776 |
+
<div style="display:flex;gap:3px;height:4px;border-radius:2px;overflow:hidden">
|
| 777 |
+
<div style="width:${a.pos_pct}%;background:var(--pos);border-radius:2px"></div>
|
| 778 |
+
<div style="width:${a.neg_pct}%;background:var(--neg);border-radius:2px"></div>
|
| 779 |
+
<div style="width:${a.neu_pct}%;background:var(--neu);border-radius:2px"></div>
|
| 780 |
+
</div>
|
| 781 |
+
<div style="font-family:var(--mono);font-size:0.5rem;color:var(--muted);margin-top:0.3rem">${a.total} sebutan · ${a.pos_pct}% pos · ${a.neg_pct}% neg</div>
|
| 782 |
+
</div>`).join('')}
|
| 783 |
+
</div>
|
| 784 |
+
<div style="font-family:var(--mono);font-size:0.58rem;color:var(--muted);margin-top:0.75rem">
|
| 785 |
+
${absa.aspects_found || 0} aspek ditemukan dari ${absa.total_texts_analyzed || 0} komentar
|
| 786 |
+
</div>`;
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
// ── NER ──
|
| 790 |
+
function renderNER(ner) {
|
| 791 |
+
const el = $('ner-grid');
|
| 792 |
+
const top = ner.top_entities || [];
|
| 793 |
+
if (!top.length) { el.innerHTML = '<span class="empty">Tidak ada entitas terdeteksi.</span>'; return; }
|
| 794 |
+
const TYPE_COLOR = {PER:'#4f9cf9',ORG:'#7b61ff',LOC:'#22c55e',PROD:'#f59e0b',EVENT:'#ef4444',MISC:'#94a3b8'};
|
| 795 |
+
const TYPE_LABEL = {PER:'Tokoh',ORG:'Institusi',LOC:'Lokasi',PROD:'Produk',EVENT:'Kejadian',MISC:'Lainnya'};
|
| 796 |
+
el.innerHTML = `
|
| 797 |
+
<div style="display:flex;flex-wrap:wrap;gap:0.4rem;margin-bottom:1rem">
|
| 798 |
+
${top.map(e => `
|
| 799 |
+
<div style="display:flex;align-items:center;gap:0.4rem;background:var(--s2);border:1px solid var(--b2);border-radius:6px;padding:0.3rem 0.65rem">
|
| 800 |
+
<span style="font-family:var(--mono);font-size:0.48rem;padding:0.1rem 0.35rem;border-radius:3px;background:${TYPE_COLOR[e.type]||'#94a3b8'}22;color:${TYPE_COLOR[e.type]||'#94a3b8'};border:1px solid ${TYPE_COLOR[e.type]||'#94a3b8'}44">${TYPE_LABEL[e.type]||e.type}</span>
|
| 801 |
+
<span style="font-size:0.72rem;font-weight:600">${esc(e.entity)}</span>
|
| 802 |
+
<span style="font-family:var(--mono);font-size:0.55rem;color:var(--muted)">${e.count}×</span>
|
| 803 |
+
</div>`).join('')}
|
| 804 |
+
</div>
|
| 805 |
+
<div style="font-family:var(--mono);font-size:0.58rem;color:var(--muted)">${ner.total_entities||0} entitas unik · ${ner.total_mentions||0} total sebutan</div>`;
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
// ── STANCE ──
|
| 809 |
+
function renderStance(stance) {
|
| 810 |
+
const el = $('stance-grid');
|
| 811 |
+
const c = stance.counts || {};
|
| 812 |
+
const total = (c.Favor||0) + (c.Against||0) + (c.Neutral||0) || 1;
|
| 813 |
+
el.innerHTML = `
|
| 814 |
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:0.5rem;margin-bottom:1rem">
|
| 815 |
+
${[['Favor','var(--pos)','Mendukung'],['Against','var(--neg)','Menolak'],['Neutral','var(--neu)','Netral']].map(([s,color,label]) => `
|
| 816 |
+
<div style="background:var(--s2);border:1px solid var(--b2);border-radius:7px;padding:0.75rem;text-align:center">
|
| 817 |
+
<div style="font-size:1.6rem;font-weight:800;color:${color};letter-spacing:-0.03em">${c[s]||0}</div>
|
| 818 |
+
<div style="font-family:var(--mono);font-size:0.5rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-top:0.2rem">${label}</div>
|
| 819 |
+
<div style="font-family:var(--mono);font-size:0.6rem;color:${color};margin-top:0.2rem">${Math.round((c[s]||0)/total*100)}%</div>
|
| 820 |
+
</div>`).join('')}
|
| 821 |
+
</div>
|
| 822 |
+
<div style="height:5px;background:var(--s2);border-radius:3px;display:flex;overflow:hidden;gap:2px">
|
| 823 |
+
<div style="width:${stance.favor_pct||0}%;background:var(--pos);border-radius:2px;transition:width 1s ease"></div>
|
| 824 |
+
<div style="width:${stance.against_pct||0}%;background:var(--neg);border-radius:2px;transition:width 1s ease"></div>
|
| 825 |
+
<div style="width:${stance.neutral_pct||0}%;background:var(--neu);border-radius:2px;transition:width 1s ease"></div>
|
| 826 |
+
</div>
|
| 827 |
+
<div style="font-family:var(--mono);font-size:0.58rem;color:var(--muted);margin-top:0.6rem">
|
| 828 |
+
Dominan: <b style="color:var(--text)">${stance.dominant||'—'}</b> · Target: <b style="color:var(--accent)">${esc(stance.target||'—')}</b>
|
| 829 |
+
</div>`;
|
| 830 |
+
}
|
| 831 |
+
|
| 832 |
+
// ── EMOTIONS ──
|
| 833 |
+
function renderEmotions(emotions) {
|
| 834 |
+
const el = $('emotion-grid');
|
| 835 |
+
const dist = emotions.distribution || {};
|
| 836 |
+
const EMO_ICON = {joy:'😊',anger:'😡',sadness:'😢',fear:'😨',surprise:'😮',disgust:'🤢',neutral:'😐'};
|
| 837 |
+
const EMO_COLOR = {joy:'#22c55e',anger:'#ef4444',sadness:'#4f9cf9',fear:'#f59e0b',surprise:'#7b61ff',disgust:'#94a3b8',neutral:'#5a6070'};
|
| 838 |
+
const entries = Object.entries(dist).sort((a,b)=>(b[1].count||0)-(a[1].count||0));
|
| 839 |
+
el.innerHTML = `
|
| 840 |
+
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.4rem;margin-bottom:0.75rem">
|
| 841 |
+
${entries.slice(0,8).map(([emo,data]) => `
|
| 842 |
+
<div style="background:var(--s2);border:1px solid var(--b2);border-radius:7px;padding:0.6rem;text-align:center;${emotions.dominant===emo?'border-color:'+EMO_COLOR[emo]||'var(--accent)':''}">
|
| 843 |
+
<div style="font-size:1.2rem">${EMO_ICON[emo]||'❓'}</div>
|
| 844 |
+
<div style="font-family:var(--mono);font-size:0.5rem;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-top:0.2rem">${emo}</div>
|
| 845 |
+
<div style="font-size:1rem;font-weight:700;color:${EMO_COLOR[emo]||'var(--text)'}">${data.count||0}</div>
|
| 846 |
+
<div style="font-family:var(--mono);font-size:0.5rem;color:var(--muted)">${data.pct||0}%</div>
|
| 847 |
+
</div>`).join('')}
|
| 848 |
+
</div>
|
| 849 |
+
<div style="font-family:var(--mono);font-size:0.58rem;color:var(--muted)">
|
| 850 |
+
Emosi dominan: <b style="color:var(--text)">${emotions.dominant||'—'}</b> · ${emotions.emotional_pct||0}% bersifat emosional
|
| 851 |
+
</div>`;
|
| 852 |
+
}
|
| 853 |
+
|
| 854 |
+
// ── KEYWORDS ──
|
| 855 |
+
function renderKeywords(keywords) {
|
| 856 |
+
const el = $('kw-grid');
|
| 857 |
+
if (!keywords.length) { el.innerHTML = '<span class="empty">Tidak ada frasa kunci.</span>'; return; }
|
| 858 |
+
const maxScore = keywords[0].score || 1;
|
| 859 |
+
const TYPE_COLOR = {'word':'var(--accent)','phrase':'var(--a2)','multi-phrase':'var(--gold)'};
|
| 860 |
+
el.innerHTML = `
|
| 861 |
+
<div style="display:flex;flex-wrap:wrap;gap:0.4rem">
|
| 862 |
+
${keywords.map(kw => {
|
| 863 |
+
const sz = 0.62 + (kw.score/maxScore)*0.5;
|
| 864 |
+
const color = TYPE_COLOR[kw.type]||'var(--m2)';
|
| 865 |
+
return `<span style="font-family:var(--mono);font-size:${sz}rem;background:var(--s2);border:1px solid var(--b2);border-radius:5px;padding:0.2rem 0.55rem;color:${color};cursor:default;transition:border-color .15s" title="${kw.type} · ${kw.frequency}× · score:${kw.score}">${esc(kw.phrase)}</span>`;
|
| 866 |
+
}).join('')}
|
| 867 |
+
</div>
|
| 868 |
+
<div style="font-family:var(--mono);font-size:0.55rem;color:var(--muted);margin-top:0.75rem;display:flex;gap:1rem">
|
| 869 |
+
<span><span style="color:var(--accent)">■</span> Kata</span>
|
| 870 |
+
<span><span style="color:var(--a2)">■</span> Frasa 2 kata</span>
|
| 871 |
+
<span><span style="color:var(--gold)">■</span> Frasa 3+ kata</span>
|
| 872 |
+
</div>`;
|
| 873 |
+
}
|
| 874 |
+
|
| 875 |
+
// ── SUMMARIES ──
|
| 876 |
+
function renderSummaries(summaries) {
|
| 877 |
+
const el = $('sum-grid');
|
| 878 |
+
if (!summaries || !Object.keys(summaries).length) {
|
| 879 |
+
el.innerHTML = '<span class="empty">Tidak ada ringkasan.</span>'; return;
|
| 880 |
+
}
|
| 881 |
+
const overall = summaries['_overall'];
|
| 882 |
+
const platforms = Object.entries(summaries).filter(([k]) => k !== '_overall');
|
| 883 |
+
el.innerHTML = `
|
| 884 |
+
${overall ? `
|
| 885 |
+
<div style="background:var(--s2);border:1px solid rgba(79,156,249,0.2);border-radius:7px;padding:1rem;margin-bottom:0.75rem">
|
| 886 |
+
<div style="font-family:var(--mono);font-size:0.5rem;letter-spacing:.15em;text-transform:uppercase;color:var(--accent);margin-bottom:0.5rem">Ringkasan Keseluruhan</div>
|
| 887 |
+
<div style="font-size:0.8rem;line-height:1.65;color:var(--text)">${esc(overall.summary||'')}</div>
|
| 888 |
+
<div style="font-family:var(--mono);font-size:0.52rem;color:var(--muted);margin-top:0.4rem">${overall.text_count} komentar dirangkum</div>
|
| 889 |
+
</div>` : ''}
|
| 890 |
+
${platforms.map(([src,data]) => `
|
| 891 |
+
<div style="background:var(--s2);border:1px solid var(--b2);border-radius:7px;padding:0.75rem;margin-bottom:0.5rem">
|
| 892 |
+
<div style="font-family:var(--mono);font-size:0.5rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-bottom:0.4rem">${esc(src.capitalize?.() || src)}</div>
|
| 893 |
+
<div style="font-size:0.75rem;line-height:1.6;color:var(--m2)">${esc(data.summary||'')}</div>
|
| 894 |
+
</div>`).join('')}`;
|
| 895 |
+
}
|
| 896 |
+
|
| 897 |
+
// ── CALL ALL NEW RENDERS ──
|
| 898 |
+
function renderNewFeatures(d) {
|
| 899 |
+
if (d.absa) renderABSA(d.absa);
|
| 900 |
+
if (d.ner) renderNER(d.ner);
|
| 901 |
+
if (d.stance) renderStance(d.stance);
|
| 902 |
+
if (d.emotions) renderEmotions(d.emotions);
|
| 903 |
+
if (d.keywords) renderKeywords(d.keywords);
|
| 904 |
+
if (d.summaries)renderSummaries(d.summaries);
|
| 905 |
+
}
|
| 906 |
</script>
|
| 907 |
</body>
|
| 908 |
</html>
|