FIX: delete button and layout in baende vendpoint

This commit is contained in:
Simon Martens
2026-01-28 21:31:59 +01:00
parent 26f2e30d72
commit ff85e83dc7
9 changed files with 379 additions and 96 deletions

View File

@@ -46,7 +46,7 @@ func (p *AlmanachEditPage) Setup(router *router.Router[*core.RequestEvent], ia p
rg.BindFunc(middleware.IsAdminOrEditor())
rg.GET(URL_ALMANACH_EDIT, p.GET(engine, app))
rg.POST(URL_ALMANACH_EDIT+"save", p.POSTSave(engine, app, ia))
rg.POST(URL_ALMANACH_EDIT+"delete", p.POSTDelete(engine, app))
rg.POST(URL_ALMANACH_EDIT+"delete", p.POSTDelete(engine, app, ia))
return nil
}
@@ -238,7 +238,7 @@ func (p *AlmanachEditPage) POSTSave(engine *templating.Engine, app core.App, ma
}
}
func (p *AlmanachEditPage) POSTDelete(engine *templating.Engine, app core.App) HandleFunc {
func (p *AlmanachEditPage) POSTDelete(engine *templating.Engine, app core.App, ma pagemodels.IApp) HandleFunc {
return func(e *core.RequestEvent) error {
id := e.Request.PathValue("id")
req := templating.NewRequest(e)
@@ -302,6 +302,9 @@ func (p *AlmanachEditPage) POSTDelete(engine *templating.Engine, app core.App) H
// Invalidate sorted entries cache since entry was deleted
InvalidateSortedEntriesCache()
// Invalidate Bände cache since entry was deleted
ma.ResetBaendeCache()
// Delete from FTS5 index asynchronously
go func(appInstance core.App, entryID string) {
if err := dbmodels.DeleteFTS5Entry(appInstance, entryID); err != nil {
@@ -333,8 +336,8 @@ type almanachEditPayload struct {
}
type almanachDeletePayload struct {
CSRFToken string `json:"csrf_token"`
LastEdited string `json:"last_edited"`
CSRFToken string `json:"csrf_token" form:"csrf_token"`
LastEdited string `json:"last_edited" form:"last_edited"`
}
type almanachEntryPayload struct {

View File

@@ -24,6 +24,7 @@ const (
URL_BAENDE = "/baende/"
URL_BAENDE_RESULTS = "/baende/results/"
URL_BAENDE_MORE = "/baende/more/"
URL_BAENDE_DELETE = "/baende/delete-info/{id}"
TEMPLATE_BAENDE = "/baende/"
URL_BAENDE_DETAILS = "/baende/details/{id}"
BAENDE_PAGE_SIZE = 150
@@ -77,6 +78,7 @@ func (p *BaendePage) Setup(router *router.Router[*core.RequestEvent], ia pagemod
rg.GET("more/", p.handleMore(engine, app, ia))
rg.GET("details/{id}", p.handleDetails(engine, app))
rg.GET("row/{id}", p.handleRow(engine, app))
rg.GET("delete-info/{id}", p.handleDeleteInfo(engine, app))
return nil
}
@@ -221,6 +223,45 @@ func (p *BaendePage) handleDetails(engine *templating.Engine, app core.App) Hand
}
}
func (p *BaendePage) handleDeleteInfo(engine *templating.Engine, app core.App) HandleFunc {
return func(e *core.RequestEvent) error {
req := templating.NewRequest(e)
if req.User() == nil {
return e.Redirect(303, "/login/")
}
id := e.Request.PathValue("id")
if id == "" {
return engine.Response404(e, nil, nil)
}
entry, err := dbmodels.Entries_MusenalmID(app, id)
if err != nil {
return engine.Response404(e, err, nil)
}
items, err := dbmodels.Items_Entry(app, entry.Id)
if err != nil {
app.Logger().Error("Failed to get items for delete dialog", "error", err)
}
contents, err := dbmodels.Contents_Entry(app, entry.Id)
if err != nil {
app.Logger().Error("Failed to get contents for delete dialog", "error", err)
}
dbmodels.Sort_Contents_Numbering(contents)
data := map[string]any{
"entry": entry,
"items": items,
"contents": contents,
}
return engine.Response200(e, "/baende/delete_info/", data, "fragment")
}
}
func (p *BaendePage) buildResultData(app core.App, ma pagemodels.IApp, e *core.RequestEvent, req *templating.Request, showAggregated bool) (map[string]any, error) {
data := map[string]any{}

File diff suppressed because one or more lines are too long

View File

@@ -150,51 +150,12 @@ class="container-normal font-sans mt-10">
<div class="mt-2">
<div class="border-b px-3 border-zinc-300">
<!-- Row 1: Search and Filters -->
<div class="flex flex-wrap items-end justify-start min-h-14 gap-x-2 gap-y-3 pb-3">
<!-- Search box -->
<div class="min-w-[22.5rem] max-w-96 flex flex-row bg-stone-50 relative font-sans text-lg items-center">
<div>
<i class="ri-search-line"></i><i class="-ml-0.5 inline-block ri-arrow-right-s-line"></i>
</div>
<div class="border-b-4 border-zinc-300 grow">
<form
method="GET"
action="/baende/"
hx-get="/baende/results/"
hx-indicator="#baende-search-spinner"
hx-push-url="false"
hx-swap="outerHTML"
hx-target="#baenderesults"
role="search"
@submit="offset = 0; hasMore = true"
aria-label="Bändesuche">
<input type="hidden" name="sort" :value="sortField" />
<input type="hidden" name="order" :value="sortOrder" />
<div class="relative">
<input
class="px-2 py-0.5 pr-7 font-sans placeholder:italic w-full text-lg"
type="search"
name="search"
value="{{ $model.search }}"
placeholder="Signatur oder Suchbegriff"
x-model="search"
@input.debounce.500="selectedLetter = ''; clearFilters(); ((search.trim().length >= 3) || /^[0-9]+$/.test(search.trim()) || search === '') && $el.form.requestSubmit()"
@search.debounce.500="selectedLetter = ''; clearFilters(); ((search.trim().length >= 3) || /^[0-9]+$/.test(search.trim()) || search === '') && $el.form.requestSubmit()"
autocomplete="off" />
<span id="baende-search-spinner" class="htmx-indicator absolute right-1 top-1/2 -translate-y-1/2 text-slate-900">
<i class="ri-loader-4-line spinning" aria-hidden="true"></i>
</span>
</div>
<button x-show="false">Suchen</button>
</form>
</div>
</div>
<!-- Row 1: Filters -->
<div class="flex flex-wrap items-end justify-center min-h-14 gap-x-2 gap-y-3 pb-3">
<!-- Alphabet navigation toggle -->
<div class="relative">
<details class="font-sans text-base list-none" data-role="alphabet-toggle" @toggle="alphabetOpen = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
<summary class="cursor-pointer select-none text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
:class="selectedLetter ? 'font-semibold text-slate-900 ring-1 ring-slate-300' : ''">
<span x-text="selectedLetter ? `Alphabet: ${selectedLetter}` : 'Alphabet'"></span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': alphabetOpen }"></i>
@@ -239,7 +200,7 @@ class="container-normal font-sans mt-10">
<!-- Status filter -->
<div class="relative" x-data="{ open: false }" data-role="baende-filter">
<details class="font-sans text-base list-none" @toggle="open = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
<summary class="cursor-pointer select-none text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
:class="activeFilterType === 'status' ? 'font-semibold text-slate-900 ring-1 ring-slate-300' : ''">
<span x-text="activeFilterType === 'status' ? `Status: ${statusLabels[activeFilterValue] || activeFilterValue}` : 'Status'"></span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
@@ -284,7 +245,7 @@ class="container-normal font-sans mt-10">
<!-- Person filter -->
<div class="relative" x-data="{ open: false }" data-role="baende-filter">
<details class="font-sans text-base list-none" @toggle="open = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
<summary class="cursor-pointer select-none text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
:class="activeFilterType === 'person' ? 'font-semibold text-slate-900 ring-1 ring-slate-300' : ''">
<span x-text="activeFilterType === 'person' ? `Person: ${personLabels[activeFilterValue] || activeFilterValue}` : 'Person'"></span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
@@ -337,7 +298,7 @@ class="container-normal font-sans mt-10">
<!-- Benutzer filter -->
<div class="relative" x-data="{ open: false }" data-role="baende-filter">
<details class="font-sans text-base list-none" @toggle="open = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
<summary class="cursor-pointer select-none text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
:class="activeFilterType === 'user' ? 'font-semibold text-slate-900 ring-1 ring-slate-300' : ''">
<span x-text="activeFilterType === 'user' ? `Benutzer: ${userLabels[activeFilterValue] || activeFilterValue}` : 'Benutzer'"></span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
@@ -385,7 +346,7 @@ class="container-normal font-sans mt-10">
<!-- Jahr filter -->
<div class="relative" x-data="{ open: false }" data-role="baende-filter">
<details class="font-sans text-base list-none" @toggle="open = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
<summary class="cursor-pointer select-none text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
:class="activeFilterType === 'year' ? 'font-semibold text-slate-900 ring-1 ring-slate-300' : ''">
<span x-text="activeFilterType === 'year' ? `Jahr: ${yearLabels[activeFilterValue] || activeFilterValue}` : 'Jahr'"></span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
@@ -435,7 +396,7 @@ class="container-normal font-sans mt-10">
<!-- Ort filter -->
<div class="relative" x-data="{ open: false }" data-role="baende-filter">
<details class="font-sans text-base list-none" @toggle="open = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
<summary class="cursor-pointer select-none text-gray-700 hover:text-slate-900 bg-gray-100 px-3 py-1.5 rounded-md flex items-center gap-2"
:class="activeFilterType === 'place' ? 'font-semibold text-slate-900 ring-1 ring-slate-300' : ''">
<span x-text="activeFilterType === 'place' ? `Ort: ${placeLabels[activeFilterValue] || activeFilterValue}` : 'Ort'"></span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
@@ -482,11 +443,51 @@ class="container-normal font-sans mt-10">
</div>
<!-- Row 2: Spalten + count -->
<div class="flex flex-wrap items-center justify-end gap-3 pb-0 mt-3">
<!-- Row 2: Search + Spalten + count -->
<div class="flex flex-wrap items-center justify-between gap-3 pb-0 mt-3">
<!-- Search box -->
<div class="min-w-[22.5rem] max-w-96 flex flex-row bg-stone-50 relative font-sans text-lg items-center">
<div>
<i class="ri-search-line"></i><i class="-ml-0.5 inline-block ri-arrow-right-s-line"></i>
</div>
<div class="border-b-4 border-zinc-300 grow">
<form
method="GET"
action="/baende/"
hx-get="/baende/results/"
hx-indicator="#baende-search-spinner"
hx-push-url="false"
hx-swap="outerHTML"
hx-target="#baenderesults"
role="search"
@submit="offset = 0; hasMore = true"
aria-label="Bändesuche">
<input type="hidden" name="sort" :value="sortField" />
<input type="hidden" name="order" :value="sortOrder" />
<div class="relative">
<input
class="px-2 py-0.5 pr-7 font-sans placeholder:italic w-full text-lg"
type="search"
name="search"
value="{{ $model.search }}"
placeholder="Signatur oder Suchbegriff"
x-model="search"
@input.debounce.500="selectedLetter = ''; clearFilters(); ((search.trim().length >= 3) || /^[0-9]+$/.test(search.trim()) || search === '') && $el.form.requestSubmit()"
@search.debounce.500="selectedLetter = ''; clearFilters(); ((search.trim().length >= 3) || /^[0-9]+$/.test(search.trim()) || search === '') && $el.form.requestSubmit()"
autocomplete="off" />
<span id="baende-search-spinner" class="htmx-indicator absolute right-1 top-1/2 -translate-y-1/2 text-slate-900">
<i class="ri-loader-4-line spinning" aria-hidden="true"></i>
</span>
</div>
<button x-show="false">Suchen</button>
</form>
</div>
</div>
<div class="flex flex-wrap items-center justify-end gap-3">
<div class="relative" x-data="{ open: false }">
<details class="font-sans text-base list-none" data-role="baende-column-toggle" @toggle="open = $el.open; if ($el.open) { closeOtherDropdowns($el); }">
<summary class="cursor-pointer text-gray-600 hover:text-slate-900 px-2 py-1 rounded-xs flex items-center gap-2 text-lg font-semibold font-sans">
<summary class="cursor-pointer select-none text-gray-600 hover:text-slate-900 px-2 py-1 rounded-xs flex items-center gap-2 text-lg font-semibold font-sans">
<i class="ri-eye-line"></i>
<span>Spalten</span>
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
@@ -504,7 +505,6 @@ class="container-normal font-sans mt-10">
</details>
</div>
<div class="flex items-center gap-3">
<div id="baende-count" class="text-lg font-semibold font-sans text-gray-600 whitespace-nowrap">
{{ if $model.current_count }}{{ $model.current_count }}&thinsp;/&thinsp;{{ end }}{{ if $model.total_count }}{{ $model.total_count }}{{ else }}{{ len $model.result.Entries }}{{ end }} Bände
</div>
@@ -544,6 +544,27 @@ class="container-normal font-sans mt-10">
</div>
</div>
<dialog data-role="baende-delete-dialog" class="dbform fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
<div class="p-5 w-[26rem]">
<div class="text-base font-bold text-gray-900">Band löschen?</div>
<div data-role="baende-delete-title" class="text-sm font-bold text-gray-900 mt-1"></div>
<div data-role="baende-delete-impacts" class="mt-2 text-sm text-gray-700">
Lade Informationen …
</div>
<div data-role="baende-delete-error" class="mt-2 text-sm text-red-700 hidden"></div>
<div class="flex items-center justify-end gap-3 mt-4">
<button type="button" class="resetbutton w-auto px-3 py-1 text-sm" data-role="baende-delete-cancel">Abbrechen</button>
<button
type="button"
class="submitbutton w-auto bg-red-700 hover:bg-red-800 px-3 py-1 text-sm"
data-role="baende-delete-confirm">
Löschen
</button>
</div>
<input type="hidden" data-role="baende-delete-csrf" value="{{ $model.csrf_token }}" />
</div>
</dialog>
<script>
(() => {
const filterRoots = document.querySelectorAll('[data-role="baende-filter"]');
@@ -563,4 +584,146 @@ class="container-normal font-sans mt-10">
});
});
})();
(() => {
const dialog = document.querySelector("[data-role='baende-delete-dialog']");
if (!dialog) return;
const titleEl = dialog.querySelector("[data-role='baende-delete-title']");
const impactsEl = dialog.querySelector("[data-role='baende-delete-impacts']");
const errorEl = dialog.querySelector("[data-role='baende-delete-error']");
const cancelBtn = dialog.querySelector("[data-role='baende-delete-cancel']");
const confirmBtn = dialog.querySelector("[data-role='baende-delete-confirm']");
const csrfInput = dialog.querySelector("[data-role='baende-delete-csrf']");
let currentEntryId = "";
let currentDeleteEndpoint = "";
const closeDialog = (event) => {
if (event) {
event.preventDefault();
}
if (dialog.open) {
dialog.close();
}
};
const openDialog = () => {
if (typeof dialog.showModal === "function") {
dialog.showModal();
}
};
const setError = (message) => {
if (!errorEl) return;
if (message) {
errorEl.textContent = message;
errorEl.classList.remove("hidden");
} else {
errorEl.textContent = "";
errorEl.classList.add("hidden");
}
};
const handleDeleteClick = async (event) => {
let button = null;
const path = typeof event.composedPath === "function" ? event.composedPath() : [];
for (const node of path) {
if (node && node.getAttribute && node.getAttribute("data-role") === "baende-delete") {
button = node;
break;
}
}
if (!button && event.target && event.target.closest) {
button = event.target.closest("[data-role='baende-delete']");
}
if (!button) return;
event.preventDefault();
event.stopPropagation();
currentEntryId = button.getAttribute("data-entry-id") || "";
currentDeleteEndpoint = button.getAttribute("data-delete-endpoint") || "";
const entryTitle = button.getAttribute("data-entry-title") || "";
if (titleEl) {
titleEl.textContent = entryTitle ? entryTitle : "Unbekannter Eintrag";
}
if (impactsEl) {
impactsEl.textContent = "Lade Informationen …";
}
setError("");
openDialog();
if (!currentEntryId || !impactsEl) return;
try {
const response = await fetch(`/baende/delete-info/${encodeURIComponent(currentEntryId)}`);
if (!response.ok) {
throw new Error("Infos konnten nicht geladen werden.");
}
const html = await response.text();
impactsEl.innerHTML = html;
} catch (error) {
impactsEl.textContent = "Infos konnten nicht geladen werden.";
}
};
document.addEventListener("click", handleDeleteClick, true);
if (cancelBtn) {
cancelBtn.addEventListener("click", closeDialog);
}
dialog.addEventListener("cancel", closeDialog);
if (confirmBtn) {
confirmBtn.addEventListener("click", async (event) => {
event.preventDefault();
if (!currentDeleteEndpoint || !csrfInput) return;
setError("");
try {
const response = await fetch(currentDeleteEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
csrf_token: csrfInput.value || "",
}),
});
let data = null;
try {
data = await response.clone().json();
} catch {
data = null;
}
if (!response.ok) {
throw new Error(data?.error || "Löschen fehlgeschlagen.");
}
closeDialog();
if (currentEntryId) {
const rows = document.querySelectorAll(`tr[data-entry-id='${CSS.escape(currentEntryId)}']`);
rows.forEach((row) => row.remove());
const countEl = document.getElementById("baende-count");
if (countEl) {
const raw = countEl.textContent || "";
const hasSlash = raw.includes("/");
const nums = raw.match(/\d+/g) || [];
if (nums.length > 0) {
let current = parseInt(nums[0], 10);
let total = nums.length > 1 ? parseInt(nums[1], 10) : null;
if (!Number.isNaN(current) && current > 0) current -= 1;
if (total !== null && !Number.isNaN(total) && total > 0) total -= 1;
if (hasSlash && total !== null) {
countEl.textContent = `${current} / ${total} Bände`;
} else {
countEl.textContent = `${current} Bände`;
}
}
}
}
} catch (error) {
setError(error instanceof Error ? error.message : "Löschen fehlgeschlagen.");
}
});
}
})();
</script>

