debug line test

This commit is contained in:
Frank Schwenk
2026-06-09 21:21:52 +02:00
commit eaa019087e
43 changed files with 2666 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
import { api } from "./lib/api.js";
import { getToken, setToken, clearToken, isLoggedIn } from "./lib/auth.js";
import { setStreakBadge } from "./lib/badge.js";
import { enqueue, flushQueue } from "./lib/offlineQueue.js";
import { registerPwa, wirePwaInstall, subscribePush } from "./lib/pwa.js";
import { renderSlots, renderHeatmap, updateStats, showToast, showConfetti, showEasterEgg } from "./lib/ui.js";
let pinBuffer = "";
const STATUS = { upcoming: "Noch nicht", pending: "Jetzt!", overdue: "Überfällig", snoozed: "Snoozed", taken: "Genommen", missed: "Verpasst" };
function showScreen(id) {
document.querySelectorAll(".screen").forEach((s) => { s.hidden = s.id !== id; });
}
function wirePinPad() {
const pad = document.getElementById("pinPad");
const keys = ["1","2","3","4","5","6","7","8","9","⌫","0","✓"];
pad.innerHTML = keys.map((k) =>
`<button type="button" class="pinKey${k.length > 1 ? " wide" : ""}" data-key="${k}">${k}</button>`
).join("");
pad.addEventListener("click", async (e) => {
const btn = e.target.closest("[data-key]");
if (!btn) return;
const key = btn.dataset.key;
const errEl = document.getElementById("loginError");
errEl.hidden = true;
if (key === "⌫") { pinBuffer = pinBuffer.slice(0, -1); }
else if (key === "✓") {
if (pinBuffer.length < 4) return;
try {
const { token } = await api.login(pinBuffer);
setToken(token);
pinBuffer = "";
await initApp();
} catch {
errEl.textContent = "Falscher PIN. Dein Gehirn auch.";
errEl.hidden = false;
pinBuffer = "";
}
} else if (pinBuffer.length < 6) {
pinBuffer += key;
}
document.getElementById("pinDisplay").textContent = "•".repeat(pinBuffer.length) || "••••";
});
}
function wireTabs() {
document.querySelectorAll(".tab").forEach((tab) => {
tab.addEventListener("click", () => {
document.querySelectorAll(".tab").forEach((t) => t.classList.remove("active"));
document.querySelectorAll(".tabPanel").forEach((p) => p.hidden = true);
tab.classList.add("active");
const panel = document.getElementById(`tab-${tab.dataset.tab}`);
panel.hidden = false;
if (tab.dataset.tab === "history") loadHistory();
});
});
}
async function handleTake(slotId) {
const card = document.querySelector(`[data-slot="${slotId}"]`);
try {
if (!navigator.onLine) {
await enqueue({ slot_id: slotId, status: "taken" });
showToast("Offline gespeichert — wird synchronisiert.");
return;
}
const result = await api.log({ slot_id: slotId, status: "taken" });
card?.classList.add("dopamin");
showConfetti();
showToast(result.message);
if (result.new_milestones?.length) {
for (const m of result.new_milestones) {
showEasterEgg(m.title, "Achievement freigeschaltet!");
}
}
await refreshDashboard(result.stats);
} catch (e) {
await enqueue({ slot_id: slotId, status: "taken" });
showToast("Gespeichert — Sync folgt.");
}
}
async function handleSnooze(slotId, minutes) {
try {
const result = await api.snooze(slotId, minutes);
showToast(result.message || `Snooze +${minutes} Min`);
await refreshDashboard();
} catch (e) {
showToast("Snooze fehlgeschlagen.");
}
}
async function refreshDashboard(stats) {
const [today, roast] = await Promise.all([
api.today(),
api.roast().catch(() => ({ text: "Dein Gebrain wartet auf Koffein und Medis." })),
]);
renderSlots(today.slots, handleTake, handleSnooze);
document.getElementById("roastText").textContent = roast.text;
const s = stats || await api.stats();
updateStats(s);
await setStreakBadge(s.streak);
}
async function loadHistory() {
const data = await api.history(90);
renderHeatmap(data.days);
updateStats(data.stats);
const oracle = await api.oracle().catch(() => ({ text: "Das Orakel schweigt. Wahrscheinlich enttäuscht." }));
document.getElementById("oracleText").textContent = oracle.text;
}
async function handleDeepLink() {
const params = new URLSearchParams(location.search);
const slot = params.get("slot");
if (params.get("action") === "take" && slot) {
await handleTake(slot);
history.replaceState({}, "", "/");
} else if (params.get("action") === "snooze" && slot) {
const minutes = parseInt(params.get("minutes") || "15", 10);
await handleSnooze(slot, minutes);
history.replaceState({}, "", "/");
}
}
async function initApp() {
showScreen("mainScreen");
wireTabs();
wirePwaInstall();
document.getElementById("logoutBtn").addEventListener("click", () => {
clearToken();
location.reload();
});
document.getElementById("enablePush").addEventListener("click", async () => {
const statusEl = document.getElementById("pushStatus");
try {
const perm = await Notification.requestPermission();
if (perm !== "granted") { statusEl.textContent = "Permission verweigert."; return; }
const { key } = await api.vapidKey();
if (!key) { statusEl.textContent = "VAPID keys nicht konfiguriert."; return; }
const sub = await subscribePush(key);
await api.pushSubscribe(sub);
statusEl.textContent = "Push aktiv! Du wirst dezent erinnert.";
} catch (e) {
statusEl.textContent = e.message || "Push-Setup fehlgeschlagen.";
}
});
await flushQueue((data) => api.log(data));
await refreshDashboard();
await handleDeepLink();
}
navigator.serviceWorker?.addEventListener("message", (event) => {
if (event.data?.type === "SYNC_QUEUE") flushQueue((data) => api.log(data));
});
async function boot() {
registerPwa();
if (isLoggedIn()) {
try { await initApp(); return; } catch { clearToken(); }
}
showScreen("loginScreen");
wirePinPad();
}
boot();