mirror of
https://forge.dns-witch.net/dns-witch/nomilo.git
synced 2026-06-25 09:42:20 +02:00
153 lines
5 KiB
JavaScript
153 lines
5 KiB
JavaScript
function serviceName(formData) {
|
|
const serviceType = formData.get('service_type');
|
|
|
|
if (serviceType == 'other') {
|
|
return formData.get('service_name') + '/' + formData.get('service_protocol');
|
|
} else {
|
|
return document.querySelector(`option[value="${serviceType}"]`).innerText;
|
|
}
|
|
}
|
|
|
|
async function insertValidationErrors(form, errorList) {
|
|
const errors = new Map()
|
|
for (const error of errorList) {
|
|
const errorsForKey = errors.get(error.path) ?? [];
|
|
errorsForKey.push(error)
|
|
errors.set(error.path, errorsForKey)
|
|
}
|
|
|
|
for (const [key, errorsForKey] of errors) {
|
|
// TODO: support for multiple error keys in dataset
|
|
const input = form.querySelector(`[data-error-keys="${key}"]`);
|
|
|
|
// TODO: support for unmanaged errors
|
|
const otherErrorIds = input.getAttribute('aria-describedby').trim().split(' ');
|
|
for (const errorId of otherErrorIds) {
|
|
if (errorId.length) {
|
|
document.getElementById(errorId)?.remove();
|
|
}
|
|
}
|
|
const errorIds = [];
|
|
for (const errorIndex in errorsForKey) {
|
|
const error = errorsForKey[errorIndex];
|
|
const errorContainer = document.createElement('p');
|
|
errorContainer.classList.add('error');
|
|
errorContainer.id = `${input.id}-error-${errorIndex}`;
|
|
errorIds.push(errorContainer.id)
|
|
errorContainer.innerText = await l10n(input.dataset.errorMessageId + '.error-' + error.code.replaceAll(':', '-'));
|
|
input.insertAdjacentElement('afterend', errorContainer);
|
|
}
|
|
input.setAttribute('aria-describedby', errorIds.join(' '));
|
|
input.setAttribute('aria-invalid', 'true');
|
|
}
|
|
|
|
form.querySelector('[aria-invalid]').focus();
|
|
}
|
|
|
|
function resetErrors(form) {
|
|
for (const errorEl of form.querySelectorAll('p.error')) {
|
|
errorEl.remove();
|
|
}
|
|
|
|
for (const input of form.querySelectorAll('[aria-invalid]')) {
|
|
input.removeAttribute("aria-invalid");
|
|
}
|
|
}
|
|
|
|
async function isFormValid(form) {
|
|
const formData = new FormData(form);
|
|
const errors = await fetch(`${window.location}&validate_only=true`, {
|
|
method: 'POST',
|
|
body: new URLSearchParams(formData.entries()),
|
|
})
|
|
.then(response => response.json())
|
|
.then(errors => errors.filter(error => error.path !== '/services/0/data/service_targets'));
|
|
|
|
if (errors.length > 0) {
|
|
insertValidationErrors(form, errors);
|
|
return false
|
|
}
|
|
|
|
resetErrors(form)
|
|
return true
|
|
}
|
|
|
|
async function addServiceCard(e) {
|
|
e.preventDefault();
|
|
|
|
const form = e.submitter.form;
|
|
const idTemplate = form.dataset.newCard;
|
|
const formData = new FormData(form);
|
|
|
|
if (!(await isFormValid(form))) {
|
|
return
|
|
}
|
|
|
|
const template = document.querySelector(`template[data-new-card-template="${idTemplate}"]`);
|
|
const newCard = document.importNode(template.content, true).firstElementChild ;
|
|
let html = newCard.innerHTML;
|
|
const allCards = document.querySelectorAll('[data-card]');
|
|
|
|
const formVars = new Map([
|
|
['service_type', formData.get('services[0][data][service_type][service_type]')],
|
|
['service_protocol', formData.get('services[0][data][service_type][protocol]')],
|
|
['service_name', formData.get('services[0][data][service_type][name]')],
|
|
]);
|
|
console.log(formVars);
|
|
|
|
const vars = [
|
|
['service_index', allCards.length],
|
|
['service_fullname', serviceName(formVars)],
|
|
...formVars.entries(),
|
|
]
|
|
for (const [key, value] of vars ) {
|
|
html = html.replaceAll(`{${key}}`, value);
|
|
}
|
|
|
|
newCard.innerHTML = html;
|
|
|
|
setupForm(newCard);
|
|
setupDeleteServiceCardButton(form, newCard);
|
|
template.insertAdjacentElement('beforebegin', newCard);
|
|
focusInput(newCard);
|
|
form.reset();
|
|
}
|
|
|
|
function deleteServiceCardButton(addCardForm, button) {
|
|
let card = button.parentElement;
|
|
|
|
while (!('card' in card.dataset)) {
|
|
card = card.parentElement;
|
|
}
|
|
|
|
const allCards = document.querySelectorAll('[data-card]');
|
|
|
|
if (allCards.length === 1) {
|
|
focusInput(addCardForm);
|
|
} else if (card.nextElementSibling && 'card' in card.nextElementSibling.dataset) {
|
|
card.nextElementSibling.querySelector('[data-delete-card]').focus();
|
|
} else {
|
|
card.previousElementSibling.querySelector('[data-delete-card]').focus();
|
|
}
|
|
|
|
card.remove();
|
|
}
|
|
|
|
function setupDeleteServiceCardButton(addCardForm, card) {
|
|
const button = card.querySelector('button[data-delete-card]');
|
|
button.addEventListener('click', () => deleteServiceCardButton(addCardForm, button));
|
|
}
|
|
|
|
function setupServiceForm() {
|
|
const addCardForm = document.querySelector('form[data-new-card="service"]');
|
|
if (addCardForm) {
|
|
addCardForm.addEventListener('submit', addServiceCard);
|
|
|
|
const cards = document.querySelectorAll('[data-card]');
|
|
for (const card of cards) {
|
|
setupDeleteServiceCardButton(addCardForm, card);
|
|
}
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', setupServiceForm);
|