change save file method, add file changes history

This commit is contained in:
pakintada@gmail.com 2023-12-05 14:33:45 +07:00
parent 519749fd3a
commit 820557a268
12 changed files with 2388 additions and 2036 deletions

View file

@ -1,329 +1,332 @@
<div class="p-4">
<form class="grid grid-cols-3 gap-4 mb-4" [formGroup]="recipeDetailForm">
<div
class="block col-span-1 p-6 bg-white border border-gray-200 rounded-lg shadow"
>
<div *ngIf="isLoaded; else indicator" [@inOutAnimation]>
<div class="flex flex-wrap">
<h5 class="mb-2 text-xl font-bold text-gray-900">
{{ recipeDetailForm.getRawValue().name }}
</h5>
<h5 class="mb-2 px-3 text-xl font-bold text-gray-900">|</h5>
<h5 class="mb-2 text-xl font-bold text-gray-900">
{{ recipeDetailForm.getRawValue().otherName }}
</h5>
</div>
<div class="flex items-center mb-2">
<div class="flex items-center">
<p class="text-sm text-gray-500">Last Modify</p>
<p class="ml-2 text-sm text-gray-900">
{{
recipeDetailForm.getRawValue().lastModified
| date : "dd/MM/yyyy HH:mm:ss"
}}
</p>
</div>
</div>
<div class="flex p-3 items-center justify-center w-full">
<img
class="rounded-lg shadow"
src="/assets/{{ 'bn_hot_america_no' }}.png"
/>
</div>
</div>
</div>
<div
class="block col-span-2 p-6 bg-white border border-gray-200 rounded-lg shadow min-h-[300px]"
>
<div
*ngIf="isLoaded; else indicator"
[@inOutAnimation]
class="grid-cols-2 w-full flex flex-col gap-2"
>
<div class="flex items-center">
<label class="label text-base mr-3 w-max"
><span class="label-text text-base mr-3 w-max">Name:</span></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="name"
/>
</div>
<div class="flex items-center">
<label class="label">
<span class="label-text text-base mr-3 w-max"
>Other Name:</span
></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="otherName"
/>
</div>
<div class="flex items-center">
<label class="label"
><span class="lable-text text-base mr-3 w-max"
>Description:</span
></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="description"
/>
</div>
<div class="flex items-center">
<label class="label"
><span class="label-text text-base mr-3 w-max"
>Other Description:</span
></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="otherDescription"
/>
</div>
</div>
</div>
<div
class="col-span-3 overflow-auto mb-4 rounded bg-white border border-gray-200 shadow"
>
<app-recipe-list
[productCode]="productCode"
(recipeListFormChange)="onRecipeListFormChange($event)"
></app-recipe-list>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
</div>
<div
class="flex items-center justify-center h-48 mb-4 rounded bg-gray-50 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div class="grid grid-cols-2 gap-4">
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
</div>
<div
class="sticky bottom-0 col-span-3 flex justify-end bg-white rounded-full drop-shadow-2xl p-3"
>
<button
(click)="onPressConfirmClose()"
class="btn btn-error w-36 text-white mr-2"
>
Cancel
</button>
<button
(click)="onPressConfirmSave()"
class="btn btn-success w-36 text-white"
[disabled]="!isValueChanged"
>
Save
</button>
</div>
</form>
<ng-template #indicator>
<div class="flex w-full h-full justify-center items-center">
<span class="loading loading-spinner w-20"></span>
</div>
</ng-template>
<confirm-modal
[title]="confirmSave.title"
[message]="confirmSave.message"
[confirmCallBack]="confirmSave.confirmCallBack"
[show]="showConfirmSaveModal"
></confirm-modal>
<confirm-modal
[title]="confirmClose.title"
[message]="confirmClose.message"
[confirmCallBack]="confirmClose.confirmCallBack"
[show]="showConfirmCloseModal"
></confirm-modal>
</div>
<div class="p-4">
<form class="grid grid-cols-3 gap-4 mb-4" [formGroup]="recipeDetailForm">
<div
class="block col-span-1 p-6 bg-white border border-gray-200 rounded-lg shadow"
>
<div *ngIf="isLoaded; else indicator" [@inOutAnimation]>
<div class="flex flex-wrap">
<h5 class="mb-2 text-xl font-bold text-gray-900">
{{ recipeDetailForm.getRawValue().name }}
</h5>
<h5 class="mb-2 px-3 text-xl font-bold text-gray-900">|</h5>
<h5 class="mb-2 text-xl font-bold text-gray-900">
{{ recipeDetailForm.getRawValue().otherName }}
</h5>
</div>
<div class="flex items-center mb-2">
<div class="flex items-center">
<p class="text-sm text-gray-500">Last Modify</p>
<p class="ml-2 text-sm text-gray-900">
{{
recipeDetailForm.getRawValue().lastModified
| date : "dd/MM/yyyy HH:mm:ss"
}}
</p>
</div>
</div>
<div class="flex p-3 items-center justify-center w-full">
<img
class="rounded-lg shadow"
src="/assets/{{ 'bn_hot_america_no' }}.png"
/>
</div>
</div>
</div>
<div
class="block col-span-2 p-6 bg-white border border-gray-200 rounded-lg shadow min-h-[300px]"
>
<div
*ngIf="isLoaded; else indicator"
[@inOutAnimation]
class="grid-cols-2 w-full flex flex-col gap-2"
>
<div class="flex items-center">
<label class="label text-base mr-3 w-max"
><span class="label-text text-base mr-3 w-max">Name:</span></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="name"
/>
</div>
<div class="flex items-center">
<label class="label">
<span class="label-text text-base mr-3 w-max"
>Other Name:</span
></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="otherName"
/>
</div>
<div class="flex items-center">
<label class="label"
><span class="lable-text text-base mr-3 w-max"
>Description:</span
></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="Description"
/>
</div>
<div class="flex items-center">
<label class="label"
><span class="label-text text-base mr-3 w-max"
>Other Description:</span
></label
>
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="otherDescription"
/>
</div>
</div>
</div>
<div
class="col-span-3 overflow-auto mb-4 rounded bg-white border border-gray-200 shadow"
>
<app-recipe-list
[productCode]="productCode"
(recipeListFormChange)="onRecipeListFormChange($event)"
></app-recipe-list>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
</div>
<div
class="flex items-center justify-center h-48 mb-4 rounded bg-gray-50 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div class="grid grid-cols-2 gap-4">
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
<div
class="flex items-center justify-center rounded bg-gray-50 h-28 dark:bg-gray-800"
>
<p class="text-2xl text-gray-400 dark:text-gray-500">
<svg
class="w-3.5 h-3.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 1v16M1 9h16"
/>
</svg>
</p>
</div>
</div>
<div
class="sticky bottom-0 col-span-3 flex justify-end bg-white rounded-full drop-shadow-2xl p-3"
>
<!-- <div> Commit Message </div> -->
<!-- <p class="text-2xl mr-8 text-gray-400 dark:text-gray-500">Commit Message</p> -->
<input type="text" name="commit-message" id="commit-message" placeholder="Commit Message" class="input input-bordered mr-8 w-full max-w-xs" [value]="productCode" (keyup)="onKeyUpCommitMsg($event)"/>
<button
(click)="onPressConfirmClose()"
class="btn btn-error w-36 text-white mr-2"
>
Cancel
</button>
<button
(click)="onPressConfirmSave()"
class="btn btn-success w-36 text-white"
[disabled]="!isValueChanged"
>
Save
</button>
</div>
</form>
<ng-template #indicator>
<div class="flex w-full h-full justify-center items-center">
<span class="loading loading-spinner w-20"></span>
</div>
</ng-template>
<confirm-modal
[title]="confirmSave.title"
[message]="confirmSave.message"
[confirmCallBack]="confirmSave.confirmCallBack"
[show]="showConfirmSaveModal"
></confirm-modal>
<confirm-modal
[title]="confirmClose.title"
[message]="confirmClose.message"
[confirmCallBack]="confirmClose.confirmCallBack"
[show]="showConfirmCloseModal"
></confirm-modal>
</div>

