+ /baende endpoint fixes

This commit is contained in:
Simon Martens
2026-01-27 09:52:06 +01:00
parent e35f3b19d4
commit cac2b0916c
12 changed files with 2214 additions and 2186 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -125,8 +125,8 @@ class="container-normal font-sans mt-10">
Alphabet
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': alphabetOpen }"></i>
</summary>
<div class="absolute left-0 mt-2 w-12 z-10 bg-white rounded-md shadow-lg border border-gray-200">
<div class="p-2 flex flex-col gap-1 text-sm text-gray-700">
<div class="absolute left-0 mt-2 z-10 bg-white rounded-md shadow-lg border border-gray-200">
<div class="p-2 grid grid-cols-13 gap-1 text-sm text-gray-700 w-[26rem]">
{{- range $_, $ch := $model.letters -}}
<a href="/baende/?letter={{ $ch }}&sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
hx-get="/baende/results/?letter={{ $ch }}&sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
@@ -134,33 +134,26 @@ class="container-normal font-sans mt-10">
hx-swap="outerHTML"
hx-indicator="body"
hx-push-url="/baende/?letter={{ $ch }}&sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
@click="offset = 0; hasMore = true; alphabetOpen = false; selectedLetter = '{{ $ch }}'"
@click="offset = 0; hasMore = true; alphabetOpen = false; selectedLetter = '{{ $ch }}'; search = ''"
:class="selectedLetter === '{{ $ch }}' ? 'bg-stone-200 font-bold' : ''"
class="text-center py-1 px-2 rounded hover:bg-gray-100 no-underline transition-colors">
{{ $ch }}
</a>
{{- end -}}
<!-- Clear filter option -->
<a href="/baende/?sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
hx-get="/baende/results/?sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
hx-target="#baenderesults"
hx-swap="outerHTML"
hx-indicator="body"
hx-push-url="/baende/?sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
@click="offset = 0; hasMore = true; alphabetOpen = false; selectedLetter = ''"
class="text-center py-1 px-2 rounded hover:bg-gray-100 no-underline transition-colors border-t mt-1">
@click="offset = 0; hasMore = true; alphabetOpen = false; selectedLetter = ''; search = ''"
class="text-center py-1 px-2 rounded hover:bg-gray-100 no-underline transition-colors col-span-13 border-t mt-1">
Alle
</a>
</div>
</div>
</details>
</div>
</div>
<!-- Right side group: Spalten menu and count/button -->
<div class="flex items-end gap-4">
<!-- Spalten toggle -->
<div class="relative" x-data="{ open: false }">
@@ -173,13 +166,16 @@ class="container-normal font-sans mt-10">
<div class="p-4 flex flex-col gap-2 text-sm text-gray-700">
<label class="inline-flex items-center gap-2"><input type="checkbox" data-col="appearance" checked /> Erscheinung</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" data-col="year" /> Jahr</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" data-col="language" /> Sprachen</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" data-col="extent" checked /> Umfang / Maße</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" data-col="signatures" checked /> Signaturen</label>
</div>
</div>
</details>
</div>
</div>
<!-- Right side group: count/button -->
<div class="flex items-end gap-4 ml-auto">
<!-- Count and New button -->
<div class="flex items-center gap-3">
@@ -202,6 +198,11 @@ class="container-normal font-sans mt-10">
<div id="baenderesults" class="mt-2" data-next-offset="{{ $model.next_offset }}">
{{ template "_baende_table" $model }}
<!-- Bottom count -->
<div id="baende-count-bottom" class="mt-4 flex justify-center text-base 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>
<!-- Load More Button -->
<div class="mt-6 flex justify-center" x-show="hasMore">
<button

View File

