diff --git a/client/src/app/core/models/recipe.model.ts b/client/src/app/core/models/recipe.model.ts index 417b89d..ddc7858 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 = { + isUse: boolean; + materialID: number; + name: string; + mixOrder: number; + feedParameter: number; + feedPattern: number; + 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..6c366de 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" }}

@@ -86,34 +87,14 @@ formControlName="otherDescription" />
-
-
- -
-
- -
-
- -
-
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..08141e5 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,66 @@ 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, }); - 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; - }); + this.recipeDetailForm.valueChanges.subscribe(this.onRecipeDetailFormChange); + + // 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 +108,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']); }, @@ -174,10 +145,14 @@ export class RecipeDetailsComponent implements OnInit { } } - get isValueChanged() { - return !isEqual( - this.recipeDetail.value, - this.originalRecipeDetail.getValue()?.recipe - ); + isValueChanged: boolean = false; + + onRecipeDetailFormChange(recipeDetail: typeof this.recipeDetailForm.value) { + console.log('Recipe Detail Form Changed', recipeDetail); + } + + onRecipeListFormChange(isValueChanged: boolean) { + console.log('Recipe List Form Changed', isValueChanged); + this.isValueChanged ||= isValueChanged; } } 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..9b37428 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,47 @@ - + - - -
-
- - - - - - - - - - - + + + + + + + + + + + + +
EnableIs Use 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..8295062 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 @@ -1,28 +1,20 @@ import { NgFor, NgIf } from '@angular/common'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } 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'; - -export interface RecipeListDataFormGroup { - id: FormControl; - name: FormControl; - enable: FormControl; - mixOrder: FormControl; - stirTime: FormControl; - powderGram: FormControl; - powderTime: FormControl; - syrupGram: FormControl; - syrupTime: FormControl; - waterCold: FormControl; - waterHot: FormControl; -} +import { isEqual, sortBy } from 'lodash'; +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'; @Component({ selector: 'app-recipe-list', @@ -31,97 +23,68 @@ 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; - - recipeListData!: FormArray>; + @Input({ required: true }) productCode!: string; + @Output() recipeListFormChange = new EventEmitter(); isMatLoaded: boolean = false; - constructor(private _materialService: MaterialService) {} + constructor( + private _recipeService: RecipeService, + private _formBuilder: FormBuilder + ) {} + + recipeListForm = this._formBuilder.group( + { + recipeListData: this._formBuilder.array([]), + }, + { updateOn: 'blur' } + ); + + private _recipeListOriginalArray!: RecipeDetailMat[]; 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 }) => { + this._recipeListOriginalArray = result; + result.forEach((recipeDetailMat: RecipeDetailMat) => { + this.recipeListData.push( + this._formBuilder.group({ + isUse: recipeDetailMat.isUse, + materialID: recipeDetailMat.materialID, + name: [{ value: recipeDetailMat.name, disabled: true }], + 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; }); + + this.recipeListForm.valueChanges.subscribe((value) => { + console.log(value.recipeListData); + console.log(this._recipeListOriginalArray); + if ( + !isEqual( + sortBy(value, 'materialID'), + sortBy(this._recipeListOriginalArray, 'materialID') + ) + ) { + this.recipeListFormChange.emit(true); + } else { + this.recipeListFormChange.emit(false); + } }); } + + get recipeListData(): FormArray { + return this.recipeListForm.get('recipeListData') as FormArray; + } } diff --git a/client/src/app/features/recipes/recipes.component.html b/client/src/app/features/recipes/recipes.component.html index da822f0..0fba628 100644 --- a/client/src/app/features/recipes/recipes.component.html +++ b/client/src/app/features/recipes/recipes.component.html @@ -1,15 +1,18 @@
- +
- +
-
+
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 @@
-
+