const adminState = { data: null, userId: localStorage.getItem("nht:adminUserId") || localStorage.getItem("nht:userId") || "", }; const admin$ = (selector, root = document) => root.querySelector(selector); function adminEscape(value) { return String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function adminNumber(value, digits = 0) { return Number(value || 0).toLocaleString(undefined, { maximumFractionDigits: digits, minimumFractionDigits: digits, }); } function adminToast(message) { const toast = admin$("#siteToast"); if (!toast) return; toast.textContent = message; toast.classList.add("show"); window.clearTimeout(adminToast.timer); adminToast.timer = window.setTimeout(() => toast.classList.remove("show"), 2400); } async function adminApi(action, payload = null, method = "POST") { const options = { method }; if (payload) { options.headers = { "Content-Type": "application/json" }; options.body = JSON.stringify(payload); } const response = await fetch(`/api.php?action=${encodeURIComponent(action)}`, options); const json = await response.json(); if (!response.ok || json.error) { throw new Error(json.error || "Request failed."); } return json; } async function loadAdminState() { const params = new URLSearchParams({ action: "state", view: "weekly", }); if (adminState.userId) { params.set("user_id", adminState.userId); } const response = await fetch(`/api.php?${params.toString()}`); const json = await response.json(); if (!response.ok || json.error) { throw new Error(json.error || "Unable to load admin dashboard."); } adminState.data = json; adminState.userId = String(json.currentUser.id); localStorage.setItem("nht:adminUserId", adminState.userId); renderAdminPage(); } function renderAdminPage() { const data = adminState.data; const profileSelect = admin$("#adminProfileSelect"); profileSelect.innerHTML = data.users.map((user) => ` `).join(""); admin$("#adminPanel").hidden = !data.currentUserIsAdmin; admin$("#adminDenied").hidden = data.currentUserIsAdmin; if (!data.currentUserIsAdmin) { return; } renderAdminStats(); renderAdminUsers(); } function renderAdminStats() { const summary = adminState.data.adminSummary || {}; const stats = [ ["Total", summary.total_users || 0], ["Active", summary.active_users || 0], ["Inactive", summary.inactive_users || 0], ["Admins", summary.admins || 0], ["Members", summary.members || 0], ["Med logs", summary.medication_logs || 0], ["Vitals", summary.vital_logs || 0], ["Labs", summary.lab_results || 0], ["Visits", summary.appointments || 0], ]; admin$("#adminStats").innerHTML = stats.map(([label, value]) => `
${adminEscape(label)} ${adminNumber(value)}
`).join(""); } function renderAdminUsers() { const currentId = Number(adminState.data.currentUser.id); const rows = adminState.data.adminUsers || []; admin$("#adminUserList").innerHTML = rows.length ? rows.map((user) => { const isCurrent = Number(user.id) === currentId; const nextStatus = user.status === "active" ? "inactive" : "active"; const height = Number(user.height_in || 0) > 0 ? ` / ${adminNumber(user.height_in, 1)} in` : ""; const training = user.training_level ? ` / ${user.training_level}` : ""; return `

${adminEscape(user.name)}${isCurrent ? " (current)" : ""}

${adminEscape(user.email || "No email")}

${adminEscape(user.fitness_focus || "No care focus set")}

${adminEscape(user.phone || "No phone")}${adminEscape(training)}${adminEscape(height)}

${adminEscape(user.role)} ${adminEscape(user.status)}
${adminNumber(user.routine_count)} routines ${adminNumber(user.workout_count)} workouts ${adminNumber(user.nutrition_log_count)} nutrition logs ${adminNumber(user.supplement_log_count)} supplement logs ${adminNumber(user.metric_count)} metrics ${adminNumber(user.medication_log_count)} meds ${adminNumber(user.vital_log_count)} vitals ${adminNumber(user.lab_result_count)} labs ${adminNumber(user.appointment_count)} appointments
`; }).join("") : `
No users found.
`; } function resetAdminForm() { const form = admin$("#adminUserForm"); form.reset(); form.elements.target_user_id.value = ""; form.elements.role.value = "member"; form.elements.status.value = "active"; form.elements.theme_mode.value = "dark"; form.elements.theme_accent.value = "green"; admin$("#adminUserFormTitle").textContent = "Create Profile"; } function fillAdminForm(user) { const form = admin$("#adminUserForm"); form.elements.target_user_id.value = user.id || ""; form.elements.name.value = user.name || ""; form.elements.email.value = user.email || ""; form.elements.role.value = user.role || "member"; form.elements.status.value = user.status || "active"; form.elements.phone.value = user.phone || ""; form.elements.birthday.value = user.birthday || ""; form.elements.height_in.value = Number(user.height_in || 0) > 0 ? user.height_in : ""; form.elements.training_level.value = user.training_level || ""; form.elements.theme_mode.value = user.theme_mode || "dark"; form.elements.theme_accent.value = user.theme_accent || "green"; form.elements.fitness_focus.value = user.fitness_focus || ""; form.elements.emergency_contact.value = user.emergency_contact || ""; admin$("#adminUserFormTitle").textContent = `Edit ${user.name}`; } function adminFormPayload(form) { const payload = Object.fromEntries(new FormData(form).entries()); payload.acting_user_id = adminState.data.currentUser.id; return payload; } function renderRefreshResult(result) { const target = admin$("#medicineSourceRefreshResult"); if (!target) return; const rows = (result.sources || []).map((source) => `
${adminEscape(source.source)}: ${adminEscape(source.status)} ${adminNumber(source.inserted)} inserted / ${adminNumber(source.updated)} updated ${adminEscape(source.message || "")}
`).join(""); target.innerHTML = `

${adminNumber(result.catalog_count)} medicines in catalog after refresh.

${rows}
`; } document.addEventListener("DOMContentLoaded", async () => { admin$("#adminProfileSelect").addEventListener("change", async (event) => { adminState.userId = event.target.value; localStorage.setItem("nht:adminUserId", adminState.userId); await loadAdminState(); }); admin$("#clearAdminUserFormButton").addEventListener("click", resetAdminForm); admin$("#adminUserList").addEventListener("click", async (event) => { const button = event.target.closest("[data-admin-action]"); if (!button) return; const user = (adminState.data.adminUsers || []).find((item) => String(item.id) === button.dataset.adminUserId); if (!user) return; if (button.dataset.adminAction === "edit") { fillAdminForm(user); admin$("#adminUserForm").scrollIntoView({ behavior: "smooth", block: "start" }); return; } try { if (button.dataset.adminAction === "status") { await adminApi("update_user", { acting_user_id: adminState.data.currentUser.id, target_user_id: user.id, status: button.dataset.nextStatus, }); adminToast("Profile status updated"); } if (button.dataset.adminAction === "delete") { const ok = window.confirm(`Delete ${user.name} and all tracker data attached to this profile?`); if (!ok) return; await adminApi("delete_user", { acting_user_id: adminState.data.currentUser.id, target_user_id: user.id, }); adminToast("Profile deleted"); } resetAdminForm(); await loadAdminState(); } catch (error) { adminToast(error.message); } }); admin$("#adminUserForm").addEventListener("submit", async (event) => { event.preventDefault(); const payload = adminFormPayload(event.currentTarget); const action = payload.target_user_id ? "update_user" : "create_user"; try { await adminApi(action, payload); resetAdminForm(); await loadAdminState(); adminToast(action === "create_user" ? "Profile created" : "Profile updated"); } catch (error) { adminToast(error.message); } }); const refreshForm = admin$("#medicineSourceRefreshForm"); if (refreshForm) { refreshForm.addEventListener("submit", async (event) => { event.preventDefault(); const payload = adminFormPayload(refreshForm); const button = refreshForm.querySelector("button[type='submit']"); if (button) button.disabled = true; admin$("#medicineSourceRefreshResult").textContent = "Refreshing medicine sources..."; try { const result = await adminApi("refresh_medication_sources", payload); renderRefreshResult(result); adminToast("Medicine sources refreshed"); } catch (error) { admin$("#medicineSourceRefreshResult").textContent = error.message; adminToast(error.message); } finally { if (button) button.disabled = false; } }); } try { await loadAdminState(); } catch (error) { adminToast(error.message); } });