@@ -1,10 +1,10 @@
{{ $model := . }}
<div class="mt-6 overflow-x-auto">
<div class="mt-6 overflow-x-auto overflow-y-visible">
<table class="min-w-full text-sm font-sans baende-text">
<thead class="text-left text-gray-600 border-b">
<tr>
<th class="py-2 pr-4 pl-2 whitespace-nowrap w-[10rem]"
<th class="py-2 pr-4 pl-2 whitespace-nowrap w-[10rem] align-bottom"
:aria-sort="sortField === 'alm' ? (sortOrder === 'asc' ? 'ascending' : 'descending') : 'none'">
<button type="button"
class="flex w-full items-center justify-between gap-1 text-left text-sm"
@@ -19,7 +19,7 @@
aria-hidden="true"></i>
</button>
</th>
<th class="py-2 pr-4 whitespace-nowrap w-[44rem]"
<th class="py-2 pr-4 whitespace-nowrap w-[44rem] align-bottom"
:aria-sort="sortField === 'title' ? (sortOrder === 'asc' ? 'ascending' : 'descending') : 'none'">
<button type="button"
class="flex w-full items-center justify-between gap-1 text-left text-sm"
@@ -34,8 +34,35 @@
aria-hidden="true"></i>
</button>
</th>
<th class="py-2 pr-4 whitespace-nowrap col-appearance w-[18rem]">Erscheinung</th>
<th class="py-2 pr-4 whitespace-nowrap col-year hidden"
<th class="py-2 pr-4 whitespace-nowrap col-appearance w-[18rem] align-bottom">
<div class="flex flex-col items-start gap-0.5 h-full justify-end">
<button type="button"
class="flex w-full items-center justify-between gap-1 text-left text-sm leading-tight"
@click="changeSort('responsibility')">
<span class="font-semibold tracking-wide">Herausgeber</span>
<i class="text-xs opacity-70 transition-colors"
:class="{
'ri-arrow-up-line text-blue-600': sortField === 'responsibility' && sortOrder === 'asc',
'ri-arrow-down-line text-blue-600': sortField === 'responsibility' && sortOrder === 'desc',
'ri-arrow-up-down-line text-gray-400': sortField !== 'responsibility'
}"
aria-hidden="true"></i>
</button>
<button type="button"
class="flex w-full items-center justify-between gap-1 text-left text-sm leading-tight"
@click="changeSort('place')">
<span class="font-semibold tracking-wide">Ortsangabe</span>
<i class="text-xs opacity-70 transition-colors"
:class="{
'ri-arrow-up-line text-blue-600': sortField === 'place' && sortOrder === 'asc',
'ri-arrow-down-line text-blue-600': sortField === 'place' && sortOrder === 'desc',
'ri-arrow-up-down-line text-gray-400': sortField !== 'place'
}"
aria-hidden="true"></i>
</button>
</div>
</th>
<th class="py-2 pr-4 whitespace-nowrap col-year hidden align-bottom"
:aria-sort="sortField === 'year' ? (sortOrder === 'asc' ? 'ascending' : 'descending') : 'none'">
<button type="button"
class="flex w-full items-center justify-between gap-1 text-left text-sm"
@@ -50,9 +77,13 @@
aria-hidden="true"></i>
</button>
</th>
<th class="py-2 pr-4 whitespace-nowrap col-language hidden">Sprachen</th>
<th class="py-2 pr-4 whitespace-nowrap col-extent w-[18rem]">Umfang / Maße</th>
<th class="py-2 pr-4 whitespace-nowrap col-signatures"
<th class="py-2 pr-4 whitespace-nowrap col-extent w-[18rem] align-bottom">
<div class="flex flex-col items-start gap-0.5 leading-tight">
<span class="font-semibold tracking-wide">Umfang&thinsp;/&thinsp;Maße</span>
<span class="font-semibold tracking-wide">Sprache</span>
</div>
</th>
<th class="py-2 pr-4 whitespace-nowrap col-signatures align-bottom"
:aria-sort="sortField === 'signatur' ? (sortOrder === 'asc' ? 'ascending' : 'descending') : 'none'">
<button type="button"
class="flex w-full items-center justify-between gap-1 text-left text-sm"
@@ -86,22 +117,22 @@
{{- end -}}
<div class="flex flex-wrap items-center gap-1.5 pt-1">
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-eye-line"></i>
</a>
<div class="data-tip">Ansehen</div>
</tool-tip>
{{- if (IsAdminOrEditor $model.request.user) -}}
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-edit-line"></i>
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="return confirm('Band wirklich löschen?');">
<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" 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="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">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
@@ -112,7 +143,7 @@
</div>
</td>
<td class="py-2 pr-4">
<div class="font-semibold text-slate-900 text-base leading-snug">
<div class="font-semibold text-slate-900 text-base leading-snug inline-flex items-center gap-1">
{{- if $entry.PreferredTitle -}}
{{ $entry.PreferredTitle }}
{{- else if ne $entry.Year 0 -}}
@@ -120,8 +151,8 @@
{{- else -}}
[o.J.]
{{- end -}}
<tool-tip position="top" class="inline">
<i class="status-icon ml-1 align-middle {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<tool-tip position="top" class="inline-flex items-center">
<i class="status-icon {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<div class="data-tip">
{{- if eq $entry.EditState "Unknown" -}}
Gesucht
@@ -190,21 +221,24 @@
{{ $entry.Year }}
{{- end -}}
</td>
<td class="py-2 pr-4 col-language hidden">
{{- if $entry.Language -}}
{{- range $i, $lang := $entry.Language -}}
{{- if $i }}, {{ end -}}{{ LanguageNameGerman $lang }}
{{- end -}}
{{- end -}}
</td>
<td class="py-2 pr-4 col-extent">
{{- if or $entry.Extent $entry.Dimensions -}}
{{- if or $entry.Extent $entry.Dimensions $entry.Language -}}
<div class="flex flex-col gap-1 text-sm text-gray-700">
{{- if $entry.Extent -}}
<div><span class="font-semibold text-gray-500 block">Struktur</span>{{ $entry.Extent }}</div>
<div>{{ $entry.Extent }}</div>
{{- end -}}
{{- if $entry.Dimensions -}}
<div><span class="font-semibold text-gray-500">Maße:</span> {{ $entry.Dimensions }}</div>
<div>{{ $entry.Dimensions }}</div>
{{- end -}}
{{- if or $entry.Extent $entry.Dimensions -}}
<div class="h-1"></div>
{{- end -}}
{{- if $entry.Language -}}
<div class="flex flex-wrap gap-1.5 pt-0.5">
{{- range $i, $lang := $entry.Language -}}
<span class="inline-flex items-center rounded-xs border border-slate-200 bg-white px-2 py-0.5 text-xs font-semibold text-slate-700">{{ LanguageNameGerman $lang }}</span>
{{- end -}}
</div>
{{- end -}}
</div>
{{- end -}}
@@ -214,12 +248,12 @@
{{- if $items -}}
<div class="flex flex-col gap-2 text-sm text-gray-700">
{{- range $_, $item := $items -}}
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-xs">
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-sm">
{{- if $item.Identifier -}}
<div class="px-2 py-1 font-semibold text-slate-900 text-center">{{ $item.Identifier }}</div>
{{- end -}}
{{- if $item.Media -}}
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug">
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug text-sm">
{{- range $i, $media := $item.Media -}}{{- if $i }}, {{ end -}}{{ $media }}{{- end -}}
</div>
{{- end -}}

