add mixing topping with override

This commit is contained in:
pakintada@gmail.com 2024-01-23 13:46:37 +07:00
parent 35ebde967d
commit 79dddd1fbc
15 changed files with 296 additions and 91 deletions

View file

@ -1,5 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import Lang from './shared/helpers/lang';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -11,5 +12,7 @@ export class AppComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.titleService.setTitle('Recipe Manager | Tao Bin'); this.titleService.setTitle('Recipe Manager | Tao Bin');
// lang
Lang.initLanguageSwitcher();
} }
} }

View file

@ -4,6 +4,7 @@ export type RecipeOverview = {
name: string; name: string;
otherName: string; otherName: string;
description: string; description: string;
otherDescription: string;
lastUpdated: Date; lastUpdated: Date;
}; };

View file

@ -105,6 +105,11 @@
/> />
</div> </div>
<div class="flex items-center">
<label class="label">Price</label>
<input class="input input-sm input-bordered input-ghost w-full text-center" type="text" name="price" value="{{recipePrice()}}">
</div>
<div *ngIf="hasSubmenu()"> <div *ngIf="hasSubmenu()">
<div *ngFor="let sub of listSubMenuProductcodes()"> <div *ngFor="let sub of listSubMenuProductcodes()">
<button <button
@ -153,17 +158,12 @@
<a <a
href="{{ department }}/recipe/{{ productCode }}#name" href="{{ department }}/recipe/{{ productCode }}#name"
class="btn btn-xs" class="btn btn-xs"
>1</a >About</a
> >
<a <a
href="{{ department }}/recipe/{{ productCode }}#recipeList" href="{{ department }}/recipe/{{ productCode }}#recipeList"
class="btn btn-xs" class="btn btn-xs"
>2</a >Recipe List</a
>
<a
href="{{ department }}/recipe/{{ productCode }}#toppingSet"
class="btn btn-xs"
>3</a
> >
</div> </div>

View file

@ -279,14 +279,6 @@ export class RecipeDetailsComponent implements OnInit {
// console.log('Recipe List Form Changed', repl); // console.log('Recipe List Form Changed', repl);
this.repl = repl[1] as never[]; this.repl = repl[1] as never[];
this.tpl = repl[0] as never[]; this.tpl = repl[0] as never[];
// check length of toppinglist
console.log(
'tpl length',
this.tpl.length,
'original length',
(this.rawRecipe! as any).ToppingSet.length
);
for (let ti = 0; ti < this.tpl.length; ti++) { for (let ti = 0; ti < this.tpl.length; ti++) {
// check at the same index // check at the same index
if (!isEqual(this.tpl[ti][0], (this.rawRecipe as any).ToppingSet[ti])) { if (!isEqual(this.tpl[ti][0], (this.rawRecipe as any).ToppingSet[ti])) {
@ -352,4 +344,11 @@ export class RecipeDetailsComponent implements OnInit {
} }
} }
} }
recipePrice = () =>
this.rawRecipe != undefined &&
this.rawRecipe != null &&
Object.keys(this.rawRecipe as any).includes('cashPrice')
? (this.rawRecipe as any).cashPrice
: 0;
} }

View file

@ -139,7 +139,7 @@
</div> </div>
</div> </div>
<div class="collapse collapse-open" *ngIf="isTopping(getTypeForRecipeListAtIndex(i)['id'])"> <div class="collapse collapse-open" *ngIf="isTopping(getTypeForRecipeListAtIndex(i)['id'])">
<div class="collapse-title">Topping Settings</div> <!-- <div class="collapse-title">Topping Settings</div> -->
<div class="collapse-content"> <div class="collapse-content">
<app-recipe-topping <app-recipe-topping
[productCode]="productCode" [productCode]="productCode"

View file

