ALAMDIENG commited on
Commit
dbbf1c8
·
1 Parent(s): 4233f05

feat(ui): refine UI/UX, replace raw emojis with CSS vector drawings and clean HTML entities

Browse files
Files changed (3) hide show
  1. frontend/app.js +3 -3
  2. frontend/index.html +44 -22
  3. frontend/style.css +183 -21
frontend/app.js CHANGED
@@ -401,10 +401,10 @@ async function fetchLiveWeather(loc) {
401
 
402
  if (weatherAlert) {
403
  if (precipToday > 30) {
404
- weatherAlert.textContent = "HEAVY RAIN 🟡";
405
  weatherAlert.className = "highlight text-warning";
406
  } else if (precipToday > 50) {
407
- weatherAlert.textContent = "FLOOD DANGER 🔴";
408
  weatherAlert.className = "highlight text-red";
409
  } else {
410
  weatherAlert.textContent = "Normal conditions";
@@ -524,7 +524,7 @@ function updateDashboardData(data, confScore, message) {
524
 
525
  const eventDay = results.find(r => r.event_info !== null);
526
  if (eventDay) {
527
- if (eventDescText) eventDescText.innerHTML = `⚠️ <strong>${eventDay.event_info}</strong> on ${eventDay.date}. Heavy crowd expected near site.`;
528
  const eBox = document.getElementById("event-box");
529
  if (eBox) eBox.style.borderColor = "var(--red)";
530
  } else {
 
401
 
402
  if (weatherAlert) {
403
  if (precipToday > 30) {
404
+ weatherAlert.textContent = "HEAVY RAIN";
405
  weatherAlert.className = "highlight text-warning";
406
  } else if (precipToday > 50) {
407
+ weatherAlert.textContent = "FLOOD RISK ALERT";
408
  weatherAlert.className = "highlight text-red";
409
  } else {
410
  weatherAlert.textContent = "Normal conditions";
 
524
 
525
  const eventDay = results.find(r => r.event_info !== null);
526
  if (eventDay) {
527
+ if (eventDescText) eventDescText.innerHTML = `<strong>${eventDay.event_info}</strong> on ${eventDay.date}. Heavy crowd expected near site.`;
528
  const eBox = document.getElementById("event-box");
529
  if (eBox) eBox.style.borderColor = "var(--red)";
530
  } else {
frontend/index.html CHANGED
@@ -166,11 +166,25 @@
166
  </div>
167
  </div>
168
 
169
- <!-- Eco-Joy Interactive CSS Animation -->
170
  <div class="eco-animation-container">
171
  <div class="monitoring-center">
172
- <div class="monitoring-person" title="Faril Putra Pratama (Monitoring)">🧑‍💻</div>
173
- <div class="monitoring-screen">📊 Aeterna AI: active</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  <div class="monitoring-waves">
175
  <span class="wave"></span>
176
  <span class="wave"></span>
@@ -178,8 +192,16 @@
178
  </div>
179
 
180
  <div class="animation-road">
181
- <div class="eco-truck">🚛</div>
182
- <div class="trash-bin">🗑️</div>
 
 
 
 
 
 
 
 
183
  </div>
184
  </div>
185
  </div>
@@ -221,23 +243,23 @@
221
  </p>
222
  <div class="sources-grid" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap:1.5rem;">
223
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
224
- <span style="font-size: 1.8rem; display:block; margin-bottom: 8px;">🏛️</span>
225
- <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800;">1. SIPSN & DLH DKI Jakarta</h4>
226
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data baseline disesuaikan dengan volume total **9.059 Ton/hari** dari Dinas Lingkungan Hidup DKI Jakarta tahun 2026.</p>
227
  </div>
228
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
229
- <span style="font-size: 1.8rem; display:block; margin-bottom: 8px;">🌧️</span>
230
- <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800;">2. BMKG & Open-Meteo API</h4>
231
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data curah hujan ditarik live menggunakan koordinat geografis presisi masing-masing kecamatan (resolusi 11 km).</p>
232
  </div>
233
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
234
- <span style="font-size: 1.8rem; display:block; margin-bottom: 8px;">👥</span>
235
- <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800;">3. Proyeksi BPS & MRT Jakarta</h4>
236
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Simulasi volume mobilitas disesuaikan dengan populasi registrasi BPS dan ridership transit harian.</p>
237
  </div>
238
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
239
- <span style="font-size: 1.8rem; display:block; margin-bottom: 8px;">🛣️</span>
240
- <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800;">4. Haversine & Winding Routing</h4>
241
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Jarak logistik dihitung dari koordinat pusat kecamatan ke TPST Bantargebang dengan faktor winding jalan raya sebesar 1.35x.</p>
242
  </div>
243
  </div>
@@ -248,25 +270,25 @@
248
  <h3 class="panel-subtitle" style="margin-bottom: 1.5rem;">ALIRAN PENGOLAHAN DATA (DATA PIPELINE FLOW)</h3>
249
  <div class="pipeline-flow-container">
250
  <div class="flow-step">
251
- <div class="flow-icon">📥 Ingest</div>
252
  <div class="flow-title">Data Ingestion</div>
253
  <div class="flow-desc">Ditarik dari API Cuaca, BPS Jumlah Jiwa, & Kalender Acara Jakarta</div>
254
  </div>
255
- <div class="flow-arrow">➡️</div>
256
  <div class="flow-step">
257
- <div class="flow-icon">⚙️ Process</div>
258
  <div class="flow-title">Feature Engineering</div>
259
  <div class="flow-desc">Ekstraksi Lag Hujan, Is_Weekend, Is_Mudik, & Skala Crowd Event</div>
260
  </div>
261
- <div class="flow-arrow">➡️</div>
262
  <div class="flow-step">
263
- <div class="flow-icon">🧠 Predict</div>
264
  <div class="flow-title">Stacking Inference</div>
265
  <div class="flow-desc">Evaluasi prediksi spasial ensembles harian 44 Kecamatan</div>
266
  </div>
267
- <div class="flow-arrow">➡️</div>
268
  <div class="flow-step">
269
- <div class="flow-icon">🚛 Dispatch</div>
270
  <div class="flow-title">Logistics Planning</div>
271
  <div class="flow-desc">Hasil tonase & rekomendasi armada truk dikirim ke dashboard</div>
272
  </div>
@@ -294,7 +316,7 @@
294
  </div>
295
  </div>
296
 
297
- <div class="diagram-connector">⬇️</div>
298
 
299
  <div class="diagram-layer base-layer">
300
  <span class="layer-title">Base Estimators (Ensemble)</span>
@@ -314,7 +336,7 @@
314
  </div>
315
  </div>
316
 
317
- <div class="diagram-connector">⬇️</div>
318
 
319
  <div class="diagram-layer meta-layer">
320
  <span class="layer-title">Meta-Estimator</span>
 
166
  </div>
167
  </div>
168
 
169
+ <!-- Eco-Joy Interactive CSS Animation (Custom Shapes) -->
170
  <div class="eco-animation-container">
171
  <div class="monitoring-center">
172
+ <div class="monitoring-avatar" title="Faril Putra Pratama (Monitoring)">
173
+ <div class="avatar-head"></div>
174
+ <div class="avatar-body"></div>
175
+ </div>
176
+ <div class="monitoring-screen-visual">
177
+ <div class="screen-header">
178
+ <span class="dot red-dot"></span>
179
+ <span class="dot green-dot"></span>
180
+ </div>
181
+ <div class="screen-chart">
182
+ <div class="chart-bar bar-1"></div>
183
+ <div class="chart-bar bar-2"></div>
184
+ <div class="chart-bar bar-3"></div>
185
+ </div>
186
+ <span class="screen-label">AETERNA AI</span>
187
+ </div>
188
  <div class="monitoring-waves">
189
  <span class="wave"></span>
190
  <span class="wave"></span>
 
192
  </div>
193
 
194
  <div class="animation-road">
195
+ <div class="vector-truck">
196
+ <div class="truck-cabin"></div>
197
+ <div class="truck-box"></div>
198
+ <div class="truck-wheel wheel-1"></div>
199
+ <div class="truck-wheel wheel-2"></div>
200
+ </div>
201
+ <div class="vector-bin">
202
+ <div class="bin-lid"></div>
203
+ <div class="bin-body"></div>
204
+ </div>
205
  </div>
206
  </div>
207
  </div>
 
243
  </p>
244
  <div class="sources-grid" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap:1.5rem;">
245
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
246
+ <span class="source-num">01</span>
247
+ <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800; margin-top: 10px;">SIPSN & DLH DKI Jakarta</h4>
248
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data baseline disesuaikan dengan volume total **9.059 Ton/hari** dari Dinas Lingkungan Hidup DKI Jakarta tahun 2026.</p>
249
  </div>
250
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
251
+ <span class="source-num">02</span>
252
+ <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800; margin-top: 10px;">BMKG & Open-Meteo API</h4>
253
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data curah hujan ditarik live menggunakan koordinat geografis presisi masing-masing kecamatan (resolusi 11 km).</p>
254
  </div>
255
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
256
+ <span class="source-num">03</span>
257
+ <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800; margin-top: 10px;">Proyeksi BPS & MRT Jakarta</h4>
258
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Simulasi volume mobilitas disesuaikan dengan populasi registrasi BPS dan ridership transit harian.</p>
259
  </div>
260
  <div class="source-item panel" style="background:var(--soft-surface); border: 2px solid var(--border-color); padding: 1.2rem; border-radius: 12px;">
261
+ <span class="source-num">04</span>
262
+ <h4 style="color:var(--primary); margin-bottom:0.5rem; font-family:var(--font-display); font-weight: 800; margin-top: 10px;">Haversine & Winding Routing</h4>
263
  <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Jarak logistik dihitung dari koordinat pusat kecamatan ke TPST Bantargebang dengan faktor winding jalan raya sebesar 1.35x.</p>
264
  </div>
265
  </div>
 
270
  <h3 class="panel-subtitle" style="margin-bottom: 1.5rem;">ALIRAN PENGOLAHAN DATA (DATA PIPELINE FLOW)</h3>
271
  <div class="pipeline-flow-container">
272
  <div class="flow-step">
273
+ <div class="flow-icon-text">Ingest</div>
274
  <div class="flow-title">Data Ingestion</div>
275
  <div class="flow-desc">Ditarik dari API Cuaca, BPS Jumlah Jiwa, & Kalender Acara Jakarta</div>
276
  </div>
277
+ <div class="flow-arrow">&rarr;</div>
278
  <div class="flow-step">
279
+ <div class="flow-icon-text">Process</div>
280
  <div class="flow-title">Feature Engineering</div>
281
  <div class="flow-desc">Ekstraksi Lag Hujan, Is_Weekend, Is_Mudik, & Skala Crowd Event</div>
282
  </div>
283
+ <div class="flow-arrow">&rarr;</div>
284
  <div class="flow-step">
285
+ <div class="flow-icon-text">Predict</div>
286
  <div class="flow-title">Stacking Inference</div>
287
  <div class="flow-desc">Evaluasi prediksi spasial ensembles harian 44 Kecamatan</div>
288
  </div>
289
+ <div class="flow-arrow">&rarr;</div>
290
  <div class="flow-step">
291
+ <div class="flow-icon-text">Dispatch</div>
292
  <div class="flow-title">Logistics Planning</div>
293
  <div class="flow-desc">Hasil tonase & rekomendasi armada truk dikirim ke dashboard</div>
294
  </div>
 
316
  </div>
317
  </div>
318
 
319
+ <div class="diagram-connector">&darr;</div>
320
 
321
  <div class="diagram-layer base-layer">
322
  <span class="layer-title">Base Estimators (Ensemble)</span>
 
336
  </div>
337
  </div>
338
 
339
+ <div class="diagram-connector">&darr;</div>
340
 
341
  <div class="diagram-layer meta-layer">
342
  <span class="layer-title">Meta-Estimator</span>
frontend/style.css CHANGED
@@ -1429,26 +1429,93 @@ footer {
1429
  position: relative;
1430
  }
1431
 
1432
- .monitoring-person {
1433
- font-size: 2rem;
 
 
1434
  animation: person-nod 1.5s ease-in-out infinite alternate;
1435
  }
1436
 
1437
- .monitoring-screen {
1438
- background: var(--primary);
1439
- color: #ffffff;
1440
- font-family: var(--font-mono);
1441
- font-size: 10px;
1442
- font-weight: bold;
1443
- padding: 4px 8px;
1444
- border-radius: 6px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1445
  border: 2px solid var(--border-color);
 
 
 
 
 
1446
  animation: screen-glow 2s infinite alternate;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1447
  }
1448
 
1449
  .monitoring-waves {
1450
  position: absolute;
1451
- left: 80px;
1452
  top: 50%;
1453
  transform: translateY(-50%);
1454
  display: flex;
@@ -1470,31 +1537,126 @@ footer {
1470
 
1471
  .animation-road {
1472
  width: 100%;
1473
- height: 36px;
1474
  background: #e8efec;
1475
- border-radius: 18px;
1476
  position: relative;
1477
- border-bottom: 3px solid var(--border-color);
1478
- overflow: hidden;
1479
  }
1480
 
1481
- .eco-truck {
1482
- font-size: 1.6rem;
 
 
1483
  position: absolute;
1484
- bottom: 2px;
1485
  left: -10%;
1486
  animation: drive-truck 8s linear infinite;
1487
- transform: scaleX(-1);
1488
  z-index: 2;
1489
  }
1490
 
1491
- .trash-bin {
1492
- font-size: 1.4rem;
1493
  position: absolute;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1494
  bottom: 4px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1495
  right: 15%;
1496
  animation: bin-wiggle 4s ease-in-out infinite;
1497
  z-index: 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1498
  }
1499
 
1500
  @keyframes person-nod {
 
1429
  position: relative;
1430
  }
1431
 
1432
+ .monitoring-avatar {
1433
+ width: 32px;
1434
+ height: 32px;
1435
+ position: relative;
1436
  animation: person-nod 1.5s ease-in-out infinite alternate;
1437
  }
1438
 
1439
+ .avatar-head {
1440
+ width: 14px;
1441
+ height: 14px;
1442
+ background: #006b5f;
1443
+ border-radius: 50%;
1444
+ margin: 0 auto;
1445
+ border: 2px solid #ffffff;
1446
+ }
1447
+
1448
+ .avatar-body {
1449
+ width: 24px;
1450
+ height: 14px;
1451
+ background: #735c00;
1452
+ border-radius: 12px 12px 0 0;
1453
+ margin: 2px auto 0 auto;
1454
+ border: 2px solid #ffffff;
1455
+ }
1456
+
1457
+ .monitoring-screen-visual {
1458
+ background: #ffffff;
1459
+ width: 120px;
1460
+ height: 56px;
1461
  border: 2px solid var(--border-color);
1462
+ border-radius: 8px;
1463
+ padding: 4px;
1464
+ display: flex;
1465
+ flex-direction: column;
1466
+ justify-content: space-between;
1467
  animation: screen-glow 2s infinite alternate;
1468
+ position: relative;
1469
+ }
1470
+
1471
+ .screen-header {
1472
+ display: flex;
1473
+ gap: 4px;
1474
+ }
1475
+
1476
+ .screen-header .dot {
1477
+ width: 4px;
1478
+ height: 4px;
1479
+ border-radius: 50%;
1480
+ }
1481
+
1482
+ .red-dot { background-color: var(--red); }
1483
+ .green-dot { background-color: var(--green); }
1484
+
1485
+ .screen-chart {
1486
+ display: flex;
1487
+ align-items: flex-end;
1488
+ gap: 4px;
1489
+ height: 24px;
1490
+ margin-top: 2px;
1491
+ }
1492
+
1493
+ .chart-bar {
1494
+ width: 8px;
1495
+ border-radius: 2px;
1496
+ background-color: var(--primary);
1497
+ }
1498
+
1499
+ .bar-1 { height: 12px; animation: bar-rise-1 1.2s infinite alternate; }
1500
+ .bar-2 { height: 20px; animation: bar-rise-2 1.2s infinite alternate 0.3s; }
1501
+ .bar-3 { height: 16px; animation: bar-rise-3 1.2s infinite alternate 0.6s; }
1502
+
1503
+ @keyframes bar-rise-1 { from { height: 6px; } to { height: 16px; } }
1504
+ @keyframes bar-rise-2 { from { height: 10px; } to { height: 24px; } }
1505
+ @keyframes bar-rise-3 { from { height: 8px; } to { height: 20px; } }
1506
+
1507
+ .screen-label {
1508
+ font-family: var(--font-mono);
1509
+ font-size: 7px;
1510
+ font-weight: bold;
1511
+ color: var(--text-muted);
1512
+ text-align: center;
1513
+ display: block;
1514
  }
1515
 
1516
  .monitoring-waves {
1517
  position: absolute;
1518
+ left: 170px;
1519
  top: 50%;
1520
  transform: translateY(-50%);
1521
  display: flex;
 
1537
 
1538
  .animation-road {
1539
  width: 100%;
1540
+ height: 40px;
1541
  background: #e8efec;
1542
+ border-radius: 20px;
1543
  position: relative;
1544
+ border-bottom: 4px solid var(--border-color);
 
1545
  }
1546
 
1547
+ /* Vector Truck Styling */
1548
+ .vector-truck {
1549
+ width: 48px;
1550
+ height: 24px;
1551
  position: absolute;
1552
+ bottom: 8px;
1553
  left: -10%;
1554
  animation: drive-truck 8s linear infinite;
 
1555
  z-index: 2;
1556
  }
1557
 
1558
+ .truck-cabin {
 
1559
  position: absolute;
1560
+ width: 14px;
1561
+ height: 16px;
1562
+ background-color: var(--secondary-container);
1563
+ border: 2px solid #ffffff;
1564
+ border-radius: 4px 6px 2px 2px;
1565
+ right: 0;
1566
+ bottom: 4px;
1567
+ }
1568
+
1569
+ .truck-box {
1570
+ position: absolute;
1571
+ width: 32px;
1572
+ height: 20px;
1573
+ background-color: var(--primary);
1574
+ border: 2px solid #ffffff;
1575
+ border-radius: 4px;
1576
+ left: 0;
1577
  bottom: 4px;
1578
+ }
1579
+
1580
+ .truck-wheel {
1581
+ position: absolute;
1582
+ width: 8px;
1583
+ height: 8px;
1584
+ background-color: #161d1b;
1585
+ border: 1.5px solid #ffffff;
1586
+ border-radius: 50%;
1587
+ bottom: 0;
1588
+ }
1589
+
1590
+ .wheel-1 { left: 6px; }
1591
+ .wheel-2 { right: 8px; }
1592
+
1593
+ /* Vector Trash Bin Styling */
1594
+ .vector-bin {
1595
+ width: 22px;
1596
+ height: 28px;
1597
+ position: absolute;
1598
+ bottom: 8px;
1599
  right: 15%;
1600
  animation: bin-wiggle 4s ease-in-out infinite;
1601
  z-index: 1;
1602
+ display: flex;
1603
+ flex-direction: column;
1604
+ }
1605
+
1606
+ .bin-lid {
1607
+ width: 26px;
1608
+ height: 5px;
1609
+ background-color: #6b7a76;
1610
+ border: 1.5px solid #ffffff;
1611
+ border-radius: 3px;
1612
+ margin-left: -2px;
1613
+ }
1614
+
1615
+ .bin-body {
1616
+ flex: 1;
1617
+ background-color: #bacac5;
1618
+ border: 2.5px solid #ffffff;
1619
+ border-radius: 0 0 6px 6px;
1620
+ position: relative;
1621
+ }
1622
+
1623
+ .bin-body::before {
1624
+ content: '';
1625
+ position: absolute;
1626
+ left: 4px;
1627
+ top: 2px;
1628
+ bottom: 2px;
1629
+ right: 4px;
1630
+ background-image: repeating-linear-gradient(90deg, #ffffff, #ffffff 1px, transparent 1px, transparent 4px);
1631
+ }
1632
+
1633
+ /* Source number tag */
1634
+ .source-num {
1635
+ display: inline-flex;
1636
+ align-items: center;
1637
+ justify-content: center;
1638
+ width: 32px;
1639
+ height: 32px;
1640
+ background-color: var(--primary);
1641
+ color: #ffffff;
1642
+ font-family: var(--font-display);
1643
+ font-size: 0.95rem;
1644
+ font-weight: 800;
1645
+ border-radius: 50%;
1646
+ box-shadow: 0 4px 8px rgba(0, 107, 95, 0.15);
1647
+ }
1648
+
1649
+ .flow-icon-text {
1650
+ font-family: var(--font-display);
1651
+ font-size: 0.75rem;
1652
+ font-weight: 800;
1653
+ color: var(--primary);
1654
+ background-color: var(--surface-container-low);
1655
+ border: 2px solid var(--border-color);
1656
+ padding: 2px 10px;
1657
+ border-radius: 12px;
1658
+ margin-bottom: 8px;
1659
+ text-transform: uppercase;
1660
  }
1661
 
1662
  @keyframes person-nod {