Build system with zip download
This commit is contained in:
81
src/webui/js/build.js
Normal file
81
src/webui/js/build.js
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Show a toast notification.
|
||||
* @param {string} message - The message to display.
|
||||
* @param {string} type - "success" or "error".
|
||||
* @param {number} duration - Duration in ms.
|
||||
*/
|
||||
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");
|
||||
setTimeout(() => container.removeChild(toast), 300);
|
||||
}, duration);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Get build button and modal elements
|
||||
const buildBtn = document.getElementById("build-btn");
|
||||
const buildModal = document.getElementById("build-success-modal");
|
||||
const buildModalClose = document.getElementById("build-success-modal-close");
|
||||
const downloadZipBtn = document.getElementById("download-zip-btn");
|
||||
const zipLoader = document.getElementById("zip-loader");
|
||||
|
||||
// Handle build button click
|
||||
if (buildBtn) {
|
||||
buildBtn.addEventListener("click", async () => {
|
||||
// Trigger build on backend
|
||||
const res = await fetch("/api/build", { method: "POST" });
|
||||
const result = await res.json();
|
||||
if (result.status === "ok") {
|
||||
// Show build success modal
|
||||
if (buildModal) buildModal.style.display = "flex";
|
||||
} else {
|
||||
showToast(result.message || "❌ Build failed!", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle download zip button click
|
||||
if (downloadZipBtn) {
|
||||
downloadZipBtn.addEventListener("click", async () => {
|
||||
if (zipLoader) zipLoader.style.display = "block";
|
||||
downloadZipBtn.disabled = true;
|
||||
|
||||
// Request zip creation and download from backend
|
||||
const res = await fetch("/download-output-zip", { method: "POST" });
|
||||
if (res.ok) {
|
||||
const blob = await res.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "site_output.zip";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
} else {
|
||||
showToast("❌ Error creating ZIP", "error");
|
||||
}
|
||||
if (zipLoader) zipLoader.style.display = "none";
|
||||
downloadZipBtn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Modal close logic
|
||||
if (buildModal && buildModalClose) {
|
||||
buildModalClose.onclick = () => {
|
||||
buildModal.style.display = "none";
|
||||
};
|
||||
window.onclick = function(event) {
|
||||
if (event.target === buildModal) {
|
||||
buildModal.style.display = "none";
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
@ -63,7 +63,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
div.style.gap = "8px";
|
||||
div.style.marginBottom = "6px";
|
||||
div.innerHTML = `
|
||||
<textarea placeholder="Paragraph" style="flex:1;" data-idx="${idx}">${item.paragraph || ""}</textarea>
|
||||
<textarea placeholder="Paragraph" required style="flex:1;" data-idx="${idx}">${item.paragraph || ""}</textarea>
|
||||
<button type="button" class="remove-ip-paragraph" data-idx="${idx}">🗑</button>
|
||||
`;
|
||||
ipList.appendChild(div);
|
||||
@ -129,9 +129,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
if (result.status === "ok") {
|
||||
if (thumbnailInput) thumbnailInput.value = result.filename;
|
||||
updateThumbnailPreview(`/photos/${result.filename}?t=${Date.now()}`);
|
||||
showToast("Thumbnail uploaded!", "success");
|
||||
showToast("✅ Thumbnail uploaded!", "success");
|
||||
} else {
|
||||
showToast("Error uploading thumbnail", "error");
|
||||
showToast("❌ Error uploading thumbnail", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -159,9 +159,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
if (result.status === "ok") {
|
||||
if (thumbnailInput) thumbnailInput.value = "";
|
||||
updateThumbnailPreview("");
|
||||
showToast("Thumbnail removed!", "success");
|
||||
showToast("✅ Thumbnail removed!", "success");
|
||||
} else {
|
||||
showToast("Error removing thumbnail", "error");
|
||||
showToast("❌ Error removing thumbnail", "error");
|
||||
}
|
||||
deleteModal.style.display = "none";
|
||||
};
|
||||
@ -182,7 +182,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const res = await fetch("/api/theme/upload", { method: "POST", body: formData });
|
||||
const result = await res.json();
|
||||
if (result.status === "ok") {
|
||||
showToast("Theme uploaded!", "success");
|
||||
showToast("✅ Theme uploaded!", "success");
|
||||
// Refresh theme select after upload
|
||||
fetch("/api/themes")
|
||||
.then(res => res.json())
|
||||
@ -196,7 +196,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
showToast("Error uploading theme", "error");
|
||||
showToast("❌ Error uploading theme", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -311,6 +311,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
updateMenuItemsFromInputs();
|
||||
updateIpParagraphsFromInputs();
|
||||
|
||||
// Check if thumbnail is set before saving (uploaded or present in input)
|
||||
if (!thumbnailInput || !thumbnailInput.value) {
|
||||
showToast("❌ Thumbnail is required.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const build = {
|
||||
theme: themeSelect ? themeSelect.value : "",
|
||||
convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
|
||||
|
Reference in New Issue
Block a user