fix(diff_commit_merge): delayed commit data load condition

This commit is contained in:
pakintada@gmail.com 2024-03-01 14:33:48 +07:00
parent da353cec84
commit ce7d595427
8 changed files with 278 additions and 131 deletions

View file

@ -41,7 +41,7 @@
</div> </div>
<!-- File Change Status --> <!-- File Change Status -->
<button onclick="patch_merge_modal.showModal()" *ngIf="showDetectChanges"> <button onclick="patch_merge_modal.showModal()" *ngIf="isCommitLoaded">
<h1 class="text-center font-extrabold text-2xl text-red-500 animate-pulse">Detect Changes! Click</h1> <h1 class="text-center font-extrabold text-2xl text-red-500 animate-pulse">Detect Changes! Click</h1>

View file

@ -60,7 +60,8 @@ export class LayoutComponent implements OnInit, OnDestroy {
redisStatus:string = "Offline"; redisStatus:string = "Offline";
showDetectChanges: boolean = false; showDetectChanges: boolean = false;
changesCommit: any = undefined; changesCommit!: any;
isCommitLoaded!: Promise<boolean>;
constructor( constructor(
private _userService: UserService, private _userService: UserService,
@ -93,6 +94,12 @@ export class LayoutComponent implements OnInit, OnDestroy {
this._recipeService.getCurrentFile() this._recipeService.getCurrentFile()
).subscribe({ ).subscribe({
next: async (data: any) => { next: async (data: any) => {
console.log("get saved tmp", data);
if(data != null && data != undefined){
this.isCommitLoaded = Promise.resolve(true);
}
if(data != undefined && typeof data === 'object'){ if(data != undefined && typeof data === 'object'){
// check if attr exists // check if attr exists
if(data.files != null && data.files != undefined){ if(data.files != null && data.files != undefined){

View file

@ -297,10 +297,10 @@ export class RecipeService {
); );
} }
async getPatchListOfCurrentFile( getPatchListOfCurrentFile(
country: string, country: string,
filename: string filename: string
): Promise<Observable<any>> { ): Observable<any> {
console.log("try get patches", country, filename); console.log("try get patches", country, filename);

View file

@ -8,7 +8,11 @@
(change)="selectCommit($event)" (change)="selectCommit($event)"
> >
<option>---</option> <option>---</option>
<option *ngFor="let commit of getPatchMapKeys()" [value]="commit" id="commit_{{commit}}"> <option
*ngFor="let commit of getPatchMapKeys()"
[value]="commit"
id="commit_{{ commit }}"
>
[{{ getCommitAttr(commit, "Created_at") }}] [{{ [{{ getCommitAttr(commit, "Created_at") }}] [{{
getCommitAttr(commit, "Editor") getCommitAttr(commit, "Editor")
}}] | {{ getCommitAttr(commit, "Msg") }} }}] | {{ getCommitAttr(commit, "Msg") }}
@ -23,12 +27,17 @@
<div class="flex flex-grow gap-4 p-4"> <div class="flex flex-grow gap-4 p-4">
<!-- from patch --> <!-- from patch -->
<div <div
class="w-100 overflow-x-visible overflow-y-visible" *ngIf="
*ngIf="hasProductCodeOfCommits() && selectedCommit != '' && selectedCommit != '---'" hasProductCodeOfCommits() &&
selectedCommit != '' &&
selectedCommit != '---'
"
> >
<details class="collapse collapse-arrow"> <details class="collapse collapse-arrow">
<summary class="collapse-title">{{ selectedCommit }}</summary> <summary class="collapse-title bg-red-200">{{ selectedCommit }}</summary>
<div class="collapse-content"> <div
class="collapse-content"
>
<app-recipe-list <app-recipe-list
[productCode]=" [productCode]="
getCommitAttr(selectedCommit, 'contents').productCode getCommitAttr(selectedCommit, 'contents').productCode

View file

@ -30,6 +30,7 @@ import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { RecipeListComponent } from '../recipes/recipe-details/recipe-list/recipe-list.component'; import { RecipeListComponent } from '../recipes/recipe-details/recipe-list/recipe-list.component';
import { ResizeEvent, ResizableModule } from 'angular-resizable-element';
@Component({ @Component({
selector: 'app-merge', selector: 'app-merge',
@ -44,13 +45,15 @@ import { RecipeListComponent } from '../recipes/recipe-details/recipe-list/recip
PortalModule, PortalModule,
MatFormFieldModule, MatFormFieldModule,
MatSelectModule, MatSelectModule,
FormsModule FormsModule,
ResizableModule
], ],
}) })
export class MergeComponent implements OnInit, AfterViewInit, OnDestroy { export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
onRecipeListFormChange($event: any) {} onRecipeListFormChange($event: any) {}
@Input() commit: Array<any> | undefined = undefined; // input from layout::getSavedTmp
@Input() commit: Array<any> | null | undefined = undefined;
patchMap: any = {}; patchMap: any = {};
fullPatches: any = {}; fullPatches: any = {};
@ -70,13 +73,11 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
selectedProductCode = ''; selectedProductCode = '';
changePackage: changePackage:
| { {
minimizeUnchanged: boolean;
highlightChangeWhenMatched: {}; highlightChangeWhenMatched: {};
targetProductCode: string; targetProductCode: string;
targetRecipe: any;
path: string; path: string;
changes: any; recipeListIndex: number[];
} }
| undefined = undefined; | undefined = undefined;
@ -102,12 +103,11 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
{} {}
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
( this._recipeService.getPatchListOfCurrentFile(
await this._recipeService.getPatchListOfCurrentFile(
await this._recipeService.getCurrentCountry(), await this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile() this._recipeService.getCurrentFile()
) )
).subscribe({ .subscribe({
next: (data: any) => { next: (data: any) => {
this.patchMap = data; this.patchMap = data;
// console.log("patches",this.patchMap); // console.log("patches",this.patchMap);
@ -156,10 +156,10 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
// this.host.attach(this.portal); // this.host.attach(this.portal);
// console.log("Portal attached!"); // console.log("Portal attached!");
this.templatePortal = new TemplatePortal( // this.templatePortal = new TemplatePortal(
this.templateRecipeChangeContent, // this.templateRecipeChangeContent,
this._containerRef // this._containerRef
); // );
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -183,7 +183,18 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
Object.keys(this.mapProductCodeWithCommits).length > 0; Object.keys(this.mapProductCodeWithCommits).length > 0;
// get commit message by commit id // get commit message by commit id
getCommitAttr = (id: string, attr: string) => this.fullPatches[id][attr]; getCommitAttr = (id: string, attr: string) => {
// console.log("test full patches ", this.fullPatches);
// console.log("test full patches ", this.fullPatches[id]);
// console.log("test full patches ", this.fullPatches[id][attr]);
if (this.fullPatches[id] == undefined) {
return "";
}
return this.fullPatches[id][attr];
};
// copy to clipboard // copy to clipboard
copyRef = async (ref: string) => await copy(ref); copyRef = async (ref: string) => await copy(ref);
@ -222,9 +233,28 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
} }
buildContext = () => { buildContext = () => {
if(this.selectedCommit == "" || this.selectedCommit == undefined || this.selectedCommit == '---'){
return {
changeContext: undefined,
skipZeroes: true,
toppingData: undefined,
}
}
// console.log("read commit contents",this.getCommitAttr(this.selectedCommit, 'contents').ToppingSet);
let patchData = this.fullPatches[this.selectedCommit];
console.log("patch data", patchData);
// this.changePackage = {
// };
return { return {
changeContext: undefined, changeContext: undefined,
skipZeroes: true, skipZeroes: true,
toppingData: this.getCommitAttr(this.selectedCommit, 'contents').ToppingSet,
}; };
}; };
@ -274,67 +304,67 @@ export class MergeComponent implements OnInit, AfterViewInit, OnDestroy {
return this.targetRecipe[productCode].recipes; return this.targetRecipe[productCode].recipes;
} }
getRecipeFromSingleLayerPath = ( // getRecipeFromSingleLayerPath = (
productCode: string, // productCode: string,
path: string, // path: string,
ref_id: string, // ref_id: string,
popLast?: boolean // popLast?: boolean
) => { // ) => {
console.log( // console.log(
'mapProductCodeWithCommits map = ', // 'mapProductCodeWithCommits map = ',
this.mapProductCodeWithCommits // this.mapProductCodeWithCommits
); // );
// split path // // split path
let pathList = path.split('.'); // let pathList = path.split('.');
if (popLast) { // if (popLast) {
// pop last pathlist // // pop last pathlist
pathList.pop(); // pathList.pop();
} // }
// get product code data // // get product code data
let productCodeData = this.targetRecipe[productCode]; // let productCodeData = this.targetRecipe[productCode];
let data = undefined; // let data = undefined;
for (let pathKey of pathList) { // for (let pathKey of pathList) {
if (data == undefined) { // if (data == undefined) {
if (!isNaN(parseInt(pathKey))) { // if (!isNaN(parseInt(pathKey))) {
data = productCodeData[parseInt(pathKey)]; // data = productCodeData[parseInt(pathKey)];
} else { // } else {
data = productCodeData[pathKey]; // data = productCodeData[pathKey];
} // }
} else { // } else {
if (!isNaN(parseInt(pathKey))) { // if (!isNaN(parseInt(pathKey))) {
data = data[parseInt(pathKey)]; // data = data[parseInt(pathKey)];
} else { // } else {
data = data[pathKey]; // data = data[pathKey];
} // }
} // }
} // }
// console.log('data from path', path,"=", data); // // console.log('data from path', path,"=", data);
// setter // // setter
this.selectedProductCode = productCode; // this.selectedProductCode = productCode;
this.currentTargetOfMaster = productCodeData.recipes; // this.currentTargetOfMaster = productCodeData.recipes;
// configure for changes display // // configure for changes display
// this.highlightChanges = data; // // this.highlightChanges = data;
// this.minimizeUnchanged = true; // // this.minimizeUnchanged = true;
// packing into one map; do update if selected // // packing into one map; do update if selected
let changeConfigure = { // let changeConfigure = {
minimizeUnchanged: true, // minimizeUnchanged: true,
highlightChangeWhenMatched: data, // highlightChangeWhenMatched: data,
targetProductCode: productCode, // targetProductCode: productCode,
targetRecipe: productCodeData, // targetRecipe: productCodeData,
path: path, // path: path,
changes: this.fullPatches[ref_id].contents, // changes: this.fullPatches[ref_id].contents,
}; // };
this.changePackage = changeConfigure; // this.changePackage = changeConfigure;
}; // };
// revert search by using commit ref id to find productCode // revert search by using commit ref id to find productCode
getProductCodeByCommitRef = (commitRef: string) => { // getProductCodeByCommitRef = (commitRef: string) => {
return this.patchMap[commitRef].productCode; // return this.patchMap[commitRef].productCode;
}; // };
} }

View file

@ -1,4 +1,7 @@
<div class="justify-center w-full flex flex-row sticky top-0 z-10" *ngIf="displayOnly == false"> <div
class="justify-center w-full flex flex-row sticky top-0 z-10"
*ngIf="displayOnly == false"
>
<button class="btn w-1/2" (click)="addRow()">Add</button> <button class="btn w-1/2" (click)="addRow()">Add</button>
<button class="btn w-1/2" (click)="removeRow()">Remove</button> <button class="btn w-1/2" (click)="removeRow()">Remove</button>
</div> </div>
@ -202,6 +205,11 @@
[index]=" [index]="
getToppingSlotNumber(getTypeForRecipeListAtIndex(i)['id']) getToppingSlotNumber(getTypeForRecipeListAtIndex(i)['id'])
" "
[diffContext]="
buildToppingContext(
getToppingSlotNumber(getTypeForRecipeListAtIndex(i)['id'])
)
"
(toppingSetChange)="onToppingSetChange($event, i)" (toppingSetChange)="onToppingSetChange($event, i)"
></app-recipe-topping> ></app-recipe-topping>
</div> </div>

View file

@ -50,6 +50,7 @@ export class RecipeListComponent implements OnInit, OnChanges {
@Input() diffChangeContext: { @Input() diffChangeContext: {
changeContext: any; changeContext: any;
skipZeroes: boolean; skipZeroes: boolean;
toppingData: any;
} | undefined = undefined; } | undefined = undefined;
@Output() recipeListFormChange = new EventEmitter<unknown[]>(); @Output() recipeListFormChange = new EventEmitter<unknown[]>();
@ -299,6 +300,7 @@ export class RecipeListComponent implements OnInit, OnChanges {
this.stringParams[index] = stringParamList; this.stringParams[index] = stringParamList;
} }
// console.log("string param debug", this.stringParamData);
// --------------- mapping missing data --------------- // --------------- mapping missing data ---------------
@ -491,6 +493,8 @@ export class RecipeListComponent implements OnInit, OnChanges {
let currStringParam = new StringParam(recipeDetailMat.StringParam); let currStringParam = new StringParam(recipeDetailMat.StringParam);
let stringParamList = currStringParam.extract().as_list(); let stringParamList = currStringParam.extract().as_list();
console.log('string param list',stringParamList);
let stringParamListTransform: any[] = []; let stringParamListTransform: any[] = [];
for (let param of stringParamList) { for (let param of stringParamList) {
// boolean transform // boolean transform
@ -508,6 +512,8 @@ export class RecipeListComponent implements OnInit, OnChanges {
this.stringParams[index] = stringParamList; this.stringParams[index] = stringParamList;
} }
console.log("string param debug change", this.stringParamData);
// --------------- mapping missing data --------------- // --------------- mapping missing data ---------------
// map name // map name
@ -593,38 +599,42 @@ export class RecipeListComponent implements OnInit, OnChanges {
] ]
}) })
); );
// map material
// fullMaterialList
this.setNameToRecipeList();
}); });
// resub listener // resub listener
this.recipeListForm.valueChanges.subscribe((value) => { // this.recipeListForm.valueChanges.subscribe((value) => {
// console.log(value.recipeListData); // // console.log(value.recipeListData);
// console.log("original recipe detail",this._recipeListOriginalArray); // // console.log("original recipe detail",this._recipeListOriginalArray);
if ( // if (
!isEqual( // !isEqual(
sortBy(value, 'materialID'), // sortBy(value, 'materialID'),
sortBy(this._recipeListOriginalArray, 'materialID') // sortBy(this._recipeListOriginalArray, 'materialID')
) // )
) { // ) {
let emitted_res: any[] = []; // let emitted_res: any[] = [];
// force type change. temporary solution // // force type change. temporary solution
forEach(value.recipeListData!, (recipeDetailMat: any) => { // forEach(value.recipeListData!, (recipeDetailMat: any) => {
recipeDetailMat.materialPathId = parseInt( // recipeDetailMat.materialPathId = parseInt(
recipeDetailMat.materialPathId! // recipeDetailMat.materialPathId!
); // );
// revert stirTime // // revert stirTime
recipeDetailMat.stirTime = recipeDetailMat.stirTime!; // recipeDetailMat.stirTime = recipeDetailMat.stirTime!;
emitted_res.push(recipeDetailMat); // emitted_res.push(recipeDetailMat);
}); // });
this.recipeListFormChange.emit([this.toppingList, emitted_res] as unknown[]); // this.recipeListFormChange.emit([this.toppingList, emitted_res] as unknown[]);
} else { // } else {
this.recipeListFormChange.emit([]); // this.recipeListFormChange.emit([]);
} // }
}); // });
this.stringParamForm.valueChanges.subscribe((value) => { this.stringParamForm.valueChanges.subscribe((value) => {
// value.stringParamData: Array // value.stringParamData: Array
@ -672,6 +682,7 @@ export class RecipeListComponent implements OnInit, OnChanges {
} }
}); });
} }
} }
} }
@ -1044,4 +1055,29 @@ export class RecipeListComponent implements OnInit, OnChanges {
return true; return true;
} }
// reinit
buildToppingContext(index: number){
if(this.diffChangeContext != undefined && this.diffChangeContext.toppingData != undefined){
// console.log('building context of topping',index, this.diffChangeContext.toppingData[index])
return {
enableEditInDiffMode: false,
preFetchedData: [this.diffChangeContext.toppingData[index]]
};
}
return undefined;
}
// name mapping
setNameToRecipeList(){
this.recipeListData.value.forEach((recipe: any, index: number) => {
// get name from full material list
let mat_name = this.fullMaterialList!.find(
(mat) => mat.materialId.toString() == recipe.materialPathId.toString()
)!.name;
this.recipeListData.at(index).get('name')?.setValue(mat_name);
});
}
} }

