Filtering now works

This commit is contained in:
Simon Martens
2025-03-01 01:23:56 +01:00
parent ca33ec7be3
commit 3968f6b1b4
10 changed files with 408 additions and 127 deletions

View File

@@ -94,6 +94,7 @@ var CONTENTS_FTS5_FIELDS = []string{
ENTRIES_TABLE, ENTRIES_TABLE,
AGENTS_TABLE, AGENTS_TABLE,
MUSENALMID_FIELD, MUSENALMID_FIELD,
MUSENALM_INHALTE_TYPE_FIELD,
ANNOTATION_FIELD, ANNOTATION_FIELD,
COMMENT_FIELD, COMMENT_FIELD,
} }
@@ -375,6 +376,11 @@ func FTS5ValuesContent(content *Content, entry *Entry, agents []*Agent) []string
entrystring += "; [o.J.]" entrystring += "; [o.J.]"
} }
typestring := ""
for _, typ := range content.MusenalmType() {
typestring += typ + " "
}
return []string{ return []string{
content.PreferredTitle(), content.PreferredTitle(),
content.VariantTitle(), content.VariantTitle(),
@@ -392,6 +398,7 @@ func FTS5ValuesContent(content *Content, entry *Entry, agents []*Agent) []string
entrystring, entrystring,
agentstring, agentstring,
strconv.Itoa(content.MusenalmID()), strconv.Itoa(content.MusenalmID()),
typestring,
datatypes.DeleteTags(content.Annotation()), datatypes.DeleteTags(content.Annotation()),
datatypes.DeleteTags(content.Comment()), datatypes.DeleteTags(content.Comment()),
} }

View File

