adjust mat selection

This commit is contained in:
pakintada@gmail.com 2023-12-29 16:10:57 +07:00
parent 17030c72ce
commit bf693aab2a
15 changed files with 548 additions and 186 deletions

View file

@ -26,6 +26,25 @@ export class MaterialService {
);
}
getFullMaterialDetail(
country?: string,
filename?: string
): Observable<{
"materialId": number,
"name": string,
"type": string
}[] | null>{
console.log("getFullMaterialDetail", country, filename);
return this._httpClient.get<{
"materialId": number,
"name": string,
"type": string
}[]>(`${environment.api}/materials/full/${country}/${filename}`, {
withCredentials: true,
});
}
getMaterialSettingById(
id: number,
country?: string,

View file

@ -0,0 +1,25 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Topping } from '../models/recipe.model';
@Injectable({
providedIn: 'root',
})
export class ToppingService {
constructor(private _httpClient: HttpClient) {}
getToppings(country: string, filename: string): Observable<Topping> {
return this._httpClient.get<Topping>(
`${environment.api}/recipes/${country}/${filename}/toppings`,
{
params: {
country: country,
filename: filename,
},
withCredentials: true
}
);
}
}

View file

@ -72,7 +72,7 @@
<input
type="text"
class="input input-sm input-bordered input-ghost w-full"
formControlName="Description"
formControlName="description"
/>
</div>
<div class="flex items-center">
@ -96,6 +96,7 @@
[productCode]="productCode"
(recipeListFormChange)="onRecipeListFormChange($event)"
></app-recipe-list>
<div *ngFor=""></div>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div

View file

