Advertisement
didkoslawow

Untitled

Mar 7th, 2024
538
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { Component, OnInit } from '@angular/core';
  2. import { Recipe } from '../../../recipes/recipe.model';
  3. import {
  4.   FormBuilder,
  5.   FormGroup,
  6.   ReactiveFormsModule,
  7.   Validators,
  8. } from '@angular/forms';
  9. import { RecipeService } from '../../../services/recipe.service';
  10. import { CarouselComponent } from '../../../shared/carousel/carousel.component';
  11. import { HttpParams } from '@angular/common/http';
  12. import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
  13. import { faTurnUp } from '@fortawesome/free-solid-svg-icons';
  14.  
  15. @Component({
  16.   selector: 'app-profile-home',
  17.   standalone: true,
  18.   imports: [ReactiveFormsModule, CarouselComponent, FontAwesomeModule],
  19.   templateUrl: './profile-search.component.html',
  20.   styleUrl: './profile-search.component.scss',
  21. })
  22. export class ProfileHomeComponent implements OnInit {
  23.   searchForm: FormGroup;
  24.   recipes: Recipe[] | null = [];
  25.   ingredients: string[] = [];
  26.   pagination: number = 0;
  27.  
  28.   declare faArrowUp;
  29.  
  30.   constructor(
  31.     private formBuilder: FormBuilder,
  32.     private recipeService: RecipeService
  33.   ) {
  34.     this.faArrowUp = faTurnUp;
  35.     this.searchForm = this.formBuilder.group({
  36.       searchQuery: ['', Validators.required],
  37.     });
  38.   }
  39.  
  40.   ngOnInit(): void {}
  41.  
  42.   onSubmit(): void {
  43.     if (this.searchForm.valid) {
  44.       const searchQuery = this.searchForm.value.searchQuery;
  45.       this.ingredients = searchQuery
  46.         .split(',')
  47.         .map((ingredient: string) => ingredient.trim());
  48.  
  49.       let params = new HttpParams();
  50.       this.ingredients.forEach((ingredient: string) => {
  51.         params = params.append('ingredients', ingredient);
  52.       });
  53.  
  54.       this.recipeService.searchRecipesByIngredients(params).subscribe({
  55.         next: (recipeData) => {
  56.           this.recipes = recipeData.recipes;
  57.           this.pagination = recipeData.count;
  58.         },
  59.         error: (error) => {
  60.           console.error('Error searching recipes:', error);
  61.         },
  62.       });
  63.     }
  64.   }
  65. }
  66.  
  67.  
  68. <section class="search">
  69.     <h2 class="search__heading subheading">Turn ingredients into inspiration</h2>
  70.     <form class="search__form" [formGroup]="searchForm" (ngSubmit)="onSubmit()">
  71.         <div class="search__form--controls">
  72.             <input type="text" placeholder="placeholder" id="search" class="search__form--controls-input"
  73.                 formControlName="searchQuery" />
  74.             <label for="search" class="search__form--controls-label">Search</label>
  75.         </div>
  76.         <button type="submit" class="search__form--btn btn btn--full">Search</button>
  77.     </form>
  78.     @if(recipes?.length) {
  79.     <app-carousel class="app-carousel" [carouselType]="'search'" [ingredients]="ingredients" />
  80.     } @else {
  81.     <p class="search__no-content">Try searching for ingredients<span
  82.             class="animate__animated animate__heartBeat animate__infinite"><fa-icon [icon]="faArrowUp"
  83.                 class="search__no-content--icon" /></span></p>
  84.     }
  85. </section>
  86.  
  87. import {
  88.   Component,
  89.   OnInit,
  90.   Input,
  91.   OnChanges,
  92.   SimpleChanges,
  93. } from '@angular/core';
  94. import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
  95. import { faAngleLeft, faAngleRight } from '@fortawesome/free-solid-svg-icons';
  96. import { Recipe, RecipeData } from '../../recipes/recipe.model';
  97. import { RecipeService } from '../../services/recipe.service';
  98. import { RecipeCardComponent } from '../../recipes/recipe-card/recipe-card.component';
  99. import { Store, select } from '@ngrx/store';
  100. import { getUserData } from '../../store/auth/auth.selectors';
  101. import { User } from '../../store/auth/user.model';
  102. import { ActivatedRoute, Router, RouterModule } from '@angular/router';
  103. import { HttpParams } from '@angular/common/http';
  104. import { CommonModule } from '@angular/common';
  105. import { Observable, Subject, switchMap, takeUntil } from 'rxjs';
  106. import { ProfileHomeComponent } from '../../user/profile/profile-search/profile-search.component';
  107.  
  108. @Component({
  109.   selector: 'app-carousel',
  110.   standalone: true,
  111.   imports: [RecipeCardComponent, FontAwesomeModule, CommonModule, RouterModule],
  112.   templateUrl: './carousel.component.html',
  113.   styleUrl: './carousel.component.scss',
  114. })
  115. export class CarouselComponent implements OnInit, OnChanges {
  116.   @Input() carouselType: 'all' | 'user' | 'search' = 'all';
  117.   @Input() title: string = '';
  118.   @Input() ingredients: string[] = [];
  119.  
  120.   declare user: User | null;
  121.   declare faArrowLeft;
  122.   declare faArrowRight;
  123.  
  124.   recipes: Recipe[] = [];
  125.   pagination: number[] = [];
  126.  
  127.   currentOffset = 0;
  128.   currentPage = 0;
  129.   recipesCount = 0;
  130.   limit = 4;
  131.  
  132.   private unsubscribe$ = new Subject<void>();
  133.  
  134.   constructor(
  135.     private recipeService: RecipeService,
  136.     private store: Store,
  137.     private route: ActivatedRoute
  138.   ) {
  139.     this.faArrowLeft = faAngleLeft;
  140.     this.faArrowRight = faAngleRight;
  141.   }
  142.  
  143.   ngOnChanges(changes: SimpleChanges) {
  144.     if (changes['ingredients'] && !changes['ingredients'].firstChange) {
  145.       // Only trigger if ingredients change and it's not the initial change
  146.       this.fetchRecipes(0);
  147.     }
  148.   }
  149.  
  150.   ngOnInit(): void {
  151.     this.route.queryParams
  152.       .pipe(
  153.         switchMap((params) => {
  154.           const offset = parseInt(params['offset'] || '0');
  155.           this.currentPage =
  156.             this.currentPage >= Math.ceil(this.pagination.length / this.limit)
  157.               ? Math.ceil(offset / this.limit + 1)
  158.               : this.currentPage;
  159.           this.currentOffset = offset <= 0 ? 0 : offset - this.limit;
  160.  
  161.           return this.fetchRecipes(offset);
  162.         }),
  163.         takeUntil(this.unsubscribe$)
  164.       )
  165.       .subscribe({
  166.         next: (recipeData: RecipeData) => {
  167.           this.recipes = recipeData.recipes;
  168.           this.recipesCount = recipeData.count;
  169.           this.pagination = new Array(Math.ceil(this.recipesCount / this.limit))
  170.             .fill(0)
  171.             .map((_, i) => i + 1);
  172.         },
  173.         error: () => {
  174.           this.recipes = [];
  175.           this.recipesCount = 0;
  176.         },
  177.       });
  178.  
  179.     this.store
  180.       .pipe(select(getUserData), takeUntil(this.unsubscribe$))
  181.       .subscribe((user: any) => {
  182.         this.user = user?.user;
  183.       });
  184.   }
  185.  
  186.   ngOnDestroy(): void {
  187.     this.unsubscribe$.next();
  188.     this.unsubscribe$.complete();
  189.   }
  190.  
  191.   private fetchRecipes(offset: number): Observable<RecipeData> {
  192.     let queryParams = new HttpParams()
  193.       .set('offset', offset.toString())
  194.       .set('limit', this.limit.toString());
  195.  
  196.     if (this.carouselType === 'search' && this.ingredients.length > 0) {
  197.       queryParams = queryParams.set('ingredients', this.ingredients.join(','));
  198.     }
  199.  
  200.     const method = this.getMethod();
  201.  
  202.     return this.recipeService[method](queryParams);
  203.   }
  204.  
  205.   private getMethod():
  206.     | 'getRecipes'
  207.     | 'getUserRecipes'
  208.     | 'searchRecipesByIngredients' {
  209.     switch (this.carouselType) {
  210.       case 'user':
  211.         return 'getUserRecipes';
  212.       case 'search':
  213.         return 'searchRecipesByIngredients';
  214.       default:
  215.         return 'getRecipes';
  216.     }
  217.   }
  218. }
  219.  
  220.  
  221. <section class="carousel" [ngClass]="{'search': carouselType === 'search'}">
  222.     @if (recipes.length) {
  223.     @if (carouselType !== 'search') {
  224.     <h2 class="carousel__heading">{{title}}</h2>
  225.     }
  226.     @for (recipe of recipes; track recipe.id) {
  227.     <app-recipe-card [recipe]="recipe" />
  228.     }
  229.     } @else {
  230.     <div class="carousel__no-content">
  231.         <p class="no-recipes">No recipes found</p>
  232.         @if(user) {
  233.         <a routerLink="/recipes/create" class="btn btn--accent">Create one</a>.
  234.         }
  235.     </div>
  236.     }
  237.     @if (recipes.length) {
  238.     <div class="carousel__pagination">
  239.         <a [routerLink]="['.']" [queryParams]="{ offset: currentOffset, limit: limit }"
  240.             [class.disabled]="currentPage <= 1" class="carousel__pagination--up">
  241.             <fa-icon [icon]="faArrowLeft"></fa-icon>
  242.         </a>
  243.         <div class="carousel__pagination--pages">
  244.             @for (page of pagination; track $index) {
  245.             <a [routerLink]="['.']" [queryParams]="{ offset: $index * limit, limit: limit }"
  246.                 [class.active-page]="currentPage === $index + 1" class="carousel__pagination--pages-page active-page">{{
  247.                 $index + 1 }}</a>
  248.             }
  249.         </div>
  250.         <a [routerLink]="['.']"
  251.             [queryParams]="{offset: currentPage === pagination.length ? 0 : currentPage * limit, limit: limit}"
  252.             [class.disabled]="currentPage === pagination.length" class="carousel__pagination--down">
  253.             <fa-icon [icon]="faArrowRight"></fa-icon>
  254.         </a>
  255.     </div>
  256.     }
  257. </section>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement