diff --git a/static/js/widget.js b/static/js/widget.js index 2c418ed..d44a51d 100644 --- a/static/js/widget.js +++ b/static/js/widget.js @@ -1,3 +1,47 @@ +function renderToast({ message, type = 'info', timeout = 3000 }) { + if (!message) { + console.warn('renderToast was called without a message.'); + return; + } + + // Auto-create the toast container if it doesn't exist + let container = document.getElementById('toast-container'); + if (!container) { + container = document.createElement('div'); + container.id = 'toast-container'; + container.className = 'toast-container position-fixed bottom-0 end-0 p-3'; + document.body.appendChild(container); + } + + const toastId = `toast-${Date.now()}`; + const wrapper = document.createElement('div'); + wrapper.innerHTML = ` + + `; + + const toastElement = wrapper.firstElementChild; + container.appendChild(toastElement); + + const toast = new bootstrap.Toast(toastElement, { delay: timeout }); + toast.show(); + + toastElement.addEventListener('hidden.bs.toast', () => { + toastElement.remove(); + + // Clean up container if empty + if (container.children.length === 0) { + container.remove(); + } + }); +} + const ComboBoxWidget = (() => { let tempIdCounter = 1; diff --git a/templates/layout.html b/templates/layout.html index b71e6a8..7256dde 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -56,6 +56,9 @@ {% block content %}{% endblock %} + +
+ diff --git a/templates/settings.html b/templates/settings.html index bf32165..fcc9202 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -221,33 +221,36 @@ }); }); - form.addEventListener('submit', (event) => { - event.preventDefault(); + form.addEventListener('submit', async (event) => { + event.preventDefault(); try { const state = buildFormState(); - fetch('/api/settings', { + + const response = await fetch('/api/settings', { method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(state) - }) - .then(response => { - if (!response.ok) { - return response.json().then(data => { - throw new Error(data.errors?.join("\n") || "Unknown error"); - }); - } - return response.json(); - }) - .then(data => { - console.log("Sync result:", data); - }) - .catch(err => { - console.error("Submission error:", err); }); + + const contentType = response.headers.get("content-type"); + + if (!response.ok) { + if (contentType && contentType.includes("application/json")) { + const data = await response.json(); + throw new Error(data.errors?.join("\n") || "Unknown error"); + } else { + const text = await response.text(); + throw new Error("Unexpected response:\n" + text.slice(0, 200)); + } + } + + const data = await response.json(); + console.log("Sync result:", data); + renderToast({ message: 'Settings updated successfully.', type: 'success' }); + } catch (err) { - console.error("Failed to build form state:", err); + console.error("Submission error:", err); + renderToast({ message: `Failed to update settings, ${err}`, type: 'danger' }); } });