readysite / website / internal / helpers / pagination.go
2.3 KB
pagination.go
package helpers

import (
	"net/http"
	"strconv"
)

// DefaultPageSize is the default number of items per page.
const DefaultPageSize = 50

// Pagination holds pagination state for list views.
type Pagination struct {
	Page       int  // Current page (1-indexed)
	PageSize   int  // Items per page
	TotalItems int  // Total number of items
	TotalPages int  // Total number of pages
	HasPrev    bool // Has previous page
	HasNext    bool // Has next page
	Offset     int  // SQL offset
}

// NewPagination creates pagination state from a request.
func NewPagination(r *http.Request, totalItems int) *Pagination {
	return NewPaginationWithSize(r, totalItems, DefaultPageSize)
}

// NewPaginationWithSize creates pagination state with custom page size.
func NewPaginationWithSize(r *http.Request, totalItems, pageSize int) *Pagination {
	page := 1
	if p := r.URL.Query().Get("page"); p != "" {
		if parsed, err := strconv.Atoi(p); err == nil && parsed > 0 {
			page = parsed
		}
	}

	totalPages := (totalItems + pageSize - 1) / pageSize
	if totalPages < 1 {
		totalPages = 1
	}

	// Clamp page to valid range
	if page > totalPages {
		page = totalPages
	}

	return &Pagination{
		Page:       page,
		PageSize:   pageSize,
		TotalItems: totalItems,
		TotalPages: totalPages,
		HasPrev:    page > 1,
		HasNext:    page < totalPages,
		Offset:     (page - 1) * pageSize,
	}
}

// PrevPage returns the previous page number.
func (p *Pagination) PrevPage() int {
	if p.Page > 1 {
		return p.Page - 1
	}
	return 1
}

// NextPage returns the next page number.
func (p *Pagination) NextPage() int {
	if p.Page < p.TotalPages {
		return p.Page + 1
	}
	return p.TotalPages
}

// Pages returns a list of page numbers to display in pagination.
// Shows up to 5 pages around the current page.
func (p *Pagination) Pages() []int {
	if p.TotalPages <= 7 {
		// Show all pages if 7 or fewer
		pages := make([]int, p.TotalPages)
		for i := range pages {
			pages[i] = i + 1
		}
		return pages
	}

	// Show: 1 ... (current-1) current (current+1) ... last
	pages := []int{1}
	start := max(2, p.Page-1)
	end := min(p.TotalPages-1, p.Page+1)

	if start > 2 {
		pages = append(pages, -1) // -1 represents "..."
	}
	for i := start; i <= end; i++ {
		pages = append(pages, i)
	}
	if end < p.TotalPages-1 {
		pages = append(pages, -1)
	}
	pages = append(pages, p.TotalPages)

	return pages
}
← Back