adding alert < 2025

This commit is contained in:
2025-11-24 08:32:53 +08:00
commit 94e3319c59
11 changed files with 1493 additions and 0 deletions

212
data/index.html Normal file
View File

@@ -0,0 +1,212 @@
<!-- data/index.html -->
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Bel Sekolah</title>
<style>
/* Inline Tailwind CSS for offline compatibility */
.bg-gradient-to-b { background-image: linear-gradient(to bottom, var(--tw-gradient-stops)); }
.from-slate-50 { --tw-gradient-from: #f8fafc; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(248, 250, 252, 0)); }
.to-white { --tw-gradient-to: #ffffff; }
.min-h-screen { min-height: 100vh; }
.font-sans { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; }
.text-slate-800 { --tw-text-opacity: 1; color: rgb(30 41 59 / var(--tw-text-opacity)); }
.max-w-2xl { max-width: 42rem; }
.mx-auto { margin-left: auto; margin-right: auto; }
.p-6 { padding: 1.5rem; }
.text-center { text-align: center; }
.mb-6 { margin-bottom: 1.5rem; }
.text-5xl { font-size: 3rem; line-height: 1; }
.text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
.font-bold { font-weight: 700; }
.text-blue-600 { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity)); }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-slate-600 { --tw-text-opacity: 1; color: rgb(71 85 105 / var(--tw-text-opacity)); }
.mt-1 { margin-top: 0.25rem; }
.bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); }
.shadow { --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-width 0px), var(--tw-ring-shadow), var(--tw-shadow); }
.rounded-xl { border-radius: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.mb-1 { margin-bottom: 0.25rem; }
.text-5xl { font-size: 3rem; line-height: 1; }
.font-extrabold { font-weight: 800; }
.tracking-wider { letter-spacing: 0.05em; }
.text-slate-500 { --tw-text-opacity: 1; color: rgb(100 116 139 / var(--tw-text-opacity)); }
.mt-2 { margin-top: 0.5rem; }
.flex { display: flex; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.gap-4 { gap: 1rem; }
.my-3 { margin-top: 0.75rem; margin-bottom: 0.75rem; }
.w-3 { width: 0.75rem; }
.h-3 { height: 0.75rem; }
.rounded-full { border-radius: 9999px; }
.bg-gray-300 { --tw-bg-opacity: 1; background-color: rgb(209 213 219 / var(--tw-bg-opacity)); }
.text-slate-700 { --tw-text-opacity: 1; color: rgb(51 65 85 / var(--tw-text-opacity)); }
.grid { display: grid; }
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.gap-3 { gap: 0.75rem; }
.mt-6 { margin-top: 1.5rem; }
.max-w-md { max-width: 28rem; }
.bg-slate-50 { --tw-bg-opacity: 1; background-color: rgb(248 250 252 / var(--tw-bg-opacity)); }
.p-3 { padding: 0.75rem; }
.rounded { border-radius: 0.25rem; }
.h-40 { height: 10rem; }
.overflow-auto { overflow: auto; }
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-slate-500 { --tw-text-opacity: 1; color: rgb(100 116 139 / var(--tw-text-opacity)); }
.mt-6 { margin-top: 1.5rem; }
/* Button styles */
.btn {
display: inline-block;
padding: 0.5rem 0.9rem;
border-radius: 8px;
color: white;
font-weight: 600;
text-decoration: none;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.btn.small { padding: 0.25rem 0.5rem; font-size: 0.9rem; }
.btn.btn-blue { background: #2563EB; }
.btn.btn-green { background: #059669; }
.btn.btn-red { background: #DC2626; }
.btn.btn-gray { background: #6B7280; }
.btn.btn-purple { background: #7C3AED; }
.btn:hover { filter: brightness(0.95); }
/* Responsive */
@media (max-width: 640px) {
.btn { padding: 0.45rem 0.7rem; font-size: 0.9rem; }
}
</style>
</head>
<body class="bg-gradient-to-b from-slate-50 to-white min-h-screen font-sans text-slate-800">
<div class="max-w-2xl mx-auto p-6">
<header class="text-center mb-6">
<div class="text-5xl">🔔</div>
<h1 class="text-3xl font-bold text-blue-600">Bel Sekolah</h1>
<div id="schoolName" class="text-sm text-slate-600 mt-1">SMA Negeri</div>
</header>
<main class="bg-white shadow rounded-xl p-6">
<div class="text-center mb-4">
<div id="hari" class="text-lg text-slate-600 mb-1"></div>
<div id="clock" class="text-5xl font-extrabold tracking-wider">--.--.--</div>
<div id="tanggal" class="text-slate-500 mt-2"></div>
</div>
<div class="flex items-center justify-center gap-4 my-3">
<div id="statusDot" class="w-3 h-3 rounded-full bg-gray-300"></div>
<div id="statusText" class="text-slate-700">Memuat...</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3 mt-6 max-w-md mx-auto">
<button id="btnTest" class="btn btn-green">Tes Bel</button>
<button id="btnStop" class="btn btn-red">Stop</button>
<a href="/setting.html" class="btn btn-blue">Pengaturan</a>
</div>
<div id="log" class="mt-6 bg-slate-50 p-3 rounded h-40 overflow-auto text-sm text-slate-700"></div>
</main>
<footer class="text-center text-slate-500 text-xs mt-6">
© Wartana 2025 — Bel Sekolah Otomatis
</footer>
</div>
<script>
// WebSocket connection (uses same host)
const protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
const ws = new WebSocket(protocol + location.host + '/ws');
const hariEl = document.getElementById('hari');
const clockEl = document.getElementById('clock');
const tanggalEl = document.getElementById('tanggal');
const statusDot = document.getElementById('statusDot');
const statusText = document.getElementById('statusText');
const schoolEl = document.getElementById('schoolName');
const logEl = document.getElementById('log');
function appendLog(s) {
const t = document.createElement('div');
t.textContent = (new Date()).toLocaleTimeString() + " — " + s;
logEl.prepend(t);
}
ws.onopen = () => {
appendLog("🔌 WebSocket tersambung.");
};
ws.onclose = () => {
appendLog("⚠️ WebSocket terputus.");
statusDot.style.background = 'gray';
statusText.textContent = 'Terputus';
};
ws.onmessage = (ev) => {
try {
const msg = JSON.parse(ev.data);
if (msg.type === 'time') {
const h = String(msg.jam).padStart(2,'0');
const m = String(msg.menit).padStart(2,'0');
const s = String(msg.detik).padStart(2,'0');
clockEl.textContent = `${h}.${m}.${s}`;
const weekday = ["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"];
hariEl.textContent = weekday[msg.weekday || new Date(msg.epoch*1000).getDay()];
const monthNames = ["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"];
tanggalEl.textContent = `${msg.hari} ${monthNames[(msg.bulan||(new Date(msg.epoch*1000)).getMonth())-1]} ${msg.tahun}`;
} else if (msg.type === 'status') {
if (msg.playing) {
statusDot.style.background = 'green';
statusText.textContent = `Bel berbunyi (Track ${msg.track})`;
} else {
statusDot.style.background = 'gray';
statusText.textContent = 'Bel siaga';
}
if (msg.schoolName) schoolEl.textContent = msg.schoolName;
// Update clients info
const clientsInfoEl = document.getElementById('clientsInfo');
if (msg.clientList && msg.clientList.length > 0) {
let info = '';
msg.clientList.forEach((client, index) => {
if (index > 0) info += ', ';
info += `${client.mac}: ${client.percentage}%`;
});
clientsInfoEl.textContent = info;
} else {
clientsInfoEl.textContent = '';
}
} else if (msg.type === 'log') {
appendLog(msg.msg);
}
} catch(e) {
console.error("ws onmessage parse err", e);
}
};
// Buttons
document.getElementById('btnTest').addEventListener('click', async () => {
// call protected API -> will prompt basic auth
// test uses track 1000 (same as button click)
try {
await fetch('/api/play?track=1000');
} catch(e) { console.error(e); }
});
document.getElementById('btnStop').addEventListener('click', async () => {
try {
await fetch('/api/stop');
} catch(e) { console.error(e); }
});
</script>
</body>
</html>