Ended project

master
Aurian JAULT 11 months ago
parent 007772da75
commit b985dd6fd1

@ -1,12 +1,12 @@
<nav> <nav>
<ul> <ul>
<li><a routerLink="/" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'title' | transloco }}</a></li> <li><a routerLink="/" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'title' | transloco }}</a></li>
<li><a routerLink="/cart" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'cart'}}</a></li>
<li *ngIf="isLogged; else elseBlock"><a routerLink="/logout" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'logout' | transloco }}</a></li> <li *ngIf="isLogged; else elseBlock"><a routerLink="/logout" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'logout' | transloco }}</a></li>
<ng-template #elseBlock> <ng-template #elseBlock>
<li><a routerLink="/login" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'login' | transloco }}</a></li> <li><a routerLink="/login" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'login' | transloco }}</a></li>
</ng-template> </ng-template>
<li><a routerLink="/recipe/add" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'recipe.add.link' | transloco }}</a></li>
<select (change)="changeLanguage($event)"> <select (change)="changeLanguage($event)">
<option value="en">🇬🇧</option> <option value="en">🇬🇧</option>
<option value="fr">🇫🇷</option> <option value="fr">🇫🇷</option>

@ -6,6 +6,8 @@ import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import { TranslocoHttpLoader } from './transloco-loader'; import { TranslocoHttpLoader } from './transloco-loader';
import { provideTransloco } from '@jsverse/transloco'; import { provideTransloco } from '@jsverse/transloco';
import { withFetch } from '@angular/common/http';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@ -21,6 +23,9 @@ export const appConfig: ApplicationConfig = {
} }
), ),
provideAnimations(), provideAnimations(),
provideHttpClient(
withFetch(),
),
] ]
}; };

