diff --git a/client/src/app/core/models/recipe.model.ts b/client/src/app/core/models/recipe.model.ts index 417b89d..73292cf 100644 --- a/client/src/app/core/models/recipe.model.ts +++ b/client/src/app/core/models/recipe.model.ts @@ -1,3 +1,50 @@ +export type RecipeOverview = { + id: string; + productCode: string; + name: string; + otherName: string; + description: string; + lastUpdated: Date; +}; + +export type RecipesDashboard = { + configNumber: number; + LastUpdated: Date; + filename: string; +}; + +export type RecipeOverviewList = { + result: RecipeOverview[]; + hasMore: boolean; + totalCount: number; +}; + +export type RecipeDetail = { + name: string; + otherName: string; + description: string; + otherDescription: string; + lastUpdated: Date; + picture: string; +}; + +export type RecipeDetailMat = { + materialID: number; + name: string; + mixOrder: number; + feedParameter: number; + feedPattern: number; + isUse: boolean; + materialPathId: number; + powderGram: number; + powderTime: number; + stirTime: number; + syrupGram: number; + syrupTime: number; + waterCold: number; + waterYield: number; +}; + export interface Recipe { Timestamp: Date; MachineSetting: MachineSetting; diff --git a/client/src/app/core/services/recipe.service.ts b/client/src/app/core/services/recipe.service.ts index 117a612..521354a 100644 --- a/client/src/app/core/services/recipe.service.ts +++ b/client/src/app/core/services/recipe.service.ts @@ -1,18 +1,31 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, tap } from 'rxjs'; -import { Recipe, Recipe01 } from '../models/recipe.model'; +import { + Recipe, + Recipe01, + RecipeDetail, + RecipeDetailMat, + RecipeOverview, + RecipeOverviewList, + RecipesDashboard, +} from '../models/recipe.model'; import { environment } from 'src/environments/environment'; import { RecipeMetaData } from 'src/app/shared/types/recipe'; -interface RecipeParams { +type RecipeOverviewParams = { filename: string; country: string; materialIds: number[]; offset: number; take: number; search: string; -} +}; + +type RecipeDashboardParams = { + filename: string; + country: string; +}; interface RecipeFiles { [key: string]: string[]; @@ -25,36 +38,80 @@ export class RecipeService { constructor(private _httpClient: HttpClient) {} - getRecipes( - params: RecipeParams = { - take: 10, - offset: 0, - search: '', + getRecipesDashboard( + params: RecipeDashboardParams = { + country: this.getCurrentCountry(), + filename: this.getCurrentFile(), + } + ): Observable { + return this._httpClient.get( + environment.api + '/recipes/dashboard', + { + params: { + country: params.country, + filename: params.filename, + }, + withCredentials: true, + responseType: 'json', + } + ); + } + + getRecipeOverview( + params: RecipeOverviewParams = { country: this.getCurrentCountry(), filename: this.getCurrentFile(), materialIds: [], + offset: 0, + take: 20, + search: '', } - ): Observable<{ - fileName: string; - recipes: Recipe; - hasMore: boolean; - }> { - return this._httpClient.get<{ - fileName: string; - recipes: Recipe; - hasMore: boolean; - }>(environment.api + '/recipes', { - params: { - offset: params.offset, - take: params.take, - search: params.search, - country: params.country, - filename: params.filename, - material_ids: params.materialIds.join(','), - }, - withCredentials: true, - responseType: 'json', - }); + ): Observable { + return this._httpClient.get( + environment.api + '/recipes/overview', + { + params: { + country: params.country, + filename: params.filename, + materialIds: params.materialIds.join(','), + offset: params.offset.toString(), + take: params.take.toString(), + search: params.search, + }, + withCredentials: true, + responseType: 'json', + } + ); + } + + getRecipeDetail(productCode: string): Observable { + return this._httpClient.get( + environment.api + '/recipes/' + productCode, + { + params: { + filename: this.getCurrentFile(), + country: this.getCurrentCountry(), + }, + withCredentials: true, + responseType: 'json', + } + ); + } + + getRecipeDetailMat( + productCode: string + ): Observable<{ result: RecipeDetailMat[] }> { + return this._httpClient.get<{ result: RecipeDetailMat[] }>( + environment.api + '/recipes/' + productCode + '/mat', + { + params: { + filename: this.getCurrentFile(), + country: this.getCurrentCountry(), + }, + withCredentials: true, + responseType: 'json', + } + ); } getCurrentFile(): string { diff --git a/client/src/app/features/recipes/recipe-details/recipe-details.component.html b/client/src/app/features/recipes/recipe-details/recipe-details.component.html index bf8a456..28cca89 100644 --- a/client/src/app/features/recipes/recipe-details/recipe-details.component.html +++ b/client/src/app/features/recipes/recipe-details/recipe-details.component.html @@ -1,16 +1,16 @@
-
+
- {{ recipeDetail.value.name }} + {{ recipeDetailForm.getRawValue().name }}
|
- {{ recipeDetail.value.otherName }} + {{ recipeDetailForm.getRawValue().otherName }}
@@ -18,7 +18,8 @@

Last Modify

{{ - recipeDetail.value.lastModified | date : "dd/MM/yyyy HH:mm:ss" + recipeDetailForm.getRawValue().lastModified + | date : "dd/MM/yyyy HH:mm:ss" }}

@@ -109,11 +110,13 @@
diff --git a/client/src/app/features/recipes/recipe-details/recipe-details.component.ts b/client/src/app/features/recipes/recipe-details/recipe-details.component.ts index 6b76810..e7fa3a0 100644 --- a/client/src/app/features/recipes/recipe-details/recipe-details.component.ts +++ b/client/src/app/features/recipes/recipe-details/recipe-details.component.ts @@ -1,32 +1,25 @@ -import { DatePipe, NgFor, NgIf } from '@angular/common'; +import { CommonModule, DatePipe } from '@angular/common'; import { Component, EventEmitter, OnInit } from '@angular/core'; -import { - FormArray, - FormControl, - FormGroup, - ReactiveFormsModule, -} from '@angular/forms'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; -import { isEqual } from 'lodash'; -import { BehaviorSubject, Subject, finalize, map } from 'rxjs'; +import { Observable, first } from 'rxjs'; import { RecipeService } from 'src/app/core/services/recipe.service'; import { ConfirmModal } from 'src/app/shared/modal/confirm/confirm-modal.component'; import { animate, style, transition, trigger } from '@angular/animations'; -import { MaterialService } from 'src/app/core/services/material.service'; -import { RecipeMetaData, RecipeDetail } from 'src/app/shared/types/recipe'; +import { RecipeListComponent } from './recipe-list/recipe-list.component'; import { - RecipeListComponent, - RecipeListDataFormGroup, -} from './recipe-list/recipe-list.component'; -import { MatRecipe } from 'src/app/core/models/recipe.model'; + RecipeDetail, + RecipeDetailMat, +} from 'src/app/core/models/recipe.model'; +import { Action, ActionRecord } from 'src/app/shared/actionRecord/actionRecord'; +import { isEqual } from 'lodash'; @Component({ selector: 'app-recipe-details', templateUrl: './recipe-details.component.html', standalone: true, imports: [ - NgIf, - NgFor, + CommonModule, RouterLink, ReactiveFormsModule, ConfirmModal, @@ -44,88 +37,65 @@ import { MatRecipe } from 'src/app/core/models/recipe.model'; }) export class RecipeDetailsComponent implements OnInit { title: string = 'Recipe Detail'; - recipeMetaData: RecipeMetaData | null = null; - originalRecipeDetail: BehaviorSubject = - new BehaviorSubject(null); - - matForRecipeList = this.originalRecipeDetail.pipe( - map((x) => x?.recipe.recipes) - ); + recipeDetail$!: Observable; isLoaded: boolean = false; isMatLoaded: boolean = false; + actionRecord: ActionRecord = + new ActionRecord(); + + recipeOriginalDetail!: typeof this.recipeDetailForm.value; + constructor( + private _formBuilder: FormBuilder, private _route: ActivatedRoute, private _router: Router, private _recipeService: RecipeService ) {} - recipeDetail = new FormGroup({ - productCode: new FormControl(''), - name: new FormControl(''), - otherName: new FormControl(''), - description: new FormControl(''), - otherDescription: new FormControl(''), - lastModified: new FormControl(new Date()), - price: new FormControl(0), - isUse: new FormControl(false), - isShow: new FormControl(false), - disable: new FormControl(false), + productCode!: string; + + recipeDetailForm = this._formBuilder.group({ + productCode: '', + name: '', + otherName: '', + description: '', + otherDescription: '', + lastModified: new Date(), + price: 0, + isUse: false, + isShow: false, + disable: false, + recipeListData: this._formBuilder.array([]), }); - materialListIds$: Subject<{ - ids: number[]; - matRecipeList: MatRecipe[]; - }> = new Subject<{ - ids: number[]; - matRecipeList: MatRecipe[]; - }>(); - ngOnInit() { - this._recipeService - .getRecipesById(this._route.snapshot.params['productCode']) - .pipe(finalize(() => {})) - .subscribe(({ recipe, recipeMetaData }) => { - this.title = recipe.name + ' | ' + recipe.productCode; - this.recipeDetail.patchValue({ - productCode: recipe.productCode, - name: recipe.name, - otherName: recipe.otherName, - description: recipe.Description, - otherDescription: recipe.otherDescription, - lastModified: recipe.LastChange, - price: recipe.cashPrice, - isUse: recipe.isUse, - isShow: recipe.isShow, - disable: recipe.disable, - }); - this.originalRecipeDetail.next({ - recipe: { - lastModified: recipe.LastChange, - productCode: recipe.productCode, - name: recipe.name, - otherName: recipe.otherName, - description: recipe.Description, - otherDescription: recipe.otherDescription, - price: recipe.cashPrice, - isUse: recipe.isUse, - isShow: recipe.isShow, - disable: recipe.disable, - }, - recipes: recipe.recipes, - }); + this.productCode = this._route.snapshot.params['productCode']; - const ids = recipe.recipes?.map((recipe) => recipe.materialPathId); - this.materialListIds$.next({ - ids: ids || [], - matRecipeList: recipe.recipes || [], - }); + this.recipeDetail$ = this._recipeService + .getRecipeDetail(this.productCode) + .pipe(first()); + this.recipeDetail$.subscribe((detail) => { + this.recipeDetailForm.patchValue(detail); + this.isLoaded = true; + this.recipeOriginalDetail = { ...this.recipeDetailForm.getRawValue() }; + }); - this.recipeMetaData = recipeMetaData; - this.isLoaded = true; - }); + // snap recipe detail form value + + this.actionRecord.registerOnAddAction((currAction, allAction) => { + if (currAction.type === 'recipeListData') { + switch (currAction.action) { + case 'add': + break; + case 'delete': + break; + } + } + console.log('Action Record', allAction); + }); } showConfirmSaveModal: EventEmitter = new EventEmitter(); @@ -137,13 +107,13 @@ export class RecipeDetailsComponent implements OnInit { confirmCallBack: () => { console.log('confirm save'); // TODO: update value in targeted recipe - this._recipeService.editChanges( - this._recipeService.getCurrentCountry(), - this._recipeService.getCurrentFile(), - { - ...this.recipeDetail, - } - ); + // this._recipeService.editChanges( + // this._recipeService.getCurrentCountry(), + // this._recipeService.getCurrentFile(), + // { + // ...this.recipeDetail, + // } + // ); console.log('Sending changes'); this._router.navigate(['/recipes']); }, @@ -176,8 +146,8 @@ export class RecipeDetailsComponent implements OnInit { get isValueChanged() { return !isEqual( - this.recipeDetail.value, - this.originalRecipeDetail.getValue()?.recipe + this.recipeOriginalDetail, + this.recipeDetailForm.getRawValue() ); } } diff --git a/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.html b/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.html index 8dbbfc4..7ab7fde 100644 --- a/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.html +++ b/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.html @@ -1,7 +1,7 @@ - + @@ -11,55 +11,57 @@ - + - - -
-
- - - - - - - - - - - + + + + + + + + + + + + +
EnableAction Material ID Material Name MixOrderSyrup Gram Syrup Time Water ColdWater HotWater Yield
- - - - - - - - - - - - - - - - - - - - - -
+ + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.ts b/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.ts index 05685c3..47e0dfa 100644 --- a/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.ts +++ b/client/src/app/features/recipes/recipe-details/recipe-list/recipe-list.component.ts @@ -2,13 +2,18 @@ import { NgFor, NgIf } from '@angular/common'; import { Component, Input, OnInit } from '@angular/core'; import { FormArray, + FormBuilder, FormControl, FormGroup, ReactiveFormsModule, } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { MatRecipe } from 'src/app/core/models/recipe.model'; -import { MaterialService } from 'src/app/core/services/material.service'; +import { first } from 'rxjs'; +import { + RecipeDetail, + RecipeDetailMat, +} from 'src/app/core/models/recipe.model'; +import { RecipeService } from 'src/app/core/services/recipe.service'; +import { Action, ActionRecord } from 'src/app/shared/actionRecord/actionRecord'; export interface RecipeListDataFormGroup { id: FormControl; @@ -31,97 +36,86 @@ export interface RecipeListDataFormGroup { imports: [NgIf, NgFor, ReactiveFormsModule], }) export class RecipeListComponent implements OnInit { - @Input({ required: true }) matRecipeList!: Observable<{ - ids: number[]; - matRecipeList: MatRecipe[]; - }>; - @Input({ required: true }) parentForm!: FormGroup; + @Input({ required: true }) actionRecord!: ActionRecord< + RecipeDetail | RecipeDetailMat + >; - recipeListData!: FormArray>; + @Input({ required: true }) recipeDetailOriginal!: any; + + @Input({ required: true }) productCode!: string; isMatLoaded: boolean = false; - constructor(private _materialService: MaterialService) {} + constructor( + private _recipeService: RecipeService, + private _formBuilder: FormBuilder + ) {} ngOnInit(): void { - this.matRecipeList.subscribe((x) => { - this._materialService.getMaterialCodes(x.ids).subscribe((data) => { - const matList = x.matRecipeList - .map((item) => { - for (let i = 0; i < data.length; i++) { - if (item.materialPathId === 0) { - return { - id: 0, - name: '', - enable: item.isUse, - mixOrder: item.MixOrder, - stirTime: item.stirTime, - powderGram: item.powderGram, - powderTime: item.powderTime, - syrupGram: item.syrupGram, - syrupTime: item.syrupTime, - waterCold: item.waterCold, - waterHot: item.waterYield, - }; - } - - if (item.materialPathId === data[i].materialID) { - return { - id: data[i].materialID, - name: data[i].PackageDescription, - enable: item.isUse, - mixOrder: item.MixOrder, - stirTime: item.stirTime, - powderGram: item.powderGram, - powderTime: item.powderTime, - syrupGram: item.syrupGram, - syrupTime: item.syrupTime, - waterCold: item.waterCold, - waterHot: item.waterYield, - }; - } - } - - return { - id: item.materialPathId, - name: '', - enable: item.isUse, - mixOrder: item.MixOrder, - stirTime: item.stirTime, - powderGram: item.powderGram, - powderTime: item.powderTime, - syrupGram: item.syrupGram, - syrupTime: item.syrupTime, - waterCold: item.waterCold, - waterHot: item.waterYield, - }; - }) - .sort((a, b) => { - return a.id === 0 ? 1 : a.id > b.id ? 1 : -1; - }); - - this.recipeListData = new FormArray>( - matList.map((item) => { - return new FormGroup({ - id: new FormControl(item.id), - name: new FormControl(item.name), - enable: new FormControl(item.enable), - mixOrder: new FormControl(item.mixOrder), - stirTime: new FormControl(item.stirTime), - powderGram: new FormControl(item.powderGram), - powderTime: new FormControl(item.powderTime), - syrupGram: new FormControl(item.syrupGram), - syrupTime: new FormControl(item.syrupTime), - waterCold: new FormControl(item.waterCold), - waterHot: new FormControl(item.waterHot), - }); - }) - ); - this.parentForm.addControl('recipes', this.recipeListData); - console.log(this.parentForm); + this._recipeService + .getRecipeDetailMat(this.productCode) + .pipe(first()) + .subscribe(({ result }) => { + if (this.recipeDetailOriginal) + this.recipeDetailOriginal.recipeListData = result; + else this.recipeDetailOriginal = { recipeListData: result }; + result.forEach((recipeDetailMat: RecipeDetailMat) => { + this.recipeListData.push( + this._formBuilder.group({ + materialID: recipeDetailMat.materialID, + name: recipeDetailMat.name, + enable: recipeDetailMat.isUse, + mixOrder: recipeDetailMat.mixOrder, + stirTime: recipeDetailMat.stirTime, + powderGram: recipeDetailMat.powderGram, + powderTime: recipeDetailMat.powderTime, + syrupGram: recipeDetailMat.syrupGram, + syrupTime: recipeDetailMat.syrupTime, + waterCold: recipeDetailMat.waterCold, + waterYield: recipeDetailMat.waterYield, + }) + ); + }); this.isMatLoaded = true; }); - }); + } + + get recipeListData(): FormArray { + return this.parentForm.get('recipeListData') as FormArray; + } + + addRecipeData(): void { + const newRecipeDetailMat: RecipeDetailMat = { + materialID: 0, + name: '', + mixOrder: 0, + feedParameter: 0, + feedPattern: 0, + isUse: false, + materialPathId: 0, + powderGram: 0, + powderTime: 0, + stirTime: 0, + syrupGram: 0, + syrupTime: 0, + waterCold: 0, + waterYield: 0, + }; + this.recipeListData.push(this._formBuilder.group(newRecipeDetailMat)); + this.actionRecord.addAction( + new Action('add', newRecipeDetailMat, 'recipeListData') + ); + } + + deleteRecipeData(index: number): void { + const recipeDetailMat: RecipeDetailMat = + this.recipeListData.at(index).value; + + this.recipeListData.removeAt(index); + + this.actionRecord.addAction( + new Action('delete', recipeDetailMat, 'recipeListData') + ); } } diff --git a/client/src/app/features/recipes/recipes.component.html b/client/src/app/features/recipes/recipes.component.html index da822f0..2b47002 100644 --- a/client/src/app/features/recipes/recipes.component.html +++ b/client/src/app/features/recipes/recipes.component.html @@ -2,14 +2,17 @@ class="relative overflow-auto max-h-[900px] shadow-md sm:rounded-lg" #table > - +
- +
-
+
Recipe Version {{ recipes?.MachineSetting?.configNumber }} | - {{ currentFile }}Recipe Version {{ recipesDashboard.configNumber }} | + {{ recipesDashboard.filename }}
@@ -106,7 +109,9 @@
Last Updated: - {{ recipes?.Timestamp | date : "dd-MMM-yyyy hh:mm:ss" }}
@@ -199,7 +204,7 @@
@@ -219,9 +224,9 @@ {{ recipe.name }} {{ recipe.otherName }}{{ recipe.Description }}{{ recipe.description }} - {{ recipe.LastChange | date : "dd-MMM-yyyy hh:mm:ss" }} + {{ recipe.lastUpdated | date : "dd-MMM-yyyy hh:mm:ss" }} @@ -249,7 +254,7 @@
-
+
@@ -272,7 +277,7 @@
-
+