Ajout des recettes et des mélanges au frontend Angular.
This commit is contained in:
parent
8fce20f978
commit
68b6ee8855
|
@ -6596,6 +6596,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"js-joda": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/js-joda/-/js-joda-1.11.0.tgz",
|
||||
"integrity": "sha512-/HJpRhwP2fPyuSsCaZuoVJuaSIt8tUXykV4wOMRXrFk7RP9h9VWaFdS9YHKdMepxb/3TdXpL6IhfC9L0sqYVBw=="
|
||||
},
|
||||
"js-levenshtein": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
|
||||
|
@ -7121,6 +7126,11 @@
|
|||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"material-design-icons": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz",
|
||||
"integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
"@mdi/angular-material": "^5.7.55",
|
||||
"bootstrap": "^4.5.2",
|
||||
"copy-webpack-plugin": "^6.2.1",
|
||||
"js-joda": "^1.11.0",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"ngx-material-file-input": "^2.1.1",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
|
|
|
@ -3,6 +3,7 @@ import {isPlatformBrowser} from "@angular/common";
|
|||
import {AppState} from "./modules/shared/app-state";
|
||||
import {Observable} from "rxjs";
|
||||
import {SubscribingComponent} from "./modules/shared/components/subscribing.component";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-root',
|
||||
|
@ -15,9 +16,11 @@ export class AppComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId: object,
|
||||
private appState: AppState
|
||||
private appState: AppState,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
|
|
@ -6,8 +6,8 @@ import {AppComponent} from './app.component';
|
|||
import {MatIconRegistry} from "@angular/material/icon";
|
||||
import {SharedModule} from "./modules/shared/shared.module";
|
||||
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||
import { CatalogComponent } from './pages/catalog/catalog.component';
|
||||
import { CompanyModule } from './modules/company/company.module';
|
||||
import {CatalogComponent} from './pages/catalog/catalog.component';
|
||||
import {CompanyModule} from './modules/company/company.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
@ -24,7 +24,7 @@ export class LoginComponent implements OnInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
if (this.accountService.isLoggedIn()) {
|
||||
this.router.navigate(['/'])
|
||||
this.router.navigate(['/color'])
|
||||
}
|
||||
|
||||
this.idFormControl = this.formBuilder.control(null, Validators.compose([Validators.required, Validators.pattern(new RegExp('^[0-9]+$'))]))
|
||||
|
|
|
@ -1,12 +1,39 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {ListComponent} from "./pages/list/list.component";
|
||||
import {AddComponent} from "./pages/add/add.component";
|
||||
import {EditComponent} from "./pages/edit/edit.component";
|
||||
import {ExploreComponent} from "./pages/explore/explore.component";
|
||||
import {MixEditComponent} from "./pages/mix/mix-edit/mix-edit.component";
|
||||
import {MixAddComponent} from "./pages/mix/mix-add/mix-add.component";
|
||||
|
||||
import { ColorsComponent } from './colors.component';
|
||||
|
||||
const routes: Routes = [{ path: '', component: ColorsComponent }];
|
||||
const routes: Routes = [{
|
||||
path: 'list',
|
||||
component: ListComponent
|
||||
}, {
|
||||
path: 'add',
|
||||
component: AddComponent
|
||||
}, {
|
||||
path: 'edit/:id',
|
||||
component: EditComponent
|
||||
}, {
|
||||
path: 'add/mix/:recipeId',
|
||||
component: MixAddComponent
|
||||
}, {
|
||||
path: 'edit/mix/:recipeId/:id',
|
||||
component: MixEditComponent
|
||||
}, {
|
||||
path: 'explore/:id',
|
||||
component: ExploreComponent
|
||||
}, {
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'list'
|
||||
}]
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class ColorsRoutingModule { }
|
||||
export class ColorsRoutingModule {
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<p>colors works!</p>
|
|
@ -1,25 +0,0 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ColorsComponent } from './colors.component';
|
||||
|
||||
describe('ColorsComponent', () => {
|
||||
let component: ColorsComponent;
|
||||
let fixture: ComponentFixture<ColorsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ColorsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ColorsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'cre-colors',
|
||||
templateUrl: './colors.component.html',
|
||||
styleUrls: ['./colors.component.sass']
|
||||
})
|
||||
export class ColorsComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
}
|
|
@ -1,15 +1,31 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {ColorsRoutingModule} from './colors-routing.module';
|
||||
import {ColorsComponent} from './colors.component';
|
||||
import {SharedModule} from "../shared/shared.module";
|
||||
import {ListComponent} from './pages/list/list.component';
|
||||
import {AddComponent} from './pages/add/add.component';
|
||||
import {EditComponent} from './pages/edit/edit.component';
|
||||
import {MatExpansionModule} from "@angular/material/expansion";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {ExploreComponent} from './pages/explore/explore.component';
|
||||
import {RecipeInfoComponent} from './components/recipe-info/recipe-info.component';
|
||||
import {MixTableComponent} from './components/mix-table/mix-table.component';
|
||||
import {StepListComponent} from './components/step-list/step-list.component';
|
||||
import {StepTableComponent} from './components/step-table/step-table.component';
|
||||
import {MixEditorComponent} from './components/mix-editor/mix-editor.component';
|
||||
import {UnitSelectorComponent} from './components/unit-selector/unit-selector.component';
|
||||
import {MixAddComponent} from './pages/mix/mix-add/mix-add.component';
|
||||
import {MixEditComponent} from './pages/mix/mix-edit/mix-edit.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [ColorsComponent],
|
||||
declarations: [ListComponent, AddComponent, EditComponent, ExploreComponent, RecipeInfoComponent, MixTableComponent, StepListComponent, StepTableComponent, MixEditorComponent, UnitSelectorComponent, MixAddComponent, MixEditComponent],
|
||||
imports: [
|
||||
ColorsRoutingModule,
|
||||
SharedModule
|
||||
SharedModule,
|
||||
MatExpansionModule,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class ColorsModule { }
|
||||
export class ColorsModule {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<mat-card *ngIf="recipe" class="x-centered y-centered">
|
||||
<mat-card-header>
|
||||
<mat-card-title *ngIf="!editionMode">Création d'un mélange pour la recette {{recipe.company.name}}
|
||||
- {{recipe.name}}</mat-card-title>
|
||||
<mat-card-title *ngIf="editionMode">Modification du mélange {{mix.mixType.name}} de la
|
||||
recette {{recipe.company.name}} - {{recipe.name}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-form-field>
|
||||
<mat-label>Nom</mat-label>
|
||||
<input matInput type="text" [formControl]="nameControl"/>
|
||||
<mat-icon svgIcon="form-textbox" matSuffix></mat-icon>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Type de produit</mat-label>
|
||||
<mat-select [formControl]="materialTypeControl">
|
||||
<mat-option
|
||||
*ngFor="let materialType of (materialTypes$ | async)"
|
||||
[value]="materialType.id">
|
||||
{{materialType.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<ng-container *ngTemplateOutlet="mixEditor"></ng-container>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" routerLink="/color/edit/{{recipeId}}">Retour</button>
|
||||
<button *ngIf="editionMode" mat-raised-button color="warn" (click)="delete()">Supprimer</button>
|
||||
<button mat-raised-button color="accent" [disabled]="!form.valid" (click)="submit()">Enregistrer</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
<ng-template #mixEditor>
|
||||
<table #mixTable mat-table [dataSource]="mixMaterials">
|
||||
<ng-container matColumnDef="position">
|
||||
<th mat-header-cell *matHeaderCellDef>Position</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial; let i = index">
|
||||
{{i + 1}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="material">
|
||||
<th mat-header-cell *matHeaderCellDef>Produit</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">
|
||||
<mat-form-field *ngIf="materials">
|
||||
<mat-select [(ngModel)]="mixMaterial.materialId">
|
||||
<mat-option *ngFor="let material of materials"
|
||||
[value]="material.id">{{material.name}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">
|
||||
<mat-form-field>
|
||||
<input matInput type="number" step="0.001" [(ngModel)]="mixMaterial.quantity"/>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="units">
|
||||
<th mat-header-cell *matHeaderCellDef>Unités</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial" class="units-wrapper">
|
||||
<ng-container *ngIf="materials">
|
||||
<ng-container *ngIf="materialUsePercentages(mixMaterial)">%</ng-container>
|
||||
<ng-container *ngIf="!materialUsePercentages(mixMaterial)">
|
||||
<ng-container *ngIf="!hoveredMixMaterial || hoveredMixMaterial != mixMaterial">
|
||||
<span>{{units}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="hoveredMixMaterial && hoveredMixMaterial == mixMaterial">
|
||||
<cre-unit-selector [(unit)]="units" [showLabel]="false" [short]="true"></cre-unit-selector>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="buttonRemove">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<button mat-raised-button color="accent" (click)="addRow()">Ajouter</button>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial; let i = index">
|
||||
<ng-container *ngIf="hoveredMixMaterial && hoveredMixMaterial == mixMaterial">
|
||||
<button mat-raised-button color="warn" (click)="removeRow(i)">Retirer</button>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="columns"></tr>
|
||||
<tr mat-row *matRowDef="let mixMaterial; columns: columns" (mouseover)="hoveredMixMaterial = mixMaterial"></tr>
|
||||
</table>
|
||||
</ng-template>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
td.units-wrapper
|
||||
width: 3rem
|
|
@ -0,0 +1,132 @@
|
|||
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
|
||||
import {Mix, MixMaterial, Recipe} from "../../../shared/model/recipe.model";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {MixService} from "../../services/mix.service";
|
||||
import {Observable} from "rxjs";
|
||||
import {RecipeService} from "../../services/recipe.service";
|
||||
import {Material} from "../../../shared/model/material.model";
|
||||
import {MaterialService} from "../../../material/service/material.service";
|
||||
import {MaterialType} from "../../../shared/model/materialtype.model";
|
||||
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
|
||||
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
|
||||
import {UNIT_MILLILITER} from "../../../shared/units";
|
||||
import {MatTable} from "@angular/material/table";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-mix-editor',
|
||||
templateUrl: './mix-editor.component.html',
|
||||
styleUrls: ['./mix-editor.component.sass']
|
||||
})
|
||||
export class MixEditorComponent extends SubscribingComponent {
|
||||
@ViewChild('mixTable') mixTable: MatTable<MixMaterial>
|
||||
|
||||
@Input() mixId: number | null
|
||||
@Input() recipeId: number | null
|
||||
@Input() materials: Material[]
|
||||
|
||||
@Output() save = new EventEmitter<any>();
|
||||
|
||||
mix: Mix | null
|
||||
recipe: Recipe | null
|
||||
materialTypes$: Observable<MaterialType[]>
|
||||
|
||||
form: FormGroup
|
||||
nameControl: FormControl
|
||||
materialTypeControl: FormControl
|
||||
|
||||
mixMaterials = []
|
||||
editionMode = false
|
||||
units = UNIT_MILLILITER
|
||||
hoveredMixMaterial: MixMaterial | null
|
||||
columns = ['position', 'material', 'quantity', 'units', 'buttonRemove']
|
||||
|
||||
constructor(
|
||||
private mixService: MixService,
|
||||
private recipeService: RecipeService,
|
||||
private materialService: MaterialService,
|
||||
private materialTypeService: MaterialTypeService,
|
||||
private formBuilder: FormBuilder,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
|
||||
this.mixId = this.urlUtils.parseIntUrlParam('id')
|
||||
|
||||
this.subscribe(
|
||||
this.recipeService.getById(this.recipeId),
|
||||
{
|
||||
next: r => {
|
||||
this.recipe = r
|
||||
if (this.mixId) {
|
||||
this.editionMode = true
|
||||
this.subscribe(
|
||||
this.mixService.getById(this.mixId),
|
||||
{
|
||||
next: m => {
|
||||
this.mix = m
|
||||
this.mixMaterials = this.mixService.extractMixMaterials(this.mix)
|
||||
this.generateForm()
|
||||
}, error: err => this.handleNotFoundError(err, '/color/list')
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.mixMaterials.push({})
|
||||
this.generateForm()
|
||||
}
|
||||
},
|
||||
error: err => this.handleNotFoundError(err, '/color/list')
|
||||
}
|
||||
)
|
||||
this.materialTypes$ = this.materialTypeService.all
|
||||
}
|
||||
|
||||
addRow() {
|
||||
this.mixMaterials.push({materialId: null, quantity: null, percents: false})
|
||||
this.mixTable.renderRows()
|
||||
}
|
||||
|
||||
removeRow(position: number) {
|
||||
this.mixMaterials.splice(position, 1)
|
||||
this.mixTable.renderRows()
|
||||
}
|
||||
|
||||
submit() {
|
||||
this.save.emit({
|
||||
name: this.nameControl.value,
|
||||
recipeId: this.recipeId,
|
||||
materialTypeId: this.materialTypeControl.value,
|
||||
mixMaterials: this.mixMaterials,
|
||||
units: this.units
|
||||
})
|
||||
}
|
||||
|
||||
delete() {
|
||||
|
||||
}
|
||||
|
||||
materialUsePercentages(mixMaterial: any) {
|
||||
if (!mixMaterial.materialId) return null
|
||||
const material = this.getMaterialFromId(mixMaterial.materialId);
|
||||
mixMaterial.percents = material && material.materialType.usePercentages
|
||||
return mixMaterial.percents
|
||||
}
|
||||
|
||||
getMaterialFromId(id: number): Material {
|
||||
return id ? this.materials.filter(m => m.id === id)[0] : null
|
||||
}
|
||||
|
||||
private generateForm() {
|
||||
this.nameControl = new FormControl(this.mix ? this.mix.mixType.name : null, Validators.required)
|
||||
this.materialTypeControl = new FormControl(this.mix ? this.mix.mixType.material.materialType.id : null, Validators.required)
|
||||
this.form = this.formBuilder.group({
|
||||
name: this.nameControl,
|
||||
materialType: this.materialTypeControl
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<mat-expansion-panel class="table-title mt-5">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{mix.mixType.name}}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<div class="mix-actions d-flex flex-row justify-content-between">
|
||||
<ng-container *ngIf="!editing">
|
||||
<div class="flex-grow-1">
|
||||
<mat-form-field class="dark">
|
||||
<mat-label>Casier</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="mix.location" (change)="changeLocation($event)"/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<button mat-raised-button color="accent">Imprimer</button>
|
||||
</div>
|
||||
<div>
|
||||
<button mat-raised-button color="accent" (click)="deduct.emit()">Déduire</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="editing">
|
||||
<div class="flex-grow-1"></div>
|
||||
<button mat-raised-button color="accent" routerLink="/color/edit/mix/{{recipe.id}}/{{mix.id}}">Modifier</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-container [ngTemplateOutlet]="mixTable"></ng-container>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<ng-template #mixTable>
|
||||
<table mat-table [dataSource]="mix.mixMaterials">
|
||||
<ng-container matColumnDef="material">
|
||||
<th mat-header-cell *matHeaderCellDef>Produit</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{mixMaterial.material.name}}</td>
|
||||
<td mat-footer-cell *matFooterCellDef></td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="materialType">
|
||||
<th mat-header-cell *matHeaderCellDef>Type</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{mixMaterial.material.materialType.name}}</td>
|
||||
<td mat-footer-cell *matFooterCellDef></td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
type="number"
|
||||
min="0.001"
|
||||
step="0.001"
|
||||
[value]="getComputedQuantityRounded(mixMaterial)"
|
||||
[disabled]="mixMaterial.material.materialType.usePercentages"
|
||||
(change)="changeQuantity($event, mixMaterial)"/>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
<td mat-footer-cell *matFooterCellDef>
|
||||
<mat-form-field>
|
||||
<mat-label>Total</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="number"
|
||||
min="0.001"
|
||||
step="0.001"
|
||||
[value]="round(getTotalQuantity())"
|
||||
(change)="changeQuantity($event, null, true)"/>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantityStatic">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{getComputedQuantityRounded(mixMaterial)}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantityCalculated">
|
||||
<th mat-header-cell *matHeaderCellDef>Calcul</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial; let i = index" [innerHTML]="getCalculatedQuantity(mixMaterial, i)"
|
||||
class="mix-calculation"></td>
|
||||
<td mat-footer-cell *matFooterCellDef></td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantityUnits">
|
||||
<th mat-header-cell *matHeaderCellDef>Unités</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">
|
||||
<ng-container *ngIf="mixMaterial.material.materialType.usePercentages">%</ng-container>
|
||||
<ng-container *ngIf="!mixMaterial.material.materialType.usePercentages">{{units}}</ng-container>
|
||||
</td>
|
||||
<td mat-footer-cell *matFooterCellDef>{{units}}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="mixColumns"></tr>
|
||||
<tr mat-row *matRowDef="let mixMaterial; columns: mixColumns"
|
||||
[class.low-quantity]="!editing && isInLowQuantity(mixMaterial.id)"></tr>
|
||||
<ng-container *ngIf="!editing">
|
||||
<tr mat-footer-row *matFooterRowDef="mixColumns"></tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
</ng-template>
|
|
@ -0,0 +1,22 @@
|
|||
@import '../../../../../custom-theme'
|
||||
|
||||
mat-expansion-panel
|
||||
width: 40rem
|
||||
margin: 2rem 0
|
||||
|
||||
.mix-actions
|
||||
background-color: $color-primary
|
||||
padding: 0 1rem
|
||||
|
||||
div:last-child
|
||||
margin-left: 1rem
|
||||
|
||||
.low-quantity
|
||||
background-color: #ffb3b3
|
||||
|
||||
::ng-deep span.mix-calculated-quantity
|
||||
&:first-child
|
||||
color: green
|
||||
|
||||
&:last-child
|
||||
color: dimgrey
|
|
@ -0,0 +1,131 @@
|
|||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {Mix, MixMaterial, Recipe} from "../../../shared/model/recipe.model";
|
||||
import {Subject} from "rxjs";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {convertMixMaterialQuantity, UNIT_MILLILITER, UNIT_RATIOS} from "../../../shared/units";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-mix-table',
|
||||
templateUrl: './mix-table.component.html',
|
||||
styleUrls: ['./mix-table.component.sass']
|
||||
})
|
||||
export class MixTableComponent extends SubscribingComponent {
|
||||
private readonly COLUMNS = ['material', 'materialType', 'quantity', 'quantityCalculated', 'quantityUnits']
|
||||
private readonly COLUMNS_STATIC = ['material', 'materialType', 'quantityStatic', 'quantityUnits']
|
||||
|
||||
@Input() mix: Mix
|
||||
@Input() recipe: Recipe
|
||||
@Input() units$: Subject<string>
|
||||
@Input() deductErrorBody
|
||||
@Input() editing: boolean
|
||||
@Output() locationChange = new EventEmitter<{ id: number, location: string }>()
|
||||
@Output() quantityChange = new EventEmitter<{ id: number, materialId: number, quantity: number }>()
|
||||
@Output() deduct = new EventEmitter<void>()
|
||||
|
||||
mixColumns = this.COLUMNS
|
||||
units = UNIT_MILLILITER
|
||||
computedQuantities: { id: number, percents: boolean, quantity: number }[] = []
|
||||
|
||||
constructor(
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
|
||||
if (this.editing) {
|
||||
this.mixColumns = this.COLUMNS_STATIC
|
||||
}
|
||||
|
||||
this.mix.mixMaterials.forEach(m => this.computedQuantities.push({
|
||||
id: m.id,
|
||||
percents: m.material.materialType.usePercentages,
|
||||
quantity: m.quantity
|
||||
}))
|
||||
|
||||
this.subscribe(
|
||||
this.units$,
|
||||
{
|
||||
next: u => this.convertQuantities(u)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
changeLocation(event: any) {
|
||||
this.locationChange.emit({id: this.mix.id, location: event.target.value})
|
||||
}
|
||||
|
||||
changeQuantity(event: any, mixMaterial: MixMaterial, isTotal = false) {
|
||||
const newQuantity = parseInt(event.target.value)
|
||||
let ratio = 1
|
||||
if (!isTotal) {
|
||||
const originalQuantity = this.getComputedQuantity(mixMaterial.id)
|
||||
ratio = newQuantity / originalQuantity.quantity
|
||||
} else {
|
||||
ratio = newQuantity / this.getTotalQuantity()
|
||||
}
|
||||
this.computedQuantities.forEach((q, i) => {
|
||||
if (!q.percents) {
|
||||
q.quantity *= ratio
|
||||
}
|
||||
this.emitQuantityChangeEvent(i)
|
||||
})
|
||||
}
|
||||
|
||||
getComputedQuantityRounded(mixMaterial: MixMaterial): number {
|
||||
return this.round(this.getComputedQuantity(mixMaterial.id).quantity)
|
||||
}
|
||||
|
||||
getTotalQuantity(index: number = -1): number {
|
||||
if (index === -1) index = this.computedQuantities.length - 1
|
||||
let totalQuantity = 0
|
||||
for (let i = 0; i <= index; i++) {
|
||||
totalQuantity += this.calculateQuantity(i)
|
||||
}
|
||||
return totalQuantity
|
||||
}
|
||||
|
||||
getCalculatedQuantity(mixMaterial: MixMaterial, index: number): string {
|
||||
const totalQuantity = this.round(this.getTotalQuantity(index))
|
||||
const addedQuantity = this.round(this.calculateQuantity(index))
|
||||
return `<span class="mix-calculated-quantity">+${addedQuantity}</span> <span class="mix-calculated-quantity">(${totalQuantity})</span>`
|
||||
}
|
||||
|
||||
isInLowQuantity(materialId: number): boolean {
|
||||
return this.deductErrorBody[this.mix.id] && this.deductErrorBody[this.mix.id].indexOf(materialId) >= 0
|
||||
}
|
||||
|
||||
round(quantity: number): number {
|
||||
return Math.round(quantity * 1000) / 1000
|
||||
}
|
||||
|
||||
private emitQuantityChangeEvent(index: number) {
|
||||
this.quantityChange.emit({
|
||||
id: this.mix.id,
|
||||
materialId: this.computedQuantities[index].id,
|
||||
quantity: this.calculateQuantity(index)
|
||||
})
|
||||
}
|
||||
|
||||
private convertQuantities(newUnit: string) {
|
||||
this.computedQuantities.forEach(q => convertMixMaterialQuantity(q, this.units, newUnit))
|
||||
this.units = newUnit
|
||||
}
|
||||
|
||||
private getComputedQuantity(id: number): any {
|
||||
return this.computedQuantities.filter(q => q.id == id)[0]
|
||||
}
|
||||
|
||||
private calculateQuantity(index: number): number {
|
||||
const computedQuantity = this.computedQuantities[index]
|
||||
if (!computedQuantity.percents) {
|
||||
return computedQuantity.quantity
|
||||
}
|
||||
return this.computedQuantities[0].quantity * (computedQuantity.quantity / 100)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<div class="recipe-info-wrapper d-flex flex-column">
|
||||
<div class="d-flex flex-column">
|
||||
<h3>{{recipe.company.name}} - {{recipe.name}}</h3>
|
||||
</div>
|
||||
<div class="d-flex flex-row">
|
||||
<div class="d-flex flex-column">
|
||||
<p>Échantillon #{{recipe.sample}}</p>
|
||||
<p *ngIf="recipe.approbationDate">Approuvée le {{recipe.approbationDate}}</p>
|
||||
<div *ngIf="!recipe.approbationDate" class="recipe-not-approved-wrapper d-flex flex-row">
|
||||
<p>Non approuvée</p>
|
||||
<mat-icon svgIcon="alert" class="color-warning"></mat-icon>
|
||||
</div>
|
||||
<p>{{recipe.remark}}</p>
|
||||
</div>
|
||||
<div class="recipe-description">
|
||||
<p>{{recipe.description}}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex-grow-1"></div>
|
||||
|
||||
<mat-icon
|
||||
*ngIf="hasModifications"
|
||||
class="color-warning has-modification-icon"
|
||||
svgIcon="pencil"
|
||||
title="Les modifications apportées n'ont pas été enregistrées"
|
||||
[inline]="true">
|
||||
</mat-icon>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,46 @@
|
|||
.recipe-info-wrapper
|
||||
background-color: black
|
||||
color: white
|
||||
padding: 1rem
|
||||
|
||||
div
|
||||
margin-right: 3rem
|
||||
|
||||
p
|
||||
margin-bottom: 0
|
||||
|
||||
h3
|
||||
font-weight: bold
|
||||
text-decoration: underline
|
||||
text-transform: uppercase
|
||||
|
||||
&.recipe-not-approved-wrapper
|
||||
p
|
||||
margin-top: 1px
|
||||
margin-right: .4em
|
||||
|
||||
&.recipe-description
|
||||
max-width: 30rem
|
||||
|
||||
&.recipe-note
|
||||
margin-right: 0
|
||||
|
||||
mat-form-field
|
||||
width: 100%
|
||||
|
||||
::ng-deep .mat-form-field-wrapper
|
||||
padding-bottom: 0
|
||||
|
||||
::ng-deep .mat-form-field-underline, ::ng-deep .mat-form-field-ripple
|
||||
opacity: 0
|
||||
|
||||
mat-label
|
||||
color: white
|
||||
|
||||
textarea:focus
|
||||
background-color: white
|
||||
color: black
|
||||
|
||||
.has-modification-icon
|
||||
width: 2em !important
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import {Component, Input} from '@angular/core';
|
||||
import {Recipe} from "../../../shared/model/recipe.model";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-recipe-info',
|
||||
templateUrl: './recipe-info.component.html',
|
||||
styleUrls: ['./recipe-info.component.sass']
|
||||
})
|
||||
export class RecipeInfoComponent {
|
||||
@Input() recipe: Recipe
|
||||
@Input() hasModifications: boolean
|
||||
|
||||
constructor() { }
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<mat-card class="mt-5">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Étapes</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="no-action">
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let step of steps;let i = index">
|
||||
{{i + 1}}.<span class="space"></span>{{step.message}}
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
|
@ -0,0 +1,2 @@
|
|||
.space
|
||||
width: 1em
|
|
@ -0,0 +1,11 @@
|
|||
import {Component, Input} from '@angular/core';
|
||||
import {RecipeStep} from "../../../shared/model/recipe.model";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-step-list',
|
||||
templateUrl: './step-list.component.html',
|
||||
styleUrls: ['./step-list.component.sass']
|
||||
})
|
||||
export class StepListComponent {
|
||||
@Input() steps: RecipeStep[]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<mat-expansion-panel class="table-title" [expanded]="true" [disabled]="true">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>Étapes</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<table #stepTable mat-table [dataSource]="steps">
|
||||
<ng-container matColumnDef="position">
|
||||
<th mat-header-cell *matHeaderCellDef>Position</th>
|
||||
<td mat-cell *matCellDef="let step; let i = index">{{i + 1}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="message">
|
||||
<th mat-header-cell *matHeaderCellDef>Message</th>
|
||||
<td mat-cell *matCellDef="let step">
|
||||
<mat-form-field>
|
||||
<input matInput type="text" [(ngModel)]="step.message"/>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="buttonRemove">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<button mat-raised-button color="accent" (click)="addStep()">Ajouter</button>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let step; let i = index">
|
||||
<button mat-raised-button color="warn" (click)="removeStep(i)">Retirer</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="columns"></tr>
|
||||
<tr mat-row *matRowDef="let step; columns: columns"></tr>
|
||||
</table>
|
||||
</mat-expansion-panel>
|
|
@ -0,0 +1,5 @@
|
|||
mat-expansion-panel
|
||||
min-width: 560px
|
||||
|
||||
mat-form-field
|
||||
width: 20rem
|
|
@ -0,0 +1,25 @@
|
|||
import {Component, Input, ViewChild} from '@angular/core';
|
||||
import {RecipeStep} from "../../../shared/model/recipe.model";
|
||||
import {MatTable} from "@angular/material/table";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-step-table',
|
||||
templateUrl: './step-table.component.html',
|
||||
styleUrls: ['./step-table.component.sass']
|
||||
})
|
||||
export class StepTableComponent {
|
||||
@ViewChild('stepTable', {static: true}) stepTable: MatTable<RecipeStep>
|
||||
readonly columns = ['position', 'message', 'buttonRemove']
|
||||
|
||||
@Input() steps: RecipeStep[]
|
||||
|
||||
addStep() {
|
||||
this.steps.push({id: null, message: ""})
|
||||
this.stepTable.renderRows()
|
||||
}
|
||||
|
||||
removeStep(position: number) {
|
||||
this.steps.splice(position, 1)
|
||||
this.stepTable.renderRows()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<mat-form-field [class.short]="short">
|
||||
<mat-label *ngIf="showLabel">Unités</mat-label>
|
||||
<mat-select [value]="unit" (selectionChange)="unitChange.emit($event.value)">
|
||||
<ng-container *ngIf="!short">
|
||||
<mat-option [value]="unitConstants.UNIT_MILLILITER">Millilitres</mat-option>
|
||||
<mat-option [value]="unitConstants.UNIT_LITER">Litres</mat-option>
|
||||
<mat-option [value]="unitConstants.UNIT_GALLON">Gallons</mat-option>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="short">
|
||||
<mat-option [value]="unitConstants.UNIT_MILLILITER">mL</mat-option>
|
||||
<mat-option [value]="unitConstants.UNIT_LITER">L</mat-option>
|
||||
<mat-option [value]="unitConstants.UNIT_GALLON">gal</mat-option>
|
||||
</ng-container>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
|
@ -0,0 +1,2 @@
|
|||
mat-form-field.short
|
||||
width: 3rem
|
|
@ -0,0 +1,17 @@
|
|||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-unit-selector',
|
||||
templateUrl: './unit-selector.component.html',
|
||||
styleUrls: ['./unit-selector.component.sass']
|
||||
})
|
||||
export class UnitSelectorComponent {
|
||||
readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
|
||||
|
||||
@Input() unit = UNIT_MILLILITER
|
||||
@Input() showLabel = true
|
||||
@Input() short = false
|
||||
|
||||
@Output() unitChange = new EventEmitter<string>()
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<cre-entity-add
|
||||
title="Création d'une recette"
|
||||
backButtonLink="/color/list"
|
||||
[unknownError]="unknownError"
|
||||
[customError]="errorMessage"
|
||||
[formFields]="formFields"
|
||||
(submit)="submit($event)">
|
||||
</cre-entity-add>
|
|
@ -0,0 +1,98 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {RecipeService} from "../../services/recipe.service";
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {CompanyService} from "../../../company/service/company.service";
|
||||
import {map} from "rxjs/operators";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-add',
|
||||
templateUrl: './add.component.html',
|
||||
styleUrls: ['./add.component.sass']
|
||||
})
|
||||
export class AddComponent extends SubscribingComponent {
|
||||
formFields: FormField[] = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Nom',
|
||||
icon: 'form-textbox',
|
||||
type: 'text',
|
||||
validator: Validators.required,
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Un nom est requis'}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
icon: 'text',
|
||||
type: 'text',
|
||||
validator: Validators.required,
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Une description est requise'}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'sample',
|
||||
label: 'Échantillon',
|
||||
icon: 'pound',
|
||||
type: 'number',
|
||||
validator: Validators.compose([Validators.required, Validators.min(0)]),
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Un numéro d\'échantillon est requis'},
|
||||
{conditionFn: errors => errors.min, message: 'Le numéro d\'échantillon doit être supérieur ou égal à 0'}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'approbationDate',
|
||||
label: 'Date d\'approbation',
|
||||
icon: 'calendar',
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'remark',
|
||||
label: 'Remarque',
|
||||
icon: 'text',
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'company',
|
||||
label: 'Bannière',
|
||||
icon: 'domain',
|
||||
type: 'select',
|
||||
validator: Validators.required,
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Une bannière est requise'}
|
||||
],
|
||||
options$: this.companyService.all.pipe(map(companies => companies.map(c => {
|
||||
return {value: c.id, label: c.name}
|
||||
})))
|
||||
}
|
||||
]
|
||||
unknownError = false
|
||||
errorMessage: string | null
|
||||
|
||||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
private companyService: CompanyService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
submit(values) {
|
||||
this.subscribe(
|
||||
this.recipeService.save(values.name, values.description, values.sample, values.approbationDate, values.remark, values.company),
|
||||
{
|
||||
next: recipe => this.urlUtils.navigateTo(`/color/edit/${recipe.id}`),
|
||||
error: err => {
|
||||
this.unknownError = true
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<div *ngIf="recipe">
|
||||
<div *ngIf="!recipe.mixes" class="alert alert-warning m-3">
|
||||
<p>Il n'y a aucun mélange dans cette recette</p>
|
||||
</div>
|
||||
<div *ngIf="recipe.steps.length <= 0" class="alert alert-warning m-3">
|
||||
<p>Il n'y a aucune étape dans cette recette</p>
|
||||
</div>
|
||||
|
||||
<div class="action-bar backward">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="mt-1 pb-2">
|
||||
<button mat-raised-button color="primary" routerLink="/color/list">Retour</button>
|
||||
<button mat-raised-button color="accent" (click)="submit(editComponent)">Enregistrer</button>
|
||||
<button mat-raised-button color="warn" *ngIf="hasDeletePermission" (click)="delete()">Supprimer</button>
|
||||
</div>
|
||||
<mat-form-field>
|
||||
<mat-label>Unités</mat-label>
|
||||
<mat-select [value]="unitConstants.UNIT_MILLILITER" (selectionChange)="changeUnits($event.value)">
|
||||
<mat-option [value]="unitConstants.UNIT_MILLILITER">Millilitres</mat-option>
|
||||
<mat-option [value]="unitConstants.UNIT_LITER">Litres</mat-option>
|
||||
<mat-option [value]="unitConstants.UNIT_GALLON">Gallons</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex-grow-1"></div>
|
||||
</div>
|
||||
|
||||
<div class="recipe-wrapper d-flex flex-row justify-content-around align-items-start flex-wrap">
|
||||
<div>
|
||||
<cre-entity-edit
|
||||
#editComponent
|
||||
title="Modifier la couleur {{recipe.name}}"
|
||||
deleteConfirmMessage="Voulez-vous vraiment supprimer la couleur {{recipe.name}}?"
|
||||
[entity]="recipe"
|
||||
[formFields]="formFields"
|
||||
[unknownError]="unknownError"
|
||||
[customError]="errorMessage"
|
||||
[disableButtons]="true">
|
||||
</cre-entity-edit>
|
||||
</div>
|
||||
|
||||
<div class="recipe-mixes-wrapper">
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>Mélanges</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="no-action pl-5">
|
||||
<ng-container *ngFor="let mix of recipe.mixes; let i = index">
|
||||
<cre-mix-table
|
||||
[class.no-top-margin]="i == 0"
|
||||
[recipe]="recipe"
|
||||
[mix]="mix"
|
||||
[units$]="units$"
|
||||
[editing]="true">
|
||||
</cre-mix-table>
|
||||
</ng-container>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="accent" routerLink="/color/add/mix/{{recipe.id}}">Ajouter</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<cre-step-table [steps]="recipe.steps"></cre-step-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,17 @@
|
|||
.recipe-wrapper > div
|
||||
margin: 0 3rem 3rem
|
||||
|
||||
mat-card
|
||||
margin-top: 3rem
|
||||
|
||||
.recipe-mixes-wrapper mat-card
|
||||
min-width: 20rem
|
||||
|
||||
mat-card
|
||||
mat-card-content
|
||||
margin-bottom: 0
|
||||
padding-top: 16px !important
|
||||
padding-bottom: 0 !important
|
||||
|
||||
mat-card-actions
|
||||
margin-top: 0
|
|
@ -0,0 +1,142 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {Recipe} from "../../../shared/model/recipe.model";
|
||||
import {RecipeService} from "../../services/recipe.service";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {Subject} from "rxjs";
|
||||
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
|
||||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {EmployeePermission} from "../../../shared/model/employee";
|
||||
import {EntityEditComponent} from "../../../shared/components/entity-edit/entity-edit.component";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-edit',
|
||||
templateUrl: './edit.component.html',
|
||||
styleUrls: ['./edit.component.sass']
|
||||
})
|
||||
export class EditComponent extends SubscribingComponent {
|
||||
readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
|
||||
|
||||
recipe: Recipe | null
|
||||
formFields = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Nom',
|
||||
icon: 'form-textbox',
|
||||
type: 'text',
|
||||
validator: Validators.required,
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Un nom est requis'}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
icon: 'text',
|
||||
type: 'text',
|
||||
validator: Validators.required,
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Une description est requise'}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'sample',
|
||||
label: 'Échantillon',
|
||||
icon: 'pound',
|
||||
type: 'number',
|
||||
validator: Validators.compose([Validators.required, Validators.min(0)]),
|
||||
errorMessages: [
|
||||
{conditionFn: errors => errors.required, message: 'Un numéro d\'échantillon est requis'},
|
||||
{conditionFn: errors => errors.min, message: 'Le numéro d\'échantillon doit être supérieur ou égal à 0'}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'approbationDate',
|
||||
label: 'Date d\'approbation',
|
||||
icon: 'calendar',
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'remark',
|
||||
label: 'Remarque',
|
||||
icon: 'text',
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'company',
|
||||
label: 'Bannière',
|
||||
icon: 'domain',
|
||||
type: 'text',
|
||||
readonly: true,
|
||||
valueFn: recipe => recipe.company.name,
|
||||
}
|
||||
]
|
||||
unknownError = false
|
||||
errorMessage: string | null
|
||||
units$ = new Subject<string>()
|
||||
|
||||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
private accountService: AccountService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
|
||||
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
|
||||
this.subscribe(
|
||||
this.recipeService.getById(id),
|
||||
{
|
||||
next: recipe => this.recipe = recipe,
|
||||
error: err => {
|
||||
if (err.status === 404) {
|
||||
this.router.navigate(['/color/list'])
|
||||
} else {
|
||||
this.unknownError = true
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
changeUnits(unit: string) {
|
||||
this.units$.next(unit)
|
||||
}
|
||||
|
||||
submit(editComponent: EntityEditComponent) {
|
||||
const values = editComponent.values
|
||||
this.subscribe(
|
||||
this.recipeService.update(this.recipe.id, values.name, values.description, values.sample, values.approbationDate, values.remark, this.recipe.steps),
|
||||
{
|
||||
next: () => this.router.navigate(['/color/list']),
|
||||
error: err => {
|
||||
if (err.status === 409) {
|
||||
this.errorMessage = `Une couleur avec le nom '${values.name}' et la bannière '${this.recipe.company.name}' existe déjà`
|
||||
} else {
|
||||
this.unknownError = true
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.subscribe(
|
||||
this.recipeService.delete(this.recipe.id),
|
||||
{
|
||||
next: () => this.router.navigate(['/color/list'])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
get hasDeletePermission(): boolean {
|
||||
return this.accountService.hasPermission(EmployeePermission.REMOVE_RECIPE)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<div *ngIf="recipe">
|
||||
<cre-recipe-info [recipe]="recipe" [hasModifications]="hasModifications"></cre-recipe-info>
|
||||
|
||||
<div *ngIf="error" class="alert alert-danger m-3">
|
||||
<p *ngIf="error === ERROR_UNKNOWN">Une erreur est survenue</p>
|
||||
<p *ngIf="error === ERROR_DEDUCT">Certains produit ne sont pas en quantité suffisante dans l'inventaire</p>
|
||||
</div>
|
||||
<div *ngIf="success" class="alert alert-success m-3">
|
||||
<p *ngIf="success === SUCCESS_SAVE">Les modifications ont été enregistrées</p>
|
||||
<p *ngIf="success === SUCCESS_DEDUCT">Les quantités des produits utilisés ont été déduites de l'inventaire</p>
|
||||
</div>
|
||||
|
||||
<div class="action-bar backward d-flex flex-row">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="mt-1 pb-2">
|
||||
<button mat-raised-button color="primary" routerLink="/color/list">Retour</button>
|
||||
<button mat-raised-button color="primary">Version Excel</button>
|
||||
<button mat-raised-button color="accent" (click)="saveModifications()" [disabled]="!hasModifications">
|
||||
Enregistrer
|
||||
</button>
|
||||
<button mat-raised-button color="accent" (click)="deductQuantities()">Déduire</button>
|
||||
</div>
|
||||
<cre-unit-selector (unitChange)="changeUnits($event)"></cre-unit-selector>
|
||||
</div>
|
||||
|
||||
<div class="flex-grow-1"></div>
|
||||
|
||||
<mat-form-field class="w-auto">
|
||||
<mat-label>Note</mat-label>
|
||||
<textarea matInput cols="40" rows="3" (change)="changeNote($event)">{{note}}</textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="recipe-content">
|
||||
<!-- Mixes -->
|
||||
<div>
|
||||
<ng-container *ngFor="let mix of recipe.mixes">
|
||||
<cre-mix-table
|
||||
[mix]="mix"
|
||||
[deductErrorBody]="deductErrorBody"
|
||||
[units$]="units$"
|
||||
(quantityChange)="changeQuantity($event)"
|
||||
(locationChange)="changeMixLocation($event)"
|
||||
(deduct)="deductMixQuantities(mix.id)">
|
||||
</cre-mix-table>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- Steps -->
|
||||
<div>
|
||||
<cre-step-list [steps]="recipe.steps"></cre-step-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Images -->
|
||||
<div class="recipe-images">
|
||||
Images
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,21 @@
|
|||
.recipe-content
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
|
||||
div
|
||||
margin-bottom: 1rem
|
||||
|
||||
.recipe-images
|
||||
margin-top: 5rem
|
||||
padding: 2rem
|
||||
background-color: grey
|
||||
|
||||
@media screen and (min-width: 1920px)
|
||||
.action-bar
|
||||
padding-bottom: 5rem
|
||||
|
||||
.recipe-content
|
||||
flex-direction: row
|
||||
justify-content: space-around
|
||||
align-items: start
|
|
@ -0,0 +1,135 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {RecipeService} from "../../services/recipe.service";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {Recipe} from "../../../shared/model/recipe.model";
|
||||
import {Observable, Subject} from "rxjs";
|
||||
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-explore',
|
||||
templateUrl: './explore.component.html',
|
||||
styleUrls: ['./explore.component.sass']
|
||||
})
|
||||
export class ExploreComponent extends SubscribingComponent {
|
||||
readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
|
||||
|
||||
recipe: Recipe | null
|
||||
error: string
|
||||
deductErrorBody = {}
|
||||
success: string
|
||||
units$ = new Subject<string>()
|
||||
|
||||
hasModifications = false
|
||||
note: string | null
|
||||
quantitiesChanges = new Map<number, Map<number, number>>()
|
||||
mixesLocationChanges = new Map<number, string>()
|
||||
|
||||
// Errors
|
||||
readonly ERROR_UNKNOWN = 'unknown'
|
||||
readonly ERROR_DEDUCT = 'deduct'
|
||||
// Success
|
||||
readonly SUCCESS_SAVE = 'save'
|
||||
readonly SUCCESS_DEDUCT = 'deduct'
|
||||
|
||||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit()
|
||||
|
||||
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
|
||||
this.subscribe(
|
||||
this.recipeService.getById(id),
|
||||
{
|
||||
next: r => {
|
||||
this.recipe = r
|
||||
this.note = r.note
|
||||
},
|
||||
error: err => {
|
||||
if (err.status == 404) {
|
||||
this.router.navigate(['/colors/list'])
|
||||
} else {
|
||||
this.error = this.ERROR_UNKNOWN
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
changeUnits(unit: string) {
|
||||
this.units$.next(unit)
|
||||
}
|
||||
|
||||
changeNote(event: any) {
|
||||
this.hasModifications = true
|
||||
this.note = event.target.value
|
||||
}
|
||||
|
||||
changeQuantity(event: { id: number, materialId: number, quantity: number }) {
|
||||
if (!this.quantitiesChanges.has(event.id)) this.quantitiesChanges.set(event.id, new Map<number, number>())
|
||||
this.quantitiesChanges.get(event.id).set(event.materialId, event.quantity)
|
||||
}
|
||||
|
||||
changeMixLocation(event: { id: number, location: string }) {
|
||||
this.hasModifications = true
|
||||
this.mixesLocationChanges.set(event.id, event.location)
|
||||
}
|
||||
|
||||
saveModifications() {
|
||||
this.subscribe(
|
||||
this.recipeService.saveExplorerModifications(this.recipe.id, this.note, this.mixesLocationChanges),
|
||||
{
|
||||
next: () => {
|
||||
this.hasModifications = false
|
||||
this.error = null
|
||||
this.success = this.SUCCESS_SAVE
|
||||
},
|
||||
error: err => {
|
||||
this.success = null
|
||||
this.error = this.ERROR_UNKNOWN
|
||||
console.error(err)
|
||||
}
|
||||
},
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
deductQuantities() {
|
||||
this.performDeductQuantities(this.recipeService.deductQuantities(this.recipe, this.quantitiesChanges))
|
||||
}
|
||||
|
||||
deductMixQuantities(mixId: number) {
|
||||
this.performDeductQuantities(this.recipeService.deductMixQuantities(this.recipe, mixId, this.quantitiesChanges.get(mixId)))
|
||||
}
|
||||
|
||||
performDeductQuantities(observable: Observable<void>) {
|
||||
this.subscribe(
|
||||
observable,
|
||||
{
|
||||
next: () => {
|
||||
this.error = null
|
||||
this.success = this.SUCCESS_DEDUCT
|
||||
},
|
||||
error: err => {
|
||||
this.success = null
|
||||
if (err.status === 409) { // There is not enough of one or more materials in the inventory
|
||||
this.error = this.ERROR_DEDUCT
|
||||
this.deductErrorBody = err.error
|
||||
} else {
|
||||
this.error = this.ERROR_UNKNOWN
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<div class="action-bar">
|
||||
<mat-form-field>
|
||||
<mat-label>Recherche</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="searchQuery"/>
|
||||
<button mat-button *ngIf="searchQuery" matSuffix mat-icon-button (click)="searchQuery=''">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
<div class="button-add">
|
||||
<button *ngIf="hasEditPermission" mat-raised-button color="accent" routerLink="/color/add">Ajouter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-expansion-panel class="table-title" *ngFor="let companyRecipes of (recipes$ | async)"
|
||||
[hidden]="isCompanyHidden(companyRecipes.recipes)" [expanded]="panelForcedExpanded">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
{{companyRecipes.company}}
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<ng-container *ngTemplateOutlet="recipeTableTemplate; context: {recipes: companyRecipes.recipes}"></ng-container>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<ng-template
|
||||
#recipeTableTemplate
|
||||
let-recipes="recipes">
|
||||
<table class="mx-auto" mat-table [dataSource]="recipes">
|
||||
<!-- Recipe's info -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>Nom</th>
|
||||
<td mat-cell *matCellDef="let recipe">{{recipe.name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Description</th>
|
||||
<td mat-cell *matCellDef="let recipe">{{recipe.description}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="sample">
|
||||
<th mat-header-cell *matHeaderCellDef>Échantillon</th>
|
||||
<td mat-cell *matCellDef="let recipe">#{{recipe.sample}}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Icons -->
|
||||
<ng-container matColumnDef="iconNotApproved">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let recipe" [class.disabled]="recipe.approbationDate">
|
||||
<mat-icon svgIcon="alert" class="color-warning" title="Cette recette n'est pas approuvée"></mat-icon>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Buttons -->
|
||||
<ng-container matColumnDef="buttonView">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let recipe">
|
||||
<button mat-flat-button color="accent" routerLink="/color/explore/{{recipe.id}}">Voir</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="buttonEdit">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let recipe" [class.disabled]="!hasEditPermission">
|
||||
<button mat-flat-button color="accent" routerLink="/color/edit/{{recipe.id}}">Modifier</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="tableCols"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: tableCols" [hidden]="!searchRecipe(row)"></tr>
|
||||
</table>
|
||||
</ng-template>
|
|
@ -0,0 +1,6 @@
|
|||
mat-expansion-panel
|
||||
width: 60rem
|
||||
margin: 20px auto
|
||||
|
||||
.button-add
|
||||
margin-top: .8rem
|
|
@ -0,0 +1,56 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {RecipeService} from "../../services/recipe.service";
|
||||
import {EmployeePermission} from "../../../shared/model/employee";
|
||||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {Recipe} from "../../../shared/model/recipe.model";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-list',
|
||||
templateUrl: './list.component.html',
|
||||
styleUrls: ['./list.component.sass']
|
||||
})
|
||||
export class ListComponent extends SubscribingComponent {
|
||||
recipes$ = this.recipeService.allSortedByCompany
|
||||
tableCols = ['name', 'description', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit']
|
||||
searchQuery = ""
|
||||
panelForcedExpanded = false
|
||||
recipesHidden = []
|
||||
|
||||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
private accountService: AccountService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
searchRecipe(recipe: Recipe) {
|
||||
if (this.searchQuery.length > 0) {
|
||||
this.panelForcedExpanded = true
|
||||
}
|
||||
const positive = this.searchString(recipe.name) ||
|
||||
this.searchString(recipe.description) ||
|
||||
this.searchString(recipe.sample.toString())
|
||||
this.recipesHidden[recipe.id] = !positive
|
||||
return positive
|
||||
}
|
||||
|
||||
isCompanyHidden(companyRecipes: Recipe[]): boolean {
|
||||
return (this.searchQuery && this.searchQuery.length > 0) && companyRecipes.map(r => this.recipesHidden[r.id]).filter(r => !r).length <= 0
|
||||
}
|
||||
|
||||
get hasEditPermission(): boolean {
|
||||
return this.accountService.hasPermission(EmployeePermission.EDIT_RECIPE)
|
||||
}
|
||||
|
||||
private searchString(value: string): boolean {
|
||||
return value.toLowerCase().indexOf(this.searchQuery.toLowerCase()) >= 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<cre-mix-editor
|
||||
[recipeId]="recipeId"
|
||||
[materials]="materials"
|
||||
(save)="submit($event)">
|
||||
</cre-mix-editor>
|
|
@ -0,0 +1,43 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {Material} from "../../../../shared/model/material.model";
|
||||
import {MaterialService} from "../../../../material/service/material.service";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {SubscribingComponent} from "../../../../shared/components/subscribing.component";
|
||||
import {MixService} from "../../../services/mix.service";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-mix-add',
|
||||
templateUrl: './mix-add.component.html',
|
||||
styleUrls: ['./mix-add.component.sass']
|
||||
})
|
||||
export class MixAddComponent extends SubscribingComponent {
|
||||
recipeId: number | null
|
||||
materials: Material[] | null
|
||||
|
||||
constructor(
|
||||
private materialService: MaterialService,
|
||||
private mixService: MixService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit()
|
||||
|
||||
this.recipeId = this.urlUtils.parseIntUrlParam('recipeId')
|
||||
|
||||
this.subscribe(
|
||||
this.materialService.getAllForMixCreation(this.recipeId),
|
||||
{next: m => this.materials = m}
|
||||
)
|
||||
}
|
||||
|
||||
submit(values) {
|
||||
this.subscribe(
|
||||
this.mixService.saveWithUnits(values.name, values.recipeId, values.materialTypeId, values.mixMaterials, values.units),
|
||||
{next: () => this.urlUtils.navigateTo(`/color/edit/${this.recipeId}`)}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<cre-mix-editor
|
||||
[mixId]="mixId"
|
||||
[recipeId]="recipeId"
|
||||
[materials]="materials"
|
||||
(save)="submit($event)">
|
||||
</cre-mix-editor>
|
|
@ -0,0 +1,45 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {SubscribingComponent} from "../../../../shared/components/subscribing.component";
|
||||
import {Material} from "../../../../shared/model/material.model";
|
||||
import {MaterialService} from "../../../../material/service/material.service";
|
||||
import {MixService} from "../../../services/mix.service";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-mix-edit',
|
||||
templateUrl: './mix-edit.component.html',
|
||||
styleUrls: ['./mix-edit.component.sass']
|
||||
})
|
||||
export class MixEditComponent extends SubscribingComponent {
|
||||
mixId: number | null
|
||||
recipeId: number | null
|
||||
materials: Material[] | null
|
||||
|
||||
constructor(
|
||||
private materialService: MaterialService,
|
||||
private mixService: MixService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit()
|
||||
|
||||
this.mixId = this.urlUtils.parseIntUrlParam('id')
|
||||
this.recipeId = this.urlUtils.parseIntUrlParam('recipeId')
|
||||
|
||||
this.subscribe(
|
||||
this.materialService.getAllForMixUpdate(this.mixId),
|
||||
{next: m => this.materials = m}
|
||||
)
|
||||
}
|
||||
|
||||
submit(values) {
|
||||
this.subscribe(
|
||||
this.mixService.updateWithUnits(this.mixId, values.name, values.materialTypeId, values.mixMaterials, values.units),
|
||||
{next: () => this.urlUtils.navigateTo(`/color/edit/${this.recipeId}`)}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {ApiService} from "../../shared/service/api.service";
|
||||
import {convertMixMaterialQuantity, UNIT_MILLILITER} from "../../shared/units";
|
||||
import {Observable} from "rxjs";
|
||||
import {Mix} from "../../shared/model/recipe.model";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MixService {
|
||||
constructor(
|
||||
private api: ApiService
|
||||
) {
|
||||
}
|
||||
|
||||
get all(): Observable<Mix[]> {
|
||||
return this.api.get<Mix[]>('/recipe/mix')
|
||||
}
|
||||
|
||||
getById(id: number): Observable<Mix> {
|
||||
return this.api.get<Mix>(`/recipe/mix/${id}`)
|
||||
}
|
||||
|
||||
saveWithUnits(name: string, recipeId: number, materialTypeId: number, mixMaterials: { materialId: number, quantity: number, percents: boolean }[], units: string): Observable<void> {
|
||||
return this.save(name, recipeId, materialTypeId, this.convertMixMaterialsToMl(mixMaterials, units))
|
||||
}
|
||||
|
||||
save(name: string, recipeId: number, materialTypeId: number, mixMaterials: { materialId: number, quantity: number }[]): Observable<void> {
|
||||
const body = {
|
||||
name,
|
||||
recipeId,
|
||||
materialTypeId,
|
||||
mixMaterials: {}
|
||||
}
|
||||
this.appendMixMaterialsToBody(mixMaterials, body)
|
||||
return this.api.post('/recipe/mix', body)
|
||||
}
|
||||
|
||||
updateWithUnits(id: number, name: string, materialTypeId: number, mixMaterials: { materialId: number, quantity: number, percents: boolean }[], units: string): Observable<void> {
|
||||
return this.update(id, name, materialTypeId, this.convertMixMaterialsToMl(mixMaterials, units))
|
||||
}
|
||||
|
||||
update(id: number, name: string, materialTypeId: number, mixMaterials: { materialId: number, quantity: number }[]): Observable<void> {
|
||||
const body = {
|
||||
id,
|
||||
name,
|
||||
materialTypeId,
|
||||
mixMaterials: {}
|
||||
}
|
||||
this.appendMixMaterialsToBody(mixMaterials, body)
|
||||
return this.api.put('/recipe/mix', body)
|
||||
}
|
||||
|
||||
extractMixMaterials(mix: Mix): { materialId: number, quantity: number, percents: boolean }[] {
|
||||
return mix.mixMaterials.map(m => {
|
||||
return {materialId: m.material.id, quantity: m.quantity, percents: m.material.materialType.usePercentages}
|
||||
})
|
||||
}
|
||||
|
||||
private convertMixMaterialsToMl(mixMaterials: { materialId: number, quantity: number, percents: boolean }[], units: string): { materialId: number, quantity: number }[] {
|
||||
return mixMaterials.map(m => {
|
||||
return {
|
||||
materialId: m.materialId,
|
||||
quantity: convertMixMaterialQuantity(m, units, UNIT_MILLILITER)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private appendMixMaterialsToBody(mixMaterials: { materialId: number, quantity: number }[], body: any) {
|
||||
mixMaterials
|
||||
.filter(m => m.materialId != null && m.quantity != null)
|
||||
.forEach(m => body.mixMaterials[m.materialId] = m.quantity)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,106 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ApiService} from "../../shared/service/api.service";
|
||||
import {Observable} from "rxjs";
|
||||
import {Recipe, RecipeStep} from "../../shared/model/recipe.model";
|
||||
import {map} from "rxjs/operators";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RecipeService {
|
||||
constructor(
|
||||
private api: ApiService
|
||||
) {
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
get all(): Observable<Recipe[]> {
|
||||
return this.api.get<Recipe[]>('/recipe')
|
||||
}
|
||||
|
||||
get allSortedByCompany(): Observable<{ company: string, recipes: Recipe[] }[]> {
|
||||
return this.all.pipe(map(recipes => {
|
||||
const mapped = []
|
||||
recipes.forEach(r => {
|
||||
if (!mapped[r.company.id]) {
|
||||
mapped[r.company.id] = {company: r.company.name, recipes: []}
|
||||
}
|
||||
mapped[r.company.id].recipes.push(r)
|
||||
})
|
||||
return mapped.filter(e => e != null) // Filter to remove empty elements in the array that appears for some reason
|
||||
}))
|
||||
}
|
||||
|
||||
getById(id: number): Observable<Recipe> {
|
||||
return this.api.get<Recipe>(`/recipe/${id}`)
|
||||
}
|
||||
|
||||
save(name: string, description: string, sample: number, approbationDate: string, remark: string, companyId: number): Observable<Recipe> {
|
||||
const body = {name, description, sample, remark, companyId}
|
||||
if (approbationDate) {
|
||||
// @ts-ignore
|
||||
body.approbationDate = approbationDate
|
||||
}
|
||||
return this.api.post<Recipe>('/recipe', body)
|
||||
}
|
||||
|
||||
update(id: number, name: string, description: string, sample: number, approbationDate: string, remark: string, steps: RecipeStep[] = null) {
|
||||
const body = {id, name, description, sample, remark, steps}
|
||||
if (approbationDate) {
|
||||
// @ts-ignore
|
||||
body.approbationDate = approbationDate
|
||||
}
|
||||
return this.api.put<Recipe>('/recipe', body)
|
||||
}
|
||||
|
||||
saveExplorerModifications(id: number, note: string, mixesLocationChange: Map<number, string>): Observable<void> {
|
||||
const body = {
|
||||
id,
|
||||
note,
|
||||
mixesLocation: {}
|
||||
}
|
||||
mixesLocationChange.forEach((l, i) => body.mixesLocation[i] = l)
|
||||
|
||||
return this.api.put<void>('/recipe/public', body)
|
||||
}
|
||||
|
||||
deductMixQuantities(recipe: Recipe, mixId: number, quantities: Map<number, number>): Observable<void> {
|
||||
return this.sendDeductBody(this.buildDeductMixBody(recipe, mixId, quantities))
|
||||
}
|
||||
|
||||
deductQuantities(recipe: Recipe, quantities: Map<number, Map<number, number>>): Observable<void> {
|
||||
return this.sendDeductBody(this.buildDeductBody(recipe, quantities))
|
||||
}
|
||||
|
||||
delete(id: number): Observable<void> {
|
||||
return this.api.delete<void>(`/recipe/${id}`)
|
||||
}
|
||||
|
||||
private buildDeductMixBody(recipe: Recipe, mixId: number, quantities: Map<number, number>): any {
|
||||
const mix = recipe.mixes.filter(m => m.id === mixId)[0]
|
||||
const body = {id: recipe.id, quantities: {}}
|
||||
body.quantities[mixId] = {}
|
||||
const firstMaterial = mix.mixMaterials[0].material.id
|
||||
mix.mixMaterials.forEach(m => {
|
||||
if (quantities && quantities.has(m.material.id)) {
|
||||
body.quantities[mix.id][m.material.id] = quantities.get(m.material.id)
|
||||
} else {
|
||||
let quantity = m.quantity
|
||||
if (m.material.materialType.usePercentages) quantity = body.quantities[mix.id][firstMaterial] * (quantity / 100)
|
||||
body.quantities[mix.id][m.material.id] = quantity
|
||||
}
|
||||
})
|
||||
return body
|
||||
}
|
||||
|
||||
private buildDeductBody(recipe: Recipe, quantities: Map<number, Map<number, number>>): any {
|
||||
const body = {id: recipe.id, quantities: {}}
|
||||
recipe.mixes.forEach(mix => {
|
||||
body.quantities[mix.id] = this.buildDeductMixBody(recipe, mix.id, quantities.get(mix.id)).quantities[mix.id]
|
||||
})
|
||||
return body
|
||||
}
|
||||
|
||||
private sendDeductBody(body: any): Observable<void> {
|
||||
return this.api.put('/recipe/deduct', body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {CompanyService} from "../../service/company.service";
|
||||
import {Validators} from "@angular/forms";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {Router} from "@angular/router";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-add',
|
||||
|
@ -28,9 +28,10 @@ export class AddComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private companyService: CompanyService,
|
||||
private router: Router
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
submit(values) {
|
||||
|
@ -39,12 +40,12 @@ export class AddComponent extends SubscribingComponent {
|
|||
{
|
||||
next: () => this.router.navigate(['/catalog/company/list']),
|
||||
error: err => {
|
||||
if (err.status == 409) {
|
||||
if (err.status === 409) {
|
||||
this.errorMessage = `Une bannière avec le nom '${values.name}' existe déjà`
|
||||
} else {
|
||||
this.unknownError = true
|
||||
console.log(err)
|
||||
}
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ import {Component} from '@angular/core';
|
|||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {Company} from "../../../shared/model/company.model";
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {Validators} from "@angular/forms";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {CompanyService} from "../../service/company.service";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
|
@ -30,10 +30,10 @@ export class EditComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private companyService: CompanyService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -2,6 +2,8 @@ import {Component} from '@angular/core';
|
|||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {CompanyService} from "../../service/company.service";
|
||||
import {EmployeePermission} from "../../../shared/model/employee";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-list',
|
||||
|
@ -20,8 +22,10 @@ export class ListComponent extends SubscribingComponent {
|
|||
}]
|
||||
|
||||
constructor(
|
||||
private companyService: CompanyService
|
||||
private companyService: CompanyService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
mat-card
|
||||
max-width: 90rem
|
|
@ -1,12 +1,12 @@
|
|||
import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {FormControl, FormGroup, Validators} from "@angular/forms";
|
||||
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
|
||||
import {PermissionsFieldComponent} from "../../../shared/components/permissions-field/permissions-field.component";
|
||||
import {EmployeeGroup} from "../../../shared/model/employee";
|
||||
import {Observable, Subject} from "rxjs";
|
||||
import {GroupService} from "../../../groups/services/group.service";
|
||||
import {take, takeUntil} from "rxjs/operators";
|
||||
import {EmployeeService} from "../../services/employee.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
|
||||
@Component({
|
||||
|
@ -30,9 +30,10 @@ export class AddComponent extends SubscribingComponent {
|
|||
constructor(
|
||||
private employeeService: EmployeeService,
|
||||
private groupService: GroupService,
|
||||
private router: Router
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
mat-card
|
||||
max-width: 90rem
|
|
@ -29,11 +29,11 @@ export class EditComponent extends SubscribingComponent {
|
|||
private accountService: AccountService,
|
||||
private employeeService: EmployeeService,
|
||||
private groupService: GroupService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private formBuilder: FormBuilder
|
||||
private formBuilder: FormBuilder,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -6,6 +6,8 @@ import {takeUntil} from "rxjs/operators";
|
|||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {animate, state, style, transition, trigger} from "@angular/animations";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-employees',
|
||||
|
@ -27,9 +29,11 @@ export class ListComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private employeeService: EmployeeService,
|
||||
private accountService: AccountService
|
||||
private accountService: AccountService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -19,10 +19,10 @@ export class PasswordEditComponent extends SubscribingComponent {
|
|||
constructor(
|
||||
private employeeService: EmployeeService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mat-card
|
||||
width: max-content
|
||||
max-width: 90rem
|
||||
|
||||
mat-checkbox
|
||||
font-size: .8em
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Component, ViewChild} from '@angular/core';
|
||||
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
|
||||
import {GroupService} from "../../services/group.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {PermissionsFieldComponent} from "../../../shared/components/permissions-field/permissions-field.component";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
|
||||
|
@ -20,9 +20,10 @@ export class AddComponent extends SubscribingComponent {
|
|||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private groupService: GroupService,
|
||||
private router: Router
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
mat-card
|
||||
max-width: 90rem
|
|
@ -21,13 +21,13 @@ export class EditComponent extends SubscribingComponent {
|
|||
private _nameControl: FormControl
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private accountService: AccountService,
|
||||
private groupService: GroupService,
|
||||
private formBuilder: FormBuilder
|
||||
private formBuilder: FormBuilder,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -6,6 +6,8 @@ import {takeUntil} from "rxjs/operators";
|
|||
import {animate, state, style, transition, trigger} from "@angular/animations";
|
||||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-groups',
|
||||
|
@ -27,9 +29,11 @@ export class ListComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private groupService: GroupService,
|
||||
private accountService: AccountService
|
||||
private accountService: AccountService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {Validators} from "@angular/forms";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {MaterialTypeService} from "../../service/material-type.service";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {Router} from "@angular/router";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-add',
|
||||
|
@ -48,9 +48,10 @@ export class AddComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private materialTypeService: MaterialTypeService,
|
||||
private router: Router
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
submit(values) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import {ActivatedRoute, Router} from "@angular/router";
|
|||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {MaterialTypeService} from "../../service/material-type.service";
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {Validators} from "@angular/forms";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-edit',
|
||||
|
@ -44,10 +44,10 @@ export class EditComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private materialTypeService: MaterialTypeService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
|
|
@ -2,6 +2,8 @@ import {Component} from '@angular/core';
|
|||
import {MaterialTypeService} from "../../service/material-type.service";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {EmployeePermission} from "../../../shared/model/employee";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-list',
|
||||
|
@ -25,8 +27,10 @@ export class ListComponent extends SubscribingComponent {
|
|||
]
|
||||
|
||||
constructor(
|
||||
private materialTypeService: MaterialTypeService
|
||||
private materialTypeService: MaterialTypeService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {Validators} from "@angular/forms";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {MaterialService} from "../../service/material.service";
|
||||
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
|
||||
import {map} from "rxjs/operators";
|
||||
|
||||
|
@ -63,9 +63,10 @@ export class AddComponent extends SubscribingComponent {
|
|||
constructor(
|
||||
private materialService: MaterialService,
|
||||
private materialTypeService: MaterialTypeService,
|
||||
private router: Router
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
submit(values) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, ViewChild} from '@angular/core';
|
||||
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
|
||||
import {Validators} from "@angular/forms";
|
||||
import {FormBuilder, Validators} from "@angular/forms";
|
||||
import {map} from "rxjs/operators";
|
||||
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
|
||||
import {MaterialService} from "../../service/material.service";
|
||||
|
@ -71,10 +71,10 @@ export class EditComponent extends SubscribingComponent {
|
|||
constructor(
|
||||
private materialService: MaterialService,
|
||||
private materialTypeService: MaterialTypeService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -114,8 +114,8 @@ export class EditComponent extends SubscribingComponent {
|
|||
this.errorMessage = `Un produit avec le nom '${values.name}' existe déjà`
|
||||
} else {
|
||||
this.unknownError = true
|
||||
console.error(err)
|
||||
}
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -128,7 +128,7 @@ export class EditComponent extends SubscribingComponent {
|
|||
next: () => this.router.navigate(['/catalog/material/list']),
|
||||
error: err => {
|
||||
this.unknownError = true
|
||||
console.log(err)
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -3,6 +3,8 @@ import {SubscribingComponent} from "../../../shared/components/subscribing.compo
|
|||
import {MaterialService} from "../../service/material.service";
|
||||
import {EmployeePermission} from "../../../shared/model/employee";
|
||||
import {environment} from "../../../../../environments/environment";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-list',
|
||||
|
@ -39,9 +41,11 @@ export class ListComponent extends SubscribingComponent {
|
|||
private hasSimdutMap: any = {}
|
||||
|
||||
constructor(
|
||||
private materialService: MaterialService
|
||||
private materialService: MaterialService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
|
|
@ -17,6 +17,14 @@ export class MaterialService {
|
|||
return this.api.get<Material[]>('/material')
|
||||
}
|
||||
|
||||
getAllForMixCreation(recipeId: number): Observable<Material[]> {
|
||||
return this.api.get<Material[]>(`/material/mix/create/${recipeId}`)
|
||||
}
|
||||
|
||||
getAllForMixUpdate(mixId: number): Observable<Material[]> {
|
||||
return this.api.get<Material[]>(`/material/mix/update/${mixId}`)
|
||||
}
|
||||
|
||||
getById(id: number): Observable<Material> {
|
||||
return this.api.get<Material>(`/material/${id}`)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
|
|||
import {SubscribingComponent} from "../subscribing.component";
|
||||
import {FormBuilder, FormControl, FormGroup, ValidatorFn} from "@angular/forms";
|
||||
import {Observable} from "rxjs";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-entity-add',
|
||||
|
@ -19,9 +20,11 @@ export class EntityAddComponent extends SubscribingComponent {
|
|||
form: FormGroup | null
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder
|
||||
private formBuilder: FormBuilder,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -45,10 +48,6 @@ export class EntityAddComponent extends SubscribingComponent {
|
|||
getControl(controlName: string): FormControl {
|
||||
return this.form.controls[controlName] as FormControl
|
||||
}
|
||||
|
||||
test(any) {
|
||||
console.log(any)
|
||||
}
|
||||
}
|
||||
|
||||
export class FormField {
|
||||
|
@ -61,6 +60,7 @@ export class FormField {
|
|||
public errorMessages?: FormErrorMessage[],
|
||||
public valueFn?: (any) => any,
|
||||
public template?: any,
|
||||
public readonly?: boolean,
|
||||
// Specifics to some types
|
||||
public step?: string,
|
||||
public options$?: Observable<{ value: any, label: string }[]>,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<mat-card-header>
|
||||
<mat-card-title>{{title}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-card-content [class.no-action]="disableButtons">
|
||||
<div *ngIf="unknownError || customError" class="alert alert-danger">
|
||||
<p *ngIf="unknownError">Une erreur est survenue</p>
|
||||
<p *ngIf="customError">{{customError}}</p>
|
||||
|
@ -32,10 +32,12 @@
|
|||
</ng-container>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<mat-card-actions *ngIf="!disableButtons">
|
||||
<button mat-raised-button color="primary" [routerLink]="backButtonLink">Retour</button>
|
||||
<button mat-raised-button color="warn" *ngIf="canDelete" (click)="confirmBoxComponent.show()">Supprimer</button>
|
||||
<button mat-raised-button color="accent" [disabled]="form.invalid" (click)="submitForm()">Enregistrer</button>
|
||||
<button mat-raised-button color="accent" [disabled]="form.invalid" (click)="submitForm()">
|
||||
Enregistrer
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
|
@ -44,7 +46,7 @@
|
|||
let-control="control" let-field="field">
|
||||
<mat-form-field>
|
||||
<mat-label>{{field.label}}</mat-label>
|
||||
<input matInput [type]="field.type" [formControl]="control"/>
|
||||
<input matInput [type]="field.type" [formControl]="control" [readonly]="field.readonly"/>
|
||||
<mat-icon [svgIcon]="field.icon" matSuffix></mat-icon>
|
||||
<mat-error *ngIf="control.invalid && field.errorMessages">
|
||||
<ng-container *ngFor="let errorMessage of field.errorMessages">
|
||||
|
@ -59,7 +61,7 @@
|
|||
let-control="control" let-field="field">
|
||||
<mat-form-field *ngIf="field.options$ | async as options">
|
||||
<mat-label>{{field.label}}</mat-label>
|
||||
<mat-select [formControl]="control">
|
||||
<mat-select [formControl]="control" [disabled]="field.readonly">
|
||||
<mat-option *ngFor="let option of options" [value]="option.value">
|
||||
{{option.label}}
|
||||
</mat-option>
|
||||
|
|
|
@ -4,6 +4,7 @@ import {SubscribingComponent} from "../subscribing.component";
|
|||
import {FormField} from "../entity-add/entity-add.component";
|
||||
import {EmployeePermission} from "../../model/employee";
|
||||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-entity-edit',
|
||||
|
@ -19,6 +20,7 @@ export class EntityEditComponent extends SubscribingComponent {
|
|||
@Input() deletePermission: EmployeePermission
|
||||
@Input() unknownError = false
|
||||
@Input() customError: string | null
|
||||
@Input() disableButtons: boolean
|
||||
@Output() submit = new EventEmitter<any>()
|
||||
@Output() delete = new EventEmitter<void>()
|
||||
|
||||
|
@ -26,9 +28,11 @@ export class EntityEditComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private formBuilder: FormBuilder
|
||||
private formBuilder: FormBuilder,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -42,17 +46,21 @@ export class EntityEditComponent extends SubscribingComponent {
|
|||
}
|
||||
|
||||
submitForm() {
|
||||
const values = {}
|
||||
this.formFields.forEach(f => {
|
||||
values[f.name] = this.getControl(f.name).value
|
||||
})
|
||||
this.submit.emit(values)
|
||||
this.submit.emit(this.values)
|
||||
}
|
||||
|
||||
getControl(controlName: string): FormControl {
|
||||
return this.form.controls[controlName] as FormControl
|
||||
}
|
||||
|
||||
get values(): any {
|
||||
const values = {}
|
||||
this.formFields.forEach(f => {
|
||||
values[f.name] = this.getControl(f.name).value
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
get canDelete(): boolean {
|
||||
return this.accountService.hasPermission(this.deletePermission)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import {Observable} from "rxjs";
|
|||
import {SubscribingComponent} from "../subscribing.component";
|
||||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {EmployeePermission} from "../../model/employee";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-entity-list',
|
||||
|
@ -18,9 +20,11 @@ export class EntityListComponent<T> extends SubscribingComponent {
|
|||
@Input() addPermission: EmployeePermission
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService
|
||||
private accountService: AccountService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
hasPermissionToUseButton(button: TableButton): boolean {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {ResolveEnd, Router} from "@angular/router";
|
||||
import {ActivatedRoute, ResolveEnd, Router} from "@angular/router";
|
||||
import {AppState} from "../../app-state";
|
||||
import {Employee, EmployeePermission} from "../../model/employee";
|
||||
import {AccountService} from "../../../accounts/services/account.service";
|
||||
import {SubscribingComponent} from "../subscribing.component";
|
||||
import {FormBuilder} from "@angular/forms";
|
||||
|
||||
@Component({
|
||||
selector: 'cre-header',
|
||||
|
@ -12,9 +13,10 @@ import {SubscribingComponent} from "../subscribing.component";
|
|||
})
|
||||
export class HeaderComponent extends SubscribingComponent {
|
||||
links: HeaderLink[] = [
|
||||
{route: '/color', title: 'Couleurs', requiredPermission: EmployeePermission.VIEW_RECIPE},
|
||||
{route: '/catalog', title: 'Catalogue', enabled: true},
|
||||
new HeaderLink('/employee', 'Employés', EmployeePermission.VIEW_EMPLOYEE),
|
||||
new HeaderLink('/group', 'Groupes', EmployeePermission.VIEW_EMPLOYEE_GROUP),
|
||||
{route: '/employee', title: 'Employés', requiredPermission: EmployeePermission.VIEW_EMPLOYEE},
|
||||
{route: '/group', title: 'Groupes', requiredPermission: EmployeePermission.VIEW_EMPLOYEE_GROUP},
|
||||
{route: '/account/login', title: 'Connexion', enabled: true},
|
||||
{route: '/account/logout', title: 'Déconnexion', enabled: false},
|
||||
];
|
||||
|
@ -22,10 +24,11 @@ export class HeaderComponent extends SubscribingComponent {
|
|||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private router: Router,
|
||||
private appState: AppState
|
||||
private appState: AppState,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
) {
|
||||
super()
|
||||
super(activatedRoute, router)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -89,7 +92,7 @@ class HeaderLink {
|
|||
public route: string,
|
||||
public title: string,
|
||||
public requiredPermission?: EmployeePermission,
|
||||
public enabled = false
|
||||
public enabled?
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
import {take, takeUntil} from "rxjs/operators";
|
||||
import {OnDestroy, OnInit} from "@angular/core";
|
||||
import {Observable, Subject} from "rxjs";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {UrlUtils} from "../utils/url.utils";
|
||||
|
||||
export abstract class SubscribingComponent implements OnInit, OnDestroy {
|
||||
protected subscribers$ = []
|
||||
protected destroy$ = new Subject<boolean>()
|
||||
unknownError = false
|
||||
|
||||
protected constructor(
|
||||
protected activatedRoute: ActivatedRoute,
|
||||
protected router: Router,
|
||||
protected urlUtils = new UrlUtils(activatedRoute, router)
|
||||
) {
|
||||
}
|
||||
|
||||
subscribe<T>(observable: Observable<T>, observer, take_count = -1) {
|
||||
if (!observer.error) {
|
||||
observer.error = err => console.log(err)
|
||||
observer.error = err => this.handleObserverError(err)
|
||||
}
|
||||
|
||||
if (take_count >= 0) {
|
||||
|
@ -19,6 +29,24 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy {
|
|||
this.subscribers$.push(observable.subscribe(observer))
|
||||
}
|
||||
|
||||
subscribeEntityByIdFromRoute<T>(service: any, entitySubject: Subject<T>, notFoundRoute: string, paramName: string = 'id') {
|
||||
const id = this.urlUtils.parseIntUrlParam(paramName)
|
||||
this.subscribe(
|
||||
service.getById(id),
|
||||
{
|
||||
next: e => entitySubject.next(e),
|
||||
error: err => {
|
||||
if (err.status === 404) {
|
||||
this.urlUtils.navigateTo(notFoundRoute)
|
||||
} else {
|
||||
this.handleObserverError(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
|
@ -26,4 +54,17 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy {
|
|||
this.destroy$.next(true)
|
||||
this.destroy$.complete()
|
||||
}
|
||||
|
||||
handleObserverError(error: any) {
|
||||
console.error(error)
|
||||
this.unknownError = true
|
||||
}
|
||||
|
||||
handleNotFoundError(error: any, route: string) {
|
||||
if (error.status === 404) {
|
||||
this.urlUtils.navigateTo(route)
|
||||
} else {
|
||||
this.handleObserverError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import {Material} from "./material.model";
|
||||
import {LocalDate} from "js-joda";
|
||||
import {Company} from "./company.model";
|
||||
|
||||
export class Recipe {
|
||||
constructor(
|
||||
public id: number,
|
||||
public name: string,
|
||||
public description: string,
|
||||
public sample: number,
|
||||
public approbationDate: LocalDate,
|
||||
public remark: string,
|
||||
public note: string,
|
||||
public company: Company,
|
||||
public mixes: Mix[],
|
||||
public steps: RecipeStep[]
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class Mix {
|
||||
constructor(
|
||||
public id: number,
|
||||
public mixType: MixType,
|
||||
public mixMaterials: MixMaterial[],
|
||||
public location: string,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class MixMaterial {
|
||||
constructor(
|
||||
public id: number,
|
||||
public material: Material,
|
||||
public quantity: number
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
class MixType {
|
||||
constructor(
|
||||
public id: number,
|
||||
public name: string,
|
||||
public material: Material
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class RecipeStep {
|
||||
constructor(
|
||||
public id: number,
|
||||
public message: string
|
||||
) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
export const UNIT_MILLILITER = 'mL'
|
||||
export const UNIT_LITER = 'L'
|
||||
export const UNIT_GALLON = 'gal'
|
||||
|
||||
export const LITER_TO_MILLILITER = 1000
|
||||
export const GALLON_TO_MILLILITER = 3785.411784
|
||||
|
||||
export const UNIT_RATIOS = {
|
||||
mL: {
|
||||
mL: 1,
|
||||
L: 1 / LITER_TO_MILLILITER,
|
||||
gal: 1 / GALLON_TO_MILLILITER
|
||||
},
|
||||
L: {
|
||||
mL: LITER_TO_MILLILITER,
|
||||
L: 1,
|
||||
gal: LITER_TO_MILLILITER / GALLON_TO_MILLILITER
|
||||
},
|
||||
gal: {
|
||||
mL: GALLON_TO_MILLILITER,
|
||||
L: 1 / (LITER_TO_MILLILITER / GALLON_TO_MILLILITER),
|
||||
gal: 1
|
||||
}
|
||||
}
|
||||
|
||||
export function convertMixMaterialQuantity(computedQuantity: { percents: boolean, quantity: number }, from: string, to: string): number {
|
||||
return !computedQuantity.percents ? convertQuantity(computedQuantity.quantity, from, to) : computedQuantity.quantity
|
||||
}
|
||||
|
||||
export function convertQuantity(quantity: number, from: string, to: string): number {
|
||||
return quantity * UNIT_RATIOS[from][to]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
|
||||
export class UrlUtils {
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
parseUrlParam(param: string): string {
|
||||
return this.activatedRoute.snapshot.paramMap.get(param)
|
||||
}
|
||||
|
||||
parseIntUrlParam(param: string): number {
|
||||
return parseInt(this.parseUrlParam(param))
|
||||
}
|
||||
|
||||
navigateTo(url: string) {
|
||||
this.router.navigate([url])
|
||||
}
|
||||
}
|
|
@ -92,6 +92,7 @@ $color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-a
|
|||
|
||||
$color-primary: map-get($theme-primary, 500);
|
||||
$color-accent: map-get($theme-accent, 500);
|
||||
$color-warn: map-get($theme-warn, 500);
|
||||
|
||||
|
||||
html, body {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
@import "custom-theme"
|
||||
@import "~material-design-icons/iconfont/material-icons.css"
|
||||
|
||||
mat-card
|
||||
padding: 0 !important
|
||||
width: max-content
|
||||
max-width: 50rem
|
||||
|
||||
&.x-centered
|
||||
margin: auto
|
||||
|
@ -22,6 +24,9 @@ mat-card
|
|||
margin-top: 16px
|
||||
padding: 0 16px
|
||||
|
||||
&.no-action
|
||||
padding: 0 24px 16px 24px !important
|
||||
|
||||
mat-form-field
|
||||
width: 100%
|
||||
|
||||
|
@ -38,6 +43,7 @@ mat-card
|
|||
|
||||
table
|
||||
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12)
|
||||
max-width: 90vw
|
||||
|
||||
th
|
||||
background-color: $color-primary
|
||||
|
@ -72,12 +78,62 @@ table
|
|||
.disabled *
|
||||
display: none
|
||||
|
||||
mat-expansion-panel.table-title
|
||||
mat-expansion-panel-header
|
||||
background-color: $color-primary
|
||||
|
||||
&:hover, &:focus
|
||||
background-color: $color-primary !important
|
||||
|
||||
mat-panel-title
|
||||
color: $light-primary-text !important
|
||||
font-weight: bold
|
||||
text-transform: uppercase
|
||||
|
||||
mat-panel-description
|
||||
color: $light-primary-text
|
||||
|
||||
mat-form-field
|
||||
display: inline
|
||||
|
||||
&.mat-expanded mat-expansion-panel-header
|
||||
border-bottom-left-radius: 0
|
||||
border-bottom-right-radius: 0
|
||||
|
||||
.mat-expansion-panel-body
|
||||
padding: 0 !important
|
||||
|
||||
table
|
||||
width: 100%
|
||||
|
||||
th
|
||||
border-top-left-radius: 0
|
||||
border-top-right-radius: 0
|
||||
|
||||
cre-mix-table.no-top-margin mat-expansion-panel
|
||||
margin-top: 0 !important
|
||||
|
||||
button
|
||||
text-transform: uppercase
|
||||
font-weight: 500
|
||||
|
||||
&.mat-accent
|
||||
color: white !important
|
||||
color: $light-primary-text !important
|
||||
|
||||
mat-form-field
|
||||
&.w-auto
|
||||
.mat-form-field-infix
|
||||
width: auto !important
|
||||
|
||||
&.dark
|
||||
input
|
||||
caret-color: $light-primary-text !important
|
||||
|
||||
mat-label, input
|
||||
color: $light-primary-text
|
||||
|
||||
.mat-form-field-underline, .mat-form-field-ripple
|
||||
background-color: $light-primary-text !important
|
||||
|
||||
div.empty
|
||||
color: $dark-secondary-text
|
||||
|
@ -92,6 +148,13 @@ div.empty
|
|||
button
|
||||
margin-left: 1rem
|
||||
|
||||
&.backward
|
||||
justify-content: flex-start
|
||||
|
||||
button
|
||||
margin-left: 0
|
||||
margin-right: 1rem
|
||||
|
||||
.alert p
|
||||
margin-bottom: 0
|
||||
|
||||
|
@ -112,3 +175,6 @@ div.empty
|
|||
left: 0
|
||||
background-color: black
|
||||
opacity: 0.4
|
||||
|
||||
.color-warning
|
||||
color: #fdd835
|
||||
|
|
|
@ -60,7 +60,7 @@ open class MixUpdateDto(
|
|||
@field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE)
|
||||
val name: String?,
|
||||
|
||||
val materialType: MaterialType?,
|
||||
val materialTypeId: Long?,
|
||||
|
||||
val mixMaterials: Map<Long, Float>?
|
||||
) : EntityDto<Mix> {
|
||||
|
@ -88,7 +88,7 @@ fun mixSaveDto(
|
|||
fun mixUpdateDto(
|
||||
id: Long = 0L,
|
||||
name: String? = "name",
|
||||
materialType: MaterialType? = materialType(),
|
||||
materialTypeId: Long? = 0L,
|
||||
mixMaterials: Map<Long, Float>? = mapOf(),
|
||||
op: MixUpdateDto.() -> Unit = {}
|
||||
) = MixUpdateDto(id, name, materialType, mixMaterials).apply(op)
|
||||
) = MixUpdateDto(id, name, materialTypeId, mixMaterials).apply(op)
|
||||
|
|
|
@ -55,17 +55,17 @@ class MixServiceImpl(
|
|||
|
||||
override fun update(entity: MixUpdateDto): Mix {
|
||||
val mix = getById(entity.id)
|
||||
if (entity.name != null || entity.materialType != null) {
|
||||
if (entity.name != null || entity.materialTypeId != null) {
|
||||
mix.mixType = if (mixTypeIsShared(mix.mixType)) {
|
||||
mixTypeService.createForNameAndMaterialType(
|
||||
entity.name ?: mix.mixType.name,
|
||||
entity.materialType ?: mix.mixType.material.materialType!!
|
||||
if (entity.materialTypeId != null) materialTypeService.getById(entity.materialTypeId) else mix.mixType.material.materialType!!
|
||||
)
|
||||
} else {
|
||||
mixTypeService.updateForNameAndMaterialType(
|
||||
mix.mixType,
|
||||
entity.name ?: mix.mixType.name,
|
||||
entity.materialType ?: mix.mixType.material.materialType!!
|
||||
if (entity.materialTypeId != null) materialTypeService.getById(entity.materialTypeId) else mix.mixType.material.materialType!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -272,7 +272,7 @@
|
|||
|
||||
let i = 0;
|
||||
$(".mixContainer").each(function () {
|
||||
formData[i++] = getMixQuantities($(this));
|
||||
formData[i++] = getMixMaterials($(this));
|
||||
});
|
||||
|
||||
clearNotEnoughClasses();
|
||||
|
@ -284,7 +284,7 @@
|
|||
$(".useMixSubmit").on({
|
||||
click: function () {
|
||||
showConfirm(formatMessage("[[#{inventory.askUseMix}]]"), false, () => {
|
||||
let formData = [getMixQuantities($(this).parents(".mixContainer"))];
|
||||
let formData = [getMixMaterials($(this).parents(".mixContainer"))];
|
||||
|
||||
clearNotEnoughClasses();
|
||||
sendPost(formData, "/inventory/use", r => displayNotEnoughReason(r));
|
||||
|
@ -384,7 +384,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
function getMixQuantities(mixContainer) {
|
||||
function getMixMaterials(mixContainer) {
|
||||
mix = $(mixContainer).find(".mix");
|
||||
|
||||
const mixId = mix.data("mixid");
|
||||
|
|
Loading…
Reference in New Issue