View file

@ -1,158 +1,203 @@
import { CommonModule, DatePipe } from '@angular/common';
import { Component, EventEmitter, OnInit } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
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 { RecipeListComponent } from './recipe-list/recipe-list.component';
import {
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: [
CommonModule,
RouterLink,
ReactiveFormsModule,
ConfirmModal,
DatePipe,
RecipeListComponent,
],
animations: [
trigger('inOutAnimation', [
transition(':enter', [
style({ opacity: 0 }),
animate('1s ease-out', style({ opacity: 1 })),
]),
]),
],
})
export class RecipeDetailsComponent implements OnInit {
title: string = 'Recipe Detail';
recipeDetail$!: Observable<RecipeDetail>;
isLoaded: boolean = false;
isMatLoaded: boolean = false;
actionRecord: ActionRecord<RecipeDetail | RecipeDetailMat> =
new ActionRecord();
recipeOriginalDetail!: typeof this.recipeDetailForm.value;
constructor(
private _formBuilder: FormBuilder,
private _route: ActivatedRoute,
private _router: Router,
private _recipeService: RecipeService
) {}
productCode!: string;
recipeDetailForm = this._formBuilder.group({
productCode: '',
name: '',
otherName: '',
description: '',
otherDescription: '',
lastModified: new Date(),
price: 0,
isUse: false,
isShow: false,
disable: false,
});
ngOnInit() {
this.productCode = this._route.snapshot.params['productCode'];
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.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<boolean> = new EventEmitter<boolean>();
showConfirmCloseModal: EventEmitter<boolean> = new EventEmitter<boolean>();
confirmSave = {
title: 'The changes detected!',
message: 'Do you want to save changes?',
confirmCallBack: () => {
console.log('confirm save');
// TODO: update value in targeted recipe
// this._recipeService.editChanges(
// this._recipeService.getCurrentCountry(),
// this._recipeService.getCurrentFile(),
// {
// ...this.recipeDetail,
// }
// );
console.log('Sending changes');
this._router.navigate(['/recipes']);
},
};
confirmClose = {
title: 'The changes will be lost!',
message: 'Do you want to close without saving?',
confirmCallBack: () => {
console.log('confirm close');
this._router.navigate(['/recipes']);
},
};
onPressConfirmSave() {
if (this.isValueChanged) {
this.showConfirmSaveModal.emit(true);
} else {
this._router.navigate(['/recipes']);
}
}
onPressConfirmClose() {
if (this.isValueChanged) {
this.showConfirmCloseModal.emit(true);
} else {
this._router.navigate(['/recipes']);
}
}
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;
}
}
import { CommonModule, DatePipe } from '@angular/common';
import { Component, EventEmitter, OnInit } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
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 { RecipeListComponent } from './recipe-list/recipe-list.component';
import {
RecipeDetail,
RecipeDetailMat,
} from 'src/app/core/models/recipe.model';
import { Action, ActionRecord } from 'src/app/shared/actionRecord/actionRecord';
import { isEqual } from 'lodash';
import { UserService } from 'src/app/core/services/user.service';
@Component({
selector: 'app-recipe-details',
templateUrl: './recipe-details.component.html',
standalone: true,
imports: [
CommonModule,
RouterLink,
ReactiveFormsModule,
ConfirmModal,
DatePipe,
RecipeListComponent,
],
animations: [
trigger('inOutAnimation', [
transition(':enter', [
style({ opacity: 0 }),
animate('1s ease-out', style({ opacity: 1 })),
]),
]),
],
})
export class RecipeDetailsComponent implements OnInit {
title: string = 'Recipe Detail';
recipeDetail$!: Observable<RecipeDetail>;
isLoaded: boolean = false;
isMatLoaded: boolean = false;
actionRecord: ActionRecord<RecipeDetail | RecipeDetailMat> =
new ActionRecord();
recipeOriginalDetail!: typeof this.recipeDetailForm.value;
commit_msg :string = "";
constructor(
private _formBuilder: FormBuilder,
private _route: ActivatedRoute,
private _router: Router,
private _recipeService: RecipeService,
private _userService: UserService
) {}
productCode!: string;
recipeDetailForm = this._formBuilder.group({
productCode: '',
name: '',
otherName: '',
Description: '',
otherDescription: '',
lastModified: new Date(),
price: 0,
isUse: false,
isShow: false,
disable: false,
});
repl = []
ngOnInit() {
this.productCode = this._route.snapshot.params['productCode'];
this.recipeDetail$ = this._recipeService
.getRecipeDetail(this.productCode)
.pipe(first());
this.recipeDetail$.subscribe((detail) => {
console.log('Recipe Detail', detail);
this.recipeDetailForm.patchValue(detail);
this.isLoaded = true;
this.recipeOriginalDetail = { ...this.recipeDetailForm.getRawValue() };
});
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<boolean> = new EventEmitter<boolean>();
showConfirmCloseModal: EventEmitter<boolean> = new EventEmitter<boolean>();
confirmSave = {
title: 'The changes detected!',
message: 'Do you want to save changes?',
confirmCallBack: () => {
console.log('confirm save');
// get username
let username:string = ""
this._userService.getCurrentUser().subscribe((user) => {
username = user.user.name;
let to_send = {
edit_by: username,
commit_msg: this.commit_msg,
productCode: this.productCode,
name: this.recipeDetailForm.getRawValue().name,
otherName: this.recipeDetailForm.getRawValue().otherName,
Description: this.recipeDetailForm.getRawValue().Description,
otherDescription: this.recipeDetailForm.getRawValue().otherDescription,
LastChange: this.recipeDetailForm.getRawValue().lastModified,
price: this.recipeDetailForm.getRawValue().price,
// isUse: this,
// isShow: null,
// disable: null,
recipes: [
...this.repl
],
}
// TODO: update value in targeted recipe
this._recipeService.editChanges(
this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile(),
{
...to_send,
}
);
console.log('Sending changes');
this._router.navigate(['/recipes']);
})
},
};
onKeyUpCommitMsg(e: any){
this.commit_msg = e.target.value;
}
confirmClose = {
title: 'The changes will be lost!',
message: 'Do you want to close without saving?',
confirmCallBack: () => {
console.log('confirm close');
this._router.navigate(['/recipes']);
},
};
onPressConfirmSave() {
if (this.isValueChanged) {
this.showConfirmSaveModal.emit(true);
} else {
this._router.navigate(['/recipes']);
}
}
onPressConfirmClose() {
if (this.isValueChanged) {
this.showConfirmCloseModal.emit(true);
} else {
this._router.navigate(['/recipes']);
}
}
isValueChanged: boolean = false;
onRecipeDetailFormChange(recipeDetail: typeof this.recipeDetailForm.value) {
console.log('Recipe Detail Form Changed', recipeDetail);
}
onRecipeListFormChange(repl: unknown[]) {
console.log('Recipe List Form Changed', repl);
this.repl = repl as never[];
this.isValueChanged ||= repl != undefined ? true : false;
}
}

View file

@ -1,57 +1,57 @@
<table class="table" [formGroup]="recipeListForm">
<thead>
<tr class="bg-gray-200">
<th class="px-6 py-3">Is Use</th>
<th class="px-6 py-3">Material ID</th>
<th class="px-6 py-3">Material Name</th>
<th class="px-6 py-3">MixOrder</th>
<th class="px-6 py-3">Stir Time</th>
<th class="px-6 py-3">Powder Gram</th>
<th class="px-6 py-3">Powder Time</th>
<th class="px-6 py-3">Syrup Gram</th>
<th class="px-6 py-3">Syrup Time</th>
<th class="px-6 py-3">Water Cold</th>
<th class="px-6 py-3">Water Yield</th>
</tr>
</thead>
<tbody
formArrayName="recipeListData"
*ngFor="let mat of recipeListData.controls; let i = index"
>
<tr class="bg-white la border-b hover:bg-secondary" formGroupName="{{ i }}">
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="checkbox" class="toggle" formControlName="isUse" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="materialID" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="name" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="mixOrder" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="powderGram" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="powderTime" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="syrupGram" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="syrupTime" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="waterCold" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="waterYield" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="stirTime" />
</td>
</tr>
</tbody>
</table>
<table class="table" [formGroup]="recipeListForm">
<thead>
<tr class="bg-gray-200">
<th class="px-6 py-3">Is Use</th>
<th class="px-6 py-3">Material ID</th>
<th class="px-6 py-3">Material Name</th>
<th class="px-6 py-3">MixOrder</th>
<th class="px-6 py-3">Stir Time</th>
<th class="px-6 py-3">Powder Gram</th>
<th class="px-6 py-3">Powder Time</th>
<th class="px-6 py-3">Syrup Gram</th>
<th class="px-6 py-3">Syrup Time</th>
<th class="px-6 py-3">Water Cold</th>
<th class="px-6 py-3">Water Yield</th>
</tr>
</thead>
<tbody
formArrayName="recipeListData"
*ngFor="let mat of recipeListData.controls; let i = index"
>
<tr class="bg-white la border-b hover:bg-secondary" formGroupName="{{ i }}">
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="checkbox" class="toggle" formControlName="isUse" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="materialPathId" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="name" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="mixOrder" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="powderGram" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="powderTime" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="syrupGram" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="syrupTime" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="waterCold" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="waterYield" />
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="stirTime" />
</td>
</tr>
</tbody>
</table>

View file

@ -1,90 +1,102 @@
import { NgFor, NgIf } from '@angular/common';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import {
FormArray,
FormBuilder,
FormControl,
FormGroup,
ReactiveFormsModule,
} from '@angular/forms';
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',
templateUrl: './recipe-list.component.html',
standalone: true,
imports: [NgIf, NgFor, ReactiveFormsModule],
})
export class RecipeListComponent implements OnInit {
@Input({ required: true }) productCode!: string;
@Output() recipeListFormChange = new EventEmitter<boolean>();
isMatLoaded: boolean = false;
constructor(
private _recipeService: RecipeService,
private _formBuilder: FormBuilder
) {}
recipeListForm = this._formBuilder.group(
{
recipeListData: this._formBuilder.array([]),
},
{ updateOn: 'blur' }
);
private _recipeListOriginalArray!: RecipeDetailMat[];
ngOnInit(): void {
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;
}
}
import { NgFor, NgIf } from '@angular/common';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import {
FormArray,
FormBuilder,
FormControl,
FormGroup,
ReactiveFormsModule,
} from '@angular/forms';
import { forEach, 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',
templateUrl: './recipe-list.component.html',
standalone: true,
imports: [NgIf, NgFor, ReactiveFormsModule],
})
export class RecipeListComponent implements OnInit {
@Input({ required: true }) productCode!: string;
@Output() recipeListFormChange = new EventEmitter<unknown[]>();
isMatLoaded: boolean = false;
constructor(
private _recipeService: RecipeService,
private _formBuilder: FormBuilder
) {}
recipeListForm = this._formBuilder.group(
{
recipeListData: this._formBuilder.array([]),
},
{ updateOn: 'blur' }
);
private _recipeListOriginalArray!: RecipeDetailMat[];
ngOnInit(): void {
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,
materialPathId: recipeDetailMat.materialPathId,
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')
)
) {
let emitted_res: any[] = []
// force type change. temporary solution
forEach(value.recipeListData!, (recipeDetailMat: any) => {
recipeDetailMat.materialPathId = parseInt(recipeDetailMat.materialPathId!)
emitted_res.push(recipeDetailMat)
})
this.recipeListFormChange.emit(emitted_res as unknown[]);
} else {
this.recipeListFormChange.emit([]);
}
});
}
get recipeListData(): FormArray {
return this.recipeListForm.get('recipeListData') as FormArray;
}
}