mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 02:25:30 +00:00
+Show contents count in /baende list
This commit is contained in:
15
app/pb.go
15
app/pb.go
@@ -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(),
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -147,11 +157,12 @@ func (p *BaendePage) handleRow(engine *templating.Engine, app core.App) HandleFu
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"entry": entry,
|
||||
"items": items,
|
||||
"editor_user": editorUser,
|
||||
"is_admin": req.IsAdmin(),
|
||||
"csrf_token": req.Session().Token,
|
||||
"entry": entry,
|
||||
"items": items,
|
||||
"editor_user": editorUser,
|
||||
"contents_count": contentsCount,
|
||||
"is_admin": req.IsAdmin(),
|
||||
"csrf_token": req.Session().Token,
|
||||
}
|
||||
|
||||
return engine.Response200(e, "/baende/row/", data, "fragment")
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
{{- 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 }}" 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-eye-line"></i>
|
||||
</a>
|
||||
<div class="flex flex-wrap items-center gap-1.5 pt-1">
|
||||
<tool-tip position="top" class="inline">
|
||||
<a href="/almanach/{{ $entry.MusenalmID }}" 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-eye-line"></i>
|
||||
</a>
|
||||
<div class="data-tip">Ansehen</div>
|
||||
</tool-tip>
|
||||
{{- if (IsAdminOrEditor $model.request.user) -}}
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -21,12 +21,19 @@
|
||||
<div class="data-tip">Ansehen</div>
|
||||
</tool-tip>
|
||||
{{- if .is_admin -}}
|
||||
<tool-tip position="top" class="inline">
|
||||
<a href="/almanach/{{ $entry.MusenalmID }}/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-edit-line"></i>
|
||||
</a>
|
||||
<div class="data-tip">Bearbeiten</div>
|
||||
</tool-tip>
|
||||
<tool-tip position="top" class="inline">
|
||||
<a href="/almanach/{{ $entry.MusenalmID }}/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-edit-line"></i>
|
||||
</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"
|
||||
|
||||
Reference in New Issue
Block a user