diff --git a/src/app/modules/configuration/config-editor.ts b/src/app/modules/configuration/config-editor.ts
index bde9502..b12502a 100644
--- a/src/app/modules/configuration/config-editor.ts
+++ b/src/app/modules/configuration/config-editor.ts
@@ -16,8 +16,8 @@ export class CreConfigEditor extends ErrorHandlingComponent {
keys = {
INSTANCE_NAME: Config.INSTANCE_NAME,
- INSTANCE_LOGO_PATH: Config.INSTANCE_LOGO_PATH,
- INSTANCE_ICON_PATH: Config.INSTANCE_ICON_PATH,
+ INSTANCE_LOGO_SET: Config.INSTANCE_LOGO_SET,
+ INSTANCE_ICON_SET: Config.INSTANCE_ICON_SET,
INSTANCE_URL: Config.INSTANCE_URL,
DATABASE_URL: Config.DATABASE_URL,
DATABASE_USER: Config.DATABASE_USER,
diff --git a/src/app/modules/configuration/config-image.html b/src/app/modules/configuration/config-image.html
index 084aad1..c801323 100644
--- a/src/app/modules/configuration/config-image.html
+++ b/src/app/modules/configuration/config-image.html
@@ -18,7 +18,7 @@
diff --git a/src/app/modules/configuration/config.ts b/src/app/modules/configuration/config.ts
index 29acafb..d40479e 100644
--- a/src/app/modules/configuration/config.ts
+++ b/src/app/modules/configuration/config.ts
@@ -1,10 +1,20 @@
-import {AfterViewInit, Component, ContentChild, Directive, EventEmitter, Input, Output, ViewChild, ViewEncapsulation} from '@angular/core'
+import {
+ AfterViewInit,
+ Component,
+ ContentChild,
+ Directive,
+ EventEmitter,
+ Input,
+ Output,
+ ViewChild,
+ ViewEncapsulation
+} from '@angular/core'
import {ConfigService} from '../shared/service/config.service'
import {Config, ConfigControl} from '../shared/model/config.model'
import {SubscribingComponent} from '../shared/components/subscribing.component'
import {ErrorService} from '../shared/service/error.service'
import {ActivatedRoute, Router} from '@angular/router'
-import {formatDate, formatDateTime, getFileUrl, readFile} from '../shared/utils/utils'
+import {formatDate, formatDateTime, getConfiguredImageUrl, getFileUrl, readFile} from '../shared/utils/utils'
import {AbstractControl} from '@angular/forms'
import {CrePromptDialog} from '../shared/components/dialogs/dialogs'
@@ -121,9 +131,9 @@ export class CreImageConfig extends _CreConfigBase {
readFile(file, (content) => this.updatedImage = content)
}
-
- get configuredImageUrl(): string {
- return getFileUrl(this.config.content)
+ get imageUrl(): string {
+ const path = this.config.key == Config.INSTANCE_ICON_SET ? 'icon' : 'logo'
+ return getConfiguredImageUrl(path)
}
}
diff --git a/src/app/modules/material-type/material-type.module.ts b/src/app/modules/material-type/material-type.module.ts
index 591a8a8..1df904c 100644
--- a/src/app/modules/material-type/material-type.module.ts
+++ b/src/app/modules/material-type/material-type.module.ts
@@ -6,6 +6,9 @@ import { ListComponent } from './pages/list/list.component';
import {SharedModule} from "../shared/shared.module";
import { AddComponent } from './pages/add/add.component';
import { EditComponent } from './pages/edit/edit.component';
+import {CreActionBarModule} from '../shared/components/action-bar/action-bar.module'
+import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
+import {CreTablesModule} from '../shared/components/tables/tables.module'
@NgModule({
@@ -13,7 +16,10 @@ import { EditComponent } from './pages/edit/edit.component';
imports: [
CommonModule,
MaterialTypeRoutingModule,
- SharedModule
+ SharedModule,
+ CreActionBarModule,
+ CreButtonsModule,
+ CreTablesModule
]
})
export class MaterialTypeModule { }
diff --git a/src/app/modules/material-type/pages/list/list.component.html b/src/app/modules/material-type/pages/list/list.component.html
index f70251f..bf32816 100644
--- a/src/app/modules/material-type/pages/list/list.component.html
+++ b/src/app/modules/material-type/pages/list/list.component.html
@@ -1,7 +1,40 @@
-
-
+
+
+ Ajouter
+
+
+
+
+ Il n'y a actuellement aucun type de produit enregistré dans le système.
+ Vous pouvez en créer un ici.
+
+
+
+
+ Nom |
+ {{materialType.name}} |
+
+
+
+ Préfix |
+ {{materialType.prefix}} |
+
+
+
+ Utilise les pourcentages |
+ {{materialType.usePercentages ? 'Oui' : 'Non'}} |
+
+
+
+ |
+
+
+ Modifier
+
+ |
+
+
diff --git a/src/app/modules/material-type/pages/list/list.component.ts b/src/app/modules/material-type/pages/list/list.component.ts
index 2556388..c25e58f 100644
--- a/src/app/modules/material-type/pages/list/list.component.ts
+++ b/src/app/modules/material-type/pages/list/list.component.ts
@@ -5,6 +5,8 @@ import {Permission} from '../../../shared/model/user'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
import {AppState} from '../../../shared/app-state'
+import {tap} from 'rxjs/operators'
+import {AccountService} from '../../../accounts/services/account.service'
@Component({
selector: 'cre-list',
@@ -12,23 +14,16 @@ import {AppState} from '../../../shared/app-state'
styleUrls: ['./list.component.sass']
})
export class ListComponent extends ErrorHandlingComponent {
- materialTypes$ = this.materialTypeService.all
- columns = [
- {def: 'name', title: 'Nom', valueFn: t => t.name},
- {def: 'prefix', title: 'Préfixe', valueFn: t => t.prefix},
- {def: 'usePercentages', title: 'Utilise les pourcentages', valueFn: t => t.usePercentages ? 'Oui' : 'Non'}
- ]
- buttons = [
- {
- text: 'Modifier',
- linkFn: t => `/catalog/materialtype/edit/${t.id}`,
- permission: Permission.EDIT_MATERIAL_TYPES,
- disabledFn: t => t.systemType
- }
- ]
+ materialTypes$ = this.materialTypeService.all.pipe(
+ tap(materialTypes => this.materialTypesEmpty = materialTypes.length <= 0)
+ )
+ materialTypesEmpty = false
+
+ columns = ['name', 'prefix', 'usePercentages', 'editButton']
constructor(
private materialTypeService: MaterialTypeService,
+ private accountService: AccountService,
private appState: AppState,
errorService: ErrorService,
router: Router,
@@ -37,4 +32,8 @@ export class ListComponent extends ErrorHandlingComponent {
super(errorService, activatedRoute, router)
this.appState.title = 'Types de produit'
}
+
+ get hasEditPermission(): boolean {
+ return this.accountService.hasPermission(Permission.EDIT_COMPANIES)
+ }
}
diff --git a/src/app/modules/material/material.module.ts b/src/app/modules/material/material.module.ts
index 6ccb444..782b3ef 100644
--- a/src/app/modules/material/material.module.ts
+++ b/src/app/modules/material/material.module.ts
@@ -6,9 +6,13 @@ import {InventoryComponent} from './pages/inventory/inventory.component';
import {SharedModule} from "../shared/shared.module";
import {AddComponent} from './pages/add/add.component';
import {EditComponent} from './pages/edit/edit.component';
-import {ColorsModule} from '../colors/colors.module'
+import {RecipesModule} from '../recipes/recipes.module'
import {MatSortModule} from '@angular/material/sort'
import {FormsModule} from '@angular/forms'
+import {CreTablesModule} from '../shared/components/tables/tables.module'
+import {CreInputsModule} from '../shared/components/inputs/inputs.module'
+import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
+import {CreActionBarModule} from '../shared/components/action-bar/action-bar.module'
@NgModule({
@@ -17,9 +21,13 @@ import {FormsModule} from '@angular/forms'
CommonModule,
MaterialRoutingModule,
SharedModule,
- ColorsModule,
+ RecipesModule,
MatSortModule,
- FormsModule
+ FormsModule,
+ CreTablesModule,
+ CreInputsModule,
+ CreButtonsModule,
+ CreActionBarModule
]
})
export class MaterialModule {
diff --git a/src/app/modules/material/pages/inventory/inventory.component.html b/src/app/modules/material/pages/inventory/inventory.component.html
index a964091..130ff57 100644
--- a/src/app/modules/material/pages/inventory/inventory.component.html
+++ b/src/app/modules/material/pages/inventory/inventory.component.html
@@ -1,62 +1,59 @@
-
+
+
+
-
+ Il n'y a actuellement aucun produit enregistré dans le système.
+ Vous pouvez en créer un ici.
+
+
+
+ 0"
class="mx-auto"
- [dataSource]="dataSource">
+ [filterPredicate]="materialFilterPredicate"
+ [filter]="filter"
+ [data]="materials"
+ [columns]="columns">
- Code |
+ Code |
{{material.name}} |
- Type de produit |
+ Type de produit |
{{material.materialType.name}} |
@@ -68,9 +65,7 @@
|
- |
+
diff --git a/src/app/modules/material/pages/inventory/inventory.component.sass b/src/app/modules/material/pages/inventory/inventory.component.sass
index 20ded20..5867821 100644
--- a/src/app/modules/material/pages/inventory/inventory.component.sass
+++ b/src/app/modules/material/pages/inventory/inventory.component.sass
@@ -1,8 +1,9 @@
-.input-group-append button
- border-radius: 0 4px 4px 0
-
mat-select
margin-top: 4px
-.form-control
- width: 6rem
+.input-group
+ cre-input
+ width: 6rem
+
+ .input-group-append button
+ border-radius: 0 4px 4px 0
diff --git a/src/app/modules/material/pages/inventory/inventory.component.ts b/src/app/modules/material/pages/inventory/inventory.component.ts
index aafe764..81f914f 100644
--- a/src/app/modules/material/pages/inventory/inventory.component.ts
+++ b/src/app/modules/material/pages/inventory/inventory.component.ts
@@ -4,14 +4,17 @@ import {MaterialService} from '../../service/material.service'
import {Permission} from '../../../shared/model/user'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
-import {Material, openSimdut} from '../../../shared/model/material.model'
+import {Material, materialFilterFieldSeparator, materialMatchesFilter, openSimdut} from '../../../shared/model/material.model'
import {AccountService} from '../../../accounts/services/account.service'
import {convertQuantity, UNIT_MILLILITER} from '../../../shared/units'
import {MatSort} from '@angular/material/sort'
-import {MatTableDataSource} from '@angular/material/table'
import {MaterialTypeService} from '../../../material-type/service/material-type.service'
import {InventoryService} from '../../service/inventory.service'
import {AppState} from '../../../shared/app-state'
+import {FormControl} from '@angular/forms'
+import {map} from 'rxjs/operators'
+import {CreInputEntry} from '../../../shared/components/inputs/inputs'
+import {round} from '../../../shared/utils/utils'
@Component({
selector: 'cre-list',
@@ -21,9 +24,10 @@ import {AppState} from '../../../shared/app-state'
export class InventoryComponent extends ErrorHandlingComponent {
@ViewChild(MatSort) sort: MatSort
- materials: Material[] | null
- materialTypes$ = this.materialTypeService.all
- dataSource: MatTableDataSource
+ materials: Material[] | null = []
+ materialTypesEntries$ = this.materialTypeService.all.pipe(map(materialTypes => {
+ return materialTypes.map(materialType => new CreInputEntry(materialType.id, materialType.name))
+ }))
columns = ['name', 'materialType', 'quantity', 'addQuantity', 'lowQuantityIcon', 'simdutIcon', 'editButton', 'openSimdutButton']
hoveredMaterial: Material | null
@@ -31,8 +35,16 @@ export class InventoryComponent extends ErrorHandlingComponent {
units = UNIT_MILLILITER
lowQuantityThreshold = 100 // TEMPORARY will be in the application settings
- materialTypeFilter = 1
- materialNameFilter = ''
+
+ materialFilterPredicate = materialMatchesFilter
+
+ private materialTypeFilter = 1
+ private materialNameFilter = ''
+ private hideLowQuantity = false
+
+ materialTypeFilterControl = new FormControl(this.materialTypeFilter)
+ materialNameFilterControl = new FormControl(this.materialNameFilter)
+ hideLowQuantityControl = new FormControl(this.hideLowQuantity)
constructor(
private materialService: MaterialService,
@@ -53,38 +65,25 @@ export class InventoryComponent extends ErrorHandlingComponent {
this.subscribe(
this.materialService.allNotMixType,
- materials => {
- this.materials = materials
- this.dataSource = this.setupDataSource()
- },
+ materials => this.materials = materials,
true,
1
)
- }
- setupDataSource(): MatTableDataSource {
- this.dataSource = new MatTableDataSource(this.materials)
- this.dataSource.sortingDataAccessor = (material, header) => {
- switch (header) {
- case 'materialType':
- return material[header].name
- case 'lowQuantityIcon':
- return this.isLowQuantity(material)
- default:
- return material[header]
- }
- }
- this.dataSource.filterPredicate = (material, filter) => {
- return (!this.materialTypeFilter || this.materialTypeFilter === 1 || material.materialType.id === this.materialTypeFilter) &&
- (!this.materialNameFilter || material.name.toLowerCase().includes(this.materialNameFilter.toLowerCase()))
- }
+ this.subscribe(
+ this.materialTypeFilterControl.valueChanges,
+ filter => this.materialTypeFilter = filter
+ )
- this.dataSource.sort = this.sort
- return this.dataSource
- }
+ this.subscribe(
+ this.materialNameFilterControl.valueChanges,
+ filter => this.materialNameFilter = filter
+ )
- filterDataSource() {
- this.dataSource.filter = 'filter'
+ this.subscribe(
+ this.hideLowQuantityControl.valueChanges,
+ filter => this.hideLowQuantity = filter
+ )
}
isLowQuantity(material: Material) {
@@ -92,7 +91,7 @@ export class InventoryComponent extends ErrorHandlingComponent {
}
getQuantity(material: Material): number {
- return Math.round(convertQuantity(material.inventoryQuantity, UNIT_MILLILITER, this.units) * 100) / 100
+ return round(convertQuantity(material.inventoryQuantity, UNIT_MILLILITER, this.units), 2)
}
materialHasSimdut(material: Material): boolean {
@@ -118,6 +117,10 @@ export class InventoryComponent extends ErrorHandlingComponent {
)
}
+ get filter(): string {
+ return [this.materialTypeFilter, this.materialNameFilter, this.hideLowQuantity, this.lowQuantityThreshold].join(materialFilterFieldSeparator)
+ }
+
get canEditMaterial(): boolean {
return this.accountService.hasPermission(Permission.EDIT_MATERIALS)
}
diff --git a/src/app/modules/recipes/add.html b/src/app/modules/recipes/add.html
new file mode 100644
index 0000000..aa827e1
--- /dev/null
+++ b/src/app/modules/recipes/add.html
@@ -0,0 +1,10 @@
+
+
+ Retour
+
+
+
+
+
+
+
diff --git a/src/app/modules/colors/bpac.js b/src/app/modules/recipes/bpac.js
similarity index 100%
rename from src/app/modules/colors/bpac.js
rename to src/app/modules/recipes/bpac.js
diff --git a/src/app/modules/colors/components/images-editor/images-editor.component.html b/src/app/modules/recipes/components/images-editor/images-editor.component.html
similarity index 89%
rename from src/app/modules/colors/components/images-editor/images-editor.component.html
rename to src/app/modules/recipes/components/images-editor/images-editor.component.html
index e07f7b8..abd0574 100644
--- a/src/app/modules/colors/components/images-editor/images-editor.component.html
+++ b/src/app/modules/recipes/components/images-editor/images-editor.component.html
@@ -4,6 +4,8 @@
+
Aucune image n'est associée à cette couleur
+
diff --git a/src/app/modules/colors/components/images-editor/images-editor.component.sass b/src/app/modules/recipes/components/images-editor/images-editor.component.sass
similarity index 100%
rename from src/app/modules/colors/components/images-editor/images-editor.component.sass
rename to src/app/modules/recipes/components/images-editor/images-editor.component.sass
diff --git a/src/app/modules/colors/components/images-editor/images-editor.component.ts b/src/app/modules/recipes/components/images-editor/images-editor.component.ts
similarity index 100%
rename from src/app/modules/colors/components/images-editor/images-editor.component.ts
rename to src/app/modules/recipes/components/images-editor/images-editor.component.ts
diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.html b/src/app/modules/recipes/components/mix-table/mix-table.component.html
similarity index 100%
rename from src/app/modules/colors/components/mix-table/mix-table.component.html
rename to src/app/modules/recipes/components/mix-table/mix-table.component.html
diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.sass b/src/app/modules/recipes/components/mix-table/mix-table.component.sass
similarity index 100%
rename from src/app/modules/colors/components/mix-table/mix-table.component.sass
rename to src/app/modules/recipes/components/mix-table/mix-table.component.sass
diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.ts b/src/app/modules/recipes/components/mix-table/mix-table.component.ts
similarity index 97%
rename from src/app/modules/colors/components/mix-table/mix-table.component.ts
rename to src/app/modules/recipes/components/mix-table/mix-table.component.ts
index 2018c5a..bd430a6 100644
--- a/src/app/modules/colors/components/mix-table/mix-table.component.ts
+++ b/src/app/modules/recipes/components/mix-table/mix-table.component.ts
@@ -1,5 +1,5 @@
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
-import {Mix, MixMaterial, MixMaterialDto, mixMaterialsAsMixMaterialsDto, Recipe} from '../../../shared/model/recipe.model'
+import {Mix, MixMaterial, MixMaterialDto, mixMaterialsToMixMaterialsDto, Recipe} from '../../../shared/model/recipe.model'
import {Subject} from 'rxjs'
import {SubscribingComponent} from '../../../shared/components/subscribing.component'
import {convertMixMaterialQuantity, UNIT_MILLILITER} from '../../../shared/units'
@@ -60,7 +60,7 @@ export class MixTableComponent extends SubscribingComponent {
this.mixColumns = this.COLUMNS_EDIT
}
- this.mixMaterials = mixMaterialsAsMixMaterialsDto(this.mix)
+ this.mixMaterials = mixMaterialsToMixMaterialsDto(this.mix)
this.subscribe(
this.units$,
@@ -191,12 +191,13 @@ export class MixTableComponent extends SubscribingComponent {
materialId: quantity.materialId,
quantity: this.calculateQuantity(index),
isPercents: quantity.isPercents,
- position: quantity.position
+ position: quantity.position,
+ units: UNIT_MILLILITER
})
}
private convertQuantities(newUnit: string) {
- this.mixMaterials.forEach(q => q.quantity = convertMixMaterialQuantity(q, this.units, newUnit))
+ this.mixMaterials.forEach(q => q.quantity = convertMixMaterialQuantity(q, newUnit))
this.units = newUnit
}
diff --git a/src/app/modules/colors/components/mixes-card/mixes-card.component.html b/src/app/modules/recipes/components/mixes-card/mixes-card.component.html
similarity index 88%
rename from src/app/modules/colors/components/mixes-card/mixes-card.component.html
rename to src/app/modules/recipes/components/mixes-card/mixes-card.component.html
index b6766df..c8a093a 100644
--- a/src/app/modules/colors/components/mixes-card/mixes-card.component.html
+++ b/src/app/modules/recipes/components/mixes-card/mixes-card.component.html
@@ -3,6 +3,8 @@
Mélanges
+ Il n'y a aucun mélange dans cette couleur
+
Étapes
-
+ 0">
{{step.position}}.{{step.message}}
diff --git a/src/app/modules/colors/components/step-list/step-list.component.sass b/src/app/modules/recipes/components/step-list/step-list.component.sass
similarity index 100%
rename from src/app/modules/colors/components/step-list/step-list.component.sass
rename to src/app/modules/recipes/components/step-list/step-list.component.sass
diff --git a/src/app/modules/colors/components/step-list/step-list.component.ts b/src/app/modules/recipes/components/step-list/step-list.component.ts
similarity index 100%
rename from src/app/modules/colors/components/step-list/step-list.component.ts
rename to src/app/modules/recipes/components/step-list/step-list.component.ts
diff --git a/src/app/modules/colors/components/step-table/step-table.component.html b/src/app/modules/recipes/components/step-table/step-table.component.html
similarity index 100%
rename from src/app/modules/colors/components/step-table/step-table.component.html
rename to src/app/modules/recipes/components/step-table/step-table.component.html
diff --git a/src/app/modules/colors/components/step-table/step-table.component.sass b/src/app/modules/recipes/components/step-table/step-table.component.sass
similarity index 100%
rename from src/app/modules/colors/components/step-table/step-table.component.sass
rename to src/app/modules/recipes/components/step-table/step-table.component.sass
diff --git a/src/app/modules/colors/components/step-table/step-table.component.ts b/src/app/modules/recipes/components/step-table/step-table.component.ts
similarity index 100%
rename from src/app/modules/colors/components/step-table/step-table.component.ts
rename to src/app/modules/recipes/components/step-table/step-table.component.ts
diff --git a/src/app/modules/colors/components/unit-selector/unit-selector.component.html b/src/app/modules/recipes/components/unit-selector/unit-selector.component.html
similarity index 89%
rename from src/app/modules/colors/components/unit-selector/unit-selector.component.html
rename to src/app/modules/recipes/components/unit-selector/unit-selector.component.html
index 4b82721..b00e879 100644
--- a/src/app/modules/colors/components/unit-selector/unit-selector.component.html
+++ b/src/app/modules/recipes/components/unit-selector/unit-selector.component.html
@@ -1,6 +1,6 @@
Unités
-
+
Millilitres
Litres
diff --git a/src/app/modules/colors/components/unit-selector/unit-selector.component.sass b/src/app/modules/recipes/components/unit-selector/unit-selector.component.sass
similarity index 100%
rename from src/app/modules/colors/components/unit-selector/unit-selector.component.sass
rename to src/app/modules/recipes/components/unit-selector/unit-selector.component.sass
diff --git a/src/app/modules/colors/components/unit-selector/unit-selector.component.ts b/src/app/modules/recipes/components/unit-selector/unit-selector.component.ts
similarity index 53%
rename from src/app/modules/colors/components/unit-selector/unit-selector.component.ts
rename to src/app/modules/recipes/components/unit-selector/unit-selector.component.ts
index 418677f..9f8d192 100644
--- a/src/app/modules/colors/components/unit-selector/unit-selector.component.ts
+++ b/src/app/modules/recipes/components/unit-selector/unit-selector.component.ts
@@ -1,17 +1,28 @@
-import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
+import {FormControl} from '@angular/forms'
@Component({
selector: 'cre-unit-selector',
templateUrl: './unit-selector.component.html',
styleUrls: ['./unit-selector.component.sass']
})
-export class UnitSelectorComponent {
+export class UnitSelectorComponent implements OnInit {
readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
@Input() unit = UNIT_MILLILITER
@Input() showLabel = true
@Input() short = false
+ @Input() control: FormControl | null
@Output() unitChange = new EventEmitter()
+
+ ngOnInit() {
+ this.control?.setValue(this.unit)
+ }
+
+ onUnitChange(newUnit: string) {
+ this.control?.setValue(newUnit)
+ this.unitChange.emit(newUnit)
+ }
}
diff --git a/src/app/modules/recipes/edit.html b/src/app/modules/recipes/edit.html
new file mode 100644
index 0000000..690dbf6
--- /dev/null
+++ b/src/app/modules/recipes/edit.html
@@ -0,0 +1,36 @@
+
+
+
+ Retour
+
+
+
+ Supprimer
+ Enregistrer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/recipes/explore.html b/src/app/modules/recipes/explore.html
new file mode 100644
index 0000000..8d8f714
--- /dev/null
+++ b/src/app/modules/recipes/explore.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+ Retour
+
+
+
+
+
+
+
+
+ Version Excel
+
+ Enregistrer
+
+
+
+
+
+
+
0">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/colors/pages/explore/explore.component.ts b/src/app/modules/recipes/explore.ts
similarity index 60%
rename from src/app/modules/colors/pages/explore/explore.component.ts
rename to src/app/modules/recipes/explore.ts
index 4fef579..084693e 100644
--- a/src/app/modules/colors/pages/explore/explore.component.ts
+++ b/src/app/modules/recipes/explore.ts
@@ -1,27 +1,34 @@
import {Component} from '@angular/core'
-import {RecipeService} from '../../services/recipe.service'
+import {RecipeService} from './services/recipe.service'
import {ActivatedRoute, Router} from '@angular/router'
-import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
-import {MixMaterialDto, Recipe, recipeMixCount, recipeNoteForGroupId, recipeStepCount} from '../../../shared/model/recipe.model'
+import {ErrorHandlingComponent} from '../shared/components/subscribing.component'
+import {
+ MixMaterialDto,
+ Recipe,
+ recipeMixCount,
+ recipeNoteForGroupId,
+ recipeStepCount
+} from '../shared/model/recipe.model'
import {Observable, Subject} from 'rxjs'
-import {ErrorHandler, 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'
-import {AccountService} from '../../../accounts/services/account.service'
-import {Permission} from '../../../shared/model/user'
+import {ErrorHandler, 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'
+import {AccountService} from '../accounts/services/account.service'
+import {Permission} from '../shared/model/user'
+import {FormControl} from '@angular/forms';
+import {map} from 'rxjs/operators';
+import {CreInputEntry} from '../shared/components/inputs/inputs';
@Component({
- selector: 'cre-explore',
- templateUrl: './explore.component.html',
- styleUrls: ['./explore.component.sass']
+ selector: 'cre-recipe-explore',
+ templateUrl: './explore.html',
+ styleUrls: ['./recipes.sass']
})
-export class ExploreComponent extends ErrorHandlingComponent {
- recipe: Recipe | null
- groups$ = this.groupService.all
+export class CreRecipeExplore extends ErrorHandlingComponent {
deductErrorBody = {}
units$ = new Subject()
selectedGroupId: number | null
@@ -33,6 +40,12 @@ export class ExploreComponent extends ErrorHandlingComponent {
deductedMixId: number | null
+ groupControl: FormControl
+ noteControl: FormControl
+ groupEntries$ = this.groupService.all.pipe(map(groups => {
+ return groups.map(group => new CreInputEntry(group.id, group.name))
+ }))
+
errorHandlers: ErrorHandler[] = [{
filter: error => error.type === 'notfound-recipe-id',
consumer: error => this.urlUtils.navigateTo('/color/list')
@@ -42,6 +55,9 @@ export class ExploreComponent extends ErrorHandlingComponent {
messageProducer: () => 'Certains produit ne sont pas en quantité suffisante dans l\'inventaire'
}]
+ private _recipe: Recipe | null
+ private _notePlaceholder = !this.canEditRecipesPublicData ? 'N/A' : ''
+
constructor(
private recipeService: RecipeService,
private inventoryService: InventoryService,
@@ -62,18 +78,30 @@ export class ExploreComponent extends ErrorHandlingComponent {
this.selectedGroupId = this.loggedInUserGroupId
- const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
+ this.fetchRecipe()
+
+ this.groupControl = new FormControl(this.selectedGroupId)
+ this.subscribe(
+ this.groupControl.valueChanges,
+ groupId => {
+ this.selectedGroupId = groupId
+ this.noteControl.setValue(this.selectedGroupNote, {emitEvent: false})
+ }
+ )
+
+ this.noteControl = new FormControl({value: this._notePlaceholder, disabled: !this.canEditRecipesPublicData})
+ this.subscribe(
+ this.noteControl.valueChanges,
+ _ => this.hasModifications = true
+ )
+ }
+
+ fetchRecipe() {
+ const recipeId = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribeEntityById(
this.recipeService,
- id,
- r => {
- this.recipe = r
- this.appState.title = r.name
-
- if (recipeMixCount(this.recipe) <= 0 || recipeStepCount(this.recipe) <= 0) {
- this.alertService.pushWarning('Cette recette n\'est pas complète')
- }
- }
+ recipeId,
+ recipe => this.recipe = recipe
)
}
@@ -128,11 +156,24 @@ export class ExploreComponent extends ErrorHandlingComponent {
subscribeDeductMix(observable: Observable) {
this.subscribe(
observable,
- () => this.alertService.pushSuccess('Les quantités quantités ont été déduites de l\'inventaire'),
+ () => this.alertService.pushSuccess('Les quantités ont été déduites de l\'inventaire'),
true
)
}
+ get recipe(): Recipe {
+ return this._recipe
+ }
+
+ set recipe(recipe: Recipe) {
+ this._recipe = recipe
+ this.appState.title = recipe.name
+
+ if (recipeMixCount(recipe) <= 0 || recipeStepCount(recipe) <= 0) {
+ this.alertService.pushWarning('Cette recette n\'est pas complète')
+ }
+ }
+
get loggedInUserGroupId(): number {
return this.appState.authenticatedUser.group?.id
}
@@ -141,11 +182,7 @@ export class ExploreComponent extends ErrorHandlingComponent {
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)
+ return this.groupsNote.get(this.selectedGroupId) ?? this._notePlaceholder
}
get canEditRecipesPublicData(): boolean {
@@ -160,7 +197,9 @@ export class ExploreComponent extends ErrorHandlingComponent {
})
this.groupsNote.forEach((content, groupId) => {
- updatedNotes.set(groupId, content)
+ if (content) {
+ updatedNotes.set(groupId, content)
+ }
})
return updatedNotes
diff --git a/src/app/modules/recipes/form.html b/src/app/modules/recipes/form.html
new file mode 100644
index 0000000..3dc9805
--- /dev/null
+++ b/src/app/modules/recipes/form.html
@@ -0,0 +1,21 @@
+
+
+ Il n'y a actuellement aucune bannière enregistrée dans le système.
+ Vous pouvez en créer une ici.
+
+
+
+
+ Ajouter une couleur
+ Modifier la couleur {{recipe.name}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/colors/pages/list/list.component.html b/src/app/modules/recipes/list.html
similarity index 57%
rename from src/app/modules/colors/pages/list/list.component.html
rename to src/app/modules/recipes/list.html
index 84493bf..37ed007 100644
--- a/src/app/modules/colors/pages/list/list.component.html
+++ b/src/app/modules/recipes/list.html
@@ -1,43 +1,47 @@
-
-
- Recherche
-
-
-
-
-
-
+
+
+
+
+
+ Ajouter
+
+
+
+
+
+ Il n'y a actuellement aucune bannière enregistrée dans le système.
+ Vous pouvez en créer une ici.
+
+
+
0 && recipes.size === 0">
+ Il n'y a actuellement aucune recette enregistrée dans le système.
+ Vous pouvez en créer une ici.
+
- {{companyRecipes.company}}
+ {{company.name}}
-
+
-
+
Nom |
@@ -85,19 +89,16 @@
|
-
-
+ |
+ Voir
|
|
-
-
+ |
+ Modifier
|
-
-
-
-
+
diff --git a/src/app/modules/recipes/list.ts b/src/app/modules/recipes/list.ts
new file mode 100644
index 0000000..121f6e7
--- /dev/null
+++ b/src/app/modules/recipes/list.ts
@@ -0,0 +1,110 @@
+import {ChangeDetectorRef, Component} from '@angular/core'
+import {ErrorHandlingComponent} from '../shared/components/subscribing.component'
+import {Company} from '../shared/model/company.model'
+import {getRecipeLuma, Recipe, recipeMatchesFilter} from '../shared/model/recipe.model'
+import {CompanyService} from '../company/service/company.service'
+import {RecipeService} from './services/recipe.service'
+import {AccountService} from '../accounts/services/account.service'
+import {ConfigService} from '../shared/service/config.service'
+import {AppState} from '../shared/app-state'
+import {ErrorService} from '../shared/service/error.service'
+import {ActivatedRoute, Router} from '@angular/router'
+import {Config} from '../shared/model/config.model'
+import {Permission} from '../shared/model/user'
+import {FormControl} from '@angular/forms'
+
+@Component({
+ selector: 'cre-recipe-list',
+ templateUrl: 'list.html',
+ styleUrls: ['recipes.sass']
+})
+export class RecipeList extends ErrorHandlingComponent {
+ companies: Company[] = []
+ recipes: Map
= new Map()
+ columns = ['name', 'description', 'color', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit']
+ panelForcedExpanded = false
+
+ searchControl: FormControl
+ searchQuery = ''
+
+ recipeFilterPredicate = recipeMatchesFilter
+
+ constructor(
+ private companyService: CompanyService,
+ private recipeService: RecipeService,
+ private accountService: AccountService,
+ private configService: ConfigService,
+ private cdRef: ChangeDetectorRef,
+ private appState: AppState,
+ errorService: ErrorService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(errorService, activatedRoute, router)
+ }
+
+ ngOnInit() {
+ super.ngOnInit()
+ this.appState.title = 'Explorateur'
+
+ // Navigate to configs if server is in emergency mode
+ this.subscribe(
+ this.configService.get(Config.EMERGENCY_MODE),
+ config => {
+ if (config.content == 'true') {
+ this.urlUtils.navigateTo('/admin/config/')
+ }
+ }
+ )
+
+ this.fetchCompanies()
+ this.fetchRecipes()
+
+ this.searchControl = new FormControl('')
+ this.subscribe(
+ this.searchControl.valueChanges,
+ value => {
+ this.searchQuery = value
+ if (value.length > 0 && !this.panelForcedExpanded) {
+ this.panelForcedExpanded = true
+ this.cdRef.detectChanges()
+ }
+ }
+ )
+ }
+
+ private fetchCompanies() {
+ this.subscribe(
+ this.companyService.all,
+ companies => this.companies = companies
+ )
+ }
+
+ private fetchRecipes() {
+ this.subscribe(
+ this.recipeService.allByCompany,
+ recipes => this.recipes = recipes,
+ true
+ )
+ }
+
+ isCompanyHidden(company: Company): boolean {
+ const companyRecipes = this.recipes.get(company.id)
+ return !(companyRecipes && companyRecipes.length >= 0) ||
+ this.searchQuery && this.searchQuery.length > 0 &&
+ !companyRecipes.some(recipe => this.recipeFilterPredicate(recipe, this.searchQuery))
+ }
+
+
+ isLight(recipe: Recipe): boolean {
+ return getRecipeLuma(recipe) > 200
+ }
+
+ get hasEditPermission(): boolean {
+ return this.accountService.hasPermission(Permission.EDIT_RECIPES)
+ }
+
+ get hasCompanyEditPermission(): boolean {
+ return this.accountService.hasPermission(Permission.EDIT_COMPANIES)
+ }
+}
diff --git a/src/app/modules/recipes/mix/add.html b/src/app/modules/recipes/mix/add.html
new file mode 100644
index 0000000..d6a6301
--- /dev/null
+++ b/src/app/modules/recipes/mix/add.html
@@ -0,0 +1,20 @@
+
+
+
+ Retour
+
+
+ Enregistrer
+
+
+
+
+
+ Ajouter un mélange à la couleur {{recipe.company.name}} - {{recipe.name}}
+
+
+
diff --git a/src/app/modules/recipes/mix/edit.html b/src/app/modules/recipes/mix/edit.html
new file mode 100644
index 0000000..14267ca
--- /dev/null
+++ b/src/app/modules/recipes/mix/edit.html
@@ -0,0 +1,25 @@
+
+
+
+ Retour
+
+
+
+ Enregistrer
+
+
+
+
+
+
+ Modification du mélange {{mix.mixType.name}} de la recette {{recipe.company.name}} - {{recipe.name}}
+
+
+
diff --git a/src/app/modules/recipes/mix/form.html b/src/app/modules/recipes/mix/form.html
new file mode 100644
index 0000000..8deb6d4
--- /dev/null
+++ b/src/app/modules/recipes/mix/form.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/recipes/mix/info-form.html b/src/app/modules/recipes/mix/info-form.html
new file mode 100644
index 0000000..adb993d
--- /dev/null
+++ b/src/app/modules/recipes/mix/info-form.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/recipes/mix/materials-form-combo-box.html b/src/app/modules/recipes/mix/materials-form-combo-box.html
new file mode 100644
index 0000000..fc440c9
--- /dev/null
+++ b/src/app/modules/recipes/mix/materials-form-combo-box.html
@@ -0,0 +1,5 @@
+
+
diff --git a/src/app/modules/recipes/mix/materials-form.html b/src/app/modules/recipes/mix/materials-form.html
new file mode 100644
index 0000000..5c7742c
--- /dev/null
+++ b/src/app/modules/recipes/mix/materials-form.html
@@ -0,0 +1,76 @@
+
+
+ Il n'y a actuellement aucun produit enregistré dans le système.
+ Vous pouvez en créer un ici.
+
+
+
+
+
+ Position |
+ {{mixMaterial.position}} |
+
+
+
+ |
+
+
+
+ |
+
+
+
+ Produit |
+
+
+
+ |
+
+
+
+ Quantité |
+
+
+ |
+
+
+
+ Unités |
+
+
+
+
+
+ %
+
+ |
+
+
+
+
+ Ajouter
+
+ |
+
+ Retirer
+
+ |
+
+
+
diff --git a/src/app/modules/recipes/mix/materials-form.ts b/src/app/modules/recipes/mix/materials-form.ts
new file mode 100644
index 0000000..06b22d7
--- /dev/null
+++ b/src/app/modules/recipes/mix/materials-form.ts
@@ -0,0 +1,255 @@
+import {AfterViewInit, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild, ViewChildren} from '@angular/core'
+import {CreTable} from '../../shared/components/tables/tables'
+import {Mix, MixMaterialDto, mixMaterialsToMixMaterialsDto, sortMixMaterialsDto} from '../../shared/model/recipe.model'
+import {Observable, Subject} from 'rxjs'
+import {Material, materialComparator} from '../../shared/model/material.model'
+import {FormControl, Validators} from '@angular/forms'
+import {takeUntil} from 'rxjs/operators'
+import {CreComboBoxComponent, CreInputEntry} from '../../shared/components/inputs/inputs'
+import {AccountService} from '../../accounts/services/account.service'
+import {Permission} from '../../shared/model/user'
+import {UNIT_MILLILITER} from '../../shared/units'
+
+@Component({
+ selector: 'cre-mix-materials-form-combo-box',
+ templateUrl: 'materials-form-combo-box.html'
+})
+export class MixMaterialsFormComboBox implements OnInit {
+ @ViewChild(CreComboBoxComponent) comboBox: CreComboBoxComponent
+
+ @Input() mixMaterial: MixMaterialDto
+ @Input() mix: Mix | null
+ @Input() mixMaterials: MixMaterialDto[]
+ @Input() control: FormControl
+ @Input() materials: Material[]
+ @Input() position: number
+
+ entries: CreInputEntry[]
+
+ ngOnInit() {
+ this.entries = this.filterMaterials()
+ }
+
+ updateEntries() {
+ this.entries = this.filterMaterials()
+ this.comboBox.reloadEntries()
+ }
+
+ private filterMaterials(): CreInputEntry[] {
+ return this.materials
+ .filter(material => {
+ if (this.mix && this.mix.mixType.material.id === material.id) {
+ return false
+ }
+
+ // Prevent use of percents in first position
+ if (material.materialType.usePercentages && this.mixMaterial.position <= 1) {
+ return false
+ }
+
+ if (this.mixMaterial.materialId === material.id) {
+ return true
+ }
+
+ return this.mixMaterials.filter(x => x.materialId === material.id).length <= 0
+ })
+ .sort(materialComparator)
+ .map(material => new CreInputEntry(material.id, material.name, material.materialType.prefix ? `[${material.materialType.prefix}] ${material.name}` : material.name))
+ }
+}
+
+@Component({
+ selector: 'cre-mix-materials-form',
+ templateUrl: 'materials-form.html'
+})
+export class MixMaterialsForm implements AfterViewInit, OnDestroy {
+ @ViewChild(CreTable) table: CreTable
+ @ViewChildren(MixMaterialsFormComboBox) comboBoxes: MixMaterialsFormComboBox[]
+
+ @Input() materials: Observable
+ @Input() mix: Mix | null
+
+ mixMaterials: MixMaterialDto[] = []
+ columns = ['position', 'positionButtons', 'material', 'quantity', 'units', 'endButton']
+ allMaterials: Material[]
+
+ private _controls: ControlsByPosition[] = []
+ private _destroy$ = new Subject()
+
+ constructor(
+ private accountService: AccountService,
+ private cdRef: ChangeDetectorRef
+ ) {
+ }
+
+ ngAfterViewInit() {
+ this.materials.subscribe({
+ next: materials => {
+ this.allMaterials = materials
+
+ if (!this.mix) {
+ this.addRow()
+ } else {
+ mixMaterialsToMixMaterialsDto(this.mix).forEach(x => this.insertRow(x))
+ }
+
+ this.table.renderRows()
+ this.cdRef.detectChanges()
+ }
+ })
+ }
+
+ ngOnDestroy() {
+ this._destroy$.next(true)
+ this._destroy$.complete()
+ }
+
+ addRow() {
+ const mixMaterial = new MixMaterialDto(null, 0, false, this.nextPosition, UNIT_MILLILITER)
+ this.insertRow(mixMaterial)
+ this.table.renderRows()
+ }
+
+ insertRow(mixMaterial: MixMaterialDto) {
+ const materialIdControl = new FormControl(mixMaterial.materialId, Validators.required)
+ const quantityControl = new FormControl(mixMaterial.quantity, Validators.required)
+ const unitsControl = new FormControl(mixMaterial.units, Validators.required)
+
+ materialIdControl.valueChanges
+ .pipe(takeUntil(this._destroy$))
+ .subscribe({
+ next: materialId => {
+ mixMaterial.materialId = materialId
+ this.refreshAvailableMaterials()
+ this.cdRef.detectChanges()
+ }
+ })
+
+ this.mixMaterials.push(mixMaterial)
+ this._controls.push({
+ position: mixMaterial.position,
+ controls: {
+ materialId: materialIdControl,
+ quantity: quantityControl,
+ units: unitsControl
+ }
+ })
+ }
+
+ removeRow(mixMaterial: MixMaterialDto) {
+ this.mixMaterials = this.mixMaterials.filter(x => x.position !== mixMaterial.position)
+ this._controls = this._controls.filter(x => x.position !== mixMaterial.position)
+
+ for (let position = mixMaterial.position + 1; position < this.mixMaterials.length; position++) {
+ this.updatePosition(this.getMixMaterialByPosition(position), position - 1, false)
+ }
+ }
+
+ updatePosition(mixMaterial: MixMaterialDto, newPosition: number, switchPositions = true) {
+ const currentPosition = mixMaterial.position
+ const currentControls = this.getControlsByPosition(currentPosition)
+
+ // Update before current to prevent position conflicts
+ if (switchPositions) {
+ this.updatePosition(this.getMixMaterialByPosition(newPosition), currentPosition, false)
+ }
+
+ mixMaterial.position = newPosition
+ currentControls.position = newPosition
+
+ this.sortTable()
+ this.refreshAvailableMaterials()
+ }
+
+ getControls(position: number): MixMaterialControls {
+ return this.getControlsByPosition(position).controls
+ }
+
+ areUnitsPercents(mixMaterial: MixMaterialDto): boolean {
+ if (!mixMaterial) {
+ return false
+ }
+
+ return mixMaterial.materialId ? this.allMaterials?.filter(x => x.id === mixMaterial.materialId)[0].materialType.usePercentages : false
+ }
+
+ isDecreasePositionButtonDisabled(mixMaterial: MixMaterialDto): boolean {
+ return mixMaterial.position <= 2 && this.areUnitsPercents(mixMaterial)
+ }
+
+ isIncreasePositionButtonDisabled(mixMaterial: MixMaterialDto): boolean {
+ if (mixMaterial.position === this.mixMaterials.length) {
+ return true
+ }
+
+ if (mixMaterial.position > 1) {
+ return false
+ }
+
+ const nextMixMaterial = this.getMixMaterialByPosition(mixMaterial.position + 1)
+ return this.areUnitsPercents(nextMixMaterial)
+ }
+
+ get hasMaterialEditPermission(): boolean {
+ return this.accountService.hasPermission(Permission.EDIT_MATERIALS)
+ }
+
+ get materialCount(): number {
+ return this.allMaterials ? this.allMaterials.length : 0
+ }
+
+ get updatedMixMaterials(): MixMaterialDto[] {
+ const updatedMixMaterials: MixMaterialDto[] = []
+ this.mixMaterials.forEach(mixMaterial => {
+ const controls = this.getControlsByPosition(mixMaterial.position).controls
+ updatedMixMaterials.push({
+ materialId: controls.materialId.value,
+ quantity: controls.quantity.value,
+ position: mixMaterial.position,
+ units: controls.units.value,
+ isPercents: this.areUnitsPercents(mixMaterial)
+ })
+ })
+ return updatedMixMaterials
+ }
+
+ get valid(): boolean {
+ return this._controls
+ .map(controls => controls.controls)
+ .map(controls => [controls.materialId, controls.quantity])
+ .flatMap(controls => controls)
+ .every(control => control.valid)
+ }
+
+ private get nextPosition(): number {
+ return this.mixMaterials.length + 1
+ }
+
+ private getMixMaterialByPosition(position: number): MixMaterialDto {
+ return this.mixMaterials.filter(x => x.position === position)[0]
+ }
+
+ private getControlsByPosition(position: number): ControlsByPosition {
+ return this._controls.filter(control => control.position === position)[0]
+ }
+
+ private refreshAvailableMaterials() {
+ this.comboBoxes.forEach(x => x.updateEntries())
+ }
+
+ private sortTable() {
+ this.mixMaterials = sortMixMaterialsDto(this.mixMaterials)
+ this.table.renderRows()
+ }
+}
+
+interface MixMaterialControls {
+ materialId: FormControl
+ quantity: FormControl
+ units: FormControl
+}
+
+interface ControlsByPosition {
+ position: number
+ controls: MixMaterialControls
+}
diff --git a/src/app/modules/recipes/mix/mix.ts b/src/app/modules/recipes/mix/mix.ts
new file mode 100644
index 0000000..a91833f
--- /dev/null
+++ b/src/app/modules/recipes/mix/mix.ts
@@ -0,0 +1,171 @@
+import {Component, Directive, Input, OnInit, ViewChild} from '@angular/core'
+import {SubscribingComponent} from '../../shared/components/subscribing.component'
+import {Mix, Recipe} from '../../shared/model/recipe.model'
+import {ErrorService} from '../../shared/service/error.service'
+import {ActivatedRoute, Router} from '@angular/router'
+import {RecipeService} from '../services/recipe.service'
+import {FormControl, Validators} from '@angular/forms'
+import {Observable} from 'rxjs'
+import {MaterialType} from '../../shared/model/materialtype.model'
+import {MaterialTypeService} from '../../material-type/service/material-type.service'
+import {CreInputEntry} from '../../shared/components/inputs/inputs'
+import {map} from 'rxjs/operators'
+import {Material} from '../../shared/model/material.model'
+import {MaterialService} from '../../material/service/material.service'
+import {CreForm} from '../../shared/components/forms/forms'
+import {MixMaterialsForm} from './materials-form'
+import {MixSaveDto, MixService, MixUpdateDto} from '../services/mix.service'
+
+@Directive()
+abstract class _BaseMixPage extends SubscribingComponent {
+ materialTypes$ = this.materialTypeService.all
+ materials$: Observable
+
+ private _recipe: Recipe | null
+
+ constructor(
+ protected mixService: MixService,
+ private recipeService: RecipeService,
+ private materialTypeService: MaterialTypeService,
+ private materialService: MaterialService,
+ errorService: ErrorService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(errorService, activatedRoute, router)
+ }
+
+ ngOnInit() {
+ this.fetchRecipe()
+ }
+
+ private fetchRecipe() {
+ const recipeId = this.urlUtils.parseIntUrlParam('recipeId')
+
+ this.subscribe(
+ this.recipeService.getById(recipeId),
+ recipe => this.recipe = recipe
+ )
+ }
+
+ set recipe(recipe: Recipe) {
+ this._recipe = recipe
+ this.materials$ = this.materialService.getAllForMixCreation(recipe.id)
+ }
+
+ get recipe(): Recipe {
+ return this._recipe
+ }
+
+ abstract submit(dto: MixSaveDto)
+}
+
+@Component({
+ selector: 'cre-mix-add',
+ templateUrl: 'add.html'
+})
+export class MixAdd extends _BaseMixPage {
+ submit(dto: MixSaveDto) {
+ this.subscribeAndNavigate(
+ this.mixService.saveDto(dto),
+ `/color/edit/${this.recipe.id}`
+ )
+ }
+}
+
+@Component({
+ selector: 'cre-mix-edit',
+ templateUrl: 'edit.html'
+})
+export class MixEdit extends _BaseMixPage {
+ mix: Mix
+
+ ngOnInit() {
+ super.ngOnInit()
+
+ this.fetchMix()
+ }
+
+ private fetchMix() {
+ const mixId = this.urlUtils.parseIntUrlParam('id')
+
+ this.subscribe(
+ this.mixService.getById(mixId),
+ mix => this.mix = mix
+ )
+ }
+
+ submit(dto: MixSaveDto) {
+ this.subscribeAndNavigate(
+ this.mixService.updateDto({...dto, id: this.mix.id}),
+ `/color/edit/${this.recipe.id}`
+ )
+ }
+}
+
+@Component({
+ selector: 'cre-mix-info-form',
+ templateUrl: 'info-form.html'
+})
+export class MixInfoForm implements OnInit {
+ @ViewChild(CreForm) form: CreForm
+
+ @Input() recipe: Recipe
+ @Input() mix: Mix | null
+ @Input() materialTypes: Observable
+
+ materialTypeEntries: Observable
+ controls: any
+
+ ngOnInit() {
+ this.materialTypeEntries = this.materialTypes.pipe(
+ map(materialTypes => {
+ return materialTypes.map(materialType => new CreInputEntry(materialType.id, materialType.name))
+ })
+ )
+
+ this.controls = {
+ name: new FormControl(this.mix?.mixType.name, Validators.required),
+ materialType: new FormControl(this.mix?.mixType.material.materialType.id, Validators.required)
+ }
+ }
+
+ get mixName(): string {
+ return this.controls.name.value
+ }
+
+ get mixMaterialTypeId(): number {
+ return this.controls.materialType.value
+ }
+
+ get valid(): boolean {
+ return this.form.valid
+ }
+}
+
+@Component({
+ selector: 'cre-mix-form',
+ templateUrl: 'form.html'
+})
+export class MixForm {
+ @ViewChild(MixInfoForm) infoForm: MixInfoForm
+ @ViewChild(MixMaterialsForm) mixMaterialsForm: MixMaterialsForm
+
+ @Input() recipe: Recipe
+ @Input() mix: Mix | null
+ @Input() materialTypes: Observable
+ @Input() materials: Observable
+
+ get formValues(): MixSaveDto {
+ return {
+ name: this.infoForm.mixName,
+ recipeId: this.recipe.id,
+ materialTypeId: this.infoForm.mixMaterialTypeId,
+ mixMaterials: this.mixMaterialsForm.updatedMixMaterials
+ }
+ }
+
+ get valid(): boolean {
+ return this.infoForm?.valid && this.mixMaterialsForm?.valid
+ }
+}
diff --git a/src/app/modules/colors/ptouchPrint.js b/src/app/modules/recipes/ptouchPrint.js
similarity index 100%
rename from src/app/modules/colors/ptouchPrint.js
rename to src/app/modules/recipes/ptouchPrint.js
diff --git a/src/app/modules/recipes/recipes-routing.module.ts b/src/app/modules/recipes/recipes-routing.module.ts
new file mode 100644
index 0000000..dca257e
--- /dev/null
+++ b/src/app/modules/recipes/recipes-routing.module.ts
@@ -0,0 +1,37 @@
+import {NgModule} from '@angular/core'
+import {RouterModule, Routes} from '@angular/router'
+import {CreRecipeExplore} from './explore'
+import {RecipeAdd, RecipeEdit} from './recipes'
+import {RecipeList} from './list'
+import {MixAdd, MixEdit} from './mix/mix'
+
+const routes: Routes = [{
+ path: 'list',
+ component: RecipeList
+}, {
+ path: 'add',
+ component: RecipeAdd
+}, {
+ path: 'edit/:id',
+ component: RecipeEdit
+}, {
+ path: 'add/mix/:recipeId',
+ component: MixAdd
+}, {
+ path: 'edit/mix/:recipeId/:id',
+ component: MixEdit
+}, {
+ path: 'explore/:id',
+ component: CreRecipeExplore
+}, {
+ path: '',
+ pathMatch: 'full',
+ redirectTo: 'list'
+}]
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class RecipesRoutingModule {
+}
diff --git a/src/app/modules/recipes/recipes.module.ts b/src/app/modules/recipes/recipes.module.ts
new file mode 100644
index 0000000..d1787b2
--- /dev/null
+++ b/src/app/modules/recipes/recipes.module.ts
@@ -0,0 +1,62 @@
+import {NgModule} from '@angular/core'
+
+import {RecipesRoutingModule} from './recipes-routing.module'
+import {SharedModule} from '../shared/shared.module'
+import {MatExpansionModule} from '@angular/material/expansion'
+import {FormsModule} from '@angular/forms'
+import {CreRecipeExplore} from './explore'
+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 {UnitSelectorComponent} from './components/unit-selector/unit-selector.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'
+import {CreInputsModule} from '../shared/components/inputs/inputs.module'
+import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
+import {RecipeAdd, RecipeEdit, RecipeForm} from './recipes'
+import {CreActionBarModule} from '../shared/components/action-bar/action-bar.module'
+import {RecipeList} from './list'
+import {MixAdd, MixEdit, MixForm, MixInfoForm} from './mix/mix'
+import {CreTablesModule} from '../shared/components/tables/tables.module'
+import {MixMaterialsForm, MixMaterialsFormComboBox} from './mix/materials-form'
+
+@NgModule({
+ declarations: [
+ CreRecipeExplore,
+ RecipeInfoComponent,
+ MixTableComponent,
+ StepListComponent,
+ StepTableComponent,
+ UnitSelectorComponent,
+ ImagesEditorComponent,
+ MixesCardComponent,
+ RecipeForm,
+ RecipeAdd,
+ RecipeEdit,
+ RecipeList,
+ MixAdd,
+ MixEdit,
+ MixForm,
+ MixInfoForm,
+ MixMaterialsForm,
+ MixMaterialsFormComboBox
+ ],
+ exports: [
+ UnitSelectorComponent
+ ],
+ imports: [
+ RecipesRoutingModule,
+ SharedModule,
+ MatExpansionModule,
+ FormsModule,
+ MatSortModule,
+ CreInputsModule,
+ CreButtonsModule,
+ CreActionBarModule,
+ CreTablesModule
+ ]
+})
+export class RecipesModule {
+}
diff --git a/src/app/modules/recipes/recipes.sass b/src/app/modules/recipes/recipes.sass
new file mode 100644
index 0000000..44c8af6
--- /dev/null
+++ b/src/app/modules/recipes/recipes.sass
@@ -0,0 +1,30 @@
+.recipe-wrapper > section
+ margin: 0 3rem 3rem
+
+cre-form
+ margin-top: 0 !important
+
+mat-expansion-panel
+ width: 60rem
+ margin: 20px auto
+
+.button-add
+ margin-top: .8rem
+
+.recipe-color-circle
+ 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)
+
+.recipe-content > div
+ margin: 0 3rem 3rem
+
+cre-table
+ .mat-column-name,
+ .mat-column-color,
+ .mat-column-iconNotApproved
+ width: 5em
+
+ .mat-column-description
+ width: 50em
+
+ .mat-column-sample
+ width: 10em
diff --git a/src/app/modules/recipes/recipes.ts b/src/app/modules/recipes/recipes.ts
new file mode 100644
index 0000000..bdc619e
--- /dev/null
+++ b/src/app/modules/recipes/recipes.ts
@@ -0,0 +1,211 @@
+import {ErrorHandlingComponent, SubscribingComponent} from '../shared/components/subscribing.component';
+import {Observable, Subject} from 'rxjs';
+import {CreInputEntry} from '../shared/components/inputs/inputs';
+import {map, tap} from 'rxjs/operators';
+import {RecipeService} from './services/recipe.service';
+import {CompanyService} from '../company/service/company.service';
+import {AppState} from '../shared/app-state';
+import {ErrorHandler, ErrorService} from '../shared/service/error.service';
+import {ActivatedRoute, Router} from '@angular/router';
+import {FormControl, Validators} from '@angular/forms';
+import {Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation} from '@angular/core';
+import {Recipe, recipeMixCount, RecipeStep, recipeStepCount} from '../shared/model/recipe.model';
+import {AccountService} from '../accounts/services/account.service';
+import {Permission} from '../shared/model/user';
+import {AlertService} from '../shared/service/alert.service';
+import {GroupService} from '../groups/services/group.service';
+import {StepTableComponent} from './components/step-table/step-table.component';
+import {anyMap} from '../shared/utils/map.utils';
+import {CreForm, ICreForm} from '../shared/components/forms/forms';
+
+@Component({
+ selector: 'recipe-form',
+ templateUrl: 'form.html',
+ styleUrls: ['recipes.sass'],
+ encapsulation: ViewEncapsulation.None
+})
+export class RecipeForm extends SubscribingComponent {
+ @ViewChild(CreForm) creForm: ICreForm
+
+ @Input() recipe: Recipe | null
+
+ @Output() submitForm = new EventEmitter();
+
+ controls: any
+ companyEntries$: Observable
+ hasCompanies = true
+
+ constructor(
+ private companyService: CompanyService,
+ private accountService: AccountService,
+ errorService: ErrorService,
+ activatedRoute: ActivatedRoute,
+ router: Router,
+ ) {
+ super(errorService, activatedRoute, router)
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+
+ this.fetchCompanies()
+
+ this.controls = {
+ name: new FormControl(this.recipe?.name, Validators.required),
+ description: new FormControl(this.recipe?.description, Validators.required),
+ color: new FormControl(this.recipe?.color ?? '#ffffff', Validators.required),
+ gloss: new FormControl(this.recipe?.gloss ?? 0, Validators.compose([Validators.required, Validators.min(0), Validators.max(100)])),
+ sample: new FormControl(this.recipe?.sample, Validators.compose([Validators.required, Validators.min(0)])),
+ approbationDate: new FormControl(this.recipe?.approbationDate),
+ remark: new FormControl(this.recipe?.remark),
+ company: new FormControl({value: this.recipe?.company.id, disabled: !!this.recipe}, Validators.required)
+ }
+ }
+
+ private fetchCompanies() {
+ this.companyEntries$ = this.companyService.all.pipe(
+ tap(companies => this.hasCompanies = companies.length > 0),
+ map(companies => companies.map(c => new CreInputEntry(c.id, c.name))),
+ )
+ }
+
+ submit() {
+ this.submitForm.emit(this.updatedRecipe)
+ }
+
+ get updatedRecipe(): Recipe {
+ return {
+ ...this.recipe,
+ name: this.controls.name.value,
+ description: this.controls.description.value,
+ color: this.controls.color.value,
+ gloss: this.controls.gloss.value,
+ sample: this.controls.sample.value,
+ approbationDate: this.controls.approbationDate.value,
+ remark: this.controls.remark.value,
+ company: this.controls.company.value,
+ }
+ }
+
+ get hasCompanyEditPermission(): boolean {
+ return this.accountService.hasPermission(Permission.EDIT_COMPANIES)
+ }
+}
+
+@Component({
+ selector: 'cre-recipe-add',
+ templateUrl: 'add.html'
+})
+export class RecipeAdd extends ErrorHandlingComponent {
+ errorHandlers = [{
+ filter: error => error.type === `exists-recipe-company-name`,
+ messageProducer: error => `Une couleur avec le nom ${error.name} existe déjà pour la bannière ${error.company}`
+ }]
+
+ constructor(
+ private recipeService: RecipeService,
+ private companyService: CompanyService,
+ private appState: AppState,
+ errorService: ErrorService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(errorService, activatedRoute, router)
+ this.appState.title = 'Nouvelle couleur'
+ }
+
+ submit(recipe: Recipe) {
+ this.subscribe(
+ this.recipeService.save(recipe),
+ recipe => this.urlUtils.navigateTo(`/color/edit/${recipe.id}`)
+ )
+ }
+}
+
+@Component({
+ selector: 'cre-recipe-edit',
+ templateUrl: 'edit.html',
+ styleUrls: ['recipes.sass']
+})
+export class RecipeEdit extends ErrorHandlingComponent {
+ @ViewChild(StepTableComponent) stepTable: StepTableComponent
+ @ViewChild(RecipeForm) form: RecipeForm
+
+ recipe: Recipe
+ groups$ = this.groupService.all
+ units$ = new Subject()
+
+ errorHandlers: ErrorHandler[] = [{
+ filter: error => error.type === 'notfound-recipe-id',
+ consumer: _ => this.urlUtils.navigateTo('/color/list')
+ }]
+
+ constructor(
+ private recipeService: RecipeService,
+ private companyService: CompanyService,
+ private groupService: GroupService,
+ private appState: AppState,
+ private alertService: AlertService,
+ errorService: ErrorService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(errorService, activatedRoute, router)
+
+ this.fetchRecipe()
+ }
+
+ private fetchRecipe() {
+ const recipeId = this.urlUtils.parseIntUrlParam('id')
+ this.subscribe(
+ this.recipeService.getById(recipeId),
+ recipe => {
+ this.recipe = recipe
+ this.appState.title = `${recipe.name} (Modifications)`
+
+ if (recipeMixCount(this.recipe) == 0) {
+ this.alertService.pushWarning('Il n\'y a aucun mélange dans cette recette')
+ }
+ if (recipeStepCount(this.recipe) == 0) {
+ this.alertService.pushWarning('Il n\'y a aucune étape dans cette recette')
+ }
+ },
+ true,
+ 1
+ )
+ }
+
+ changeUnits(unit: string) {
+ this.units$.next(unit)
+ }
+
+ submit() {
+ const recipe = this.form.updatedRecipe
+ const steps = this.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(recipe, steps),
+ '/color/list'
+ )
+ }
+
+ delete() {
+ this.subscribeAndNavigate(
+ this.recipeService.delete(this.recipe.id),
+ '/color/list'
+ )
+ }
+
+ get loggedInUserGroupId(): number {
+ return this.appState.authenticatedUser.group?.id
+ }
+
+ private stepsPositionsAreValid(steps: Map): boolean {
+ return !anyMap(steps, (groupId, steps) => !!steps.find(s => s.position === 0))
+ }
+}
diff --git a/src/app/modules/colors/services/mix.service.ts b/src/app/modules/recipes/services/mix.service.ts
similarity index 66%
rename from src/app/modules/colors/services/mix.service.ts
rename to src/app/modules/recipes/services/mix.service.ts
index cfd57bf..6a523e1 100644
--- a/src/app/modules/colors/services/mix.service.ts
+++ b/src/app/modules/recipes/services/mix.service.ts
@@ -21,8 +21,17 @@ export class MixService {
return this.api.get(`/recipe/mix/${id}`)
}
- saveWithUnits(name: string, recipeId: number, materialTypeId: number, mixMaterials: MixMaterialDto[], units: string): Observable {
- return this.save(name, recipeId, materialTypeId, this.convertMixMaterialsToMl(mixMaterials, units))
+ saveDto(dto: MixSaveDto): Observable {
+ return this.saveWithUnits(
+ dto.name,
+ dto.recipeId,
+ dto.materialTypeId,
+ dto.mixMaterials,
+ )
+ }
+
+ saveWithUnits(name: string, recipeId: number, materialTypeId: number, mixMaterials: MixMaterialDto[]): Observable {
+ return this.save(name, recipeId, materialTypeId, this.convertMixMaterialsToMl(mixMaterials))
}
save(name: string, recipeId: number, materialTypeId: number, mixMaterials: MixMaterialDto[]): Observable {
@@ -36,8 +45,17 @@ export class MixService {
return this.api.post('/recipe/mix', body)
}
- updateWithUnits(id: number, name: string, materialTypeId: number, mixMaterials: MixMaterialDto[], units: string): Observable {
- return this.update(id, name, materialTypeId, this.convertMixMaterialsToMl(mixMaterials, units))
+ updateDto(dto: MixUpdateDto): Observable {
+ return this.updateWithUnits(
+ dto.id,
+ dto.name,
+ dto.materialTypeId,
+ dto.mixMaterials
+ )
+ }
+
+ updateWithUnits(id: number, name: string, materialTypeId: number, mixMaterials: MixMaterialDto[]): Observable {
+ return this.update(id, name, materialTypeId, this.convertMixMaterialsToMl(mixMaterials))
}
update(id: number, name: string, materialTypeId: number, mixMaterials: MixMaterialDto[]): Observable {
@@ -56,11 +74,12 @@ export class MixService {
return this.api.delete(`/recipe/mix/${id}`)
}
- private convertMixMaterialsToMl(mixMaterials: MixMaterialDto[], units: string): MixMaterialDto[] {
- return mixMaterials.map(m => {
- m.quantity = convertMixMaterialQuantity(m, units, UNIT_MILLILITER)
- return m
- })
+ private convertMixMaterialsToMl(mixMaterials: MixMaterialDto[]): MixMaterialDto[] {
+ return mixMaterials.map(mixMaterial => ({
+ ...mixMaterial,
+ quantity: convertMixMaterialQuantity(mixMaterial, UNIT_MILLILITER),
+ units: UNIT_MILLILITER
+ }))
}
private appendMixMaterialsToBody(mixMaterials: MixMaterialDto[], body: any) {
@@ -74,3 +93,17 @@ export class MixService {
}
}
+export interface MixSaveDto {
+ name: string
+ recipeId: number
+ materialTypeId: number
+ mixMaterials: MixMaterialDto[]
+}
+
+export interface MixUpdateDto {
+ id: number
+ name: string
+ materialTypeId: number
+ mixMaterials: MixMaterialDto[]
+}
+
diff --git a/src/app/modules/colors/services/recipe-image.service.ts b/src/app/modules/recipes/services/recipe-image.service.ts
similarity index 100%
rename from src/app/modules/colors/services/recipe-image.service.ts
rename to src/app/modules/recipes/services/recipe-image.service.ts
diff --git a/src/app/modules/colors/services/recipe.service.ts b/src/app/modules/recipes/services/recipe.service.ts
similarity index 61%
rename from src/app/modules/colors/services/recipe.service.ts
rename to src/app/modules/recipes/services/recipe.service.ts
index 1cda0ce..4554569 100644
--- a/src/app/modules/colors/services/recipe.service.ts
+++ b/src/app/modules/recipes/services/recipe.service.ts
@@ -21,16 +21,16 @@ export class RecipeService {
return this.api.get(`/recipe?name=${name}`)
}
- get allSortedByCompany(): Observable<{ company: string, recipes: Recipe[] }[]> {
+ get allByCompany(): Observable