BUGFIX: annoyances when editing a almanach

This commit is contained in:
Simon Martens
2026-01-09 22:04:00 +01:00
parent 65f83aa6a6
commit 22b303dc72
13 changed files with 657 additions and 403 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -24,7 +24,7 @@ type AlmanachResult struct {
<div class="flex flex-col justify-end-safe flex-2/5">
<h1 class="text-2xl w-full font-bold text-slate-900 mb-4">
{{- if $model.is_new -}}
Neuer Almanach
Almanach
{{- else -}}
{{- $model.result.Entry.PreferredTitle -}}
{{- end -}}
@@ -283,19 +283,9 @@ type AlmanachResult struct {
</div>
<div class="mt-3">
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-2 text-lg font-bold text-gray-700">
<i class="ri-links-line"></i>
<span>Normdaten &amp; Verknüpfungen</span>
</div>
<div class="flex items-center gap-3">
<button type="button" id="series-add-toggle" class="text-gray-700 hover:text-gray-900">
<i class="ri-add-line"></i> Reihe verlinken
</button>
<button type="button" id="agents-add-toggle" class="text-gray-700 hover:text-gray-900">
<i class="ri-add-line"></i> Person verlinken
</button>
</div>
<div class="flex items-center gap-2 text-lg font-bold text-gray-700">
<i class="ri-links-line"></i>
<span>Normdaten &amp; Verknüpfungen</span>
</div>
<hr class="border-slate-400 mt-2 mb-3" />
<div class="mt-3">
@@ -303,9 +293,14 @@ type AlmanachResult struct {
<div class="inputwrapper">
<div class="flex items-center justify-between">
<label class="inputlabel" for="series-section">Reihen</label>
<a href="/reihen/new/" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3" target="_blank" rel="noreferrer">
<i class="ri-add-line"></i> Neue Reihe anlegen
</a>
<div class="flex items-center gap-3">
<a href="/reihen/new/" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline" target="_blank" rel="noreferrer">
<i class="ri-add-line"></i> Neue Reihe anlegen
</a>
<button type="button" id="series-add-toggle" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3">
<i class="ri-link"></i> Reihe verlinken
</button>
</div>
</div>
<div id="series-section" class="rel-section-container">
{{- if $model.result.Series -}}
@@ -357,7 +352,7 @@ type AlmanachResult struct {
<div class="rel-empty-text">Keine Reihen verknüpft.</div>
{{- end -}}
</div>
<div data-role="relation-add-row" class="mt-2 px-2"></div>
<div data-role="relation-add-row" class="flex flex-col gap-2 mt-2 px-2"></div>
<div data-role="relation-add-panel" class="mt-2 px-2 hidden">
<div class="rel-row">
<div class="rel-grid">
@@ -437,9 +432,14 @@ type AlmanachResult struct {
<div class="inputwrapper">
<div class="flex items-center justify-between">
<label class="inputlabel" for="agents-section">Personen &amp; Körperschaften</label>
<a href="/personen/new/" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3" target="_blank" rel="noreferrer">
<i class="ri-add-line"></i> Neue Person/Körperschaft anlegen
</a>
<div class="flex items-center gap-3">
<a href="/personen/new/" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline" target="_blank" rel="noreferrer">
<i class="ri-add-line"></i> Neue Person/Körperschaft anlegen
</a>
<button type="button" id="agents-add-toggle" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3">
<i class="ri-link"></i> Person verlinken
</button>
</div>
</div>
<div id="agents-section" class="rel-section-container">
{{- if $model.result.EntriesAgents -}}
@@ -493,7 +493,7 @@ type AlmanachResult struct {
<div class="rel-empty-text">Keine Personen verknüpft.</div>
{{- end -}}
</div>
<div data-role="relation-add-row" class="mt-2 px-2"></div>
<div data-role="relation-add-row" class="flex flex-col gap-2 mt-2 px-2"></div>
<div data-role="relation-add-panel" class="mt-2 px-2 hidden">
<div class="rel-row">
<div class="rel-grid">
@@ -573,19 +573,23 @@ type AlmanachResult struct {
<div class="inputwrapper">
<div class="flex items-center justify-between">
<label for="places" class="inputlabel">Erscheinungs- und Verlagsorte</label>
<a href="/orte/new/" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3" target="_blank" rel="noreferrer">
<i class="ri-add-line"></i> Neuen Ort anlegen
</a>
<div class="flex items-center gap-3">
<a href="/orte/new/" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline" target="_blank" rel="noreferrer">
<i class="ri-add-line"></i> Neuen Ort anlegen
</a>
<button type="button" id="places-add-toggle" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3">
<i class="ri-link"></i> Ort verlinken
</button>
</div>
</div>
<multi-select-simple
id="places"
name="places[]"
data-edit-base="/ort/"
data-edit-suffix="/edit"
data-external-toggle-id="places-add-toggle"
value='[{{- range $i, $place := $model.result.Places -}}{{- if $i }},{{ end -}}"{{ $place.Id }}"{{- end -}}]'
placeholder="Orte suchen..."
data-toggle-label='<i class="ri-add-circle-line"></i>'
data-empty-text="Keine Orte ausgewählt..."
data-empty-text="Keine Orte verknüpft"
show-create-button="false"
data-endpoint="/api/places/search"
data-result-key="places"
@@ -682,14 +686,20 @@ type AlmanachResult struct {
<div class="flex flex-col gap-4 mt-4">
<!-- Languages -->
<div class="inputwrapper">
<label for="languages" class="inputlabel">Sprachen</label>
<div class="flex items-center justify-between">
<label for="languages" class="inputlabel">Sprachen</label>
<button type="button" id="languages-add-toggle" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline pr-3">
<i class="ri-link"></i> Sprache verlinken
</button>
</div>
<multi-select-simple
id="languages"
name="languages[]"
show-create-button="false"
placeholder="Sprachen suchen..."
data-toggle-label='<i class="ri-add-circle-line"></i>'
data-empty-text="Keine Sprachen ausgewählt..."
data-empty-text="Keine Sprachen verknüpft"
data-external-toggle-id="languages-add-toggle"
value='[{{- range $i, $lang := $model.result.Entry.Language -}}{{- if $i }},{{ end -}}"{{ $lang }}"{{- end -}}]'></multi-select-simple>
</div>

View File

@@ -7,7 +7,7 @@
<div class="flex flex-col justify-end-safe flex-2/5">
<h1 class="text-2xl w-full font-bold text-slate-900 mb-4">
{{- if $model.is_new -}}
Neuer Ort
Ort
{{- else -}}
{{- $place.Name -}}
{{- end -}}
@@ -95,6 +95,7 @@
<div class="container-normal mx-auto mt-4 !px-0">
{{ template "_usermessage" $model }}
<form
autocomplete="off"
class="w-full dbform"
id="changeplaceform"
method="POST"
@@ -126,7 +127,8 @@
<div class="inputwrapper">
<div class="px-3 py-2 flex flex-row gap-2 font-bold">
<input type="checkbox" name="fictional" id="fictional" {{ if $place.Fictional }}checked{{ end }} />
<label class="flex items-center gap-2 text-gray-700" for="fictional">Fiktiv</label>
<label class="flex items-center gap-2 text-gray-700"
for="fictional">Fiktional</label>
</div>
</div>
</div>

View File

@@ -15,18 +15,28 @@
{{- if $model.result.Places -}}
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
{{- range $place := $model.result.Places -}}
<li class="flex items-baseline justify-between gap-4">
<li class="px-2 py-0.5 ml-0 flex items-baseline justify-between gap-4 odd:bg-stone-100">
<div class="flex flex-col">
<span class="font-bold text-slate-900">{{ $place.Name }}</span>
<span class="font-bold text-stone-900">{{ $place.Name }}</span>
{{- if $place.Pseudonyms -}}
<span class="text-sm text-gray-600 italic">{{ $place.Pseudonyms }}</span>
<span class="text-sm text-stone-700 italic">{{ $place.Pseudonyms }}</span>
{{- end -}}
</div>
<div class="flex items-center gap-4">
{{- if (index $model.bcount $place.Id) -}}
<div class="font-sans text-sm">
<tool-tip position="top">
<i class="ri-book-line"></i> {{ index $model.bcount $place.Id }}
<div class="data-tip">Bände</div>
</tool-tip>
</div>
{{- end -}}
{{- if (IsAdminOrEditor $model.request.user) -}}
<a href="/ort/{{ $place.Id }}/edit" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline">
<i class="ri-edit-line"></i> Bearbeiten
</a>
{{- end -}}
</div>
{{- if (IsAdminOrEditor $model.request.user) -}}
<a href="/ort/{{ $place.Id }}/edit" class="text-sm font-bold text-gray-700 hover:text-slate-950 no-underline">
<i class="ri-edit-line"></i> Bearbeiten
</a>
{{- end -}}
</li>
{{- end -}}
</ul>

View File

@@ -7,7 +7,7 @@
<div class="flex flex-col justify-end-safe flex-2/5">
<h1 class="text-2xl w-full font-bold text-slate-900 mb-4">
{{- if $model.is_new -}}
Neue Person
Person
{{- else -}}
{{- $agent.Name -}}
{{- end -}}
@@ -97,6 +97,7 @@
<div class="container-normal mx-auto mt-4 !px-0">
{{ template "_usermessage" $model }}
<form
autocomplete="off"
class="w-full dbform"
id="changepersonform"
method="POST"
@@ -135,16 +136,18 @@
<textarea name="annotation" id="annotation" class="inputinput" autocomplete="off" rows="2">{{- $agent.Annotation -}}</textarea>
</div>
<div class="inputwrapper">
<label class="inputlabel">Typ</label>
<div class="px-3 py-2 flex flex-col gap-2">
<label class="flex items-center gap-2 text-sm text-gray-700">
<input type="checkbox" name="corporate_body" {{ if $agent.CorporateBody }}checked{{ end }} />
Körperschaft
</label>
<label class="flex items-center gap-2 text-sm text-gray-700">
<input type="checkbox" name="fictional" {{ if $agent.Fictional }}checked{{ end }} />
Fiktiv
</label>
<div class="px-3 py-2 flex flex-row gap-2 font-bold">
<input type="checkbox" name="fictional" id="fictional" {{ if $agent.Fictional }}checked{{ end }} />
<label class="flex items-center gap-2 text-gray-700"
for="fictional">Fiktional</label>
</div>
</div>
<div class="inputwrapper">
<div class="px-3 py-2 flex flex-row gap-2 font-bold">
<input type="checkbox" name="corporate_body" id="corporate_body" {{ if
$agent.CorporateBody }}checked{{ end }} />
<label class="flex items-center gap-2 text-gray-700"
for="corporate_body">Körperschaft (Verlag/Vertrieb)</label>
</div>
</div>
</div>

View File

@@ -7,7 +7,7 @@
<div class="flex flex-col justify-end-safe flex-2/5">
<h1 class="text-2xl w-full font-bold text-slate-900 mb-4">
{{- if $model.is_new -}}
Neue Reihe
Reihe
{{- else -}}
{{- $series.Title -}}
{{- end -}}
@@ -97,6 +97,7 @@
<div class="container-normal mx-auto mt-4 !px-0">
{{ template "_usermessage" $model }}
<form
autocomplete="off"
class="w-full dbform"
id="changeseriesform"
method="POST"

View File

@@ -126,22 +126,78 @@ export class FabMenu extends HTMLElement {
<div class="px-3 py-1.5 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Erstellen
</div>
<a href="/almanach-new/" class="flex items-center px-4 py-2 hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-book-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neuer Band</span>
</a>
<a href="/reihen/new/" class="flex items-center px-4 py-2 hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-stack-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neue Reihe</span>
</a>
<a href="/orte/new/" class="flex items-center px-4 py-2 hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-map-pin-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neuer Ort</span>
</a>
<a href="/personen/new/" class="flex items-center px-4 py-2 hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neue Person</span>
</a>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/almanach-new/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-book-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neuer Band</span>
</a>
<a href="/almanach-new/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/reihen/new/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-stack-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neue Reihe</span>
</a>
<a href="/reihen/new/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/orte/new/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-map-pin-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neuer Ort</span>
</a>
<a href="/orte/new/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/personen/new/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Neue Person</span>
</a>
<a href="/personen/new/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="border-t border-gray-200 my-1"></div>
`
: "";
const listenSection = isAdminOrEditor
? `
<div class="px-3 py-1.5 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Listen
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/reihen/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-stack-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Reihen</span>
</a>
<a href="/reihen/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/orte/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-map-pin-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Orte</span>
</a>
<a href="/orte/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/personen/" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Personen</span>
</a>
<a href="/personen/" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="border-t border-gray-200 my-1"></div>
`
: "";
@@ -151,14 +207,24 @@ export class FabMenu extends HTMLElement {
<div class="px-3 py-1.5 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Administration
</div>
<a href="/user/management/access/User?redirectTo=${redirectPath}" class="flex items-center px-4 py-2 hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-3-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Nutzer einladen</span>
</a>
<a href="/user/management?redirectTo=${redirectPath}" class="flex items-center px-4 py-2 hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-2-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Benutzerverwaltung</span>
</a>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/user/management/access/User?redirectTo=${redirectPath}" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-3-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Nutzer einladen</span>
</a>
<a href="/user/management/access/User?redirectTo=${redirectPath}" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="grid grid-cols-[1fr_auto] group">
<a href="/user/management?redirectTo=${redirectPath}" class="flex items-center px-4 py-2 group-hover:bg-gray-100 transition-colors no-underline text-sm">
<i class="ri-group-2-line text-base text-gray-700 mr-2.5"></i>
<span class="text-gray-900">Benutzerverwaltung</span>
</a>
<a href="/user/management?redirectTo=${redirectPath}" target="_blank" class="flex items-center justify-center px-3 py-2 group-hover:bg-gray-100 text-gray-700 hover:text-slate-900 transition-colors no-underline text-sm" title="In neuem Tab öffnen">
<i class="ri-external-link-line text-base"></i>
</a>
</div>
<div class="border-t border-gray-200 my-1"></div>
`
: "";
@@ -226,6 +292,7 @@ export class FabMenu extends HTMLElement {
<!-- Rest of menu (hidden in half state, shown in full state) -->
<div class="fab-full-content overflow-hidden transition-all duration-300 ease-in-out" style="max-height: 0; opacity: 0;">
${createSection}
${listenSection}
${adminSection}
<div class="px-4 py-2">
<div class="font-semibold text-gray-900 text-sm">${userName}</div>

View File

@@ -357,7 +357,7 @@
}
.mss-no-items-text {
@apply italic text-sm text-gray-500 w-full;
@apply italic text-base text-gray-600 w-full;
}
.mss-selected-item-pill {

View File

@@ -403,6 +403,15 @@ export class MultiSelectSimple extends HTMLElement {
this.toggleButton.addEventListener("click", this._handleToggleClick);
}
// Setup external toggle button if specified
const externalToggleId = this.getAttribute("data-external-toggle-id");
if (externalToggleId) {
this.externalToggleButton = document.getElementById(externalToggleId);
if (this.externalToggleButton) {
this.externalToggleButton.addEventListener("click", this._handleToggleClick);
}
}
this._updateRootElementStateClasses();
if (this.hasAttribute("value")) {
const attrValue = this.getAttribute("value");
@@ -418,6 +427,10 @@ export class MultiSelectSimple extends HTMLElement {
this._renderSelectedItems();
this._synchronizeHiddenSelect();
}
// Ensure selected items are rendered even if value is empty
if (this._value.length === 0) {
this._renderSelectedItems();
}
if (this.hasAttribute("disabled")) this.disabledCallback(true);
if (this._toggleInput) {
this._hideInputControls();
@@ -444,6 +457,7 @@ export class MultiSelectSimple extends HTMLElement {
if (this.createNewButton) this.createNewButton.removeEventListener("click", this._handleCreateNewButtonClick);
if (this.selectedItemsContainer) this.selectedItemsContainer.removeEventListener("click", this._handleSelectedItemsContainerClick);
if (this.toggleButton) this.toggleButton.removeEventListener("click", this._handleToggleClick);
if (this.externalToggleButton) this.externalToggleButton.removeEventListener("click", this._handleToggleClick);
clearTimeout(this._blurTimeout);
if (this._remoteFetchTimeout) {
clearTimeout(this._remoteFetchTimeout);
@@ -641,7 +655,9 @@ export class MultiSelectSimple extends HTMLElement {
const displayIds = [...this._value, ...removedInOrder];
if (displayIds.length === 0) {
const emptyText = this.getAttribute("data-empty-text") || "Keine Auswahl...";
this.selectedItemsContainer.innerHTML = `<span class="${MSS_NO_ITEMS_TEXT_CLASS}">${emptyText}</span>`;
// Start with hidden class - visibility will be managed by show/hide input controls
const hiddenClass = this._inputCollapsed ? '' : 'hidden';
this.selectedItemsContainer.innerHTML = `<span class="${MSS_NO_ITEMS_TEXT_CLASS} ${hiddenClass}">${emptyText}</span>`;
} else {
displayIds.forEach((id) => {
const pillEl = this._createSelectedItemElement(id);

View File

@@ -113,8 +113,17 @@ export class RelationsEditor extends HTMLElement {
if (this._addToggle) {
this._addToggle.addEventListener("click", () => {
const wasHidden = this._addPanel.classList.contains("hidden");
this._addPanel.classList.toggle("hidden");
this._updateEmptyTextVisibility();
// Auto-focus the search input when opening the panel
if (wasHidden && this._addInput) {
// Use setTimeout to ensure the panel is visible before focusing
setTimeout(() => {
this._addInput.focus();
}, 0);
}
});
}
@@ -256,7 +265,7 @@ export class RelationsEditor extends HTMLElement {
const deleteButton = fragment.querySelector(ROLE_NEW_DELETE);
if (deleteButton) {
deleteButton.addEventListener("click", () => {
this._addRow.innerHTML = "";
row.remove();
this._pendingItem = null;
this._clearAddPanel();
if (this._addPanel) {
@@ -266,7 +275,6 @@ export class RelationsEditor extends HTMLElement {
});
}
this._addRow.innerHTML = "";
this._addRow.appendChild(fragment);
this._pendingItem = null;
this._clearAddPanel();