want to extract recipe list from recipe detail. But still have no idea about link formControl between 2 component
This commit is contained in:
parent
72f27f198d
commit
bb29e7de5a
7 changed files with 257 additions and 130 deletions
|
|
@ -95,18 +95,18 @@ export interface ToppingList {
|
|||
}
|
||||
|
||||
export interface MatRecipe {
|
||||
MixOrder: string;
|
||||
FeedParameter: string;
|
||||
FeedPattern: string;
|
||||
MixOrder: number;
|
||||
FeedParameter: number;
|
||||
FeedPattern: number;
|
||||
isUse: boolean;
|
||||
materialPathId: number;
|
||||
powderGram: string;
|
||||
powderTime: string;
|
||||
stirTime: string;
|
||||
syrupGram: string;
|
||||
syrupTime: string;
|
||||
waterCold: string;
|
||||
waterYield: string;
|
||||
powderGram: number;
|
||||
powderTime: number;
|
||||
stirTime: number;
|
||||
syrupGram: number;
|
||||
syrupTime: number;
|
||||
waterCold: number;
|
||||
waterYield: number;
|
||||
}
|
||||
|
||||
export interface ToppingSet {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
|
|||
import { Observable, tap } from 'rxjs';
|
||||
import { Recipe, Recipe01 } from '../models/recipe.model';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { RecipeMetaData } from 'src/app/shared/types/recipe';
|
||||
|
||||
interface RecipeParams {
|
||||
version: string;
|
||||
|
|
@ -62,25 +63,11 @@ export class RecipeService {
|
|||
|
||||
getRecipesById(id: string): Observable<{
|
||||
recipe: Recipe01;
|
||||
recipeMetaData: {
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
picture: string;
|
||||
};
|
||||
recipeMetaData: RecipeMetaData;
|
||||
}> {
|
||||
return this._httpClient.get<{
|
||||
recipe: Recipe01;
|
||||
recipeMetaData: {
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
picture: string;
|
||||
};
|
||||
recipeMetaData: RecipeMetaData;
|
||||
}>(environment.api + '/recipes/' + id, {
|
||||
withCredentials: true,
|
||||
responseType: 'json',
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@
|
|||
<div *ngIf="isLoaded; else indicator" [@inOutAnimation]>
|
||||
<div class="flex flex-wrap">
|
||||
<h5 class="mb-2 text-xl font-bold text-gray-900">
|
||||
{{ originalRecipeDetail.name }}
|
||||
{{ originalRecipeDetail.recipe.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">
|
||||
{{ originalRecipeDetail.productCode }}
|
||||
{{ originalRecipeDetail.recipe.productCode }}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="flex items-center mb-2">
|
||||
|
|
@ -18,7 +18,8 @@
|
|||
<p class="text-sm text-gray-500">Last Modify</p>
|
||||
<p class="ml-2 text-sm text-gray-900">
|
||||
{{
|
||||
originalRecipeDetail.lastModified | date : "dd/MM/yyyy HH:mm:ss"
|
||||
originalRecipeDetail.recipe.lastModified
|
||||
| date : "dd/MM/yyyy HH:mm:ss"
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -30,11 +31,6 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
@ -43,10 +39,10 @@
|
|||
<div
|
||||
*ngIf="isLoaded; else indicator"
|
||||
[@inOutAnimation]
|
||||
class="grid grid-cols-2 gap-4 w-full"
|
||||
class="grid-cols-2 w-full flex flex-col gap-2"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<label class="label"
|
||||
<label class="label text-base mr-3 w-max"
|
||||
><span class="label-text text-base mr-3 w-max">Name:</span></label
|
||||
>
|
||||
<input
|
||||
|
|
@ -56,7 +52,11 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<label class="text-base mr-3 w-max">Other Name:</label>
|
||||
<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"
|
||||
|
|
@ -64,7 +64,11 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<label class="text-base mr-3 w-max">Description:</label>
|
||||
<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"
|
||||
|
|
@ -72,72 +76,45 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<label class="text-base mr-3 w-max">Other Description:</label>
|
||||
<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 class="flex flex-row col-span-2 gap-5">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<label class="flex justify-start gap-2 cursor-pointer label">
|
||||
<span class="label-text">Is Use?</span>
|
||||
<input type="checkbox" class="toggle" formControlName="isUse" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<label class="flex justify-start gap-2 cursor-pointer label">
|
||||
<span class="label-text">Is Show?</span>
|
||||
<input type="checkbox" class="toggle" formControlName="isShow" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<label class="flex justify-start gap-2 cursor-pointer label">
|
||||
<span class="label-text">Is Disable?</span>
|
||||
<input type="checkbox" class="toggle" formControlName="disable" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<div class="col-span-3 max-h-[300px] overflow-auto mb-4 rounded">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="bg-gray-200">
|
||||
<th class="px-6 py-3">Enable</th>
|
||||
<th class="px-6 py-3">Material ID</th>
|
||||
<th class="px-6 py-3">Material Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody *ngIf="isMatLoaded; else indicator">
|
||||
<tr
|
||||
*ngFor="let mat of originalRecipeDetail?.matData"
|
||||
class="bg-white la border-b hover:bg-secondary"
|
||||
>
|
||||
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[checked]="mat.enable"
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
{{ mat.id }}
|
||||
</td>
|
||||
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
{{ mat.name }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="col-span-3 min-h-[500px] max-h-[500px] overflow-auto mb-4 rounded bg-white border border-gray-200 shadow"
|
||||
>
|
||||
<app-recipe-list
|
||||
[recipeListData]="originalRecipeDetail.recipe.recipes"
|
||||
></app-recipe-list>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<div
|
||||
|
|
@ -350,6 +327,11 @@
|
|||
</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"
|
||||
|
|
|
|||
|
|
@ -3,43 +3,13 @@ import { Component, EventEmitter, OnInit } from '@angular/core';
|
|||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { isEqual } from 'lodash';
|
||||
import { delay, finalize } from 'rxjs';
|
||||
import { finalize } from 'rxjs';
|
||||
import { RecipeService } from 'src/app/core/services/recipe.service';
|
||||
import { ConfirmModal } from 'src/app/shared/modal/confirm/confirm-modal.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { MatRecipe } from 'src/app/core/models/recipe.model';
|
||||
import { MaterialService } from 'src/app/core/services/material.service';
|
||||
|
||||
interface RecipeDetail {
|
||||
lastModified: Date;
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
price: number;
|
||||
isUse: boolean;
|
||||
isShow: boolean;
|
||||
disable: boolean;
|
||||
recipes: MatRecipe[];
|
||||
matData?: MaterialData[];
|
||||
}
|
||||
|
||||
interface MaterialData {
|
||||
id: number;
|
||||
name: string;
|
||||
enable: boolean;
|
||||
}
|
||||
|
||||
interface RecipeMetaData {
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
picture: string;
|
||||
}
|
||||
import { RecipeMetaData, RecipeDetail } from 'src/app/shared/types/recipe';
|
||||
import { RecipeListComponent } from './recipe-list/recipe-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recipe-details',
|
||||
|
|
@ -52,6 +22,7 @@ interface RecipeMetaData {
|
|||
ReactiveFormsModule,
|
||||
ConfirmModal,
|
||||
DatePipe,
|
||||
RecipeListComponent,
|
||||
],
|
||||
animations: [
|
||||
trigger('inOutAnimation', [
|
||||
|
|
@ -108,35 +79,81 @@ export class RecipeDetailsComponent implements OnInit {
|
|||
disable: recipe.disable,
|
||||
});
|
||||
this.originalRecipeDetail = {
|
||||
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,
|
||||
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.recipeMetaData = recipeMetaData;
|
||||
this.isLoaded = true;
|
||||
|
||||
const ids = this.originalRecipeDetail.recipes.map(
|
||||
const ids = this.originalRecipeDetail.recipes?.map(
|
||||
(recipe) => recipe.materialPathId
|
||||
);
|
||||
|
||||
this._materialService.getMaterialCodes(ids).subscribe((data) => {
|
||||
this.originalRecipeDetail.matData = data
|
||||
this.originalRecipeDetail.recipe.recipes = recipe.recipes
|
||||
.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.materialID,
|
||||
name: item.PackageDescription,
|
||||
enable: false,
|
||||
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) => (a.id > b.id ? -1 : -1));
|
||||
.sort((a, b) => {
|
||||
return a.id === 0 ? 1 : a.id > b.id ? 1 : -1;
|
||||
});
|
||||
this.isMatLoaded = true;
|
||||
});
|
||||
});
|
||||
|
|
@ -180,6 +197,6 @@ export class RecipeDetailsComponent implements OnInit {
|
|||
}
|
||||
|
||||
get isValueChanged() {
|
||||
return !isEqual(this.recipeDetail.value, this.originalRecipeDetail);
|
||||
return !isEqual(this.recipeDetail.value, this.originalRecipeDetail?.recipe);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
<table class="table" *ngIf="$isLoaded; else indicator">
|
||||
<thead>
|
||||
<tr class="bg-gray-200">
|
||||
<th class="px-6 py-3">Enable</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 Hot</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody formArrayName="recipes">
|
||||
<tr
|
||||
*ngFor="let mat of recipeListData"
|
||||
class="bg-white la border-b hover:bg-secondary"
|
||||
>
|
||||
<div>
|
||||
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[disabled]="mat.enable"
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
<input type="text" class="input" formControlName="id" />
|
||||
</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="stirTime" />
|
||||
</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="waterHot" />
|
||||
</td>
|
||||
</div>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { NgFor, NgIf } from '@angular/common';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, map } from 'rxjs';
|
||||
import { MaterialData } from 'src/app/shared/types/recipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recipe-list',
|
||||
templateUrl: './recipe-list.component.html',
|
||||
standalone: true,
|
||||
imports: [NgIf, NgFor],
|
||||
})
|
||||
export class RecipeListComponent implements OnInit {
|
||||
@Input({ required: true }) recipeListData: MaterialData[] | undefined;
|
||||
|
||||
$isLoaded: Observable<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
53
client/src/app/shared/types/recipe.d.ts
vendored
Normal file
53
client/src/app/shared/types/recipe.d.ts
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { MatRecipe } from 'src/app/core/models/recipe.model';
|
||||
|
||||
export interface RecipeMetaData {
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
picture: string;
|
||||
lastModified: Date;
|
||||
}
|
||||
|
||||
export interface MaterialData {
|
||||
id: number;
|
||||
name: string;
|
||||
enable: boolean;
|
||||
mixOrder: number;
|
||||
stirTime: number;
|
||||
powderGram: number;
|
||||
powderTime: number;
|
||||
syrupGram: number;
|
||||
syrupTime: number;
|
||||
waterCold: number;
|
||||
waterHot: number;
|
||||
}
|
||||
|
||||
export interface RecipeDetail {
|
||||
recipe: {
|
||||
lastModified: Date;
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
price: number;
|
||||
isUse: boolean;
|
||||
isShow: boolean;
|
||||
disable: boolean;
|
||||
recipes?: MaterialData[];
|
||||
};
|
||||
recipes?: MatRecipe[];
|
||||
}
|
||||
|
||||
export interface RecipeDetailEditable {
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
otherDescription: string;
|
||||
price: number;
|
||||
isUse: boolean;
|
||||
isShow: boolean;
|
||||
disable: boolean;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue