File size: 3,013 Bytes
a7ffb22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 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();
}