Files
org_web/assets/scripts/comments.js
2026-04-02 11:27:50 +01:00

198 lines
4.5 KiB
JavaScript
Executable File

let pageSlug = null;
/* -----------------------------
* DOM builders
* ----------------------------- */
function showReplyForm(container, parentId) {
if (container.querySelector(".reply-form")) return;
const form = document.createElement("form");
form.className = "reply-form";
form.innerHTML = `
<input type="text" name="author" placeholder="Your name (optional)">
<textarea name="content" required placeholder="Write a reply..."></textarea>
<button type="submit">Post reply</button>
`;
form.addEventListener("submit", async e => {
e.preventDefault();
const author = form.author.value.trim();
const content = form.content.value.trim();
if (!content) return;
const ok = await postComment(author, content, parentId);
if (ok) loadComments();
else alert("Failed to post reply");
});
container.appendChild(form);
}
function createComment(comment) {
const wrapper = document.createElement("div");
wrapper.className = "comment";
wrapper.dataset.id = comment.id;
const author = document.createElement("strong");
author.textContent = comment.author || "Anonymous";
const body = document.createElement("p");
body.textContent = comment.content;
const time = document.createElement("time");
time.className = "comment-date";
time.dateTime = comment.created_at;
time.textContent = new Date(comment.created_at).toLocaleString();
const replyBtn = document.createElement("button");
replyBtn.className = "comment-reply";
replyBtn.textContent = "Reply";
replyBtn.onclick = () =>
showReplyForm(wrapper, Number(comment.id));
wrapper.append(author, body, time, replyBtn);
if (comment.children?.length) {
const replies = document.createElement("div");
replies.className = "comment-children";
comment.children.forEach(child => {
replies.appendChild(createComment(child));
});
wrapper.appendChild(replies);
}
console.log("PARENT_ID RECEIVED:", comment.parent_id)
return wrapper;
}
function buildCommentTree(comments) {
const byId = {};
const roots = [];
comments.forEach(comment => {
comment.children = [];
byId[comment.id] = comment;
});
comments.forEach(comment => {
if (comment.parent_id) {
const parent = byId[comment.parent_id];
if (parent) parent.children.push(comment);
} else {
roots.push(comment);
}
});
return roots;
}
/* -----------------------------
* Rendering
* ----------------------------- */
function renderComments(comments) {
const list = document.getElementById("comments-list");
list.innerHTML = "";
if (!comments.length) {
const empty = document.createElement("p");
empty.className = "comments-empty";
empty.textContent = "No comments yet.";
list.appendChild(empty);
return;
}
const tree = buildCommentTree(comments);
tree.forEach(comment => list.appendChild(createComment(comment)));
}
/* -----------------------------
* API
* ----------------------------- */
async function fetchComments() {
const res = await fetch(`/api/comments/${pageSlug}`);
if (!res.ok) {
console.error("Failed to fetch comments");
return [];
}
return await res.json();
}
async function postComment(author, content, parentId = null) {
const res = await fetch("/api/comments", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
page_slug: pageSlug,
author: author || null,
content,
parent_id: parentId
})
});
return res.ok;
}
/* -----------------------------
* Form handling
* ----------------------------- */
function wireCommentForm() {
const form = document.getElementById("comment-form");
form.addEventListener("submit", async event => {
event.preventDefault();
const authorInput = form.querySelector("input[name='author']");
const textarea = form.querySelector("textarea[name='content']");
const author = authorInput.value.trim();
const content = textarea.value.trim();
if (!content) return;
const ok = await postComment(author, content);
if (ok) {
textarea.value = "";
loadComments();
} else {
alert("Failed to post comment");
}
});
}
/* -----------------------------
* Entry point
* ----------------------------- */
async function loadComments() {
const comments = await fetchComments();
renderComments(comments);
}
function initComments() {
const section = document.getElementById("comments");
if (!section) return;
pageSlug = section.dataset.slug;
wireCommentForm();
loadComments();
}
initComments();