@@ -15,6 +15,13 @@ func Sort_Series_Title(series []*Series) {
}) })
} }
func Sort_Agents_Name(agents []*Agent) {
collator := collate.New(language.German)
slices.SortFunc(agents, func(i, j *Agent) int {
return collator.CompareString(i.Name(), j.Name())
})
}
func Sort_Entries_Title_Year(entries []*Entry) { func Sort_Entries_Title_Year(entries []*Entry) {
collator := collate.New(language.German) collator := collate.New(language.German)
slices.SortFunc(entries, func(i, j *Entry) int { slices.SortFunc(entries, func(i, j *Entry) int {

View File

@@ -76,12 +76,13 @@ func (p *SuchePage) SimpleSearchReihenRequest(app core.App, engine *templating.E
func (p *SuchePage) SearchBeitraegeRequest(app core.App, engine *templating.Engine, e *core.RequestEvent, params SearchParameters) error { func (p *SuchePage) SearchBeitraegeRequest(app core.App, engine *templating.Engine, e *core.RequestEvent, params SearchParameters) error {
data := make(map[string]interface{}) data := make(map[string]interface{})
filterparams := NewBeitraegeFilterParameters(e)
result, err := NewSearchBeitraege(app, params) result, err := NewSearchBeitraege(app, params, filterparams)
if err != nil { if err != nil {
return engine.Response404(e, err, nil) return engine.Response404(e, err, nil)
} }
data["filters"] = filterparams
data["parameters"] = params data["parameters"] = params
data["result"] = result data["result"] = result
return engine.Response200(e, p.Template+params.Collection+"/", data, p.Layout) return engine.Response200(e, p.Template+params.Collection+"/", data, p.Layout)

View File

@@ -2,6 +2,9 @@ package pages
import ( import (
"database/sql" "database/sql"
"maps"
"slices"
"sort"
"github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels"
"github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes" "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes"
@@ -10,8 +13,60 @@ import (
const ( const (
DEFAULT_PAGESIZE = 80 DEFAULT_PAGESIZE = 80
FILTER_PARAM_BEIAEGE_AGENT = "agentfilter"
FILTER_PARAM_BEIAEGE_TYPE = "typefilter"
FILTER_PARAM_BEIAEGE_ONLYSCANS = "onlyscans"
FILTER_PARAM_BEIAEGE_YEAR = "yearfilter"
) )
type BeitraegeFilterParameters struct {
Agent string
Type string
Year string
OnlyScans bool
}
func NewBeitraegeFilterParameters(ev *core.RequestEvent) BeitraegeFilterParameters {
agent := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_AGENT)
typ := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_TYPE)
year := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_YEAR)
onlyscans := ev.Request.URL.Query().Get(FILTER_PARAM_BEIAEGE_ONLYSCANS) == "on"
return BeitraegeFilterParameters{
Agent: agent,
Type: typ,
Year: year,
OnlyScans: onlyscans,
}
}
func (p *BeitraegeFilterParameters) FieldSetBeitraege() []dbmodels.FTS5QueryRequest {
ret := []dbmodels.FTS5QueryRequest{}
if p.Agent != "" {
q := "\"" + p.Agent + "\""
que := dbmodels.NormalizeQuery(q)
req := dbmodels.IntoQueryRequests([]string{dbmodels.AGENTS_TABLE}, que)
ret = append(ret, req...)
}
if p.Type != "" {
q := "\"" + p.Type + "\""
que := dbmodels.NormalizeQuery(q)
req := dbmodels.IntoQueryRequests([]string{dbmodels.MUSENALM_INHALTE_TYPE_FIELD}, que)
ret = append(ret, req...)
}
if p.Year != "" {
q := "\"" + p.Year + "\""
que := dbmodels.NormalizeQuery(q)
req := dbmodels.IntoQueryRequests([]string{dbmodels.ENTRIES_TABLE}, que)
ret = append(ret, req...)
}
return ret
}
type SearchResultBeitraege struct { type SearchResultBeitraege struct {
Queries []dbmodels.FTS5QueryRequest Queries []dbmodels.FTS5QueryRequest
@@ -23,6 +78,10 @@ type SearchResultBeitraege struct {
ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key: Content ID ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key: Content ID
Pages []int Pages []int
AgentsList []*dbmodels.Agent
TypesList []string
YearList []int
} }
func EmptyResultBeitraege() *SearchResultBeitraege { func EmptyResultBeitraege() *SearchResultBeitraege {
@@ -35,9 +94,11 @@ func EmptyResultBeitraege() *SearchResultBeitraege {
} }
} }
func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBeitraege, error) { func NewSearchBeitraege(app core.App, params SearchParameters, filters BeitraegeFilterParameters) (*SearchResultBeitraege, error) {
contents := []*dbmodels.Content{} contents := []*dbmodels.Content{}
queries := params.FieldSetBeitraege() queries := params.FieldSetBeitraege()
fqueries := filters.FieldSetBeitraege()
queries = append(queries, fqueries...)
if params.AlmString != "" { if params.AlmString != "" {
e, err := dbmodels.Contents_MusenalmID(app, params.AlmString) e, err := dbmodels.Contents_MusenalmID(app, params.AlmString)
@@ -70,14 +131,33 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei
return nil, err return nil, err
} }
if filters.OnlyScans {
scans := []*dbmodels.Content{}
for _, c := range cs {
if len(c.Scans()) > 0 {
scans = append(scans, c)
}
}
cs = scans
}
contents = append(contents, cs...) contents = append(contents, cs...)
} }
resultids := []any{} resultids := []any{}
resultentryids := []string{} uniqueresultentryids := map[string]bool{}
types := make(map[string]bool)
for _, content := range contents { for _, content := range contents {
resultids = append(resultids, content.Id) resultids = append(resultids, content.Id)
resultentryids = append(resultentryids, content.Entry()) uniqueresultentryids[content.Entry()] = true
for _, typ := range content.MusenalmType() {
types[typ] = true
}
}
resultentryids := []any{}
for entryid, _ := range uniqueresultentryids {
resultentryids = append(resultentryids, entryid)
} }
entries, err := dbmodels.Entries_IDs(app, datatypes.ToAny(resultentryids)) entries, err := dbmodels.Entries_IDs(app, datatypes.ToAny(resultentryids))
@@ -96,9 +176,14 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei
return nil, err return nil, err
} }
aids := []any{} uniqueaids := map[string]bool{}
for _, a := range arels { for _, a := range arels {
aids = append(aids, a.Agent()) uniqueaids[a.Agent()] = true
}
aids := []any{}
for aid, _ := range uniqueaids {
aids = append(aids, aid)
} }
agents, err := dbmodels.Agents_IDs(app, aids) agents, err := dbmodels.Agents_IDs(app, aids)
@@ -111,6 +196,10 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei
contentsmap[c.Entry()] = append(contentsmap[c.Entry()], c) contentsmap[c.Entry()] = append(contentsmap[c.Entry()], c)
} }
for _, c := range contentsmap {
dbmodels.Sort_Contents_Numbering(c)
}
contentsagents := make(map[string][]*dbmodels.RContentsAgents) contentsagents := make(map[string][]*dbmodels.RContentsAgents)
for _, a := range arels { for _, a := range arels {
contentsagents[a.Content()] = append(contentsagents[a.Content()], a) contentsagents[a.Content()] = append(contentsagents[a.Content()], a)
@@ -122,8 +211,10 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei
} }
entriesmap := make(map[string]*dbmodels.Entry) entriesmap := make(map[string]*dbmodels.Entry)
years := make(map[int]bool)
for _, e := range entries { for _, e := range entries {
entriesmap[e.Id] = e entriesmap[e.Id] = e
years[e.Year()] = true
} }
hits := []string{} hits := []string{}
@@ -142,6 +233,14 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei
hits = hits[pages[params.Page-1]:pages[params.Page]] hits = hits[pages[params.Page-1]:pages[params.Page]]
} }
tL := slices.Collect(maps.Keys(types))
sort.Strings(tL)
yL := slices.Collect(maps.Keys(years))
sort.Ints(yL)
dbmodels.Sort_Agents_Name(agents)
return &SearchResultBeitraege{ return &SearchResultBeitraege{
Queries: queries, Queries: queries,
Hits: hits, Hits: hits,
@@ -150,6 +249,9 @@ func NewSearchBeitraege(app core.App, params SearchParameters) (*SearchResultBei
Contents: contentsmap, Contents: contentsmap,
ContentsAgents: contentsagents, ContentsAgents: contentsagents,
Pages: pages, Pages: pages,
AgentsList: agents,
TypesList: tL,
YearList: yL,
}, nil }, nil
} }

View File

@@ -22,6 +22,7 @@ const (
BAENDE_PARAM_REFS = "references" BAENDE_PARAM_REFS = "references"
BEITRAEGE_PARAM_ENTRY = "entry" BEITRAEGE_PARAM_ENTRY = "entry"
BEITRAEGE_PARAM_INCIPT = "incipit" BEITRAEGE_PARAM_INCIPT = "incipit"
BEITRAEGE_PARAM_TYPE = "type"
) )
type SearchParameters struct { type SearchParameters struct {
@@ -48,6 +49,7 @@ type SearchParameters struct {
YearString string YearString string
EntryString string EntryString string
IncipitString string IncipitString string
TypeString string
Page int Page int
} }
@@ -61,7 +63,9 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters,
annotations := e.Request.URL.Query().Get(SEARCH_PARAM_ANNOTATIONS) == "on" annotations := e.Request.URL.Query().Get(SEARCH_PARAM_ANNOTATIONS) == "on"
annotationsstring := e.Request.URL.Query().Get(SEARCH_PARAM_ANNOTATIONS + "string") annotationsstring := e.Request.URL.Query().Get(SEARCH_PARAM_ANNOTATIONS + "string")
year := e.Request.URL.Query().Get(SEARCH_PARAM_YEAR) == "on" year := e.Request.URL.Query().Get(SEARCH_PARAM_YEAR) == "on"
yearstring := e.Request.URL.Query().Get(SEARCH_PARAM_YEAR + "string") yearstring := e.Request.URL.Query().Get(SEARCH_PARAM_YEAR + "string")
typestring := e.Request.URL.Query().Get(BEITRAEGE_PARAM_TYPE)
series := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES) == "on" series := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES) == "on"
seriesstring := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES + "string") seriesstring := e.Request.URL.Query().Get(BAENDE_PARAM_SERIES + "string")
@@ -125,6 +129,7 @@ func NewSearchParameters(e *core.RequestEvent, p Parameters) (*SearchParameters,
YearString: yearstring, YearString: yearstring,
EntryString: entrystring, EntryString: entrystring,
IncipitString: incipitstring, IncipitString: incipitstring,
TypeString: typestring,
}, nil }, nil
} }
@@ -151,6 +156,7 @@ func (p SearchParameters) AllSearchTermsBeitraege() string {
res = append(res, p.includedParams(p.YearString)...) res = append(res, p.includedParams(p.YearString)...)
res = append(res, p.includedParams(p.EntryString)...) res = append(res, p.includedParams(p.EntryString)...)
res = append(res, p.includedParams(p.IncipitString)...) res = append(res, p.includedParams(p.IncipitString)...)
res = append(res, p.includedParams(p.TypeString)...)
return strings.Join(res, " ") return strings.Join(res, " ")
} }
@@ -199,6 +205,7 @@ func (p SearchParameters) ToQueryParamsBeitraege() string {
if p.Incipit { if p.Incipit {
q += "&incipit=on" q += "&incipit=on"
} }
} }
if p.YearString != "" { if p.YearString != "" {
@@ -225,6 +232,10 @@ func (p SearchParameters) ToQueryParamsBeitraege() string {
q += fmt.Sprintf("&incipitstring=%s", p.IncipitString) q += fmt.Sprintf("&incipitstring=%s", p.IncipitString)
} }
if p.TypeString != "" {
q += fmt.Sprintf("&typestring=%s", p.TypeString)
}
return q return q
} }
@@ -359,6 +370,12 @@ func (p SearchParameters) FieldSetBeitraege() []dbmodels.FTS5QueryRequest {
ret = append(ret, req...) ret = append(ret, req...)
} }
if p.TypeString != "" {
que := dbmodels.NormalizeQuery(p.TypeString)
req := dbmodels.IntoQueryRequests([]string{dbmodels.MUSENALM_INHALTE_TYPE_FIELD}, que)
ret = append(ret, req...)
}
return ret return ret
} }