View File

@@ -157,15 +157,19 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="event.stopPropagation(); return confirm('Band wirklich löschen?');">
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" />
<tool-tip position="top" class="inline">
<button type="submit" onclick="event.stopPropagation();" class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<button
type="button"
onclick="event.stopPropagation();"
data-role="baende-delete"
data-entry-id="{{ $entry.MusenalmID }}"
data-entry-title="{{- if $entry.PreferredTitle -}}{{ $entry.PreferredTitle }}{{- else if ne $entry.Year 0 -}}{{ $entry.Year }}{{- else -}}[o.J.]{{- end -}}"
data-delete-endpoint="/almanach/{{ $entry.MusenalmID }}/edit/delete"
class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
</tool-tip>
</form>
{{- end -}}
</div>
</div>

View File

@@ -0,0 +1,61 @@
{{ $entry := .entry }}
{{ $items := .items }}
{{ $contents := .contents }}
<div>
<div class="text-sm text-gray-700">
Der Eintrag wird dauerhaft gelöscht. Verknüpfungen, Exemplare und Beiträge werden entfernt.
</div>
<div class="mt-3">
<div class="text-sm font-semibold text-gray-700">Betroffene Beiträge ({{ len $contents }})</div>
<div class="mt-2 max-h-40 overflow-auto pr-1">
{{- if $contents -}}
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
{{- range $content := $contents -}}
<li class="flex items-baseline justify-between gap-3 ml-0 pl-0 text-sm text-gray-700">
<span>
{{- if $content.PreferredTitle -}}
{{ $content.PreferredTitle }}
{{- else if $content.TitleStmt -}}
{{ $content.TitleStmt }}
{{- else -}}
[Ohne Titel]
{{- end -}}
</span>
<span class="text-xs text-gray-500">Alm {{ $content.MusenalmID }}</span>
</li>
{{- end -}}
</ul>
{{- else -}}
<div class="italic text-gray-500">Keine Beiträge betroffen.</div>
{{- end -}}
</div>
</div>
<div class="mt-3">
<div class="text-sm font-semibold text-gray-700">Betroffene Exemplare ({{ len $items }})</div>
<div class="mt-2 max-h-40 overflow-auto pr-1">
{{- if $items -}}
<ul class="flex flex-col gap-2 pl-0 pr-0 m-0 list-none">
{{- range $item := $items -}}
<li class="flex items-baseline justify-between gap-3 ml-0 pl-0 text-sm text-gray-700">
<span>
{{- if $item.Identifier -}}
{{ $item.Identifier }}
{{- else -}}
[Ohne Signatur]
{{- end -}}
</span>
{{- if $item.Location -}}
<span class="text-xs text-gray-500">{{ $item.Location }}</span>
{{- end -}}
</li>
{{- end -}}
</ul>
{{- else -}}
<div class="italic text-gray-500">Keine Exemplare betroffen.</div>
{{- end -}}
</div>
</div>
</div>