View file

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgSelectModule } from '@ng-select/ng-select'; import { NgSelectModule } from '@ng-select/ng-select';
import { import {
@ -22,9 +22,13 @@ import { ActivatedRoute } from '@angular/router';
imports: [CommonModule, NgSelectModule, FormsModule, ReactiveFormsModule], imports: [CommonModule, NgSelectModule, FormsModule, ReactiveFormsModule],
templateUrl: './recipe-topping.component.html', templateUrl: './recipe-topping.component.html',
}) })
export class RecipeToppingComponent implements OnInit { export class RecipeToppingComponent implements OnInit, OnChanges {
@Input() productCode: string = ''; @Input() productCode: string = '';
@Input() index: number | undefined = undefined; @Input() index: number | undefined = undefined;
@Input() diffContext: {
enableEditInDiffMode: boolean;
preFetchedData: any;
} | undefined = undefined;
@Output() toppingSetChange = new EventEmitter<unknown[]>(); @Output() toppingSetChange = new EventEmitter<unknown[]>();
allToppings: Topping | undefined = undefined; allToppings: Topping | undefined = undefined;
@ -70,39 +74,62 @@ export class RecipeToppingComponent implements OnInit {
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
// get topping of this recipe // get topping of this recipe
// initialize toppinglist form // is read mode?
if(this.diffContext != undefined){
let toppingData = this.diffContext.preFetchedData! as Array<any>;
let readonly = this.diffContext.enableEditInDiffMode;
// suppose the topping data is array
( // reset form
await this._toppingService.getToppingsOfRecipe( this.toppingList.clear();
await this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile(), // initialize toppinglist form
this.productCode toppingData.forEach((topping, index) => {
) this.listGroupId.push(topping.ListGroupID);
).subscribe((data) => {
this._toppingSetOriginalArray = data; this.toppingList.push(
console.log('ToppingSet', data, this.index, data.length >= this.index!, data[0]); this._formBuilder.group({
isUse: {value: topping.isUse, disabled: readonly},
groupID: {value: topping.groupID, disabled: readonly},
defaultIDSelect: {value: topping.defaultIDSelect, disabled: readonly},
ListGroupID: {value: topping.ListGroupID, disabled: readonly},
})
);
});
} else {
// initialize toppinglist form
(
await this._toppingService.getToppingsOfRecipe(
await this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile(),
this.productCode
)
).subscribe((data) => {
this._toppingSetOriginalArray = data;
console.log('ToppingSet', data, this.index, data.length >= this.index!, data[0]);
if(data[this.index!] != undefined && data[this.index!] != null){ if(data[this.index!] != undefined && data[this.index!] != null){
this.listGroupId.push(data[this.index!].ListGroupID); this.listGroupId.push(data[this.index!].ListGroupID);
// check length of toppingList if in range with given index // check length of toppingList if in range with given index
if (data.length >= this.index!) { if (data.length >= this.index!) {
this.toppingList.push( this.toppingList.push(
this._formBuilder.group({ this._formBuilder.group({
isUse: data[this.index!].isUse, isUse: data[this.index!].isUse,
groupID: data[this.index!].groupID, groupID: data[this.index!].groupID,
defaultIDSelect: data[this.index!].defaultIDSelect, defaultIDSelect: data[this.index!].defaultIDSelect,
ListGroupID: data[this.index!].ListGroupID, ListGroupID: data[this.index!].ListGroupID,
}) })
); );
}
console.log('SubscribeToppingSet', this.toppingList, "list group id=", this.listGroupId);
} }
console.log('SubscribeToppingSet', this.toppingList, "list group id=", this.listGroupId); });
} }
});
// get all topping // get all topping
( (
@ -136,6 +163,32 @@ export class RecipeToppingComponent implements OnInit {
this.toppingForm.valueChanges.subscribe((value) => {this.triggerValueChange();}); this.toppingForm.valueChanges.subscribe((value) => {this.triggerValueChange();});
} }
// apply on read mode
ngOnChanges(changes: SimpleChanges): void {
console.log('changes on topping', changes);
if(changes['preFetchedData']){
let toppingSet = changes['preFetchedData'].currentValue as Array<any>;
this.toppingList.clear();
// initialize toppinglist form
toppingSet.forEach((topping, index) => {
this.listGroupId.push(topping.ListGroupID);
this.toppingList.push(
this._formBuilder.group({
isUse: {value: topping.isUse, disabled: true},
groupID: {value: topping.groupID, disabled: true},
defaultIDSelect: {value: topping.defaultIDSelect, disabled: true},
ListGroupID: {value: topping.ListGroupID, disabled: true},
})
);
});
}
}
compareFunc = (a: any, b: any) => a.toString() === b.toString(); compareFunc = (a: any, b: any) => a.toString() === b.toString();
mapToppingListToMember = (mm: string[]) => mapToppingListToMember = (mm: string[]) =>
@ -307,4 +360,8 @@ export class RecipeToppingComponent implements OnInit {
ListGroupID: value.ListGroupID, ListGroupID: value.ListGroupID,
}; };
}; };
// read only mode
isReadonly = () => this.diffContext != undefined && this.diffContext.enableEditInDiffMode;
} }