From 729da35987fe0b8a7ad0780d961ab0b30160dd5d Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Sun, 30 Jun 2024 09:20:33 +0200 Subject: [PATCH] Add delivery and ingredients CRUD --- src/app/app.component.html | 14 +++-- src/app/app.component.ts | 10 +-- src/app/app.routes.ts | 8 ++- src/app/delivery.service.spec.ts | 16 +++++ src/app/delivery.service.ts | 22 +++++++ src/app/delivery/delivery.component.html | 13 ++++ src/app/delivery/delivery.component.spec.ts | 23 +++++++ src/app/delivery/delivery.component.ts | 12 ++++ .../ingredient-add.component.html | 15 +++++ .../ingredient-add.component.spec.ts | 23 +++++++ .../ingredient-add.component.ts | 63 +++++++++++++++++++ .../ingredients/ingredients.component.html | 34 ++++++++++ .../ingredients/ingredients.component.spec.ts | 23 +++++++ src/app/ingredients/ingredients.component.ts | 52 +++++++++++++++ src/app/recipe.service.ts | 24 +++++++ src/app/recipe/recipe.component.ts | 6 +- src/app/recipes/recipes.component.html | 17 +++-- src/app/recipes/recipes.component.ts | 12 +++- 18 files changed, 366 insertions(+), 21 deletions(-) create mode 100644 src/app/delivery.service.spec.ts create mode 100644 src/app/delivery.service.ts create mode 100644 src/app/delivery/delivery.component.html create mode 100644 src/app/delivery/delivery.component.spec.ts create mode 100644 src/app/delivery/delivery.component.ts create mode 100644 src/app/ingredient-add/ingredient-add.component.html create mode 100644 src/app/ingredient-add/ingredient-add.component.spec.ts create mode 100644 src/app/ingredient-add/ingredient-add.component.ts create mode 100644 src/app/ingredients/ingredients.component.html create mode 100644 src/app/ingredients/ingredients.component.spec.ts create mode 100644 src/app/ingredients/ingredients.component.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 46f646e..911bbe5 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,7 +1,13 @@ Tiramisu - - + + +@if (login.isLoggedIn()) { + + + +} @else { + +} - - \ No newline at end of file + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bda1aaa..081b544 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,8 +1,10 @@ import { Component } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatMenuModule } from '@angular/material/menu'; import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { DeliveryService } from './delivery.service'; +import { LoginService } from './login.service'; import { RecipeService } from './recipe.service'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatButtonModule } from '@angular/material/button'; @Component({ selector: 'app-root', @@ -10,11 +12,11 @@ import { MatButtonModule } from '@angular/material/button'; imports: [RouterOutlet, RouterLink, RouterLinkActive, MatMenuModule, MatButtonModule], templateUrl: './app.component.html', styleUrl: './app.component.css', - providers: [RecipeService], + providers: [RecipeService, LoginService, DeliveryService], }) export class AppComponent { title = 'tiramisu'; - constructor(protected recipes: RecipeService) { + constructor(protected recipes: RecipeService, protected login: LoginService) { } } diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 1180ca1..72e5f71 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,6 +1,9 @@ import { inject } from '@angular/core'; import { CanActivateFn, Routes } from '@angular/router'; import { AuthComponent } from './auth/auth.component'; +import { DeliveryComponent } from './delivery/delivery.component'; +import { IngredientAddComponent } from './ingredient-add/ingredient-add.component'; +import { IngredientsComponent } from './ingredients/ingredients.component'; import { LoginService } from './login.service'; import { RecipeAddComponent } from './recipe-add/recipe-add.component'; import { RecipeComponent } from './recipe/recipe.component'; @@ -8,11 +11,14 @@ import { RecipesComponent } from './recipes/recipes.component'; const LoggedGuard: CanActivateFn = () => inject(LoginService).isLoggedIn(); export const routes: Routes = [ + { path: '', component: DeliveryComponent, pathMatch: 'full' }, { path: 'recipes', component: RecipesComponent }, { path: 'recipe/add', component: RecipeAddComponent }, { path: 'recipe/:id', component: RecipeComponent }, { path: 'recipe/:id/edit', component: RecipeAddComponent }, - { path: 'ingredients', component: RecipesComponent, canActivate: [LoggedGuard] }, + { path: 'ingredients', component: IngredientsComponent, canActivate: [LoggedGuard] }, + { path: 'ingredients/add', component: IngredientAddComponent, canActivate: [LoggedGuard] }, + { path: 'ingredients/:id/edit', component: IngredientAddComponent, canActivate: [LoggedGuard] }, { path: 'login', component: AuthComponent }, { path: 'logout', component: AuthComponent, data: { registering: false }, canActivate: [LoggedGuard] }, ]; diff --git a/src/app/delivery.service.spec.ts b/src/app/delivery.service.spec.ts new file mode 100644 index 0000000..cdbac5f --- /dev/null +++ b/src/app/delivery.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { DeliveryService } from './delivery.service'; + +describe('DeliveryService', () => { + let service: DeliveryService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DeliveryService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/delivery.service.ts b/src/app/delivery.service.ts new file mode 100644 index 0000000..2739d61 --- /dev/null +++ b/src/app/delivery.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { Recipe } from '../cookbook/type'; + +@Injectable({ + providedIn: 'root', +}) +export class DeliveryService { + readonly #shoppingCart: Recipe[]; + + constructor() { + this.#shoppingCart = JSON.parse(localStorage.getItem('shoppingCart') || '[]'); + } + + addToCart(recipe: Recipe): void { + this.#shoppingCart.push(recipe); + localStorage.setItem('shoppingCart', JSON.stringify(this.#shoppingCart)); + } + + get shoppingCart(): readonly Recipe[] { + return this.#shoppingCart; + } +} diff --git a/src/app/delivery/delivery.component.html b/src/app/delivery/delivery.component.html new file mode 100644 index 0000000..49917af --- /dev/null +++ b/src/app/delivery/delivery.component.html @@ -0,0 +1,13 @@ +

Commande en cours

+ +@for (item of delivery.shoppingCart; track item.id) { +
+ +
+

{{item.name}}

+

+ {{item.description}} +

+
+
+} diff --git a/src/app/delivery/delivery.component.spec.ts b/src/app/delivery/delivery.component.spec.ts new file mode 100644 index 0000000..1bca60d --- /dev/null +++ b/src/app/delivery/delivery.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeliveryComponent } from './delivery.component'; + +describe('DeliveryComponent', () => { + let component: DeliveryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DeliveryComponent], + }) + .compileComponents(); + + fixture = TestBed.createComponent(DeliveryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/delivery/delivery.component.ts b/src/app/delivery/delivery.component.ts new file mode 100644 index 0000000..8a0221e --- /dev/null +++ b/src/app/delivery/delivery.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { DeliveryService } from '../delivery.service'; + +@Component({ + selector: 'app-delivery', + standalone: true, + imports: [], + templateUrl: './delivery.component.html', +}) +export class DeliveryComponent { + constructor(protected delivery: DeliveryService) {} +} diff --git a/src/app/ingredient-add/ingredient-add.component.html b/src/app/ingredient-add/ingredient-add.component.html new file mode 100644 index 0000000..2c7be28 --- /dev/null +++ b/src/app/ingredient-add/ingredient-add.component.html @@ -0,0 +1,15 @@ +
+
+ + + + @if (createForm.controls.name.errors?.['minlength']) { + Le nom doit contenir au moins {{ createForm.controls.name.errors!['minlength'].requiredLength }} caractères. + } + @if (createForm.controls.name.errors?.['maxlength']) { + Le nom ne peut dépasser {{ createForm.controls.name.errors!['maxlength'].requiredLength }} caractères. + } + +
+ +
diff --git a/src/app/ingredient-add/ingredient-add.component.spec.ts b/src/app/ingredient-add/ingredient-add.component.spec.ts new file mode 100644 index 0000000..57a6474 --- /dev/null +++ b/src/app/ingredient-add/ingredient-add.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IngredientAddComponent } from './ingredient-add.component'; + +describe('IngredientAddComponent', () => { + let component: IngredientAddComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [IngredientAddComponent], + }) + .compileComponents(); + + fixture = TestBed.createComponent(IngredientAddComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/ingredient-add/ingredient-add.component.ts b/src/app/ingredient-add/ingredient-add.component.ts new file mode 100644 index 0000000..1954bd0 --- /dev/null +++ b/src/app/ingredient-add/ingredient-add.component.ts @@ -0,0 +1,63 @@ +import { Component, Input } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { MatOption } from '@angular/material/core'; +import { MatError, MatFormField } from '@angular/material/form-field'; +import { MatInput } from '@angular/material/input'; +import { MatSelect } from '@angular/material/select'; +import { Router } from '@angular/router'; +import { RecipeService } from '../recipe.service'; + +@Component({ + selector: 'app-ingredient-add', + standalone: true, + imports: [ + MatButton, + MatError, + MatFormField, + MatInput, + MatOption, + MatSelect, + ReactiveFormsModule, + ], + templateUrl: './ingredient-add.component.html', +}) +export class IngredientAddComponent { + createForm = this.formBuilder.group({ + name: '', + }); + + #ingredientId: number = -1; + @Input() + set id(recipeId: string) { + if (recipeId === undefined) return; + this.#ingredientId = parseInt(recipeId); + const ingredient = this.recipes.getIngredientById(this.#ingredientId); + if (ingredient === null) { + this.router.navigateByUrl('404'); + return; + } + this.createForm.patchValue({ + name: ingredient.name, + }); + } + get ingredientId() { + return this.#ingredientId; + } + + constructor(private formBuilder: FormBuilder, private recipes: RecipeService, private router: Router) { + } + + onSubmit() { + if (this.createForm.invalid) { + return; + } + const value = this.createForm.value; + if (this.ingredientId !== -1) { + this.recipes.editIngredient({ id: this.ingredientId, name: value.name! }); + } else { + this.recipes.addIngredient({ name: value.name! }); + } + this.router.navigateByUrl('/ingredients'); + } +} diff --git a/src/app/ingredients/ingredients.component.html b/src/app/ingredients/ingredients.component.html new file mode 100644 index 0000000..20c1394 --- /dev/null +++ b/src/app/ingredients/ingredients.component.html @@ -0,0 +1,34 @@ +
+ + + + + + + + + + + + + + + + + + + +
id + {{element.id}} + name + {{element.name}} + actions + + +
+ + + +
diff --git a/src/app/ingredients/ingredients.component.spec.ts b/src/app/ingredients/ingredients.component.spec.ts new file mode 100644 index 0000000..70dc120 --- /dev/null +++ b/src/app/ingredients/ingredients.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IngredientsComponent } from './ingredients.component'; + +describe('IngredientsComponent', () => { + let component: IngredientsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [IngredientsComponent], + }) + .compileComponents(); + + fixture = TestBed.createComponent(IngredientsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/ingredients/ingredients.component.ts b/src/app/ingredients/ingredients.component.ts new file mode 100644 index 0000000..660c32d --- /dev/null +++ b/src/app/ingredients/ingredients.component.ts @@ -0,0 +1,52 @@ +import { Component } from '@angular/core'; +import { MatButton } from '@angular/material/button'; +import { MatPaginator } from '@angular/material/paginator'; +import { + MatCell, + MatCellDef, + MatColumnDef, + MatHeaderCell, + MatHeaderCellDef, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, + MatTable, + MatTableDataSource, +} from '@angular/material/table'; +import { RouterLink } from '@angular/router'; +import { Ingredient } from '../../cookbook/type'; +import { RecipeService } from '../recipe.service'; + +@Component({ + selector: 'app-ingredients', + standalone: true, + imports: [ + MatButton, + MatCell, + MatCellDef, + MatColumnDef, + MatHeaderCell, + MatHeaderRow, + MatHeaderRowDef, + MatPaginator, + MatRow, + MatRowDef, + MatTable, + MatHeaderCellDef, + RouterLink, + ], + templateUrl: './ingredients.component.html', +}) +export class IngredientsComponent { + displayedColumns: string[] = ['id', 'name', 'actions']; + dataSource = new MatTableDataSource(); + + constructor(protected recipes: RecipeService) { + this.dataSource = new MatTableDataSource(recipes.getAllIngredients()); + } + + delete(ingredient: Ingredient): void { + this.recipes.deleteIngredient(ingredient); + } +} diff --git a/src/app/recipe.service.ts b/src/app/recipe.service.ts index eea5f4e..77b2f51 100644 --- a/src/app/recipe.service.ts +++ b/src/app/recipe.service.ts @@ -67,4 +67,28 @@ export class RecipeService { } } } + + addIngredient(ingredient: Omit) { + const id = this.#ingredients.length ? Math.max(...this.#ingredients.map((ingredient) => ingredient.id)) + 1 : 1; + this.#ingredients.push({ + id, + ...ingredient, + }); + } + + editIngredient(ingredient: Ingredient) { + for (let i = 0; i < this.#ingredients.length; ++i) { + if (this.#ingredients[i].id === ingredient.id) { + this.#ingredients[i] = ingredient; + } + } + } + + deleteIngredient(ingredient: Ingredient) { + const index = this.#ingredients.findIndex((v) => v.id === ingredient.id); + if (index === -1) { + return; + } + this.#ingredients.splice(index, 1); + } } diff --git a/src/app/recipe/recipe.component.ts b/src/app/recipe/recipe.component.ts index d566f26..3496ff0 100644 --- a/src/app/recipe/recipe.component.ts +++ b/src/app/recipe/recipe.component.ts @@ -1,10 +1,10 @@ import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { Recipe } from '../../cookbook/type'; -import { RecipeService } from '../recipe.service'; -import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; import { RouterLink } from '@angular/router'; +import { Recipe } from '../../cookbook/type'; +import { RecipeService } from '../recipe.service'; @Component({ selector: 'app-recipe', diff --git a/src/app/recipes/recipes.component.html b/src/app/recipes/recipes.component.html index d9e2334..13f9dab 100644 --- a/src/app/recipes/recipes.component.html +++ b/src/app/recipes/recipes.component.html @@ -1,10 +1,10 @@
- + - @@ -30,13 +30,18 @@ - + + + + + - -
id + {{element.id}} actions + + +
- + diff --git a/src/app/recipes/recipes.component.ts b/src/app/recipes/recipes.component.ts index d400d66..58cf2d4 100644 --- a/src/app/recipes/recipes.component.ts +++ b/src/app/recipes/recipes.component.ts @@ -1,21 +1,23 @@ import { AfterViewInit, Component, ViewChild } from '@angular/core'; +import { MatButton } from '@angular/material/button'; import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { RouterLink } from '@angular/router'; import { Recipe } from '../../cookbook/type'; +import { DeliveryService } from '../delivery.service'; import { RecipeService } from '../recipe.service'; @Component({ selector: 'app-recipes', standalone: true, - imports: [MatTableModule, MatPaginatorModule, RouterLink], + imports: [MatTableModule, MatPaginatorModule, RouterLink, MatButton], templateUrl: './recipes.component.html', }) export class RecipesComponent implements AfterViewInit { - displayedColumns: string[] = ['id', 'name', 'description', 'image']; + displayedColumns: string[] = ['id', 'name', 'description', 'image', 'actions']; dataSource = new MatTableDataSource(); - constructor(protected recipes: RecipeService) { + constructor(protected recipes: RecipeService, private delivery: DeliveryService) { this.dataSource = new MatTableDataSource(recipes.getAll()); } @@ -25,4 +27,8 @@ export class RecipesComponent implements AfterViewInit { ngAfterViewInit() { this.dataSource.paginator = this.paginator; } + + deliver(recipe: Recipe): void { + this.delivery.addToCart(recipe); + } }