aboutsummaryrefslogtreecommitdiffstats
path: root/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe
diff options
context:
space:
mode:
Diffstat (limited to 'public/projects/angular-small-apps/apps/recipes/src/app/components/recipe')
-rw-r--r--public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.html111
-rw-r--r--public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.scss54
-rw-r--r--public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.spec.ts24
-rw-r--r--public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.ts140
4 files changed, 329 insertions, 0 deletions
diff --git a/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.html b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.html
new file mode 100644
index 0000000..9f875d9
--- /dev/null
+++ b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.html
@@ -0,0 +1,111 @@
+<article class="recipe">
+ <header class="recipe__header">
+ <toolbar (isEditionMode)="isContentEditable($event)"></toolbar>
+ <h2 class="recipe__title">
+ <ng-container
+ *ngIf="isEditable; then editableTitle; else staticTitle"
+ ></ng-container>
+ <ng-template #editableTitle>
+ <input
+ type="text"
+ value="{{ recipe.strMeal }}"
+ name="strMeal"
+ (input)="updateRecipe($event)"
+ class="recipe__field"
+ />
+ </ng-template>
+ <ng-template #staticTitle>{{ recipe.strMeal }}</ng-template>
+ </h2>
+ <dl class="meta">
+ <dt class="meta__term">Category:</dt>
+ <dd class="meta__description">
+ <ng-container
+ *ngIf="isEditable; then editableCategory; else staticCategory"
+ ></ng-container>
+ <ng-template #staticCategory>
+ {{ recipe.strCategory }}
+ </ng-template>
+ <ng-template #editableCategory>
+ <input
+ type="text"
+ name="strCategory"
+ value="{{ recipe.strCategory }}"
+ (input)="updateRecipe($event)"
+ class="recipe__field"
+ />
+ </ng-template>
+ </dd>
+ </dl>
+ </header>
+ <div class="recipe__body">
+ <div class="recipe__preview">
+ <h3>Preview</h3>
+ <img
+ [src]="getPreview()"
+ alt="{{ recipe.strMeal }} preview"
+ class="recipe__thumb"
+ />
+ <ng-container *ngIf="isEditable">
+ <label class="recipe__label btn" for="recipe-thumb">
+ Upload a new image
+ <input
+ type="file"
+ name="recipe-thumb"
+ id="recipe-thumb"
+ accept="image/png, image/jpeg"
+ class="recipe__field recipe__field--file"
+ (change)="updatePreview($event)"
+ />
+ </label>
+ </ng-container>
+ </div>
+ <div class="recipe__ingredients">
+ <h3>Ingredients</h3>
+ <ul class="ingredients-list">
+ <li
+ *ngFor="let ingredient of getIngredients(); index as i"
+ class="ingredients-list__item"
+ >
+ <ng-container
+ *ngIf="isEditable; then editableIngredients; else staticIngredients"
+ ></ng-container>
+ <ng-template #staticIngredients>
+ {{ ingredient }}
+ </ng-template>
+ <ng-template #editableIngredients>
+ <input
+ type="text"
+ name="strIngredient{{ i + 1 }}"
+ value="{{ ingredient }}"
+ (input)="updateRecipe($event)"
+ class="recipe__field"
+ />
+ </ng-template>
+ </li>
+ </ul>
+ </div>
+ <div class="recipe__instructions">
+ <h3>Instructions</h3>
+ <ng-container
+ *ngIf="isEditable; then editableInstructions; else staticInstructions"
+ ></ng-container>
+ <ng-template #editableInstructions>
+ <textarea
+ textareaResize
+ name="strInstructions"
+ (input)="updateRecipe($event)"
+ class="recipe__field recipe__field--textarea"
+ >{{ recipe.strInstructions }}</textarea
+ >
+ </ng-template>
+ <ng-template #staticInstructions>{{
+ recipe.strInstructions
+ }}</ng-template>
+ </div>
+ </div>
+ <footer class="recipe__footer">
+ <a routerLink="" class="btn">
+ <span class="btn__icon">&leftarrow; </span>Back to your recipes
+ </a>
+ </footer>
+</article>
diff --git a/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.scss b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.scss
new file mode 100644
index 0000000..82294b8
--- /dev/null
+++ b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.scss
@@ -0,0 +1,54 @@
+.recipe {
+ background: #fff;
+ border: 1px solid #d7d7d7;
+ border-radius: 5px;
+ box-shadow: 0 0 5px -3px #000000a5;
+ padding: clamp(4rem, 3vw, 5rem) clamp(1rem, 3vw, 2rem) clamp(2rem, 3vw, 4rem);
+ position: relative;
+
+ &__body {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: flex-start;
+ gap: clamp(1rem, 3vw, 2rem);
+ margin: 1rem 0;
+ }
+
+ &__thumb {
+ max-width: min(100vw - 4rem, 300px);
+ }
+
+ &__instructions {
+ flex: 0 0 100%;
+ white-space: pre-line;
+ }
+
+ &__footer {
+ margin: 2rem 0 0;
+ }
+
+ &__field {
+ all: inherit;
+ width: 100%;
+
+ &--textarea {
+ width: 100%;
+ min-height: 10rem;
+ }
+
+ &--file {
+ display: none;
+ }
+ }
+
+ &__label {
+ display: block;
+ cursor: pointer;
+ width: max-content;
+ margin: auto;
+ }
+}
+
+.ingredients-list {
+ padding: 0 1rem;
+}
diff --git a/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.spec.ts b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.spec.ts
new file mode 100644
index 0000000..55a83e9
--- /dev/null
+++ b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.spec.ts
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RecipeComponent } from './recipe.component';
+
+describe('RecipeComponent', () => {
+ let component: RecipeComponent;
+ let fixture: ComponentFixture<RecipeComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [RecipeComponent],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RecipeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.ts b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.ts
new file mode 100644
index 0000000..070220f
--- /dev/null
+++ b/public/projects/angular-small-apps/apps/recipes/src/app/components/recipe/recipe.component.ts
@@ -0,0 +1,140 @@
+import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute, Event, Router } from '@angular/router';
+import { Recipes } from 'src/app/shared/recipes';
+import { LocalStorageService } from 'src/app/shared/services/local-storage.service';
+import { RecipesService } from 'src/app/shared/services/recipes.service';
+
+@Component({
+ templateUrl: './recipe.component.html',
+ styleUrls: ['./recipe.component.scss'],
+ encapsulation: ViewEncapsulation.Emulated,
+})
+export class RecipeComponent implements OnInit {
+ recipe: Partial<Recipes> = {};
+ isEditable: boolean = false;
+ savedRecipes: Recipes[] = this.storage.get('recipes');
+ preview: string = this.recipe.strMealThumb!;
+
+ constructor(
+ private storage: LocalStorageService,
+ private route: ActivatedRoute,
+ private recipes: RecipesService,
+ private router: Router
+ ) {}
+
+ ngOnInit(): void {
+ const slug = this.route.snapshot.paramMap.get('slug') || '';
+ this.setRecipe(slug);
+ this.preview = this.recipe.strMealThumb!;
+ }
+
+ setRecipe(slug: string): void {
+ const allRecipes = this.storage.get('recipes');
+ const filteredRecipes = allRecipes.filter(
+ (meal: Recipes) => meal.slug === slug
+ );
+
+ if (filteredRecipes.length === 0) {
+ const recipeId = history.state?.id;
+ if (recipeId) {
+ this.recipes.getRecipeById(recipeId).subscribe((recipes: any) => {
+ this.recipe = recipes.meals[0];
+ this.preview = this.recipe.strMealThumb!;
+ });
+ } else {
+ this.router.navigateByUrl('/404');
+ }
+ } else {
+ this.recipe = filteredRecipes[0] ? filteredRecipes[0] : {};
+ }
+ }
+
+ getIngredients(): string[] {
+ const ingredients = [];
+
+ for (let i = 1; i <= 20; i++) {
+ const currentIngredient = `strIngredient${i}` as keyof Recipes;
+ let ingredient;
+
+ if (this.recipe[currentIngredient]) {
+ const currentMeasure = `strMeasure${i}` as keyof Recipes;
+
+ if (this.recipe[currentMeasure]) {
+ ingredient = `${this.recipe[currentMeasure]} ${this.recipe[currentIngredient]}`;
+ } else {
+ ingredient = `${this.recipe[currentIngredient]}`;
+ }
+ }
+
+ if (ingredient) ingredients.push(ingredient);
+ }
+
+ return ingredients;
+ }
+
+ isContentEditable(value: boolean): void {
+ this.isEditable = value;
+ }
+
+ updateRecipe(e: any) {
+ const recipeProperty = e.target.name as keyof Recipes;
+
+ if (!recipeProperty) {
+ return;
+ }
+
+ this.recipe[recipeProperty] = e.target.value;
+
+ if (recipeProperty.startsWith('strIngredient')) {
+ const ingredientNumber = recipeProperty.replace('strIngredient', '');
+ const measureProperty = `strMeasure${ingredientNumber}` as keyof Recipes;
+ this.recipe[measureProperty] = undefined;
+ }
+
+ const updatedRecipes = this.savedRecipes.map((recipe: Recipes) => {
+ if (recipe.idMeal === this.recipe.idMeal) {
+ return { ...this.recipe };
+ }
+ return recipe;
+ });
+ this.storage.set('recipes', updatedRecipes);
+ }
+
+ getPreview() {
+ return this.preview;
+ }
+
+ getImage(img: any) {
+ var canvas = document.createElement('canvas');
+ canvas.width = img.width;
+ canvas.height = img.height;
+
+ var ctx = canvas.getContext('2d')!;
+ ctx.drawImage(img, 0, 0);
+
+ var dataURL = canvas.toDataURL('image/png');
+
+ return dataURL.replace(/^data:image\/(png|jpg);base64,/, '');
+ }
+
+ updatePreview(e: any) {
+ const newPreview = e.target.files[0];
+
+ if (FileReader && newPreview) {
+ const fileReader = new FileReader();
+ fileReader.onload = (reader) => {
+ if (reader.target?.result) {
+ const updatedRecipes = this.savedRecipes.map((recipe: Recipes) => {
+ if (recipe.idMeal === this.recipe.idMeal) {
+ return { ...recipe, strMealThumb: reader.target?.result };
+ }
+ return recipe;
+ });
+ this.preview = reader.target?.result as string;
+ this.storage.set('recipes', updatedRecipes);
+ }
+ };
+ fileReader.readAsDataURL(newPreview);
+ }
+ }
+}