generated from nhcarrigan/template
feat: pagination
This commit is contained in:
@@ -4,17 +4,18 @@
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { Component, OnInit, inject, signal } from '@angular/core';
|
||||
import { Component, OnInit, inject, signal, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SuggestionService } from '../../services/suggestion.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { PaginationComponent } from '../shared/pagination.component';
|
||||
import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-suggestions',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -65,8 +66,16 @@ import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-
|
||||
<p>No suggestions found.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredSuggestions()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="suggestions-list">
|
||||
@for (suggestion of filteredSuggestions(); track suggestion.id) {
|
||||
@for (suggestion of paginatedSuggestions(); track suggestion.id) {
|
||||
<div class="suggestion-card" [class]="'status-' + suggestion.status.toLowerCase()">
|
||||
<div class="suggestion-header">
|
||||
<div class="badges">
|
||||
@@ -185,6 +194,14 @@ import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredSuggestions()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,19 +549,32 @@ export class AdminSuggestionsComponent implements OnInit {
|
||||
decliningsuggestion = signal<Suggestion | null>(null);
|
||||
declineReason = '';
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
SuggestionStatus = SuggestionStatus;
|
||||
|
||||
unreviewedCount = () => this.suggestions().filter(s => s.status === SuggestionStatus.UNREVIEWED).length;
|
||||
acceptedCount = () => this.suggestions().filter(s => s.status === SuggestionStatus.ACCEPTED).length;
|
||||
declinedCount = () => this.suggestions().filter(s => s.status === SuggestionStatus.DECLINED).length;
|
||||
|
||||
filteredSuggestions = () => {
|
||||
filteredSuggestions = computed(() => {
|
||||
const filter = this.statusFilter();
|
||||
if (filter === 'all') {
|
||||
return this.suggestions();
|
||||
}
|
||||
return this.suggestions().filter(s => s.status === filter);
|
||||
};
|
||||
});
|
||||
|
||||
paginatedSuggestions = computed(() => {
|
||||
const suggestions = this.filteredSuggestions();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return suggestions.slice(start, end);
|
||||
});
|
||||
|
||||
totalFilteredSuggestions = computed(() => this.filteredSuggestions().length);
|
||||
|
||||
ngOnInit() {
|
||||
if (this.authService.isAdmin()) {
|
||||
@@ -568,6 +598,19 @@ export class AdminSuggestionsComponent implements OnInit {
|
||||
|
||||
setFilter(filter: 'all' | SuggestionStatus) {
|
||||
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: SuggestionStatus): string {
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { Component, OnInit, inject, signal } from '@angular/core';
|
||||
import { Component, OnInit, inject, signal, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Router } from '@angular/router';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { PaginationComponent } from '../shared/pagination.component';
|
||||
import { User } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-users',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="admin-container">
|
||||
<h2>User Management</h2>
|
||||
@@ -24,8 +25,16 @@ import { User } from '@library/shared-types';
|
||||
} @else if (error()) {
|
||||
<p class="error">{{ error() }}</p>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalUsers()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="users-list">
|
||||
@for (user of users(); track user.id) {
|
||||
@for (user of paginatedUsers(); track user.id) {
|
||||
<div class="user-card" [class.banned]="user.isBanned">
|
||||
<div class="user-info">
|
||||
@if (user.avatar) {
|
||||
@@ -70,6 +79,14 @@ import { User } from '@library/shared-types';
|
||||
<p class="no-users">No users found.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalUsers()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -270,6 +287,18 @@ export class AdminUsersComponent implements OnInit {
|
||||
loading = signal(true);
|
||||
error = signal<string | null>(null);
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
paginatedUsers = computed(() => {
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return this.users().slice(start, end);
|
||||
});
|
||||
|
||||
totalUsers = computed(() => this.users().length);
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.authService.isAdmin()) {
|
||||
this.router.navigate(['/']);
|
||||
@@ -320,4 +349,16 @@ export class AdminUsersComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPageChange(page: number): void {
|
||||
this.currentPage.set(page);
|
||||
}
|
||||
|
||||
onPageSizeChange(pageSize: number): void {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { Component, OnInit, inject, signal } from '@angular/core';
|
||||
import { Component, OnInit, inject, signal, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ArtService } from '../../services/art.service';
|
||||
@@ -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 { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-art-gallery',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -337,8 +338,16 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
||||
<p>No artwork in the gallery yet.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalArtPieces()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="gallery-grid">
|
||||
@for (art of artPieces(); track art.id) {
|
||||
@for (art of paginatedArtPieces(); track art.id) {
|
||||
<div class="art-card">
|
||||
<div class="art-image-container">
|
||||
<img
|
||||
@@ -465,6 +474,14 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalArtPieces()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
|
||||
@if (lightboxArt()) {
|
||||
@@ -1047,6 +1064,10 @@ export class ArtGalleryComponent implements OnInit {
|
||||
editingArt = signal<Art | null>(null);
|
||||
lightboxArt = signal<Art | null>(null);
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
// Comments state
|
||||
comments = signal<Record<string, Comment[]>>({});
|
||||
commentsLoading = signal<Record<string, boolean>>({});
|
||||
@@ -1083,6 +1104,16 @@ export class ArtGalleryComponent implements OnInit {
|
||||
editLinkTitle = '';
|
||||
editLinkUrl = '';
|
||||
|
||||
// Computed properties for pagination
|
||||
paginatedArtPieces = computed(() => {
|
||||
const artPieces = this.artPieces();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return artPieces.slice(start, end);
|
||||
});
|
||||
|
||||
totalArtPieces = computed(() => this.artPieces().length);
|
||||
|
||||
ngOnInit() {
|
||||
this.loadArt();
|
||||
}
|
||||
@@ -1388,4 +1419,16 @@ export class ArtGalleryComponent implements OnInit {
|
||||
alert('Failed to submit suggestion. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-games-list',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -398,8 +399,16 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
||||
<p>No games found in this category.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredGames()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="games-grid">
|
||||
@for (game of filteredGames(); track game.id) {
|
||||
@for (game of paginatedGames(); track game.id) {
|
||||
<div class="game-card" [class.completed]="game.status === GameStatus.completed">
|
||||
@if (game.coverImage) {
|
||||
<img [src]="game.coverImage" [alt]="game.title" class="game-cover">
|
||||
@@ -537,6 +546,14 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredGames()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -1029,6 +1046,10 @@ export class GamesListComponent implements OnInit {
|
||||
editingGame = signal<Game | null>(null);
|
||||
statusFilter = signal<'all' | GameStatus>('all');
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
// Comments state
|
||||
comments = signal<Record<string, Comment[]>>({});
|
||||
commentsLoading = signal<Record<string, boolean>>({});
|
||||
@@ -1069,6 +1090,15 @@ export class GamesListComponent implements OnInit {
|
||||
return this.games().filter(game => game.status === filter);
|
||||
});
|
||||
|
||||
paginatedGames = computed(() => {
|
||||
const games = this.filteredGames();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return games.slice(start, end);
|
||||
});
|
||||
|
||||
totalFilteredGames = computed(() => this.filteredGames().length);
|
||||
|
||||
newGame: Partial<CreateGameDto> = {
|
||||
title: '',
|
||||
platform: '',
|
||||
@@ -1108,6 +1138,19 @@ export class GamesListComponent implements OnInit {
|
||||
|
||||
setFilter(filter: 'all' | GameStatus) {
|
||||
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: GameStatus): string {
|
||||
|
||||
@@ -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 { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-manga-list',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -401,8 +402,16 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
||||
<p>No manga found in this category.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredManga()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="manga-grid">
|
||||
@for (manga of filteredManga(); track manga.id) {
|
||||
@for (manga of paginatedManga(); track manga.id) {
|
||||
<div class="manga-card" [class.completed]="manga.status === MangaStatus.completed">
|
||||
@if (manga.coverImage) {
|
||||
<img [src]="manga.coverImage" [alt]="manga.title" class="manga-cover">
|
||||
@@ -538,6 +547,14 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredManga()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -1036,6 +1053,10 @@ export class MangaListComponent implements OnInit {
|
||||
editingManga = signal<Manga | null>(null);
|
||||
statusFilter = signal<'all' | MangaStatus>('all');
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
comments = signal<Record<string, Comment[]>>({});
|
||||
commentsLoading = signal<Record<string, boolean>>({});
|
||||
expandedComments = signal<Record<string, boolean>>({});
|
||||
@@ -1072,6 +1093,15 @@ export class MangaListComponent implements OnInit {
|
||||
return this.mangaList().filter(m => m.status === filter);
|
||||
});
|
||||
|
||||
paginatedManga = computed(() => {
|
||||
const manga = this.filteredManga();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return manga.slice(start, end);
|
||||
});
|
||||
|
||||
totalFilteredManga = computed(() => this.filteredManga().length);
|
||||
|
||||
newManga: Partial<CreateMangaDto> = {
|
||||
title: '',
|
||||
author: '',
|
||||
@@ -1111,6 +1141,19 @@ export class MangaListComponent implements OnInit {
|
||||
|
||||
setFilter(filter: 'all' | MangaStatus) {
|
||||
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: MangaStatus): string {
|
||||
|
||||
@@ -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 { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-music-list',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -463,8 +464,16 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
||||
<p>No music found with these filters.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredMusic()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="music-grid">
|
||||
@for (music of filteredMusic(); track music.id) {
|
||||
@for (music of paginatedMusic(); track music.id) {
|
||||
<div class="music-card" [class.completed]="music.status === MusicStatus.completed">
|
||||
@if (music.coverArt) {
|
||||
<img [src]="music.coverArt" [alt]="music.title" class="music-cover">
|
||||
@@ -620,6 +629,14 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredMusic()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -1244,6 +1261,10 @@ export class MusicListComponent implements OnInit {
|
||||
typeFilter = signal<'all' | MusicType>('all');
|
||||
statusFilter = signal<'all' | MusicStatus>('all');
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
// Comments state
|
||||
comments = signal<Record<string, Comment[]>>({});
|
||||
commentsLoading = signal<Record<string, boolean>>({});
|
||||
@@ -1299,6 +1320,15 @@ export class MusicListComponent implements OnInit {
|
||||
return filtered;
|
||||
});
|
||||
|
||||
paginatedMusic = computed(() => {
|
||||
const music = this.filteredMusic();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return music.slice(start, end);
|
||||
});
|
||||
|
||||
totalFilteredMusic = computed(() => this.filteredMusic().length);
|
||||
|
||||
newMusic: Partial<CreateMusicDto> = {
|
||||
title: '',
|
||||
artist: '',
|
||||
@@ -1339,10 +1369,24 @@ export class MusicListComponent implements OnInit {
|
||||
|
||||
setTypeFilter(filter: 'all' | MusicType) {
|
||||
this.typeFilter.set(filter);
|
||||
this.currentPage.set(1); // Reset to first page when filter changes
|
||||
}
|
||||
|
||||
setStatusFilter(filter: 'all' | MusicStatus) {
|
||||
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);
|
||||
}
|
||||
|
||||
getTypeLabel(type: MusicType): string {
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { Component, OnInit, inject, signal } from '@angular/core';
|
||||
import { Component, OnInit, inject, signal, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SuggestionService } from '../../services/suggestion.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { PaginationComponent } from '../shared/pagination.component';
|
||||
import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-my-suggestions',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -64,8 +65,16 @@ import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredSuggestions()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="suggestions-list">
|
||||
@for (suggestion of filteredSuggestions(); track suggestion.id) {
|
||||
@for (suggestion of paginatedSuggestions(); track suggestion.id) {
|
||||
<div class="suggestion-card" [class]="'status-' + suggestion.status.toLowerCase()">
|
||||
<div class="suggestion-header">
|
||||
<span class="entity-badge" [class]="'entity-' + suggestion.entityType.toLowerCase()">
|
||||
@@ -123,6 +132,14 @@ import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredSuggestions()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -314,19 +331,32 @@ export class MySuggestionsComponent implements OnInit {
|
||||
loading = signal(true);
|
||||
statusFilter = signal<'all' | SuggestionStatus>('all');
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
SuggestionStatus = SuggestionStatus;
|
||||
|
||||
unreviewedCount = () => this.suggestions().filter(s => s.status === SuggestionStatus.UNREVIEWED).length;
|
||||
acceptedCount = () => this.suggestions().filter(s => s.status === SuggestionStatus.ACCEPTED).length;
|
||||
declinedCount = () => this.suggestions().filter(s => s.status === SuggestionStatus.DECLINED).length;
|
||||
|
||||
filteredSuggestions = () => {
|
||||
filteredSuggestions = computed(() => {
|
||||
const filter = this.statusFilter();
|
||||
if (filter === 'all') {
|
||||
return this.suggestions();
|
||||
}
|
||||
return this.suggestions().filter(s => s.status === filter);
|
||||
};
|
||||
});
|
||||
|
||||
paginatedSuggestions = computed(() => {
|
||||
const suggestions = this.filteredSuggestions();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return suggestions.slice(start, end);
|
||||
});
|
||||
|
||||
totalFilteredSuggestions = computed(() => this.filteredSuggestions().length);
|
||||
|
||||
ngOnInit() {
|
||||
if (this.authService.isAuthenticated()) {
|
||||
@@ -350,6 +380,19 @@ export class MySuggestionsComponent implements OnInit {
|
||||
|
||||
setFilter(filter: 'all' | SuggestionStatus) {
|
||||
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: SuggestionStatus): string {
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* @copyright 2026 NHCarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { Component, Input, Output, EventEmitter, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pagination',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
template: `
|
||||
<div class="pagination-container">
|
||||
<div class="pagination-info">
|
||||
Showing {{ startItem() }} - {{ endItem() }} of {{ totalItems }} items
|
||||
</div>
|
||||
|
||||
<div class="pagination-controls">
|
||||
<button
|
||||
(click)="onPageChange(1)"
|
||||
[disabled]="currentPage === 1"
|
||||
class="btn btn-sm pagination-btn"
|
||||
title="First page"
|
||||
>
|
||||
⟪
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="onPageChange(currentPage - 1)"
|
||||
[disabled]="currentPage === 1"
|
||||
class="btn btn-sm pagination-btn"
|
||||
title="Previous page"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
|
||||
<div class="page-numbers">
|
||||
@for (page of pageNumbers(); track page) {
|
||||
@if (page === '...') {
|
||||
<span class="page-ellipsis">{{ page }}</span>
|
||||
} @else {
|
||||
<button
|
||||
(click)="onPageChange(+page)"
|
||||
[class.active]="currentPage === +page"
|
||||
class="btn btn-sm pagination-btn page-number"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<button
|
||||
(click)="onPageChange(currentPage + 1)"
|
||||
[disabled]="currentPage === totalPages()"
|
||||
class="btn btn-sm pagination-btn"
|
||||
title="Next page"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="onPageChange(totalPages())"
|
||||
[disabled]="currentPage === totalPages()"
|
||||
class="btn btn-sm pagination-btn"
|
||||
title="Last page"
|
||||
>
|
||||
⟫
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="page-size-selector">
|
||||
<label for="page-size">Items per page:</label>
|
||||
<select
|
||||
id="page-size"
|
||||
[value]="pageSize"
|
||||
(change)="onPageSizeChange($event)"
|
||||
class="page-size-select"
|
||||
>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
color: var(--witch-plum);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.pagination-btn {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border: 1px solid var(--witch-lavender);
|
||||
background: var(--witch-moon);
|
||||
color: var(--witch-purple);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
min-width: 2.5rem;
|
||||
}
|
||||
|
||||
.pagination-btn:hover:not(:disabled) {
|
||||
background: var(--witch-lavender);
|
||||
border-color: var(--witch-mauve);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.pagination-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pagination-btn.active {
|
||||
background: #8b6f47;
|
||||
color: white;
|
||||
border-color: #8b6f47;
|
||||
}
|
||||
|
||||
.page-numbers {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.page-number {
|
||||
min-width: 2.5rem;
|
||||
}
|
||||
|
||||
.page-ellipsis {
|
||||
padding: 0 0.5rem;
|
||||
color: var(--witch-mauve);
|
||||
}
|
||||
|
||||
.page-size-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.page-size-selector label {
|
||||
font-size: 0.9rem;
|
||||
color: var(--witch-plum);
|
||||
}
|
||||
|
||||
.page-size-select {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border: 2px solid var(--witch-lavender);
|
||||
border-radius: 4px;
|
||||
background: var(--witch-moon);
|
||||
color: var(--witch-purple);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.page-size-select:hover {
|
||||
border-color: var(--witch-mauve);
|
||||
}
|
||||
|
||||
.page-size-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--witch-rose);
|
||||
box-shadow: 0 0 0 3px rgba(168, 87, 126, 0.2);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pagination-container {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
order: 2;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
order: 1;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-size-selector {
|
||||
order: 3;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class PaginationComponent {
|
||||
@Input() currentPage = 1;
|
||||
@Input() pageSize = 25;
|
||||
@Input() totalItems = 0;
|
||||
@Output() pageChange = new EventEmitter<number>();
|
||||
@Output() pageSizeChange = new EventEmitter<number>();
|
||||
|
||||
totalPages = computed(() => Math.ceil(this.totalItems / this.pageSize));
|
||||
|
||||
startItem = computed(() => {
|
||||
if (this.totalItems === 0) return 0;
|
||||
return (this.currentPage - 1) * this.pageSize + 1;
|
||||
});
|
||||
|
||||
endItem = computed(() => {
|
||||
return Math.min(this.currentPage * this.pageSize, this.totalItems);
|
||||
});
|
||||
|
||||
pageNumbers = computed(() => {
|
||||
const total = this.totalPages();
|
||||
const current = this.currentPage;
|
||||
const pages: (number | string)[] = [];
|
||||
|
||||
if (total <= 7) {
|
||||
// Show all pages if 7 or fewer
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
// Always show first page
|
||||
pages.push(1);
|
||||
|
||||
if (current <= 3) {
|
||||
// Near the beginning
|
||||
for (let i = 2; i <= 5; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push('...');
|
||||
pages.push(total);
|
||||
} else if (current >= total - 2) {
|
||||
// Near the end
|
||||
pages.push('...');
|
||||
for (let i = total - 4; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
// In the middle
|
||||
pages.push('...');
|
||||
pages.push(current - 1);
|
||||
pages.push(current);
|
||||
pages.push(current + 1);
|
||||
pages.push('...');
|
||||
pages.push(total);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
});
|
||||
|
||||
onPageChange(page: number) {
|
||||
if (page >= 1 && page <= this.totalPages() && page !== this.currentPage) {
|
||||
this.pageChange.emit(page);
|
||||
}
|
||||
}
|
||||
|
||||
onPageSizeChange(event: Event) {
|
||||
const target = event.target as HTMLSelectElement;
|
||||
const newSize = parseInt(target.value, 10);
|
||||
this.pageSizeChange.emit(newSize);
|
||||
}
|
||||
}
|
||||
@@ -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 { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shows-list',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [CommonModule, FormsModule, PaginationComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
@@ -395,8 +396,16 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
||||
<p>No shows found in this category.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredShows()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
|
||||
<div class="shows-grid">
|
||||
@for (show of filteredShows(); track show.id) {
|
||||
@for (show of paginatedShows(); track show.id) {
|
||||
<div class="show-card" [class.completed]="show.status === ShowStatus.completed">
|
||||
@if (show.coverImage) {
|
||||
<img [src]="show.coverImage" [alt]="show.title" class="show-cover">
|
||||
@@ -532,6 +541,14 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<app-pagination
|
||||
[currentPage]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[totalItems]="totalFilteredShows()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
(pageSizeChange)="onPageSizeChange($event)"
|
||||
></app-pagination>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
@@ -1029,6 +1046,10 @@ export class ShowsListComponent implements OnInit {
|
||||
editingShow = signal<Show | null>(null);
|
||||
statusFilter = signal<'all' | ShowStatus>('all');
|
||||
|
||||
// Pagination state
|
||||
currentPage = signal(1);
|
||||
pageSize = signal(25);
|
||||
|
||||
comments = signal<Record<string, Comment[]>>({});
|
||||
commentsLoading = signal<Record<string, boolean>>({});
|
||||
expandedComments = signal<Record<string, boolean>>({});
|
||||
@@ -1066,6 +1087,15 @@ export class ShowsListComponent implements OnInit {
|
||||
return this.shows().filter(show => show.status === filter);
|
||||
});
|
||||
|
||||
paginatedShows = computed(() => {
|
||||
const shows = this.filteredShows();
|
||||
const start = (this.currentPage() - 1) * this.pageSize();
|
||||
const end = start + this.pageSize();
|
||||
return shows.slice(start, end);
|
||||
});
|
||||
|
||||
totalFilteredShows = computed(() => this.filteredShows().length);
|
||||
|
||||
newShow: Partial<CreateShowDto> = {
|
||||
title: '',
|
||||
type: ShowType.tvSeries,
|
||||
@@ -1105,6 +1135,19 @@ export class ShowsListComponent implements OnInit {
|
||||
|
||||
setFilter(filter: 'all' | ShowStatus) {
|
||||
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: ShowStatus): string {
|
||||
|
||||
Reference in New Issue
Block a user