Coaching Centre Intelligence,
Simplified

Track marks, manage fees, and understand performance — all in one minimal dashboard.

Loading notices…
// ============================================================ const API_BASE = "https://krate-api.possible04102128.workers.dev"; // ↑ REPLACE THIS with your actual Worker URL after deploying Step 3 const Auth = { setToken(token) { sessionStorage.setItem("statvo_token", token); }, getToken() { return sessionStorage.getItem("statvo_token"); }, clearToken() { sessionStorage.removeItem("statvo_token"); sessionStorage.removeItem("statvo_user"); }, decodeToken(token) { try { return JSON.parse(atob(token.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"))); } catch { return null; } }, // session() is an alias for getUser() — used throughout dashboard pages session() { return this.getUser(); }, getUser() { const cached = sessionStorage.getItem("statvo_user"); if (cached) { try { return JSON.parse(cached); } catch {} } const token = this.getToken(); if (!token) return null; const user = this.decodeToken(token); if (user) sessionStorage.setItem("statvo_user", JSON.stringify(user)); return user; }, isLoggedIn() { const user = this.getUser(); return !!(user && user.exp > Math.floor(Date.now() / 1000)); }, login() { window.location.href = `${API_BASE}/auth/google`; }, logout() { this.clearToken(); window.location.href = "/"; }, // Guard function — call at top of each protected dashboard page // Auth.require('student'), Auth.require('teacher'), etc. require(role) { if (!this.isLoggedIn()) { window.location.href = "/?error=login_required"; return false; } const user = this.getUser(); if (role && user.role !== role) { // Director can also access admin pages if (role === "managing_admin" && user.role === "director") return true; window.location.href = "/?error=forbidden"; return false; } return true; } }; // ── API CLIENT ─────────────────────────────────────────────── // Returns { ok: true, data: {...} } or { ok: false, error: "..." } // Never throws — all errors are caught and returned as { ok: false } const api = { _headers() { const h = { "Content-Type": "application/json" }; const t = Auth.getToken(); if (t) h["Authorization"] = `Bearer ${t}`; return h; }, async _handle(res) { if (res.status === 401) { Auth.clearToken(); window.location.href = "/?error=session_expired"; return { ok: false, error: "Session expired" }; } try { const data = await res.json(); if (!res.ok) return { ok: false, error: data.error || "API error" }; return { ok: true, data }; } catch { return { ok: false, error: "Invalid server response" }; } }, async get(path) { try { const res = await fetch(`${API_BASE}${path}`, { method: "GET", headers: this._headers() }); return this._handle(res); } catch (e) { return { ok: false, error: e.message }; } }, async post(path, body) { try { const res = await fetch(`${API_BASE}${path}`, { method: "POST", headers: this._headers(), body: JSON.stringify(body) }); return this._handle(res); } catch (e) { return { ok: false, error: e.message }; } } }; // ── AUTH CALLBACK HANDLER ──────────────────────────────────── // Run on auth-callback.html — reads token from URL hash after OAuth function handleAuthCallback() { const hash = window.location.hash; // "#token=eyJ..." const token = hash.startsWith("#token=") ? hash.slice(7) : null; if (!token) { window.location.href = "/?error=auth_failed"; return; } Auth.setToken(token); const user = Auth.getUser(); if (!user) { window.location.href = "/?error=invalid_token"; return; } const routes = { student : "/dashboard/student.html", teacher : "/dashboard/teacher.html", managing_admin: "/dashboard/admin.html", director : "/dashboard/director.html" }; window.location.href = routes[user.role] || "/"; } // ── NAV RENDERER ───────────────────────────────────────────── // Call renderNav() with NO arguments on every page // Requires a in the page function renderNav() { const el = document.getElementById("nav-area"); if (!el) return; const user = Auth.getUser(); if (!user) { el.innerHTML = ``; return; } el.innerHTML = ` ${_esc(user.name)} `; } // Internal escape helper (also available globally as esc() in pages) function _esc(s) { if (!s) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; } document.addEventListener("DOMContentLoaded", renderNav);