163 lines
5.2 KiB
JavaScript
163 lines
5.2 KiB
JavaScript
(() => {
|
|
const form = document.querySelector('[data-command-form]');
|
|
const list = document.querySelector('[data-command-list]');
|
|
if (!form) return;
|
|
|
|
const idInput = form.querySelector('input[name="id"]');
|
|
const labelInput = form.querySelector('input[name="label"]');
|
|
const commandInput = form.querySelector('textarea[name="command"]');
|
|
const timeoutInput = form.querySelector('input[name="timeout_sec"]');
|
|
const adminInput = form.querySelector('input[name="admin_only"]');
|
|
const submitBtn = form.querySelector('[data-command-submit]');
|
|
const cancelBtn = form.querySelector('[data-command-cancel]');
|
|
const modal = document.querySelector('[data-command-modal]');
|
|
const modalTitle = document.querySelector('[data-command-modal-title]');
|
|
const closeBtn = document.querySelector('[data-command-close]');
|
|
const newBtn = document.querySelector('[data-command-new]');
|
|
const unsavedBar = document.querySelector('[data-command-unsaved]');
|
|
const discardBtn = document.querySelector('[data-command-discard]');
|
|
|
|
let initialSnapshot = '';
|
|
|
|
const resetForm = () => {
|
|
if (idInput) idInput.value = '';
|
|
if (labelInput) labelInput.value = '';
|
|
if (commandInput) commandInput.value = '';
|
|
if (timeoutInput) timeoutInput.value = '';
|
|
if (adminInput) adminInput.checked = false;
|
|
if (submitBtn) submitBtn.textContent = 'Speichern';
|
|
if (unsavedBar) unsavedBar.style.display = 'none';
|
|
};
|
|
|
|
const snapshot = () => {
|
|
return JSON.stringify({
|
|
id: idInput ? idInput.value : '',
|
|
label: labelInput ? labelInput.value : '',
|
|
command: commandInput ? commandInput.value : '',
|
|
timeout: timeoutInput ? timeoutInput.value : '',
|
|
admin: adminInput ? adminInput.checked : false,
|
|
});
|
|
};
|
|
|
|
const isDirty = () => snapshot() !== initialSnapshot;
|
|
|
|
const openModal = () => {
|
|
if (!modal) return;
|
|
modal.classList.add('is-open');
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
initialSnapshot = snapshot();
|
|
};
|
|
|
|
const closeModal = (force = false) => {
|
|
if (!modal) return;
|
|
if (!force && isDirty()) {
|
|
if (unsavedBar) unsavedBar.style.display = 'flex';
|
|
return;
|
|
}
|
|
modal.classList.remove('is-open');
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
if (unsavedBar) unsavedBar.style.display = 'none';
|
|
};
|
|
|
|
document.querySelectorAll('[data-command-edit]').forEach((btn) => {
|
|
btn.addEventListener('click', () => {
|
|
const item = btn.closest('.command-item');
|
|
if (!item) return;
|
|
if (idInput) idInput.value = item.dataset.commandId || '';
|
|
if (labelInput) labelInput.value = item.dataset.label || '';
|
|
if (commandInput) commandInput.value = item.dataset.command || '';
|
|
if (timeoutInput) timeoutInput.value = item.dataset.timeout || '';
|
|
if (adminInput) adminInput.checked = item.dataset.admin === '1';
|
|
if (submitBtn) submitBtn.textContent = 'Aktualisieren';
|
|
if (modalTitle) modalTitle.textContent = 'Befehl bearbeiten';
|
|
const details = btn.closest('details');
|
|
if (details) details.removeAttribute('open');
|
|
openModal();
|
|
});
|
|
});
|
|
|
|
if (cancelBtn) {
|
|
cancelBtn.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
resetForm();
|
|
});
|
|
}
|
|
|
|
if (newBtn) {
|
|
newBtn.addEventListener('click', () => {
|
|
resetForm();
|
|
if (modalTitle) modalTitle.textContent = 'Neuer Befehl';
|
|
openModal();
|
|
});
|
|
}
|
|
|
|
if (closeBtn) {
|
|
closeBtn.addEventListener('click', () => closeModal(false));
|
|
}
|
|
|
|
if (discardBtn) {
|
|
discardBtn.addEventListener('click', () => {
|
|
resetForm();
|
|
closeModal(true);
|
|
});
|
|
}
|
|
|
|
form.addEventListener('input', () => {
|
|
if (unsavedBar && isDirty()) {
|
|
unsavedBar.style.display = 'flex';
|
|
}
|
|
});
|
|
|
|
if (!list) return;
|
|
|
|
let dragging = null;
|
|
|
|
list.querySelectorAll('.command-item').forEach((item) => {
|
|
item.addEventListener('dragstart', () => {
|
|
dragging = item;
|
|
item.classList.add('is-dragging');
|
|
});
|
|
item.addEventListener('dragend', () => {
|
|
item.classList.remove('is-dragging');
|
|
dragging = null;
|
|
saveOrder();
|
|
});
|
|
});
|
|
|
|
list.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
if (!dragging) return;
|
|
const after = getDragAfterElement(list, e.clientY);
|
|
if (after == null) {
|
|
list.appendChild(dragging);
|
|
} else if (after !== dragging) {
|
|
list.insertBefore(dragging, after);
|
|
}
|
|
});
|
|
|
|
const getDragAfterElement = (container, y) => {
|
|
const elements = [...container.querySelectorAll('.command-item:not(.is-dragging)')];
|
|
return elements.reduce(
|
|
(closest, child) => {
|
|
const box = child.getBoundingClientRect();
|
|
const offset = y - box.top - box.height / 2;
|
|
if (offset < 0 && offset > closest.offset) {
|
|
return { offset, element: child };
|
|
}
|
|
return closest;
|
|
},
|
|
{ offset: Number.NEGATIVE_INFINITY, element: null }
|
|
).element;
|
|
};
|
|
|
|
const saveOrder = () => {
|
|
const order = [...list.querySelectorAll('.command-item')].map((el) => el.dataset.commandId);
|
|
fetch(window.location.pathname + '?reorder_json=1', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ order }),
|
|
cache: 'no-store',
|
|
}).catch(() => {});
|
|
};
|
|
})();
|