Merge branch 'model-modifications' into 'master'
Ajout des informations de groupe et des positions dans les étapes et les ingrédients See merge request color-recipes-explorer/frontend!12
This commit is contained in:
commit
fccbf7c32f
|
@ -18,6 +18,7 @@ import {MixAddComponent} from './pages/mix/mix-add/mix-add.component';
|
|||
import {MixEditComponent} from './pages/mix/mix-edit/mix-edit.component';
|
||||
import { ImagesEditorComponent } from './components/images-editor/images-editor.component';
|
||||
import { MixesCardComponent } from './components/mixes-card/mixes-card.component';
|
||||
import {MatSortModule} from '@angular/material/sort'
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
@ -29,7 +30,8 @@ import { MixesCardComponent } from './components/mixes-card/mixes-card.component
|
|||
ColorsRoutingModule,
|
||||
SharedModule,
|
||||
MatExpansionModule,
|
||||
FormsModule
|
||||
FormsModule,
|
||||
MatSortModule
|
||||
]
|
||||
})
|
||||
export class ColorsModule {
|
||||
|
|
|
@ -28,17 +28,42 @@
|
|||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" routerLink="/color/edit/{{recipeId}}">Retour</button>
|
||||
<button *ngIf="editionMode && canDeleteMix" mat-raised-button color="warn" (click)="deleteConfirmBox.show()">Supprimer</button>
|
||||
<button *ngIf="editionMode && canDeleteMix" mat-raised-button color="warn" (click)="deleteConfirmBox.show()">
|
||||
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">
|
||||
<table #matTable mat-table [dataSource]="mixMaterials">
|
||||
<ng-container matColumnDef="position">
|
||||
<th mat-header-cell *matHeaderCellDef>Position</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">
|
||||
{{mixMaterial.position}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="buttonsPosition">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let mixMaterial; let i = index">
|
||||
{{i + 1}}
|
||||
<ng-container *ngIf="(!hoveredMixMaterial && i === 0) || hoveredMixMaterial === mixMaterial">
|
||||
<button
|
||||
mat-mini-fab
|
||||
color="primary"
|
||||
class="mr-1"
|
||||
[disabled]="mixMaterial.position <= 1"
|
||||
(click)="decreasePosition(mixMaterial, matTable)">
|
||||
<mat-icon svgIcon="arrow-up"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-mini-fab
|
||||
color="primary"
|
||||
[disabled]="mixMaterial.position >= mixMaterials.length"
|
||||
(click)="increasePosition(mixMaterial, matTable)">
|
||||
<mat-icon svgIcon="arrow-down"></mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
@ -46,9 +71,15 @@
|
|||
<th mat-header-cell *matHeaderCellDef>Produit</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">
|
||||
<mat-form-field *ngIf="materials">
|
||||
<mat-select #select [(ngModel)]="mixMaterial.materialId" [value]="mixMaterial.materialId">
|
||||
<mat-option *ngFor="let material of getAvailableMaterials(select)"
|
||||
[value]="material.id">{{material.name}}</mat-option>
|
||||
<mat-select
|
||||
#select
|
||||
[value]="mixMaterial.materialId"
|
||||
(valueChange)="setMixMaterialMaterial(mixMaterial, $event)">
|
||||
<mat-option
|
||||
*ngFor="let material of getAvailableMaterials(mixMaterial)"
|
||||
[value]="material.id">
|
||||
{{material.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
|
@ -67,10 +98,10 @@
|
|||
<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 *ngIf="mixMaterial.isPercents">
|
||||
<p>%</p>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!materialUsePercentages(mixMaterial)">
|
||||
<ng-container *ngIf="!mixMaterial.isPercents">
|
||||
<ng-container *ngIf="!hoveredMixMaterial || hoveredMixMaterial != mixMaterial">
|
||||
<span>{{units}}</span>
|
||||
</ng-container>
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
mat-card
|
||||
max-width: unset !important
|
||||
|
||||
td.units-wrapper p
|
||||
width: 3rem
|
||||
margin-bottom: 0
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
|
||||
import {Mix, MixMaterial, Recipe} from '../../../shared/model/recipe.model'
|
||||
import {
|
||||
Mix,
|
||||
MixMaterial,
|
||||
MixMaterialDto,
|
||||
mixMaterialsAsMixMaterialsDto,
|
||||
Recipe, RecipeStep,
|
||||
sortMixMaterialsDto
|
||||
} from '../../../shared/model/recipe.model'
|
||||
import {ErrorHandlingComponent} 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'
|
||||
|
@ -24,7 +29,7 @@ import {MatSelect} from '@angular/material/select'
|
|||
styleUrls: ['./mix-editor.component.sass']
|
||||
})
|
||||
export class MixEditorComponent extends ErrorHandlingComponent {
|
||||
@ViewChild('mixTable') mixTable: MatTable<MixMaterial>
|
||||
@ViewChild('matTable') mixTable: MatTable<MixMaterial>
|
||||
@ViewChild('deleteConfirmBox') deleteConfirmBox: ConfirmBoxComponent
|
||||
|
||||
@Input() mixId: number | null
|
||||
|
@ -35,24 +40,24 @@ export class MixEditorComponent extends ErrorHandlingComponent {
|
|||
|
||||
mix: Mix | null
|
||||
recipe: Recipe | null
|
||||
materialTypes$: Observable<MaterialType[]>
|
||||
materialTypes$ = this.materialTypeService.all
|
||||
|
||||
form: FormGroup
|
||||
nameControl: FormControl
|
||||
materialTypeControl: FormControl
|
||||
|
||||
mixMaterials = []
|
||||
mixMaterials: MixMaterialDto[] = []
|
||||
editionMode = false
|
||||
units = UNIT_MILLILITER
|
||||
hoveredMixMaterial: MixMaterial | null
|
||||
columns = ['position', 'material', 'quantity', 'units', 'buttonRemove']
|
||||
columns = ['position', 'buttonsPosition', 'material', 'quantity', 'units', 'buttonRemove']
|
||||
|
||||
deleting = false
|
||||
handledErrorModels = [{
|
||||
filter: error => error.status === 409 && !this.deleting,
|
||||
messageProducer: error => `Un mélange avec le nom '${error.id}' existe déjà dans cette recette`
|
||||
}, {
|
||||
filter: error => error.error.status === 409 && this.deleting,
|
||||
filter: error => error.error && error.error.status === 409 && this.deleting,
|
||||
consumer: () => this.deleting = false,
|
||||
messageProducer: () => 'Ce mélange est utilisé par un ou plusieurs autres mélanges'
|
||||
}]
|
||||
|
@ -85,36 +90,53 @@ export class MixEditorComponent extends ErrorHandlingComponent {
|
|||
r => {
|
||||
this.recipe = r
|
||||
if (this.editionMode) {
|
||||
this.subscribeEntityById(
|
||||
this.mixService,
|
||||
this.mixId,
|
||||
m => {
|
||||
this.mix = m
|
||||
this.mixMaterials = this.mixService.extractMixMaterials(this.mix)
|
||||
this.generateForm()
|
||||
},
|
||||
'/color/list'
|
||||
)
|
||||
this.mix = this.recipe.mixes.find(m => m.id === this.mixId)
|
||||
this.mixMaterials = mixMaterialsAsMixMaterialsDto(this.mix)
|
||||
} else {
|
||||
this.mixMaterials.push({})
|
||||
this.generateForm()
|
||||
this.addBlankMixMaterial()
|
||||
}
|
||||
this.generateForm()
|
||||
},
|
||||
'/color/list'
|
||||
)
|
||||
this.materialTypes$ = this.materialTypeService.all
|
||||
}
|
||||
|
||||
addRow() {
|
||||
this.mixMaterials.push({materialId: null, quantity: null, percents: false})
|
||||
this.addBlankMixMaterial()
|
||||
this.mixTable.renderRows()
|
||||
}
|
||||
|
||||
removeRow(position: number) {
|
||||
this.mixMaterials.splice(position, 1)
|
||||
|
||||
// Decreases the position of each mix material above the removed one
|
||||
for (let i = position; i < this.mixMaterials.length; i++) {
|
||||
this.mixMaterials[i].position -= 1
|
||||
}
|
||||
|
||||
this.mixTable.renderRows()
|
||||
}
|
||||
|
||||
increasePosition(mixMaterial: MixMaterialDto, table: MatTable<any>) {
|
||||
this.updateMixMaterialPosition(mixMaterial, mixMaterial.position + 1)
|
||||
this.sort(table)
|
||||
}
|
||||
|
||||
decreasePosition(mixMaterial: MixMaterialDto, table: MatTable<any>) {
|
||||
this.updateMixMaterialPosition(mixMaterial, mixMaterial.position - 1)
|
||||
this.sort(table)
|
||||
}
|
||||
|
||||
sort(table: MatTable<any>) {
|
||||
this.mixMaterials = sortMixMaterialsDto(this.mixMaterials)
|
||||
table.renderRows()
|
||||
}
|
||||
|
||||
setMixMaterialMaterial(mixMaterial: MixMaterialDto, materialId: number) {
|
||||
mixMaterial.isPercents = this.materials.find(m => m.id === materialId).materialType.usePercentages
|
||||
mixMaterial.materialId = materialId
|
||||
}
|
||||
|
||||
submit() {
|
||||
this.save.emit({
|
||||
name: this.nameControl.value,
|
||||
|
@ -130,21 +152,8 @@ export class MixEditorComponent extends ErrorHandlingComponent {
|
|||
this.subscribeAndNavigate(this.mixService.delete(this.mixId), `/color/edit/${this.recipeId}`)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
getAvailableMaterials(selector: MatSelect): Material[] {
|
||||
return this.materials.filter(m => selector.value === m.id || this.mixMaterials.filter(mm => mm.materialId === m.id).length === 0)
|
||||
getAvailableMaterials(mixMaterial: MixMaterialDto): Material[] {
|
||||
return this.materials.filter(m => mixMaterial.materialId === m.id || this.mixMaterials.filter(mm => mm.materialId === m.id).length === 0)
|
||||
}
|
||||
|
||||
get canDeleteMix() {
|
||||
|
@ -159,4 +168,24 @@ export class MixEditorComponent extends ErrorHandlingComponent {
|
|||
materialType: this.materialTypeControl
|
||||
})
|
||||
}
|
||||
|
||||
private addBlankMixMaterial() {
|
||||
this.mixMaterials.push(
|
||||
new MixMaterialDto(null, 0, false, this.mixMaterials.length + 1)
|
||||
)
|
||||
}
|
||||
|
||||
private updateMixMaterialPosition(mixMaterial: MixMaterialDto, updatedPosition: number) {
|
||||
if (!this.mixMaterialAtPosition(updatedPosition)) {
|
||||
mixMaterial.position = updatedPosition
|
||||
} else {
|
||||
const conflictingStep = this.mixMaterialAtPosition(updatedPosition)
|
||||
conflictingStep.position = mixMaterial.position
|
||||
mixMaterial.position = updatedPosition
|
||||
}
|
||||
}
|
||||
|
||||
private mixMaterialAtPosition(position: number): MixMaterialDto {
|
||||
return this.mixMaterials.find(m => m.position === position)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,16 +33,22 @@
|
|||
</mat-expansion-panel>
|
||||
|
||||
<ng-template #mixTable>
|
||||
<table mat-table [dataSource]="mix.mixMaterials">
|
||||
<table mat-table [dataSource]="mixMaterials">
|
||||
<ng-container matColumnDef="position">
|
||||
<th mat-header-cell *matHeaderCellDef>Position</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{mixMaterial.position}}</td>
|
||||
<td mat-footer-cell *matFooterCellDef></td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="material">
|
||||
<th mat-header-cell *matHeaderCellDef>Produit</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{mixMaterial.material.name}}</td>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{getMixMaterialFromDto(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-cell *matCellDef="let mixMaterial">{{getMixMaterialFromDto(mixMaterial).material.materialType.name}}</td>
|
||||
<td mat-footer-cell *matFooterCellDef></td>
|
||||
</ng-container>
|
||||
|
||||
|
@ -55,8 +61,8 @@
|
|||
type="number"
|
||||
min="0.001"
|
||||
step="0.001"
|
||||
[value]="getComputedQuantityRounded(mixMaterial)"
|
||||
[disabled]="mixMaterial.material.materialType.usePercentages"
|
||||
[value]="getMixMaterialQuantityRounded(mixMaterial)"
|
||||
[disabled]="mixMaterial.isPercents"
|
||||
(keyup)="changeQuantity($event, mixMaterial)"/>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
|
@ -76,12 +82,12 @@
|
|||
|
||||
<ng-container matColumnDef="quantityStatic">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{getComputedQuantityRounded(mixMaterial)}}</td>
|
||||
<td mat-cell *matCellDef="let mixMaterial">{{getMixMaterialQuantityRounded(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)"
|
||||
<td mat-cell *matCellDef="let mixMaterial; let i = index" [innerHTML]="getCalculatedQuantityHtml(mixMaterial, i)"
|
||||
class="mix-calculation"></td>
|
||||
<td mat-footer-cell *matFooterCellDef></td>
|
||||
</ng-container>
|
||||
|
@ -89,8 +95,8 @@
|
|||
<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>
|
||||
<ng-container *ngIf="mixMaterial.isPercents">%</ng-container>
|
||||
<ng-container *ngIf="!mixMaterial.isPercents">{{units}}</ng-container>
|
||||
</td>
|
||||
<td mat-footer-cell *matFooterCellDef>{{units}}</td>
|
||||
</ng-container>
|
||||
|
@ -103,7 +109,7 @@
|
|||
<button
|
||||
mat-raised-button
|
||||
color="accent"
|
||||
[disabled]="!hasSimdutMap[mixMaterial.material.id]"
|
||||
[disabled]="!hasSimdutMap[getMixMaterialFromDto(mixMaterial).material.id]"
|
||||
(click)="openSimdutFile(mixMaterial)">
|
||||
Fiche signalitique
|
||||
</button>
|
||||
|
@ -115,7 +121,7 @@
|
|||
<tr mat-header-row *matHeaderRowDef="mixColumns"></tr>
|
||||
<tr mat-row
|
||||
*matRowDef="let mixMaterial; columns: mixColumns"
|
||||
[class.low-quantity]="!editionMode && isInLowQuantity(mixMaterial.material.id)"
|
||||
[class.low-quantity]="!editionMode && isInLowQuantity(mixMaterial.materialId)"
|
||||
(mouseover)="hoveredMixMaterial = mixMaterial">
|
||||
</tr>
|
||||
<ng-container *ngIf="!editionMode">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
|
||||
import {Mix, MixMaterial, Recipe} from '../../../shared/model/recipe.model'
|
||||
import {Mix, MixMaterial, MixMaterialDto, mixMaterialsAsMixMaterialsDto, Recipe} from '../../../shared/model/recipe.model'
|
||||
import {Subject} from 'rxjs'
|
||||
import {SubscribingComponent} from '../../../shared/components/subscribing.component'
|
||||
import {convertMixMaterialQuantity, UNIT_MILLILITER} from '../../../shared/units'
|
||||
|
@ -18,7 +18,7 @@ import {MaterialService} from '../../../material/service/material.service'
|
|||
})
|
||||
export class MixTableComponent extends SubscribingComponent {
|
||||
private readonly COLUMNS = ['material', 'materialType', 'quantity', 'quantityCalculated', 'quantityUnits', 'simdut']
|
||||
private readonly COLUMNS_STATIC = ['material', 'materialType', 'quantityStatic', 'quantityUnits']
|
||||
private readonly COLUMNS_EDIT = ['position', 'material', 'materialType', 'quantityStatic', 'quantityUnits']
|
||||
|
||||
@ViewChild('printingConfirmBox') printingConfirmBox: ConfirmBoxComponent
|
||||
|
||||
|
@ -28,13 +28,13 @@ export class MixTableComponent extends SubscribingComponent {
|
|||
@Input() deductErrorBody
|
||||
@Input() editionMode: boolean
|
||||
@Output() locationChange = new EventEmitter<{ id: number, location: string }>()
|
||||
@Output() quantityChange = new EventEmitter<{ id: number, materialId: number, quantity: number }>()
|
||||
@Output() quantityChange = new EventEmitter<MixMaterialDto>()
|
||||
@Output() deduct = new EventEmitter<void>()
|
||||
@Output() printingErrorChange = new EventEmitter<number>()
|
||||
|
||||
mixColumns = this.COLUMNS
|
||||
units = UNIT_MILLILITER
|
||||
computedQuantities: { id: number, percents: boolean, quantity: number }[] = []
|
||||
mixMaterials: MixMaterialDto[] = []
|
||||
hoveredMixMaterial: MixMaterial | null
|
||||
hasSimdutMap: any = {}
|
||||
|
||||
|
@ -55,23 +55,19 @@ export class MixTableComponent extends SubscribingComponent {
|
|||
super.ngOnInit()
|
||||
|
||||
if (this.editionMode) {
|
||||
this.mixColumns = this.COLUMNS_STATIC
|
||||
this.mixColumns = this.COLUMNS_EDIT
|
||||
}
|
||||
|
||||
this.mix.mixMaterials.forEach(m => this.computedQuantities.push({
|
||||
id: m.id,
|
||||
percents: m.material.materialType.usePercentages,
|
||||
quantity: m.quantity
|
||||
}))
|
||||
this.mixMaterials = mixMaterialsAsMixMaterialsDto(this.mix)
|
||||
|
||||
this.subscribe(
|
||||
this.units$,
|
||||
u => this.convertQuantities(u)
|
||||
)
|
||||
|
||||
this.mix.mixMaterials.map(mm => mm.material).forEach(material => this.subscribe(
|
||||
this.materialService.hasSimdut(material.id),
|
||||
b => this.hasSimdutMap[material.id] = b
|
||||
this.mixMaterials.forEach(mixMaterial => this.subscribe(
|
||||
this.materialService.hasSimdut(mixMaterial.materialId),
|
||||
b => this.hasSimdutMap[mixMaterial.materialId] = b
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -80,30 +76,39 @@ export class MixTableComponent extends SubscribingComponent {
|
|||
this.locationChange.emit({id: this.mix.id, location: event.target.value})
|
||||
}
|
||||
|
||||
changeQuantity(event: any, mixMaterial: MixMaterial, isTotal = false) {
|
||||
changeQuantity(event: any, mixMaterial: MixMaterialDto, isTotal = false) {
|
||||
const newQuantity = parseInt(event.target.value)
|
||||
// Skip if there if the input is null
|
||||
if (!newQuantity) {
|
||||
return
|
||||
}
|
||||
|
||||
let ratio = 1
|
||||
if (!isTotal) {
|
||||
const originalQuantity = this.getComputedQuantity(mixMaterial.id)
|
||||
const originalQuantity = this.findMixMaterialDto(mixMaterial.materialId)
|
||||
ratio = newQuantity / originalQuantity.quantity
|
||||
} else {
|
||||
ratio = newQuantity / this.getTotalQuantity()
|
||||
}
|
||||
this.computedQuantities.forEach((q, i) => {
|
||||
if (!q.percents) {
|
||||
this.mixMaterials.forEach((q, i) => {
|
||||
if (!q.isPercents) {
|
||||
q.quantity *= ratio
|
||||
}
|
||||
this.emitQuantityChangeEvent(i)
|
||||
})
|
||||
}
|
||||
|
||||
getComputedQuantityRounded(mixMaterial: MixMaterial): number {
|
||||
return this.round(this.getComputedQuantity(mixMaterial.id).quantity)
|
||||
getMixMaterialFromDto(mixMaterialDto: MixMaterialDto): MixMaterial {
|
||||
return this.mix.mixMaterials.find(m => m.material.id === mixMaterialDto.materialId)
|
||||
}
|
||||
|
||||
getMixMaterialQuantityRounded(mixMaterial: MixMaterialDto): number {
|
||||
return this.round(this.findMixMaterialDto(mixMaterial.materialId).quantity)
|
||||
}
|
||||
|
||||
getTotalQuantity(index: number = -1): number {
|
||||
if (index === -1) {
|
||||
index = this.computedQuantities.length - 1
|
||||
index = this.mixMaterials.length - 1
|
||||
}
|
||||
let totalQuantity = 0
|
||||
for (let i = 0; i <= index; i++) {
|
||||
|
@ -112,7 +117,7 @@ export class MixTableComponent extends SubscribingComponent {
|
|||
return totalQuantity
|
||||
}
|
||||
|
||||
getCalculatedQuantity(mixMaterial: MixMaterial, index: number): string {
|
||||
getCalculatedQuantityHtml(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>`
|
||||
|
@ -127,8 +132,8 @@ export class MixTableComponent extends SubscribingComponent {
|
|||
return Math.round(quantity * 1000) / 1000
|
||||
}
|
||||
|
||||
openSimdutFile(mixMaterial: MixMaterial) {
|
||||
window.open(`${environment.apiUrl}/material/${mixMaterial.material.id}/simdut`, '_blank')
|
||||
openSimdutFile(mixMaterial: MixMaterialDto) {
|
||||
window.open(`${environment.apiUrl}/material/${mixMaterial.materialId}/simdut`, '_blank')
|
||||
}
|
||||
|
||||
async print() {
|
||||
|
@ -169,28 +174,30 @@ export class MixTableComponent extends SubscribingComponent {
|
|||
}
|
||||
|
||||
private emitQuantityChangeEvent(index: number) {
|
||||
const quantity = this.mixMaterials[index]
|
||||
this.quantityChange.emit({
|
||||
id: this.mix.id,
|
||||
materialId: this.computedQuantities[index].id,
|
||||
quantity: this.calculateQuantity(index)
|
||||
materialId: quantity.materialId,
|
||||
quantity: this.calculateQuantity(index),
|
||||
isPercents: quantity.isPercents,
|
||||
position: quantity.position
|
||||
})
|
||||
}
|
||||
|
||||
private convertQuantities(newUnit: string) {
|
||||
this.computedQuantities.forEach(q => q.quantity = convertMixMaterialQuantity(q, this.units, newUnit))
|
||||
this.mixMaterials.forEach(q => q.quantity = convertMixMaterialQuantity(q, this.units, newUnit))
|
||||
this.units = newUnit
|
||||
}
|
||||
|
||||
private getComputedQuantity(id: number): any {
|
||||
return this.computedQuantities.filter(q => q.id == id)[0]
|
||||
private findMixMaterialDto(materialId: number): MixMaterialDto {
|
||||
return this.mixMaterials.find(q => q.materialId == materialId)
|
||||
}
|
||||
|
||||
private calculateQuantity(index: number): number {
|
||||
const computedQuantity = this.computedQuantities[index]
|
||||
if (!computedQuantity.percents) {
|
||||
const computedQuantity = this.mixMaterials[index]
|
||||
if (!computedQuantity.isPercents) {
|
||||
return computedQuantity.quantity
|
||||
}
|
||||
return this.computedQuantities[0].quantity * (computedQuantity.quantity / 100)
|
||||
return this.mixMaterials[0].quantity * (computedQuantity.quantity / 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[units$]="units$"
|
||||
[deductErrorBody]="deductErrorBody"
|
||||
[editionMode]="editionMode"
|
||||
(quantityChange)="quantityChange.emit($event)"
|
||||
(quantityChange)="quantityChange.emit({mixId: mix.id, mixMaterial: $event})"
|
||||
(locationChange)="locationChange.emit($event)"
|
||||
(deduct)="deduct.emit(mix.id)">
|
||||
</cre-mix-table>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {Recipe} from "../../../shared/model/recipe.model";
|
||||
import {MixMaterialDto, Recipe} from '../../../shared/model/recipe.model'
|
||||
import {Subject} from "rxjs";
|
||||
|
||||
@Component({
|
||||
|
@ -14,7 +14,7 @@ export class MixesCardComponent {
|
|||
@Input() editionMode = false
|
||||
|
||||
@Output() locationChange = new EventEmitter<{ id: number, location: string }>()
|
||||
@Output() quantityChange = new EventEmitter<{ id: number, materialId: number, quantity: number }>()
|
||||
@Output() quantityChange = new EventEmitter<{ mixId: number, mixMaterial: MixMaterialDto}>()
|
||||
@Output() deduct = new EventEmitter<number>()
|
||||
@Output() printingErrorChange = new EventEmitter<number>()
|
||||
}
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
</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 *ngFor="let step of steps">
|
||||
{{step.position}}.<span class="space"></span>{{step.message}}
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
|
||||
<p *ngIf="!selectedGroupId" class="empty-text text-center">Aucun groupe n'est sélectionné</p>
|
||||
<p *ngIf="selectedGroupId && steps.length === 0" class="empty-text text-center">Il n'y a aucune étape définie pour ce groupe</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, Input} from '@angular/core';
|
||||
import {RecipeStep} from "../../../shared/model/recipe.model";
|
||||
import {Recipe, RecipeStep, recipeStepsForGroupId} from '../../../shared/model/recipe.model'
|
||||
|
||||
@Component({
|
||||
selector: 'cre-step-list',
|
||||
|
@ -7,5 +7,10 @@ import {RecipeStep} from "../../../shared/model/recipe.model";
|
|||
styleUrls: ['./step-list.component.sass']
|
||||
})
|
||||
export class StepListComponent {
|
||||
@Input() steps: RecipeStep[]
|
||||
@Input() recipe: Recipe
|
||||
@Input() selectedGroupId: number | null
|
||||
|
||||
get steps(): RecipeStep[] {
|
||||
return recipeStepsForGroupId(this.recipe, this.selectedGroupId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,63 @@
|
|||
<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>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>Étapes</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="no-action">
|
||||
<mat-form-field>
|
||||
<mat-label>Groupe</mat-label>
|
||||
<mat-select [(ngModel)]="selectedGroupId">
|
||||
<mat-option *ngFor="let group of (groups$ | async)" [value]="group.id">
|
||||
{{group.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<table #stepTable mat-table [dataSource]="steps">
|
||||
<p *ngIf="!selectedGroupId" class="empty-text text-center">Aucun groupe n'est sélectionné</p>
|
||||
|
||||
<ng-container
|
||||
*ngIf="selectedGroupId"
|
||||
[ngTemplateOutlet]="stepTableTemplate">
|
||||
</ng-container>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<ng-template
|
||||
#stepTableTemplate>
|
||||
<table
|
||||
#matTable
|
||||
mat-table
|
||||
matSort
|
||||
[dataSource]="selectedGroupSteps">
|
||||
<ng-container matColumnDef="position">
|
||||
<th mat-header-cell *matHeaderCellDef>Position</th>
|
||||
<td mat-cell *matCellDef="let step; let i = index">{{i + 1}}</td>
|
||||
<td mat-cell *matCellDef="let step">{{step.position}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="buttonsPosition">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let step; let i = index">
|
||||
<ng-container *ngIf="(!hoveredStep && i === 0) || hoveredStep === step">
|
||||
<button
|
||||
mat-mini-fab
|
||||
color="primary"
|
||||
class="mr-1"
|
||||
[disabled]="step.position <= 1"
|
||||
(click)="decreasePosition(step, matTable)">
|
||||
<mat-icon svgIcon="arrow-up"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-mini-fab
|
||||
color="primary"
|
||||
[disabled]="step.position >= selectedGroupStepsCount"
|
||||
(click)="increasePosition(step, matTable)">
|
||||
<mat-icon svgIcon="arrow-down"></mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="message">
|
||||
<th mat-header-cell *matHeaderCellDef>Message</th>
|
||||
<th mat-header-cell *matHeaderCellDef>Contenu</th>
|
||||
<td mat-cell *matCellDef="let step">
|
||||
<mat-form-field>
|
||||
<input matInput type="text" [(ngModel)]="step.message"/>
|
||||
|
@ -20,11 +67,11 @@
|
|||
|
||||
<ng-container matColumnDef="buttonRemove">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<button mat-raised-button color="accent" (click)="addStep()">Ajouter</button>
|
||||
<button mat-raised-button color="accent" (click)="addStep(matTable)">Ajouter</button>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let step; let i = index">
|
||||
<ng-container *ngIf="(!hoveredStep && i === 0) || hoveredStep === step">
|
||||
<button mat-raised-button color="warn" (click)="removeStep(i)">Retirer</button>
|
||||
<button mat-raised-button color="warn" (click)="removeStep(i, matTable)">Retirer</button>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
@ -32,4 +79,4 @@
|
|||
<tr mat-header-row *matHeaderRowDef="columns"></tr>
|
||||
<tr mat-row *matRowDef="let step; columns: columns" (mouseover)="hoveredStep = step"></tr>
|
||||
</table>
|
||||
</mat-expansion-panel>
|
||||
</ng-template>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {Component, Input, ViewChild} from '@angular/core';
|
||||
import {RecipeStep} from "../../../shared/model/recipe.model";
|
||||
import {MatTable} from "@angular/material/table";
|
||||
import {Component, Input} from '@angular/core'
|
||||
import {Recipe, RecipeStep, recipeStepsForGroupId, sortRecipeSteps} from '../../../shared/model/recipe.model'
|
||||
import {MatTable} from '@angular/material/table'
|
||||
import {Observable} from 'rxjs'
|
||||
import {EmployeeGroup} from '../../../shared/model/employee'
|
||||
|
||||
@Component({
|
||||
selector: 'cre-step-table',
|
||||
|
@ -8,20 +10,89 @@ import {MatTable} from "@angular/material/table";
|
|||
styleUrls: ['./step-table.component.sass']
|
||||
})
|
||||
export class StepTableComponent {
|
||||
@ViewChild('stepTable', {static: true}) stepTable: MatTable<RecipeStep>
|
||||
readonly columns = ['position', 'message', 'buttonRemove']
|
||||
readonly columns = ['position', 'buttonsPosition', 'message', 'buttonRemove']
|
||||
|
||||
@Input() steps: RecipeStep[]
|
||||
@Input() recipe: Recipe
|
||||
@Input() groups$: Observable<EmployeeGroup[]>
|
||||
@Input() selectedGroupId: number | null
|
||||
|
||||
hoveredStep : RecipeStep | null
|
||||
hoveredStep: RecipeStep | null
|
||||
|
||||
addStep() {
|
||||
this.steps.push({id: null, message: ""})
|
||||
this.stepTable.renderRows()
|
||||
private groupSteps = new Map<number, RecipeStep[]>()
|
||||
|
||||
addStep(table: MatTable<any>) {
|
||||
const addedStep = new RecipeStep(null, '', this.selectedGroupSteps.length + 1)
|
||||
this.selectedGroupSteps.push(addedStep)
|
||||
table.renderRows()
|
||||
}
|
||||
|
||||
removeStep(position: number) {
|
||||
this.steps.splice(position, 1)
|
||||
this.stepTable.renderRows()
|
||||
removeStep(position: number, table: MatTable<any>) {
|
||||
this.selectedGroupSteps.splice(position, 1)
|
||||
|
||||
// Decreases the position of each step above the removed one
|
||||
for (let i = position; i < this.selectedGroupSteps.length; i++) {
|
||||
this.selectedGroupSteps[i].position -= 1
|
||||
}
|
||||
|
||||
table.renderRows()
|
||||
}
|
||||
|
||||
increasePosition(step: RecipeStep, table: MatTable<any>) {
|
||||
this.updateStepPosition(step, step.position + 1)
|
||||
this.sort(table)
|
||||
}
|
||||
|
||||
decreasePosition(step: RecipeStep, table: MatTable<any>) {
|
||||
this.updateStepPosition(step, step.position - 1)
|
||||
this.sort(table)
|
||||
}
|
||||
|
||||
sort(table: MatTable<any>) {
|
||||
this.groupSteps.set(this.selectedGroupId, sortRecipeSteps(this.selectedGroupSteps))
|
||||
table.renderRows()
|
||||
}
|
||||
|
||||
selectedGroupStepAtPosition(position: number): RecipeStep {
|
||||
return this.selectedGroupSteps.find(s => s.position === position)
|
||||
}
|
||||
|
||||
selectedGroupHasStepAtPosition(position: number): boolean {
|
||||
return this.selectedGroupStepAtPosition(position) != undefined
|
||||
}
|
||||
|
||||
get selectedGroupSteps(): RecipeStep[] {
|
||||
if (!this.groupSteps.has(this.selectedGroupId)) {
|
||||
this.groupSteps.set(this.selectedGroupId, recipeStepsForGroupId(this.recipe, this.selectedGroupId))
|
||||
}
|
||||
return this.groupSteps.get(this.selectedGroupId)
|
||||
}
|
||||
|
||||
get selectedGroupStepsCount(): number {
|
||||
return this.selectedGroupSteps.length
|
||||
}
|
||||
|
||||
get mappedUpdatedSteps(): Map<number, RecipeStep[]> {
|
||||
const updatedStepsMap = new Map<number, RecipeStep[]>()
|
||||
this.recipe.groupsInformation.forEach(i => {
|
||||
updatedStepsMap.set(i.group.id, i.steps)
|
||||
})
|
||||
|
||||
// Add steps for groups that were not already in the recipe
|
||||
this.groupSteps.forEach((steps, groupId) => {
|
||||
if (!updatedStepsMap.has(groupId)) {
|
||||
updatedStepsMap.set(groupId, steps)
|
||||
}
|
||||
})
|
||||
return updatedStepsMap
|
||||
}
|
||||
|
||||
private updateStepPosition(step: RecipeStep, updatedPosition: number) {
|
||||
if (!this.selectedGroupHasStepAtPosition(updatedPosition)) {
|
||||
step.position = updatedPosition
|
||||
} else {
|
||||
const conflictingStep = this.selectedGroupStepAtPosition(updatedPosition)
|
||||
conflictingStep.position = step.position
|
||||
step.position = updatedPosition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
<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)" [disabled]="editComponent.form && editComponent.form.invalid">Enregistrer</button>
|
||||
<button mat-raised-button color="warn" *ngIf="hasDeletePermission" (click)="delete()">Supprimer</button>
|
||||
<button mat-raised-button color="accent" (click)="submit(editComponent, stepTable)"
|
||||
[disabled]="editComponent.form && editComponent.form.invalid">Enregistrer
|
||||
</button>
|
||||
<button mat-raised-button color="warn" *ngIf="hasDeletePermission" (click)="confirmBoxComponent.show()">Supprimer</button>
|
||||
</div>
|
||||
<mat-form-field>
|
||||
<mat-label>Unités</mat-label>
|
||||
|
@ -36,7 +38,12 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<cre-step-table [steps]="recipe.steps"></cre-step-table>
|
||||
<cre-step-table
|
||||
#stepTable
|
||||
[recipe]="recipe"
|
||||
[groups$]="groups$"
|
||||
[selectedGroupId]="loggedInEmployeeGroupId">
|
||||
</cre-step-table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -44,3 +51,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<cre-confirm-box #confirmBoxComponent message="Voulez-vous vraiment supprimer la couleur {{recipe.name}}?" (confirm)="delete()"></cre-confirm-box>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, ViewChild} from '@angular/core'
|
||||
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
|
||||
import {Recipe} from '../../../shared/model/recipe.model'
|
||||
import {Recipe, recipeMixCount, RecipeStep, recipeStepCount} from '../../../shared/model/recipe.model'
|
||||
import {RecipeService} from '../../services/recipe.service'
|
||||
import {ActivatedRoute, Router} from '@angular/router'
|
||||
import {Validators} from '@angular/forms'
|
||||
|
@ -12,6 +12,9 @@ import {EntityEditComponent} from '../../../shared/components/entity-edit/entity
|
|||
import {ImagesEditorComponent} from '../../components/images-editor/images-editor.component'
|
||||
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
|
||||
import {AlertService} from '../../../shared/service/alert.service'
|
||||
import {GroupService} from '../../../groups/services/group.service'
|
||||
import {AppState} from '../../../shared/app-state'
|
||||
import {StepTableComponent} from '../../components/step-table/step-table.component'
|
||||
|
||||
@Component({
|
||||
selector: 'cre-edit',
|
||||
|
@ -24,6 +27,7 @@ export class EditComponent extends ErrorHandlingComponent {
|
|||
@ViewChild('imagesEditor') imagesEditor: ImagesEditorComponent
|
||||
|
||||
recipe: Recipe | null
|
||||
groups$ = this.groupService.all
|
||||
formFields = [
|
||||
{
|
||||
name: 'name',
|
||||
|
@ -108,8 +112,10 @@ export class EditComponent extends ErrorHandlingComponent {
|
|||
|
||||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
private groupService: GroupService,
|
||||
private accountService: AccountService,
|
||||
private alertService: AlertService,
|
||||
private appState: AppState,
|
||||
errorService: ErrorService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
|
@ -125,10 +131,10 @@ export class EditComponent extends ErrorHandlingComponent {
|
|||
parseInt(this.activatedRoute.snapshot.paramMap.get('id')),
|
||||
recipe => {
|
||||
this.recipe = recipe
|
||||
if (this.recipe.mixes.length == 0) {
|
||||
if (recipeMixCount(this.recipe) == 0) {
|
||||
this.alertService.pushWarning('Il n\'y a aucun mélange dans cette recette')
|
||||
}
|
||||
if (this.recipe.steps.length == 0) {
|
||||
if (recipeStepCount(this.recipe) == 0) {
|
||||
this.alertService.pushWarning('Il n\'y a aucune étape dans cette recette')
|
||||
}
|
||||
},
|
||||
|
@ -140,11 +146,18 @@ export class EditComponent extends ErrorHandlingComponent {
|
|||
this.units$.next(unit)
|
||||
}
|
||||
|
||||
submit(editComponent: EntityEditComponent) {
|
||||
submit(editComponent: EntityEditComponent, stepTable: StepTableComponent) {
|
||||
const values = editComponent.values
|
||||
this.submittedValues = values
|
||||
|
||||
const steps = stepTable.mappedUpdatedSteps
|
||||
if (!this.stepsPositionsAreValid(steps)) {
|
||||
this.alertService.pushError('Les étapes ne peuvent pas avoir une position inférieure à 1')
|
||||
return
|
||||
}
|
||||
|
||||
this.subscribeAndNavigate(
|
||||
this.recipeService.update(this.recipe.id, values.name, values.description, values.color, values.gloss, values.sample, values.approbationDate, values.remark, this.recipe.steps),
|
||||
this.recipeService.update(this.recipe.id, values.name, values.description, values.color, values.gloss, values.sample, values.approbationDate, values.remark, steps),
|
||||
'/color/list'
|
||||
)
|
||||
}
|
||||
|
@ -159,4 +172,19 @@ export class EditComponent extends ErrorHandlingComponent {
|
|||
get hasDeletePermission(): boolean {
|
||||
return this.accountService.hasPermission(EmployeePermission.REMOVE_RECIPE)
|
||||
}
|
||||
|
||||
get loggedInEmployeeGroupId(): number {
|
||||
return this.appState.authenticatedEmployee.group?.id
|
||||
}
|
||||
|
||||
private stepsPositionsAreValid(steps: Map<number, RecipeStep[]>): boolean {
|
||||
let valid = true
|
||||
steps.forEach((steps, _) => {
|
||||
if (steps.find(s => s.position === 0)) {
|
||||
valid = false
|
||||
return
|
||||
}
|
||||
})
|
||||
return valid
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,14 +10,30 @@
|
|||
Enregistrer
|
||||
</button>
|
||||
</div>
|
||||
<cre-unit-selector (unitChange)="changeUnits($event)"></cre-unit-selector>
|
||||
|
||||
<div>
|
||||
<cre-unit-selector (unitChange)="changeUnits($event)"></cre-unit-selector>
|
||||
<mat-form-field class="ml-3">
|
||||
<mat-label>Groupe</mat-label>
|
||||
<mat-select [(ngModel)]="selectedGroupId">
|
||||
<mat-option *ngFor="let group of (groups$ | async)" [value]="group.id">
|
||||
{{group.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</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>
|
||||
<textarea
|
||||
matInput
|
||||
cols="40" rows="3"
|
||||
[(ngModel)]="selectedGroupNote"
|
||||
(keyup)="hasModifications = true">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
@ -35,13 +51,13 @@
|
|||
</div>
|
||||
|
||||
<!-- Steps -->
|
||||
<div *ngIf="recipe.steps.length > 0">
|
||||
<cre-step-list [steps]="recipe.steps"></cre-step-list>
|
||||
<div>
|
||||
<cre-step-list [recipe]="recipe" [selectedGroupId]="selectedGroupId"></cre-step-list>
|
||||
</div>
|
||||
|
||||
<!-- Images -->
|
||||
<div>
|
||||
<cre-images-editor [recipe]="recipe" [editionMode]="false"></cre-images-editor>
|
||||
<div [hidden]="!images.hasImages">
|
||||
<cre-images-editor #images [recipe]="recipe" [editionMode]="false"></cre-images-editor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,13 +2,15 @@ import {Component} from '@angular/core'
|
|||
import {RecipeService} from '../../services/recipe.service'
|
||||
import {ActivatedRoute, Router} from '@angular/router'
|
||||
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
|
||||
import {Recipe} from '../../../shared/model/recipe.model'
|
||||
import {MixMaterialDto, Recipe, recipeMixCount, recipeNoteForGroupId, recipeStepCount} from '../../../shared/model/recipe.model'
|
||||
import {Observable, Subject} from 'rxjs'
|
||||
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
|
||||
import {AlertService} from '../../../shared/service/alert.service'
|
||||
import {GlobalAlertHandlerComponent} from '../../../shared/components/global-alert-handler/global-alert-handler.component'
|
||||
import {InventoryService} from '../../../material/service/inventory.service'
|
||||
import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confirm-box.component'
|
||||
import {GroupService} from '../../../groups/services/group.service'
|
||||
import {AppState} from '../../../shared/app-state'
|
||||
|
||||
@Component({
|
||||
selector: 'cre-explore',
|
||||
|
@ -17,13 +19,15 @@ import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confir
|
|||
})
|
||||
export class ExploreComponent extends ErrorHandlingComponent {
|
||||
recipe: Recipe | null
|
||||
groups$ = this.groupService.all
|
||||
deductErrorBody = {}
|
||||
units$ = new Subject<string>()
|
||||
selectedGroupId: number | null
|
||||
|
||||
hasModifications = false
|
||||
note: string | null
|
||||
quantitiesChanges = new Map<number, Map<number, number>>()
|
||||
mixesLocationChanges = new Map<number, string>()
|
||||
groupsNote = new Map<number, string>()
|
||||
|
||||
deductedMixId: number | null
|
||||
handledErrorModels: ErrorModel[] = [{
|
||||
|
@ -35,7 +39,9 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
private inventoryService: InventoryService,
|
||||
private groupService: GroupService,
|
||||
private alertService: AlertService,
|
||||
private appState: AppState,
|
||||
errorService: ErrorService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
|
@ -47,15 +53,16 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
super.ngOnInit()
|
||||
GlobalAlertHandlerComponent.extraTopMarginMultiplier = 2.5
|
||||
|
||||
this.selectedGroupId = this.loggedInEmployeeGroupId
|
||||
|
||||
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
|
||||
this.subscribeEntityById(
|
||||
this.recipeService,
|
||||
id,
|
||||
r => {
|
||||
this.recipe = r
|
||||
this.note = r.note
|
||||
|
||||
if (this.recipe.mixes.length <= 0 || this.recipe.steps.length <= 0) {
|
||||
if (recipeMixCount(this.recipe) <= 0 || recipeStepCount(this.recipe) <= 0) {
|
||||
this.alertService.pushWarning('Cette recette n\'est pas complète')
|
||||
}
|
||||
},
|
||||
|
@ -72,16 +79,11 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
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>())
|
||||
changeQuantity(event: { mixId: number, mixMaterial: MixMaterialDto}) {
|
||||
if (!this.quantitiesChanges.has(event.mixId)) {
|
||||
this.quantitiesChanges.set(event.mixId, new Map<number, number>())
|
||||
}
|
||||
this.quantitiesChanges.get(event.id).set(event.materialId, event.quantity)
|
||||
this.quantitiesChanges.get(event.mixId).set(event.mixMaterial.materialId, event.mixMaterial.quantity)
|
||||
}
|
||||
|
||||
changeMixLocation(event: { id: number, location: string }) {
|
||||
|
@ -91,8 +93,11 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
|
||||
saveModifications() {
|
||||
this.subscribe(
|
||||
this.recipeService.saveExplorerModifications(this.recipe.id, this.note, this.mixesLocationChanges),
|
||||
() => this.alertService.pushSuccess('Les modifications ont été enregistrées'),
|
||||
this.recipeService.updateExplorerModifications(this.recipe.id, this.mappedUpdatedNotes, this.mixesLocationChanges),
|
||||
() => {
|
||||
this.hasModifications = false
|
||||
this.alertService.pushSuccess('Les modifications ont été enregistrées')
|
||||
},
|
||||
true
|
||||
)
|
||||
}
|
||||
|
@ -120,4 +125,33 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
true
|
||||
)
|
||||
}
|
||||
|
||||
get loggedInEmployeeGroupId(): number {
|
||||
return this.appState.authenticatedEmployee.group?.id
|
||||
}
|
||||
|
||||
get selectedGroupNote(): string {
|
||||
if (!this.groupsNote.has(this.selectedGroupId)) {
|
||||
this.groupsNote.set(this.selectedGroupId, recipeNoteForGroupId(this.recipe, this.selectedGroupId))
|
||||
}
|
||||
return this.groupsNote.get(this.selectedGroupId)
|
||||
}
|
||||
|
||||
set selectedGroupNote(value: string) {
|
||||
this.groupsNote.set(this.selectedGroupId, value)
|
||||
}
|
||||
|
||||
private get mappedUpdatedNotes(): Map<number, string> {
|
||||
const updatedNotes = new Map<number, string>()
|
||||
|
||||
this.recipe.groupsInformation.forEach(i => {
|
||||
updatedNotes.set(i.group.id, i.note)
|
||||
})
|
||||
|
||||
this.groupsNote.forEach((content, groupId) => {
|
||||
updatedNotes.set(groupId, content)
|
||||
})
|
||||
|
||||
return updatedNotes
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import {Material} from '../../../../shared/model/material.model'
|
|||
import {MaterialService} from '../../../../material/service/material.service'
|
||||
import {MixService} from '../../../services/mix.service'
|
||||
import {ErrorModel, ErrorService} from '../../../../shared/service/error.service'
|
||||
import {MixMaterialDto} from '../../../../shared/model/recipe.model'
|
||||
import {AlertService} from '../../../../shared/service/alert.service'
|
||||
|
||||
@Component({
|
||||
selector: 'cre-mix-edit',
|
||||
|
@ -21,6 +23,7 @@ export class MixEditComponent extends ErrorHandlingComponent {
|
|||
constructor(
|
||||
private materialService: MaterialService,
|
||||
private mixService: MixService,
|
||||
private alertService: AlertService,
|
||||
errorService: ErrorService,
|
||||
router: Router,
|
||||
activatedRoute: ActivatedRoute
|
||||
|
@ -41,9 +44,18 @@ export class MixEditComponent extends ErrorHandlingComponent {
|
|||
}
|
||||
|
||||
submit(values) {
|
||||
if(!this.mixMaterialsPositionAreValid(values.mixMaterials)) {
|
||||
this.alertService.pushError('Les ingrédients ne peuvent pas avoir une position inférieure à 1')
|
||||
return
|
||||
}
|
||||
|
||||
this.subscribeAndNavigate(
|
||||
this.mixService.updateWithUnits(this.mixId, values.name, values.materialTypeId, values.mixMaterials, values.units),
|
||||
`/color/edit/${this.recipeId}`
|
||||
)
|
||||
}
|
||||
|
||||
private mixMaterialsPositionAreValid(mixMaterials: MixMaterialDto[]): boolean {
|
||||
return !mixMaterials.find(m => m.position <= 0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
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";
|
||||
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, MixMaterialDto} from '../../shared/model/recipe.model'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -21,32 +21,33 @@ export class MixService {
|
|||
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> {
|
||||
saveWithUnits(name: string, recipeId: number, materialTypeId: number, mixMaterials: MixMaterialDto[], 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> {
|
||||
save(name: string, recipeId: number, materialTypeId: number, mixMaterials: MixMaterialDto[]): Observable<void> {
|
||||
const body = {
|
||||
name,
|
||||
recipeId,
|
||||
materialTypeId,
|
||||
mixMaterials: {}
|
||||
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> {
|
||||
updateWithUnits(id: number, name: string, materialTypeId: number, mixMaterials: MixMaterialDto[], 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> {
|
||||
update(id: number, name: string, materialTypeId: number, mixMaterials: MixMaterialDto[]): Observable<void> {
|
||||
const body = {
|
||||
id,
|
||||
name,
|
||||
materialTypeId,
|
||||
mixMaterials: {}
|
||||
mixMaterials: []
|
||||
}
|
||||
|
||||
this.appendMixMaterialsToBody(mixMaterials, body)
|
||||
return this.api.put('/recipe/mix', body)
|
||||
}
|
||||
|
@ -55,25 +56,21 @@ export class MixService {
|
|||
return this.api.delete(`/recipe/mix/${id}`)
|
||||
}
|
||||
|
||||
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 }[] {
|
||||
private convertMixMaterialsToMl(mixMaterials: MixMaterialDto[], units: string): MixMaterialDto[] {
|
||||
return mixMaterials.map(m => {
|
||||
return {
|
||||
materialId: m.materialId,
|
||||
quantity: convertMixMaterialQuantity(m, units, UNIT_MILLILITER)
|
||||
}
|
||||
m.quantity = convertMixMaterialQuantity(m, units, UNIT_MILLILITER)
|
||||
return m
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
private appendMixMaterialsToBody(mixMaterials: MixMaterialDto[], body: any) {
|
||||
mixMaterials.filter(m => m.materialId != null && m.quantity != null).forEach(m => {
|
||||
body.mixMaterials.push({
|
||||
materialId: m.materialId,
|
||||
quantity: m.quantity,
|
||||
position: m.position
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import {ApiService} from '../../shared/service/api.service'
|
|||
import {Observable} from 'rxjs'
|
||||
import {Recipe, RecipeStep} from '../../shared/model/recipe.model'
|
||||
import {map} from 'rxjs/operators'
|
||||
import {MaterialQuantity} from '../../material/service/inventory.service'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -44,22 +43,37 @@ export class RecipeService {
|
|||
return this.api.post<Recipe>('/recipe', body)
|
||||
}
|
||||
|
||||
update(id: number, name: string, description: string, color: string, gloss: number, sample: number, approbationDate: string, remark: string, steps: RecipeStep[] = null) {
|
||||
const body = {id, name, description, color, gloss, sample, remark, steps}
|
||||
update(id: number, name: string, description: string, color: string, gloss: number, sample: number, approbationDate: string, remark: string, steps: Map<number, RecipeStep[]>) {
|
||||
const body = {id, name, description, color, gloss, sample, remark, steps: []}
|
||||
if (approbationDate) {
|
||||
// @ts-ignore
|
||||
body.approbationDate = approbationDate
|
||||
}
|
||||
|
||||
steps.forEach((groupSteps, groupId) => {
|
||||
const mappedGroupSteps = groupSteps.map(s => {
|
||||
return {message: s.message, position: s.position}
|
||||
})
|
||||
body.steps.push({groupId, steps: mappedGroupSteps})
|
||||
})
|
||||
|
||||
return this.api.put<Recipe>('/recipe', body)
|
||||
}
|
||||
|
||||
saveExplorerModifications(id: number, note: string, mixesLocationChange: Map<number, string>): Observable<void> {
|
||||
updateExplorerModifications(id: number, notes: Map<number, string>, mixesLocationChange: Map<number, string>): Observable<void> {
|
||||
const body = {
|
||||
id,
|
||||
note,
|
||||
mixesLocation: {}
|
||||
recipeId: id,
|
||||
notes: [],
|
||||
mixesLocation: []
|
||||
}
|
||||
mixesLocationChange.forEach((l, i) => body.mixesLocation[i] = l)
|
||||
|
||||
notes.forEach((content, groupId) => {
|
||||
body.notes.push({groupId, content})
|
||||
})
|
||||
|
||||
mixesLocationChange.forEach((location, mixId) => {
|
||||
body.mixesLocation.push({mixId, location})
|
||||
})
|
||||
|
||||
return this.api.put<void>('/recipe/public', body)
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ export class AddComponent extends ErrorHandlingComponent {
|
|||
icon: 'account-multiple',
|
||||
type: 'select',
|
||||
defaultValue: -1,
|
||||
options$: this.groupService.all.pipe(map(groups => groups.map(g => {
|
||||
options$: this.groupService.allWithDefault.pipe(map(groups => groups.map(g => {
|
||||
return {value: g.id, label: g.name}
|
||||
})))
|
||||
}, {
|
||||
|
|
|
@ -49,7 +49,7 @@ export class EditComponent extends ErrorHandlingComponent {
|
|||
icon: 'account-multiple',
|
||||
type: 'select',
|
||||
valueFn: employee => employee.group ? employee.group.id : -1,
|
||||
options$: this.groupService.all.pipe(map(groups => groups.map(g => {
|
||||
options$: this.groupService.allWithDefault.pipe(map(groups => groups.map(g => {
|
||||
return {value: g.id, label: g.name}
|
||||
})))
|
||||
}, {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {Component} from '@angular/core'
|
||||
import {GroupService} from '../../services/group.service'
|
||||
import {EmployeeGroup, EmployeePermission} from '../../../shared/model/employee'
|
||||
import {map} from 'rxjs/operators'
|
||||
import {AccountService} from '../../../accounts/services/account.service'
|
||||
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
|
||||
import {ActivatedRoute, Router} from '@angular/router'
|
||||
|
@ -14,7 +13,7 @@ import {AlertService} from '../../../shared/service/alert.service'
|
|||
styleUrls: ['./list.component.sass']
|
||||
})
|
||||
export class ListComponent extends ErrorHandlingComponent {
|
||||
groups$ = this.groupService.all.pipe(map(groups => groups.filter(g => g.id >= 0)))
|
||||
groups$ = this.groupService.all
|
||||
defaultGroup: EmployeeGroup = null
|
||||
columns = [
|
||||
{def: 'name', title: 'Nom', valueFn: g => g.name},
|
||||
|
|
|
@ -15,6 +15,10 @@ export class GroupService {
|
|||
|
||||
get all(): Observable<EmployeeGroup[]> {
|
||||
return this.api.get<EmployeeGroup[]>('/employee/group')
|
||||
}
|
||||
|
||||
get allWithDefault(): Observable<EmployeeGroup[]> {
|
||||
return this.all
|
||||
.pipe(tap(groups => groups.unshift({
|
||||
id: -1,
|
||||
name: 'Aucun',
|
||||
|
|
|
@ -17,6 +17,7 @@ export class PermissionsListComponent {
|
|||
}
|
||||
|
||||
get permissions(): EmployeePermission[] {
|
||||
// @ts-ignore
|
||||
return this.filterPermissions(this.employee ? this.employee.permissions : this.group.permissions)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {Material} from "./material.model";
|
||||
import {LocalDate} from "js-joda";
|
||||
import {Company} from "./company.model";
|
||||
import {Material} from './material.model'
|
||||
import {LocalDate} from 'js-joda'
|
||||
import {Company} from './company.model'
|
||||
import {EmployeeGroup} from './employee'
|
||||
|
||||
export class Recipe {
|
||||
constructor(
|
||||
|
@ -12,9 +13,18 @@ export class Recipe {
|
|||
public sample: number,
|
||||
public approbationDate: LocalDate,
|
||||
public remark: string,
|
||||
public note: string,
|
||||
public company: Company,
|
||||
public mixes: Mix[],
|
||||
public groupsInformation: RecipeGroupInformation[]
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class RecipeGroupInformation {
|
||||
constructor(
|
||||
public id: number,
|
||||
public group: EmployeeGroup,
|
||||
public note: string,
|
||||
public steps: RecipeStep[]
|
||||
) {
|
||||
}
|
||||
|
@ -34,7 +44,18 @@ export class MixMaterial {
|
|||
constructor(
|
||||
public id: number,
|
||||
public material: Material,
|
||||
public quantity: number
|
||||
public quantity: number,
|
||||
public position: number
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class MixMaterialDto {
|
||||
constructor(
|
||||
public materialId: number,
|
||||
public quantity: number,
|
||||
public isPercents: boolean,
|
||||
public position: number
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +72,44 @@ class MixType {
|
|||
export class RecipeStep {
|
||||
constructor(
|
||||
public id: number,
|
||||
public message: string
|
||||
public message: string,
|
||||
public position: number
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export function recipeMixCount(recipe: Recipe): number {
|
||||
return recipe.mixes.length
|
||||
}
|
||||
|
||||
export function recipeStepCount(recipe: Recipe): number {
|
||||
return recipe.groupsInformation.length > 0 ? recipe.groupsInformation.flatMap(i => i.steps).length : 0
|
||||
}
|
||||
|
||||
export function recipeNoteForGroupId(recipe: Recipe, groupId: number): string | null {
|
||||
const groupInformation = recipe.groupsInformation.find(i => i.group.id === groupId)
|
||||
return groupInformation ? groupInformation.note : null
|
||||
}
|
||||
|
||||
export function recipeStepsForGroupId(recipe: Recipe, groupId: number): RecipeStep[] {
|
||||
const groupInformation = recipe.groupsInformation.find(i => i.group.id === groupId)
|
||||
return groupInformation ? sortRecipeSteps(groupInformation.steps) : []
|
||||
}
|
||||
|
||||
export function sortRecipeSteps(steps: RecipeStep[]): RecipeStep[] {
|
||||
return steps.sort((a, b) => a.position - b.position)
|
||||
}
|
||||
|
||||
export function mixMaterialsAsMixMaterialsDto(mix: Mix): MixMaterialDto[] {
|
||||
return sortMixMaterialsDto(mix.mixMaterials.map(m => new MixMaterialDto(
|
||||
m.material.id,
|
||||
m.quantity,
|
||||
m.material.materialType.usePercentages,
|
||||
m.position
|
||||
)))
|
||||
}
|
||||
|
||||
export function sortMixMaterialsDto(mixMaterials: MixMaterialDto[]): MixMaterialDto[] {
|
||||
return mixMaterials.sort((a, b) => a.position - b.position)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {MixMaterialDto} from './model/recipe.model'
|
||||
|
||||
export const UNIT_MILLILITER = 'mL'
|
||||
export const UNIT_LITER = 'L'
|
||||
export const UNIT_GALLON = 'gal'
|
||||
|
@ -23,8 +25,8 @@ export const UNIT_RATIOS = {
|
|||
}
|
||||
}
|
||||
|
||||
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 convertMixMaterialQuantity(computedQuantity: MixMaterialDto, from: string, to: string): number {
|
||||
return !computedQuantity.isPercents ? convertQuantity(computedQuantity.quantity, from, to) : computedQuantity.quantity
|
||||
}
|
||||
|
||||
export function convertQuantity(quantity: number, from: string, to: string): number {
|
||||
|
|
|
@ -85,6 +85,15 @@ mat-expansion-panel.table-title
|
|||
&:hover, &:focus
|
||||
background-color: $color-primary !important
|
||||
|
||||
&.header-field .mat-form-field-flex
|
||||
background-color: white
|
||||
|
||||
.mat-form-field-outline
|
||||
opacity: 1
|
||||
|
||||
&:first-child
|
||||
opacity: 0
|
||||
|
||||
mat-panel-title
|
||||
color: $light-primary-text !important
|
||||
font-weight: bold
|
||||
|
@ -182,6 +191,9 @@ div.empty
|
|||
.alert p
|
||||
margin-bottom: 0
|
||||
|
||||
.empty-text
|
||||
color: rgba(0, 0, 0, 0.54)
|
||||
|
||||
.dark-background
|
||||
position: fixed
|
||||
width: 100%
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"es2019",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue