td.js

 1document.addEventListener("DOMContentLoaded", () => {
 2  document.querySelectorAll("time[datetime]").forEach(el => {
 3    const d = new Date(el.getAttribute("datetime"));
 4    if (!isNaN(d)) {
 5      el.textContent = d.toLocaleString(undefined, {
 6        day: "numeric", month: "short", year: "numeric",
 7        hour: "2-digit", minute: "2-digit"
 8      });
 9    }
10  });
11
12  // FAB toggle
13  const fabToggle = document.getElementById("fab-toggle");
14  const fabActions = document.getElementById("fab-actions");
15  if (fabToggle && fabActions) {
16    fabToggle.addEventListener("click", () => {
17      const expanded = fabToggle.getAttribute("aria-expanded") === "true";
18      fabToggle.setAttribute("aria-expanded", String(!expanded));
19      fabActions.hidden = expanded;
20    });
21    // Collapse when a dialog opens from a FAB action
22    fabActions.querySelectorAll(".fab-action").forEach(btn => {
23      btn.addEventListener("click", () => {
24        fabToggle.setAttribute("aria-expanded", "false");
25        fabActions.hidden = true;
26      });
27    });
28  }
29
30  // New task form: set the action URL based on the selected project
31  const taskForm = document.getElementById("form-new-task");
32  const projectSelect = document.getElementById("nt-project");
33  if (taskForm && projectSelect) {
34    const updateAction = () => {
35      taskForm.action = "/projects/" + encodeURIComponent(projectSelect.value) + "/tasks";
36    };
37    updateAction();
38    projectSelect.addEventListener("change", updateAction);
39  }
40
41  // Reveal copy-to-clipboard buttons only when the API is available
42  // (requires a secure context: HTTPS or localhost).
43  if (navigator.clipboard) {
44    const copyIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>';
45    const checkIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 15 2 2 4-4"/><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>';
46    document.querySelectorAll(".js-copy-id").forEach(btn => {
47      btn.hidden = false;
48      btn.addEventListener("click", () => {
49        navigator.clipboard.writeText(btn.dataset.copy).then(() => {
50          btn.innerHTML = checkIcon;
51          setTimeout(() => { btn.innerHTML = copyIcon; }, 1500);
52        });
53      });
54    });
55  }
56});