db3558872e
Modernisiert die öffentliche Oberfläche mit Inter, Dark Mode und Live-Suche, ergänzt KI-gestützte Entwürfe im Admin mit Nachweis-Uploads und dokumentiert Daphne/Nginx für den Serverbetrieb. Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
1.9 KiB
JavaScript
64 lines
1.9 KiB
JavaScript
(function () {
|
|
const DEBOUNCE_MS = 300;
|
|
|
|
function debounce(fn, delay) {
|
|
let timer;
|
|
return function (...args) {
|
|
clearTimeout(timer);
|
|
timer = setTimeout(() => fn.apply(this, args), delay);
|
|
};
|
|
}
|
|
|
|
function buildUrl(form) {
|
|
const params = new URLSearchParams();
|
|
new FormData(form).forEach((value, key) => {
|
|
if (value) {
|
|
params.append(key, value);
|
|
}
|
|
});
|
|
const action = form.getAttribute('action') || window.location.pathname;
|
|
const query = params.toString();
|
|
return query ? action + '?' + query : action;
|
|
}
|
|
|
|
function runSearch(form) {
|
|
const target = document.querySelector(form.dataset.liveTarget);
|
|
if (!target) {
|
|
return;
|
|
}
|
|
const url = buildUrl(form);
|
|
fetch(url, {
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
})
|
|
.then((response) => {
|
|
if (!response.ok) {
|
|
throw new Error('search failed');
|
|
}
|
|
return response.text();
|
|
})
|
|
.then((html) => {
|
|
target.innerHTML = html;
|
|
history.replaceState(null, '', url);
|
|
})
|
|
.catch(() => {});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
document.querySelectorAll('form[data-live-search]').forEach(function (form) {
|
|
const search = debounce(function () {
|
|
runSearch(form);
|
|
}, DEBOUNCE_MS);
|
|
|
|
form.querySelectorAll('input, select').forEach(function (field) {
|
|
field.addEventListener('input', search);
|
|
field.addEventListener('change', search);
|
|
});
|
|
|
|
form.addEventListener('submit', function (event) {
|
|
event.preventDefault();
|
|
runSearch(form);
|
|
});
|
|
});
|
|
});
|
|
})();
|