Compare commits
No commits in common. "b97803d31d1d0610fe998bb84394de58737fd785" and "fb4e9f7de1ddd698559da78f4e634112d5cd57d6" have entirely different histories.
b97803d31d
...
fb4e9f7de1
@ -3,8 +3,8 @@ import { By } from '@angular/platform-browser';
|
|||||||
import { EditImageDialog } from './edit-image-dialog';
|
import { EditImageDialog } from './edit-image-dialog';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Mocked } from 'vitest';
|
import { Mocked } from 'vitest';
|
||||||
import { ImageService } from '../../../../services/ImageService';
|
import { ImageService } from '../../../../../../shared/services/ImageService';
|
||||||
import { ImageView } from '../../../../models/ImageView.model';
|
import { ImageView } from '../../../../../../shared/models/ImageView.model';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
describe('EditImageDialog', () => {
|
describe('EditImageDialog', () => {
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { Component, inject, OnInit, signal } from '@angular/core';
|
import { Component, inject, OnInit, signal } from '@angular/core';
|
||||||
import { DialogContainer } from '../../../dialog-container/dialog-container';
|
import { DialogContainer } from '../../../../../../shared/components/dialog-container/dialog-container';
|
||||||
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { ImageView } from '../../../../models/ImageView.model';
|
import { ImageView } from '../../../../../../shared/models/ImageView.model';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { ImageService } from '../../../../services/ImageService';
|
import { ImageService } from '../../../../../../shared/services/ImageService';
|
||||||
import { notNullOrUndefined } from '../../../../util';
|
import { notNullOrUndefined } from '../../../../../../shared/util';
|
||||||
import { Spinner } from '../../../spinner/spinner';
|
import { Spinner } from '../../../../../../shared/components/spinner/spinner';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-edit-image-dialog',
|
selector: 'app-edit-image-dialog',
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ImageSelect } from './image-select';
|
import { ImageSelect } from './image-select';
|
||||||
import { Mocked } from 'vitest';
|
import { Mocked } from 'vitest';
|
||||||
import { ImageService } from '../../../services/ImageService';
|
import { ImageService } from '../../../../../shared/services/ImageService';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { SliceView, SliceViewMeta } from '../../../models/SliceView.model';
|
import { SliceView, SliceViewMeta } from '../../../../../shared/models/SliceView.model';
|
||||||
import { ImageViewWithBlobUrl } from '../../../client-models/ImageViewWithBlobUrl';
|
import { ImageViewWithBlobUrl } from '../../../../../shared/client-models/ImageViewWithBlobUrl';
|
||||||
|
|
||||||
describe('ImageSelect', () => {
|
describe('ImageSelect', () => {
|
||||||
let component: ImageSelect;
|
let component: ImageSelect;
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Component, inject, input, OnInit, output, signal } from '@angular/core';
|
import { Component, inject, input, OnInit, output, signal } from '@angular/core';
|
||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
import { ImageService } from '../../../services/ImageService';
|
import { ImageService } from '../../../../../shared/services/ImageService';
|
||||||
import { Spinner } from '../../spinner/spinner';
|
import { Spinner } from '../../../../../shared/components/spinner/spinner';
|
||||||
import { ImageView } from '../../../models/ImageView.model';
|
import { ImageView } from '../../../../../shared/models/ImageView.model';
|
||||||
import { MatCard, MatCardActions, MatCardContent, MatCardImage } from '@angular/material/card';
|
import { MatCard, MatCardActions, MatCardContent, MatCardImage } from '@angular/material/card';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||||
@ -11,8 +11,8 @@ import { faEllipsis } from '@fortawesome/free-solid-svg-icons';
|
|||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { EditImageDialog } from './edit-image-dialog/edit-image-dialog';
|
import { EditImageDialog } from './edit-image-dialog/edit-image-dialog';
|
||||||
import { SliceView } from '../../../models/SliceView.model';
|
import { SliceView } from '../../../../../shared/models/SliceView.model';
|
||||||
import { ImageViewWithBlobUrl } from '../../../client-models/ImageViewWithBlobUrl';
|
import { ImageViewWithBlobUrl } from '../../../../../shared/client-models/ImageViewWithBlobUrl';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-image-select',
|
selector: 'app-image-select',
|
||||||
@ -1,16 +1,16 @@
|
|||||||
import { Component, computed, inject, signal } from '@angular/core';
|
import { Component, computed, inject, signal } from '@angular/core';
|
||||||
import { ImageService } from '../../../services/ImageService';
|
import { ImageService } from '../../../../../shared/services/ImageService';
|
||||||
import { FileUpload } from '../../file-upload/file-upload';
|
import { FileUpload } from '../../../../../shared/components/file-upload/file-upload';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { faFileImage } from '@fortawesome/free-solid-svg-icons';
|
import { faFileImage } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FileUploadEvent } from '../../file-upload/FileUploadEvent';
|
import { FileUploadEvent } from '../../../../../shared/components/file-upload/FileUploadEvent';
|
||||||
import { Spinner } from '../../spinner/spinner';
|
import { Spinner } from '../../../../../shared/components/spinner/spinner';
|
||||||
import { MatError, MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
import { MatError, MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { ImageDoesNotExistValidator } from '../../../validators/image-does-not-exist-validator';
|
import { ImageDoesNotExistValidator } from '../../../../../shared/validators/image-does-not-exist-validator';
|
||||||
import { DialogContainer } from '../../dialog-container/dialog-container';
|
import { DialogContainer } from '../../../../../shared/components/dialog-container/dialog-container';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-image-upload-dialog',
|
selector: 'app-image-upload-dialog',
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import { DialogContainer } from '../../dialog-container/dialog-container';
|
import { DialogContainer } from '../../../../../shared/components/dialog-container/dialog-container';
|
||||||
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { IngredientDraftClientModel } from '../../../client-models/IngredientDraftClientModel';
|
import { IngredientDraftClientModel } from '../../../../../shared/client-models/IngredientDraftClientModel';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-ingredient-dialog',
|
selector: 'app-ingredient-dialog',
|
||||||
@ -5,11 +5,4 @@
|
|||||||
<p>There was an error loading recipes: {{ loadRecipesError() }}</p>
|
<p>There was an error loading recipes: {{ loadRecipesError() }}</p>
|
||||||
} @else {
|
} @else {
|
||||||
<app-recipe-card-grid [recipes]="recipes()"></app-recipe-card-grid>
|
<app-recipe-card-grid [recipes]="recipes()"></app-recipe-card-grid>
|
||||||
<mat-paginator
|
|
||||||
[length]="recipeCount()"
|
|
||||||
[pageIndex]="currentPage()"
|
|
||||||
[pageSize]="pageSize()"
|
|
||||||
[pageSizeOptions]="[5, 10, 25, 50]"
|
|
||||||
(page)="onPage($event)"
|
|
||||||
></mat-paginator>
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,12 +3,10 @@ import { RecipeService } from '../../shared/services/RecipeService';
|
|||||||
import { RecipeCardGrid } from '../../shared/components/recipe-card-grid/recipe-card-grid';
|
import { RecipeCardGrid } from '../../shared/components/recipe-card-grid/recipe-card-grid';
|
||||||
import { RecipeInfoView } from '../../shared/models/Recipe.model';
|
import { RecipeInfoView } from '../../shared/models/Recipe.model';
|
||||||
import { Spinner } from '../../shared/components/spinner/spinner';
|
import { Spinner } from '../../shared/components/spinner/spinner';
|
||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
|
||||||
import { combineLatest } from 'rxjs';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recipes-page',
|
selector: 'app-recipes-page',
|
||||||
imports: [RecipeCardGrid, Spinner, MatPaginator],
|
imports: [RecipeCardGrid, Spinner],
|
||||||
templateUrl: './recipes-page.html',
|
templateUrl: './recipes-page.html',
|
||||||
styleUrl: './recipes-page.css',
|
styleUrl: './recipes-page.css',
|
||||||
})
|
})
|
||||||
@ -19,27 +17,12 @@ export class RecipesPage implements OnInit {
|
|||||||
protected readonly loadRecipesError = signal<Error | null>(null);
|
protected readonly loadRecipesError = signal<Error | null>(null);
|
||||||
protected readonly recipes = signal<RecipeInfoView[]>([]);
|
protected readonly recipes = signal<RecipeInfoView[]>([]);
|
||||||
|
|
||||||
protected readonly recipeCount = signal(50);
|
|
||||||
protected readonly currentPage = signal(0);
|
|
||||||
protected readonly pageSize = signal(10);
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.loadRecipes();
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadRecipes() {
|
|
||||||
this.loadingRecipes.set(true);
|
this.loadingRecipes.set(true);
|
||||||
combineLatest([
|
this.recipeService.getRecipes().subscribe({
|
||||||
this.recipeService.getRecipes({
|
next: (sliceView) => {
|
||||||
page: this.currentPage(),
|
|
||||||
size: this.pageSize(),
|
|
||||||
}),
|
|
||||||
this.recipeService.getRecipeCount(),
|
|
||||||
]).subscribe({
|
|
||||||
next: ([sliceView, count]) => {
|
|
||||||
this.loadingRecipes.set(false);
|
this.loadingRecipes.set(false);
|
||||||
this.recipes.set(sliceView.content);
|
this.recipes.set(sliceView.content);
|
||||||
this.recipeCount.set(count);
|
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.loadingRecipes.set(false);
|
this.loadingRecipes.set(false);
|
||||||
@ -47,24 +30,4 @@ 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import { ImageSelect } from './image-select/image-select';
|
import { ImageSelect } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import {
|
import {
|
||||||
MatCell,
|
MatCell,
|
||||||
@ -34,11 +34,11 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
|
|||||||
import { faBars, faEllipsis } from '@fortawesome/free-solid-svg-icons';
|
import { faBars, faEllipsis } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { IngredientDraftClientModel } from '../../client-models/IngredientDraftClientModel';
|
import { IngredientDraftClientModel } from '../../client-models/IngredientDraftClientModel';
|
||||||
import { ImageView } from '../../models/ImageView.model';
|
import { ImageView } from '../../models/ImageView.model';
|
||||||
import { IngredientDialog } from './ingredient-dialog/ingredient-dialog';
|
import { IngredientDialog } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/ingredient-dialog/ingredient-dialog';
|
||||||
import { RecipeDraftViewModel } from '../../models/RecipeDraftView.model';
|
import { RecipeDraftViewModel } from '../../models/RecipeDraftView.model';
|
||||||
import { RecipeEditFormSubmitEvent } from './RecipeEditFormSubmitEvent';
|
import { RecipeEditFormSubmitEvent } from './RecipeEditFormSubmitEvent';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ImageUploadDialog } from './image-upload-dialog/image-upload-dialog';
|
import { ImageUploadDialog } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/image-upload-dialog/image-upload-dialog';
|
||||||
import { FullRecipeView } from '../../models/Recipe.model';
|
import { FullRecipeView } from '../../models/Recipe.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@ -16,17 +16,6 @@ import { RecipeUpdateBody } from '../bodies/RecipeUpdateBody';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class RecipeService {
|
export class RecipeService {
|
||||||
public static readonly RecipeProperties = [
|
|
||||||
'id',
|
|
||||||
'created',
|
|
||||||
'modified',
|
|
||||||
'title',
|
|
||||||
'slug',
|
|
||||||
'preparationTime',
|
|
||||||
'cookingTime',
|
|
||||||
'totalTime',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
private readonly http = inject(HttpClient);
|
private readonly http = inject(HttpClient);
|
||||||
private readonly authService = inject(AuthService);
|
private readonly authService = inject(AuthService);
|
||||||
private readonly queryClient = inject(QueryClient);
|
private readonly queryClient = inject(QueryClient);
|
||||||
@ -57,23 +46,13 @@ export class RecipeService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRecipes(
|
public getRecipes(): Observable<SliceView<RecipeInfoView>> {
|
||||||
queryParams?: QueryParams<typeof RecipeService.RecipeProperties>,
|
return this.http.get<SliceView<WithStringDates<RecipeInfoView>>>(this.endpointService.getUrl('recipes')).pipe(
|
||||||
): Observable<SliceView<RecipeInfoView>> {
|
map((sliceView) => ({
|
||||||
return this.http
|
...sliceView,
|
||||||
.get<SliceView<WithStringDates<RecipeInfoView>>>(this.endpointService.getUrl('recipes', [], queryParams))
|
content: sliceView.content.map((withStringDates) => this.hydrateRecipeInfoView(withStringDates)),
|
||||||
.pipe(
|
})),
|
||||||
map((sliceView) => ({
|
);
|
||||||
...sliceView,
|
|
||||||
content: sliceView.content.map((withStringDates) => this.hydrateRecipeInfoView(withStringDates)),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getRecipeCount(): Observable<number> {
|
|
||||||
return this.http
|
|
||||||
.get<{ count: number }>(this.endpointService.getUrl('recipes', ['meta', 'count']))
|
|
||||||
.pipe(map((res) => res.count));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRecipeView(username: string, slug: string): Promise<FullRecipeViewWrapper> {
|
public getRecipeView(username: string, slug: string): Promise<FullRecipeViewWrapper> {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user