@ -10,10 +10,12 @@ import { RecipeListComponent } from './recipe-list/recipe-list.component';
import {
RecipeDetail,
RecipeDetailMat,
Topping,
} from 'src/app/core/models/recipe.model';
import { ActionRecord } from 'src/app/shared/actionRecord/actionRecord';
import { UserService } from 'src/app/core/services/user.service';
import { UserPermissions } from 'src/app/core/auth/userPermissions';
import { ToppingService } from 'src/app/core/services/topping.service';
@Component({
selector: 'app-recipe-details',
@ -59,6 +61,7 @@ export class RecipeDetailsComponent implements OnInit {
private _route: ActivatedRoute,
private _router: Router,
private _recipeService: RecipeService,
private _toppingService: ToppingService,
private _userService: UserService
) {}
@ -68,7 +71,7 @@ export class RecipeDetailsComponent implements OnInit {
productCode: '',
name: '',
otherName: '',
Description: '',
description: '',
otherDescription: '',
lastModified: new Date(),
price: 0,
@ -79,6 +82,8 @@ export class RecipeDetailsComponent implements OnInit {
repl = []
topping: Topping | null = null;
ngOnInit() {
this.productCode = this._route.snapshot.params['productCode'];
@ -96,6 +101,11 @@ export class RecipeDetailsComponent implements OnInit {
this.recipeDetailForm.valueChanges.subscribe(this.onRecipeDetailFormChange);
this._toppingService.getToppings(this.department, this._recipeService.getCurrentFile()).subscribe((data) => {
this.topping = data;
})
// snap recipe detail form value
this.actionRecord.registerOnAddAction((currAction, allAction) => {
@ -132,7 +142,7 @@ export class RecipeDetailsComponent implements OnInit {
productCode: this.productCode,
name: this.recipeDetailForm.getRawValue().name != this.recipeOriginalDetail.name ? this.recipeDetailForm.getRawValue().name : this.recipeOriginalDetail.name,
otherName: this.recipeDetailForm.getRawValue().otherName != this.recipeOriginalDetail.otherName ? this.recipeDetailForm.getRawValue().otherName : this.recipeOriginalDetail.otherName,
Description: this.recipeDetailForm.getRawValue().Description != this.recipeOriginalDetail.Description ? this.recipeDetailForm.getRawValue().Description : this.recipeOriginalDetail.Description,
description: this.recipeDetailForm.getRawValue().description != this.recipeOriginalDetail.description ? this.recipeDetailForm.getRawValue().description : this.recipeOriginalDetail.description,
otherDescription: this.recipeDetailForm.getRawValue().otherDescription != this.recipeOriginalDetail.otherDescription ? this.recipeDetailForm.getRawValue().otherDescription : this.recipeOriginalDetail.otherDescription,
LastChange: this.recipeDetailForm.getRawValue().lastModified != this.recipeOriginalDetail.lastModified ? this.recipeDetailForm.getRawValue().lastModified : this.recipeOriginalDetail.lastModified,
price: this.recipeDetailForm.getRawValue().price != this.recipeOriginalDetail.price ? this.recipeDetailForm.getRawValue().price : this.recipeOriginalDetail.price,

View file

@ -23,7 +23,12 @@
<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" />
<input
type="text"
class="input"
formControlName="materialPathId"
(click)="openMaterialList(i)"
/>
</td>
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
<input type="text" class="input" formControlName="name" />
@ -55,3 +60,32 @@
</tr>
</tbody>
</table>
<!-- show material selector modal -->
<input
type="checkbox"
id="material_selector"
class="modal-toggle"
#checkBox="ngModel"
[(ngModel)]="showMaterialSelector"
/>
<label for="material_selector" class="modal">
<div class="modal-box max-h-[400px] overflow-scroll">
<div class="flex flex-row m-2 modal">
<p class="font-bold text-lg m-2">Materials</p>
</div>
<div
*ngFor="let material of materialList"
class="flex flex-row m-2 overflow-y-scroll"
>
<button
class="btn bg-primary btn-md border-2 text-base text-gray-700"
(click)="selectMaterial(currentSelectRecipeList!, material)"
>
{{ material.materialID }}
</button>
<h3 class="m-3">{{ material.PackageDescription }}</h3>
</div>
</div>
</label>

View file

@ -5,33 +5,44 @@ import {
FormBuilder,
FormControl,
FormGroup,
NgModel,
ReactiveFormsModule,
} from '@angular/forms';
import { forEach, isEqual, sortBy } from 'lodash';
import { first } from 'rxjs';
import { UserPermissions } from 'src/app/core/auth/userPermissions';
import {
MaterialCode,
RecipeDetail,
RecipeDetailMat,
} from 'src/app/core/models/recipe.model';
import { MaterialService } from 'src/app/core/services/material.service';
import { RecipeService } from 'src/app/core/services/recipe.service';
import { UserService } from 'src/app/core/services/user.service';
import { Action, ActionRecord } from 'src/app/shared/actionRecord/actionRecord';
import { FormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
@Component({
selector: 'app-recipe-list',
templateUrl: './recipe-list.component.html',
standalone: true,
imports: [NgIf, NgFor, ReactiveFormsModule],
imports: [NgIf, NgFor, ReactiveFormsModule, FormsModule],
})
export class RecipeListComponent implements OnInit {
@Input({ required: true }) productCode!: string;
@Output() recipeListFormChange = new EventEmitter<unknown[]>();
materialList: MaterialCode[] = [];
showMaterialSelector: boolean = false;
currentSelectRecipeList: number | null = null;
isMatLoaded: boolean = false;
constructor(
private _recipeService: RecipeService,
private _materialService: MaterialService,
private _formBuilder: FormBuilder,
private _userService: UserService
) {}
@ -97,6 +108,10 @@ export class RecipeListComponent implements OnInit {
});
this._materialService.getMaterialCodes().subscribe((materials) => {
this.materialList = materials;
})
}
get recipeListData(): FormArray {
@ -106,4 +121,18 @@ export class RecipeListComponent implements OnInit {
isEditable(){
return this._userService.getCurrentUser()!.permissions.includes(UserPermissions.EDITOR);
}
openMaterialList(i: any){
console.log("open material list for ", i);
this.showMaterialSelector = true;
this.currentSelectRecipeList = i;
}
selectMaterial(i: number, material: MaterialCode){
this.showMaterialSelector = false;
this.recipeListData.at(i).get('materialPathId')?.setValue(material.materialID);
console.log("set mat ", material.materialID, "to slot", i);
}
}

View file

@ -141,6 +141,7 @@
<th>Comment</th>
<th>Editor</th>
<th>Date</th>
<th>Based on</th>
</tr>
<tr
class="row hover:bg-secondary"
@ -150,6 +151,7 @@
<td>{{ file.Msg }}</td>
<td>{{ file.Editor }}</td>
<td>{{ file.Created_at }}</td>
<td>{{ file.Relation }}</td>
<button class="btn bg-blue-400" (click)="loadSavedFile(file)">Select</button>
</tr>
</table>
@ -223,7 +225,7 @@
(input)="setSearch($event)"
(keydown.enter)="search($event)"
/>
<ng-select
<!-- <ng-select
[items]="materialList"
class="join-item text-base"
bindLabel="name"
@ -236,6 +238,24 @@
<p class="text-xs">{{ item.name }}</p>
<small class="text-xs text-gray-500">{{ item.id }}</small>
</ng-template>
</ng-select> -->
<!-- test material setting -->
<ng-select
[items]="materialDetail"
class="join-item text-base"
bindLabel="name"
bindValue="materialId"
[searchable]="true"
[multiple]="true"
[searchFn]="filterMaterial"
[closeOnSelect]="false"
[(ngModel)]="selectMaterialFilter"
>
<ng-template ng-option-tmp let-item="item">
<p class="text-xs">{{ item.name }} ({{ item.materialId }})</p>
<small class="text-xs text-gray-500">{{ item.materialId }}</small>
</ng-template>
</ng-select>
<button class="btn join-item" (click)="search($event)">
Search

View file

@ -30,6 +30,7 @@ import { FormsModule } from '@angular/forms';
import { MaterialService } from 'src/app/core/services/material.service';
import { UserService } from 'src/app/core/services/user.service';
import { UserPermissions } from 'src/app/core/auth/userPermissions';
import { ToppingService } from 'src/app/core/services/topping.service';
@Component({
selector: 'app-recipes',
@ -50,6 +51,19 @@ export class RecipesComponent implements OnInit, OnDestroy {
selectMaterialFilter: number[] | null = null;
materialList: { id: number; name: string | number }[] | null = null;
materialDetail:
| {
materialId: number;
name: string;
type: string;
}[]
| null = null;
toppings: {
toppingGroup: {} | null;
toppingList: {} | null;
} | null = null;
tableHeads: string[] = [
'Product Code',
'Name',
@ -73,7 +87,6 @@ export class RecipesComponent implements OnInit, OnDestroy {
department: string = this.route.parent!.snapshot.params['department'];
@ViewChild('table', { static: false }) set content(table: ElementRef) {
// expose element ref for other fn
@ -117,6 +130,7 @@ export class RecipesComponent implements OnInit, OnDestroy {
constructor(
private _recipeService: RecipeService,
private _materialService: MaterialService,
private _toppingService: ToppingService,
private route: ActivatedRoute,
private _userService: UserService
) {}
@ -147,26 +161,38 @@ export class RecipesComponent implements OnInit, OnDestroy {
})
);
this._recipeService.getSavedTmp(
this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile()
).subscribe({
next: (files:any) => {
console.log("Obtain saves: ", typeof files, files);
if(files != undefined && typeof files === 'object'){
if(files.files != null){
console.log("Obtain saves object: ", files.files[0], typeof files);
this._recipeService
.getSavedTmp(
this._recipeService.getCurrentCountry(),
this._recipeService.getCurrentFile()
)
.subscribe({
next: (files: any) => {
console.log('Obtain saves: ', typeof files, files);
if (files != undefined && typeof files === 'object') {
if (files.files != null) {
console.log(
'Obtain saves object: ',
files.files[0],
typeof files
);
this.savedTmpfiles = files.files;
} else {
this.showSaveNoti = false;
this.savedTmpfiles = [];
console.log(this.showSaveNoti, this.savedTmpfiles);
}
// let svf = (document.getElementById('select_savefile_modal') as HTMLInputElement)!.checked;
// console.log("isSavedModalOpened",svf)
} else {
this.showSaveNoti = false;
this.savedTmpfiles = [];
console.log(this.showSaveNoti, this.savedTmpfiles);
}
},
});
},
})
// TODO: get all materials; MaterialSetting + MaterialCode
this._materialService
.getMaterialCodes()
.pipe(
@ -181,10 +207,31 @@ export class RecipesComponent implements OnInit, OnDestroy {
this.materialList = materials;
});
this._materialService
.getFullMaterialDetail(
this.department,
this._recipeService.getCurrentFile()
)
.subscribe((mat) => {
this.materialDetail = mat;
console.log(this.materialDetail?.length);
});
this._toppingService
.getToppings(this.department, this._recipeService.getCurrentFile())
.subscribe((tp) => {
this.toppings = {
toppingGroup: tp.ToppingGroup,
toppingList: tp.ToppingList,
};
// console.log(this.toppings);
});
this.initRecipeSelection();
}
setSearch(event: Event) {
console.log((event.target as HTMLInputElement).value);
this.searchStr = (event.target as HTMLInputElement).value;
}
@ -268,18 +315,18 @@ export class RecipesComponent implements OnInit, OnDestroy {
// do perms
let accessibleCountries = [];
for(let country of countries){
if(this.grantAccessCountry(country)){
for (let country of countries) {
if (this.grantAccessCountry(country)) {
accessibleCountries.push(country);
}
}
console.log('granted accessible countries', accessibleCountries);
if (accessibleCountries.length > 0) {
this.recipeCountryFiltered = lodash.filter(accessibleCountries, (country) =>
country.toLowerCase().includes(c.toLowerCase())
this.recipeCountryFiltered = lodash.filter(
accessibleCountries,
(country) => country.toLowerCase().includes(c.toLowerCase())
);
}
}
@ -287,19 +334,35 @@ export class RecipesComponent implements OnInit, OnDestroy {
}
// Grant access to view a country recipe
grantAccessCountry(countryName: string):boolean{
if(countryName.toLowerCase().startsWith("tha")
&& this._userService.getCurrentUser()!.permissions.includes(UserPermissions.THAI_PERMISSION)){
grantAccessCountry(countryName: string): boolean {
if (
countryName.toLowerCase().startsWith('tha') &&
this._userService
.getCurrentUser()!
.permissions.includes(UserPermissions.THAI_PERMISSION)
) {
return true;
} else if (
countryName.toLowerCase().startsWith('aus') &&
this._userService
.getCurrentUser()!
.permissions.includes(UserPermissions.AUS_PERMISSION)
) {
return true;
} else if (
countryName.toLowerCase().startsWith('malay') &&
this._userService
.getCurrentUser()!
.permissions.includes(UserPermissions.MALAY_PERMISSION)
) {
return true;
} else if (
countryName.toLowerCase().startsWith('alpha-3') &&
this._userService
.getCurrentUser()!
.permissions.includes(UserPermissions.ALPHA3_PERMISSION)
) {
return true;
} else if (countryName.toLowerCase().startsWith("aus")
&& this._userService.getCurrentUser()!.permissions.includes(UserPermissions.AUS_PERMISSION)){
return true;
} else if (countryName.toLowerCase().startsWith("malay")
&& this._userService.getCurrentUser()!.permissions.includes(UserPermissions.MALAY_PERMISSION)){
return true;
} else if (countryName.toLowerCase().startsWith("alpha-3")
&& this._userService.getCurrentUser()!.permissions.includes(UserPermissions.ALPHA3_PERMISSION)){
return true;
}
return false;
}
@ -379,11 +442,11 @@ export class RecipesComponent implements OnInit, OnDestroy {
}
// get tmp files
openTmpFilesList(){
openTmpFilesList() {
// TODO: get tmp files to display or
}
openLoadSaves(){
openLoadSaves() {
this.showSaveNoti = false;
this.saveTab = true;
}
@ -395,34 +458,38 @@ export class RecipesComponent implements OnInit, OnDestroy {
let country = this.department!;
switch(country){
case "tha":
country = "Thailand";
switch (country) {
case 'tha':
country = 'Thailand';
break;
case "aus":
country = "Australia";
case 'aus':
country = 'Australia';
break;
case "mys":
country = "Malaysia";
case 'mys':
country = 'Malaysia';
break;
case "alpha-3":
country = "alpha-3";
case 'alpha-3':
country = 'alpha-3';
break;
}
console.log(file_commit.Change_file.split("/")[2]);
console.log(file_commit.Change_file.split('/')[2]);
this._recipeService
.getRecipeOverview({
offset: this.offset,
take: this.take,
search: this.oldSearchStr,
filename: file_commit.Change_file.split("/")[2],
filename: file_commit.Change_file.split('/')[2],
country: country,
materialIds: this.selectMaterialFilter || [],
}).subscribe(({ result, hasMore, totalCount }) => {
})
.subscribe(({ result, hasMore, totalCount }) => {
console.log('loadSavedFile', result);
this._recipeService.setCurrentFile(file_commit.Change_file.split("/")[2]);
this._recipeService.setCurrentFile(
file_commit.Change_file.split('/')[2]
);
window.location.reload();
});
}
@ -434,4 +501,8 @@ export class RecipesComponent implements OnInit, OnDestroy {
this.currentFileFilterSubScription.unsubscribe();
}
}
filterMaterial = (term: string, item: any) =>
item.name.toLowerCase().includes(term.toLowerCase()) ||
item.materialId.toString().includes(term);
}