mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 02:25:30 +00:00
+/baende endpoint
This commit is contained in:
@@ -1,29 +1,97 @@
|
||||
{{ $model := . }}
|
||||
|
||||
<div x-data="{ search : '{{ $model.search }}' }" class="container-normal font-sans mt-10">
|
||||
<div x-data="{
|
||||
search: '{{ $model.search }}',
|
||||
offset: {{ if $model.next_offset }}{{ $model.next_offset }}{{ else }}0{{ end }},
|
||||
hasMore: {{ if $model.has_more }}true{{ else }}false{{ end }},
|
||||
loading: false,
|
||||
alphabetOpen: false,
|
||||
selectedLetter: '{{ $model.letter }}',
|
||||
sortField: '{{ if $model.sort_field }}{{ $model.sort_field }}{{ else }}title{{ end }}',
|
||||
sortOrder: '{{ if $model.sort_order }}{{ $model.sort_order }}{{ else }}asc{{ end }}',
|
||||
changeSort(field) {
|
||||
if (this.sortField === field) {
|
||||
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
this.sortField = field;
|
||||
this.sortOrder = 'asc';
|
||||
}
|
||||
this.offset = 0;
|
||||
this.hasMore = true;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.set('sort', this.sortField);
|
||||
params.set('order', this.sortOrder);
|
||||
params.set('offset', 0);
|
||||
if (this.search) {
|
||||
params.set('search', this.search);
|
||||
}
|
||||
if (this.selectedLetter) {
|
||||
params.set('letter', this.selectedLetter);
|
||||
}
|
||||
|
||||
const queryString = params.toString();
|
||||
htmx.ajax('GET', `/baende/results/?${queryString}`, {
|
||||
target: '#baenderesults',
|
||||
swap: 'outerHTML',
|
||||
indicator: 'body'
|
||||
});
|
||||
},
|
||||
updateUrl() {
|
||||
const params = new URLSearchParams();
|
||||
params.set('offset', this.offset);
|
||||
params.set('sort', this.sortField);
|
||||
params.set('order', this.sortOrder);
|
||||
if (this.search) {
|
||||
params.set('search', this.search);
|
||||
}
|
||||
if (this.selectedLetter) {
|
||||
params.set('letter', this.selectedLetter);
|
||||
}
|
||||
const query = params.toString();
|
||||
const newUrl = query ? `/baende/?${query}` : '/baende/';
|
||||
window.history.replaceState(null, '', newUrl);
|
||||
},
|
||||
loadMoreUrl() {
|
||||
const params = new URLSearchParams();
|
||||
params.set('offset', this.offset);
|
||||
params.set('sort', this.sortField);
|
||||
params.set('order', this.sortOrder);
|
||||
if (this.search) {
|
||||
params.set('search', this.search);
|
||||
}
|
||||
if (this.selectedLetter) {
|
||||
params.set('letter', this.selectedLetter);
|
||||
}
|
||||
return `/baende/more/?${params.toString()}`;
|
||||
}
|
||||
}"
|
||||
@htmx:after-swap.window="
|
||||
if ($event.detail.target && $event.detail.target.id === 'baenderesults') {
|
||||
const responseUrl = $event.detail.xhr?.responseURL || window.location.href;
|
||||
const params = new URL(responseUrl).searchParams;
|
||||
selectedLetter = params.get('letter') || '';
|
||||
sortField = params.get('sort') || sortField;
|
||||
sortOrder = params.get('order') || sortOrder;
|
||||
const nextOffsetAttr = $event.detail.target.dataset.nextOffset;
|
||||
if (nextOffsetAttr) {
|
||||
const parsed = Number(nextOffsetAttr);
|
||||
if (!Number.isNaN(parsed)) {
|
||||
offset = parsed;
|
||||
}
|
||||
}
|
||||
updateUrl();
|
||||
}
|
||||
"
|
||||
class="container-normal font-sans mt-10">
|
||||
<div id="pageheading" class="headingcontainer">
|
||||
<h1 class="heading">Bände A–Z</h1>
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="flex flex-wrap flex-row border-b px-3 border-zinc-300 items-end justify-between min-h-14 gap-y-4">
|
||||
<!-- Left side group: Count and Alphabet -->
|
||||
<div class="flex items-end gap-x-6">
|
||||
<div id="alphabet" class="alphabet flex flex-row items-end text-xl">
|
||||
{{- range $_, $ch := $model.letters -}}
|
||||
<a
|
||||
href="/baende/?letter={{ $ch }}"
|
||||
:class="search ? 'inactive pointer-events-none' : ''"
|
||||
class="odd:bg-stone-100 even:bg-zinc-100 mr-1 border-zinc-300 border-x border-t [&>a[aria-current='page']]:font-bold px-2 py-0.5 no-underline transition-all duration-75"
|
||||
{{ if and (not $model.search) (eq $model.letter $ch) }}aria-current="page"{{ end }}>
|
||||
{{ $ch }}
|
||||
</a>
|
||||
{{- end -}}
|
||||
<i class="ml-2 ri-hourglass-2-fill request-indicator spinning"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right side group: Search and Spalten menu -->
|
||||
<!-- Left side group: Search and Alphabet -->
|
||||
<div class="flex items-end gap-4">
|
||||
<!-- 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>
|
||||
@@ -38,10 +106,10 @@
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#baenderesults"
|
||||
role="search"
|
||||
@submit="offset = 0; hasMore = true"
|
||||
aria-label="Bändesuche">
|
||||
{{- if $model.letter -}}
|
||||
<input type="hidden" name="letter" value="{{- $model.letter -}}" />
|
||||
{{- end -}}
|
||||
<input type="hidden" name="sort" :value="sortField" />
|
||||
<input type="hidden" name="order" :value="sortOrder" />
|
||||
<input
|
||||
class="px-2 py-0.5 font-sans placeholder:italic w-full text-lg"
|
||||
type="search"
|
||||
@@ -56,45 +124,114 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alphabet navigation toggle -->
|
||||
<div class="relative">
|
||||
<details class="font-sans text-base list-none" data-role="alphabet-toggle" @toggle="alphabetOpen = $el.open">
|
||||
<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">
|
||||
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">
|
||||
{{- 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 }}"
|
||||
hx-target="#baenderesults"
|
||||
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 }}'"
|
||||
: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">
|
||||
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 }">
|
||||
<details class="font-sans text-base list-none" data-role="baende-column-toggle" @toggle="open = $el.open">
|
||||
<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">
|
||||
Spalten
|
||||
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
|
||||
</summary>
|
||||
<div class="absolute left-0 mt-2 w-56 z-10 bg-white rounded-md shadow-lg border border-gray-200">
|
||||
<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>
|
||||
|
||||
<!-- Count and New button -->
|
||||
<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 }} / {{ end }}{{ if $model.total_count }}{{ $model.total_count }}{{ else }}{{ len $model.result.Entries }}{{ end }} Bände
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="content-action-button"
|
||||
onclick="window.location.assign('/almanach/new')">
|
||||
<i class="ri-add-line"></i>
|
||||
<span>Neuer Band</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="flex justify-between mt-10">
|
||||
<div class="relative" x-data="{ open: false }">
|
||||
<details class="font-sans text-base list-none" data-role="baende-column-toggle" @toggle="open = $el.open">
|
||||
<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">
|
||||
Spalten
|
||||
<i class="ri-arrow-down-s-line transform origin-center transition-transform" :class="{ 'rotate-180': open }"></i>
|
||||
</summary>
|
||||
<div class="absolute left-0 mt-2 w-56 z-10 bg-white rounded-md shadow-lg border border-gray-200">
|
||||
<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 class="flex items-center gap-3">
|
||||
<div id="baende-count" class="text-lg font-semibold font-sans text-gray-600 whitespace-nowrap">
|
||||
{{ len $model.result.Entries }} Bände
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="content-action-button"
|
||||
onclick="window.location.assign('/almanach/new')">
|
||||
<i class="ri-add-line"></i>
|
||||
<span>Neuer Band</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="baenderesults" class="mt-2">
|
||||
<div id="baenderesults" class="mt-2" data-next-offset="{{ $model.next_offset }}">
|
||||
{{ template "_baende_table" $model }}
|
||||
|
||||
<!-- Load More Button -->
|
||||
<div class="mt-6 flex justify-center" x-show="hasMore">
|
||||
<button
|
||||
type="button"
|
||||
class="content-action-button"
|
||||
:hx-get="loadMoreUrl()"
|
||||
hx-target="#baende-tbody"
|
||||
hx-swap="beforeend"
|
||||
hx-indicator="this"
|
||||
@htmx:before-request="loading = true"
|
||||
@htmx:after-request="
|
||||
loading = false;
|
||||
hasMore = $event.detail.xhr.getResponseHeader('X-Has-More') === 'true';
|
||||
const nextOffsetHeader = Number($event.detail.xhr.getResponseHeader('X-Next-Offset'));
|
||||
if (!Number.isNaN(nextOffsetHeader)) {
|
||||
offset = nextOffsetHeader;
|
||||
}
|
||||
updateUrl();
|
||||
"
|
||||
:disabled="loading">
|
||||
<i class="ri-arrow-down-line" :class="{ 'spinning': loading }"></i>
|
||||
<span x-text="loading ? 'Lädt...' : 'Weitere 150 laden'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,16 +4,72 @@
|
||||
<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>
|
||||
<th class="py-2 pr-4 whitespace-nowrap w-[44rem]">Titel</th>
|
||||
<th class="py-2 pr-4 pl-2 whitespace-nowrap w-[10rem]"
|
||||
: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"
|
||||
@click="changeSort('alm')">
|
||||
<span class="font-semibold tracking-wide">Alm</span>
|
||||
<i class="text-xs opacity-70 transition-colors"
|
||||
:class="{
|
||||
'ri-arrow-up-line text-blue-600': sortField === 'alm' && sortOrder === 'asc',
|
||||
'ri-arrow-down-line text-blue-600': sortField === 'alm' && sortOrder === 'desc',
|
||||
'ri-arrow-up-down-line text-gray-400': sortField !== 'alm'
|
||||
}"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th class="py-2 pr-4 whitespace-nowrap w-[44rem]"
|
||||
: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"
|
||||
@click="changeSort('title')">
|
||||
<span class="font-semibold tracking-wide">Titel</span>
|
||||
<i class="text-xs opacity-70 transition-colors"
|
||||
:class="{
|
||||
'ri-arrow-up-line text-blue-600': sortField === 'title' && sortOrder === 'asc',
|
||||
'ri-arrow-down-line text-blue-600': sortField === 'title' && sortOrder === 'desc',
|
||||
'ri-arrow-up-down-line text-gray-400': sortField !== 'title'
|
||||
}"
|
||||
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">Jahr</th>
|
||||
<th class="py-2 pr-4 whitespace-nowrap col-year hidden"
|
||||
: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"
|
||||
@click="changeSort('year')">
|
||||
<span class="font-semibold tracking-wide">Jahr</span>
|
||||
<i class="text-xs opacity-70 transition-colors"
|
||||
:class="{
|
||||
'ri-arrow-up-line text-blue-600': sortField === 'year' && sortOrder === 'asc',
|
||||
'ri-arrow-down-line text-blue-600': sortField === 'year' && sortOrder === 'desc',
|
||||
'ri-arrow-up-down-line text-gray-400': sortField !== 'year'
|
||||
}"
|
||||
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">Signaturen</th>
|
||||
<th class="py-2 pr-4 whitespace-nowrap col-signatures"
|
||||
: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"
|
||||
@click="changeSort('signatur')">
|
||||
<span class="font-semibold tracking-wide">Signaturen</span>
|
||||
<i class="text-xs opacity-70 transition-colors"
|
||||
:class="{
|
||||
'ri-arrow-up-line text-blue-600': sortField === 'signatur' && sortOrder === 'asc',
|
||||
'ri-arrow-down-line text-blue-600': sortField === 'signatur' && sortOrder === 'desc',
|
||||
'ri-arrow-up-down-line text-gray-400': sortField !== 'signatur'
|
||||
}"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody id="baende-tbody">
|
||||
{{- range $_, $entry := $model.result.Entries -}}
|
||||
<tr class="border-b align-top cursor-pointer transition-colors odd:bg-white even:bg-stone-50/60 hover:bg-stone-100"
|
||||
data-role="baende-row"
|
||||
|
||||
166
views/routes/baende/more/body.gohtml
Normal file
166
views/routes/baende/more/body.gohtml
Normal file
@@ -0,0 +1,166 @@
|
||||
{{ $model := . }}
|
||||
|
||||
{{- range $_, $entry := $model.result.Entries -}}
|
||||
<tr class="border-b align-top cursor-pointer transition-colors odd:bg-white even:bg-stone-50/60 hover:bg-stone-100"
|
||||
data-role="baende-row"
|
||||
data-entry-id="{{ $entry.MusenalmID }}"
|
||||
hx-get="/baende/details/{{ $entry.MusenalmID }}"
|
||||
hx-target="this"
|
||||
hx-swap="outerHTML">
|
||||
<td class="py-2 pr-4 pl-2 whitespace-nowrap w-[10rem]">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="inline-flex items-center rounded-xs bg-stone-100 px-2.5 py-0.5 text-xs font-semibold text-slate-700">Alm {{ $entry.MusenalmID }}</span>
|
||||
{{- if $entry.References -}}
|
||||
<span class="inline-flex items-center rounded-xs bg-stone-100 px-2.5 py-0.5 text-xs text-slate-700 max-w-[10rem] whitespace-normal break-words" title="{{ $entry.References }}">{{ $entry.References }}</span>
|
||||
{{- 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">
|
||||
<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">
|
||||
<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?');">
|
||||
<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">
|
||||
<i class="ri-delete-bin-line"></i>
|
||||
</button>
|
||||
<div class="data-tip">Löschen</div>
|
||||
</tool-tip>
|
||||
</form>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-2 pr-4">
|
||||
<div class="font-semibold text-slate-900 text-base leading-snug">
|
||||
{{- if $entry.PreferredTitle -}}
|
||||
{{ $entry.PreferredTitle }}
|
||||
{{- else if ne $entry.Year 0 -}}
|
||||
{{ $entry.Year }}
|
||||
{{- 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>
|
||||
<div class="data-tip">
|
||||
{{- if eq $entry.EditState "Unknown" -}}
|
||||
Gesucht
|
||||
{{- else if eq $entry.EditState "ToDo" -}}
|
||||
Zu erledigen
|
||||
{{- else if eq $entry.EditState "Review" -}}
|
||||
Überprüfen
|
||||
{{- else if eq $entry.EditState "Seen" -}}
|
||||
Autopsiert
|
||||
{{- else if eq $entry.EditState "Edited" -}}
|
||||
Vollständig Erfasst
|
||||
{{- else -}}
|
||||
{{ $entry.EditState }}
|
||||
{{- end -}}
|
||||
</div>
|
||||
</tool-tip>
|
||||
</div>
|
||||
{{- if $entry.TitleStmt -}}
|
||||
<div class="text-gray-700 text-base mt-1 leading-snug">
|
||||
{{ $entry.TitleStmt }}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- if or $entry.SubtitleStmt $entry.VariantTitle $entry.ParallelTitle $entry.IncipitStmt -}}
|
||||
<div class="flex flex-col gap-1 text-sm text-gray-700 mt-1">
|
||||
{{- if $entry.SubtitleStmt -}}
|
||||
<div><span class="font-semibold text-gray-500">Untertitel:</span> {{ $entry.SubtitleStmt }}</div>
|
||||
{{- end -}}
|
||||
{{- if $entry.VariantTitle -}}
|
||||
<div><span class="font-semibold text-gray-500">Varianten:</span> {{ $entry.VariantTitle }}</div>
|
||||
{{- end -}}
|
||||
{{- if $entry.ParallelTitle -}}
|
||||
<div><span class="font-semibold text-gray-500">Parallel:</span> {{ $entry.ParallelTitle }}</div>
|
||||
{{- end -}}
|
||||
{{- if $entry.IncipitStmt -}}
|
||||
<div><span class="font-semibold text-gray-500">Incipit:</span> {{ $entry.IncipitStmt }}</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</td>
|
||||
<td class="py-2 pr-4 col-appearance">
|
||||
{{- if or $entry.ResponsibilityStmt $entry.PublicationStmt $entry.PlaceStmt -}}
|
||||
<div class="flex flex-col gap-2 text-sm text-gray-700">
|
||||
{{- if and $entry.ResponsibilityStmt (not (eq $entry.ResponsibilityStmt "unbezeichnet")) -}}
|
||||
<div>
|
||||
<div class="text-xs font-semibold text-gray-500">Herausgaberangabe</div>
|
||||
<div>{{ $entry.ResponsibilityStmt }}</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- if $entry.PublicationStmt -}}
|
||||
<div>
|
||||
<div class="text-xs font-semibold text-gray-500">Publikationsangabe</div>
|
||||
<div>{{ $entry.PublicationStmt }}</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- if $entry.PlaceStmt -}}
|
||||
<div>
|
||||
<div class="text-xs font-semibold text-gray-500">Ortsangabe</div>
|
||||
<div>{{ $entry.PlaceStmt }}</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</td>
|
||||
<td class="py-2 pr-4 whitespace-nowrap col-year hidden">
|
||||
{{- if ne $entry.Year 0 -}}
|
||||
{{ $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 -}}
|
||||
<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>
|
||||
{{- end -}}
|
||||
{{- if $entry.Dimensions -}}
|
||||
<div><span class="font-semibold text-gray-500">Maße:</span> {{ $entry.Dimensions }}</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</td>
|
||||
<td class="py-2 pr-4 col-signatures">
|
||||
{{- $items := index $model.result.Items $entry.Id -}}
|
||||
{{- 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">
|
||||
{{- 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">
|
||||
{{- range $i, $media := $item.Media -}}{{- if $i }}, {{ end -}}{{ $media }}{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</td>
|
||||
</tr>
|
||||
{{- end -}}
|
||||
|
||||
<!-- Update count display with OOB swap -->
|
||||
<div id="baende-count" class="text-lg font-semibold font-sans text-gray-600 whitespace-nowrap" hx-swap-oob="true">
|
||||
{{ if $model.current_count }}{{ $model.current_count }} / {{ end }}{{ if $model.total_count }}{{ $model.total_count }}{{ else }}{{ len $model.result.Entries }}{{ end }} Bände
|
||||
</div>
|
||||
@@ -1,11 +1,32 @@
|
||||
{{ $model := . }}
|
||||
|
||||
<div id="baenderesults">
|
||||
<div id="baenderesults"
|
||||
x-data="{ hasMore: {{ if $model.has_more }}true{{ else }}false{{ end }}, offset: {{ if $model.next_offset }}{{ $model.next_offset }}{{ else }}0{{ end }}, loading: false }">
|
||||
|
||||
<div id="baende-count" class="text-lg font-semibold font-sans text-gray-600 whitespace-nowrap" hx-swap-oob="true">
|
||||
{{ len $model.result.Entries }} Bände
|
||||
{{ if $model.current_count }}{{ $model.current_count }} / {{ end }}{{ if $model.total_count }}{{ $model.total_count }}{{ else }}{{ len $model.result.Entries }}{{ end }} Bände
|
||||
</div>
|
||||
|
||||
{{ template "_baende_table" $model }}
|
||||
|
||||
<!-- Load More Button -->
|
||||
<div class="mt-6 flex justify-center" x-show="hasMore">
|
||||
<button
|
||||
type="button"
|
||||
class="content-action-button"
|
||||
hx-get="/baende/more/?offset={{ if $model.next_offset }}{{ $model.next_offset }}{{ else }}0{{ end }}{{ if $model.search }}&search={{ $model.search }}{{ end }}{{ if $model.letter }}&letter={{ $model.letter }}{{ end }}&sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
|
||||
hx-push-url="/baende/?offset={{ if $model.next_offset }}{{ $model.next_offset }}{{ else }}0{{ end }}{{ if $model.search }}&search={{ $model.search }}{{ end }}{{ if $model.letter }}&letter={{ $model.letter }}{{ end }}&sort={{ $model.sort_field }}&order={{ $model.sort_order }}"
|
||||
hx-target="#baende-tbody"
|
||||
hx-swap="beforeend"
|
||||
hx-indicator="this"
|
||||
@htmx:before-request="loading = true"
|
||||
@htmx:after-request="loading = false; hasMore = $event.detail.xhr.getResponseHeader('X-Has-More') === 'true'; offset = {{ if $model.next_offset }}{{ $model.next_offset }}{{ else }}0{{ end }}"
|
||||
:disabled="loading">
|
||||
<i class="ri-arrow-down-line" :class="{ 'spinning': loading }"></i>
|
||||
<span x-text="loading ? 'Lädt...' : 'Weitere 150 laden'"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ if $model.search }}
|
||||
<script type="module">
|
||||
let elements = document.querySelectorAll('.baende-text');
|
||||
|
||||
Reference in New Issue
Block a user