263 lines
9.8 KiB
HTML
263 lines
9.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Dashboard — Smart Donation Box</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
|
<link rel="stylesheet" href="style.css">
|
|
<style>
|
|
.foto-section {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 20px;
|
|
margin-top: 24px;
|
|
}
|
|
.foto-card {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
.foto-card-header {
|
|
padding: 16px 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
.foto-card-header h3 {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.pulse-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
background: var(--primary-green);
|
|
border-radius: 50%;
|
|
animation: pulse 2s infinite;
|
|
display: inline-block;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
50% { opacity: 0.4; transform: scale(1.3); }
|
|
}
|
|
.foto-container {
|
|
position: relative;
|
|
width: 100%;
|
|
aspect-ratio: 4/3;
|
|
background: #0a0f1a;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
}
|
|
.foto-container img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
display: block;
|
|
transition: opacity 0.4s ease;
|
|
}
|
|
.foto-placeholder {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 12px;
|
|
color: var(--text-muted);
|
|
}
|
|
.foto-placeholder i { font-size: 40px; opacity: 0.3; }
|
|
.foto-placeholder p { font-size: 13px; }
|
|
.foto-footer {
|
|
padding: 12px 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.foto-label {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--primary-green);
|
|
background: var(--primary-green-light);
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
}
|
|
.foto-time {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
}
|
|
.summary-grid-dash {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 20px;
|
|
margin-bottom: 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<script type="module">
|
|
if (localStorage.getItem('isLoggedIn') !== 'true') {
|
|
window.location.href = 'login.html';
|
|
}
|
|
|
|
window.logout = function() {
|
|
if (confirm('Apakah Anda yakin ingin keluar?')) {
|
|
localStorage.removeItem('isLoggedIn');
|
|
window.location.href = 'login.html';
|
|
}
|
|
}
|
|
|
|
import { initLayout } from './layout.js';
|
|
import { db, formatRp, formatWaktu, isToday } from './firebase-config.js';
|
|
import { ref, onValue } from "https://www.gstatic.com/firebasejs/12.10.0/firebase-database.js";
|
|
|
|
initLayout('dashboard');
|
|
|
|
document.getElementById('content-area').innerHTML = `
|
|
<div class="summary-grid-dash">
|
|
<div class="card stat-card">
|
|
<div class="stat-icon bg-green-light"><i class="fa-regular fa-calendar-check"></i></div>
|
|
<div class="stat-info">
|
|
<p>Donasi Hari Ini</p>
|
|
<h2 id="dash-donasi-hari-ini">Rp 0</h2>
|
|
</div>
|
|
</div>
|
|
<div class="card stat-card">
|
|
<div class="stat-icon bg-blue-light"><i class="fa-solid fa-wallet"></i></div>
|
|
<div class="stat-info">
|
|
<p>Total Donasi Terkumpul</p>
|
|
<h2 id="dash-total-donasi">Rp 0</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="foto-section">
|
|
<div class="foto-card">
|
|
<div class="foto-card-header">
|
|
<h3>
|
|
<span class="pulse-dot"></span>
|
|
Foto Deteksi Terakhir
|
|
</h3>
|
|
<span id="foto-badge-status" style="font-size:12px; color:var(--text-muted);">Menunggu...</span>
|
|
</div>
|
|
<div class="foto-container">
|
|
<div class="foto-placeholder" id="foto-placeholder">
|
|
<i class="fa-solid fa-camera"></i>
|
|
<p>Belum ada deteksi</p>
|
|
</div>
|
|
<img id="foto-deteksi" src="" alt="Foto Deteksi"
|
|
style="display:none;"
|
|
onerror="this.style.display='none'; document.getElementById('foto-placeholder').style.display='flex';">
|
|
</div>
|
|
<div class="foto-footer">
|
|
<span class="foto-label" id="foto-label-hasil">—</span>
|
|
<span class="foto-time" id="foto-waktu">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>Transaksi Terakhir</h3>
|
|
<a href="log.html" style="font-size:13px; color:var(--primary-green); text-decoration:none; font-weight:500;">
|
|
Lihat Semua
|
|
</a>
|
|
</div>
|
|
<table class="styled-table" id="table-transaksi-terakhir">
|
|
<tr><td colspan="2" style="text-align:center; color:var(--text-muted); padding:32px;">
|
|
<i class="fa-solid fa-spinner fa-spin"></i> Memuat...
|
|
</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
onValue(ref(db, 'smart_donation_box/summary'), (snap) => {
|
|
const d = snap.val() || {};
|
|
document.getElementById('dash-total-donasi').innerText = formatRp(d.total_donasi);
|
|
});
|
|
|
|
onValue(ref(db, 'smart_donation_box/donasi_log'), (snap) => {
|
|
const data = snap.val() || {};
|
|
|
|
// Filter: buang nominal 0
|
|
const keys = Object.keys(data)
|
|
.reverse()
|
|
.filter(key => (data[key].nominal || 0) > 0);
|
|
|
|
let donasiHariIni = 0;
|
|
let html = '';
|
|
|
|
if (keys.length === 0) {
|
|
html = `<tr><td colspan="2" style="text-align:center; color:var(--text-muted); padding:32px;">
|
|
Belum ada transaksi
|
|
</td></tr>`;
|
|
} else {
|
|
keys.forEach((key, i) => {
|
|
const item = data[key];
|
|
if (isToday(item.timestamp)) donasiHariIni += item.nominal;
|
|
|
|
if (i < 5) {
|
|
html += `
|
|
<tr>
|
|
<td>
|
|
<div style="display:flex; align-items:center; gap:12px;">
|
|
<div style="background:var(--primary-green-light);
|
|
color:var(--primary-green);
|
|
width:32px; height:32px; border-radius:50%;
|
|
display:flex; align-items:center; justify-content:center;
|
|
flex-shrink:0;">
|
|
<i class="fa-solid fa-arrow-down" style="font-size:12px;"></i>
|
|
</div>
|
|
<div>
|
|
<div style="font-weight:600; font-size:13px;">Donasi Masuk</div>
|
|
<div style="font-size:11px; color:var(--text-muted);">${formatWaktu(item.timestamp)}</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td style="font-weight:600; text-align:right;">${formatRp(item.nominal)}</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
});
|
|
}
|
|
|
|
document.getElementById('dash-donasi-hari-ini').innerText = formatRp(donasiHariIni);
|
|
document.getElementById('table-transaksi-terakhir').innerHTML = html;
|
|
});
|
|
|
|
onValue(ref(db, 'smart_donation_box/last_detection'), (snap) => {
|
|
const d = snap.val();
|
|
if (!d || !d.foto_url) return;
|
|
|
|
const img = document.getElementById('foto-deteksi');
|
|
const placeholder = document.getElementById('foto-placeholder');
|
|
const badge = document.getElementById('foto-badge-status');
|
|
const label = document.getElementById('foto-label-hasil');
|
|
const waktu = document.getElementById('foto-waktu');
|
|
|
|
badge.textContent = 'Update otomatis';
|
|
badge.style.color = 'var(--primary-green)';
|
|
label.textContent = d.label || '—';
|
|
waktu.textContent = formatWaktu(d.timestamp);
|
|
|
|
const isBase64 = d.foto_url.startsWith('data:image');
|
|
|
|
img.onload = () => {
|
|
img.style.display = 'block';
|
|
placeholder.style.display = 'none';
|
|
};
|
|
img.onerror = () => {
|
|
img.style.display = 'none';
|
|
placeholder.style.display = 'flex';
|
|
};
|
|
img.src = isBase64 ? d.foto_url : d.foto_url + '?t=' + d.timestamp;
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |