From b74f1bb3505940fdb312c483708c9a357e1ca50b Mon Sep 17 00:00:00 2001 From: Djeex Date: Sun, 17 Aug 2025 13:52:25 +0200 Subject: [PATCH] Site-info front --- config/site.yaml | 2 + src/py/webui/webui.py | 22 ++++ src/webui/js/site-info.js | 197 +++++++++++++++++++++++++++++++++ src/webui/site-info/index.html | 109 ++++++++++++++++++ 4 files changed, 330 insertions(+) create mode 100644 src/webui/js/site-info.js create mode 100644 src/webui/site-info/index.html diff --git a/config/site.yaml b/config/site.yaml index b3211f4..166b129 100644 --- a/config/site.yaml +++ b/config/site.yaml @@ -24,6 +24,8 @@ footer: # 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 legals: diff --git a/src/py/webui/webui.py b/src/py/webui/webui.py index ea0bcb6..b40b126 100644 --- a/src/py/webui/webui.py +++ b/src/py/webui/webui.py @@ -1,4 +1,5 @@ import logging +import yaml from pathlib import Path from flask import Flask, jsonify, request, send_from_directory, render_template from src.py.builder.gallery_builder import ( @@ -18,6 +19,8 @@ app = Flask( static_url_path="" ) +SITE_YAML = Path(__file__).resolve().parents[3] / "config" / "site.yaml" + # --- Photos directory (configurable) --- PHOTOS_DIR = Path(__file__).resolve().parents[3] / "config" / "photos" app.config["PHOTOS_DIR"] = PHOTOS_DIR @@ -133,6 +136,25 @@ def photos(section, filename): """Serve uploaded photos from disk.""" return send_from_directory(PHOTOS_DIR / section, filename) + +@app.route("/site-info") +def site_info(): + return render_template("site-info/index.html") + +@app.route("/api/site-info", methods=["GET"]) +def get_site_info(): + with open(SITE_YAML, "r") as f: + data = yaml.safe_load(f) + return jsonify(data) + +@app.route("/api/site-info", methods=["POST"]) +def update_site_info(): + data = request.json + 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 new file mode 100644 index 0000000..3ce842c --- /dev/null +++ b/src/webui/js/site-info.js @@ -0,0 +1,197 @@ +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"); + + let menuItems = []; + + function renderMenuItems() { + menuList.innerHTML = ""; + menuItems.forEach((item, idx) => { + const div = document.createElement("div"); + div.style.display = "flex"; + div.style.gap = "8px"; + div.style.marginBottom = "6px"; + div.innerHTML = ` + + + + `; + menuList.appendChild(div); + }); + } + + function updateMenuItemsFromInputs() { + const inputs = menuList.querySelectorAll("input"); + const items = []; + for (let i = 0; i < inputs.length; i += 2) { + const label = inputs[i].value.trim(); + const href = inputs[i + 1].value.trim(); + if (label || href) items.push({ label, href }); + } + menuItems = items; + } + + const ipList = document.getElementById("ip-list"); + const addIpBtn = document.getElementById("add-ip-paragraph"); + let ipParagraphs = []; + + function renderIpParagraphs() { + ipList.innerHTML = ""; + ipParagraphs.forEach((item, idx) => { + const div = document.createElement("div"); + div.style.display = "flex"; + div.style.gap = "8px"; + div.style.marginBottom = "6px"; + div.innerHTML = ` + + + `; + ipList.appendChild(div); + }); + } + + function updateIpParagraphsFromInputs() { + const inputs = ipList.querySelectorAll("input"); + ipParagraphs = Array.from(inputs).map(input => ({ + paragraph: input.value.trim() + })).filter(item => item.paragraph !== ""); + } + + // --- Build checkboxes --- + const convertImagesCheckbox = document.getElementById("convert-images-checkbox"); + const resizeImagesCheckbox = document.getElementById("resize-images-checkbox"); + + // Load config + if (form) { + fetch("/api/site-info") + .then(res => res.json()) + .then(data => { + ipParagraphs = Array.isArray(data.legals?.intellectual_property) + ? data.legals.intellectual_property + : []; + renderIpParagraphs(); + menuItems = Array.isArray(data.menu?.items) ? data.menu.items : []; + renderMenuItems(); + form.elements["info.title"].value = data.info?.title || ""; + form.elements["info.subtitle"].value = data.info?.subtitle || ""; + form.elements["info.description"].value = data.info?.description || ""; + form.elements["info.canonical"].value = data.info?.canonical || ""; + form.elements["info.keywords"].value = Array.isArray(data.info?.keywords) ? data.info.keywords.join(", ") : (data.info?.keywords || ""); + form.elements["info.author"].value = data.info?.author || ""; + form.elements["social.instagram_url"].value = data.social?.instagram_url || ""; + form.elements["social.thumbnail"].value = data.social?.thumbnail || ""; + form.elements["footer.copyright"].value = data.footer?.copyright || ""; + form.elements["footer.legal_label"].value = data.footer?.legal_label || ""; + form.elements["build.theme"].value = data.build?.theme || ""; + form.elements["legals.hoster_name"].value = data.legals?.hoster_name || ""; + form.elements["legals.hoster_adress"].value = data.legals?.hoster_adress || ""; + form.elements["legals.hoster_contact"].value = data.legals?.hoster_contact || ""; + // --- Build checkboxes --- + if (convertImagesCheckbox) { + convertImagesCheckbox.checked = !!data.build?.convert_images; + } + if (resizeImagesCheckbox) { + resizeImagesCheckbox.checked = !!data.build?.resize_images; + } + }); + } + + // Add menu item + if (addMenuBtn) { + addMenuBtn.addEventListener("click", () => { + menuItems.push({ label: "", href: "" }); + renderMenuItems(); + }); + } + + // Remove menu item + menuList.addEventListener("click", (e) => { + if (e.target.classList.contains("remove-menu-item")) { + const idx = parseInt(e.target.getAttribute("data-idx")); + menuItems.splice(idx, 1); + renderMenuItems(); + } + }); + + // Update menuItems on input change + menuList.addEventListener("input", () => { + updateMenuItemsFromInputs(); + }); + + // Add paragraph + if (addIpBtn) { + addIpBtn.addEventListener("click", () => { + ipParagraphs.push({ paragraph: "" }); + renderIpParagraphs(); + }); + } + + // Remove paragraph + ipList.addEventListener("click", (e) => { + if (e.target.classList.contains("remove-ip-paragraph")) { + const idx = parseInt(e.target.getAttribute("data-idx")); + ipParagraphs.splice(idx, 1); + renderIpParagraphs(); + } + }); + + // Update ipParagraphs on input change + ipList.addEventListener("input", () => { + updateIpParagraphsFromInputs(); + }); + + // Save config + if (form) { + form.addEventListener("submit", async (e) => { + e.preventDefault(); + updateMenuItemsFromInputs(); + updateIpParagraphsFromInputs(); + + // --- Build object with checkboxes --- + const build = { + theme: form.elements["build.theme"].value, + convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked), + resize_images: !!(resizeImagesCheckbox && resizeImagesCheckbox.checked) + }; + + const payload = { + info: { + title: form.elements["info.title"].value, + subtitle: form.elements["info.subtitle"].value, + description: form.elements["info.description"].value, + canonical: form.elements["info.canonical"].value, + keywords: form.elements["info.keywords"].value.split(",").map(i => i.trim()).filter(Boolean), + author: form.elements["info.author"].value + }, + social: { + instagram_url: form.elements["social.instagram_url"].value, + thumbnail: form.elements["social.thumbnail"].value + }, + menu: { + items: menuItems + }, + footer: { + copyright: form.elements["footer.copyright"].value, + legal_label: form.elements["footer.legal_label"].value + }, + build, + legals: { + hoster_name: form.elements["legals.hoster_name"].value, + hoster_adress: form.elements["legals.hoster_adress"].value, + hoster_contact: form.elements["legals.hoster_contact"].value, + intellectual_property: ipParagraphs + } + }; + const res = await fetch("/api/site-info", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload) + }); + const result = await res.json(); + status.textContent = result.status === "ok" ? "✅ Saved!" : "❌ Error saving"; + setTimeout(() => status.textContent = "", 2000); + }); + } +}); \ No newline at end of file diff --git a/src/webui/site-info/index.html b/src/webui/site-info/index.html new file mode 100644 index 0000000..58ecae2 --- /dev/null +++ b/src/webui/site-info/index.html @@ -0,0 +1,109 @@ + + + + + + Lumeex + + + + + + +
+
+

Edit Site Info

+
+
+ Info +
+
+
+
+
+
+
+
+ Social +
+
+
+
+ Menu + + +
+
+ Footer +
+
+
+
+ Build +
+
+
+
+
+ Legals +
+
+
+
+ +
+ +
+
+ +
+
+
+