/** * GoingsOn - Pagination Manager * Generic pagination handling for lists. * Replaces duplicate pagination code in tasks.js and emails.js. */ class PaginationManager { /** * Create a pagination manager. * @param {string} type - Item type identifier (used for DOM element IDs) * @param {number} itemsPerPage - Number of items per page */ constructor(type, itemsPerPage = 25) { this.type = type; this.itemsPerPage = itemsPerPage; this.currentPage = 1; this.totalItems = 0; } /** * Navigate to a page. * @param {'prev'|'next'|number} direction - Direction or page number * @returns {number} The new current page */ goToPage(direction) { const maxPage = this.getMaxPage(); if (direction === 'prev') { this.currentPage = Math.max(1, this.currentPage - 1); } else if (direction === 'next') { this.currentPage = Math.min(maxPage, this.currentPage + 1); } else if (typeof direction === 'number') { this.currentPage = Math.max(1, Math.min(maxPage, direction)); } return this.currentPage; } /** * Get the current page number. * @returns {number} */ getCurrentPage() { return this.currentPage; } /** * Get the maximum page number. * @returns {number} */ getMaxPage() { return Math.max(1, Math.ceil(this.totalItems / this.itemsPerPage)); } /** * Set the total number of items and validate current page. * @param {number} total - Total number of items */ setTotalItems(total) { this.totalItems = total; // Validate current page const maxPage = this.getMaxPage(); if (this.currentPage > maxPage) { this.currentPage = maxPage; } if (this.currentPage < 1) { this.currentPage = 1; } } /** * Reset pagination to first page. */ reset() { this.currentPage = 1; } /** * Get the offset for the current page (for backend pagination). * @returns {number} The number of items to skip */ getOffset() { return (this.currentPage - 1) * this.itemsPerPage; } /** * Get a paginated slice of items (for client-side pagination). * @param {Array} items - All items * @returns {Array} Paginated items */ paginate(items) { const start = this.getOffset(); return items.slice(start, start + this.itemsPerPage); } /** * Update the pagination UI elements. */ updateUI() { const container = document.getElementById(`${this.type}-pagination`); const prevBtn = document.getElementById(`${this.type}-prev-page`); const nextBtn = document.getElementById(`${this.type}-next-page`); const pageInfo = document.getElementById(`${this.type}-page-info`); if (!container) return; // Show/hide pagination if (this.totalItems <= this.itemsPerPage) { container.style.display = 'none'; return; } container.style.display = 'flex'; const maxPage = this.getMaxPage(); // Update buttons if (prevBtn) prevBtn.disabled = this.currentPage <= 1; if (nextBtn) nextBtn.disabled = this.currentPage >= maxPage; // Update page info if (pageInfo) { const start = (this.currentPage - 1) * this.itemsPerPage + 1; const end = Math.min(this.currentPage * this.itemsPerPage, this.totalItems); pageInfo.textContent = `${start}-${end} of ${this.totalItems} (Page ${this.currentPage}/${maxPage})`; } } /** * Get pagination info for display. * @returns {Object} Pagination info */ getInfo() { const maxPage = this.getMaxPage(); const start = (this.currentPage - 1) * this.itemsPerPage + 1; const end = Math.min(this.currentPage * this.itemsPerPage, this.totalItems); return { currentPage: this.currentPage, maxPage, start, end, total: this.totalItems, hasPrev: this.currentPage > 1, hasNext: this.currentPage < maxPage }; } } // ============ Populate GoingsOn Namespace ============ if (window.GoingsOn) { GoingsOn.PaginationManager = PaginationManager; }