taggart

Simple golang tagging filesystem webapp
Log | Files | Refs

commit 0361107ddaba8f72092f706558f6b94f2ef80f18
parent f32568d1a2384824c64910a7bc3565c9be9a0432
Author: breadcat <breadcat@users.noreply.github.com>
Date:   Tue,  7 Oct 2025 18:13:51 +0100

Pagination improvements

Add pagination to tag filter pages
Add first/last buttons
Add manual navigation jump input

Diffstat:
Mmain.go | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Atemplates/_pagination.html | 37+++++++++++++++++++++++++++++++++++++
Mtemplates/list.html | 20+-------------------
Mtemplates/untagged.html | 20+-------------------
4 files changed, 104 insertions(+), 41 deletions(-)

diff --git a/main.go b/main.go @@ -869,6 +869,23 @@ func tagsHandler(w http.ResponseWriter, r *http.Request) { } func tagFilterHandler(w http.ResponseWriter, r *http.Request) { + // Get page number from query params + pageStr := r.URL.Query().Get("page") + page := 1 + if pageStr != "" { + if p, err := strconv.Atoi(pageStr); err == nil && p > 0 { + page = p + } + } + + // Get per page from config + perPage := 50 + if config.ItemsPerPage != "" { + if pp, err := strconv.Atoi(config.ItemsPerPage); err == nil && pp > 0 { + perPage = pp + } + } + // Split by /and/tag/ to get individual tag pairs fullPath := strings.TrimPrefix(r.URL.Path, "/tag/") tagPairs := strings.Split(fullPath, "/and/tag/") @@ -888,6 +905,42 @@ func tagFilterHandler(w http.ResponseWriter, r *http.Request) { filters = append(filters, filter{parts[0], parts[1]}) } + // Build count query first + countQuery := `SELECT COUNT(DISTINCT f.id) FROM files f WHERE 1=1` + countArgs := []interface{}{} + for _, f := range filters { + if f.Value == "unassigned" { + countQuery += ` + AND NOT EXISTS ( + SELECT 1 + FROM file_tags ft + JOIN tags t ON ft.tag_id = t.id + JOIN categories c ON c.id = t.category_id + WHERE ft.file_id = f.id AND c.name = ? + )` + countArgs = append(countArgs, f.Category) + } else { + countQuery += ` + AND EXISTS ( + SELECT 1 + FROM file_tags ft + JOIN tags t ON ft.tag_id = t.id + JOIN categories c ON c.id = t.category_id + WHERE ft.file_id = f.id AND c.name = ? AND t.value = ? + )` + countArgs = append(countArgs, f.Category, f.Value) + } + } + + // Get total count + var total int + err := db.QueryRow(countQuery, countArgs...).Scan(&total) + if err != nil { + renderError(w, "Failed to count files", http.StatusInternalServerError) + return + } + + // Build main query with pagination query := `SELECT f.id, f.filename, f.path, COALESCE(f.description, '') as description FROM files f WHERE 1=1` args := []interface{}{} for _, f := range filters { @@ -914,7 +967,16 @@ func tagFilterHandler(w http.ResponseWriter, r *http.Request) { } } - files, _ := queryFilesWithTags(query, args...) + // Add pagination + offset := (page - 1) * perPage + query += ` ORDER BY f.id DESC LIMIT ? OFFSET ?` + args = append(args, perPage, offset) + + files, err := queryFilesWithTags(query, args...) + if err != nil { + renderError(w, "Failed to fetch files", http.StatusInternalServerError) + return + } var titleParts []string for _, f := range filters { @@ -922,10 +984,10 @@ func tagFilterHandler(w http.ResponseWriter, r *http.Request) { } title := "Tagged: " + strings.Join(titleParts, ", ") - pageData := buildPageData(title, struct { + pageData := buildPageDataWithPagination(title, struct { Tagged []File Untagged []File - }{files, nil}) + }{files, nil}, page, total, perPage) renderTemplate(w, "list.html", pageData) } diff --git a/templates/_pagination.html b/templates/_pagination.html @@ -0,0 +1,36 @@ +{{define "_pagination"}} + +{{if .Pagination}} +{{if gt .Pagination.TotalPages 1}} +<div class="pagination"> + {{if .Pagination.HasPrev}} + <a href="?page=1">&laquo;&laquo; First</a> + <a href="?page={{.Pagination.PrevPage}}">&laquo; Previous</a> + {{else}} + <span class="disabled">&laquo;&laquo; First</span> + <span class="disabled">&laquo; Previous</span> + {{end}} + + <span class="page-info"> + Page + <input type="number" + id="pageInput" + value="{{.Pagination.CurrentPage}}" + min="1" + max="{{.Pagination.TotalPages}}" + style="width: 60px; text-align: center;" + onkeypress="if(event.key === 'Enter') { var page = parseInt(this.value); if(page >= 1 && page <= {{.Pagination.TotalPages}}) { window.location.href = '?page=' + page; } }"> + of {{.Pagination.TotalPages}} + </span> + + {{if .Pagination.HasNext}} + <a href="?page={{.Pagination.NextPage}}">Next &raquo;</a> + <a href="?page={{.Pagination.TotalPages}}">Last &raquo;&raquo;</a> + {{else}} + <span class="disabled">Next &raquo;</span> + <span class="disabled">Last &raquo;&raquo;</span> + {{end}} +</div> +{{end}} +{{end}} +{{end}} +\ No newline at end of file diff --git a/templates/list.html b/templates/list.html @@ -23,24 +23,6 @@ </div> {{end}} -{{if .Pagination}} -{{if gt .Pagination.TotalPages 1}} -<div class="pagination"> - {{if .Pagination.HasPrev}} - <a href="?page={{.Pagination.PrevPage}}">&laquo; Previous</a> - {{else}} - <span class="disabled">&laquo; Previous</span> - {{end}} - - <span class="page-info">Page {{.Pagination.CurrentPage}} of {{.Pagination.TotalPages}}</span> - - {{if .Pagination.HasNext}} - <a href="?page={{.Pagination.NextPage}}">Next &raquo;</a> - {{else}} - <span class="disabled">Next &raquo;</span> - {{end}} -</div> -{{end}} -{{end}} +{{template "_pagination" .}} {{template "_footer"}} diff --git a/templates/untagged.html b/templates/untagged.html @@ -9,24 +9,6 @@ {{end}} </div> -{{if .Pagination}} -{{if gt .Pagination.TotalPages 1}} -<div class="pagination"> - {{if .Pagination.HasPrev}} - <a href="?page={{.Pagination.PrevPage}}">&laquo; Previous</a> - {{else}} - <span class="disabled">&laquo; Previous</span> - {{end}} - - <span class="page-info">Page {{.Pagination.CurrentPage}} of {{.Pagination.TotalPages}}</span> - - {{if .Pagination.HasNext}} - <a href="?page={{.Pagination.NextPage}}">Next &raquo;</a> - {{else}} - <span class="disabled">Next &raquo;</span> - {{end}} -</div> -{{end}} -{{end}} +{{template "_pagination" .}} {{template "_footer"}}