fix loginpage error message /display onyl once

This commit is contained in:
GeNii96 2026-06-02 11:18:19 +02:00
parent 7a347b093e
commit 6df5485707

View File

@ -1,95 +1,111 @@
import './linq.js' import "./linq.js";
import { Template } from './template.js'; import { Template } from "./template.js";
export function formToObject(form) { export function formToObject(form) {
const data = new FormData(form); const data = new FormData(form);
const object = {}; const object = {};
data.forEach((value, key) => { data.forEach((value, key) => {
setNestedValue(object, key, value); setNestedValue(object, key, value);
}); });
return object; return object;
} }
function setNestedValue(obj, path, value) { function setNestedValue(obj, path, value) {
path.split('.').asEnumerable() path
.isLast() .split(".")
.forEach((key, isLast) => { .asEnumerable()
if (isLast) { .isLast()
obj[key] = value; .forEach((key, isLast) => {
} if (isLast) {
else { obj[key] = value;
if (!obj[key]) { } else {
obj[key] = {}; if (!obj[key]) {
} obj[key] = {};
}
obj = obj[key]; obj = obj[key];
} }
}); });
} }
export async function sendFormAsync(form, url, method) { export async function sendFormAsync(form, url, method) {
url = url || form.action; url = url || form.action;
method = method || form.method || 'post'; method = method || form.method || "post";
const data = formToObject(form); const data = formToObject(form);
const response = await sendJsonAsync(url, data, method); const response = await sendJsonAsync(url, data, method);
if (response.ok && response.redirected) { if (response.ok && response.redirected) {
window.location.href = response.url; window.location.href = response.url;
return null; return null;
} }
const responseText = await response.text(); const responseText = await response.text();
if (response.ok == false && handleValidationError(response, responseText, form)) { if (
return null; response.ok == false &&
} handleValidationError(response, responseText, form)
) {
return null;
}
if (response.ok == false) { if (response.ok == false) {
handleGenericFormError(response, responseText, form); handleGenericFormError(response, responseText, form);
return null; return null;
} else { } else {
return responseText.length == 0 ? null : JSON.parse(responseText); return responseText.length == 0 ? null : JSON.parse(responseText);
} }
} }
export async function sendJsonAsync(url, data, method = 'post') { export async function sendJsonAsync(url, data, method = "post") {
const response = await fetch(url, { const response = await fetch(url, {
method: method.toUpperCase(), method: method.toUpperCase(),
headers: { headers: {
'Content-Type': 'application/json' "Content-Type": "application/json",
}, },
body: JSON.stringify(data) body: JSON.stringify(data),
}); });
return response; return response;
} }
export async function postAndRenderAsync(url, data, template, targetElement) { export async function postAndRenderAsync(url, data, template, targetElement) {
const response = await sendJsonAsync(url, data); const response = await sendJsonAsync(url, data);
if (response.ok) { if (response.ok) {
const responseText = await response.text(); const responseText = await response.text();
targetElement.innerHTML = template.render(responseText.length == 0 ? undefined : JSON.parse(responseText)); targetElement.innerHTML = template.render(
} responseText.length == 0 ? undefined : JSON.parse(responseText),
);
}
} }
export async function postFormAndRenderAsync(url, form, template, targetElement) { export async function postFormAndRenderAsync(
const object = formToObject(form); url,
const data = await postFormAsync(url, object, template, targetElement); form,
if (data) { template,
targetElement.innerHTML = template.render(data); targetElement,
} ) {
const object = formToObject(form);
const data = await postFormAsync(url, object, template, targetElement);
if (data) {
targetElement.innerHTML = template.render(data);
}
} }
const genericFormErrorTemplate = new Template(` const genericFormErrorTemplate = new Template(`
<div class="form-error"> <div class="form-error">
An error occurred while submitting the form. Please try again later. {{ $this }}
{{ $this }}
</div> </div>
`); `);
function handleGenericFormError(response, responseText, form) { function handleGenericFormError(response, responseText, form) {
if (!response.ok) { if (!response.ok) {
const html = genericFormErrorTemplate.render(responseText); // Remove all existing form-level errors before adding a new one
form.insertAdjacentHTML('beforeend', html); form.querySelectorAll(":scope > .form-error").forEach((el) => el.remove());
} let message = responseText;
try {
message = JSON.parse(responseText);
} catch (_) {}
const html = genericFormErrorTemplate.render(message);
form.insertAdjacentHTML("beforeend", html);
}
} }
const validationErrorTemplate = new Template(` const validationErrorTemplate = new Template(`
@ -116,45 +132,49 @@ const unknownInputErrorTemplate = new Template(`
`); `);
function toCamelCase(str) { function toCamelCase(str) {
str = str.replace(/([-_][a-z])/gi, (match) => { str = str.replace(/([-_][a-z])/gi, (match) => {
return match.toUpperCase() return match.toUpperCase().replace("-", "").replace("_", "");
.replace('-', '') });
.replace('_', '');
});
str = str[0].toLowerCase() + str.substring(1); str = str[0].toLowerCase() + str.substring(1);
return str; return str;
} }
function handleValidationError(response, responseText, form) { function handleValidationError(response, responseText, form) {
if (response.status !== 400) return false; if (response.status !== 400) return false;
const responseObject = JSON.parse(responseText); const responseObject = JSON.parse(responseText);
const unknownInputErrors = {}; const unknownInputErrors = {};
if (responseObject.type === 'https://tools.ietf.org/html/rfc9110#section-15.5.1' && responseObject.errors) { if (
for (const [field, messages] of Object.entries(responseObject.errors)) { responseObject.type ===
const input = form.querySelector(`[name="${toCamelCase(field)}"]`); "https://tools.ietf.org/html/rfc9110#section-15.5.1" &&
if (input) { responseObject.errors
const parent = input.parentElement; ) {
const errorHtml = validationErrorTemplate.render(messages); for (const [field, messages] of Object.entries(responseObject.errors)) {
let errorContainer = parent.querySelector('.form-error'); // Check if an error container already exists const input = form.querySelector(`[name="${toCamelCase(field)}"]`);
if (errorContainer) { if (input) {
errorContainer.outerHTML = errorHtml; // Replace existing error container const parent = input.parentElement;
} else { const errorHtml = validationErrorTemplate.render(messages);
parent.insertAdjacentHTML('beforeend', errorHtml); let errorContainer = parent.querySelector(".form-error"); // Check if an error container already exists
} if (errorContainer) {
} else { errorContainer.outerHTML = errorHtml; // Replace existing error container
unknownInputErrors[field] = messages; } else {
} parent.insertAdjacentHTML("beforeend", errorHtml);
} }
} else {
if (Object.keys(unknownInputErrors).length > 0) { unknownInputErrors[field] = messages;
const html = unknownInputErrorTemplate.render(unknownInputErrors); }
form.insertAdjacentHTML('beforeend', html);
}
return true;
} }
return false; if (Object.keys(unknownInputErrors).length > 0) {
} form
.querySelectorAll(":scope > .form-error")
.forEach((el) => el.remove());
const html = unknownInputErrorTemplate.render(unknownInputErrors);
form.insertAdjacentHTML("beforeend", html);
}
return true;
}
return false;
}