const GW = "http://localhost:3700"; let simData: any = null; let currentDay = 0; async function api(path: string, body?: any) { const opts: RequestInit = body ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) } : {}; const r = await fetch(GW + path, opts); return r.json(); } function addLog(msg: string, cls = "") { const el = document.getElementById("log")!; const div = document.createElement("div"); const t = new Date().toLocaleTimeString(); div.textContent = `${t} ${msg}`; if (cls) div.className = cls; el.prepend(div); while (el.children.length > 50) el.lastChild?.remove(); } function setText(id: string, text: string) { const el = document.getElementById(id); if (el) el.textContent = text; } function renderContracts(day: any) { const el = document.getElementById("contracts")!; el.replaceChildren(); if (!day?.contracts?.length) { el.textContent = "No contracts for this day"; return; } for (const c of day.contracts) { const div = document.createElement("div"); div.className = `contract ${c.filled >= c.headcount ? "filled" : c.priority === "urgent" ? "urgent" : c.priority === "high" ? "high" : ""}`; const icon = c.priority === "urgent" ? "!!" : c.priority === "high" ? "!" : ""; const title = document.createElement("div"); title.className = "title"; title.textContent = `${icon} ${c.id} - ${c.client}`; div.appendChild(title); const meta = document.createElement("div"); meta.className = "meta"; meta.textContent = `${c.role} x${c.headcount} | ${c.city || c.state} | ${c.filled}/${c.headcount} filled | ${c.priority}`; div.appendChild(meta); if (c.matches?.length) { const workers = document.createElement("div"); workers.className = "workers"; workers.textContent = c.matches.map((m: any) => `${m.name || m.doc_id} (${m.score?.toFixed(2) || "?"})`).join(", "); div.appendChild(workers); } if (c.notes) { const notes = document.createElement("div"); notes.className = "meta"; notes.textContent = c.notes; div.appendChild(notes); } el.appendChild(div); } } function renderDayNav() { const nav = document.getElementById("day-nav")!; nav.replaceChildren(); if (!simData?.days) return; simData.days.forEach((d: any, i: number) => { const btn = document.createElement("button"); btn.className = `day-btn ${i === currentDay ? "active" : ""}`; btn.textContent = d.label; btn.onclick = () => { currentDay = i; renderDayNav(); renderContracts(simData.days[i]); }; nav.appendChild(btn); }); } function renderWeekStats() { const el = document.getElementById("week-stats")!; el.replaceChildren(); if (!simData?.summary) return; const s = simData.summary; const rows = [ ["Total contracts", s.total_contracts], ["Total positions", s.total_needed], ["Filled", s.total_filled], ["Fill rate", `${s.fill_pct}%`], ["Emergencies", s.emergencies], ["Handoffs", s.handoffs], ["Playbooks", s.playbook_entries], ]; for (const [label, val] of rows) { const row = document.createElement("div"); row.className = "stat-row"; const nameSpan = document.createElement("span"); nameSpan.textContent = String(label); const valSpan = document.createElement("span"); valSpan.className = "val"; valSpan.textContent = String(val); row.appendChild(nameSpan); row.appendChild(valSpan); el.appendChild(row); } } async function loadAlerts() { const el = document.getElementById("alerts")!; el.replaceChildren(); try { const r = await api("/sql", { sql: "SELECT archetype, COUNT(*) cnt FROM ethereal_workers WHERE archetype IN ('erratic','silent') GROUP BY archetype" }); for (const row of r.rows || []) { const div = document.createElement("div"); div.className = "alert warn"; div.textContent = `${row.archetype}: ${row.cnt} workers flagged`; el.appendChild(div); } const info = document.createElement("div"); info.className = "alert info"; info.textContent = "System: all services running"; el.appendChild(info); } catch { const div = document.createElement("div"); div.className = "alert warn"; div.textContent = "Could not load alerts"; el.appendChild(div); } } async function loadPlaybooks() { const el = document.getElementById("playbooks")!; el.replaceChildren(); try { const r = await api("/playbooks", {}); const pbs = r.playbooks || []; if (pbs.length === 0) { el.textContent = "No playbooks yet - run the simulation"; return; } for (const p of pbs.slice(0, 8)) { const div = document.createElement("div"); div.className = "playbook"; const op = document.createElement("span"); op.className = "op"; op.textContent = (p.operation || "").slice(0, 50); div.appendChild(op); div.appendChild(document.createElement("br")); div.appendChild(document.createTextNode((p.result || "").slice(0, 80))); el.appendChild(div); } } catch { el.textContent = "Could not load playbooks"; } } async function checkServices() { try { await fetch(GW + "/health"); document.getElementById("svc-gw")!.className = "dot green"; } catch { document.getElementById("svc-gw")!.className = "dot red"; } try { const r = await api("/vram"); setText("vram-display", `VRAM: ${r.gpu?.used_mib || "?"}/${r.gpu?.total_mib || "?"} MiB`); } catch {} } // Week simulation runner (window as any).runWeek = async function () { addLog("Starting week simulation..."); try { const r = await fetch(GW + "/simulation/run", { method: "POST" }); simData = await r.json(); if (simData.error) { addLog(`Error: ${simData.error}`, "fail"); return; } renderDayNav(); renderContracts(simData.days[0]); renderWeekStats(); addLog(`Week complete: ${simData.summary?.total_filled}/${simData.summary?.total_needed} filled`, "ok"); } catch (e) { addLog(`Simulation failed: ${e}`, "fail"); } }; (window as any).refresh = async function () { addLog("Refreshing..."); await checkServices(); await loadAlerts(); await loadPlaybooks(); }; // Initial load checkServices(); loadAlerts(); loadPlaybooks(); setInterval(checkServices, 30000);