Hi, I'm using whatsapp.
This commit is contained in:
commit
be01fa5299
12 files changed
+2462
No files matched your search
+214
@@ -0,0 +1,214 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const MOODS = ["😎", "🔥", "💀", "😭", "🥲", "✨", "😤", "🖤", "🫠", "👾"];
|
||||
const MAX_BADGES = 5;
|
||||
|
||||
// ---------------- tabs ----------------
|
||||
const tabForm = document.getElementById("tab-form");
|
||||
const tabGallery = document.getElementById("tab-gallery");
|
||||
const panelForm = document.getElementById("panel-form");
|
||||
const panelGallery = document.getElementById("panel-gallery");
|
||||
|
||||
function showPanel(which) {
|
||||
const showForm = which === "form";
|
||||
panelForm.classList.toggle("is-hidden", !showForm);
|
||||
panelGallery.classList.toggle("is-hidden", showForm);
|
||||
tabForm.classList.toggle("is-active", showForm);
|
||||
tabGallery.classList.toggle("is-active", !showForm);
|
||||
tabForm.setAttribute("aria-selected", String(showForm));
|
||||
tabGallery.setAttribute("aria-selected", String(!showForm));
|
||||
if (!showForm) loadEntries();
|
||||
}
|
||||
|
||||
tabForm.addEventListener("click", () => showPanel("form"));
|
||||
tabGallery.addEventListener("click", () => showPanel("gallery"));
|
||||
|
||||
// ---------------- mood picker ----------------
|
||||
const moodPicker = document.querySelector(".mood-picker");
|
||||
const moodInput = document.getElementById("mood");
|
||||
|
||||
MOODS.forEach((emoji, i) => {
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = "mood-btn";
|
||||
btn.textContent = emoji;
|
||||
btn.setAttribute("role", "radio");
|
||||
btn.setAttribute("aria-pressed", i === MOODS.length - 1 ? "true" : "false"); // default last = 🖤
|
||||
btn.setAttribute("aria-label", `Mood: ${emoji}`);
|
||||
btn.addEventListener("click", () => {
|
||||
moodPicker.querySelectorAll(".mood-btn").forEach((b) => b.setAttribute("aria-pressed", "false"));
|
||||
btn.setAttribute("aria-pressed", "true");
|
||||
moodInput.value = emoji;
|
||||
});
|
||||
moodPicker.appendChild(btn);
|
||||
});
|
||||
moodInput.value = MOODS[MOODS.length - 1];
|
||||
|
||||
// ---------------- badge preview ----------------
|
||||
const badgeInput = document.getElementById("badges");
|
||||
const badgePreview = document.getElementById("badge-preview");
|
||||
const formError = document.getElementById("form-error");
|
||||
const formSuccess = document.getElementById("form-success");
|
||||
|
||||
badgeInput.addEventListener("change", () => {
|
||||
badgePreview.innerHTML = "";
|
||||
const files = Array.from(badgeInput.files || []);
|
||||
|
||||
if (files.length > MAX_BADGES) {
|
||||
showError(`Please choose at most ${MAX_BADGES} images.`);
|
||||
badgeInput.value = "";
|
||||
return;
|
||||
}
|
||||
clearError();
|
||||
|
||||
files.forEach((file) => {
|
||||
const url = URL.createObjectURL(file);
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = file.name;
|
||||
img.onload = () => URL.revokeObjectURL(url);
|
||||
badgePreview.appendChild(img);
|
||||
});
|
||||
});
|
||||
|
||||
function showError(msg) {
|
||||
clearSuccess();
|
||||
formError.textContent = msg;
|
||||
formError.hidden = false;
|
||||
}
|
||||
function clearError() {
|
||||
formError.hidden = true;
|
||||
formError.textContent = "";
|
||||
}
|
||||
function showSuccess(msg) {
|
||||
clearError();
|
||||
formSuccess.textContent = msg;
|
||||
formSuccess.hidden = false;
|
||||
}
|
||||
function clearSuccess() {
|
||||
formSuccess.hidden = true;
|
||||
formSuccess.textContent = "";
|
||||
}
|
||||
|
||||
// ---------------- form submit ----------------
|
||||
const form = document.getElementById("entry-form");
|
||||
const submitBtn = document.getElementById("submit-btn");
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
clearError();
|
||||
clearSuccess();
|
||||
|
||||
const name = document.getElementById("name").value.trim();
|
||||
if (!name) {
|
||||
showError("Your Name is required.");
|
||||
document.getElementById("name").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.querySelector("span").textContent = "ADDING...";
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/entries", { method: "POST", body: formData });
|
||||
const data = await res.json().catch(() => ({}));
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(data.error || "Something went wrong submitting your page.");
|
||||
}
|
||||
|
||||
form.reset();
|
||||
badgePreview.innerHTML = "";
|
||||
moodPicker.querySelectorAll(".mood-btn").forEach((b, i) =>
|
||||
b.setAttribute("aria-pressed", i === MOODS.length - 1 ? "true" : "false")
|
||||
);
|
||||
moodInput.value = MOODS[MOODS.length - 1];
|
||||
|
||||
showSuccess("Thanks! Your page has been submitted and is waiting for approval — check back on \u201cEveryone's pages\u201d soon 💌");
|
||||
} catch (err) {
|
||||
showError(err.message);
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.querySelector("span").textContent = "ADD MY PAGE";
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------- gallery ----------------
|
||||
const galleryGrid = document.getElementById("gallery-grid");
|
||||
const galleryStatus = document.getElementById("gallery-status");
|
||||
|
||||
function escapeHtml(str) {
|
||||
const div = document.createElement("div");
|
||||
div.textContent = str == null ? "" : String(str);
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function ordinal(n) {
|
||||
const s = ["th", "st", "nd", "rd"];
|
||||
const v = n % 100;
|
||||
return n + (s[(v - 20) % 10] || s[v] || s[0]);
|
||||
}
|
||||
|
||||
function formatDate(ts) {
|
||||
const d = new Date(ts);
|
||||
const months = [
|
||||
"January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December",
|
||||
];
|
||||
return `${ordinal(d.getDate())} ${months[d.getMonth()]} ${d.getFullYear()}`;
|
||||
}
|
||||
|
||||
function renderRow(label, value) {
|
||||
if (!value) return "";
|
||||
return `<div class="entry-card__row"><span class="k">${escapeHtml(label)}</span><span class="v">${escapeHtml(value)}</span></div>`;
|
||||
}
|
||||
|
||||
function renderCard(entry) {
|
||||
const card = document.createElement("article");
|
||||
card.className = "entry-card";
|
||||
|
||||
const badgesHtml = (entry.badges || [])
|
||||
.map((src) => `<img src="${escapeHtml(src)}" alt="badge" loading="lazy" />`)
|
||||
.join("");
|
||||
|
||||
card.innerHTML = `
|
||||
<span class="entry-card__mood" aria-hidden="true">${escapeHtml(entry.mood || "🖤")}</span>
|
||||
<h3 class="entry-card__name">${escapeHtml(entry.name)}</h3>
|
||||
${renderRow("Fun fact", entry.funFact)}
|
||||
${renderRow("Favourite colour", entry.favColor)}
|
||||
${renderRow("Favourite music genre", entry.favGenre)}
|
||||
${renderRow("Favourite agent", entry.petPeeves)}
|
||||
${renderRow("On Valo", entry.merzHate)}
|
||||
${renderRow("IGN", entry.socialLink)}
|
||||
${badgesHtml ? `<div class="entry-card__badges">${badgesHtml}</div>` : ""}
|
||||
<div class="entry-card__time">${escapeHtml(formatDate(entry.createdAt))}</div>
|
||||
`;
|
||||
return card;
|
||||
}
|
||||
|
||||
async function loadEntries() {
|
||||
galleryStatus.textContent = "flipping through pages...";
|
||||
galleryGrid.innerHTML = "";
|
||||
try {
|
||||
const res = await fetch("/api/entries");
|
||||
if (!res.ok) throw new Error("Could not load pages.");
|
||||
const entries = await res.json();
|
||||
|
||||
if (!entries.length) {
|
||||
galleryStatus.textContent = "no pages yet — be the first to sign the book!";
|
||||
return;
|
||||
}
|
||||
|
||||
galleryStatus.textContent = `${entries.length} page${entries.length === 1 ? "" : "s"} taped in so far`;
|
||||
entries.forEach((entry) => galleryGrid.appendChild(renderCard(entry)));
|
||||
} catch (err) {
|
||||
galleryStatus.textContent = "hmm, couldn't load the pages. is the server running?";
|
||||
}
|
||||
}
|
||||
|
||||
// initial load in the background so the gallery is ready when switched to
|
||||
loadEntries();
|
||||
})();
|
||||
Reference in new issue
Block a user