FIX: better loading time of /reihen

This commit is contained in:
Simon Martens
2026-01-24 16:59:19 +01:00
parent 9e49aeb3d1
commit 5d75934c37
12 changed files with 272 additions and 188 deletions

View File

@@ -15,6 +15,7 @@ import (
const ( const (
URL_REIHEN = "/reihen/" URL_REIHEN = "/reihen/"
URL_REIHEN_RESULTS = "/reihen/results/"
PARAM_LETTER = "letter" PARAM_LETTER = "letter"
PARAM_SEARCH = "search" PARAM_SEARCH = "search"
PARAM_PERSON = "agent" PARAM_PERSON = "agent"
@@ -42,54 +43,87 @@ type ReihenPage struct {
func (p *ReihenPage) Setup(router *router.Router[*core.RequestEvent], ia pagemodels.IApp, engine *templating.Engine) error { func (p *ReihenPage) Setup(router *router.Router[*core.RequestEvent], ia pagemodels.IApp, engine *templating.Engine) error {
app := ia.Core() app := ia.Core()
router.GET(URL_REIHEN, func(e *core.RequestEvent) error { router.GET(URL_REIHEN, p.handlePage(engine, app))
search := e.Request.URL.Query().Get(PARAM_SEARCH) router.GET(URL_REIHEN_RESULTS, p.handleResults(engine, app))
if search != "" {
return p.SearchRequest(app, engine, e)
}
person := e.Request.URL.Query().Get(PARAM_PERSON)
if person != "" {
return p.PersonRequest(app, engine, e)
}
place := e.Request.URL.Query().Get(PARAM_PLACE)
if place != "" {
return p.PlaceRequest(app, engine, e)
}
year := e.Request.URL.Query().Get(PARAM_YEAR)
if year != "" {
return p.YearRequest(app, engine, e)
}
return p.LetterRequest(app, engine, e)
})
return nil return nil
} }
func (p *ReihenPage) YearRequest(app core.App, engine *templating.Engine, e *core.RequestEvent) error { func (p *ReihenPage) handlePage(engine *templating.Engine, app core.App) HandleFunc {
year := e.Request.URL.Query().Get(PARAM_YEAR) return func(e *core.RequestEvent) error {
data := make(map[string]any) data, err := p.buildResultData(app, e)
data[PARAM_HIDDEN] = e.Request.URL.Query().Get(PARAM_HIDDEN) if err != nil {
data[PARAM_YEAR] = year return engine.Response404(e, err, data)
}
data["common"] = NewCommonReihenData(app)
return engine.Response200(e, URL_REIHEN, data)
}
}
func (p *ReihenPage) handleResults(engine *templating.Engine, app core.App) HandleFunc {
return func(e *core.RequestEvent) error {
data, err := p.buildResultData(app, e)
if err != nil {
return engine.Response404(e, err, data)
}
return engine.Response200(e, URL_REIHEN_RESULTS, data, "fragment")
}
}
// TODO: Suchverhalten bei gefilterten Personen, Orten und Jahren
func (p *ReihenPage) buildResultData(app core.App, e *core.RequestEvent) (map[string]any, error) {
data := map[string]any{}
data[PARAM_HIDDEN] = e.Request.URL.Query().Get(PARAM_HIDDEN)
search := e.Request.URL.Query().Get(PARAM_SEARCH)
if search != "" {
data[PARAM_SEARCH] = search
result, err := NewSeriesResult_Search(app, search)
if err != nil {
return data, err
}
data["result"] = result
return data, nil
}
person := e.Request.URL.Query().Get(PARAM_PERSON)
if person != "" {
data[PARAM_PERSON] = person
result, err := NewSeriesResult_Agent(app, person)
if err != nil {
return data, err
}
data["result"] = result
return data, nil
}
place := e.Request.URL.Query().Get(PARAM_PLACE)
if place != "" {
data[PARAM_PLACE] = place
result, err := NewSeriesResult_Place(app, place)
if err != nil {
return data, err
}
data["result"] = result
return data, nil
}
year := e.Request.URL.Query().Get(PARAM_YEAR)
if year != "" {
data[PARAM_YEAR] = year
y, err := strconv.Atoi(year) y, err := strconv.Atoi(year)
if err != nil { if err != nil {
return engine.Response404(e, err, data) return data, err
} }
result, err := NewSeriesResult_Year(app, y) result, err := NewSeriesResult_Year(app, y)
if err != nil { if err != nil {
return engine.Response404(e, err, data) return data, err
} }
data["result"] = result data["result"] = result
return p.Get(e, engine, data) return data, nil
} }
func (p *ReihenPage) LetterRequest(app core.App, engine *templating.Engine, e *core.RequestEvent) error {
letter := e.Request.URL.Query().Get(PARAM_LETTER) letter := e.Request.URL.Query().Get(PARAM_LETTER)
data := map[string]interface{}{}
data[PARAM_HIDDEN] = e.Request.URL.Query().Get(PARAM_HIDDEN)
if letter == "" { if letter == "" {
data["startpage"] = true data["startpage"] = true
letter = "A" letter = "A"
@@ -98,61 +132,11 @@ func (p *ReihenPage) LetterRequest(app core.App, engine *templating.Engine, e *c
result, err := NewSeriesListResult_Letter(app, letter) result, err := NewSeriesListResult_Letter(app, letter)
if err != nil { if err != nil {
return engine.Response404(e, err, data) return data, err
} }
data["result"] = result data["result"] = result
return p.Get(e, engine, data) return data, nil
}
func (p *ReihenPage) PersonRequest(app core.App, engine *templating.Engine, e *core.RequestEvent) error {
person := e.Request.URL.Query().Get(PARAM_PERSON)
data := map[string]interface{}{}
data[PARAM_PERSON] = person
data[PARAM_HIDDEN] = e.Request.URL.Query().Get(PARAM_HIDDEN)
result, err := NewSeriesResult_Agent(app, person)
if err != nil {
return engine.Response404(e, err, data)
}
data["result"] = result
return p.Get(e, engine, data)
}
func (p *ReihenPage) PlaceRequest(app core.App, engine *templating.Engine, e *core.RequestEvent) error {
place := e.Request.URL.Query().Get(PARAM_PLACE)
data := map[string]interface{}{}
data[PARAM_PLACE] = place
data[PARAM_HIDDEN] = e.Request.URL.Query().Get(PARAM_HIDDEN)
result, err := NewSeriesResult_Place(app, place)
if err != nil {
return engine.Response404(e, err, data)
}
data["result"] = result
return p.Get(e, engine, data)
}
// TODO: Suchverhalten bei gefilterten Personen, Orten und Jahren
func (p *ReihenPage) SearchRequest(app core.App, engine *templating.Engine, e *core.RequestEvent) error {
search := e.Request.URL.Query().Get(PARAM_SEARCH)
data := map[string]interface{}{}
data[PARAM_SEARCH] = search
result, err := NewSeriesResult_Search(app, search)
if err != nil {
return engine.Response404(e, err, data)
}
data["result"] = result
return p.Get(e, engine, data)
}
func (p *ReihenPage) Get(request *core.RequestEvent, engine *templating.Engine, data map[string]interface{}) error {
data["common"] = NewCommonReihenData(request.App)
return engine.Response200(request, URL_REIHEN, data)
} }
type CommonReihenData struct { type CommonReihenData struct {

View File

@@ -33,7 +33,7 @@
<i class="ri-links-line"></i> Link <i class="ri-links-line"></i> Link
</a> </a>
{{- if (IsAdminOrEditor $request.user) -}} {{- if (IsAdminOrEditor $request.user) -}}
<a href="/reihe/{{ $r.MusenalmID }}/edit/" class="no-underline rounded bg-stone-100 px-1.5"> <a href="/reihe/{{ $r.MusenalmID }}/edit" class="no-underline rounded bg-stone-100 px-1.5">
<i class="ri-edit-line"></i> <i class="ri-edit-line"></i>
Bearbeiten Bearbeiten
</a> </a>

View File

@@ -29,7 +29,6 @@
<i class="ri-eye-line"></i> Orte <i class="ri-eye-line"></i> Orte
</a> </a>
</div> </div>
&middot;
</div> </div>
{{- end -}} {{- end -}}
</div> </div>
@@ -45,7 +44,7 @@
<tool-tip position="top" class="!inline"> <tool-tip position="top" class="!inline">
<div class="data-tip">{{ $model.result.Prev.Name }}</div> <div class="data-tip">{{ $model.result.Prev.Name }}</div>
<a <a
href="/ort/{{ $model.result.Prev.Id }}/edit/" href="/ort/{{ $model.result.Prev.Id }}/edit"
class="text-gray-700 hover:text-slate-950 no-underline"> class="text-gray-700 hover:text-slate-950 no-underline">
<i class="ri-arrow-left-s-line"></i> <i class="ri-arrow-left-s-line"></i>
</a> </a>
@@ -62,7 +61,7 @@
<tool-tip position="top" class="!inline"> <tool-tip position="top" class="!inline">
<div class="data-tip">{{ $model.result.Next.Name }}</div> <div class="data-tip">{{ $model.result.Next.Name }}</div>
<a <a
href="/ort/{{ $model.result.Next.Id }}/edit/" href="/ort/{{ $model.result.Next.Id }}/edit"
class="text-gray-700 hover:text-slate-950 no-underline"> class="text-gray-700 hover:text-slate-950 no-underline">
<i class="ri-arrow-right-s-line"></i> <i class="ri-arrow-right-s-line"></i>
</a> </a>

View File

@@ -32,7 +32,7 @@
</div> </div>
{{- end -}} {{- end -}}
{{- if (IsAdminOrEditor $model.request.user) -}} {{- 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"> <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 <i class="ri-edit-line"></i> Bearbeiten
</a> </a>
{{- end -}} {{- end -}}

View File

@@ -25,7 +25,6 @@
<i class="ri-eye-line"></i> Anschauen <i class="ri-eye-line"></i> Anschauen
</a> </a>
</div> </div>
&middot;
</div> </div>
{{- end -}} {{- end -}}
</div> </div>

View File

@@ -48,7 +48,7 @@
<div class="font-sans"> <div class="font-sans">
&middot;&nbsp; &middot;&nbsp;
<i class="ri-edit-line"></i> <i class="ri-edit-line"></i>
<a href="/reihe/{{ $r.MusenalmID }}/edit/">Bearbeiten</a> <a href="/reihe/{{ $r.MusenalmID }}/edit">Bearbeiten</a>
</div> </div>
{{- end -}} {{- end -}}
</div> </div>

View File

@@ -25,7 +25,6 @@
<i class="ri-eye-line"></i> Anschauen <i class="ri-eye-line"></i> Anschauen
</a> </a>
</div> </div>
&middot;
</div> </div>
{{- end -}} {{- end -}}
</div> </div>
@@ -41,7 +40,7 @@
<tool-tip position="top" class="!inline"> <tool-tip position="top" class="!inline">
<div class="data-tip">{{ $model.result.Prev.Title }}</div> <div class="data-tip">{{ $model.result.Prev.Title }}</div>
<a <a
href="/reihe/{{ $model.result.Prev.MusenalmID }}/edit/" href="/reihe/{{ $model.result.Prev.MusenalmID }}/edit"
class="text-gray-700 hover:text-slate-950 no-underline"> class="text-gray-700 hover:text-slate-950 no-underline">
<i class="ri-arrow-left-s-line"></i> <i class="ri-arrow-left-s-line"></i>
</a> </a>
@@ -58,7 +57,7 @@
<tool-tip position="top" class="!inline"> <tool-tip position="top" class="!inline">
<div class="data-tip">{{ $model.result.Next.Title }}</div> <div class="data-tip">{{ $model.result.Next.Title }}</div>
<a <a
href="/reihe/{{ $model.result.Next.MusenalmID }}/edit/" href="/reihe/{{ $model.result.Next.MusenalmID }}/edit"
class="text-gray-700 hover:text-slate-950 no-underline"> class="text-gray-700 hover:text-slate-950 no-underline">
<i class="ri-arrow-right-s-line"></i> <i class="ri-arrow-right-s-line"></i>
</a> </a>

View File

@@ -69,79 +69,17 @@
{{ end }} {{ end }}
<!-- INFO: 3. Treffer --> <div
id="reihenresults"
hx-get="/reihen/results/{{- if $model.request.query -}}?{{- $model.request.query -}}{{- end -}}"
hx-trigger="load"
hx-target="this"
hx-swap="outerHTML"
hx-history="false"
hx-indicator="body">
<div id="searchcontent" class="font-serif"> <div id="searchcontent" class="font-serif">
{{ if and .search $model.result.IDSeries }} <div class="flex justify-center text-sm text-stone-600">Reihen laden…</div>
<div class="mb-1 max-w-[60rem] hyphens-auto">
{{ range $id, $r := $model.result.IDSeries }}
{{ template "_reihe" (Arr $r $model.result.Entries $model.result.EntriesSeries
true false false $model.request) }}
{{ end }}
</div> </div>
{{ end }}
{{ if $model.result.Series }}
<div class="mb-1 max-w-[60rem] hyphens-auto">
{{ range $id, $r := $model.result.Series }}
{{ template "_reihe" (Arr $r $model.result.Entries $model.result.EntriesSeries false false
false $model.request)
}}
{{ end }}
</div>
{{ end }}
{{ if and .search $model.result.AltSeries }}
{{ if $model.result.IDSeries }}
<div class="border-b text-sm font-sans text-right pb-0.5">
Treffer in Almanach-Nummer &uarr;
</div>
{{ end }}
{{ if $model.result.Series }}
<div class="border-b text-sm font-sans text-right pb-0.5">
Treffer in Reihentiteln &uarr;
</div>
{{ end }}
{{ if not (or $model.result.Series $model.result.IDSeries) }}
<div class="border-b text-sm font-sans text-right pb-0.5">
Keine Treffer im Reihentitel
<i class="ri-forbid-line inline-bloc -mr-0.5"></i>
</div>
{{ end }}
<div class="border-t mb-1.5 text-sm font-sans text-right pt-0.5">
Treffer in allen Feldern (inkl. Anmerkungen &amp; Verweise) &darr;
</div>
<div class="mb-1 max-w-[60rem] hyphens-auto">
{{ range $id, $r := $model.result.AltSeries }}
{{ template "_reihe" (Arr $r $model.result.Entries $model.result.EntriesSeries
false true true $model.request) }}
{{ end }}
</div>
{{ end }}
{{ if not (or $model.result.Series $model.result.AltSeries $model.result.IDSeries) }}
<div class="mt-8">
Keine Reihen
{{ if .search }}für {{ .search }}{{ end }}
gefunden.
</div>
{{ end }}
{{ if $model.search }}
<script type="module">
let elements = document.querySelectorAll('.reihen-text');
let mark_instance = new Mark(elements);
// INFO: we wait a little bit before marking, to settle everything
setTimeout(() => {
mark_instance.mark('{{ $model.search }}', {
"seperateWordSearch": true,
});
}, 200);
</script>
{{ end }}
<!-- INFO: Ende 3. Treffer -->
</div> </div>
<!-- INFO: Ende 2. Breite Anzeige --> <!-- INFO: Ende 2. Breite Anzeige -->

View File

@@ -32,21 +32,26 @@
<div class="pb-0 border-b-4 border-zinc-300 grow"> <div class="pb-0 border-b-4 border-zinc-300 grow">
<form <form
method="GET" method="GET"
hx-boost="false" action="/reihen/"
hx-get="/reihen/results/"
hx-indicator="body"
hx-push-url="false"
hx-swap="outerHTML"
hx-target="#reihenresults"
x-target="searchcontent" x-target="searchcontent"
role="search" role="search"
aria-label="Reihentitelsuche"> aria-label="Reihentitelsuche">
{{- if $model.letter -}} {{- if $model.letter -}}
<input type="hidden" name="letter" value="{{- $model.letter -}}" /> <input type="hidden" name="letter" value="{{- $model.letter -}}" :disabled="search" />
{{- end -}} {{- end -}}
{{- if and $model.result $model.result.Agent -}} {{- if and $model.result $model.result.Agent -}}
<input type="hidden" name="agent" value="{{- $model.result.Agent.Id -}}" /> <input type="hidden" name="agent" value="{{- $model.result.Agent.Id -}}" :disabled="search" />
{{- end -}} {{- end -}}
{{- if and $model.result $model.result.Place -}} {{- if and $model.result $model.result.Place -}}
<input type="hidden" name="place" value="{{- $model.result.Place.Id -}}" /> <input type="hidden" name="place" value="{{- $model.result.Place.Id -}}" :disabled="search" />
{{- end -}} {{- end -}}
{{- if $model.year -}} {{- if $model.year -}}
<input type="hidden" name="year" value="{{- $model.year -}}" /> <input type="hidden" name="year" value="{{- $model.year -}}" :disabled="search" />
{{- end -}} {{- end -}}
<input <input
class="px-1.5 font-serif placeholder:italic w-full" class="px-1.5 font-serif placeholder:italic w-full"

View File

@@ -44,7 +44,7 @@
<span class="filtercategory">Erscheinungsort</span> &middot; <span class="filtercategory">Erscheinungsort</span> &middot;
<span class="filterterm">{{ $model.result.Place.Name }}</span> <span class="filterterm">{{ $model.result.Place.Name }}</span>
{{- if (IsAdminOrEditor $model.request.user) -}} {{- if (IsAdminOrEditor $model.request.user) -}}
<a href="/ort/{{ $model.result.Place.Id }}/edit/" class="no-underline rounded bg-stone-100 px-1.5"> <a href="/ort/{{ $model.result.Place.Id }}/edit" class="no-underline rounded bg-stone-100 px-1.5">
<i class="ri-edit-line"></i> <i class="ri-edit-line"></i>
</a> </a>
{{- end -}} {{- end -}}

View File

@@ -0,0 +1,83 @@
{{ $model := . }}
<div id="reihenfilters">
{{ $model := . }}
<div
class="hidden lg:flex min-w-[32rem] max-w-[32rem] float-right ml-6 flex-col gap-y-8 [&>*]:pb-12
[&>*]:px-12 [&>*]:pt-8 [&>*]:bg-slate-100"
id="reihenfilter">
{{ if $model.common.Agents }}
<div class="">
<h2 class="mb-6">Herausgeber:innen, Verlage &amp; Druckereien</h2>
<filter-list
class="min-h-[20rem] border-b border-zinc-300"
id="agent-list"
data-url="/reihen/?{{- if $model.query_without_agent -}}{{- $model.query_without_agent -}}&{{- end -}}agent="
data-queryparam="agent"
data-placeholder="Personen und Körperschaften filtern..."></filter-list>
</div>
<script type="module">
let agentList = document.getElementById("agent-list");
if (agentList) {
agentList.items = {{ $model.common.Agents }};
agentList.setSearchTextFunc((item) => {
return item.name;
});
agentList.setLinkTextFunc((item) => {
return `
<span class="filter-list-searchable">${item.name}</span>
<span class="text-xs text-stone-500 whitespace-nowrap font-sans">
${item.corporate_body ? "Verlag/Druck/Vertrieb" : item.biographical_data}
</span>
`;
});
}
</script>
{{ end }}
{{ if $model.common.Places }}
<div>
<h2 class="mb-6">Erscheinungsorte</h2>
<filter-list
id="place-list"
data-url="/reihen/?{{- if $model.query_without_place -}}{{- $model.query_without_place -}}&{{- end -}}place="
data-queryparam="place"
data-placeholder="Erscheinungsorte filtern..."></filter-list>
</div>
<script type="module">
let placeList = document.getElementById("place-list");
if (placeList) placeList.items = {{ $model.common.Places }};
</script>
{{ end }}
{{ if $model.common.Years }}
<div>
<h2 class="mb-6">Geltungsjahre</h2>
<filter-list
id="year-list"
data-url="/reihen/?{{- if $model.query_without_year -}}{{- $model.query_without_year -}}&{{- end -}}year="
data-queryparam="year"
data-filterstart="true"
data-placeholder="Nach Geltungsjahren filtern..."></filter-list>
</div>
<script type="module">
let yearList = document.getElementById("year-list");
if (yearList) {
yearList.items = {{ $model.common.Years }};
yearList.setHREFFunc((item) => {
return String(item);
});
yearList.setLinkTextFunc((item) => {
if (item === 0) return "ohne Jahr";
return String(item);
});
}
</script>
{{ end }}
</div>
</div>

View File

@@ -0,0 +1,77 @@
{{ $model := . }}
<div id="reihenresults">
<!-- INFO: 3. Treffer -->
<div id="searchcontent" class="font-serif">
{{ if and .search $model.result.IDSeries }}
<div class="mb-1 max-w-[60rem] hyphens-auto">
{{ range $id, $r := $model.result.IDSeries }}
{{ template "_reihe" (Arr $r $model.result.Entries $model.result.EntriesSeries
true false false $model.request) }}
{{ end }}
</div>
{{ end }}
{{ if $model.result.Series }}
<div class="mb-1 max-w-[60rem] hyphens-auto">
{{ range $id, $r := $model.result.Series }}
{{ template "_reihe" (Arr $r $model.result.Entries $model.result.EntriesSeries false false
false $model.request)
}}
{{ end }}
</div>
{{ end }}
{{ if and .search $model.result.AltSeries }}
{{ if $model.result.IDSeries }}
<div class="border-b text-sm font-sans text-right pb-0.5">
Treffer in Almanach-Nummer &uarr;
</div>
{{ end }}
{{ if $model.result.Series }}
<div class="border-b text-sm font-sans text-right pb-0.5">
Treffer in Reihentiteln &uarr;
</div>
{{ end }}
{{ if not (or $model.result.Series $model.result.IDSeries) }}
<div class="border-b text-sm font-sans text-right pb-0.5">
Keine Treffer im Reihentitel
<i class="ri-forbid-line inline-bloc -mr-0.5"></i>
</div>
{{ end }}
<div class="border-t mb-1.5 text-sm font-sans text-right pt-0.5">
Treffer in allen Feldern (inkl. Anmerkungen &amp; Verweise) &darr;
</div>
<div class="mb-1 max-w-[60rem] hyphens-auto">
{{ range $id, $r := $model.result.AltSeries }}
{{ template "_reihe" (Arr $r $model.result.Entries $model.result.EntriesSeries
false true true $model.request) }}
{{ end }}
</div>
{{ end }}
{{ if not (or $model.result.Series $model.result.AltSeries $model.result.IDSeries) }}
<div class="mt-8">
Keine Reihen
{{ if .search }}für {{ .search }}{{ end }}
gefunden.
</div>
{{ end }}
{{ if $model.search }}
<script type="module">
let elements = document.querySelectorAll('.reihen-text');
let mark_instance = new Mark(elements);
// INFO: we wait a little bit before marking, to settle everything
setTimeout(() => {
mark_instance.mark('{{ $model.search }}', {
"seperateWordSearch": true,
});
}, 200);
</script>
{{ end }}
<!-- INFO: Ende 3. Treffer -->
</div>
</div>