View File

@@ -61,11 +61,8 @@ TODO danach:
- Cache? - Cache?
- HTMX + Smooth scroll
- Personen: related - Personen: related
- Inhaltsliste: Personen sehen komisch aus - Inhaltsliste: Personen sehen komisch aus
- V\Boosted links gen by webcomponents
- Sammlungen u. Querverweise müssen die URL als Parameter bekommen
- Sammlungen neuer versuch - Sammlungen neuer versuch
- Inhaltsliste Personen - Inhaltsliste Personen
- Sortierung nach Band A-Z? - Sortierung nach Band A-Z?

View File

@@ -37,6 +37,14 @@
EntryString string EntryString string
IncipitString string IncipitString string
} }
.filters
type BeitraegeFilterParameters struct {
Agent string
Type string
Year string
OnlyScans bool
}
*/}} */}}
{{ $isAlm := false }} {{ $isAlm := false }}
@@ -76,33 +84,51 @@
{{ template "_searchboxsimple" Arr $model.parameters true $q }} {{ template "_searchboxsimple" Arr $model.parameters true $q }}
<fieldset class="selectgroup"> <fieldset class="selectgroup">
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="title" id="title" <input
{{ if or $isBase $isTitle -}}checked{{- end -}} /> type="checkbox"
name="title"
id="title"
{{ if or $isBase $isTitle -}}checked{{- end -}} />
<label for="title">Titel</label> <label for="title">Titel</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="incipit" id="incipit" <input
{{ if or $isBase $isIncipit -}}checked{{- end -}} /> type="checkbox"
name="incipit"
id="incipit"
{{ if or $isBase $isIncipit -}}checked{{- end -}} />
<label for="incipit">Incipit</label> <label for="incipit">Incipit</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="persons" id="persons" <input
{{ if or $isBase $isPerson -}}checked{{- end -}} /> type="checkbox"
name="persons"
id="persons"
{{ if or $isBase $isPerson -}}checked{{- end -}} />
<label for="person">Personen &amp; Pseudonyme</label> <label for="person">Personen &amp; Pseudonyme</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="entry" id="entry" <input
{{ if or $isBase $isEntry -}}checked{{- end -}} /> type="checkbox"
name="entry"
id="entry"
{{ if or $isBase $isEntry -}}checked{{- end -}} />
<label for="entry">Bandtitel</label> <label for="entry">Bandtitel</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="year" id="year" <input
{{ if or $isBase $isYear -}}checked{{- end -}} /> type="checkbox"
name="year"
id="year"
{{ if or $isBase $isYear -}}checked{{- end -}} />
<label for="year">Jahr</label> <label for="year">Jahr</label>
</div> </div>
<div class="selectgroup-option"> <div class="selectgroup-option">
<input type="checkbox" name="annotations" id="annotations" <input
{{ if or $isBase $isAnnotation -}}checked{{- end -}} /> type="checkbox"
name="annotations"
id="annotations"
{{ if or $isBase $isAnnotation -}}checked{{- end -}} />
<label for="annotations">Anmerkungen</label> <label for="annotations">Anmerkungen</label>
</div> </div>
</fieldset> </fieldset>
@@ -116,114 +142,37 @@
{{- if $model.parameters.IsBeitraegeSearch -}} {{- if $model.parameters.IsBeitraegeSearch -}}
<div class="container-normal" id="searchresults"> <div class="container-normal" id="searchresults">
<div class="border-b border-zinc-300 flex flex-row justify-between"> {{ template "_filterlist" $model }}
<div> {{ template "tablehead" $model }}
{{ if $model.parameters.Query -}} {{- if $model.result.Hits -}}
Suche nach <b>»{{ $model.parameters.Query }}«</b> &middot; <div class="mt-8" id="almanachcontents">
{{- end -}} {{- range $_, $hit := $model.result.Hits -}}
{{- if $isAlm -}} {{- $e := index $model.result.Entries $hit -}}
Inhaltsnummer <b>»{{ $model.parameters.AlmString }}«</b> &middot; {{- $contents := index $model.result.Contents $e.Id -}}
{{- end -}} <div
<i class="ri-book-line"></i> class="font-serif flex flex-row justify-between text-stone-800
{{ if eq $model.result.Count 1 -}} font-bold border-b pb-0.5 mb-2 tab-list-head items-end">
Ein Beitrag <div>
{{ else -}} {{ $e.PreferredTitle }}
{{ $model.result.Count }} Beiträge </div>
{{- end }} <div
in class="inline-block font-sans bg-slate-800 text-white h-max text-sm px-1.5 rounded">
{{ if eq ($model.result.Entries | len) 1 -}} {{- len $contents -}}
einem Band </div>
{{ else -}} </div>
{{ $model.result.Entries | len }} Bänden <div class="mb-7 tab-list-panel">
{{- end -}} {{- range $i, $c := $contents -}}
</div> {{- $rels := index $model.result.ContentsAgents $c.Id -}}
{{- if gt (len $model.result.Pages) 1 }} {{- template "_content" Arr $c $e $rels $model.result.Agents false true
<div> $model.parameters
{{ if gt $model.parameters.Page 1 -}} -}}
<a {{- end -}}
href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Prev </div>
}}" class="mr-1.5">
<i class="ri-arrow-left-long-line"></i>
</a>
{{- end -}} {{- end -}}
S. {{ $model.parameters.Page }} /
{{ $model.result.PagesCount }}
{{ if lt $model.parameters.Page ($model.result.PagesCount) -}}
<a
href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Next
}}" class="ml-1.5">
<i class="ri-arrow-right-long-line"></i>
</a>
{{- end -}}
</div>
{{- end -}}
{{- if not $isAlm -}}
<div>
<label
for="sort"
class="align-baseline h-min self-end pb-1 mr-2 text-sm font-sans
text-stone-700"
>Sortierung</label
>
{{/* INFO: We always redrect to letter = A bc some letters dont exist for other professions */}}
<select
class="h-min pb-1 border-b-4 border-zinc-300 px-1.5"
name="sort"
id="sort"
autocomplete="off"
hx-get="{{- $model.parameters.ToQueryParamsBeitraege -}}"
trigger="change"
hx-push-url="true"
hx-select="main"
hx-target="main">
<option
value="year"
{{ if eq $model.parameters.Sort "year" -}}
selected
{{- end -}}>
Jahr
</option>
<option value="entry" {{ if eq $model.parameters.Sort "entry" -}}selected{{- end -}}>
Bände A-Z
</option>
</select>
</div> </div>
{{- end -}} {{- end -}}
</div> </div>
{{- if $model.result.Hits -}}
<div class="mt-8" id="almanachcontents">
{{- range $_, $hit := $model.result.Hits -}}
{{- $e := index $model.result.Entries $hit -}}
{{- $contents := index $model.result.Contents $e.Id -}}
<div
class="font-serif flex flex-row justify-between text-stone-800
font-bold border-b pb-0.5 mb-2 tab-list-head items-end">
<div>
{{ $e.PreferredTitle }}
</div>
<div
class="inline-block font-sans bg-slate-800 text-white h-max text-sm px-1.5 rounded">
{{- len $contents -}}
</div>
</div>
<div class="mb-7 tab-list-panel">
{{- range $i, $c := $contents -}}
{{- $rels := index $model.result.ContentsAgents $c.Id -}}
{{- template "_content" Arr $c $e $rels $model.result.Agents false true
$model.parameters
-}}
{{- end -}}
</div>
{{- end -}}
</div>
{{- end -}}
</div>
<script type="module"> <script type="module">
let elements = document.querySelectorAll('.search-text'); let elements = document.querySelectorAll('.search-text');
let mark_instance = new Mark(elements); let mark_instance = new Mark(elements);
@@ -235,5 +184,4 @@
}, 200); }, 200);
</script> </script>
</div> </div>
{{- end -}} {{- end -}}

View File

@@ -0,0 +1,119 @@
{{ $model := . }}
{{ $isAlm := false }}
{{ $isTitle := false }}
{{ $isYear := false }}
{{ $isPerson := false }}
{{ $isAnnotation := false }}
{{ $isIncipit := false }}
{{ $isEntry := false }}
{{- $isAlm = $model.parameters.AlmString -}}
{{- $isTitle = or $model.parameters.Title $model.parameters.TitleString -}}
{{- $isYear = or $model.parameters.Year $model.parameters.YearString -}}
{{- $isPerson = or $model.parameters.Persons $model.parameters.PersonsString -}}
{{- $isAnnotation = or $model.parameters.Annotations $model.parameters.AnnotationsString -}}
{{- $isIncipit = or $model.parameters.Incipit $model.parameters.IncipitString -}}
{{- $isEntry = or $model.parameters.Entry $model.parameters.EntryString -}}
{{- $isBase := not (or $isTitle $isYear $isPerson $isAnnotation $isIncipit $isEntry) -}}
<div class="border-b border-zinc-300 flex flex-row justify-between">
<div class="flex flex-row gap-x-2">
{{ if $model.parameters.Query -}}
<div>Suche nach <b>»{{ $model.parameters.Query }}«</b></div>
<div>&middot;</div>
{{- end -}}
{{- if $isAlm -}}
<div>Inhaltsnummer <b>»{{ $model.parameters.AlmString }}«</b></div>
<div>&middot;</div>
{{- end -}}
<div>
<i class="ri-book-line"></i>
{{ if eq $model.result.Count 1 -}}
Ein Beitrag
{{ else -}}
{{ $model.result.Count }} Beiträge
{{- end }}
in
{{ if eq ($model.result.Entries | len) 1 -}}
einem Band
{{ else -}}
{{ $model.result.Entries | len }} Bänden
{{- end -}}
</div>
{{- if gt (len $model.result.Pages) 1 }}
<div>&middot;</div>
<div class="[&>_a]:no-underline">
{{ if gt $model.parameters.Page 1 -}}
<a
href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Prev }}"
class="mr-1.5">
<i class="ri-arrow-left-long-line"></i>
</a>
{{- end -}}
Seite {{ $model.parameters.Page }} /
{{ $model.result.PagesCount }}
{{ if lt $model.parameters.Page ($model.result.PagesCount) -}}
<a
href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Next }}"
class="ml-1.5">
<i class="ri-arrow-right-long-line"></i>
</a>
{{- end -}}
</div>
{{- end -}}
</div>
{{- if not $isAlm -}}
<div>
<label
for="onlyscans"
class="align-baseline h-min self-end pb-1 mr-1 text-sm font-sans text-stone-700">
Nur Digitalisate
</label>
<input
class="mr-4"
type="checkbox"
id="onlyscans"
name="onlyscans"
autocomplete="off"
hx-get="{{- $model.parameters.ToQueryParamsBeitraege -}}"
trigger="change"
hx-push-url="true"
hx-select="main"
hx-target="main"
{{ if $model.filters.OnlyScans -}}checked{{- end -}} />
<label
for="sort"
class="align-baseline h-min self-end pb-1 mr-2 text-sm font-sans
text-stone-700"
>Sortierung</label
>
<select
class="h-min pb-1 border-b-4 border-zinc-300 px-1.5"
name="sort"
id="sort"
autocomplete="off"
hx-get="{{- $model.parameters.ToQueryParamsBeitraege -}}"
trigger="change"
hx-push-url="true"
hx-select="main"
hx-target="main">
<option
value="year"
{{ if eq $model.parameters.Sort "year" -}}
selected
{{- end -}}>
Jahr
</option>
<option value="entry" {{ if eq $model.parameters.Sort "entry" -}}selected{{- end -}}>
Bände A-Z
</option>
</select>
</div>
{{- end -}}
</div>

