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' });
}
});