mathly / mathly2 /static /admin.js
3v324v23's picture
Add specifications for exercise tests, grades, learning path, lesson quizzes, readiness meter, real tests, rewards, simulation tests, skill mastery, smart board, student home, and test countdown
a7ffb22
Raw History Blame Contribute Delete
3.01 kB
// The admin's screen: the accounts the app knows, with their roles; give a role to any email (even one that never signed in) or take it away.
import { api } from "./api.js";
import { esc } from "./math.js";
import { ROLE_NAMES } from "./roles.js";
const GRANTABLE = ["student", "teacher", "manager", "admin"];
let draw;
async function showAccounts(message = "", error = "") {
const accounts = await api("/api/accounts");
draw(`<section id="accounts"><h2>ืชืคืงื™ื“ื™ื</h2>
<section class="card"><h3>ืžืชืŸ ืชืคืงื™ื“</h3>
<p class="muted">ืืคืฉืจ ืœื”ืงืœื™ื“ ื’ื ื›ืชื•ื‘ืช ืฉืขื•ื“ ืœื ื ื›ื ืกื” ืœืืคืœื™ืงืฆื™ื”: ื”ืชืคืงื™ื“ ื™ื—ื•ืœ ืžื”ื›ื ื™ืกื” ื”ืจืืฉื•ื ื” ืฉืœื”. ืฉื™ื ื•ื™ ืชืคืงื™ื“ ื ืงืœื˜ ืชื•ืš ื›ื—ืฆื™ ื“ืงื”.</p>
<form id="give"><label for="g-email">ืžื™ื™ืœ</label><input id="g-email" type="email" dir="ltr" required autocomplete="off">
<label for="g-role">ืชืคืงื™ื“</label><select id="g-role">${GRANTABLE.map(r => `<option value="${r}">${ROLE_NAMES[r]}</option>`).join("")}</select>
${error ? `<p class="error" role="alert">${esc(error)}</p>` : ""}${message ? `<p role="status">${esc(message)}</p>` : ""}
<p><button class="primary" type="submit">ืžืชืŸ ื”ืชืคืงื™ื“</button></p></form></section>
<div class="tablewrap"><table class="data"><thead><tr><th>ื—ืฉื‘ื•ืŸ</th><th>ืชืคืงื™ื“ื™ื ืฉื ื™ืชื ื•</th><th>ืžื”ื”ื’ื“ืจื•ืช</th></tr></thead>
<tbody>${accounts.map(a => `<tr data-email="${esc(a.email)}"><td dir="ltr">${esc(a.email)}</td>
<td>${a.roles.map(r => `<span class="tag">${ROLE_NAMES[r]} <button type="button" data-take="${r}" aria-label="ื”ืกืจืช ื”ืชืคืงื™ื“ ${ROLE_NAMES[r]} ืžึพ${esc(a.email)}">โœ•</button></span>`).join(" ") || '<span class="muted">โ€”</span>'}</td>
<td>${a.from_settings.map(r => `<span class="tag">${ROLE_NAMES[r]}</span>`).join(" ") || '<span class="muted">โ€”</span>'}</td></tr>`).join("")}</tbody></table></div></section>`);
document.getElementById("give").addEventListener("submit", async event => {
event.preventDefault();
const email = document.getElementById("g-email").value.trim(), role = document.getElementById("g-role").value;
try {
await api(`/api/accounts/${encodeURIComponent(email)}/roles/${role}`, { method: "POST" });
await showAccounts(`${email}: ื ื™ืชืŸ ื”ืชืคืงื™ื“ ${ROLE_NAMES[role]}.`);
} catch (e) { await showAccounts("", e.message); }
});
document.querySelector("#accounts tbody").addEventListener("click", async event => {
const role = event.target.closest("[data-take]")?.dataset.take;
if (!role) return;
const email = event.target.closest("tr").dataset.email;
try {
await api(`/api/accounts/${encodeURIComponent(email)}/roles/${role}`, { method: "DELETE" });
await showAccounts(`${email}: ื”ืชืคืงื™ื“ ${ROLE_NAMES[role]} ื”ื•ืกืจ.`);
} catch (e) { await showAccounts("", e.message); }
});
}
export async function show(me, view) {
draw = view;
await showAccounts();
}