Compare commits
27 Commits
add-recipe
...
master
Author | SHA1 | Date |
---|---|---|
|
4fcb9dc607 | 11 months ago |
|
b985dd6fd1 | 11 months ago |
|
007772da75 | 11 months ago |
|
aa1f297106 | 11 months ago |
|
62dfca2838 | 12 months ago |
|
5b8b6f33ee | 12 months ago |
|
aaaa3a3a53 | 12 months ago |
|
2295d02452 | 12 months ago |
|
5cedd5a720 | 12 months ago |
|
88d5451a2c | 12 months ago |
|
b630f74622 | 12 months ago |
|
a9905dcf9a | 12 months ago |
|
e3533445d2 | 12 months ago |
|
c6141226a2 | 12 months ago |
|
657461163b | 12 months ago |
|
c415c3c19f | 12 months ago |
|
8e49b63a99 | 12 months ago |
|
b2c05f1440 | 12 months ago |
|
398a2ceeef | 12 months ago |
|
91c7389a62 | 12 months ago |
|
9d52ef64cc | 12 months ago |
|
77add72cab | 12 months ago |
|
8971a4e0f1 | 12 months ago |
|
b198c891e6 | 12 months ago |
|
a41065a4bc | 12 months ago |
|
2ae3528919 | 12 months ago |
|
2b64147432 | 12 months ago |
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
|
||||||
"recommendations": ["angular.ng-template"]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "ng serve",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "npm: start",
|
|
||||||
"url": "http://localhost:4200/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ng test",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "npm: test",
|
|
||||||
"url": "http://localhost:9876/debug.html"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "start",
|
|
||||||
"isBackground": true,
|
|
||||||
"problemMatcher": {
|
|
||||||
"owner": "typescript",
|
|
||||||
"pattern": "$tsc",
|
|
||||||
"background": {
|
|
||||||
"activeOnStart": true,
|
|
||||||
"beginsPattern": {
|
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "test",
|
|
||||||
"isBackground": true,
|
|
||||||
"problemMatcher": {
|
|
||||||
"owner": "typescript",
|
|
||||||
"pattern": "$tsc",
|
|
||||||
"background": {
|
|
||||||
"activeOnStart": true,
|
|
||||||
"beginsPattern": {
|
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
|||||||
|
nav {
|
||||||
|
border: 3px solid black;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
font-size: 2rem;
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-wrapper {
|
||||||
|
width: 70%;
|
||||||
|
border: 3px solid black;
|
||||||
|
|
||||||
|
margin: 1rem auto;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
@ -1,6 +1,20 @@
|
|||||||
<h1>Ratatouille</h1>
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<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>
|
||||||
|
<ng-template #elseBlock>
|
||||||
|
<li><a routerLink="/login" routerLinkActive="active" ariaCurrentWhenActive="page">{{ 'login' | transloco }}</a></li>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<div class="wrapper">
|
<select (change)="changeLanguage($event)">
|
||||||
<app-recipe-list></app-recipe-list>
|
<option value="en">🇬🇧</option>
|
||||||
<app-recipe-form></app-recipe-form>
|
<option value="fr">🇫🇷</option>
|
||||||
|
<option value="ru">🇷🇺</option>
|
||||||
|
</select>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,27 +1,64 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet, RouterLink } from '@angular/router';
|
||||||
import { RecipeFormComponent } from './recipe-form/recipe-form.component';
|
import { RecipeFormComponent } from './components/recipe-form/recipe-form.component';
|
||||||
import { RecipeService } from './services/recipe.service';
|
import { RecipeService } from './services/recipe.service';
|
||||||
import { Recipe } from './model/recipe.model';
|
import { Recipe } from './model/recipe.model';
|
||||||
import { RecipeListComponent } from './components/recipe-list/recipe-list.component';
|
import { RecipeListComponent } from './components/recipe-list/recipe-list.component';
|
||||||
|
import { TranslocoPipe, TranslocoService } from '@jsverse/transloco';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
RouterOutlet,
|
RouterOutlet,
|
||||||
|
RouterLink,
|
||||||
RecipeFormComponent,
|
RecipeFormComponent,
|
||||||
RecipeListComponent,
|
RecipeListComponent,
|
||||||
|
TranslocoPipe,
|
||||||
|
NgIf,
|
||||||
],
|
],
|
||||||
providers: [RecipeService],
|
providers: [RecipeService, TranslocoService, NgIf],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
|
styleUrl: './app.component.css'
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'bromista-nisqa-receta';
|
title = 'bromista-nisqa-receta';
|
||||||
|
isLogged = false;
|
||||||
|
constructor(protected recipeService: RecipeService, private translocoService: TranslocoService){}
|
||||||
|
|
||||||
constructor(protected recipeService: RecipeService){}
|
ngOnInit(){
|
||||||
|
const rawCookie = decodeURIComponent(document.cookie);
|
||||||
|
const array = rawCookie.split(";");
|
||||||
|
const name = "isAdmin=";
|
||||||
|
let res:String = "";
|
||||||
|
|
||||||
|
for(let cookie of array)
|
||||||
|
{
|
||||||
|
while (cookie.charAt(0) === ' ') {
|
||||||
|
cookie = cookie.substring(1);
|
||||||
|
}
|
||||||
|
if (cookie.indexOf(name) === 0) {
|
||||||
|
res = cookie.substring(name.length, cookie.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res !== "true" ){
|
||||||
|
this.isLogged = false
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
this.isLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
addRecipe($event: Recipe): void {
|
addRecipe($event: Recipe): void {
|
||||||
this.recipeService.addRecipe($event);
|
this.recipeService.addRecipe($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeLanguage(event: Event) {
|
||||||
|
if (event.target && event.target instanceof HTMLSelectElement) {
|
||||||
|
const lang = event.target.value;
|
||||||
|
this.translocoService.setActiveLang(lang);
|
||||||
|
} else {
|
||||||
|
console.log('Error on language option value')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,31 @@
|
|||||||
import { ApplicationConfig } from '@angular/core';
|
import { ApplicationConfig, isDevMode } from '@angular/core';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
||||||
|
|
||||||
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
|
import { TranslocoHttpLoader } from './transloco-loader';
|
||||||
|
import { provideTransloco } from '@jsverse/transloco';
|
||||||
|
import { withFetch } from '@angular/common/http';
|
||||||
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [provideRouter(routes)]
|
providers: [
|
||||||
|
provideRouter(routes, withComponentInputBinding()), provideHttpClient(), provideTransloco({
|
||||||
|
config: {
|
||||||
|
availableLangs: ['en', 'fr', 'ru'],
|
||||||
|
defaultLang: 'en',
|
||||||
|
// Remove this option if your application doesn't support changing language in runtime.
|
||||||
|
reRenderOnLangChange: true,
|
||||||
|
prodMode: !isDevMode(),
|
||||||
|
},
|
||||||
|
loader: TranslocoHttpLoader
|
||||||
|
}
|
||||||
|
),
|
||||||
|
provideAnimations(),
|
||||||
|
provideHttpClient(
|
||||||
|
withFetch(),
|
||||||
|
),
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,23 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router'
|
||||||
|
|
||||||
export const routes: Routes = [];
|
import { Error404Component } from "./components/errors/errors.component";
|
||||||
|
import { RecipeFormComponent } from "./components/recipe-form/recipe-form.component";
|
||||||
|
import { RecipeListComponent } from "./components/recipe-list/recipe-list.component";
|
||||||
|
import { RecipeDetailComponent } from './components/recipe-detail/recipe-detail.component';
|
||||||
|
import { LoginComponent } from './components/login/login.component';
|
||||||
|
import { IngredientListComponent } from './components/ingredient-list/ingredient-list.component';
|
||||||
|
import { LogoutComponent } from './components/logout/logout.component';
|
||||||
|
import { AuthGuard } from './auth.guard';
|
||||||
|
import { SavedRecipeComponent } from './components/saved-recipe/saved-recipe.component';
|
||||||
|
|
||||||
|
export const routes: Routes = [
|
||||||
|
{ path: '', component: RecipeListComponent },
|
||||||
|
{ path: 'error/:status', component: Error404Component},
|
||||||
|
|
||||||
|
{ path: 'recipe/add', component: RecipeFormComponent},
|
||||||
|
{ path: 'cart', component: SavedRecipeComponent},
|
||||||
|
{ path: 'recipe/:id', component: RecipeDetailComponent},
|
||||||
|
{ path: 'ingredients', component: IngredientListComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: 'logout', component: LogoutComponent, canActivate: [AuthGuard] },
|
||||||
|
];
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import { inject } from "@angular/core";
|
||||||
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
|
export const AuthGuard = () => {
|
||||||
|
const router = inject(Router);
|
||||||
|
const rawCookie = decodeURIComponent(document.cookie);
|
||||||
|
const array = rawCookie.split(";");
|
||||||
|
const name = "isAdmin=";
|
||||||
|
let res:String = "";
|
||||||
|
|
||||||
|
for(let cookie of array)
|
||||||
|
{
|
||||||
|
while (cookie.charAt(0) === ' ') {
|
||||||
|
cookie = cookie.substring(1);
|
||||||
|
}
|
||||||
|
if (cookie.indexOf(name) === 0) {
|
||||||
|
res = cookie.substring(name.length, cookie.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(res);
|
||||||
|
|
||||||
|
// Check cookie
|
||||||
|
if(res !== "true") {
|
||||||
|
router.navigateByUrl('/login')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<h1>{{ status }}</h1>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { Error404Component } from './error404.component';
|
||||||
|
|
||||||
|
describe('Error404Component', () => {
|
||||||
|
let component: Error404Component;
|
||||||
|
let fixture: ComponentFixture<Error404Component>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [Error404Component]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(Error404Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,21 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-errors',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './errors.component.html',
|
||||||
|
styleUrl: './errors.component.css'
|
||||||
|
})
|
||||||
|
export class Error404Component {
|
||||||
|
status: string | null = "Unknown error";
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute){}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
this.status = params.get('status');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
<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>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { IngredientListComponent } from './ingredient-list.component';
|
||||||
|
|
||||||
|
describe('IngredientListComponent', () => {
|
||||||
|
let component: IngredientListComponent;
|
||||||
|
let fixture: ComponentFixture<IngredientListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [IngredientListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(IngredientListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,54 @@
|
|||||||
|
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({
|
||||||
|
selector: 'app-ingredient-list',
|
||||||
|
standalone: true,
|
||||||
|
imports: [NgIf, NgFor, FormsModule],
|
||||||
|
providers: [],
|
||||||
|
templateUrl: './ingredient-list.component.html',
|
||||||
|
styleUrl: './ingredient-list.component.css'
|
||||||
|
})
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
.ingredient-mini {
|
||||||
|
min-width: 130px;
|
||||||
|
background-color: #d3d3d3;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.ingredient-mini-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
<div class="ingredient-mini">
|
||||||
|
<span class="ingredient-mini-title">{{ ingredient.name }}</span>
|
||||||
|
<span>{{ ingredient.qty }}g</span>
|
||||||
|
</div>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { IngredientMiniComponent } from './ingredient-mini.component';
|
||||||
|
|
||||||
|
describe('IngredientMiniComponent', () => {
|
||||||
|
let component: IngredientMiniComponent;
|
||||||
|
let fixture: ComponentFixture<IngredientMiniComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [IngredientMiniComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(IngredientMiniComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,14 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Ingredient } from '../../model/ingredient.model'
|
||||||
|
import { Input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-ingredient-mini',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './ingredient-mini.component.html',
|
||||||
|
styleUrl: './ingredient-mini.component.css'
|
||||||
|
})
|
||||||
|
export class IngredientMiniComponent {
|
||||||
|
@Input() ingredient!: Ingredient;
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<p>login works!</p>
|
||||||
|
<button (click)="setCookie()"> Log Me ! </button>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [LoginComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,21 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from "@angular/router";
|
||||||
|
import { Inject } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrl: './login.component.css'
|
||||||
|
})
|
||||||
|
export class LoginComponent {
|
||||||
|
|
||||||
|
constructor(@Inject(Router) private router: Router) {}
|
||||||
|
|
||||||
|
|
||||||
|
setCookie():void{
|
||||||
|
document.cookie = "isAdmin=true";
|
||||||
|
this.router.navigateByUrl('/ingredients');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<p>logout works!</p>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LogoutComponent } from './logout.component';
|
||||||
|
|
||||||
|
describe('LogoutComponent', () => {
|
||||||
|
let component: LogoutComponent;
|
||||||
|
let fixture: ComponentFixture<LogoutComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [LogoutComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(LogoutComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Inject } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-logout',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './logout.component.html',
|
||||||
|
styleUrl: './logout.component.css'
|
||||||
|
})
|
||||||
|
export class LogoutComponent {
|
||||||
|
constructor(@Inject(Router) private router: Router) {}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
document.cookie = "isAdmin=false";
|
||||||
|
this.router.navigateByUrl('/');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
img {
|
||||||
|
max-height: 300px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#recipe-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
<div id="recipe-detail">
|
||||||
|
<img src="{{ recipe.image }}" />
|
||||||
|
<h1>{{ recipe.name }}</h1>
|
||||||
|
<p>{{ recipe.description }}</p>
|
||||||
|
|
||||||
|
<h2>{{ 'recipe.ingredients' | transloco }}</h2>
|
||||||
|
|
||||||
|
<div id="recipe-list">
|
||||||
|
<app-ingredient-mini *ngFor="let ingredient of recipe.ingredients" [ingredient]="ingredient"></app-ingredient-mini>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RecipeDetailComponent } from './recipe-detail.component';
|
||||||
|
|
||||||
|
describe('RecipeDetailComponent', () => {
|
||||||
|
let component: RecipeDetailComponent;
|
||||||
|
let fixture: ComponentFixture<RecipeDetailComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [RecipeDetailComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RecipeDetailComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,39 @@
|
|||||||
|
import { Component} from '@angular/core';
|
||||||
|
import { Recipe } from '../../model/recipe.model';
|
||||||
|
import { RecipeService } from '../../services/recipe.service';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { TranslocoPipe } from '@jsverse/transloco';
|
||||||
|
import { IngredientMiniComponent } from '../ingredient-mini/ingredient-mini.component';
|
||||||
|
import { NgFor } from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-recipe-detail',
|
||||||
|
standalone: true,
|
||||||
|
imports: [NgFor, TranslocoPipe, IngredientMiniComponent],
|
||||||
|
templateUrl: './recipe-detail.component.html',
|
||||||
|
styleUrl: './recipe-detail.component.css'
|
||||||
|
})
|
||||||
|
export class RecipeDetailComponent {
|
||||||
|
|
||||||
|
recipe!: Recipe;
|
||||||
|
id: string | null = null;
|
||||||
|
|
||||||
|
constructor(private router: Router, private route: ActivatedRoute, private recipeService: RecipeService) {
|
||||||
|
this.recipeService.getRecipes();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
this.id = params.get('id');
|
||||||
|
if (this.id) {
|
||||||
|
this.recipe = this.recipeService.getRecipeById(Number(this.id))!;
|
||||||
|
console.log(this.recipe);
|
||||||
|
}
|
||||||
|
if (this.recipe === undefined) {
|
||||||
|
this.router.navigate(['/error/404'])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
.mat-mdc-form-field {
|
||||||
|
width: 40%;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-submit {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
.mat-mdc-form-field {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
<h1>{{ 'recipe.add.link' | transloco }}</h1>
|
||||||
|
<form [formGroup]="recipeForm" (ngSubmit)="onSubmit()">
|
||||||
|
<p>
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input matInput formControlName="name">
|
||||||
|
</mat-form-field>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Description</mat-label>
|
||||||
|
<textarea matInput formControlName="description"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Image</mat-label>
|
||||||
|
<input matInput type="text" placeholder="No file chosen" [value]="filename" readonly>
|
||||||
|
<input type="file" formControlName="image" (change)="onFileChange($event)" hidden #fileInput>
|
||||||
|
<button mat-stroked-button type="button" timeout (click)="fileInput.click()">Browse</button>
|
||||||
|
</mat-form-field>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div formArrayName="ingredients">
|
||||||
|
<h3>{{ 'recipe.ingredients' | transloco }}</h3>
|
||||||
|
<div *ngFor="let ingredient of ingredients.controls; let i = index" [formGroupName]="i" class="form-row">
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>{{ 'recipe.ingredients' | transloco }}</mat-label>
|
||||||
|
<mat-select formControlName="name">
|
||||||
|
@for (ingredient of ingredientsOptions; track ingredient) {
|
||||||
|
<mat-option [value]="ingredient.name">{{ ingredient.name }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Quantity</mat-label>
|
||||||
|
<input matInput formControlName="qty">
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-stroked-button type="button" (click)="removeIngredient(i)">Remove</button>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<button mat-stroked-button type="button" (click)="addIngredient()">{{
|
||||||
|
'recipe.add.button-ingredients'|transloco }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="form-submit" mat-stroked-button type="submit">{{ 'recipe.add.button-recipe' | transloco }}</button>
|
||||||
|
</form>
|
@ -0,0 +1,37 @@
|
|||||||
|
#recipe-list {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,11 @@
|
|||||||
<p>recipe-list works!</p>
|
<h1>{{ 'recipe.list' | transloco }}</h1>
|
||||||
<div>
|
|
||||||
<app-recipe-mini *ngFor="let recipe of recipes" [recipe]="recipe"></app-recipe-mini>
|
<div id="recipe-list">
|
||||||
|
<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"
|
||||||
|
[pageIndex]="pageIndex">
|
||||||
|
</mat-paginator>
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<div class="recipe-card">
|
<div class="recipe-card">
|
||||||
<div class="recipe-image">
|
<div class="recipe-image">
|
||||||
<img [src]="recipe.image" onerror="this.onerror=null;this.src='https://placehold.co/100x100';" alt="{{recipe.name}}">
|
<img [src]="recipe.image" onerror="this.onerror=null;this.src='https://placehold.co/100x100';"
|
||||||
|
alt="{{ recipe.name }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="recipe-details">
|
<div class="recipe-details">
|
||||||
<h2>{{recipe.name}}</h2>
|
<h2>{{ recipe.name }}</h2>
|
||||||
<p>{{recipe.description}}</p>
|
<p>{{ recipe.description | truncate:50:false }}</p>
|
||||||
<button (click)="navigateToRecipe()" style="--clr:#39FF14"><span>Voir la recette</span><i></i></button>
|
<a [routerLink]="['/recipe', recipe.id]">{{ 'recipe.see' | transloco }}</a>
|
||||||
|
<a (click)='addRecipe(recipe)'>Add Recipe to cart</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Recipe } from '../../model/recipe.model';
|
import { Recipe } from '../../model/recipe.model';
|
||||||
import { Input } from '@angular/core';
|
import { Input } from '@angular/core';
|
||||||
|
import { TruncatePipe } from '../../pipes/truncate.pipe';
|
||||||
|
import { TranslocoPipe } from '@jsverse/transloco';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { CommandeService } from '../../services/commandes.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recipe-mini',
|
selector: 'app-recipe-mini',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [TruncatePipe, TranslocoPipe, RouterModule],
|
||||||
templateUrl: './recipe-mini.component.html',
|
templateUrl: './recipe-mini.component.html',
|
||||||
styleUrl: './recipe-mini.component.css'
|
styleUrl: './recipe-mini.component.css'
|
||||||
})
|
})
|
||||||
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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { TruncatePipe } from './truncate.pipe';
|
||||||
|
|
||||||
|
describe('TruncatePipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new TruncatePipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'truncate',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class TruncatePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: string, limit: number = 50, completeWords: boolean = false, ellipsis: string = '...'): string {
|
||||||
|
if (!value) return '';
|
||||||
|
if (value.length <= limit) return value;
|
||||||
|
|
||||||
|
if (completeWords) {
|
||||||
|
limit = value.substring(0, limit).lastIndexOf(' ');
|
||||||
|
}
|
||||||
|
return `${value.substring(0, limit)}${ellipsis}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
<h2>New Recipe</h2>
|
|
||||||
<form [formGroup]="recipeForm" (ngSubmit)="onSubmit()">
|
|
||||||
<label for="name">Name:</label>
|
|
||||||
<input id="name" formControlName="name">
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<label for="description">Description:</label>
|
|
||||||
<textarea id="description" formControlName="description"></textarea>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<label for="image">Image URL:</label>
|
|
||||||
<input id="image" type="file" formControlName="image" (change)="onFileChange($event)">
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div formArrayName="ingredients">
|
|
||||||
<h3>Ingredients</h3>
|
|
||||||
<button type="button" (click)="addIngredient()">Add Ingredient</button>
|
|
||||||
<div *ngFor="let ingredient of ingredients.controls; let i = index" [formGroupName]="i">
|
|
||||||
<label for="ingredientName">Ingredient:</label>
|
|
||||||
<select id="ingredientName" formControlName="name">
|
|
||||||
<option *ngFor="let option of ingredientsOptions" [value]="option" [selected]="option === defaultOption">
|
|
||||||
{{option}}</option>
|
|
||||||
</select>
|
|
||||||
<label for="ingredientQuantity">Quantity:</label>
|
|
||||||
<input id="ingredientQuantity" formControlName="qty">
|
|
||||||
<button type="button" (click)="removeIngredient(i)">Remove</button>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit">Add Recipe</button>
|
|
||||||
</form>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import { inject, Injectable } from "@angular/core";
|
||||||
|
import { Translation, TranslocoLoader } from "@jsverse/transloco";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class TranslocoHttpLoader implements TranslocoLoader {
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
|
||||||
|
getTranslation(lang: string) {
|
||||||
|
return this.http.get<Translation>(`../assets/i18n/${lang}.json`);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"title": "Home",
|
||||||
|
"recipe": {
|
||||||
|
"add": {
|
||||||
|
"link": "Add Recipe",
|
||||||
|
"button-ingredients": "Add Ingredients",
|
||||||
|
"button-recipe": "Add Recipe"
|
||||||
|
},
|
||||||
|
"see": "View Recipe",
|
||||||
|
"list": "Recipe List",
|
||||||
|
"ingredients": "Ingredients"
|
||||||
|
},
|
||||||
|
"logout": "logout",
|
||||||
|
"login": "login"
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"title": "Accueil",
|
||||||
|
"recipe": {
|
||||||
|
"add": {
|
||||||
|
"link": "Ajouter une recette",
|
||||||
|
"button-ingredients": "Ajouter des ingrédients",
|
||||||
|
"button-recipe": "Ajouter la recette"
|
||||||
|
},
|
||||||
|
"see": "Voir la recette",
|
||||||
|
"list": "Liste de recettes",
|
||||||
|
"ingredients": "Ingrédients"
|
||||||
|
},
|
||||||
|
"logout": "déconnexion",
|
||||||
|
"login": "connexion"
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"title": "Домой",
|
||||||
|
"recipe": {
|
||||||
|
"add": {
|
||||||
|
"link": "Добавить рецепт",
|
||||||
|
"button-ingredients": "Добавить ингредиенты",
|
||||||
|
"button-recipe": "Добавить рецепт"
|
||||||
|
},
|
||||||
|
"see": "Посмотреть рецепт",
|
||||||
|
"list": "Список рецептов",
|
||||||
|
"ingredients": "Ингредиенты"
|
||||||
|
},
|
||||||
|
"logout": "logout",
|
||||||
|
"login": "login"
|
||||||
|
}
|
@ -1,13 +1,18 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>BromistaNisqaReceta</title>
|
<title>BromistaNisqaReceta</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
|
||||||
|
</html>
|
@ -1 +1,3 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
:root {
|
||||||
|
font-family: "Helvetica";
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
rootTranslationsPath: 'src/assets/i18n/',
|
||||||
|
langs: ['en', 'fr', 'ru'],
|
||||||
|
keysManager: {}
|
||||||
|
};
|
Loading…
Reference in new issue