163 lines
3.0 KiB
Go
163 lines
3.0 KiB
Go
package db
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
type PageOpts struct {
|
|
Page int
|
|
PerPage int
|
|
Order bun.Order
|
|
OrderBy string
|
|
}
|
|
|
|
type OrderOpts struct {
|
|
Order bun.Order
|
|
OrderBy string
|
|
Label string
|
|
}
|
|
|
|
func setPageOpts(q *bun.SelectQuery, p, d *PageOpts, totalitems int) (*bun.SelectQuery, *PageOpts) {
|
|
if p == nil {
|
|
p = new(PageOpts)
|
|
}
|
|
if p.Page <= 0 {
|
|
p.Page = d.Page
|
|
}
|
|
if p.PerPage == 0 {
|
|
p.PerPage = d.PerPage
|
|
}
|
|
maxpage := p.TotalPages(totalitems)
|
|
if p.Page > maxpage && maxpage > 0 {
|
|
p.Page = maxpage
|
|
}
|
|
if p.Order == "" {
|
|
p.Order = d.Order
|
|
}
|
|
if p.OrderBy == "" {
|
|
p.OrderBy = d.OrderBy
|
|
}
|
|
p.OrderBy = sanitiseOrderBy(p.OrderBy)
|
|
q = q.OrderBy(p.OrderBy, p.Order).
|
|
Limit(p.PerPage).
|
|
Offset(p.PerPage * (p.Page - 1))
|
|
return q, p
|
|
}
|
|
|
|
func sanitiseOrderBy(orderby string) string {
|
|
result := strings.ToLower(orderby)
|
|
var builder strings.Builder
|
|
for _, r := range result {
|
|
if isValidChar(r) {
|
|
builder.WriteRune(r)
|
|
}
|
|
}
|
|
sanitized := builder.String()
|
|
|
|
if sanitized == "" {
|
|
return "_"
|
|
}
|
|
|
|
if !isValidFirstChar(rune(sanitized[0])) {
|
|
sanitized = "_" + sanitized
|
|
}
|
|
|
|
if len(sanitized) > 63 {
|
|
sanitized = sanitized[:63]
|
|
}
|
|
|
|
return sanitized
|
|
}
|
|
|
|
func isValidChar(r rune) bool {
|
|
return (r >= 'a' && r <= 'z') ||
|
|
(r >= '0' && r <= '9') ||
|
|
r == '_'
|
|
}
|
|
|
|
func isValidFirstChar(r rune) bool {
|
|
return (r >= 'a' && r <= 'z') || r == '_'
|
|
}
|
|
|
|
// TotalPages calculates the total number of pages
|
|
func (p *PageOpts) TotalPages(total int) int {
|
|
if p.PerPage == 0 {
|
|
return 0
|
|
}
|
|
pages := total / p.PerPage
|
|
if total%p.PerPage > 0 {
|
|
pages++
|
|
}
|
|
return pages
|
|
}
|
|
|
|
// HasPrevPage checks if there is a previous page
|
|
func (p *PageOpts) HasPrevPage() bool {
|
|
return p.Page > 1
|
|
}
|
|
|
|
// HasNextPage checks if there is a next page
|
|
func (p *PageOpts) HasNextPage(total int) bool {
|
|
return p.Page < p.TotalPages(total)
|
|
}
|
|
|
|
// GetPageRange returns an array of page numbers to display
|
|
// maxButtons controls how many page buttons to show
|
|
func (p *PageOpts) GetPageRange(total int, maxButtons int) []int {
|
|
totalPages := p.TotalPages(total)
|
|
if totalPages == 0 {
|
|
return []int{}
|
|
}
|
|
|
|
// If total pages is less than max buttons, show all pages
|
|
if totalPages <= maxButtons {
|
|
pages := make([]int, totalPages)
|
|
for i := range totalPages {
|
|
pages[i] = i + 1
|
|
}
|
|
return pages
|
|
}
|
|
|
|
// Calculate range around current page
|
|
halfButtons := maxButtons / 2
|
|
start := p.Page - halfButtons
|
|
end := p.Page + halfButtons
|
|
|
|
// Adjust if at beginning
|
|
if start < 1 {
|
|
start = 1
|
|
end = maxButtons
|
|
}
|
|
|
|
// Adjust if at end
|
|
if end > totalPages {
|
|
end = totalPages
|
|
start = totalPages - maxButtons + 1
|
|
}
|
|
|
|
pages := make([]int, 0, maxButtons)
|
|
for i := start; i <= end; i++ {
|
|
pages = append(pages, i)
|
|
}
|
|
return pages
|
|
}
|
|
|
|
// StartItem returns the number of the first item on the current page
|
|
func (p *PageOpts) StartItem() int {
|
|
if p.Page < 1 {
|
|
return 0
|
|
}
|
|
return (p.Page-1)*p.PerPage + 1
|
|
}
|
|
|
|
// EndItem returns the number of the last item on the current page
|
|
func (p *PageOpts) EndItem(total int) int {
|
|
end := p.Page * p.PerPage
|
|
if end > total {
|
|
return total
|
|
}
|
|
return end
|
|
}
|