Spaces:
Running
Running
Update templates/result.html
Browse files- templates/result.html +580 -165
templates/result.html
CHANGED
|
@@ -2,202 +2,617 @@
|
|
| 2 |
<html lang="id">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
-
<
|
| 6 |
-
|
| 7 |
-
<link href="https://
|
| 8 |
-
<
|
| 9 |
-
|
| 10 |
<style>
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
}
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
</head>
|
| 21 |
-
|
| 22 |
-
<body>
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
<!-- π₯ CHART -->
|
| 31 |
-
<div class="row mt-3">
|
| 32 |
-
|
| 33 |
-
<div class="col-md-6">
|
| 34 |
-
<div class="card p-3">
|
| 35 |
-
<h5>Distribusi Sentimen</h5>
|
| 36 |
-
<canvas id="pieChart"></canvas>
|
| 37 |
-
</div>
|
| 38 |
-
</div>
|
| 39 |
-
|
| 40 |
-
<div class="col-md-6">
|
| 41 |
-
<div class="card p-3">
|
| 42 |
-
<h5>Per Platform</h5>
|
| 43 |
-
<canvas id="platformChart"></canvas>
|
| 44 |
-
</div>
|
| 45 |
-
</div>
|
| 46 |
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
</div>
|
| 67 |
|
| 68 |
-
<!--
|
| 69 |
-
<div class="
|
| 70 |
-
<
|
| 71 |
-
<
|
| 72 |
-
|
| 73 |
-
<
|
| 74 |
-
|
| 75 |
-
</
|
|
|
|
|
|
|
| 76 |
</div>
|
| 77 |
|
| 78 |
-
<!--
|
| 79 |
-
<div class="
|
| 80 |
-
<
|
| 81 |
-
<
|
| 82 |
-
|
| 83 |
-
<
|
| 84 |
-
|
| 85 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
</div>
|
| 87 |
|
| 88 |
-
<!--
|
| 89 |
-
<div class="
|
| 90 |
-
<
|
| 91 |
-
<
|
|
|
|
|
|
|
| 92 |
</div>
|
| 93 |
|
| 94 |
-
<!--
|
| 95 |
-
<div class="
|
| 96 |
-
<
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
<
|
| 100 |
-
|
| 101 |
-
<
|
| 102 |
-
|
| 103 |
-
<
|
| 104 |
-
<
|
| 105 |
-
<
|
| 106 |
-
<
|
| 107 |
-
<
|
| 108 |
-
<th>F1</th>
|
| 109 |
-
</tr>
|
| 110 |
-
|
| 111 |
-
{% for label, metrics in eval_result.report.items() %}
|
| 112 |
-
{% if label in ['positive','neutral','negative'] %}
|
| 113 |
-
<tr>
|
| 114 |
-
<td>{{ label }}</td>
|
| 115 |
-
<td>{{ metrics.precision|round(2) }}</td>
|
| 116 |
-
<td>{{ metrics.recall|round(2) }}</td>
|
| 117 |
-
<td>{{ metrics['f1-score']|round(2) }}</td>
|
| 118 |
-
</tr>
|
| 119 |
-
{% endif %}
|
| 120 |
-
{% endfor %}
|
| 121 |
-
|
| 122 |
-
</table>
|
| 123 |
-
{% endif %}
|
| 124 |
-
|
| 125 |
</div>
|
| 126 |
|
| 127 |
-
<!--
|
| 128 |
-
<div class="
|
| 129 |
-
<
|
| 130 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
</div>
|
| 132 |
|
| 133 |
-
<!--
|
| 134 |
-
<div class="
|
| 135 |
-
<
|
| 136 |
-
|
| 137 |
-
<table class="table table-dark table-striped">
|
| 138 |
-
<thead>
|
| 139 |
-
<tr>
|
| 140 |
-
<th>No</th>
|
| 141 |
-
<th>Komentar</th>
|
| 142 |
-
<th>Sentimen</th>
|
| 143 |
-
<th>Source</th>
|
| 144 |
-
</tr>
|
| 145 |
-
</thead>
|
| 146 |
-
|
| 147 |
-
<tbody>
|
| 148 |
-
{% for t,s,src in data %}
|
| 149 |
-
<tr>
|
| 150 |
-
<td>{{ loop.index }}</td>
|
| 151 |
-
<td>{{ t }}</td>
|
| 152 |
-
<td>{{ s }}</td>
|
| 153 |
-
<td>{{ src }}</td>
|
| 154 |
-
</tr>
|
| 155 |
-
{% endfor %}
|
| 156 |
-
</tbody>
|
| 157 |
-
|
| 158 |
-
</table>
|
| 159 |
-
|
| 160 |
</div>
|
| 161 |
|
| 162 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
-
<!-- π₯ CHART SCRIPT -->
|
| 165 |
<script>
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
data: {
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
}
|
| 180 |
-
});
|
| 181 |
|
| 182 |
-
//
|
| 183 |
-
const
|
| 184 |
-
const
|
| 185 |
-
const
|
| 186 |
-
const
|
| 187 |
|
| 188 |
-
new Chart(
|
| 189 |
-
type: '
|
| 190 |
data: {
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
}
|
| 198 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
</script>
|
| 201 |
-
|
| 202 |
</body>
|
| 203 |
</html>
|
|
|
|
| 2 |
<html lang="id">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>SentiScope β Hasil Analisis</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 8 |
+
<link href="https://fonts.googleapis.com/css2?family=Cabinet+Grotesk:wght@400;500;700;800&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
|
| 9 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js"></script>
|
| 10 |
<style>
|
| 11 |
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
| 12 |
+
:root {
|
| 13 |
+
--bg: #080a0f;
|
| 14 |
+
--s1: #0e1117;
|
| 15 |
+
--s2: #141820;
|
| 16 |
+
--s3: #1a2030;
|
| 17 |
+
--b1: rgba(255,255,255,0.06);
|
| 18 |
+
--b2: rgba(255,255,255,0.10);
|
| 19 |
+
--text: #e8eaf0;
|
| 20 |
+
--muted: #4a5568;
|
| 21 |
+
--m2: #8892a4;
|
| 22 |
+
--accent: #4f9cf9;
|
| 23 |
+
--a2: #7b61ff;
|
| 24 |
+
--pos: #22c55e;
|
| 25 |
+
--neg: #ef4444;
|
| 26 |
+
--neu: #94a3b8;
|
| 27 |
+
--gold: #f59e0b;
|
| 28 |
+
--display:'Cabinet Grotesk', sans-serif;
|
| 29 |
+
--mono: 'JetBrains Mono', monospace;
|
| 30 |
}
|
| 31 |
+
html { scroll-behavior: smooth; }
|
| 32 |
+
body { font-family: var(--display); background: var(--bg); color: var(--text); min-height: 100vh; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
+
body::before {
|
| 35 |
+
content:''; position:fixed; inset:0;
|
| 36 |
+
background-image: linear-gradient(rgba(79,156,249,0.02) 1px, transparent 1px), linear-gradient(90deg, rgba(79,156,249,0.02) 1px, transparent 1px);
|
| 37 |
+
background-size: 64px 64px; pointer-events:none; z-index:0;
|
| 38 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
+
/* ββ TOPBAR ββ */
|
| 41 |
+
.topbar {
|
| 42 |
+
position: sticky; top: 0; z-index: 100;
|
| 43 |
+
display: grid; grid-template-columns: auto 1fr auto;
|
| 44 |
+
align-items: center; gap: 1.5rem;
|
| 45 |
+
padding: 0.9rem 2rem;
|
| 46 |
+
background: rgba(8,10,15,0.88); backdrop-filter: blur(20px);
|
| 47 |
+
border-bottom: 1px solid var(--b1);
|
| 48 |
+
}
|
| 49 |
+
.back {
|
| 50 |
+
display: flex; align-items: center; gap: 0.4rem;
|
| 51 |
+
font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase;
|
| 52 |
+
color: var(--m2); text-decoration: none; transition: color .2s;
|
| 53 |
+
}
|
| 54 |
+
.back:hover { color: var(--text); }
|
| 55 |
+
.kw-display { font-size: 0.9rem; font-weight: 700; letter-spacing: -0.01em; }
|
| 56 |
+
.kw-display span { color: var(--accent); }
|
| 57 |
+
.topbar-right { display: flex; align-items: center; gap: 1rem; }
|
| 58 |
+
.tb-stat { display: flex; flex-direction: column; align-items: flex-end; gap: 0.1rem; }
|
| 59 |
+
.tb-stat-l { font-family: var(--mono); font-size: 0.5rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
|
| 60 |
+
.tb-stat-v { font-family: var(--mono); font-size: 0.8rem; font-weight: 500; color: var(--text); }
|
| 61 |
+
.btn-dl {
|
| 62 |
+
font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase;
|
| 63 |
+
background: var(--s2); color: var(--m2); border: 1px solid var(--b2);
|
| 64 |
+
padding: 0.5rem 1rem; border-radius: 6px; text-decoration: none;
|
| 65 |
+
transition: border-color .2s, color .2s;
|
| 66 |
+
}
|
| 67 |
+
.btn-dl:hover { border-color: var(--accent); color: var(--accent); }
|
| 68 |
+
|
| 69 |
+
/* ββ METRIC CARDS ββ */
|
| 70 |
+
.metrics {
|
| 71 |
+
display: grid; grid-template-columns: repeat(5, 1fr);
|
| 72 |
+
gap: 1px; background: var(--b1);
|
| 73 |
+
border-bottom: 1px solid var(--b1);
|
| 74 |
+
position: relative; z-index: 1;
|
| 75 |
+
}
|
| 76 |
+
.metric {
|
| 77 |
+
background: var(--s1); padding: 1.5rem 2rem;
|
| 78 |
+
display: flex; flex-direction: column; gap: 0.5rem;
|
| 79 |
+
transition: background .2s;
|
| 80 |
+
opacity: 0; transform: translateY(12px);
|
| 81 |
+
animation: fu 0.45s both;
|
| 82 |
+
}
|
| 83 |
+
.metric:hover { background: var(--s2); }
|
| 84 |
+
.metric:nth-child(1){animation-delay:.05s}
|
| 85 |
+
.metric:nth-child(2){animation-delay:.1s}
|
| 86 |
+
.metric:nth-child(3){animation-delay:.15s}
|
| 87 |
+
.metric:nth-child(4){animation-delay:.2s}
|
| 88 |
+
.metric:nth-child(5){animation-delay:.25s}
|
| 89 |
+
.metric-l { font-family: var(--mono); font-size: 0.52rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }
|
| 90 |
+
.metric-n { font-size: 2.8rem; font-weight: 800; letter-spacing: -0.04em; line-height: 1; }
|
| 91 |
+
.metric-n.c-pos { color: var(--pos); }
|
| 92 |
+
.metric-n.c-neg { color: var(--neg); }
|
| 93 |
+
.metric-n.c-neu { color: var(--neu); }
|
| 94 |
+
.metric-n.c-up { color: var(--pos); }
|
| 95 |
+
.metric-n.c-dn { color: var(--neg); }
|
| 96 |
+
.metric-n.c-base{ color: var(--text); }
|
| 97 |
+
.metric-sub { font-family: var(--mono); font-size: 0.58rem; color: var(--muted); }
|
| 98 |
+
|
| 99 |
+
/* ββ DISTRIBUTION BAR ββ */
|
| 100 |
+
.dist-bar {
|
| 101 |
+
position: relative; z-index: 1;
|
| 102 |
+
display: flex; align-items: center; gap: 1.5rem;
|
| 103 |
+
padding: 1rem 2rem; border-bottom: 1px solid var(--b1);
|
| 104 |
+
background: var(--s1);
|
| 105 |
+
}
|
| 106 |
+
.dist-l { font-family: var(--mono); font-size: 0.55rem; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
|
| 107 |
+
.dist-track { flex: 1; height: 5px; background: var(--s2); border-radius: 3px; display: flex; overflow: hidden; gap: 2px; }
|
| 108 |
+
.ds { height: 100%; transition: width 1.2s cubic-bezier(.25,.46,.45,.94); border-radius: 2px; }
|
| 109 |
+
.ds-p { background: var(--pos); }
|
| 110 |
+
.ds-n { background: var(--neg); }
|
| 111 |
+
.ds-u { background: var(--neu); }
|
| 112 |
+
.dist-info { font-family: var(--mono); font-size: 0.58rem; color: var(--m2); white-space: nowrap; }
|
| 113 |
+
|
| 114 |
+
/* ββ GRID LAYOUT ββ */
|
| 115 |
+
.main-grid {
|
| 116 |
+
position: relative; z-index: 1;
|
| 117 |
+
display: grid; grid-template-columns: 1fr 1fr;
|
| 118 |
+
gap: 1px; background: var(--b1);
|
| 119 |
+
border-bottom: 1px solid var(--b1);
|
| 120 |
+
}
|
| 121 |
+
.three-col {
|
| 122 |
+
display: grid; grid-template-columns: 1fr 1fr 1fr;
|
| 123 |
+
gap: 1px; background: var(--b1);
|
| 124 |
+
border-bottom: 1px solid var(--b1);
|
| 125 |
+
}
|
| 126 |
+
.full-row {
|
| 127 |
+
position: relative; z-index: 1;
|
| 128 |
+
border-bottom: 1px solid var(--b1);
|
| 129 |
+
}
|
| 130 |
|
| 131 |
+
/* ββ PANEL ββ */
|
| 132 |
+
.panel { background: var(--s1); padding: 1.75rem 2rem; }
|
| 133 |
+
.panel:hover { background: var(--s1); }
|
| 134 |
+
.panel-tag {
|
| 135 |
+
font-family: var(--mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase;
|
| 136 |
+
color: var(--muted); margin-bottom: 1.25rem;
|
| 137 |
+
display: flex; align-items: center; gap: 0.8rem;
|
| 138 |
+
}
|
| 139 |
+
.panel-tag::after { content:''; flex:1; height:1px; background: var(--b2); }
|
| 140 |
+
.panel-title { font-size: 1.1rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 1.25rem; }
|
| 141 |
+
|
| 142 |
+
/* ββ CHART ββ */
|
| 143 |
+
.chart-wrap { position:relative; height:240px; }
|
| 144 |
+
|
| 145 |
+
/* ββ TABLE ββ */
|
| 146 |
+
.tbl-wrap { overflow-x:auto; max-height: 320px; overflow-y:auto; }
|
| 147 |
+
.tbl-wrap::-webkit-scrollbar { width: 3px; height: 3px; }
|
| 148 |
+
.tbl-wrap::-webkit-scrollbar-track { background: transparent; }
|
| 149 |
+
.tbl-wrap::-webkit-scrollbar-thumb { background: var(--b2); border-radius: 2px; }
|
| 150 |
+
table { width:100%; border-collapse:collapse; font-family: var(--mono); font-size:0.68rem; }
|
| 151 |
+
th {
|
| 152 |
+
text-align:left; font-size:0.5rem; letter-spacing:.15em; text-transform:uppercase;
|
| 153 |
+
color: var(--muted); padding: 0.5rem 0.75rem; border-bottom:1px solid var(--b2);
|
| 154 |
+
position: sticky; top: 0; background: var(--s1);
|
| 155 |
+
}
|
| 156 |
+
td { padding: 0.65rem 0.75rem; border-bottom: 1px solid rgba(255,255,255,0.03); vertical-align:top; line-height:1.45; }
|
| 157 |
+
tr:last-child td { border-bottom:none; }
|
| 158 |
+
tr:hover td { background: var(--s2); }
|
| 159 |
+
.tag {
|
| 160 |
+
display:inline-block; font-size:0.5rem; letter-spacing:.08em; text-transform:uppercase;
|
| 161 |
+
padding: 0.15rem 0.4rem; border-radius: 4px; border: 1px solid; white-space:nowrap;
|
| 162 |
+
}
|
| 163 |
+
.tag-p { color: var(--pos); border-color: rgba(34,197,94,0.3); background: rgba(34,197,94,0.08); }
|
| 164 |
+
.tag-n { color: var(--neg); border-color: rgba(239,68,68,0.3); background: rgba(239,68,68,0.08); }
|
| 165 |
+
.tag-u { color: var(--neu); border-color: rgba(148,163,184,0.3); background: rgba(148,163,184,0.08); }
|
| 166 |
+
.src-badge {
|
| 167 |
+
font-size: 0.48rem; letter-spacing: .06em; text-transform:uppercase;
|
| 168 |
+
padding: 0.12rem 0.35rem; border-radius: 3px; border: 1px solid var(--b2); color: var(--muted);
|
| 169 |
+
}
|
| 170 |
|
| 171 |
+
/* ββ WORD CLOUD ββ */
|
| 172 |
+
.word-cloud { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; }
|
| 173 |
+
.w-chip { font-family: var(--mono); color: var(--m2); cursor:default; transition: color .2s; line-height: 1.2; }
|
| 174 |
+
.w-chip:hover { color: var(--accent); }
|
| 175 |
+
|
| 176 |
+
/* ββ TOPIC CARDS ββ */
|
| 177 |
+
.topic-list { display: flex; flex-direction: column; gap: 0.75rem; }
|
| 178 |
+
.topic-card { background: var(--s2); border: 1px solid var(--b2); border-radius: 8px; padding: 0.9rem 1rem; }
|
| 179 |
+
.topic-card-h { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.6rem; }
|
| 180 |
+
.topic-num { font-family: var(--mono); font-size: 0.5rem; letter-spacing: .15em; text-transform: uppercase; color: var(--muted); }
|
| 181 |
+
.topic-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); opacity: 0.6; }
|
| 182 |
+
.topic-words { display: flex; flex-wrap: wrap; gap: 0.3rem; }
|
| 183 |
+
.tw { font-family: var(--mono); font-size: 0.62rem; background: var(--s3); border: 1px solid var(--b2); padding: 0.18rem 0.45rem; border-radius: 4px; color: var(--m2); }
|
| 184 |
+
|
| 185 |
+
/* ββ CLUSTERS ββ */
|
| 186 |
+
.cluster-list { display: flex; flex-direction: column; gap: 0.75rem; }
|
| 187 |
+
.cl-item { border-left: 2px solid var(--b2); padding-left: 0.9rem; }
|
| 188 |
+
.cl-head { font-family: var(--mono); font-size: 0.52rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 0.35rem; }
|
| 189 |
+
.cl-text { font-family: var(--mono); font-size: 0.65rem; color: var(--m2); line-height: 1.5; }
|
| 190 |
+
|
| 191 |
+
/* ββ HOAX LIST ββ */
|
| 192 |
+
.hoax-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
| 193 |
+
.hoax-item {
|
| 194 |
+
display: flex; align-items: flex-start; gap: 0.7rem;
|
| 195 |
+
background: var(--s2); border: 1px solid var(--b1); border-radius: 6px; padding: 0.65rem 0.75rem;
|
| 196 |
+
transition: border-color .2s;
|
| 197 |
+
}
|
| 198 |
+
.hoax-item:hover { border-color: var(--b2); }
|
| 199 |
+
.h-badge {
|
| 200 |
+
font-family: var(--mono); font-size: 0.48rem; letter-spacing: .08em; text-transform: uppercase;
|
| 201 |
+
padding: 0.15rem 0.4rem; border-radius: 3px; white-space: nowrap; flex-shrink: 0; margin-top: 0.1rem;
|
| 202 |
+
}
|
| 203 |
+
.hb-hoax { background: rgba(239,68,68,0.15); color: var(--neg); border: 1px solid rgba(239,68,68,0.25); }
|
| 204 |
+
.hb-norm { background: var(--s3); color: var(--muted); border: 1px solid var(--b2); }
|
| 205 |
+
.h-text { font-family: var(--mono); font-size: 0.65rem; color: var(--m2); line-height: 1.5; }
|
| 206 |
+
|
| 207 |
+
/* ββ BOT LIST ββ */
|
| 208 |
+
.bot-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
| 209 |
+
.bot-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0; border-bottom: 1px solid var(--b1); }
|
| 210 |
+
.bot-item:last-child { border-bottom: none; }
|
| 211 |
+
.bot-id { font-family: var(--mono); font-size: 0.62rem; color: var(--m2); min-width: 3.5rem; }
|
| 212 |
+
.bot-bar { flex: 1; height: 3px; background: var(--s2); border-radius: 2px; overflow: hidden; }
|
| 213 |
+
.bot-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--a2)); border-radius: 2px; transition: width 1s ease; }
|
| 214 |
+
.bot-score { font-family: var(--mono); font-size: 0.6rem; color: var(--muted); min-width: 2.5rem; text-align: right; }
|
| 215 |
+
|
| 216 |
+
/* ββ INSIGHT ββ */
|
| 217 |
+
.insight-block {
|
| 218 |
+
position: relative; z-index: 1;
|
| 219 |
+
background: linear-gradient(135deg, var(--s2) 0%, var(--s3) 100%);
|
| 220 |
+
border-bottom: 1px solid var(--b1);
|
| 221 |
+
padding: 2.5rem 2rem;
|
| 222 |
+
display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: center;
|
| 223 |
+
overflow: hidden;
|
| 224 |
+
}
|
| 225 |
+
.insight-block::before {
|
| 226 |
+
content:''; position:absolute; top:0;left:0;right:0;height:1px;
|
| 227 |
+
background: linear-gradient(90deg, transparent, rgba(79,156,249,0.4), transparent);
|
| 228 |
+
}
|
| 229 |
+
.insight-text { font-size: 1.2rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.5; }
|
| 230 |
+
.insight-text em { font-style: normal; color: var(--accent); }
|
| 231 |
+
.insight-meta { font-family: var(--mono); font-size: 0.58rem; color: var(--muted); line-height: 1.7; white-space: nowrap; text-align: right; }
|
| 232 |
+
|
| 233 |
+
/* ββ IMAGES ββ */
|
| 234 |
+
.img-grid {
|
| 235 |
+
position: relative; z-index: 1;
|
| 236 |
+
display: grid; grid-template-columns: 1fr 1fr;
|
| 237 |
+
gap: 1px; background: var(--b1);
|
| 238 |
+
border-bottom: 1px solid var(--b1);
|
| 239 |
+
}
|
| 240 |
+
.img-panel { background: var(--s1); padding: 1.75rem 2rem; }
|
| 241 |
+
.img-panel img { width:100%; height:auto; display:block; border: 1px solid var(--b2); border-radius: 6px; }
|
| 242 |
+
.img-ph {
|
| 243 |
+
width:100%; height:160px; background: var(--s2); border: 1px dashed var(--b2); border-radius: 6px;
|
| 244 |
+
display:flex; align-items:center; justify-content:center;
|
| 245 |
+
font-family: var(--mono); font-size:0.62rem; color: var(--muted);
|
| 246 |
+
}
|
| 247 |
|
| 248 |
+
/* ββ FOOTER ββ */
|
| 249 |
+
footer {
|
| 250 |
+
position: relative; z-index: 1;
|
| 251 |
+
padding: 1.5rem 2rem;
|
| 252 |
+
display: flex; align-items: center; justify-content: space-between;
|
| 253 |
+
border-top: 1px solid var(--b1);
|
| 254 |
+
}
|
| 255 |
+
.ft-kw { font-family: var(--mono); font-size: 0.6rem; color: var(--muted); }
|
| 256 |
+
.ft-kw strong { font-family: var(--display); font-size: 0.8rem; font-weight: 700; color: var(--accent); }
|
| 257 |
+
.ft-r { display: flex; gap: 1rem; }
|
| 258 |
+
.ft-r a {
|
| 259 |
+
font-family: var(--mono); font-size: 0.6rem; letter-spacing: .08em; text-transform: uppercase;
|
| 260 |
+
color: var(--muted); text-decoration: none; transition: color .2s;
|
| 261 |
+
}
|
| 262 |
+
.ft-r a:hover { color: var(--text); }
|
| 263 |
+
|
| 264 |
+
/* ββ EMPTY ββ */
|
| 265 |
+
.empty { font-family: var(--mono); font-size: 0.65rem; color: var(--muted); font-style: italic; }
|
| 266 |
+
|
| 267 |
+
/* ββ ANIM ββ */
|
| 268 |
+
@keyframes fu { from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:translateY(0)} }
|
| 269 |
+
|
| 270 |
+
/* ββ RESPONSIVE ββ */
|
| 271 |
+
@media(max-width:960px){
|
| 272 |
+
.metrics { grid-template-columns: repeat(3,1fr); }
|
| 273 |
+
.main-grid { grid-template-columns: 1fr; }
|
| 274 |
+
.three-col { grid-template-columns: 1fr; }
|
| 275 |
+
.img-grid { grid-template-columns: 1fr; }
|
| 276 |
+
.insight-block { grid-template-columns: 1fr; }
|
| 277 |
+
.insight-meta { text-align: left; white-space: normal; }
|
| 278 |
+
.topbar { padding: 0.8rem 1.2rem; }
|
| 279 |
+
.panel { padding: 1.5rem; }
|
| 280 |
+
}
|
| 281 |
+
</style>
|
| 282 |
+
</head>
|
| 283 |
+
<body>
|
| 284 |
|
| 285 |
+
<!-- TOPBAR -->
|
| 286 |
+
<header class="topbar">
|
| 287 |
+
<a href="/" class="back">β Kembali</a>
|
| 288 |
+
<div class="kw-display">Analisis: <span id="h-kw">β</span></div>
|
| 289 |
+
<div class="topbar-right">
|
| 290 |
+
<div class="tb-stat">
|
| 291 |
+
<span class="tb-stat-l">Total</span>
|
| 292 |
+
<span class="tb-stat-v" id="h-total">β</span>
|
| 293 |
+
</div>
|
| 294 |
+
<div class="tb-stat">
|
| 295 |
+
<span class="tb-stat-l">Sumber</span>
|
| 296 |
+
<span class="tb-stat-v" id="h-src">β</span>
|
| 297 |
+
</div>
|
| 298 |
+
<a href="/download" class="btn-dl">β CSV</a>
|
| 299 |
+
</div>
|
| 300 |
+
</header>
|
| 301 |
+
|
| 302 |
+
<!-- METRICS -->
|
| 303 |
+
<section class="metrics">
|
| 304 |
+
<div class="metric">
|
| 305 |
+
<span class="metric-l">Total Komentar</span>
|
| 306 |
+
<div class="metric-n c-base" id="m-total">β</div>
|
| 307 |
+
<span class="metric-sub">data diproses</span>
|
| 308 |
+
</div>
|
| 309 |
+
<div class="metric">
|
| 310 |
+
<span class="metric-l">Positif</span>
|
| 311 |
+
<div class="metric-n c-pos" id="m-pos">β</div>
|
| 312 |
+
<span class="metric-sub" id="m-pos-p">0%</span>
|
| 313 |
+
</div>
|
| 314 |
+
<div class="metric">
|
| 315 |
+
<span class="metric-l">Negatif</span>
|
| 316 |
+
<div class="metric-n c-neg" id="m-neg">β</div>
|
| 317 |
+
<span class="metric-sub" id="m-neg-p">0%</span>
|
| 318 |
+
</div>
|
| 319 |
+
<div class="metric">
|
| 320 |
+
<span class="metric-l">Netral</span>
|
| 321 |
+
<div class="metric-n c-neu" id="m-neu">β</div>
|
| 322 |
+
<span class="metric-sub" id="m-neu-p">0%</span>
|
| 323 |
+
</div>
|
| 324 |
+
<div class="metric">
|
| 325 |
+
<span class="metric-l">Prediksi Tren</span>
|
| 326 |
+
<div class="metric-n" id="m-trend" style="font-size:1.4rem;line-height:1.6">β</div>
|
| 327 |
+
<span class="metric-sub">regresi linier</span>
|
| 328 |
+
</div>
|
| 329 |
+
</section>
|
| 330 |
+
|
| 331 |
+
<!-- DISTRIBUTION -->
|
| 332 |
+
<div class="dist-bar">
|
| 333 |
+
<span class="dist-l">Distribusi Sentimen</span>
|
| 334 |
+
<div class="dist-track">
|
| 335 |
+
<div class="ds ds-p" id="ds-p" style="width:0%"></div>
|
| 336 |
+
<div class="ds ds-n" id="ds-n" style="width:0%"></div>
|
| 337 |
+
<div class="ds ds-u" id="ds-u" style="width:0%"></div>
|
| 338 |
+
</div>
|
| 339 |
+
<span class="dist-info" id="dist-info">β</span>
|
| 340 |
</div>
|
| 341 |
|
| 342 |
+
<!-- CHART + WORDS -->
|
| 343 |
+
<div class="main-grid">
|
| 344 |
+
<div class="panel">
|
| 345 |
+
<div class="panel-tag">01 β Distribusi Sentimen</div>
|
| 346 |
+
<div class="chart-wrap"><canvas id="c-pie"></canvas></div>
|
| 347 |
+
</div>
|
| 348 |
+
<div class="panel">
|
| 349 |
+
<div class="panel-tag">02 β Kata Dominan</div>
|
| 350 |
+
<div class="word-cloud" id="words"><span class="empty">Memuatβ¦</span></div>
|
| 351 |
+
</div>
|
| 352 |
</div>
|
| 353 |
|
| 354 |
+
<!-- TABLE + TOPICS -->
|
| 355 |
+
<div class="main-grid">
|
| 356 |
+
<div class="panel">
|
| 357 |
+
<div class="panel-tag">03 β Data Komentar</div>
|
| 358 |
+
<div class="tbl-wrap">
|
| 359 |
+
<table>
|
| 360 |
+
<thead><tr><th>#</th><th>Sumber</th><th>Komentar</th><th>Sentimen</th></tr></thead>
|
| 361 |
+
<tbody id="tbl"></tbody>
|
| 362 |
+
</table>
|
| 363 |
+
</div>
|
| 364 |
+
</div>
|
| 365 |
+
<div class="panel">
|
| 366 |
+
<div class="panel-tag">04 β Topic Modeling (LDA)</div>
|
| 367 |
+
<div class="topic-list" id="topics"><span class="empty">Memuatβ¦</span></div>
|
| 368 |
+
</div>
|
| 369 |
</div>
|
| 370 |
|
| 371 |
+
<!-- TIMELINE -->
|
| 372 |
+
<div class="full-row">
|
| 373 |
+
<div class="panel">
|
| 374 |
+
<div class="panel-tag">05 β Timeline Sentimen</div>
|
| 375 |
+
<div class="chart-wrap" style="height:200px"><canvas id="c-line"></canvas></div>
|
| 376 |
+
</div>
|
| 377 |
</div>
|
| 378 |
|
| 379 |
+
<!-- CLUSTER + HOAX + BOT -->
|
| 380 |
+
<div class="three-col">
|
| 381 |
+
<div class="panel">
|
| 382 |
+
<div class="panel-tag">06 β Kluster Opini</div>
|
| 383 |
+
<div class="cluster-list" id="clusters"><span class="empty">Memuatβ¦</span></div>
|
| 384 |
+
</div>
|
| 385 |
+
<div class="panel">
|
| 386 |
+
<div class="panel-tag">07 β Deteksi Konten Hoaks</div>
|
| 387 |
+
<div class="hoax-list" id="hoax"><span class="empty">Memuatβ¦</span></div>
|
| 388 |
+
</div>
|
| 389 |
+
<div class="panel">
|
| 390 |
+
<div class="panel-tag">08 β Bot Network Score</div>
|
| 391 |
+
<div class="bot-list" id="bots"><span class="empty">Memuatβ¦</span></div>
|
| 392 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
</div>
|
| 394 |
|
| 395 |
+
<!-- IMAGES -->
|
| 396 |
+
<div class="img-grid">
|
| 397 |
+
<div class="img-panel">
|
| 398 |
+
<div class="panel-tag">09 β Word Cloud</div>
|
| 399 |
+
<div id="wc-box"><div class="img-ph">Wordcloud diproses server</div></div>
|
| 400 |
+
</div>
|
| 401 |
+
<div class="img-panel">
|
| 402 |
+
<div class="panel-tag">10 β Heatmap Sumber Γ Sentimen</div>
|
| 403 |
+
<div id="hm-box"><div class="img-ph">Heatmap diproses server</div></div>
|
| 404 |
+
</div>
|
| 405 |
</div>
|
| 406 |
|
| 407 |
+
<!-- INSIGHT -->
|
| 408 |
+
<div class="insight-block">
|
| 409 |
+
<p class="insight-text" id="insight-text">Mengolah data analisisβ¦</p>
|
| 410 |
+
<p class="insight-meta" id="insight-meta"></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
</div>
|
| 412 |
|
| 413 |
+
<!-- FOOTER -->
|
| 414 |
+
<footer>
|
| 415 |
+
<div class="ft-kw">Kata kunci: <strong id="ft-kw">β</strong></div>
|
| 416 |
+
<div class="ft-r">
|
| 417 |
+
<a href="/">β Analisis Baru</a>
|
| 418 |
+
<a href="/download">β Unduh CSV</a>
|
| 419 |
+
</div>
|
| 420 |
+
</footer>
|
| 421 |
|
|
|
|
| 422 |
<script>
|
| 423 |
+
// ββ CHART.JS DEFAULTS ββ
|
| 424 |
+
Chart.defaults.color = '#4a5568';
|
| 425 |
+
Chart.defaults.borderColor = 'rgba(255,255,255,0.05)';
|
| 426 |
+
Chart.defaults.font.family = "'JetBrains Mono', monospace";
|
| 427 |
+
|
| 428 |
+
// ββ HELPERS ββ
|
| 429 |
+
const pct = (n, t) => Math.round((n / (t || 1)) * 100);
|
| 430 |
+
const esc = s => String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
| 431 |
+
const sc = s => { if(!s)return'u'; const l=s.toLowerCase(); return l.includes('pos')?'p':l.includes('neg')?'n':'u'; };
|
| 432 |
+
const $ = id => document.getElementById(id);
|
| 433 |
+
|
| 434 |
+
// ββ LOAD DATA ββ
|
| 435 |
+
let D = null;
|
| 436 |
+
try { const r = sessionStorage.getItem('analysisResult'); if(r) D = JSON.parse(r); } catch(e){}
|
| 437 |
+
if(!D && typeof window.__DATA__!=='undefined') D = window.__DATA__;
|
| 438 |
+
if(D) render(D);
|
| 439 |
+
else $('insight-text').textContent = 'Tidak ada data. Kembali ke halaman utama untuk memulai analisis baru.';
|
| 440 |
+
|
| 441 |
+
function render(d) {
|
| 442 |
+
const data = d.data || [];
|
| 443 |
+
const words = d.top_words || [];
|
| 444 |
+
const topics= d.topics || [];
|
| 445 |
+
const clust = d.clusters || [];
|
| 446 |
+
const hoax = d.hoax || [];
|
| 447 |
+
const botN = d.bot_network || { nodes:[], edges:[], bots:[] };
|
| 448 |
+
const trend = d.trend || 'β';
|
| 449 |
+
const kw = d.keyword || 'β';
|
| 450 |
+
const src = d.source || 'all';
|
| 451 |
+
|
| 452 |
+
const pos = data.filter(x=>x.sentiment==='Positive').length;
|
| 453 |
+
const neg = data.filter(x=>x.sentiment==='Negative').length;
|
| 454 |
+
const neu = data.filter(x=>x.sentiment==='Neutral').length;
|
| 455 |
+
const tot = data.length || 1;
|
| 456 |
+
|
| 457 |
+
// HEADER
|
| 458 |
+
$('h-kw').textContent = kw;
|
| 459 |
+
$('h-total').textContent = data.length;
|
| 460 |
+
$('h-src').textContent = src==='all'?'YT+Reddit':src;
|
| 461 |
+
$('ft-kw').textContent = kw;
|
| 462 |
+
|
| 463 |
+
// METRICS
|
| 464 |
+
$('m-total').textContent = data.length;
|
| 465 |
+
$('m-pos').textContent = pos;
|
| 466 |
+
$('m-neg').textContent = neg;
|
| 467 |
+
$('m-neu').textContent = neu;
|
| 468 |
+
$('m-pos-p').textContent = pct(pos,tot)+'%';
|
| 469 |
+
$('m-neg-p').textContent = pct(neg,tot)+'%';
|
| 470 |
+
$('m-neu-p').textContent = pct(neu,tot)+'%';
|
| 471 |
+
|
| 472 |
+
const tEl = $('m-trend');
|
| 473 |
+
tEl.textContent = trend;
|
| 474 |
+
const isUp = trend && trend.toLowerCase().includes('positif');
|
| 475 |
+
tEl.className = 'metric-n ' + (isUp ? 'c-up' : 'c-dn');
|
| 476 |
+
|
| 477 |
+
// DIST BAR
|
| 478 |
+
setTimeout(() => {
|
| 479 |
+
$('ds-p').style.width = pct(pos,tot)+'%';
|
| 480 |
+
$('ds-n').style.width = pct(neg,tot)+'%';
|
| 481 |
+
$('ds-u').style.width = pct(neu,tot)+'%';
|
| 482 |
+
}, 100);
|
| 483 |
+
$('dist-info').textContent = `${pct(pos,tot)}% Pos Β· ${pct(neg,tot)}% Neg Β· ${pct(neu,tot)}% Neu`;
|
| 484 |
+
|
| 485 |
+
// PIE
|
| 486 |
+
new Chart($('c-pie').getContext('2d'), {
|
| 487 |
+
type: 'doughnut',
|
| 488 |
data: {
|
| 489 |
+
labels: ['Positif','Negatif','Netral'],
|
| 490 |
+
datasets: [{
|
| 491 |
+
data: [pos, neg, neu],
|
| 492 |
+
backgroundColor: ['rgba(34,197,94,0.85)','rgba(239,68,68,0.85)','rgba(148,163,184,0.85)'],
|
| 493 |
+
borderColor: ['rgba(34,197,94,0.2)','rgba(239,68,68,0.2)','rgba(148,163,184,0.2)'],
|
| 494 |
+
borderWidth: 1, hoverOffset: 6
|
| 495 |
+
}]
|
| 496 |
+
},
|
| 497 |
+
options: {
|
| 498 |
+
cutout: '68%', responsive: true, maintainAspectRatio: false,
|
| 499 |
+
plugins: {
|
| 500 |
+
legend: { position:'right', labels:{ font:{size:11}, color:'#8892a4', padding:16, usePointStyle:true, pointStyleWidth:8 } },
|
| 501 |
+
tooltip: { callbacks: { label: ctx => ` ${ctx.label}: ${ctx.parsed} (${pct(ctx.parsed,tot)}%)` } }
|
| 502 |
+
}
|
| 503 |
}
|
| 504 |
+
});
|
| 505 |
|
| 506 |
+
// LINE
|
| 507 |
+
const roll = (arr, n=5) => arr.map((_,i)=>{ const sl=arr.slice(Math.max(0,i-n),i+1); return sl.reduce((a,b)=>a+b,0)/sl.length; });
|
| 508 |
+
const pl = data.map(x=>x.sentiment==='Positive'?1:0);
|
| 509 |
+
const nl = data.map(x=>x.sentiment==='Negative'?1:0);
|
| 510 |
+
const ul = data.map(x=>x.sentiment==='Neutral'?1:0);
|
| 511 |
|
| 512 |
+
new Chart($('c-line').getContext('2d'), {
|
| 513 |
+
type: 'line',
|
| 514 |
data: {
|
| 515 |
+
labels: data.map((_,i)=>i+1),
|
| 516 |
+
datasets: [
|
| 517 |
+
{ label:'Positif', data:roll(pl), borderColor:'rgba(34,197,94,0.8)', backgroundColor:'rgba(34,197,94,0.06)', borderWidth:1.5, tension:0.4, pointRadius:0, fill:true },
|
| 518 |
+
{ label:'Negatif', data:roll(nl), borderColor:'rgba(239,68,68,0.8)', backgroundColor:'rgba(239,68,68,0.06)', borderWidth:1.5, tension:0.4, pointRadius:0, fill:true },
|
| 519 |
+
{ label:'Netral', data:roll(ul), borderColor:'rgba(148,163,184,0.5)',backgroundColor:'rgba(148,163,184,0.03)',borderWidth:1, tension:0.4, pointRadius:0, fill:true },
|
| 520 |
+
]
|
| 521 |
+
},
|
| 522 |
+
options: {
|
| 523 |
+
responsive: true, maintainAspectRatio: false,
|
| 524 |
+
interaction: { mode:'index', intersect:false },
|
| 525 |
+
scales: {
|
| 526 |
+
x: { ticks:{ maxTicksLimit:12, font:{size:9} }, grid:{ color:'rgba(255,255,255,0.04)' } },
|
| 527 |
+
y: { ticks:{ font:{size:9} }, grid:{ color:'rgba(255,255,255,0.04)' } }
|
| 528 |
+
},
|
| 529 |
+
plugins: { legend:{ labels:{ font:{size:10}, color:'#8892a4', usePointStyle:true } } }
|
| 530 |
}
|
| 531 |
+
});
|
| 532 |
+
|
| 533 |
+
// WORDS
|
| 534 |
+
const wg = $('words');
|
| 535 |
+
if(words.length) {
|
| 536 |
+
const mx = words[0].count || 1;
|
| 537 |
+
wg.innerHTML = words.map(w=>{
|
| 538 |
+
const sz = 0.65 + (w.count/mx)*0.95;
|
| 539 |
+
const op = 0.45 + (w.count/mx)*0.55;
|
| 540 |
+
return `<span class="w-chip" style="font-size:${sz}rem;opacity:${op}" title="${w.count}x">${w.word}</span>`;
|
| 541 |
+
}).join('');
|
| 542 |
+
} else wg.innerHTML = '<span class="empty">Tidak ada data kata.</span>';
|
| 543 |
+
|
| 544 |
+
// TABLE
|
| 545 |
+
const tb = $('tbl');
|
| 546 |
+
if(data.length) {
|
| 547 |
+
tb.innerHTML = data.slice(0,50).map((r,i)=>`
|
| 548 |
+
<tr>
|
| 549 |
+
<td style="color:var(--muted)">${i+1}</td>
|
| 550 |
+
<td><span class="src-badge">${esc(r.source||'β')}</span></td>
|
| 551 |
+
<td style="max-width:280px;color:var(--m2)">${esc((r.text||'').substring(0,100))}</td>
|
| 552 |
+
<td><span class="tag tag-${sc(r.sentiment)}">${r.sentiment||'β'}</span></td>
|
| 553 |
+
</tr>`).join('');
|
| 554 |
+
} else tb.innerHTML = '<tr><td colspan="4" class="empty" style="padding:.75rem">Tidak ada data.</td></tr>';
|
| 555 |
+
|
| 556 |
+
// TOPICS
|
| 557 |
+
const tg = $('topics');
|
| 558 |
+
if(topics.length) {
|
| 559 |
+
tg.innerHTML = topics.map((t,i)=>`
|
| 560 |
+
<div class="topic-card">
|
| 561 |
+
<div class="topic-card-h"><span class="topic-num">Topik ${i+1}</span><span class="topic-dot"></span></div>
|
| 562 |
+
<div class="topic-words">${(Array.isArray(t)?t:[]).map(w=>`<span class="tw">${w}</span>`).join('')}</div>
|
| 563 |
+
</div>`).join('');
|
| 564 |
+
} else tg.innerHTML = '<span class="empty">Data tidak cukup untuk topic modeling.</span>';
|
| 565 |
+
|
| 566 |
+
// CLUSTERS
|
| 567 |
+
const cg = $('clusters');
|
| 568 |
+
if(clust.length) {
|
| 569 |
+
cg.innerHTML = clust.map(c=>`
|
| 570 |
+
<div class="cl-item">
|
| 571 |
+
<div class="cl-head">Kluster ${c.cluster}</div>
|
| 572 |
+
${(c.samples||[]).slice(0,2).map(s=>`<div class="cl-text">"${esc(s.substring(0,85))}β¦"</div>`).join('')}
|
| 573 |
+
</div>`).join('');
|
| 574 |
+
} else cg.innerHTML = '<span class="empty">Data tidak cukup untuk clustering.</span>';
|
| 575 |
+
|
| 576 |
+
// HOAX
|
| 577 |
+
const hg = $('hoax');
|
| 578 |
+
if(hoax.length) {
|
| 579 |
+
hg.innerHTML = hoax.map(h=>`
|
| 580 |
+
<div class="hoax-item">
|
| 581 |
+
<span class="h-badge ${h.label==='Hoax'?'hb-hoax':'hb-norm'}">${h.label}</span>
|
| 582 |
+
<span class="h-text">${esc((h.text||'').substring(0,90))}</span>
|
| 583 |
+
</div>`).join('');
|
| 584 |
+
} else hg.innerHTML = '<span class="empty">Tidak ada konten terdeteksi.</span>';
|
| 585 |
+
|
| 586 |
+
// BOT
|
| 587 |
+
const bg = $('bots');
|
| 588 |
+
const bots = (botN.bots||[]);
|
| 589 |
+
if(bots.length) {
|
| 590 |
+
bg.innerHTML = bots.slice(0,8).map(b=>`
|
| 591 |
+
<div class="bot-item">
|
| 592 |
+
<span class="bot-id">Node ${b.node}</span>
|
| 593 |
+
<div class="bot-bar"><div class="bot-fill" style="width:${b.score*100}%"></div></div>
|
| 594 |
+
<span class="bot-score">${b.score}</span>
|
| 595 |
+
</div>`).join('');
|
| 596 |
+
} else bg.innerHTML = '<span class="empty">Tidak ada bot terdeteksi.</span>';
|
| 597 |
+
|
| 598 |
+
// INSIGHT
|
| 599 |
+
const dom = pos>=neg&&pos>=neu?'positif':neg>=pos&&neg>=neu?'negatif':'netral';
|
| 600 |
+
$('insight-text').innerHTML = `Dari <em>${data.length}</em> komentar, opini publik terhadap "<em>${esc(kw)}</em>" cenderung <em>${dom}</em> β ${pct(pos,tot)}% positif, ${pct(neg,tot)}% negatif, ${pct(neu,tot)}% netral.`;
|
| 601 |
+
$('insight-meta').innerHTML = `Model: IndoBERT (w11wo/indonesian-roberta-base)<br>${new Date().toLocaleDateString('id-ID',{weekday:'long',year:'numeric',month:'long',day:'numeric'})}<br>Tren: ${trend}`;
|
| 602 |
+
|
| 603 |
+
// IMAGES
|
| 604 |
+
loadImg('static/wordcloud.png','wc-box');
|
| 605 |
+
loadImg('static/heatmap.png','hm-box');
|
| 606 |
+
}
|
| 607 |
|
| 608 |
+
function loadImg(src, boxId) {
|
| 609 |
+
const img = new Image();
|
| 610 |
+
img.onload = () => {
|
| 611 |
+
img.style.width='100%'; img.style.borderRadius='6px';
|
| 612 |
+
$(boxId).innerHTML = ''; $(boxId).appendChild(img);
|
| 613 |
+
};
|
| 614 |
+
img.src = '/' + src + '?t=' + Date.now();
|
| 615 |
+
}
|
| 616 |
</script>
|
|
|
|
| 617 |
</body>
|
| 618 |
</html>
|