@ -8,12 +8,14 @@ import { LoginComponent } from './components/login/login.component';
import { IngredientListComponent } from './components/ingredient-list/ingredient-list.component'; import { IngredientListComponent } from './components/ingredient-list/ingredient-list.component';
import { LogoutComponent } from './components/logout/logout.component'; import { LogoutComponent } from './components/logout/logout.component';
import { AuthGuard } from './auth.guard'; import { AuthGuard } from './auth.guard';
import { SavedRecipeComponent } from './components/saved-recipe/saved-recipe.component';
export const routes: Routes = [ export const routes: Routes = [
{ path: '', component: RecipeListComponent }, { path: '', component: RecipeListComponent },
{ path: 'error/:status', component: Error404Component}, { path: 'error/:status', component: Error404Component},
{ path: 'recipe/add', component: RecipeFormComponent}, { path: 'recipe/add', component: RecipeFormComponent},
{ path: 'cart', component: SavedRecipeComponent},
{ path: 'recipe/:id', component: RecipeDetailComponent}, { path: 'recipe/:id', component: RecipeDetailComponent},
{ path: 'ingredients', component: IngredientListComponent, canActivate: [AuthGuard] }, { path: 'ingredients', component: IngredientListComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent }, { path: 'login', component: LoginComponent },

@ -1 +1,37 @@
<p>ingredient-list works!</p> <p>ingredient-list works!</p>
<ng-container *ngIf="ingredients">
<div *ngFor="let ingre of ingredients">
<p>-----------</p>
<p><a>{{ ingre.name}}</a></p>
<p>Description: {{ ingre.description}}</p>
<p><button (click)="edit(ingre)">Modifier</button></p>
</div>
</ng-container>
<div class="general-form-add">
<h2>Ajouter un ingrédient</h2>
<div class="form-group">
<label for="newName">Nom:</label>
<input id="newName" [(ngModel)]="newIngredient.name" required>
</div>
<div class="form-group">
<label for="newDescription">Description:</label>
<textarea id="newDescription" [(ngModel)]="newIngredient.description" required></textarea>
</div>
<button class="button-form" (click)="addIngredient()">Ajouter</button>
</div>
<div *ngIf="editIngredient" class="general-form-edit">
<h2>Modifier l'ingrédient</h2>
<div class="form-group">
<label for="editName">Nom:</label>
<input id="editName" [(ngModel)]="editIngredient.name">
</div>
<div class="form-group">
<label for="editDescription">Description:</label>
<textarea id="editDescription" [(ngModel)]="editIngredient.description"></textarea>
</div>
<button class="button-form" (click)="updateIngredient()">Enregistrer</button>
<button class="button-form" (click)="cancelEdit()">Annuler</button>
</div>

@ -1,12 +1,54 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Ingredient } from '../../model/ingredient.model';
import { IngredientService } from '../../services/ingredient.service';
import { OnInit } from '@angular/core';
import { NgFor, NgIf } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({ @Component({
selector: 'app-ingredient-list', selector: 'app-ingredient-list',
standalone: true, standalone: true,
imports: [], imports: [NgIf, NgFor, FormsModule],
providers: [],
templateUrl: './ingredient-list.component.html', templateUrl: './ingredient-list.component.html',
styleUrl: './ingredient-list.component.css' styleUrl: './ingredient-list.component.css'
}) })
export class IngredientListComponent { export class IngredientListComponent {
public ingredients! : Ingredient[];
newIngredient: Ingredient = { id: 0, name: '', description: '', qty: 0 };
editIngredient: Ingredient | null = null;
constructor(private serviceIngredient : IngredientService){}
ngOnInit(): void
{
this.serviceIngredient.getIngredient().subscribe(ingredients => {
this.ingredients = ingredients;
});
}
edit(ingre: Ingredient){
this.editIngredient = ingre;
}
addIngredient() {
this.serviceIngredient.add(this.newIngredient).subscribe(ingredient => {
this.ingredients.push(ingredient);
this.newIngredient = { id: 0, name: '', description: '', qty: 0}
});
}
updateIngredient() {
if (this.editIngredient) {
this.serviceIngredient.update(this.editIngredient).subscribe(updatedIngredient => {
const index = this.ingredients.findIndex(i => i.id === updatedIngredient.id);
this.ingredients[index] = updatedIngredient;
this.editIngredient = null;
});
}
}
cancelEdit() {
this.editIngredient = null;
}
} }

@ -28,7 +28,7 @@
<mat-label>{{ 'recipe.ingredients' | transloco }}</mat-label> <mat-label>{{ 'recipe.ingredients' | transloco }}</mat-label>
<mat-select formControlName="name"> <mat-select formControlName="name">
@for (ingredient of ingredientsOptions; track ingredient) { @for (ingredient of ingredientsOptions; track ingredient) {
<mat-option [value]="ingredient">{{ ingredient }}</mat-option> <mat-option [value]="ingredient">{{ ingredient.name }}</mat-option>
} }
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>

@ -1,17 +1,18 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Recipe } from '../../model/recipe.model'; import { Recipe } from '../../model/recipe.model';
import { Ingredient } from '../../model/ingredient.model';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { RecipeService } from '../../services/recipe.service'; import { RecipeService } from '../../services/recipe.service';
import { TranslocoPipe } from '@jsverse/transloco'; import { TranslocoPipe } from '@jsverse/transloco';
import { Ingredient } from '../../model/ingredient.model';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule} from '@angular/material/select'; import { MatSelectModule} from '@angular/material/select';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { IngredientService } from '../../services/ingredient.service';
@Component({ @Component({
selector: 'app-recipe-form', selector: 'app-recipe-form',
@ -32,17 +33,20 @@ import { Router } from '@angular/router';
export class RecipeFormComponent { export class RecipeFormComponent {
recipeForm: FormGroup; recipeForm: FormGroup;
base64Image: string | ArrayBuffer | null = null; base64Image: string | ArrayBuffer | null = null;
ingredientsOptions = ['Champignon', 'Tomata', 'Mozarella']; ingredientsOptions! : Ingredient[];
defaultOption: string = this.ingredientsOptions[2]; defaultOption: string = 'Veuillez choisir';
filename: string = ''; filename: string = '';
constructor(private fb: FormBuilder, private recipeService: RecipeService, private router: Router) { constructor(private fb: FormBuilder, private recipeService: RecipeService, private router: Router,private serviceIngredients: IngredientService) {
this.recipeForm = this.fb.group({ this.recipeForm = this.fb.group({
name: ['', Validators.required], name: ['', Validators.required],
description: ['', Validators.required], description: ['', Validators.required],
image: ['', Validators.required], image: ['', Validators.required],
ingredients: this.fb.array([]), ingredients: this.fb.array([]),
}); });
this.serviceIngredients.getIngredient().subscribe(ingredients => {
this.ingredientsOptions = ingredients;
});
} }
get ingredients(): FormArray { get ingredients(): FormArray {

@ -8,3 +8,30 @@
.pagination { .pagination {
font-size: 1.2rem; font-size: 1.2rem;
} }
.plus-button {
position: fixed;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
background-color: #28a745;
color: white;
border-radius: 50%;
text-align: center;
line-height: 50px;
font-size: 24px;
text-decoration: none;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
transition: background-color 0.3s ease;
z-index: 1000; /* Assure que le bouton est au-dessus des autres éléments */
}
.plus-button:hover {
background-color: #218838;
}
.plus-button:active {
background-color: #1e7e34;
}

@ -2,9 +2,10 @@
<div id="recipe-list"> <div id="recipe-list">
<app-recipe-mini *ngFor="let recipe of paginatedRecipes" [recipe]="recipe"></app-recipe-mini> <app-recipe-mini *ngFor="let recipe of paginatedRecipes" [recipe]="recipe"></app-recipe-mini>
</div> </div>
<a routerLink="/recipe/add" class="plus-button">+</a>
<mat-paginator class="pagination" (page)="handlePageEvent($event)" [length]="length" [pageSize]="pageSize" <mat-paginator class="pagination" (page)="handlePageEvent($event)" [length]="length" [pageSize]="pageSize"
[pageIndex]="pageIndex"> [pageIndex]="pageIndex">
</mat-paginator> </mat-paginator>

@ -5,11 +5,12 @@ import { NgFor } from '@angular/common';
import { RecipeMiniComponent } from '../recipe-mini/recipe-mini.component'; import { RecipeMiniComponent } from '../recipe-mini/recipe-mini.component';
import { TranslocoPipe } from '@jsverse/transloco'; import { TranslocoPipe } from '@jsverse/transloco';
import { MatPaginatorModule, PageEvent} from '@angular/material/paginator'; import { MatPaginatorModule, PageEvent} from '@angular/material/paginator';
import { RouterLink } from '@angular/router';
@Component({ @Component({
selector: 'app-recipe-list', selector: 'app-recipe-list',
standalone: true, standalone: true,
imports: [NgFor, RecipeMiniComponent, TranslocoPipe, MatPaginatorModule], imports: [NgFor, RecipeMiniComponent, TranslocoPipe, MatPaginatorModule, RouterLink],
templateUrl: './recipe-list.component.html', templateUrl: './recipe-list.component.html',
styleUrl: './recipe-list.component.css' styleUrl: './recipe-list.component.css'
}) })

@ -7,5 +7,6 @@
<h2>{{ recipe.name }}</h2> <h2>{{ recipe.name }}</h2>
<p>{{ recipe.description | truncate:50:false }}</p> <p>{{ recipe.description | truncate:50:false }}</p>
<a [routerLink]="['/recipe', recipe.id]">{{ 'recipe.see' | transloco }}</a> <a [routerLink]="['/recipe', recipe.id]">{{ 'recipe.see' | transloco }}</a>
<a (click)='addRecipe(recipe)'>Add Recipe to cart</a>
</div> </div>
</div> </div>

@ -4,6 +4,7 @@ import { Input } from '@angular/core';
import { TruncatePipe } from '../../pipes/truncate.pipe'; import { TruncatePipe } from '../../pipes/truncate.pipe';
import { TranslocoPipe } from '@jsverse/transloco'; import { TranslocoPipe } from '@jsverse/transloco';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { CommandeService } from '../../services/commandes.service';
@Component({ @Component({
selector: 'app-recipe-mini', selector: 'app-recipe-mini',
@ -15,7 +16,10 @@ import { RouterModule } from '@angular/router';
export class RecipeMiniComponent { export class RecipeMiniComponent {
@Input() recipe!: Recipe; @Input() recipe!: Recipe;
navigateToRecipe(){ constructor(protected serviceCommande: CommandeService){}
console.log("TODO");
addRecipe(recipe: Recipe)
{
this.serviceCommande.addRecipe(recipe);
} }
} }

@ -0,0 +1,21 @@
.button {
display: inline-block;
padding: 10px 20px;
font-size: 16px;
color: white;
background-color: #007BFF;
text-align: center;
text-decoration: none;
border-radius: 5px;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #0056b3;
}
.button:active {
background-color: #004085;
}

@ -0,0 +1,4 @@
<div *ngIf="commandes">
<app-recipe-mini *ngFor="let recipe of commandes" [recipe]="recipe"></app-recipe-mini>
<a class="button" (click)="deleteCart()">Clear recipes</a>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SavedRecipeComponent } from './saved-recipe.component';
describe('SavedRecipeComponent', () => {
let component: SavedRecipeComponent;
let fixture: ComponentFixture<SavedRecipeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SavedRecipeComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SavedRecipeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import { CommandeService } from '../../services/commandes.service';
import { OnInit } from '@angular/core';
import { Recipe } from '../../model/recipe.model';
import { NgFor, NgIf } from '@angular/common';
import { RecipeMiniComponent } from '../recipe-mini/recipe-mini.component';
@Component({
selector: 'app-saved-recipe',
standalone: true,
imports: [NgIf, NgFor, RecipeMiniComponent],
templateUrl: './saved-recipe.component.html',
styleUrl: './saved-recipe.component.css'
})
export class SavedRecipeComponent {
public commandes?: Recipe[];
constructor(private serviceCommande : CommandeService){}
ngOnInit(){
this.commandes = this.serviceCommande.getRecipe();
}
deleteCart(){
this.commandes = [];
this.serviceCommande.clearRecipes();
}
}

@ -2,4 +2,5 @@ export interface Ingredient{
id: number; id: number;
name: string; name: string;
qty: number; qty: number;
description: string;
} }

@ -0,0 +1,38 @@
import { Injectable } from "@angular/core";
import { Recipe } from "../model/recipe.model";
@Injectable({
providedIn: 'root'
})
export class CommandeService {
private commande: Recipe[] = [];
private localStorageKey = 'commande';
constructor(){
console.log(this.commande);
}
getRecipeById(id: number) {
return this.commande.find(e => e.id === id);
}
// Get recipes from local storage
getRecipe(): Recipe[] {
const recipesJson = localStorage.getItem(this.localStorageKey) || "[]";
this.commande = JSON.parse(recipesJson) || [];
return this.commande;
}
// Add a new recipe
addRecipe(recipe: Recipe): number {
this.getRecipe();
recipe.id = this.commande.length
let newId = this.commande.push(recipe);
localStorage.setItem(this.localStorageKey, JSON.stringify(this.commande));
return newId;
}
// Clear all recipes (for example, if needed)
clearRecipes(): void {
localStorage.removeItem(this.localStorageKey);
}
}

@ -0,0 +1,25 @@
import { Injectable } from "@angular/core";
import { Ingredient } from "../model/ingredient.model";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class IngredientService {
private path:string = "https://664ba07f35bbda10987d9f99.mockapi.io/api/ingredients";
constructor(private http: HttpClient) { }
getIngredient() : Observable<Ingredient[]>{
return this.http.get<Ingredient[]>(this.path);
}
add(ingredient: Ingredient): Observable<Ingredient> {
return this.http.post<Ingredient>(this.path, ingredient);
}
update(ingredient: Ingredient): Observable<Ingredient> {
return this.http.put<Ingredient>(`${this.path}/${ingredient.id}`, ingredient);
}
}
Loading…
Cancel
Save