noranisa commited on
Commit
3890fa2
·
verified ·
1 Parent(s): 473078c

Update templates/result.html

Browse files
Files changed (1) hide show
  1. 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>