mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 18:45:31 +00:00
+Mor almanach edit, finished
This commit is contained in:
@@ -6,10 +6,20 @@ export class AlmanachEditPage extends HTMLElement {
|
||||
this._pendingAgent = null;
|
||||
this._form = null;
|
||||
this._saveButton = null;
|
||||
this._resetButton = null;
|
||||
this._deleteButton = null;
|
||||
this._deleteDialog = null;
|
||||
this._deleteConfirmButton = null;
|
||||
this._deleteCancelButton = null;
|
||||
this._statusEl = null;
|
||||
this._saveEndpoint = "";
|
||||
this._deleteEndpoint = "";
|
||||
this._isSaving = false;
|
||||
this._handleSaveClick = this._handleSaveClick.bind(this);
|
||||
this._handleResetClick = this._handleResetClick.bind(this);
|
||||
this._handleDeleteClick = this._handleDeleteClick.bind(this);
|
||||
this._handleDeleteConfirmClick = this._handleDeleteConfirmClick.bind(this);
|
||||
this._handleDeleteCancelClick = this._handleDeleteCancelClick.bind(this);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -86,19 +96,60 @@ export class AlmanachEditPage extends HTMLElement {
|
||||
this._teardownSaveHandling();
|
||||
this._form = this.querySelector("#changealmanachform");
|
||||
this._saveButton = this.querySelector("[data-role='almanach-save']");
|
||||
this._resetButton = this.querySelector("[data-role='almanach-reset']");
|
||||
this._deleteButton = this.querySelector("[data-role='almanach-delete']");
|
||||
this._deleteDialog = this.querySelector("[data-role='almanach-delete-dialog']");
|
||||
this._deleteConfirmButton = this.querySelector("[data-role='almanach-delete-confirm']");
|
||||
this._deleteCancelButton = this.querySelector("[data-role='almanach-delete-cancel']");
|
||||
this._statusEl = this.querySelector("#almanach-save-feedback");
|
||||
if (!this._form || !this._saveButton) {
|
||||
return;
|
||||
}
|
||||
this._saveEndpoint = this._form.getAttribute("data-save-endpoint") || this._deriveSaveEndpoint();
|
||||
this._deleteEndpoint = this._form.getAttribute("data-delete-endpoint") || "";
|
||||
this._saveButton.addEventListener("click", this._handleSaveClick);
|
||||
if (this._resetButton) {
|
||||
this._resetButton.addEventListener("click", this._handleResetClick);
|
||||
}
|
||||
if (this._deleteButton) {
|
||||
this._deleteButton.addEventListener("click", this._handleDeleteClick);
|
||||
}
|
||||
if (this._deleteConfirmButton) {
|
||||
this._deleteConfirmButton.addEventListener("click", this._handleDeleteConfirmClick);
|
||||
}
|
||||
if (this._deleteCancelButton) {
|
||||
this._deleteCancelButton.addEventListener("click", this._handleDeleteCancelClick);
|
||||
}
|
||||
if (this._deleteDialog) {
|
||||
this._deleteDialog.addEventListener("cancel", this._handleDeleteCancelClick);
|
||||
}
|
||||
}
|
||||
|
||||
_teardownSaveHandling() {
|
||||
if (this._saveButton) {
|
||||
this._saveButton.removeEventListener("click", this._handleSaveClick);
|
||||
}
|
||||
if (this._resetButton) {
|
||||
this._resetButton.removeEventListener("click", this._handleResetClick);
|
||||
}
|
||||
if (this._deleteButton) {
|
||||
this._deleteButton.removeEventListener("click", this._handleDeleteClick);
|
||||
}
|
||||
if (this._deleteConfirmButton) {
|
||||
this._deleteConfirmButton.removeEventListener("click", this._handleDeleteConfirmClick);
|
||||
}
|
||||
if (this._deleteCancelButton) {
|
||||
this._deleteCancelButton.removeEventListener("click", this._handleDeleteCancelClick);
|
||||
}
|
||||
if (this._deleteDialog) {
|
||||
this._deleteDialog.removeEventListener("cancel", this._handleDeleteCancelClick);
|
||||
}
|
||||
this._saveButton = null;
|
||||
this._resetButton = null;
|
||||
this._deleteButton = null;
|
||||
this._deleteDialog = null;
|
||||
this._deleteConfirmButton = null;
|
||||
this._deleteCancelButton = null;
|
||||
this._statusEl = null;
|
||||
}
|
||||
|
||||
@@ -157,6 +208,81 @@ export class AlmanachEditPage extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
async _handleResetClick(event) {
|
||||
event.preventDefault();
|
||||
if (this._isSaving) {
|
||||
return;
|
||||
}
|
||||
this._clearStatus();
|
||||
try {
|
||||
await this._reloadForm("");
|
||||
} catch (error) {
|
||||
this._showStatus(error instanceof Error ? error.message : "Formular konnte nicht aktualisiert werden.", "error");
|
||||
}
|
||||
}
|
||||
|
||||
async _handleDeleteClick(event) {
|
||||
event.preventDefault();
|
||||
if (this._isSaving) {
|
||||
return;
|
||||
}
|
||||
if (this._deleteDialog && typeof this._deleteDialog.showModal === "function") {
|
||||
this._deleteDialog.showModal();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDeleteCancelClick(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (this._deleteDialog && this._deleteDialog.open) {
|
||||
this._deleteDialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
async _handleDeleteConfirmClick(event) {
|
||||
event.preventDefault();
|
||||
if (!this._form || !this._deleteEndpoint || this._isSaving) {
|
||||
return;
|
||||
}
|
||||
if (this._deleteDialog && this._deleteDialog.open) {
|
||||
this._deleteDialog.close();
|
||||
}
|
||||
this._clearStatus();
|
||||
this._setSavingState(true);
|
||||
try {
|
||||
const formData = new FormData(this._form);
|
||||
const payload = {
|
||||
csrf_token: this._readValue(formData, "csrf_token"),
|
||||
last_edited: this._readValue(formData, "last_edited"),
|
||||
};
|
||||
const response = await fetch(this._deleteEndpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
let data = null;
|
||||
try {
|
||||
data = await response.clone().json();
|
||||
} catch {
|
||||
data = null;
|
||||
}
|
||||
if (!response.ok) {
|
||||
const message = data?.error || `Löschen fehlgeschlagen (${response.status}).`;
|
||||
throw new Error(message);
|
||||
}
|
||||
const redirect = data?.redirect || "/suche/baende";
|
||||
window.location.assign(redirect);
|
||||
} catch (error) {
|
||||
this._showStatus(error instanceof Error ? error.message : "Löschen fehlgeschlagen.", "error");
|
||||
} finally {
|
||||
this._setSavingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
_buildPayload() {
|
||||
if (!this._form) {
|
||||
throw new Error("Formular konnte nicht gefunden werden.");
|
||||
@@ -206,12 +332,15 @@ export class AlmanachEditPage extends HTMLElement {
|
||||
targetField: "series",
|
||||
});
|
||||
const newSeriesRelations = this._collectNewRelations("entries_series");
|
||||
const hasPreferredSeries = [...seriesRelations, ...newSeriesRelations].some(
|
||||
const preferredCount = [...seriesRelations, ...newSeriesRelations].filter(
|
||||
(relation) => relation.type === PREFERRED_SERIES_RELATION,
|
||||
);
|
||||
if (!hasPreferredSeries) {
|
||||
).length;
|
||||
if (preferredCount === 0) {
|
||||
throw new Error("Mindestens ein bevorzugter Reihentitel muss verknüpft sein.");
|
||||
}
|
||||
if (preferredCount > 1) {
|
||||
throw new Error("Es darf nur ein bevorzugter Reihentitel gesetzt sein.");
|
||||
}
|
||||
|
||||
const {
|
||||
relations: agentRelations,
|
||||
@@ -375,6 +504,12 @@ export class AlmanachEditPage extends HTMLElement {
|
||||
if (label) {
|
||||
label.textContent = isSaving ? "Speichern..." : "Speichern";
|
||||
}
|
||||
if (this._resetButton) {
|
||||
this._resetButton.disabled = isSaving;
|
||||
}
|
||||
if (this._deleteButton) {
|
||||
this._deleteButton.disabled = isSaving;
|
||||
}
|
||||
}
|
||||
|
||||
_clearStatus() {
|
||||
|
||||
@@ -253,6 +253,12 @@ export class DivManager extends HTMLElement {
|
||||
// Small delay to ensure element is visible before measuring
|
||||
setTimeout(() => {
|
||||
textareas.forEach((textarea) => {
|
||||
if (textarea.dataset.dmResizeBound !== "true") {
|
||||
textarea.dataset.dmResizeBound = "true";
|
||||
textarea.addEventListener("input", () => {
|
||||
window.TextareaAutoResize(textarea);
|
||||
});
|
||||
}
|
||||
window.TextareaAutoResize(textarea);
|
||||
});
|
||||
}, 10);
|
||||
|
||||
@@ -24,9 +24,11 @@ export class RelationsEditor extends HTMLElement {
|
||||
this._linkBase = this.getAttribute("data-link-base") || "";
|
||||
this._newLabel = this.getAttribute("data-new-label") || "(Neu)";
|
||||
this._addToggleId = this.getAttribute("data-add-toggle-id") || "";
|
||||
this._preferredLabel = (this.getAttribute("data-preferred-label") || "").trim();
|
||||
this._emptyText = this.querySelector(".rel-empty-text");
|
||||
this._setupAddPanel();
|
||||
this._setupDeleteToggles();
|
||||
this._setupPreferredOptionHandling();
|
||||
}
|
||||
|
||||
_getExistingIds() {
|
||||
@@ -230,6 +232,7 @@ export class RelationsEditor extends HTMLElement {
|
||||
typeSelect.innerHTML = this._typeSelect.innerHTML;
|
||||
typeSelect.value = this._typeSelect.value;
|
||||
typeSelect.name = `${this._prefix}_new_type`;
|
||||
typeSelect.addEventListener("change", () => this._updatePreferredOptions());
|
||||
}
|
||||
|
||||
const uncertain = fragment.querySelector("[data-rel-input='uncertain']");
|
||||
@@ -271,6 +274,7 @@ export class RelationsEditor extends HTMLElement {
|
||||
this._addPanel.classList.add("hidden");
|
||||
}
|
||||
this._updateEmptyTextVisibility();
|
||||
this._updatePreferredOptions();
|
||||
}
|
||||
|
||||
_setupDeleteToggles() {
|
||||
@@ -328,6 +332,8 @@ export class RelationsEditor extends HTMLElement {
|
||||
icon.classList.remove("ri-arrow-go-back-line");
|
||||
}
|
||||
}
|
||||
|
||||
this._updatePreferredOptions();
|
||||
});
|
||||
|
||||
button.addEventListener("mouseenter", () => {
|
||||
@@ -374,4 +380,79 @@ export class RelationsEditor extends HTMLElement {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_setupPreferredOptionHandling() {
|
||||
if (this._prefix !== "entries_series" || !this._preferredLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.querySelectorAll(`select[name^="${this._prefix}_type["]`).forEach((select) => {
|
||||
select.addEventListener("change", () => this._updatePreferredOptions());
|
||||
});
|
||||
|
||||
if (this._typeSelect) {
|
||||
this._typeSelect.addEventListener("change", () => this._updatePreferredOptions());
|
||||
}
|
||||
|
||||
this._updatePreferredOptions();
|
||||
}
|
||||
|
||||
_updatePreferredOptions() {
|
||||
if (this._prefix !== "entries_series" || !this._preferredLabel) {
|
||||
return;
|
||||
}
|
||||
const preferredLabel = this._preferredLabel.trim();
|
||||
|
||||
const selects = [];
|
||||
this.querySelectorAll(`select[name^="${this._prefix}_type["]`).forEach((select) => {
|
||||
selects.push({ select, row: select.closest(ROLE_REL_ROW), isAddPanel: false });
|
||||
});
|
||||
if (this._addRow) {
|
||||
this._addRow.querySelectorAll(`select[name='${this._prefix}_new_type']`).forEach((select) => {
|
||||
selects.push({ select, row: select.closest(ROLE_REL_ROW), isAddPanel: false });
|
||||
});
|
||||
}
|
||||
if (this._typeSelect) {
|
||||
selects.push({ select: this._typeSelect, row: this._typeSelect.closest(ROLE_REL_ROW), isAddPanel: true });
|
||||
}
|
||||
|
||||
const hasPreferred = selects.some(({ select, row, isAddPanel }) => {
|
||||
if (isAddPanel) {
|
||||
return false;
|
||||
}
|
||||
const currentValue = (select?.value || "").trim();
|
||||
if (!select || currentValue !== preferredLabel) {
|
||||
return false;
|
||||
}
|
||||
if (!row) {
|
||||
return true;
|
||||
}
|
||||
const deleteInput = row.querySelector(`input[name^="${this._prefix}_delete["]`);
|
||||
return !(deleteInput && deleteInput.checked);
|
||||
});
|
||||
|
||||
selects.forEach(({ select, row, isAddPanel }) => {
|
||||
if (!select) {
|
||||
return;
|
||||
}
|
||||
const option = Array.from(select.options).find((opt) => opt.value.trim() === preferredLabel);
|
||||
if (!option) {
|
||||
return;
|
||||
}
|
||||
const deleteInput = row ? row.querySelector(`input[name^="${this._prefix}_delete["]`) : null;
|
||||
const rowDeleted = Boolean(deleteInput && deleteInput.checked);
|
||||
const currentValue = (select.value || "").trim();
|
||||
const keepVisible = !hasPreferred || (currentValue === preferredLabel && !rowDeleted);
|
||||
if (isAddPanel && hasPreferred && currentValue === preferredLabel) {
|
||||
const fallback = Array.from(select.options).find((opt) => opt.value.trim() !== preferredLabel);
|
||||
if (fallback) {
|
||||
select.value = fallback.value;
|
||||
}
|
||||
}
|
||||
const shouldHide = !keepVisible || (isAddPanel && hasPreferred);
|
||||
option.hidden = shouldHide;
|
||||
option.disabled = shouldHide;
|
||||
option.style.display = shouldHide ? "none" : "";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user