Enhance toast notifications and improve settings form submission; implement async handling and error reporting for better user feedback
This commit is contained in:
parent
5a3176cad1
commit
398800b681
3 changed files with 71 additions and 21 deletions
|
@ -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 = `
|
||||||
|
<div id="${toastId}" class="toast align-items-center text-bg-${type}" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="toast-body">
|
||||||
|
${message}
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 = (() => {
|
const ComboBoxWidget = (() => {
|
||||||
let tempIdCounter = 1;
|
let tempIdCounter = 1;
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,9 @@
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Toast Container -->
|
||||||
|
<div id="toast-container" class="toast-container position-fixed bottom-0 end-0 p-3"></div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js"
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js"
|
||||||
integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO"
|
integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
|
|
|
@ -221,33 +221,36 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
form.addEventListener('submit', (event) => {
|
form.addEventListener('submit', async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
try {
|
try {
|
||||||
const state = buildFormState();
|
const state = buildFormState();
|
||||||
fetch('/api/settings', {
|
|
||||||
|
const response = await fetch('/api/settings', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(state)
|
body: JSON.stringify(state)
|
||||||
})
|
});
|
||||||
.then(response => {
|
|
||||||
|
const contentType = response.headers.get("content-type");
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return response.json().then(data => {
|
if (contentType && contentType.includes("application/json")) {
|
||||||
|
const data = await response.json();
|
||||||
throw new Error(data.errors?.join("\n") || "Unknown error");
|
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));
|
||||||
}
|
}
|
||||||
return response.json();
|
}
|
||||||
})
|
|
||||||
.then(data => {
|
const data = await response.json();
|
||||||
console.log("Sync result:", data);
|
console.log("Sync result:", data);
|
||||||
})
|
renderToast({ message: 'Settings updated successfully.', type: 'success' });
|
||||||
.catch(err => {
|
|
||||||
console.error("Submission error:", err);
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to build form state:", err);
|
console.error("Submission error:", err);
|
||||||
|
renderToast({ message: `Failed to update settings, ${err}`, type: 'danger' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue