+Show contents count in /baende list

This commit is contained in:
Simon Martens
2026-01-30 19:39:02 +01:00
parent e7e279aeeb
commit a70cdd6488
9 changed files with 100 additions and 19 deletions

View File

@@ -57,6 +57,7 @@ type BaendeCache struct {
EntriesAgents map[string][]*dbmodels.REntriesAgents
Items map[string][]*dbmodels.Item
Users map[string]*dbmodels.User
ContentsCount map[string]int
CachedAt time.Time
}
@@ -93,6 +94,9 @@ func (bc *BaendeCache) GetUsers() interface{} {
return bc.Users
}
func (bc *BaendeCache) GetContentsCount() interface{} {
return bc.ContentsCount
}
const (
TEST_SUPERUSER_MAIL = "demo@example.com"
TEST_SUPERUSER_PASS = "password"
@@ -671,6 +675,16 @@ func (app *App) EnsureBaendeCache() (*BaendeCache, error) {
}
}
// Load contents counts
contentsCount := map[string]int{}
if len(entryIDs) > 0 {
counts, err := dbmodels.CountContentsEntries(app.PB.App, entryIDs)
if err != nil {
return nil, err
}
contentsCount = counts
}
// Load users (editors)
usersMap := map[string]*dbmodels.User{}
editorIDs := map[string]struct{}{}
@@ -704,6 +718,7 @@ func (app *App) EnsureBaendeCache() (*BaendeCache, error) {
EntriesAgents: entryAgentsMap,
Items: itemsMap,
Users: usersMap,
ContentsCount: contentsCount,
CachedAt: time.Now(),
}

View File

@@ -27,7 +27,7 @@ const (
URL_BAENDE_DELETE = "/baende/delete-info/{id}"
TEMPLATE_BAENDE = "/baende/"
URL_BAENDE_DETAILS = "/baende/details/{id}"
BAENDE_PAGE_SIZE = 150
BAENDE_PAGE_SIZE = 100
)
func init() {
@@ -55,6 +55,7 @@ type BaendeResult struct {
EntriesAgents map[string][]*dbmodels.REntriesAgents
Items map[string][]*dbmodels.Item
Users map[string]*dbmodels.User
ContentsCount map[string]int
}
type BaendeDetailsResult struct {
@@ -136,6 +137,15 @@ func (p *BaendePage) handleRow(engine *templating.Engine, app core.App) HandleFu
app.Logger().Error("Failed to get items for entry", "error", err)
}
contents, err := dbmodels.Contents_Entry(app, entry.Id)
if err != nil {
app.Logger().Error("Failed to get contents for entry", "error", err)
}
contentsCount := 0
if contents != nil {
contentsCount = len(contents)
}
var editorUser *dbmodels.User
if editorID := entry.Editor(); editorID != "" {
user, err := dbmodels.Users_ID(app, editorID)
@@ -150,6 +160,7 @@ func (p *BaendePage) handleRow(engine *templating.Engine, app core.App) HandleFu
"entry": entry,
"items": items,
"editor_user": editorUser,
"contents_count": contentsCount,
"is_admin": req.IsAdmin(),
"csrf_token": req.Session().Token,
}
@@ -362,6 +373,11 @@ func (p *BaendePage) buildResultData(app core.App, ma pagemodels.IApp, e *core.R
return data, fmt.Errorf("failed to get users from cache")
}
contentsCount, ok := cacheInterface.GetContentsCount().(map[string]int)
if !ok {
return data, fmt.Errorf("failed to get contents count from cache")
}
// Apply search/letter/filters
filteredEntries := allEntries
if search != "" {
@@ -472,6 +488,7 @@ func (p *BaendePage) buildResultData(app core.App, ma pagemodels.IApp, e *core.R
EntriesAgents: entryAgentsMap,
Items: itemsMap,
Users: usersMap,
ContentsCount: contentsCount,
}
data["offset"] = offset
data["total_count"] = totalCount

View File

@@ -294,6 +294,31 @@ func Items_Entries(app core.App, ids []any) ([]*Item, error) {
return ret, err
}
type EntryCount struct {
Count int `db:"count"`
ID string `db:"id"`
}
func CountContentsEntries(app core.App, ids []any) (map[string]int, error) {
if len(ids) == 0 {
return map[string]int{}, nil
}
counts := []EntryCount{}
err := app.RecordQuery(CONTENTS_TABLE).
Select("count(*) as count, " + ENTRIES_TABLE + " as id").
Where(dbx.HashExp{ENTRIES_TABLE: ids}).
GroupBy(ENTRIES_TABLE).
All(&counts)
if err != nil {
return nil, err
}
ret := make(map[string]int, len(counts))
for _, c := range counts {
ret[c.ID] = c.Count
}
return ret, nil
}
func Contents_MusenalmID(app core.App, id string) (*Content, error) {
ret, err := TableByField[Content](app, CONTENTS_TABLE, MUSENALMID_FIELD, id)
return &ret, err

View File

@@ -18,6 +18,7 @@ type BaendeCacheInterface interface {
GetEntriesAgents() interface{} // Returns map[string][]*dbmodels.REntriesAgents
GetItems() interface{} // Returns map[string][]*dbmodels.Item
GetUsers() interface{} // Returns map[string]*dbmodels.User
GetContentsCount() interface{} // Returns map[string]int
}
type IApp interface {

View File

@@ -608,7 +608,7 @@ class="container-normal font-sans mt-10">
"
:disabled="loading">
<i class="ri-arrow-down-line" :class="{ 'spinning': loading }"></i>
<span x-text="loading ? 'Lädt...' : 'Weitere 150 laden'"></span>
<span x-text="loading ? 'Lädt...' : 'Weitere 100 laden'"></span>
</button>
</div>
</div>

View File

@@ -157,6 +157,14 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/contents/edit" onclick="event.stopPropagation();" hx-target="body" 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-file-list-3-line"></i>
{{- $count := index $model.result.ContentsCount $entry.Id -}}
<span>{{ if $count }}{{ $count }}{{ else }}0{{ end }}</span>
</a>
<div class="data-tip">Beiträge bearbeiten</div>
</tool-tip>
<tool-tip position="top" class="inline">
<button
type="button"

View File

@@ -27,6 +27,14 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/contents/edit" onclick="event.stopPropagation();" hx-target="body" 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-file-list-3-line"></i>
{{- $count := index $model.result.ContentsCount $entry.Id -}}
<span>{{ if $count }}{{ $count }}{{ else }}0{{ end }}</span>
</a>
<div class="data-tip">Beiträge bearbeiten</div>
</tool-tip>
<tool-tip position="top" class="inline">
<button
type="button"

View File

@@ -42,7 +42,7 @@
"
:disabled="loading">
<i class="ri-arrow-down-line" :class="{ 'spinning': loading }"></i>
<span x-text="loading ? 'Lädt...' : 'Weitere 150 laden'"></span>
<span x-text="loading ? 'Lädt...' : 'Weitere 100 laden'"></span>
</button>
</div>

View File

@@ -27,6 +27,13 @@
</a>
<div class="data-tip">Bearbeiten</div>
</tool-tip>
<tool-tip position="top" class="inline">
<a href="/almanach/{{ $entry.MusenalmID }}/contents/edit" onclick="event.stopPropagation();" hx-target="body" 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-file-list-3-line"></i>
<span>{{ if .contents_count }}{{ .contents_count }}{{ else }}0{{ end }}</span>
</a>
<div class="data-tip">Beiträge bearbeiten</div>
</tool-tip>
<tool-tip position="top" class="inline">
<button
type="button"