View File

@@ -21,16 +21,19 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="event.stopPropagation(); return confirm('Band wirklich löschen?');">
<input type="hidden" name="csrf_token" value="{{ $result.CSRFToken }}" />
<tool-tip position="top" class="inline">
<button type="submit" onclick="event.stopPropagation();" class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<button
type="button"
onclick="event.stopPropagation();"
data-role="baende-delete"
data-entry-id="{{ $entry.MusenalmID }}"
data-entry-title="{{- if $entry.PreferredTitle -}}{{ $entry.PreferredTitle }}{{- else if ne $entry.Year 0 -}}{{ $entry.Year }}{{- else -}}[o.J.]{{- end -}}"
data-delete-endpoint="/almanach/{{ $entry.MusenalmID }}/edit/delete"
class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
</tool-tip>
</form>
{{- end -}}
<tool-tip position="top" class="inline">
<button

View File

@@ -27,15 +27,19 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="event.stopPropagation(); return confirm('Band wirklich löschen?');">
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" />
<tool-tip position="top" class="inline">
<button type="submit" onclick="event.stopPropagation();" class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<button
type="button"
onclick="event.stopPropagation();"
data-role="baende-delete"
data-entry-id="{{ $entry.MusenalmID }}"
data-entry-title="{{- if $entry.PreferredTitle -}}{{ $entry.PreferredTitle }}{{- else if ne $entry.Year 0 -}}{{ $entry.Year }}{{- else -}}[o.J.]{{- end -}}"
data-delete-endpoint="/almanach/{{ $entry.MusenalmID }}/edit/delete"
class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
</tool-tip>
</form>
{{- end -}}
</div>
</div>

View File

@@ -27,15 +27,19 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="event.stopPropagation(); return confirm('Band wirklich löschen?');">
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" />
<tool-tip position="top" class="inline">
<button type="submit" onclick="event.stopPropagation();" class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<button
type="button"
onclick="event.stopPropagation();"
data-role="baende-delete"
data-entry-id="{{ $entry.MusenalmID }}"
data-entry-title="{{- if $entry.PreferredTitle -}}{{ $entry.PreferredTitle }}{{- else if ne $entry.Year 0 -}}{{ $entry.Year }}{{- else -}}[o.J.]{{- end -}}"
data-delete-endpoint="/almanach/{{ $entry.MusenalmID }}/edit/delete"
class="inline-flex items-center gap-1 rounded-xs bg-red-50 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-100 hover:text-red-900">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
</tool-tip>
</form>
{{- end -}}
</div>
</div>