View File

@@ -9,22 +9,22 @@
{{- end -}}
<div class="flex flex-wrap items-center gap-1.5 pt-2">
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-eye-line"></i>
</a>
<div class="data-tip">Ansehen</div>
</tool-tip>
{{- if $result.IsAdmin -}}
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-edit-line"></i>
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="return confirm('Band wirklich löschen?');">
<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" 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="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">
<i class="ri-delete-bin-line"></i>
</button>
@@ -35,6 +35,7 @@
<tool-tip position="top" class="inline">
<button
type="button"
onclick="event.stopPropagation();"
class="inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900"
hx-get="/baende/row/{{ $entry.MusenalmID }}"
hx-target="closest tr"
@@ -50,7 +51,7 @@
<td class="py-2 pr-4 pl-2" colspan="6">
<div class="rounded-md border border-slate-200 bg-white shadow-sm p-3 text-base">
<div class="flex items-start justify-between gap-4 mb-3">
<div class="font-sans text-base font-semibold text-slate-900 inline-flex items-center">
<div class="font-sans text-base font-semibold text-slate-900 inline-flex items-center gap-1">
{{- if $entry.PreferredTitle -}}
{{ $entry.PreferredTitle }}
{{- else if ne $entry.Year 0 -}}
@@ -59,7 +60,7 @@
[o.J.]
{{- end -}}
<tool-tip position="top" class="inline-flex items-center">
<i class="status-icon ml-1 {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<i class="status-icon {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<div class="data-tip">
{{- if eq $entry.EditState "Unknown" -}}
Gesucht
@@ -78,7 +79,7 @@
</tool-tip>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-[1fr_18rem] gap-6 text-base font-sans text-gray-700">
<div class="grid grid-cols-1 lg:grid-cols-[1fr_18rem] gap-6 text-base font-sans text-gray-700 baende-details-card">
<div class="flex flex-col gap-3">
{{- if or $entry.TitleStmt $entry.SubtitleStmt $entry.VariantTitle $entry.ParallelTitle $entry.IncipitStmt -}}
<div>
@@ -105,7 +106,7 @@
{{- if $entry.Annotation -}}
<div>
<div class="text-xs uppercase tracking-wide text-gray-500">Annotation</div>
<div>{{ $entry.Annotation }}</div>
<div class="annotation-content">{{- Safe (ReplaceSlashParen $entry.Annotation) -}}</div>
</div>
{{- end -}}
{{- if $result.SeriesRels -}}
@@ -162,7 +163,7 @@
{{- if $entry.Comment -}}
<div>
<div class="text-xs uppercase tracking-wide text-gray-500">Kommentar</div>
<div>{{ $entry.Comment }}</div>
<div class="annotation-content">{{- Safe (ReplaceSlashParen $entry.Comment) -}}</div>
</div>
{{- end -}}
{{- if ne $entry.Year 0 -}}
@@ -206,33 +207,62 @@
{{- if $result.Items -}}
<div>
<div class="text-xs uppercase tracking-wide text-gray-500">Exemplare</div>
<div class="flex flex-col gap-2">
<div class="grid grid-cols-1 gap-4">
{{- range $_, $item := $result.Items -}}
<div class="rounded-md border border-slate-200 bg-white px-3 py-2 shadow-sm">
<div class="flex items-start gap-3">
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-xs min-w-[6rem]">
{{- if $item.Identifier -}}
<div class="px-2 py-1 font-semibold text-slate-900 text-center">{{ $item.Identifier }}</div>
{{- end -}}
{{- if $item.Media -}}
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug">
<div class="rounded-sm border border-slate-200 bg-white">
<div class="divide-y divide-slate-200 text-sm text-gray-700">
{{- if $item.Identifier -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">Signatur</span>
<span class="font-semibold text-slate-900">{{ $item.Identifier }}</span>
</div>
{{- end -}}
{{- if $item.Media -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">Status</span>
<span class="font-medium text-slate-800">
{{- range $i, $media := $item.Media -}}{{- if $i }}, {{ end -}}{{ $media }}{{- end -}}
</div>
{{- end -}}
</div>
<div class="flex-1">
{{- if or $item.Location $item.Owner -}}
<div class="text-sm text-gray-600">
{{- if $item.Location -}}<span>{{ $item.Location }}</span>{{- end -}}
{{- if $item.Owner -}}<span class="ml-2">{{ $item.Owner }}</span>{{- end -}}
</div>
{{- end -}}
</div>
</span>
</div>
{{- end -}}
{{- if $item.Location -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">Standort</span>
<span class="font-medium text-slate-800">{{ $item.Location }}</span>
</div>
{{- end -}}
{{- if $item.Owner -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">Träger</span>
<span class="font-medium text-slate-800">{{ $item.Owner }}</span>
</div>
{{- end -}}
{{- if $item.Condition -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">Zustand</span>
<span class="font-medium text-slate-800">{{ $item.Condition }}</span>
</div>
{{- end -}}
{{- if $item.Annotation -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">Anmerkung</span>
<span class="text-slate-700 annotation-content annotation-small">{{- Safe (ReplaceSlashParen $item.Annotation) -}}</span>
</div>
{{- end -}}
{{- if $item.Uri -}}
<div class="grid grid-cols-[5rem_1fr] items-center gap-3 px-3 py-1.5">
<span class="text-xs uppercase tracking-wide text-gray-500 whitespace-nowrap">URI</span>
<a href="{{ $item.Uri }}" class="text-slate-700 hover:text-slate-900 underline underline-offset-2" target="_blank" rel="noopener">Link öffnen</a>
</div>
{{- end -}}
</div>
{{- if or $item.Location $item.Owner -}}
{{- if or $item.Location $item.Owner $item.Condition $item.Annotation $item.Uri -}}
<div class="sr-only">
{{- if $item.Location -}}<span>{{ $item.Location }}</span>{{- end -}}
{{- if $item.Owner -}}<span class="ml-2">{{ $item.Owner }}</span>{{- end -}}
{{- if $item.Condition -}}<span class="ml-2">{{ $item.Condition }}</span>{{- end -}}
{{- if $item.Annotation -}}<span class="ml-2">{{ $item.Annotation }}</span>{{- end -}}
{{- if $item.Uri -}}<span class="ml-2">{{ $item.Uri }}</span>{{- end -}}
</div>
{{- end -}}
</div>
@@ -245,4 +275,3 @@
</div>
</td>
</tr>

View File

@@ -15,22 +15,22 @@
{{- end -}}
<div class="flex flex-wrap items-center gap-1.5 pt-1">
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-eye-line"></i>
</a>
<div class="data-tip">Ansehen</div>
</tool-tip>
{{- if (IsAdminOrEditor $model.request.user) -}}
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-edit-line"></i>
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="return confirm('Band wirklich löschen?');">
<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" 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="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">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
@@ -41,7 +41,7 @@
</div>
</td>
<td class="py-2 pr-4">
<div class="font-semibold text-slate-900 text-base leading-snug">
<div class="font-semibold text-slate-900 text-base leading-snug inline-flex items-center gap-1">
{{- if $entry.PreferredTitle -}}
{{ $entry.PreferredTitle }}
{{- else if ne $entry.Year 0 -}}
@@ -49,8 +49,8 @@
{{- else -}}
[o.J.]
{{- end -}}
<tool-tip position="top" class="inline">
<i class="status-icon ml-1 align-middle {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<tool-tip position="top" class="inline-flex items-center">
<i class="status-icon {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<div class="data-tip">
{{- if eq $entry.EditState "Unknown" -}}
Gesucht
@@ -119,21 +119,24 @@
{{ $entry.Year }}
{{- end -}}
</td>
<td class="py-2 pr-4 col-language hidden">
{{- if $entry.Language -}}
{{- range $i, $lang := $entry.Language -}}
{{- if $i }}, {{ end -}}{{ LanguageNameGerman $lang }}
{{- end -}}
{{- end -}}
</td>
<td class="py-2 pr-4 col-extent">
{{- if or $entry.Extent $entry.Dimensions -}}
{{- if or $entry.Extent $entry.Dimensions $entry.Language -}}
<div class="flex flex-col gap-1 text-sm text-gray-700">
{{- if $entry.Extent -}}
<div><span class="font-semibold text-gray-500 block">Struktur</span>{{ $entry.Extent }}</div>
<div>{{ $entry.Extent }}</div>
{{- end -}}
{{- if $entry.Dimensions -}}
<div><span class="font-semibold text-gray-500">Maße:</span> {{ $entry.Dimensions }}</div>
<div>{{ $entry.Dimensions }}</div>
{{- end -}}
{{- if or $entry.Extent $entry.Dimensions -}}
<div class="h-1"></div>
{{- end -}}
{{- if $entry.Language -}}
<div class="flex flex-wrap gap-1.5 pt-0.5">
{{- range $i, $lang := $entry.Language -}}
<span class="inline-flex items-center rounded-xs border border-slate-200 bg-white px-2 py-0.5 text-xs font-semibold text-slate-700">{{ LanguageNameGerman $lang }}</span>
{{- end -}}
</div>
{{- end -}}
</div>
{{- end -}}
@@ -143,12 +146,12 @@
{{- if $items -}}
<div class="flex flex-col gap-2 text-sm text-gray-700">
{{- range $_, $item := $items -}}
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-xs">
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-sm">
{{- if $item.Identifier -}}
<div class="px-2 py-1 font-semibold text-slate-900 text-center">{{ $item.Identifier }}</div>
{{- end -}}
{{- if $item.Media -}}
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug">
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug text-sm">
{{- range $i, $media := $item.Media -}}{{- if $i }}, {{ end -}}{{ $media }}{{- end -}}
</div>
{{- end -}}

View File

@@ -17,6 +17,10 @@
{{ template "_baende_table" $model }}
<div id="baende-count-bottom" class="mt-4 flex justify-center text-base 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>
<!-- Load More Button -->
<div class="mt-6 flex justify-center" x-show="hasMore">
<button

View File

@@ -15,22 +15,22 @@
{{- end -}}
<div class="flex flex-wrap items-center gap-1.5 pt-1">
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-eye-line"></i>
</a>
<div class="data-tip">Ansehen</div>
</tool-tip>
{{- if .is_admin -}}
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<a href="/almanach/{{ $entry.MusenalmID }}/edit" onclick="event.stopPropagation();" class="no-underline inline-flex items-center gap-1 rounded-xs bg-stone-100 px-2 py-1 text-xs font-semibold text-slate-700 hover:bg-stone-200 hover:text-slate-900">
<i class="ri-edit-line"></i>
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<form method="POST" action="/almanach/{{ $entry.MusenalmID }}/edit/delete" class="inline" onsubmit="return confirm('Band wirklich löschen?');">
<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" 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="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">
<i class="ri-delete-bin-line"></i>
</button>
<div class="data-tip">Löschen</div>
@@ -41,7 +41,7 @@
</div>
</td>
<td class="py-2 pr-4">
<div class="font-semibold text-slate-900 text-base leading-snug">
<div class="font-semibold text-slate-900 text-base leading-snug inline-flex items-center gap-1">
{{- if $entry.PreferredTitle -}}
{{ $entry.PreferredTitle }}
{{- else if ne $entry.Year 0 -}}
@@ -49,8 +49,8 @@
{{- else -}}
[o.J.]
{{- end -}}
<tool-tip position="top" class="inline">
<i class="status-icon ml-1 align-middle {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<tool-tip position="top" class="inline-flex items-center">
<i class="status-icon {{- if eq $entry.EditState "Edited" }} ri-checkbox-circle-line{{- else if eq $entry.EditState "Seen" }} ri-information-line{{- else if eq $entry.EditState "Review" }} ri-search-line{{- else if eq $entry.EditState "ToDo" }} ri-list-check{{- else }} ri-forbid-2-line{{- end }}" data-status="{{ $entry.EditState }}"></i>
<div class="data-tip">
{{- if eq $entry.EditState "Unknown" -}}
Gesucht
@@ -119,21 +119,24 @@
{{ $entry.Year }}
{{- end -}}
</td>
<td class="py-2 pr-4 col-language hidden">
{{- if $entry.Language -}}
{{- range $i, $lang := $entry.Language -}}
{{- if $i }}, {{ end -}}{{ LanguageNameGerman $lang }}
{{- end -}}
{{- end -}}
</td>
<td class="py-2 pr-4 col-extent">
{{- if or $entry.Extent $entry.Dimensions -}}
{{- if or $entry.Extent $entry.Dimensions $entry.Language -}}
<div class="flex flex-col gap-1 text-sm text-gray-700">
{{- if $entry.Extent -}}
<div><span class="font-semibold text-gray-500 block">Struktur</span>{{ $entry.Extent }}</div>
<div>{{ $entry.Extent }}</div>
{{- end -}}
{{- if $entry.Dimensions -}}
<div><span class="font-semibold text-gray-500">Maße:</span> {{ $entry.Dimensions }}</div>
<div>{{ $entry.Dimensions }}</div>
{{- end -}}
{{- if or $entry.Extent $entry.Dimensions -}}
<div class="h-1"></div>
{{- end -}}
{{- if $entry.Language -}}
<div class="flex flex-wrap gap-1.5 pt-0.5">
{{- range $i, $lang := $entry.Language -}}
<span class="inline-flex items-center rounded-xs border border-slate-200 bg-white px-2 py-0.5 text-xs font-semibold text-slate-700">{{ LanguageNameGerman $lang }}</span>
{{- end -}}
</div>
{{- end -}}
</div>
{{- end -}}
@@ -143,12 +146,12 @@
{{- if $items -}}
<div class="flex flex-col gap-2 text-sm text-gray-700">
{{- range $_, $item := $items -}}
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-xs">
<div class="inline-flex flex-col items-center justify-center rounded-xs border border-slate-200 bg-white text-sm">
{{- if $item.Identifier -}}
<div class="px-2 py-1 font-semibold text-slate-900 text-center">{{ $item.Identifier }}</div>
{{- end -}}
{{- if $item.Media -}}
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug">
<div class="w-full border-t border-slate-200 px-2 py-1 text-gray-600 text-center leading-snug text-sm">
{{- range $i, $media := $item.Media -}}{{- if $i }}, {{ end -}}{{ $media }}{{- end -}}
</div>
{{- end -}}

View File

@@ -300,6 +300,15 @@
@apply font-serif;
}
/* Card view (baende details) should use sans annotations */
.baende-details-card .annotation-content {
@apply font-sans text-base;
}
.baende-details-card .annotation-small {
@apply text-sm;
}
/* Headings - bold with spacing, same font size */
.annotation-content h1 {
@apply font-bold mt-2 mb-1 leading-normal;

View File

@@ -81,7 +81,7 @@ export class ToolTip extends HTMLElement {
"tooltip-box",
"opacity-0",
"hidden",
"absolute",
"fixed",
"px-2",
"py-1",
"text-sm",
@@ -89,7 +89,7 @@ export class ToolTip extends HTMLElement {
"bg-gray-900",
"rounded",
"shadow",
"z-10",
"z-50",
"whitespace-nowrap",
"transition-all",
"duration-200",
@@ -169,6 +169,7 @@ export class ToolTip extends HTMLElement {
clearTimeout(this._hideTimeout);
clearTimeout(this._hiddenTimeout);
this._tooltipBox.classList.remove("hidden");
this._updatePosition();
setTimeout(() => {
this._tooltipBox.classList.remove("opacity-0");
this._tooltipBox.classList.add("opacity-100");
@@ -186,61 +187,40 @@ export class ToolTip extends HTMLElement {
}
_updatePosition() {
this._tooltipBox.classList.remove(
"bottom-full",
"left-1/2",
"-translate-x-1/2",
"mb-2", // top
"top-full",
"mt-2", // bottom
"right-full",
"-translate-y-1/2",
"mr-2",
"top-1/2", // left
"left-full",
"ml-2", // right
);
const anchorRect = this.getBoundingClientRect();
const tipRect = this._tooltipBox.getBoundingClientRect();
const gap = 6;
let top = 0;
let left = 0;
const pos = this.getAttribute("position") || "top";
switch (pos) {
case "bottom":
this._tooltipBox.classList.add(
"top-full",
"left-1/2",
"transform",
"-translate-x-1/2",
"mt-0.5",
);
top = anchorRect.bottom + gap;
left = anchorRect.left + (anchorRect.width - tipRect.width) / 2;
break;
case "left":
this._tooltipBox.classList.add(
"right-full",
"top-1/2",
"transform",
"-translate-y-1/2",
"mr-0.5",
);
top = anchorRect.top + (anchorRect.height - tipRect.height) / 2;
left = anchorRect.left - tipRect.width - gap;
break;
case "right":
this._tooltipBox.classList.add(
"left-full",
"top-1/2",
"transform",
"-translate-y-1/2",
"ml-0.5",
);
top = anchorRect.top + (anchorRect.height - tipRect.height) / 2;
left = anchorRect.right + gap;
break;
case "top":
default:
// top as default
this._tooltipBox.classList.add(
"bottom-full",
"left-1/2",
"transform",
"-translate-x-1/2",
"mb-0.5",
);
top = anchorRect.top - tipRect.height - gap;
left = anchorRect.left + (anchorRect.width - tipRect.width) / 2;
}
const padding = 4;
const maxLeft = window.innerWidth - tipRect.width - padding;
const maxTop = window.innerHeight - tipRect.height - padding;
left = Math.max(padding, Math.min(left, maxLeft));
top = Math.max(padding, Math.min(top, maxTop));
this._tooltipBox.style.left = `${left}px`;
this._tooltipBox.style.top = `${top}px`;
}
}