diff --git a/config/photos/thumbnail.png b/config/photos/thumbnail.png deleted file mode 100644 index 9626d55..0000000 Binary files a/config/photos/thumbnail.png and /dev/null differ diff --git a/config/site.yaml b/config/site.yaml index 166b129..0e09717 100644 --- a/config/site.yaml +++ b/config/site.yaml @@ -1,36 +1,26 @@ -# Please change this by your settings. info: - title: - subtitle: - description: - canonical: - keywords: - author: - + title: '' + subtitle: '' + description: '' + canonical: '' + keywords: [] + author: '' social: - instagram_url: - thumbnail: - + instagram_url: '' + thumbnail: '' menu: items: - label: Home href: / - footer: copyright: Copyright © 2025 - legal_link: '/legals/' legal_label: Legal notice - -# Build parameters build: - theme: modern # choose a theme in config/theme folder - convert_images: true # true to enable image conversion - resize_images: true # true to enable image resizing - -# Change this by your legals + theme: modern + convert_images: true + resize_images: true legals: - hoster_name: - hoster_adress: - hoster_contact: - intellectual_property: - - paragraph: "" + hoster_name: '' + hoster_adress: '' + hoster_contact: '' + intellectual_property: [] diff --git a/src/py/webui/webui.py b/src/py/webui/webui.py index ae1c554..4565144 100644 --- a/src/py/webui/webui.py +++ b/src/py/webui/webui.py @@ -181,6 +181,22 @@ def upload_thumbnail(): yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True) return jsonify({"status": "ok", "filename": filename}) +@app.route("/api/thumbnail/remove", methods=["POST"]) +def remove_thumbnail(): + PHOTOS_DIR = app.config["PHOTOS_DIR"] + thumbnail_path = PHOTOS_DIR / "thumbnail.png" + # Remove thumbnail file if exists + if thumbnail_path.exists(): + thumbnail_path.unlink() + # Update site.yaml to remove thumbnail key + with open(SITE_YAML, "r") as f: + data = yaml.safe_load(f) + if "social" in data and "thumbnail" in data["social"]: + data["social"]["thumbnail"] = "" + with open(SITE_YAML, "w") as f: + yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True) + return jsonify({"status": "ok"}) + # --- Run server --- if __name__ == "__main__": logging.info("Starting WebUI at http://127.0.0.1:5000") diff --git a/src/webui/js/site-info.js b/src/webui/js/site-info.js index 7e4e9b5..733871f 100644 --- a/src/webui/js/site-info.js +++ b/src/webui/js/site-info.js @@ -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 = ` - + `; 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"); + } }); } }); \ No newline at end of file diff --git a/src/webui/site-info/index.html b/src/webui/site-info/index.html index ae293a0..5fbd7a1 100644 --- a/src/webui/site-info/index.html +++ b/src/webui/site-info/index.html @@ -31,9 +31,9 @@
@@ -43,107 +43,131 @@