diff --git a/src/app/pages/recipes-page/recipes-page.html b/src/app/pages/recipes-page/recipes-page.html
index cf69ce0..bd99848 100644
--- a/src/app/pages/recipes-page/recipes-page.html
+++ b/src/app/pages/recipes-page/recipes-page.html
@@ -5,4 +5,11 @@
There was an error loading recipes: {{ loadRecipesError() }}
} @else {
+
}
diff --git a/src/app/pages/recipes-page/recipes-page.ts b/src/app/pages/recipes-page/recipes-page.ts
index 0c9f0cf..738acd6 100644
--- a/src/app/pages/recipes-page/recipes-page.ts
+++ b/src/app/pages/recipes-page/recipes-page.ts
@@ -3,10 +3,12 @@ import { RecipeService } from '../../shared/services/RecipeService';
import { RecipeCardGrid } from '../../shared/components/recipe-card-grid/recipe-card-grid';
import { RecipeInfoView } from '../../shared/models/Recipe.model';
import { Spinner } from '../../shared/components/spinner/spinner';
+import { MatPaginator, PageEvent } from '@angular/material/paginator';
+import { combineLatest } from 'rxjs';
@Component({
selector: 'app-recipes-page',
- imports: [RecipeCardGrid, Spinner],
+ imports: [RecipeCardGrid, Spinner, MatPaginator],
templateUrl: './recipes-page.html',
styleUrl: './recipes-page.css',
})
@@ -17,12 +19,27 @@ export class RecipesPage implements OnInit {
protected readonly loadRecipesError = signal(null);
protected readonly recipes = signal([]);
+ protected readonly recipeCount = signal(50);
+ protected readonly currentPage = signal(0);
+ protected readonly pageSize = signal(10);
+
public ngOnInit(): void {
+ this.loadRecipes();
+ }
+
+ private loadRecipes() {
this.loadingRecipes.set(true);
- this.recipeService.getRecipes().subscribe({
- next: (sliceView) => {
+ combineLatest([
+ this.recipeService.getRecipes({
+ page: this.currentPage(),
+ size: this.pageSize(),
+ }),
+ this.recipeService.getRecipeCount(),
+ ]).subscribe({
+ next: ([sliceView, count]) => {
this.loadingRecipes.set(false);
this.recipes.set(sliceView.content);
+ this.recipeCount.set(count);
},
error: (e) => {
this.loadingRecipes.set(false);
@@ -30,4 +47,24 @@ export class RecipesPage implements OnInit {
},
});
}
+
+ protected onPage(pageEvent: PageEvent): void {
+ // chart
+ // | size-change | size-same
+ // page-change | reload | reload
+ // page-same | reload | nothing
+ if (pageEvent.pageSize !== this.pageSize() || pageEvent.pageIndex !== this.currentPage()) {
+ this.pageSize.set(pageEvent.pageSize);
+ this.currentPage.update((old) => {
+ if (pageEvent.pageIndex < old) {
+ return Math.max(old - 1, 0);
+ } else if (pageEvent.pageIndex > old) {
+ return (this.currentPage() + 1) * this.pageSize() <= this.recipeCount() ? old + 1 : old;
+ } else {
+ return old;
+ }
+ });
+ this.loadRecipes();
+ }
+ }
}
diff --git a/src/app/shared/components/recipe-edit-form/recipe-edit-form.ts b/src/app/shared/components/recipe-edit-form/recipe-edit-form.ts
index 4a3ff86..1703e98 100644
--- a/src/app/shared/components/recipe-edit-form/recipe-edit-form.ts
+++ b/src/app/shared/components/recipe-edit-form/recipe-edit-form.ts
@@ -14,7 +14,7 @@ import {
} from '@angular/core';
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
-import { ImageSelect } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select';
+import { ImageSelect } from './image-select/image-select';
import { MatButton } from '@angular/material/button';
import {
MatCell,
@@ -34,11 +34,11 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
import { faBars, faEllipsis } from '@fortawesome/free-solid-svg-icons';
import { IngredientDraftClientModel } from '../../client-models/IngredientDraftClientModel';
import { ImageView } from '../../models/ImageView.model';
-import { IngredientDialog } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/ingredient-dialog/ingredient-dialog';
+import { IngredientDialog } from './ingredient-dialog/ingredient-dialog';
import { RecipeDraftViewModel } from '../../models/RecipeDraftView.model';
import { RecipeEditFormSubmitEvent } from './RecipeEditFormSubmitEvent';
import { MatDialog } from '@angular/material/dialog';
-import { ImageUploadDialog } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/image-upload-dialog/image-upload-dialog';
+import { ImageUploadDialog } from './image-upload-dialog/image-upload-dialog';
import { FullRecipeView } from '../../models/Recipe.model';
@Component({
diff --git a/src/app/shared/services/RecipeService.ts b/src/app/shared/services/RecipeService.ts
index ec0124c..21af289 100644
--- a/src/app/shared/services/RecipeService.ts
+++ b/src/app/shared/services/RecipeService.ts
@@ -16,6 +16,17 @@ import { RecipeUpdateBody } from '../bodies/RecipeUpdateBody';
providedIn: 'root',
})
export class RecipeService {
+ public static readonly RecipeProperties = [
+ 'id',
+ 'created',
+ 'modified',
+ 'title',
+ 'slug',
+ 'preparationTime',
+ 'cookingTime',
+ 'totalTime',
+ ] as const;
+
private readonly http = inject(HttpClient);
private readonly authService = inject(AuthService);
private readonly queryClient = inject(QueryClient);
@@ -46,13 +57,23 @@ export class RecipeService {
};
}
- public getRecipes(): Observable> {
- return this.http.get>>(this.endpointService.getUrl('recipes')).pipe(
- map((sliceView) => ({
- ...sliceView,
- content: sliceView.content.map((withStringDates) => this.hydrateRecipeInfoView(withStringDates)),
- })),
- );
+ public getRecipes(
+ queryParams?: QueryParams,
+ ): Observable> {
+ return this.http
+ .get>>(this.endpointService.getUrl('recipes', [], queryParams))
+ .pipe(
+ map((sliceView) => ({
+ ...sliceView,
+ content: sliceView.content.map((withStringDates) => this.hydrateRecipeInfoView(withStringDates)),
+ })),
+ );
+ }
+
+ public getRecipeCount(): Observable {
+ return this.http
+ .get<{ count: number }>(this.endpointService.getUrl('recipes', ['meta', 'count']))
+ .pipe(map((res) => res.count));
}
public getRecipeView(username: string, slug: string): Promise {