View File

@@ -0,0 +1,75 @@
{{ $model := . }}
<div class="flex flex-row gap-x-6 mb-4">
<filter-list
id="agent-list"
data-queryparam="personsstring"
data-url="{{ $model.parameters.ToQueryParamsBeitraege }}&agentfilter="
data-placeholder="Personen und Körperschaften filtern..."></filter-list>
<filter-list
id="year-list"
data-queryparam="yearstring"
data-url="{{ $model.parameters.ToQueryParamsBeitraege }}&yearfilter="
data-placeholder="Jahre filtern..."></filter-list>
<filter-list
id="types-list"
data-queryparam="type"
data-url="{{ $model.parameters.ToQueryParamsBeitraege }}&typefilter="
data-placeholder="Betragskategorien filtern..."></filter-list>
</div>
<script type="module">
let agentList = document.getElementById("agent-list");
if (agentList) {
agentList.items = {{ $model.result.AgentsList }};
agentList.setSearchTextFunc((item) => {
return item.name;
});
agentList.setHREFFunc((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>
`;
});
}
let yearList = document.getElementById("year-list");
if (yearList) {
yearList.items = {{ $model.result.YearList }};
yearList.setHREFFunc((item) => {
return String(item);
});
yearList.setLinkTextFunc((item) => {
if (item === 0) return "ohne Jahr";
return String(item);
});
}
let typesList = document.getElementById("types-list");
if (typesList) {
typesList.items = {{ $model.result.TypesList }};
typesList.setHREFFunc((item) => {
return String(item);
});
typesList.setLinkTextFunc((item) => {
return item;
});
}
</script>

View File

@@ -146,6 +146,10 @@ class FilterList extends HTMLElement {
this.addEventListener("keydown", this.onEnter.bind(this)); this.addEventListener("keydown", this.onEnter.bind(this));
this.addEventListener("focusin", this.onGainFocus.bind(this)); this.addEventListener("focusin", this.onGainFocus.bind(this));
this.addEventListener("focusout", this.onLoseFocus.bind(this)); this.addEventListener("focusout", this.onLoseFocus.bind(this));
if (htmx) {
htmx.process(this);
}
} }
attributeChangedCallback(name, oldValue, newValue) { attributeChangedCallback(name, oldValue, newValue) {
@@ -253,6 +257,10 @@ class FilterList extends HTMLElement {
return item.id; return item.id;
} }
getHREFEncoded(item) {
return encodeURIComponent(this.getHREF(item));
}
getSearchText(item) { getSearchText(item) {
if (!item) { if (!item) {
return ""; return "";
@@ -351,7 +359,7 @@ class FilterList extends HTMLElement {
.map( .map(
(item, index) => ` (item, index) => `
<a <a
href="${this._url}${this.getHREF(item)}" href="${this._url}${this.getHREFEncoded(item)}"
class="${FILTER_LIST_ITEM} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${ class="${FILTER_LIST_ITEM} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${
index % 2 === 0 ? "bg-stone-100" : "bg-stone-50" index % 2 === 0 ? "bg-stone-100" : "bg-stone-50"
}" }"