Add material code and settings

This commit is contained in:
Kenta420 2023-10-06 15:33:10 +07:00
parent 36be0426f6
commit 498bcf1c24
9 changed files with 279 additions and 60 deletions

View file

@ -3,7 +3,8 @@ export interface Recipe {
MachineSetting: MachineSetting; MachineSetting: MachineSetting;
Recipe01: Recipe01[]; Recipe01: Recipe01[];
Topping: Topping; Topping: Topping;
MaterailCode: MaterailCode[]; MaterialCode: MaterialCode[];
MaterialSetting: MaterialSetting[];
} }
export interface MachineSetting { export interface MachineSetting {
@ -14,10 +15,10 @@ export interface MachineSetting {
temperatureMin: string; temperatureMin: string;
} }
export interface MaterailCode { export interface MaterialCode {
PackageDescription: string; PackageDescription: string;
RefillValuePerStep: string; RefillValuePerStep: string;
materialID: string; materialID: number;
materialCode: string; materialCode: string;
} }
@ -25,7 +26,7 @@ export interface Recipe01 {
Description: string; Description: string;
ExtendID: string; ExtendID: string;
OnTOP: string; OnTOP: string;
LastChange: string; LastChange: Date;
MenuStatus: string; MenuStatus: string;
RemainingCups: string; RemainingCups: string;
StringParam: string; StringParam: string;
@ -97,8 +98,8 @@ export interface MatRecipe {
MixOrder: string; MixOrder: string;
FeedParameter: string; FeedParameter: string;
FeedPattern: string; FeedPattern: string;
isUse: string; isUse: boolean;
materialPathId: string; materialPathId: number;
powderGram: string; powderGram: string;
powderTime: string; powderTime: string;
stirTime: string; stirTime: string;

View file

@ -0,0 +1,41 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MaterialCode, MaterialSetting } from '../models/recipe.model';
import { environment } from 'src/environments/environment';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class MaterialService {
constructor(private _httpClient: HttpClient) {}
getMaterialCodes(
matIds?: number[],
version?: string
): Observable<MaterialCode[]> {
return this._httpClient.get<MaterialCode[]>(
`${environment.api}/materials/code`,
{
params: {
version: version || '',
mat_ids: matIds?.join(',') || '',
},
withCredentials: true,
}
);
}
getMaterialSettingById(
id: number,
version?: string
): Observable<MaterialSetting> {
return this._httpClient.get<MaterialSetting>(
`${environment.api}/materials/setting/${id}`,
{
params: {
version: version || '',
},
withCredentials: true,
}
);
}
}

View file

@ -4,20 +4,23 @@
class="block col-span-1 p-6 bg-white border border-gray-200 rounded-lg shadow" class="block col-span-1 p-6 bg-white border border-gray-200 rounded-lg shadow"
> >
<div *ngIf="isLoaded; else indicator" [@inOutAnimation]> <div *ngIf="isLoaded; else indicator" [@inOutAnimation]>
<h5 class="mb-2 text-xl font-bold text-gray-900"> <div class="flex flex-wrap">
{{ originalRecipeDetail?.name }} | <h5 class="mb-2 text-xl font-bold text-gray-900">
{{ originalRecipeDetail?.productCode }} {{ originalRecipeDetail.name }}
</h5> </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 }}
</h5>
</div>
<div class="flex items-center mb-2"> <div class="flex items-center mb-2">
<div class="flex items-center mr-4">
<p class="text-sm text-gray-500">สูตร</p>
<p class="ml-2 text-sm text-gray-900">
{{ originalRecipeDetail?.name }}
</p>
</div>
<div class="flex items-center"> <div class="flex items-center">
<p class="text-sm text-gray-500">ประเภท</p> <p class="text-sm text-gray-500">Last Modify</p>
<p class="ml-2 text-sm text-gray-900">เครื่องดื่ม</p> <p class="ml-2 text-sm text-gray-900">
{{
originalRecipeDetail.lastModified | date : "dd/MM/yyyy HH:mm:ss"
}}
</p>
</div> </div>
</div> </div>
<div class="flex p-3 items-center justify-center w-full"> <div class="flex p-3 items-center justify-center w-full">
@ -103,26 +106,38 @@
</div> </div>
</ng-template> </ng-template>
</div> </div>
<div <div class="col-span-3 max-h-[300px] overflow-auto mb-4 rounded">
class="flex items-center justify-center h-48 mb-4 rounded bg-gray-50 dark:bg-gray-800" <table class="table">
> <thead>
<p class="text-2xl text-gray-400 dark:text-gray-500"> <tr class="bg-gray-200">
<svg <th class="px-6 py-3">Enable</th>
class="w-3.5 h-3.5" <th class="px-6 py-3">Material ID</th>
aria-hidden="true" <th class="px-6 py-3">Material Name</th>
xmlns="http://www.w3.org/2000/svg" </tr>
fill="none" </thead>
viewBox="0 0 18 18" <tbody *ngIf="isMatLoaded; else indicator">
> <tr
<path *ngFor="let mat of originalRecipeDetail?.matData"
stroke="currentColor" class="bg-white la border-b hover:bg-secondary"
stroke-linecap="round" >
stroke-linejoin="round" <td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
stroke-width="2" <label>
d="M9 1v16M1 9h16" <input
/> type="checkbox"
</svg> class="toggle toggle-sm"
</p> [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> </div>
<div class="grid grid-cols-2 gap-4 mb-4"> <div class="grid grid-cols-2 gap-4 mb-4">
<div <div
@ -318,7 +333,7 @@
</div> </div>
</div> </div>
<div <div
class="sticky bottom-0 col-span-3 flex justify-end bg-white rounded-full shadow-xl p-3" class="sticky bottom-0 col-span-3 flex justify-end bg-white rounded-full drop-shadow-2xl p-3"
> >
<button <button
(click)="onPressConfirmClose()" (click)="onPressConfirmClose()"

View file

@ -1,15 +1,18 @@
import { NgIf } from '@angular/common'; import { DatePipe, NgFor, NgIf } from '@angular/common';
import { Component, EventEmitter, OnInit } from '@angular/core'; import { Component, EventEmitter, OnInit } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { delay } from 'rxjs'; import { delay, finalize } from 'rxjs';
import { RecipeService } from 'src/app/core/services/recipe.service'; import { RecipeService } from 'src/app/core/services/recipe.service';
import { ConfirmModal } from 'src/app/shared/modal/confirm/confirm-modal.component'; import { ConfirmModal } from 'src/app/shared/modal/confirm/confirm-modal.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { animate, style, transition, trigger } from '@angular/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 { interface RecipeDetail {
lastModified: Date;
productCode: string; productCode: string;
name: string; name: string;
otherName: string; otherName: string;
@ -19,6 +22,14 @@ interface RecipeDetail {
isUse: boolean; isUse: boolean;
isShow: boolean; isShow: boolean;
disable: boolean; disable: boolean;
recipes: MatRecipe[];
matData?: MaterialData[];
}
interface MaterialData {
id: number;
name: string;
enable: boolean;
} }
interface RecipeMetaData { interface RecipeMetaData {
@ -34,7 +45,14 @@ interface RecipeMetaData {
selector: 'app-recipe-details', selector: 'app-recipe-details',
templateUrl: './recipe-details.component.html', templateUrl: './recipe-details.component.html',
standalone: true, standalone: true,
imports: [NgIf, RouterLink, ReactiveFormsModule, ConfirmModal], imports: [
NgIf,
NgFor,
RouterLink,
ReactiveFormsModule,
ConfirmModal,
DatePipe,
],
animations: [ animations: [
trigger('inOutAnimation', [ trigger('inOutAnimation', [
transition(':enter', [ transition(':enter', [
@ -48,14 +66,16 @@ export class RecipeDetailsComponent implements OnInit {
title: string = 'Recipe Detail'; title: string = 'Recipe Detail';
recipeMetaData: RecipeMetaData | null = null; recipeMetaData: RecipeMetaData | null = null;
originalRecipeDetail: RecipeDetail | null = null; originalRecipeDetail!: RecipeDetail;
isLoaded: boolean = false; isLoaded: boolean = false;
isMatLoaded: boolean = false;
constructor( constructor(
private _route: ActivatedRoute, private _route: ActivatedRoute,
private _router: Router, private _router: Router,
private _recipeService: RecipeService private _recipeService: RecipeService,
private _materialService: MaterialService
) {} ) {}
recipeDetail = new FormGroup({ recipeDetail = new FormGroup({
@ -73,6 +93,7 @@ export class RecipeDetailsComponent implements OnInit {
ngOnInit() { ngOnInit() {
this._recipeService this._recipeService
.getRecipesById(this._route.snapshot.params['productCode']) .getRecipesById(this._route.snapshot.params['productCode'])
.pipe(finalize(() => {}))
.subscribe(({ recipe, recipeMetaData }) => { .subscribe(({ recipe, recipeMetaData }) => {
this.title = recipe.name + ' | ' + recipe.productCode; this.title = recipe.name + ' | ' + recipe.productCode;
this.recipeDetail.patchValue({ this.recipeDetail.patchValue({
@ -87,6 +108,7 @@ export class RecipeDetailsComponent implements OnInit {
disable: recipe.disable, disable: recipe.disable,
}); });
this.originalRecipeDetail = { this.originalRecipeDetail = {
lastModified: recipe.LastChange,
productCode: recipe.productCode, productCode: recipe.productCode,
name: recipe.name, name: recipe.name,
otherName: recipe.otherName, otherName: recipe.otherName,
@ -96,9 +118,27 @@ export class RecipeDetailsComponent implements OnInit {
isUse: recipe.isUse, isUse: recipe.isUse,
isShow: recipe.isShow, isShow: recipe.isShow,
disable: recipe.disable, disable: recipe.disable,
recipes: recipe.recipes,
}; };
this.recipeMetaData = recipeMetaData; this.recipeMetaData = recipeMetaData;
this.isLoaded = true; this.isLoaded = true;
const ids = this.originalRecipeDetail.recipes.map(
(recipe) => recipe.materialPathId
);
this._materialService.getMaterialCodes(ids).subscribe((data) => {
this.originalRecipeDetail.matData = data
.map((item) => {
return {
id: item.materialID,
name: item.PackageDescription,
enable: false,
};
})
.sort((a, b) => (a.id > b.id ? -1 : -1));
this.isMatLoaded = true;
});
}); });
} }

View file

@ -84,12 +84,7 @@ export class DashboardComponent implements OnInit {
); );
} }
constructor( constructor(private _recipeService: RecipeService) {}
private _route: ActivatedRoute,
private _router: Router,
private _userService: UserService,
private _recipeService: RecipeService
) {}
ngOnInit(): void { ngOnInit(): void {
this._recipeService this._recipeService

View file

@ -89,3 +89,49 @@ func (d *Data) GetRecipe(version string) models.Recipe {
func (d *Data) GetRecipe01() []models.Recipe01 { func (d *Data) GetRecipe01() []models.Recipe01 {
return d.recipe.Recipe01 return d.recipe.Recipe01
} }
func (d *Data) GetMaterialSetting(version string) []models.MaterialSetting {
result := make([]models.MaterialSetting, 0)
if version == "" || version == d.CurrentVersion {
copy(result, d.recipe.MaterialSetting)
return result
}
d.CurrentVersion = version
d.recipe = readFile(version)
copy(result, d.recipe.MaterialSetting)
return result
}
func (d *Data) GetMaterialCode(ids []uint64, version string) []models.MaterialCode {
var result []models.MaterialCode
if version == "" || version == d.CurrentVersion {
result = d.recipe.MaterialCode
} else {
d.CurrentVersion = version
d.recipe = readFile(version)
result = d.recipe.MaterialCode
}
if len(ids) == 0 {
return result
}
resultFilter := make([]models.MaterialCode, len(ids))
for _, id := range ids {
if id == 0 {
continue
}
for _, m := range result {
if m.MaterialID == id {
resultFilter = append(resultFilter, m)
break
}
}
}
return resultFilter
}

View file

@ -1,14 +1,15 @@
package models package models
type Recipe struct { type Recipe struct {
Timestamp string `json:"Timestamp"` Timestamp string `json:"Timestamp"`
MachineSetting MatchineSetting `json:"MachineSetting"` MachineSetting MachineSetting `json:"MachineSetting"`
Recipe01 []Recipe01 `json:"Recipe01"` Recipe01 []Recipe01 `json:"Recipe01"`
Topping Topping `json:"Topping"` Topping Topping `json:"Topping"`
MaterailCode []MaterailCode `json:"MaterailCode"` MaterialCode []MaterialCode `json:"MaterialCode"`
MaterialSetting []MaterialSetting `json:"MaterialSetting"`
} }
type MatchineSetting struct { type MachineSetting struct {
RecipeTag string `json:"RecipeTag"` RecipeTag string `json:"RecipeTag"`
StrTextShowError []string `json:"strTextShowError"` StrTextShowError []string `json:"strTextShowError"`
ConfigNumber int `json:"configNumber"` ConfigNumber int `json:"configNumber"`
@ -16,10 +17,10 @@ type MatchineSetting struct {
TemperatureMin int `json:"temperatureMin"` TemperatureMin int `json:"temperatureMin"`
} }
type MaterailCode struct { type MaterialCode struct {
PackageDescription string `json:"PackageDescription"` PackageDescription string `json:"PackageDescription"`
RefillValuePerStep int `json:"RefillValuePerStep"` RefillValuePerStep int `json:"RefillValuePerStep"`
MaterialID int `json:"materialID"` MaterialID uint64 `json:"materialID"`
MaterialCode string `json:"materialCode"` MaterialCode string `json:"materialCode"`
} }
@ -40,7 +41,7 @@ type MaterialSetting struct {
SodaChannel bool `json:"SodaChannel"` SodaChannel bool `json:"SodaChannel"`
StockAdjust int `json:"StockAdjust"` StockAdjust int `json:"StockAdjust"`
SyrupChannel bool `json:"SyrupChannel"` SyrupChannel bool `json:"SyrupChannel"`
ID int `json:"id"` ID uint64 `json:"id"`
IDAlternate int `json:"idAlternate"` IDAlternate int `json:"idAlternate"`
IsUse bool `json:"isUse"` IsUse bool `json:"isUse"`
PayRettryMaxCount int `json:"pay_rettry_max_count"` PayRettryMaxCount int `json:"pay_rettry_max_count"`

View file

@ -0,0 +1,76 @@
package routers
import (
"encoding/json"
"net/http"
"recipe-manager/data"
"recipe-manager/models"
"strconv"
"strings"
"github.com/go-chi/chi/v5"
)
type MaterialRouter struct {
data *data.Data
}
func NewMaterialRouter(data *data.Data) *MaterialRouter {
return &MaterialRouter{
data: data,
}
}
func (mr *MaterialRouter) Route(r chi.Router) {
r.Route("/materials", func(r chi.Router) {
r.Get("/code", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
version := r.URL.Query().Get("version")
matIDs := r.URL.Query().Get("mat_ids")
var matIDsUint []uint64
for _, v := range strings.Split(matIDs, ",") {
matIDUint, err := strconv.ParseUint(v, 10, 64)
if err != nil || matIDUint == 0 {
continue
}
matIDsUint = append(matIDsUint, matIDUint)
}
material := mr.data.GetMaterialCode(matIDsUint, version)
json.NewEncoder(w).Encode(material)
})
r.Get("/setting/{mat_id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
version := r.URL.Query().Get("version")
material := mr.data.GetMaterialSetting(version)
matID := chi.URLParam(r, "mat_id")
matIDuint, err := strconv.ParseUint(matID, 10, 64)
if err != nil {
http.Error(w, "Invalid material id", http.StatusBadRequest)
return
}
var matSetting models.MaterialSetting
for _, mat := range material {
if mat.ID == matIDuint {
matSetting = mat
break
}
}
json.NewEncoder(w).Encode(matSetting)
})
})
}

View file

@ -437,7 +437,6 @@ func (s *Server) createHandler() {
Log.Debug("Scan dir completed < ", zap.String("path", r.RequestURI)) Log.Debug("Scan dir completed < ", zap.String("path", r.RequestURI))
}) })
// Recipe Router
sheetService, err := sheet.NewSheetService(context.Background(), s.cfg) sheetService, err := sheet.NewSheetService(context.Background(), s.cfg)
if err != nil { if err != nil {
@ -445,9 +444,14 @@ func (s *Server) createHandler() {
return return
} }
// Recipe Router
rr := routers.NewRecipeRouter(database, sheetService) rr := routers.NewRecipeRouter(database, sheetService)
rr.Route(r) rr.Route(r)
// Material Router
mr := routers.NewMaterialRouter(database)
mr.Route(r)
}) })
r.NotFound(func(w http.ResponseWriter, r *http.Request) { r.NotFound(func(w http.ResponseWriter, r *http.Request) {