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

View file

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

View file

@ -105,6 +105,11 @@
/>
</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 *ngFor="let sub of listSubMenuProductcodes()">
<button
@ -153,17 +158,12 @@
<a
href="{{ department }}/recipe/{{ productCode }}#name"
class="btn btn-xs"
>1</a
>About</a
>
<a
href="{{ department }}/recipe/{{ productCode }}#recipeList"
class="btn btn-xs"
>2</a
>
<a
href="{{ department }}/recipe/{{ productCode }}#toppingSet"
class="btn btn-xs"
>3</a
>Recipe List</a
>
</div>

View file

@ -279,14 +279,6 @@ export class RecipeDetailsComponent implements OnInit {
// console.log('Recipe List Form Changed', repl);
this.repl = repl[1] 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++) {
// check at the same index
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 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">
<app-recipe-topping
[productCode]="productCode"

View file

@ -509,12 +509,19 @@ export class RecipeListComponent implements OnInit {
}, 100);
}
openRecipeListEditor(i: number) {
async openRecipeListEditor(i: number) {
if(this.timeoutHandler){
await Promise.resolve();
if (this.timeoutHandler) {
console.log("timeout get", this.timeout);
if(this.timeout >= 20){
this.showDetailRecipeList = true;
if (this.timeout >= 20) {
// 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);

View file

@ -1,38 +1,101 @@
<div [formGroup]="toppingForm">
<div formArrayName="toppingList" *ngFor="let topping of toppingList.controls; let i = index">
<div formGroupName="{{ i }}">
<div
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"/>
<!-- toppingGroup -->
<ng-select
appendTo="body"
[clearable]="false"
[compareWith]="this.compareFunc"
formControlName="groupID"
(close)="getDefaultOfGroup(getGroupIdByIndex(i))"
>
<ng-option
*ngFor="let item of allToppingsDefinitions"
[value]="item.groupId.toString()"
>
<div>{{ item.name }} ({{ item.groupId }})</div>
</ng-option>
</ng-select>
<!-- defaultSelect -->
<ng-select
appendTo="body"
[clearable]="false"
[compareWith]="this.compareFunc"
formControlName="defaultIDSelect"
>
<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 space-x-2">
<h3>Enable</h3>
<input class="toggle" type="checkbox" formControlName="isUse" />
</div>
<div class="flex space-x-2 items-center">
<!-- toppingGroup -->
<ng-select
class="w-52"
appendTo="body"
[clearable]="false"
[compareWith]="this.compareFunc"
formControlName="groupID"
(close)="getDefaultOfGroup(getGroupIdByIndex(i))"
>
<ng-option
*ngFor="let item of allToppingsDefinitions"
[value]="item.groupId.toString()"
>
<div>{{ item.name }} ({{ item.groupId }})</div>
</ng-option>
</ng-select>
<!-- defaultSelect -->
<ng-select
class="w-52"
appendTo="body"
[clearable]="false"
[compareWith]="this.compareFunc"
formControlName="defaultIDSelect"
>
<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 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 *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:
| { 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: {
id: string;
@ -39,8 +39,14 @@ export class RecipeToppingComponent implements OnInit {
}[] = [];
department = this._route.snapshot.paramMap.get('department');
// list group id
listGroupId: any[] = [];
private _toppingSetOriginalArray!: ToppingSet[];
extraTopping: [] = [];
extraToppingDefault: {[key: string]: string} = {};
// form
toppingForm = this._formBuilder.group(
{
@ -76,6 +82,8 @@ export class RecipeToppingComponent implements OnInit {
this._toppingSetOriginalArray = data;
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
if (data.length >= this.index!) {
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
@ -120,27 +128,7 @@ export class RecipeToppingComponent implements OnInit {
});
// emit value changes
this.toppingForm.valueChanges.subscribe((value) => {
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]);
});
this.toppingForm.valueChanges.subscribe((value) => {this.triggerValueChange();});
}
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) => {
@ -192,6 +263,34 @@ export class RecipeToppingComponent implements OnInit {
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 {
isUse: value.isUse,
groupID: value.groupID,

View file

@ -346,6 +346,7 @@
</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.otherDescription }}</td>
<td class="px-6 py-4">
{{ recipe.lastUpdated | date : "dd-MMM-yyyy hh:mm:ss" }}
</td>

View file

@ -73,6 +73,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
'Name',
'Other Name',
'Description',
'Other Description',
'Last Updated',
];
private offset = 0;
@ -116,6 +117,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
materialIds: this.selectMaterialFilter || [],
}))
.subscribe(({ result, hasMore, totalCount }) => {
// console.log("result in scroll", result);
if (this.recipeOverviewList) {
this.recipeOverviewList =
this.recipeOverviewList.concat(result);
@ -143,6 +145,10 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
async ngOnInit(): Promise<void> {
console.log('Trigger onInit where department = ', this.department);
this.recipesDashboard$ = this._recipeService
.getRecipesDashboard({
filename: this._recipeService.getCurrentFile(),
@ -224,9 +230,7 @@ export class RecipesComponent implements OnInit, OnDestroy, AfterViewInit {
},
});
// TODO: get all materials; MaterialSetting + MaterialCode
(await
// TODO: get all materials; MaterialSetting + MaterialCode
this._materialService
.getMaterialCodes())
.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 ==================================
type RecipeOverview struct {
ID int `json:"id"`
ProductCode string `json:"productCode"`
Name string `json:"name"`
OtherName string `json:"otherName"`
Description string `json:"description"`
LastUpdated string `json:"lastUpdated"`
ID int `json:"id"`
ProductCode string `json:"productCode"`
Name string `json:"name"`
OtherName string `json:"otherName"`
Description string `json:"description"`
OtherDescription string `json:"otherDescription"`
LastUpdated string `json:"lastUpdated"`
}
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))
for _, v := range d.currentRecipe[countryID].Recipe01 {
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{}{
"materialId": matSetting.ID,
"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),
})
}

View file

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