@ -509,12 +509,19 @@ export class RecipeListComponent implements OnInit {
}, 100); }, 100);
} }
openRecipeListEditor(i: number) { async openRecipeListEditor(i: number) {
if(this.timeoutHandler){ await Promise.resolve();
if (this.timeoutHandler) {
console.log("timeout get", this.timeout); console.log("timeout get", this.timeout);
if(this.timeout >= 20){ if (this.timeout >= 20) {
this.showDetailRecipeList = true; // alert("Opening Recipe List Editor in detail")
if (confirm("Are you sure you want to open Recipe List Editor in detail?")) {
this.showDetailRecipeList = true;
} else {
this.showDetailRecipeList = false;
}
} }
clearInterval(this.timeoutHandler); clearInterval(this.timeoutHandler);

View file

@ -1,38 +1,101 @@
<div [formGroup]="toppingForm"> <div [formGroup]="toppingForm">
<div formArrayName="toppingList" *ngFor="let topping of toppingList.controls; let i = index"> <div
<div formGroupName="{{ i }}"> formArrayName="toppingList"
*ngFor="let topping of toppingList.controls; let i = index"
>
<!-- toppingList.at(i) -->
<div class="space-y-2" formGroupName="{{ i }}">
<!-- iterate through ListGroupId -->
<input type="checkbox" formControlName="isUse"/> <div class="flex space-x-2">
<!-- toppingGroup --> <h3>Enable</h3>
<ng-select <input class="toggle" type="checkbox" formControlName="isUse" />
appendTo="body" </div>
[clearable]="false"
[compareWith]="this.compareFunc" <div class="flex space-x-2 items-center">
formControlName="groupID" <!-- toppingGroup -->
(close)="getDefaultOfGroup(getGroupIdByIndex(i))" <ng-select
> class="w-52"
<ng-option appendTo="body"
*ngFor="let item of allToppingsDefinitions" [clearable]="false"
[value]="item.groupId.toString()" [compareWith]="this.compareFunc"
> formControlName="groupID"
<div>{{ item.name }} ({{ item.groupId }})</div> (close)="getDefaultOfGroup(getGroupIdByIndex(i))"
</ng-option> >
</ng-select> <ng-option
<!-- defaultSelect --> *ngFor="let item of allToppingsDefinitions"
<ng-select [value]="item.groupId.toString()"
appendTo="body" >
[clearable]="false" <div>{{ item.name }} ({{ item.groupId }})</div>
[compareWith]="this.compareFunc" </ng-option>
formControlName="defaultIDSelect" </ng-select>
> <!-- defaultSelect -->
<ng-option <ng-select
*ngFor="let item of getMembersByGroupId(getGroupIdByIndex(this.index!))" class="w-52"
[value]="item.id.toString()" appendTo="body"
> [clearable]="false"
<div>{{ item.name }} ({{ item.id }})</div> [compareWith]="this.compareFunc"
</ng-option> formControlName="defaultIDSelect"
</ng-select> >
</div> <ng-option
*ngFor="
let item of getMembersByGroupId(getGroupIdByIndex(this.index!))
"
[value]="item.id.toString()"
>
<div>{{ item.name }} ({{ item.id }})</div>
</ng-option>
</ng-select>
</div>
<div class="flex items-center justify-center">
<p class="text-sm">Mix with</p>
</div>
</div> </div>
</div>
</div>
<div class="flex flex-row space-x-2 items-center justify-center">
<ng-select
class="w-80"
appendTo="body"
[clearable]="false"
(compareWith)="(this.compareFunc)"
[multiple]="true"
[maxSelectedItems]="3"
(close)="registerExtraTopping(extraTopping)"
[(ngModel)]="extraTopping"
>
<ng-option
*ngFor="let item of allToppingsDefinitions"
[value]="item.groupId.toString()"
>
<div>
<p>{{ item.name }} ({{ item.groupId }}) </p>
</div>
</ng-option>
</ng-select>
</div> </div>
<div *ngFor="let item of extraTopping; let i = index">
<div class="flex items-center space-y-2 space-x-2 justify-start">
<p>{{getToppingDefinitionByGroupId(item)['name']}} ({{getToppingDefinitionByGroupId(item)['groupId']}})</p>
<ng-select
class="w-52"
appendTo="body"
[clearable]="false"
[compareWith]="this.compareFunc"
bindLabel="name"
[(ngModel)]="this.extraToppingDefault[item]"
(close)="triggerValueChange()"
>
<ng-option
*ngFor="let item2 of getMembersByGroupId(item)"
[value]="item2.id.toString()"
>
<div>{{ item2.name }} ({{ item2.id }})</div>
</ng-option>
</ng-select>
</div>
</div>

View file

@ -31,7 +31,7 @@ export class RecipeToppingComponent implements OnInit {
allToppingsDefinitions: allToppingsDefinitions:
| { groupId: string; name: string; members: string; default: string }[] | { groupId: string; name: string; members: string; default: string }[]
| null = [{ groupId: '0', name: 'none', members: '0', default: '0' }]; | null = [{ groupId: '0', name: 'disabled', members: '0', default: '0' }];
allToppingMembersByGroup: { allToppingMembersByGroup: {
id: string; id: string;
@ -39,8 +39,14 @@ export class RecipeToppingComponent implements OnInit {
}[] = []; }[] = [];
department = this._route.snapshot.paramMap.get('department'); department = this._route.snapshot.paramMap.get('department');
// list group id
listGroupId: any[] = [];
private _toppingSetOriginalArray!: ToppingSet[]; private _toppingSetOriginalArray!: ToppingSet[];
extraTopping: [] = [];
extraToppingDefault: {[key: string]: string} = {};
// form // form
toppingForm = this._formBuilder.group( toppingForm = this._formBuilder.group(
{ {
@ -76,6 +82,8 @@ export class RecipeToppingComponent implements OnInit {
this._toppingSetOriginalArray = data; this._toppingSetOriginalArray = data;
console.log('ToppingSet', data, this.index, data.length >= this.index!, data[0]); console.log('ToppingSet', data, this.index, data.length >= this.index!, data[0]);
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(
@ -88,7 +96,7 @@ export class RecipeToppingComponent implements OnInit {
); );
} }
console.log('ToppingSet', this.toppingList); console.log('SubscribeToppingSet', this.toppingList, "list group id=", this.listGroupId);
}); });
// get all topping // get all topping
@ -120,27 +128,7 @@ export class RecipeToppingComponent implements OnInit {
}); });
// emit value changes // emit value changes
this.toppingForm.valueChanges.subscribe((value) => { this.toppingForm.valueChanges.subscribe((value) => {this.triggerValueChange();});
console.log('emit value', value, 'for index: ', this.index! );
// transform data
this.toppingList.value.forEach((value: any) => {
value = this.transformToppingsetStructure(value);
});
// extend lifetime of data
let newMapping = this.toppingList.value.map((value: {
isUse: boolean;
groupID: string;
defaultIDSelect: string;
ListGroupID: string[];
}) => this.transformToppingsetStructure(value));
// add debug here!
console.log('newMapping', newMapping);
this.toppingSetChange.emit([this.index! , newMapping]);
});
} }
compareFunc = (a: any, b: any) => a.toString() === b.toString(); compareFunc = (a: any, b: any) => a.toString() === b.toString();
@ -175,6 +163,89 @@ export class RecipeToppingComponent implements OnInit {
} }
}); });
} }
getToppingDefinitionByGroupId(groupID: any) {
let result = (this.allToppingsDefinitions as any).find(
(x: any) => x.groupId == groupID
);
return result;
}
// for additional topping
registerExtraTopping(event: any) {
console.log("get value from extra topping", this.extraTopping, "default", this.extraToppingDefault, "param event", event);
this.extraTopping.forEach((x: any) => {
// do check if x is registered
if(this.extraToppingDefault[x] == null || this.extraToppingDefault[x] == undefined){
this.bindDefaultToExtraTopping(x);
}
});
}
syncWithExtraToppingDefault(groupId: string){
console.log("syncWithExtraToppingDefault::GetId", groupId, "default", this.extraToppingDefault);
// sync to map
}
clearExtraTopping($event: any){
console.log("clear extra topping", $event);
// get index value of removing element
// let index = this.extraTopping.indexOf($event as never);
let groupId = $event;
// get index from extra default
// let index = this.extraToppingDefault?.find((x: any) => x.groupId == $event);
// clear default from extra
console.log("after clear", this.extraTopping, "default", this.extraToppingDefault);
}
// getExtraDefault = (groupId: any) => (this.extraToppingDefault?.find((x: any) => x.groupId == groupId) as any)?.default;
triggerValueChange(){
console.log('triggerValueChange');
// transform data
this.toppingList.value.forEach((value: any) => {
value = this.transformToppingsetStructure(value);
});
// extend lifetime of data
let newMapping = this.toppingList.value.map((value: {
isUse: boolean;
groupID: string;
defaultIDSelect: string;
ListGroupID: string[];
}) => this.transformToppingsetStructure(value));
// before emit
console.log("emit", newMapping);
// get current value of controls
this.toppingSetChange.emit([this.index! , newMapping]);
}
// bind default back to extraTopping
bindDefaultToExtraTopping(groupId: any){
this.extraToppingDefault![groupId] = '0';
}
cloneValue(value: any){
console.log('cloneValue', value);
// return a clone value that not address to the original value
return JSON.parse(JSON.stringify(value));
}
transformToppingsetStructure = (value: any) => { transformToppingsetStructure = (value: any) => {
@ -192,6 +263,34 @@ export class RecipeToppingComponent implements OnInit {
value.groupID = '0'; value.groupID = '0';
} }
// in case of.doing mix override topping
if(this.extraTopping.length > 0){
let concatArr = [value.ListGroupID[0], ...((this.extraTopping).map((x: string) => parseInt(x)))];
if(concatArr.length < 4){
// fill with 0
while (concatArr.length < 4) {
concatArr.push(0);
}
}
// console.log("concatArr", concatArr);
value.ListGroupID = concatArr;
// get last element that is not 0
let lastIndex = concatArr.findIndex((x: any) => x == 0) - 1;
if(lastIndex <= -1){
lastIndex = 0;
}
value.defaultIDSelect = parseInt(this.extraToppingDefault![value.groupID]);
// console.log("value.defaultIDSelect", value.defaultIDSelect);
}
// do cat array from list group id
// let testSpliceArr = Array(value.ListGroupID).splice(1, value.ListGroupID.length -1 , ...(this.listGroupId[0] as Array<string>).slice(1, value.ListGroupID.length));
return { return {
isUse: value.isUse, isUse: value.isUse,
groupID: value.groupID, groupID: value.groupID,

View file

@ -346,6 +346,7 @@
</td> </td>
<td class="px-6 py-4">{{ recipe.otherName }}</td> <td class="px-6 py-4">{{ recipe.otherName }}</td>
<td class="px-6 py-4 flex-wrap max-w-xs">{{ recipe.description }}</td> <td class="px-6 py-4 flex-wrap max-w-xs">{{ recipe.description }}</td>
<td class="px-6 py-4 flex-wrap max-w-xs">{{ recipe.otherDescription }}</td>
<td class="px-6 py-4"> <td class="px-6 py-4">
{{ recipe.lastUpdated | date : "dd-MMM-yyyy hh:mm:ss" }} {{ recipe.lastUpdated | date : "dd-MMM-yyyy hh:mm:ss" }}
</td> </td>

View file

@ -73,6 +73,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
'Name', 'Name',
'Other Name', 'Other Name',
'Description', 'Description',
'Other Description',
'Last Updated', 'Last Updated',
]; ];
private offset = 0; private offset = 0;
@ -116,6 +117,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
materialIds: this.selectMaterialFilter || [], materialIds: this.selectMaterialFilter || [],
})) }))
.subscribe(({ result, hasMore, totalCount }) => { .subscribe(({ result, hasMore, totalCount }) => {
// console.log("result in scroll", result);
if (this.recipeOverviewList) { if (this.recipeOverviewList) {
this.recipeOverviewList = this.recipeOverviewList =
this.recipeOverviewList.concat(result); this.recipeOverviewList.concat(result);
@ -143,6 +145,10 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
console.log('Trigger onInit where department = ', this.department); console.log('Trigger onInit where department = ', this.department);
this.recipesDashboard$ = this._recipeService this.recipesDashboard$ = this._recipeService
.getRecipesDashboard({ .getRecipesDashboard({
filename: this._recipeService.getCurrentFile(), filename: this._recipeService.getCurrentFile(),
@ -224,9 +230,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
}, },
}); });
// TODO: get all materials; MaterialSetting + MaterialCode
(await (await
// TODO: get all materials; MaterialSetting + MaterialCode
this._materialService this._materialService
.getMaterialCodes()) .getMaterialCodes())
.pipe( .pipe(

View file

@ -0,0 +1,25 @@
import { AsyncStorage } from "./asyncStorage";
export default class Lang {
// init default language
static async initLanguageSwitcher(){
let currentLanguage = await Lang.getCurrentLanguage();
if(currentLanguage == null || currentLanguage == undefined){
await AsyncStorage.setItem('currentLanguage', 'th');
}
console.log('initLanguageSwitcher::SetCurrentTo=>', await Lang.getCurrentLanguage());
}
static getCurrentLanguage() {
return AsyncStorage.getItem('currentLanguage');
}
static async switchLanguage() {
let currentLanguage = await Lang.getCurrentLanguage();
let resultLang = currentLanguage == 'th' ? 'en' : 'th';
// set back lang
await AsyncStorage.setItem('currentLanguage', resultLang);
}
}

View file

@ -3,12 +3,13 @@ package contracts
// ================================== Recipes Dashboard and Overview ================================== // ================================== Recipes Dashboard and Overview ==================================
type RecipeOverview struct { type RecipeOverview struct {
ID int `json:"id"` ID int `json:"id"`
ProductCode string `json:"productCode"` ProductCode string `json:"productCode"`
Name string `json:"name"` Name string `json:"name"`
OtherName string `json:"otherName"` OtherName string `json:"otherName"`
Description string `json:"description"` Description string `json:"description"`
LastUpdated string `json:"lastUpdated"` OtherDescription string `json:"otherDescription"`
LastUpdated string `json:"lastUpdated"`
} }
type RecipeDashboardRequest struct { type RecipeDashboardRequest struct {

View file

@ -311,7 +311,7 @@ func (d *Data) GetRecipe01ByProductCode(filename, countryID, productCode string)
d.taoLogger.Log.Error("GetRecipe01ByProductCode: Error when read recipe file, Return default recipe", zap.Error(err)) d.taoLogger.Log.Error("GetRecipe01ByProductCode: Error when read recipe file, Return default recipe", zap.Error(err))
for _, v := range d.currentRecipe[countryID].Recipe01 { for _, v := range d.currentRecipe[countryID].Recipe01 {
if v.ProductCode == productCode { if v.ProductCode == productCode {
return v, nil return v, fmt.Errorf("[DEFAULT]-ERR")
} }
} }
} }

View file

@ -71,6 +71,7 @@ func (mr *MaterialRouter) GetFullMaterialDetail(w http.ResponseWriter, r *http.R
materialDetails = append(materialDetails, map[string]interface{}{ materialDetails = append(materialDetails, map[string]interface{}{
"materialId": matSetting.ID, "materialId": matSetting.ID,
"name": mat_name, "name": mat_name,
"nameEN": matSetting.MaterialOtherName,
"type": "powder:" + strconv.FormatBool(matSetting.PowderChannel) + ",syrup:" + strconv.FormatBool(matSetting.SyrupChannel) + ",bean:" + strconv.FormatBool(matSetting.BeanChannel) + ",equipment:" + strconv.FormatBool(matSetting.IsEquipment) + ",soda:" + strconv.FormatBool(matSetting.SodaChannel) + ",icecream:" + strconv.FormatBool(matSetting.IceScreamBingsuChannel), "type": "powder:" + strconv.FormatBool(matSetting.PowderChannel) + ",syrup:" + strconv.FormatBool(matSetting.SyrupChannel) + ",bean:" + strconv.FormatBool(matSetting.BeanChannel) + ",equipment:" + strconv.FormatBool(matSetting.IsEquipment) + ",soda:" + strconv.FormatBool(matSetting.SodaChannel) + ",icecream:" + strconv.FormatBool(matSetting.IceScreamBingsuChannel),
}) })
} }

View file

@ -245,12 +245,13 @@ func (rs *recipeService) GetRecipeOverview(request *contracts.RecipeOverviewRequ
// Map to contracts.RecipeOverview // Map to contracts.RecipeOverview
for _, v := range recipeFilter { for _, v := range recipeFilter {
result.Result = append(result.Result, contracts.RecipeOverview{ result.Result = append(result.Result, contracts.RecipeOverview{
ID: v.ID, ID: v.ID,
ProductCode: v.ProductCode, ProductCode: v.ProductCode,
Name: v.Name, Name: v.Name,
OtherName: v.OtherName, OtherName: v.OtherName,
Description: v.Description, Description: v.Description,
LastUpdated: v.LastChange, OtherDescription: v.OtherDescription,
LastUpdated: v.LastChange,
}) })
} }