feat: pagination

This commit is contained in:
2026-02-04 20:17:04 -08:00
parent b9f33bc055
commit ca288eaac4
10 changed files with 696 additions and 30 deletions
@@ -12,12 +12,13 @@ import { AuthService } from '../../services/auth.service';
import { CommentsService } from '../../services/comments.service';
import { SanitizeService } from '../../services/sanitize.service';
import { SuggestionService } from '../../services/suggestion.service';
import { PaginationComponent } from '../shared/pagination.component';
import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
@Component({
selector: 'app-books-list',
standalone: true,
imports: [CommonModule, FormsModule],
imports: [CommonModule, FormsModule, PaginationComponent],
template: `
<div class="container">
<div class="header-section">
@@ -434,8 +435,16 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
<p>No books found in this category.</p>
</div>
} @else {
<app-pagination
[currentPage]="currentPage()"
[pageSize]="pageSize()"
[totalItems]="totalFilteredBooks()"
(pageChange)="onPageChange($event)"
(pageSizeChange)="onPageSizeChange($event)"
></app-pagination>
<div class="books-grid">
@for (book of filteredBooks(); track book.id) {
@for (book of paginatedBooks(); track book.id) {
<div class="book-card" [class.finished]="book.status === BookStatus.finished">
@if (book.coverImage) {
<img [src]="book.coverImage" [alt]="book.title" class="book-cover">
@@ -584,6 +593,14 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
</div>
}
</div>
<app-pagination
[currentPage]="currentPage()"
[pageSize]="pageSize()"
[totalItems]="totalFilteredBooks()"
(pageChange)="onPageChange($event)"
(pageSizeChange)="onPageSizeChange($event)"
></app-pagination>
}
</div>
`,
@@ -1173,6 +1190,10 @@ export class BooksListComponent implements OnInit {
editingBook = signal<Book | null>(null);
statusFilter = signal<'all' | BookStatus>('all');
// Pagination state
currentPage = signal(1);
pageSize = signal(25);
// Comments state
comments = signal<Record<string, Comment[]>>({});
commentsLoading = signal<Record<string, boolean>>({});
@@ -1208,12 +1229,21 @@ export class BooksListComponent implements OnInit {
filteredBooks = computed(() => {
const filter = this.statusFilter();
if (filter === 'all') {
return this.books();
}
return this.books().filter(book => book.status === filter);
const books = filter === 'all'
? this.books()
: this.books().filter(book => book.status === filter);
return books;
});
paginatedBooks = computed(() => {
const books = this.filteredBooks();
const start = (this.currentPage() - 1) * this.pageSize();
const end = start + this.pageSize();
return books.slice(start, end);
});
totalFilteredBooks = computed(() => this.filteredBooks().length);
newBook: Partial<CreateBookDto> = {
title: '',
author: '',
@@ -1254,6 +1284,19 @@ export class BooksListComponent implements OnInit {
setFilter(filter: 'all' | BookStatus) {
this.statusFilter.set(filter);
this.currentPage.set(1); // Reset to first page when filter changes
}
onPageChange(page: number) {
this.currentPage.set(page);
}
onPageSizeChange(pageSize: number) {
this.pageSize.set(pageSize);
// Calculate new current page to stay on approximately the same content
const firstItemIndex = (this.currentPage() - 1) * this.pageSize();
const newPage = Math.floor(firstItemIndex / pageSize) + 1;
this.currentPage.set(newPage);
}
getStatusLabel(status: BookStatus): string {