113 lines
3.6 KiB
JavaScript
113 lines
3.6 KiB
JavaScript
// Create page: pick dates on the calendar, optionally give each a time.
|
|
|
|
const picked = new Map(); // dateStr -> time ("" if none)
|
|
const today = todayStr();
|
|
|
|
const cal = new Calendar(document.getElementById("calendar"), {
|
|
canClick: (ds) => ds >= today,
|
|
isPicked: (ds) => picked.has(ds),
|
|
onToggle: (ds) => {
|
|
if (picked.has(ds)) picked.delete(ds);
|
|
else picked.set(ds, "");
|
|
},
|
|
afterToggle: renderPickedList,
|
|
decorate: (ds, cell) => {
|
|
const t = picked.get(ds);
|
|
if (t) {
|
|
const chip = document.createElement("span");
|
|
chip.className = "timechip";
|
|
chip.textContent = t;
|
|
cell.appendChild(chip);
|
|
}
|
|
},
|
|
});
|
|
|
|
const listEl = document.getElementById("picked-list");
|
|
|
|
function renderPickedList() {
|
|
listEl.innerHTML = "";
|
|
if (picked.size === 0) {
|
|
listEl.innerHTML = '<p class="empty">Nothing circled yet — tap dates on the calendar above.</p>';
|
|
return;
|
|
}
|
|
for (const ds of [...picked.keys()].sort()) {
|
|
const row = document.createElement("div");
|
|
row.className = "picked-row";
|
|
|
|
const d = document.createElement("span");
|
|
d.className = "d";
|
|
d.textContent = formatDate(ds);
|
|
|
|
const time = document.createElement("input");
|
|
time.type = "time";
|
|
time.value = picked.get(ds);
|
|
time.setAttribute("aria-label", "Time for " + formatDate(ds) + " (optional)");
|
|
time.addEventListener("change", () => {
|
|
picked.set(ds, time.value);
|
|
cal.render(); // refresh time chips
|
|
});
|
|
|
|
const rm = document.createElement("button");
|
|
rm.type = "button";
|
|
rm.className = "rm";
|
|
rm.textContent = "Remove";
|
|
rm.addEventListener("click", () => {
|
|
picked.delete(ds);
|
|
renderPickedList();
|
|
cal.render();
|
|
});
|
|
|
|
row.append(d, time, rm);
|
|
listEl.appendChild(row);
|
|
}
|
|
}
|
|
|
|
const errEl = document.getElementById("error");
|
|
const createBtn = document.getElementById("create-btn");
|
|
|
|
createBtn.addEventListener("click", async () => {
|
|
errEl.textContent = "";
|
|
const title = document.getElementById("title").value.trim();
|
|
if (!title) { errEl.textContent = "Give the plan a name first."; return; }
|
|
if (picked.size === 0) { errEl.textContent = "Circle at least one date on the calendar."; return; }
|
|
|
|
createBtn.disabled = true;
|
|
try {
|
|
const res = await fetch("/api/polls", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
title,
|
|
description: document.getElementById("description").value.trim(),
|
|
options: [...picked.entries()].map(([date, time]) => ({ date, time })),
|
|
}),
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.error || "Something went wrong.");
|
|
|
|
const shareURL = location.origin + "/p/" + data.id;
|
|
const adminURL = shareURL + "?admin=" + data.adminToken;
|
|
document.getElementById("share-url").value = shareURL;
|
|
document.getElementById("admin-url").value = adminURL;
|
|
document.getElementById("open-poll").href = adminURL;
|
|
document.getElementById("create-view").hidden = true;
|
|
document.getElementById("done-view").hidden = false;
|
|
window.scrollTo(0, 0);
|
|
} catch (e) {
|
|
errEl.textContent = e.message;
|
|
} finally {
|
|
createBtn.disabled = false;
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll("[data-copy]").forEach((btn) => {
|
|
btn.addEventListener("click", async () => {
|
|
const input = document.getElementById(btn.dataset.copy);
|
|
input.select();
|
|
try { await navigator.clipboard.writeText(input.value); } catch { document.execCommand("copy"); }
|
|
const old = btn.textContent;
|
|
btn.textContent = "Copied";
|
|
setTimeout(() => (btn.textContent = old), 1200);
|
|
});
|
|
});
|