Better UI form
This commit is contained in:
@ -1,6 +1,19 @@
|
||||
function showToast(message, type = "success", duration = 3000) {
|
||||
const container = document.getElementById("toast-container");
|
||||
if (!container) return;
|
||||
const toast = document.createElement("div");
|
||||
toast.className = `toast ${type}`;
|
||||
toast.textContent = message;
|
||||
container.appendChild(toast);
|
||||
requestAnimationFrame(() => toast.classList.add("show"));
|
||||
setTimeout(() => {
|
||||
toast.classList.remove("show");
|
||||
toast.addEventListener("transitionend", () => toast.remove());
|
||||
}, duration);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const form = document.getElementById("site-info-form");
|
||||
const status = document.getElementById("site-info-status");
|
||||
const menuList = document.getElementById("menu-items-list");
|
||||
const addMenuBtn = document.getElementById("add-menu-item");
|
||||
|
||||
@ -45,7 +58,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
div.style.gap = "8px";
|
||||
div.style.marginBottom = "6px";
|
||||
div.innerHTML = `
|
||||
<input type="text" placeholder="Paragraph" value="${item.paragraph || ""}" style="flex:1;" data-idx="${idx}">
|
||||
<textarea placeholder="Paragraph" style="flex:1;" data-idx="${idx}">${item.paragraph || ""}</textarea>
|
||||
<button type="button" class="remove-ip-paragraph" data-idx="${idx}">🗑</button>
|
||||
`;
|
||||
ipList.appendChild(div);
|
||||
@ -53,9 +66,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
|
||||
function updateIpParagraphsFromInputs() {
|
||||
const inputs = ipList.querySelectorAll("input");
|
||||
ipParagraphs = Array.from(inputs).map(input => ({
|
||||
paragraph: input.value.trim()
|
||||
const textareas = ipList.querySelectorAll("textarea");
|
||||
ipParagraphs = Array.from(textareas).map(textarea => ({
|
||||
paragraph: textarea.value.trim()
|
||||
})).filter(item => item.paragraph !== "");
|
||||
}
|
||||
|
||||
@ -66,10 +79,36 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
// --- Theme select ---
|
||||
const themeSelect = document.getElementById("theme-select");
|
||||
|
||||
// --- Thumbnail upload ---
|
||||
// --- Thumbnail upload & modal logic ---
|
||||
const thumbnailInput = form?.elements["social.thumbnail"];
|
||||
const thumbnailUpload = document.getElementById("thumbnail-upload");
|
||||
const chooseThumbnailBtn = document.getElementById("choose-thumbnail-btn");
|
||||
const thumbnailPreview = document.getElementById("thumbnail-preview");
|
||||
const removeThumbnailBtn = document.getElementById("remove-thumbnail-btn");
|
||||
|
||||
// Modal elements
|
||||
const deleteModal = document.getElementById("delete-modal");
|
||||
const deleteModalClose = document.getElementById("delete-modal-close");
|
||||
const deleteModalConfirm = document.getElementById("delete-modal-confirm");
|
||||
const deleteModalCancel = document.getElementById("delete-modal-cancel");
|
||||
|
||||
// Show/hide remove button, preview, and choose button
|
||||
function updateThumbnailPreview(src) {
|
||||
if (thumbnailPreview) {
|
||||
thumbnailPreview.src = src || "";
|
||||
thumbnailPreview.style.display = src ? "block" : "none";
|
||||
}
|
||||
if (removeThumbnailBtn) {
|
||||
removeThumbnailBtn.style.display = src ? "inline-block" : "none";
|
||||
}
|
||||
if (chooseThumbnailBtn) {
|
||||
chooseThumbnailBtn.style.display = src ? "none" : "inline-block";
|
||||
}
|
||||
}
|
||||
|
||||
if (chooseThumbnailBtn && thumbnailUpload) {
|
||||
chooseThumbnailBtn.addEventListener("click", () => thumbnailUpload.click());
|
||||
}
|
||||
|
||||
if (thumbnailUpload) {
|
||||
thumbnailUpload.addEventListener("change", async (e) => {
|
||||
@ -81,19 +120,45 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const result = await res.json();
|
||||
if (result.status === "ok") {
|
||||
if (thumbnailInput) thumbnailInput.value = result.filename;
|
||||
if (thumbnailPreview) {
|
||||
thumbnailPreview.src = `/photos/${result.filename}`;
|
||||
thumbnailPreview.style.display = "block";
|
||||
}
|
||||
status.textContent = "✅ Thumbnail uploaded!";
|
||||
setTimeout(() => status.textContent = "", 2000);
|
||||
updateThumbnailPreview(`/photos/${result.filename}`);
|
||||
showToast("Thumbnail uploaded!", "success");
|
||||
} else {
|
||||
status.textContent = "❌ Error uploading thumbnail";
|
||||
setTimeout(() => status.textContent = "", 2000);
|
||||
showToast("Error uploading thumbnail", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Attach modal logic to remove button
|
||||
if (removeThumbnailBtn) {
|
||||
removeThumbnailBtn.addEventListener("click", () => {
|
||||
deleteModal.style.display = "flex";
|
||||
});
|
||||
}
|
||||
|
||||
// Modal logic
|
||||
if (deleteModal && deleteModalClose && deleteModalConfirm && deleteModalCancel) {
|
||||
deleteModalClose.onclick = deleteModalCancel.onclick = () => {
|
||||
deleteModal.style.display = "none";
|
||||
};
|
||||
window.onclick = function(event) {
|
||||
if (event.target === deleteModal) {
|
||||
deleteModal.style.display = "none";
|
||||
}
|
||||
};
|
||||
deleteModalConfirm.onclick = async () => {
|
||||
const res = await fetch("/api/thumbnail/remove", { method: "POST" });
|
||||
const result = await res.json();
|
||||
if (result.status === "ok") {
|
||||
if (thumbnailInput) thumbnailInput.value = "";
|
||||
updateThumbnailPreview("");
|
||||
showToast("Thumbnail removed!", "success");
|
||||
} else {
|
||||
showToast("Error removing thumbnail", "error");
|
||||
}
|
||||
deleteModal.style.display = "none";
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch theme list and populate select
|
||||
if (themeSelect) {
|
||||
fetch("/api/themes")
|
||||
@ -134,10 +199,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
form.elements["info.author"].value = data.info?.author || "";
|
||||
form.elements["social.instagram_url"].value = data.social?.instagram_url || "";
|
||||
if (thumbnailInput) thumbnailInput.value = data.social?.thumbnail || "";
|
||||
if (thumbnailPreview && data.social?.thumbnail) {
|
||||
thumbnailPreview.src = `/photos/${data.social.thumbnail}`;
|
||||
thumbnailPreview.style.display = "block";
|
||||
}
|
||||
updateThumbnailPreview(data.social?.thumbnail ? `/photos/${data.social.thumbnail}` : "");
|
||||
form.elements["footer.copyright"].value = data.footer?.copyright || "";
|
||||
form.elements["footer.legal_label"].value = data.footer?.legal_label || "";
|
||||
if (themeSelect) {
|
||||
@ -207,7 +269,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
updateMenuItemsFromInputs();
|
||||
updateIpParagraphsFromInputs();
|
||||
|
||||
// --- Build object with checkboxes and theme select ---
|
||||
const build = {
|
||||
theme: themeSelect ? themeSelect.value : "",
|
||||
convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
|
||||
@ -248,8 +309,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const result = await res.json();
|
||||
status.textContent = result.status === "ok" ? "✅ Saved!" : "❌ Error saving";
|
||||
setTimeout(() => status.textContent = "", 2000);
|
||||
if (result.status === "ok") {
|
||||
showToast("Site info saved!", "success");
|
||||
} else {
|
||||
showToast("Error saving site info", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user