diff --git a/src/main/frontend/package-lock.json b/src/main/frontend/package-lock.json
index 1afe657..6c9c4f8 100644
--- a/src/main/frontend/package-lock.json
+++ b/src/main/frontend/package-lock.json
@@ -6596,6 +6596,11 @@
}
}
},
+ "js-joda": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/js-joda/-/js-joda-1.11.0.tgz",
+ "integrity": "sha512-/HJpRhwP2fPyuSsCaZuoVJuaSIt8tUXykV4wOMRXrFk7RP9h9VWaFdS9YHKdMepxb/3TdXpL6IhfC9L0sqYVBw=="
+ },
"js-levenshtein": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
@@ -7121,6 +7126,11 @@
"object-visit": "^1.0.0"
}
},
+ "material-design-icons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz",
+ "integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78="
+ },
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
diff --git a/src/main/frontend/package.json b/src/main/frontend/package.json
index fa044c2..b65b272 100644
--- a/src/main/frontend/package.json
+++ b/src/main/frontend/package.json
@@ -24,6 +24,8 @@
"@mdi/angular-material": "^5.7.55",
"bootstrap": "^4.5.2",
"copy-webpack-plugin": "^6.2.1",
+ "js-joda": "^1.11.0",
+ "material-design-icons": "^3.0.1",
"ngx-material-file-input": "^2.1.1",
"rxjs": "~6.5.4",
"tslib": "^1.10.0",
diff --git a/src/main/frontend/src/app/app.component.ts b/src/main/frontend/src/app/app.component.ts
index a3a66dc..69b3e57 100644
--- a/src/main/frontend/src/app/app.component.ts
+++ b/src/main/frontend/src/app/app.component.ts
@@ -3,6 +3,7 @@ import {isPlatformBrowser} from "@angular/common";
import {AppState} from "./modules/shared/app-state";
import {Observable} from "rxjs";
import {SubscribingComponent} from "./modules/shared/components/subscribing.component";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-root',
@@ -15,9 +16,11 @@ export class AppComponent extends SubscribingComponent {
constructor(
@Inject(PLATFORM_ID) private platformId: object,
- private appState: AppState
+ private appState: AppState,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit() {
diff --git a/src/main/frontend/src/app/app.module.ts b/src/main/frontend/src/app/app.module.ts
index eb260be..323f08e 100644
--- a/src/main/frontend/src/app/app.module.ts
+++ b/src/main/frontend/src/app/app.module.ts
@@ -6,8 +6,8 @@ import {AppComponent} from './app.component';
import {MatIconRegistry} from "@angular/material/icon";
import {SharedModule} from "./modules/shared/shared.module";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
-import { CatalogComponent } from './pages/catalog/catalog.component';
-import { CompanyModule } from './modules/company/company.module';
+import {CatalogComponent} from './pages/catalog/catalog.component';
+import {CompanyModule} from './modules/company/company.module';
@NgModule({
declarations: [
diff --git a/src/main/frontend/src/app/modules/accounts/pages/login/login.component.ts b/src/main/frontend/src/app/modules/accounts/pages/login/login.component.ts
index 024e06c..9f7b3ba 100644
--- a/src/main/frontend/src/app/modules/accounts/pages/login/login.component.ts
+++ b/src/main/frontend/src/app/modules/accounts/pages/login/login.component.ts
@@ -24,7 +24,7 @@ export class LoginComponent implements OnInit {
ngOnInit(): void {
if (this.accountService.isLoggedIn()) {
- this.router.navigate(['/'])
+ this.router.navigate(['/color'])
}
this.idFormControl = this.formBuilder.control(null, Validators.compose([Validators.required, Validators.pattern(new RegExp('^[0-9]+$'))]))
diff --git a/src/main/frontend/src/app/modules/colors/colors-routing.module.ts b/src/main/frontend/src/app/modules/colors/colors-routing.module.ts
index b248eee..879d0a1 100644
--- a/src/main/frontend/src/app/modules/colors/colors-routing.module.ts
+++ b/src/main/frontend/src/app/modules/colors/colors-routing.module.ts
@@ -1,12 +1,39 @@
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {ListComponent} from "./pages/list/list.component";
+import {AddComponent} from "./pages/add/add.component";
+import {EditComponent} from "./pages/edit/edit.component";
+import {ExploreComponent} from "./pages/explore/explore.component";
+import {MixEditComponent} from "./pages/mix/mix-edit/mix-edit.component";
+import {MixAddComponent} from "./pages/mix/mix-add/mix-add.component";
-import { ColorsComponent } from './colors.component';
-
-const routes: Routes = [{ path: '', component: ColorsComponent }];
+const routes: Routes = [{
+ path: 'list',
+ component: ListComponent
+}, {
+ path: 'add',
+ component: AddComponent
+}, {
+ path: 'edit/:id',
+ component: EditComponent
+}, {
+ path: 'add/mix/:recipeId',
+ component: MixAddComponent
+}, {
+ path: 'edit/mix/:recipeId/:id',
+ component: MixEditComponent
+}, {
+ path: 'explore/:id',
+ component: ExploreComponent
+}, {
+ path: '',
+ pathMatch: 'full',
+ redirectTo: 'list'
+}]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
-export class ColorsRoutingModule { }
+export class ColorsRoutingModule {
+}
diff --git a/src/main/frontend/src/app/modules/colors/colors.component.html b/src/main/frontend/src/app/modules/colors/colors.component.html
deleted file mode 100644
index 8a12e74..0000000
--- a/src/main/frontend/src/app/modules/colors/colors.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
colors works!
diff --git a/src/main/frontend/src/app/modules/colors/colors.component.spec.ts b/src/main/frontend/src/app/modules/colors/colors.component.spec.ts
deleted file mode 100644
index 65dda2b..0000000
--- a/src/main/frontend/src/app/modules/colors/colors.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ColorsComponent } from './colors.component';
-
-describe('ColorsComponent', () => {
- let component: ColorsComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [ ColorsComponent ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(ColorsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/src/main/frontend/src/app/modules/colors/colors.component.ts b/src/main/frontend/src/app/modules/colors/colors.component.ts
deleted file mode 100644
index c8d9021..0000000
--- a/src/main/frontend/src/app/modules/colors/colors.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import {Component, OnInit} from '@angular/core';
-
-@Component({
- selector: 'cre-colors',
- templateUrl: './colors.component.html',
- styleUrls: ['./colors.component.sass']
-})
-export class ColorsComponent implements OnInit {
-
- constructor() {
- }
-
- ngOnInit(): void {
- }
-}
diff --git a/src/main/frontend/src/app/modules/colors/colors.module.ts b/src/main/frontend/src/app/modules/colors/colors.module.ts
index 20e8b4e..d8aa665 100644
--- a/src/main/frontend/src/app/modules/colors/colors.module.ts
+++ b/src/main/frontend/src/app/modules/colors/colors.module.ts
@@ -1,15 +1,31 @@
import {NgModule} from '@angular/core';
import {ColorsRoutingModule} from './colors-routing.module';
-import {ColorsComponent} from './colors.component';
import {SharedModule} from "../shared/shared.module";
+import {ListComponent} from './pages/list/list.component';
+import {AddComponent} from './pages/add/add.component';
+import {EditComponent} from './pages/edit/edit.component';
+import {MatExpansionModule} from "@angular/material/expansion";
+import {FormsModule} from "@angular/forms";
+import {ExploreComponent} from './pages/explore/explore.component';
+import {RecipeInfoComponent} from './components/recipe-info/recipe-info.component';
+import {MixTableComponent} from './components/mix-table/mix-table.component';
+import {StepListComponent} from './components/step-list/step-list.component';
+import {StepTableComponent} from './components/step-table/step-table.component';
+import {MixEditorComponent} from './components/mix-editor/mix-editor.component';
+import {UnitSelectorComponent} from './components/unit-selector/unit-selector.component';
+import {MixAddComponent} from './pages/mix/mix-add/mix-add.component';
+import {MixEditComponent} from './pages/mix/mix-edit/mix-edit.component';
@NgModule({
- declarations: [ColorsComponent],
+ declarations: [ListComponent, AddComponent, EditComponent, ExploreComponent, RecipeInfoComponent, MixTableComponent, StepListComponent, StepTableComponent, MixEditorComponent, UnitSelectorComponent, MixAddComponent, MixEditComponent],
imports: [
ColorsRoutingModule,
- SharedModule
+ SharedModule,
+ MatExpansionModule,
+ FormsModule
]
})
-export class ColorsModule { }
+export class ColorsModule {
+}
diff --git a/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.html b/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.html
new file mode 100644
index 0000000..59bfaad
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.html
@@ -0,0 +1,96 @@
+
+
+ Création d'un mélange pour la recette {{recipe.company.name}}
+ - {{recipe.name}}
+ Modification du mélange {{mix.mixType.name}} de la
+ recette {{recipe.company.name}} - {{recipe.name}}
+
+
+
+ Nom
+
+
+
+
+ Type de produit
+
+
+ {{materialType.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.sass b/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.sass
new file mode 100644
index 0000000..3d15487
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.sass
@@ -0,0 +1,2 @@
+td.units-wrapper
+ width: 3rem
diff --git a/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.ts b/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.ts
new file mode 100644
index 0000000..8839e3b
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/mix-editor/mix-editor.component.ts
@@ -0,0 +1,132 @@
+import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
+import {Mix, MixMaterial, Recipe} from "../../../shared/model/recipe.model";
+import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {MixService} from "../../services/mix.service";
+import {Observable} from "rxjs";
+import {RecipeService} from "../../services/recipe.service";
+import {Material} from "../../../shared/model/material.model";
+import {MaterialService} from "../../../material/service/material.service";
+import {MaterialType} from "../../../shared/model/materialtype.model";
+import {MaterialTypeService} from "../../../material-type/service/material-type.service";
+import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
+import {UNIT_MILLILITER} from "../../../shared/units";
+import {MatTable} from "@angular/material/table";
+import {ActivatedRoute, Router} from "@angular/router";
+
+@Component({
+ selector: 'cre-mix-editor',
+ templateUrl: './mix-editor.component.html',
+ styleUrls: ['./mix-editor.component.sass']
+})
+export class MixEditorComponent extends SubscribingComponent {
+ @ViewChild('mixTable') mixTable: MatTable
+
+ @Input() mixId: number | null
+ @Input() recipeId: number | null
+ @Input() materials: Material[]
+
+ @Output() save = new EventEmitter();
+
+ mix: Mix | null
+ recipe: Recipe | null
+ materialTypes$: Observable
+
+ form: FormGroup
+ nameControl: FormControl
+ materialTypeControl: FormControl
+
+ mixMaterials = []
+ editionMode = false
+ units = UNIT_MILLILITER
+ hoveredMixMaterial: MixMaterial | null
+ columns = ['position', 'material', 'quantity', 'units', 'buttonRemove']
+
+ constructor(
+ private mixService: MixService,
+ private recipeService: RecipeService,
+ private materialService: MaterialService,
+ private materialTypeService: MaterialTypeService,
+ private formBuilder: FormBuilder,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+
+ this.mixId = this.urlUtils.parseIntUrlParam('id')
+
+ this.subscribe(
+ this.recipeService.getById(this.recipeId),
+ {
+ next: r => {
+ this.recipe = r
+ if (this.mixId) {
+ this.editionMode = true
+ this.subscribe(
+ this.mixService.getById(this.mixId),
+ {
+ next: m => {
+ this.mix = m
+ this.mixMaterials = this.mixService.extractMixMaterials(this.mix)
+ this.generateForm()
+ }, error: err => this.handleNotFoundError(err, '/color/list')
+ }
+ )
+ } else {
+ this.mixMaterials.push({})
+ this.generateForm()
+ }
+ },
+ error: err => this.handleNotFoundError(err, '/color/list')
+ }
+ )
+ this.materialTypes$ = this.materialTypeService.all
+ }
+
+ addRow() {
+ this.mixMaterials.push({materialId: null, quantity: null, percents: false})
+ this.mixTable.renderRows()
+ }
+
+ removeRow(position: number) {
+ this.mixMaterials.splice(position, 1)
+ this.mixTable.renderRows()
+ }
+
+ submit() {
+ this.save.emit({
+ name: this.nameControl.value,
+ recipeId: this.recipeId,
+ materialTypeId: this.materialTypeControl.value,
+ mixMaterials: this.mixMaterials,
+ units: this.units
+ })
+ }
+
+ delete() {
+
+ }
+
+ materialUsePercentages(mixMaterial: any) {
+ if (!mixMaterial.materialId) return null
+ const material = this.getMaterialFromId(mixMaterial.materialId);
+ mixMaterial.percents = material && material.materialType.usePercentages
+ return mixMaterial.percents
+ }
+
+ getMaterialFromId(id: number): Material {
+ return id ? this.materials.filter(m => m.id === id)[0] : null
+ }
+
+ private generateForm() {
+ this.nameControl = new FormControl(this.mix ? this.mix.mixType.name : null, Validators.required)
+ this.materialTypeControl = new FormControl(this.mix ? this.mix.mixType.material.materialType.id : null, Validators.required)
+ this.form = this.formBuilder.group({
+ name: this.nameControl,
+ materialType: this.materialTypeControl
+ })
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.html b/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.html
new file mode 100644
index 0000000..1b540bb
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.html
@@ -0,0 +1,100 @@
+
+
+ {{mix.mixType.name}}
+
+
+
+
+
+
+ Casier
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.sass b/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.sass
new file mode 100644
index 0000000..0601391
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.sass
@@ -0,0 +1,22 @@
+@import '../../../../../custom-theme'
+
+mat-expansion-panel
+ width: 40rem
+ margin: 2rem 0
+
+.mix-actions
+ background-color: $color-primary
+ padding: 0 1rem
+
+ div:last-child
+ margin-left: 1rem
+
+.low-quantity
+ background-color: #ffb3b3
+
+::ng-deep span.mix-calculated-quantity
+ &:first-child
+ color: green
+
+ &:last-child
+ color: dimgrey
diff --git a/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.ts b/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.ts
new file mode 100644
index 0000000..a8e8374
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/mix-table/mix-table.component.ts
@@ -0,0 +1,131 @@
+import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {Mix, MixMaterial, Recipe} from "../../../shared/model/recipe.model";
+import {Subject} from "rxjs";
+import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {convertMixMaterialQuantity, UNIT_MILLILITER, UNIT_RATIOS} from "../../../shared/units";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
+
+@Component({
+ selector: 'cre-mix-table',
+ templateUrl: './mix-table.component.html',
+ styleUrls: ['./mix-table.component.sass']
+})
+export class MixTableComponent extends SubscribingComponent {
+ private readonly COLUMNS = ['material', 'materialType', 'quantity', 'quantityCalculated', 'quantityUnits']
+ private readonly COLUMNS_STATIC = ['material', 'materialType', 'quantityStatic', 'quantityUnits']
+
+ @Input() mix: Mix
+ @Input() recipe: Recipe
+ @Input() units$: Subject
+ @Input() deductErrorBody
+ @Input() editing: boolean
+ @Output() locationChange = new EventEmitter<{ id: number, location: string }>()
+ @Output() quantityChange = new EventEmitter<{ id: number, materialId: number, quantity: number }>()
+ @Output() deduct = new EventEmitter()
+
+ mixColumns = this.COLUMNS
+ units = UNIT_MILLILITER
+ computedQuantities: { id: number, percents: boolean, quantity: number }[] = []
+
+ constructor(
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+
+ if (this.editing) {
+ this.mixColumns = this.COLUMNS_STATIC
+ }
+
+ this.mix.mixMaterials.forEach(m => this.computedQuantities.push({
+ id: m.id,
+ percents: m.material.materialType.usePercentages,
+ quantity: m.quantity
+ }))
+
+ this.subscribe(
+ this.units$,
+ {
+ next: u => this.convertQuantities(u)
+ }
+ )
+ }
+
+ changeLocation(event: any) {
+ this.locationChange.emit({id: this.mix.id, location: event.target.value})
+ }
+
+ changeQuantity(event: any, mixMaterial: MixMaterial, isTotal = false) {
+ const newQuantity = parseInt(event.target.value)
+ let ratio = 1
+ if (!isTotal) {
+ const originalQuantity = this.getComputedQuantity(mixMaterial.id)
+ ratio = newQuantity / originalQuantity.quantity
+ } else {
+ ratio = newQuantity / this.getTotalQuantity()
+ }
+ this.computedQuantities.forEach((q, i) => {
+ if (!q.percents) {
+ q.quantity *= ratio
+ }
+ this.emitQuantityChangeEvent(i)
+ })
+ }
+
+ getComputedQuantityRounded(mixMaterial: MixMaterial): number {
+ return this.round(this.getComputedQuantity(mixMaterial.id).quantity)
+ }
+
+ getTotalQuantity(index: number = -1): number {
+ if (index === -1) index = this.computedQuantities.length - 1
+ let totalQuantity = 0
+ for (let i = 0; i <= index; i++) {
+ totalQuantity += this.calculateQuantity(i)
+ }
+ return totalQuantity
+ }
+
+ getCalculatedQuantity(mixMaterial: MixMaterial, index: number): string {
+ const totalQuantity = this.round(this.getTotalQuantity(index))
+ const addedQuantity = this.round(this.calculateQuantity(index))
+ return `+${addedQuantity} (${totalQuantity})`
+ }
+
+ isInLowQuantity(materialId: number): boolean {
+ return this.deductErrorBody[this.mix.id] && this.deductErrorBody[this.mix.id].indexOf(materialId) >= 0
+ }
+
+ round(quantity: number): number {
+ return Math.round(quantity * 1000) / 1000
+ }
+
+ private emitQuantityChangeEvent(index: number) {
+ this.quantityChange.emit({
+ id: this.mix.id,
+ materialId: this.computedQuantities[index].id,
+ quantity: this.calculateQuantity(index)
+ })
+ }
+
+ private convertQuantities(newUnit: string) {
+ this.computedQuantities.forEach(q => convertMixMaterialQuantity(q, this.units, newUnit))
+ this.units = newUnit
+ }
+
+ private getComputedQuantity(id: number): any {
+ return this.computedQuantities.filter(q => q.id == id)[0]
+ }
+
+ private calculateQuantity(index: number): number {
+ const computedQuantity = this.computedQuantities[index]
+ if (!computedQuantity.percents) {
+ return computedQuantity.quantity
+ }
+ return this.computedQuantities[0].quantity * (computedQuantity.quantity / 100)
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.html b/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.html
new file mode 100644
index 0000000..ac847fc
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.html
@@ -0,0 +1,29 @@
+
+
+
{{recipe.company.name}} - {{recipe.name}}
+
+
+
+
Échantillon #{{recipe.sample}}
+
Approuvée le {{recipe.approbationDate}}
+
+
{{recipe.remark}}
+
+
+
{{recipe.description}}
+
+
+
+
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.sass b/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.sass
new file mode 100644
index 0000000..36d3085
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.sass
@@ -0,0 +1,46 @@
+.recipe-info-wrapper
+ background-color: black
+ color: white
+ padding: 1rem
+
+ div
+ margin-right: 3rem
+
+ p
+ margin-bottom: 0
+
+ h3
+ font-weight: bold
+ text-decoration: underline
+ text-transform: uppercase
+
+ &.recipe-not-approved-wrapper
+ p
+ margin-top: 1px
+ margin-right: .4em
+
+ &.recipe-description
+ max-width: 30rem
+
+ &.recipe-note
+ margin-right: 0
+
+ mat-form-field
+ width: 100%
+
+ ::ng-deep .mat-form-field-wrapper
+ padding-bottom: 0
+
+ ::ng-deep .mat-form-field-underline, ::ng-deep .mat-form-field-ripple
+ opacity: 0
+
+ mat-label
+ color: white
+
+ textarea:focus
+ background-color: white
+ color: black
+
+.has-modification-icon
+ width: 2em !important
+
diff --git a/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.ts b/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.ts
new file mode 100644
index 0000000..119133d
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/recipe-info/recipe-info.component.ts
@@ -0,0 +1,14 @@
+import {Component, Input} from '@angular/core';
+import {Recipe} from "../../../shared/model/recipe.model";
+
+@Component({
+ selector: 'cre-recipe-info',
+ templateUrl: './recipe-info.component.html',
+ styleUrls: ['./recipe-info.component.sass']
+})
+export class RecipeInfoComponent {
+ @Input() recipe: Recipe
+ @Input() hasModifications: boolean
+
+ constructor() { }
+}
diff --git a/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.html b/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.html
new file mode 100644
index 0000000..0951d5d
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.html
@@ -0,0 +1,12 @@
+
+
+ Étapes
+
+
+
+
+ {{i + 1}}.{{step.message}}
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.sass b/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.sass
new file mode 100644
index 0000000..f2760a1
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.sass
@@ -0,0 +1,2 @@
+.space
+ width: 1em
diff --git a/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.ts b/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.ts
new file mode 100644
index 0000000..79666f6
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/step-list/step-list.component.ts
@@ -0,0 +1,11 @@
+import {Component, Input} from '@angular/core';
+import {RecipeStep} from "../../../shared/model/recipe.model";
+
+@Component({
+ selector: 'cre-step-list',
+ templateUrl: './step-list.component.html',
+ styleUrls: ['./step-list.component.sass']
+})
+export class StepListComponent {
+ @Input() steps: RecipeStep[]
+}
diff --git a/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.html b/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.html
new file mode 100644
index 0000000..9eb22d4
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.html
@@ -0,0 +1,33 @@
+
+
+ Étapes
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.sass b/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.sass
new file mode 100644
index 0000000..44d6821
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.sass
@@ -0,0 +1,5 @@
+mat-expansion-panel
+ min-width: 560px
+
+mat-form-field
+ width: 20rem
diff --git a/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.ts b/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.ts
new file mode 100644
index 0000000..4038674
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/step-table/step-table.component.ts
@@ -0,0 +1,25 @@
+import {Component, Input, ViewChild} from '@angular/core';
+import {RecipeStep} from "../../../shared/model/recipe.model";
+import {MatTable} from "@angular/material/table";
+
+@Component({
+ selector: 'cre-step-table',
+ templateUrl: './step-table.component.html',
+ styleUrls: ['./step-table.component.sass']
+})
+export class StepTableComponent {
+ @ViewChild('stepTable', {static: true}) stepTable: MatTable
+ readonly columns = ['position', 'message', 'buttonRemove']
+
+ @Input() steps: RecipeStep[]
+
+ addStep() {
+ this.steps.push({id: null, message: ""})
+ this.stepTable.renderRows()
+ }
+
+ removeStep(position: number) {
+ this.steps.splice(position, 1)
+ this.stepTable.renderRows()
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.html b/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.html
new file mode 100644
index 0000000..4b82721
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.html
@@ -0,0 +1,15 @@
+
+ Unités
+
+
+ Millilitres
+ Litres
+ Gallons
+
+
+ mL
+ L
+ gal
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.sass b/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.sass
new file mode 100644
index 0000000..146f5c5
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.sass
@@ -0,0 +1,2 @@
+mat-form-field.short
+ width: 3rem
diff --git a/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.ts b/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.ts
new file mode 100644
index 0000000..418677f
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/components/unit-selector/unit-selector.component.ts
@@ -0,0 +1,17 @@
+import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
+
+@Component({
+ selector: 'cre-unit-selector',
+ templateUrl: './unit-selector.component.html',
+ styleUrls: ['./unit-selector.component.sass']
+})
+export class UnitSelectorComponent {
+ readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
+
+ @Input() unit = UNIT_MILLILITER
+ @Input() showLabel = true
+ @Input() short = false
+
+ @Output() unitChange = new EventEmitter()
+}
diff --git a/src/main/frontend/src/app/modules/colors/pages/add/add.component.html b/src/main/frontend/src/app/modules/colors/pages/add/add.component.html
new file mode 100644
index 0000000..f9d4e9d
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/add/add.component.html
@@ -0,0 +1,8 @@
+
+
diff --git a/src/main/frontend/src/app/modules/colors/pages/add/add.component.sass b/src/main/frontend/src/app/modules/colors/pages/add/add.component.sass
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/frontend/src/app/modules/colors/pages/add/add.component.ts b/src/main/frontend/src/app/modules/colors/pages/add/add.component.ts
new file mode 100644
index 0000000..3fc5e33
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/add/add.component.ts
@@ -0,0 +1,98 @@
+import {Component} from '@angular/core';
+import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {RecipeService} from "../../services/recipe.service";
+import {FormField} from "../../../shared/components/entity-add/entity-add.component";
+import {FormBuilder, Validators} from "@angular/forms";
+import {CompanyService} from "../../../company/service/company.service";
+import {map} from "rxjs/operators";
+import {ActivatedRoute, Router} from "@angular/router";
+
+@Component({
+ selector: 'cre-add',
+ templateUrl: './add.component.html',
+ styleUrls: ['./add.component.sass']
+})
+export class AddComponent extends SubscribingComponent {
+ formFields: FormField[] = [
+ {
+ name: 'name',
+ label: 'Nom',
+ icon: 'form-textbox',
+ type: 'text',
+ validator: Validators.required,
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Un nom est requis'}
+ ]
+ },
+ {
+ name: 'description',
+ label: 'Description',
+ icon: 'text',
+ type: 'text',
+ validator: Validators.required,
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Une description est requise'}
+ ]
+ },
+ {
+ name: 'sample',
+ label: 'Échantillon',
+ icon: 'pound',
+ type: 'number',
+ validator: Validators.compose([Validators.required, Validators.min(0)]),
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Un numéro d\'échantillon est requis'},
+ {conditionFn: errors => errors.min, message: 'Le numéro d\'échantillon doit être supérieur ou égal à 0'}
+ ]
+ },
+ {
+ name: 'approbationDate',
+ label: 'Date d\'approbation',
+ icon: 'calendar',
+ type: 'date'
+ },
+ {
+ name: 'remark',
+ label: 'Remarque',
+ icon: 'text',
+ type: 'text'
+ },
+ {
+ name: 'company',
+ label: 'Bannière',
+ icon: 'domain',
+ type: 'select',
+ validator: Validators.required,
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Une bannière est requise'}
+ ],
+ options$: this.companyService.all.pipe(map(companies => companies.map(c => {
+ return {value: c.id, label: c.name}
+ })))
+ }
+ ]
+ unknownError = false
+ errorMessage: string | null
+
+ constructor(
+ private recipeService: RecipeService,
+ private companyService: CompanyService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ submit(values) {
+ this.subscribe(
+ this.recipeService.save(values.name, values.description, values.sample, values.approbationDate, values.remark, values.company),
+ {
+ next: recipe => this.urlUtils.navigateTo(`/color/edit/${recipe.id}`),
+ error: err => {
+ this.unknownError = true
+ console.error(err)
+ }
+ }
+ )
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.html b/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.html
new file mode 100644
index 0000000..7d0ab6b
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.html
@@ -0,0 +1,68 @@
+
+
+
Il n'y a aucun mélange dans cette recette
+
+
+
Il n'y a aucune étape dans cette recette
+
+
+
+
+
+
+
+
+
+
+ Unités
+
+ Millilitres
+ Litres
+ Gallons
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mélanges
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.sass b/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.sass
new file mode 100644
index 0000000..a474d96
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.sass
@@ -0,0 +1,17 @@
+.recipe-wrapper > div
+ margin: 0 3rem 3rem
+
+ mat-card
+ margin-top: 3rem
+
+.recipe-mixes-wrapper mat-card
+ min-width: 20rem
+
+mat-card
+ mat-card-content
+ margin-bottom: 0
+ padding-top: 16px !important
+ padding-bottom: 0 !important
+
+ mat-card-actions
+ margin-top: 0
diff --git a/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.ts b/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.ts
new file mode 100644
index 0000000..59f8923
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/edit/edit.component.ts
@@ -0,0 +1,142 @@
+import {Component} from '@angular/core';
+import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {Recipe} from "../../../shared/model/recipe.model";
+import {RecipeService} from "../../services/recipe.service";
+import {ActivatedRoute, Router} from "@angular/router";
+import {FormBuilder, Validators} from "@angular/forms";
+import {Subject} from "rxjs";
+import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
+import {AccountService} from "../../../accounts/services/account.service";
+import {EmployeePermission} from "../../../shared/model/employee";
+import {EntityEditComponent} from "../../../shared/components/entity-edit/entity-edit.component";
+
+@Component({
+ selector: 'cre-edit',
+ templateUrl: './edit.component.html',
+ styleUrls: ['./edit.component.sass']
+})
+export class EditComponent extends SubscribingComponent {
+ readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
+
+ recipe: Recipe | null
+ formFields = [
+ {
+ name: 'name',
+ label: 'Nom',
+ icon: 'form-textbox',
+ type: 'text',
+ validator: Validators.required,
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Un nom est requis'}
+ ]
+ },
+ {
+ name: 'description',
+ label: 'Description',
+ icon: 'text',
+ type: 'text',
+ validator: Validators.required,
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Une description est requise'}
+ ]
+ },
+ {
+ name: 'sample',
+ label: 'Échantillon',
+ icon: 'pound',
+ type: 'number',
+ validator: Validators.compose([Validators.required, Validators.min(0)]),
+ errorMessages: [
+ {conditionFn: errors => errors.required, message: 'Un numéro d\'échantillon est requis'},
+ {conditionFn: errors => errors.min, message: 'Le numéro d\'échantillon doit être supérieur ou égal à 0'}
+ ]
+ },
+ {
+ name: 'approbationDate',
+ label: 'Date d\'approbation',
+ icon: 'calendar',
+ type: 'date'
+ },
+ {
+ name: 'remark',
+ label: 'Remarque',
+ icon: 'text',
+ type: 'text'
+ },
+ {
+ name: 'company',
+ label: 'Bannière',
+ icon: 'domain',
+ type: 'text',
+ readonly: true,
+ valueFn: recipe => recipe.company.name,
+ }
+ ]
+ unknownError = false
+ errorMessage: string | null
+ units$ = new Subject()
+
+ constructor(
+ private recipeService: RecipeService,
+ private accountService: AccountService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+
+ const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
+ this.subscribe(
+ this.recipeService.getById(id),
+ {
+ next: recipe => this.recipe = recipe,
+ error: err => {
+ if (err.status === 404) {
+ this.router.navigate(['/color/list'])
+ } else {
+ this.unknownError = true
+ }
+ }
+ },
+ 1
+ )
+ }
+
+ changeUnits(unit: string) {
+ this.units$.next(unit)
+ }
+
+ submit(editComponent: EntityEditComponent) {
+ const values = editComponent.values
+ this.subscribe(
+ this.recipeService.update(this.recipe.id, values.name, values.description, values.sample, values.approbationDate, values.remark, this.recipe.steps),
+ {
+ next: () => this.router.navigate(['/color/list']),
+ error: err => {
+ if (err.status === 409) {
+ this.errorMessage = `Une couleur avec le nom '${values.name}' et la bannière '${this.recipe.company.name}' existe déjà`
+ } else {
+ this.unknownError = true
+ console.error(err)
+ }
+ }
+ }
+ )
+ }
+
+ delete() {
+ this.subscribe(
+ this.recipeService.delete(this.recipe.id),
+ {
+ next: () => this.router.navigate(['/color/list'])
+ }
+ )
+ }
+
+ get hasDeletePermission(): boolean {
+ return this.accountService.hasPermission(EmployeePermission.REMOVE_RECIPE)
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.html b/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.html
new file mode 100644
index 0000000..961922f
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.html
@@ -0,0 +1,59 @@
+
+
+
+
+
Une erreur est survenue
+
Certains produit ne sont pas en quantité suffisante dans l'inventaire
+
+
+
Les modifications ont été enregistrées
+
Les quantités des produits utilisés ont été déduites de l'inventaire
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Note
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Images
+
+
diff --git a/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.sass b/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.sass
new file mode 100644
index 0000000..24c7079
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.sass
@@ -0,0 +1,21 @@
+.recipe-content
+ display: flex
+ flex-direction: column
+ align-items: center
+
+ div
+ margin-bottom: 1rem
+
+.recipe-images
+ margin-top: 5rem
+ padding: 2rem
+ background-color: grey
+
+@media screen and (min-width: 1920px)
+ .action-bar
+ padding-bottom: 5rem
+
+ .recipe-content
+ flex-direction: row
+ justify-content: space-around
+ align-items: start
diff --git a/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.ts b/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.ts
new file mode 100644
index 0000000..cac50da
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/explore/explore.component.ts
@@ -0,0 +1,135 @@
+import {Component} from '@angular/core';
+import {RecipeService} from "../../services/recipe.service";
+import {ActivatedRoute, Router} from "@angular/router";
+import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {Recipe} from "../../../shared/model/recipe.model";
+import {Observable, Subject} from "rxjs";
+import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
+import {FormBuilder} from "@angular/forms";
+
+@Component({
+ selector: 'cre-explore',
+ templateUrl: './explore.component.html',
+ styleUrls: ['./explore.component.sass']
+})
+export class ExploreComponent extends SubscribingComponent {
+ readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
+
+ recipe: Recipe | null
+ error: string
+ deductErrorBody = {}
+ success: string
+ units$ = new Subject()
+
+ hasModifications = false
+ note: string | null
+ quantitiesChanges = new Map>()
+ mixesLocationChanges = new Map()
+
+ // Errors
+ readonly ERROR_UNKNOWN = 'unknown'
+ readonly ERROR_DEDUCT = 'deduct'
+ // Success
+ readonly SUCCESS_SAVE = 'save'
+ readonly SUCCESS_DEDUCT = 'deduct'
+
+ constructor(
+ private recipeService: RecipeService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit(): void {
+ super.ngOnInit()
+
+ const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
+ this.subscribe(
+ this.recipeService.getById(id),
+ {
+ next: r => {
+ this.recipe = r
+ this.note = r.note
+ },
+ error: err => {
+ if (err.status == 404) {
+ this.router.navigate(['/colors/list'])
+ } else {
+ this.error = this.ERROR_UNKNOWN
+ }
+ }
+ },
+ 1
+ )
+ }
+
+ changeUnits(unit: string) {
+ this.units$.next(unit)
+ }
+
+ changeNote(event: any) {
+ this.hasModifications = true
+ this.note = event.target.value
+ }
+
+ changeQuantity(event: { id: number, materialId: number, quantity: number }) {
+ if (!this.quantitiesChanges.has(event.id)) this.quantitiesChanges.set(event.id, new Map())
+ this.quantitiesChanges.get(event.id).set(event.materialId, event.quantity)
+ }
+
+ changeMixLocation(event: { id: number, location: string }) {
+ this.hasModifications = true
+ this.mixesLocationChanges.set(event.id, event.location)
+ }
+
+ saveModifications() {
+ this.subscribe(
+ this.recipeService.saveExplorerModifications(this.recipe.id, this.note, this.mixesLocationChanges),
+ {
+ next: () => {
+ this.hasModifications = false
+ this.error = null
+ this.success = this.SUCCESS_SAVE
+ },
+ error: err => {
+ this.success = null
+ this.error = this.ERROR_UNKNOWN
+ console.error(err)
+ }
+ },
+ 1
+ )
+ }
+
+ deductQuantities() {
+ this.performDeductQuantities(this.recipeService.deductQuantities(this.recipe, this.quantitiesChanges))
+ }
+
+ deductMixQuantities(mixId: number) {
+ this.performDeductQuantities(this.recipeService.deductMixQuantities(this.recipe, mixId, this.quantitiesChanges.get(mixId)))
+ }
+
+ performDeductQuantities(observable: Observable) {
+ this.subscribe(
+ observable,
+ {
+ next: () => {
+ this.error = null
+ this.success = this.SUCCESS_DEDUCT
+ },
+ error: err => {
+ this.success = null
+ if (err.status === 409) { // There is not enough of one or more materials in the inventory
+ this.error = this.ERROR_DEDUCT
+ this.deductErrorBody = err.error
+ } else {
+ this.error = this.ERROR_UNKNOWN
+ console.error(err)
+ }
+ }
+ },
+ 1
+ )
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/pages/list/list.component.html b/src/main/frontend/src/app/modules/colors/pages/list/list.component.html
new file mode 100644
index 0000000..c6a02ab
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/list/list.component.html
@@ -0,0 +1,71 @@
+
+
+ Recherche
+
+
+
+
+
+
+
+
+
+
+
+ {{companyRecipes.company}}
+
+
+
+
+
+
+
+
+
+
+ Nom |
+ {{recipe.name}} |
+
+
+
+ Description |
+ {{recipe.description}} |
+
+
+
+ Échantillon |
+ #{{recipe.sample}} |
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+
+
+
+
diff --git a/src/main/frontend/src/app/modules/colors/pages/list/list.component.sass b/src/main/frontend/src/app/modules/colors/pages/list/list.component.sass
new file mode 100644
index 0000000..61985de
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/list/list.component.sass
@@ -0,0 +1,6 @@
+mat-expansion-panel
+ width: 60rem
+ margin: 20px auto
+
+.button-add
+ margin-top: .8rem
diff --git a/src/main/frontend/src/app/modules/colors/pages/list/list.component.ts b/src/main/frontend/src/app/modules/colors/pages/list/list.component.ts
new file mode 100644
index 0000000..1107b7e
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/list/list.component.ts
@@ -0,0 +1,56 @@
+import {Component} from '@angular/core';
+import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {RecipeService} from "../../services/recipe.service";
+import {EmployeePermission} from "../../../shared/model/employee";
+import {AccountService} from "../../../accounts/services/account.service";
+import {Recipe} from "../../../shared/model/recipe.model";
+import {ActivatedRoute, Router} from "@angular/router";
+
+@Component({
+ selector: 'cre-list',
+ templateUrl: './list.component.html',
+ styleUrls: ['./list.component.sass']
+})
+export class ListComponent extends SubscribingComponent {
+ recipes$ = this.recipeService.allSortedByCompany
+ tableCols = ['name', 'description', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit']
+ searchQuery = ""
+ panelForcedExpanded = false
+ recipesHidden = []
+
+ constructor(
+ private recipeService: RecipeService,
+ private accountService: AccountService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+ }
+
+ searchRecipe(recipe: Recipe) {
+ if (this.searchQuery.length > 0) {
+ this.panelForcedExpanded = true
+ }
+ const positive = this.searchString(recipe.name) ||
+ this.searchString(recipe.description) ||
+ this.searchString(recipe.sample.toString())
+ this.recipesHidden[recipe.id] = !positive
+ return positive
+ }
+
+ isCompanyHidden(companyRecipes: Recipe[]): boolean {
+ return (this.searchQuery && this.searchQuery.length > 0) && companyRecipes.map(r => this.recipesHidden[r.id]).filter(r => !r).length <= 0
+ }
+
+ get hasEditPermission(): boolean {
+ return this.accountService.hasPermission(EmployeePermission.EDIT_RECIPE)
+ }
+
+ private searchString(value: string): boolean {
+ return value.toLowerCase().indexOf(this.searchQuery.toLowerCase()) >= 0
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.html b/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.html
new file mode 100644
index 0000000..128e7d2
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.html
@@ -0,0 +1,5 @@
+
+
diff --git a/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.sass b/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.sass
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.ts b/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.ts
new file mode 100644
index 0000000..ca267d1
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/mix/mix-add/mix-add.component.ts
@@ -0,0 +1,43 @@
+import {Component} from '@angular/core';
+import {Material} from "../../../../shared/model/material.model";
+import {MaterialService} from "../../../../material/service/material.service";
+import {ActivatedRoute, Router} from "@angular/router";
+import {SubscribingComponent} from "../../../../shared/components/subscribing.component";
+import {MixService} from "../../../services/mix.service";
+
+@Component({
+ selector: 'cre-mix-add',
+ templateUrl: './mix-add.component.html',
+ styleUrls: ['./mix-add.component.sass']
+})
+export class MixAddComponent extends SubscribingComponent {
+ recipeId: number | null
+ materials: Material[] | null
+
+ constructor(
+ private materialService: MaterialService,
+ private mixService: MixService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit(): void {
+ super.ngOnInit()
+
+ this.recipeId = this.urlUtils.parseIntUrlParam('recipeId')
+
+ this.subscribe(
+ this.materialService.getAllForMixCreation(this.recipeId),
+ {next: m => this.materials = m}
+ )
+ }
+
+ submit(values) {
+ this.subscribe(
+ this.mixService.saveWithUnits(values.name, values.recipeId, values.materialTypeId, values.mixMaterials, values.units),
+ {next: () => this.urlUtils.navigateTo(`/color/edit/${this.recipeId}`)}
+ )
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.html b/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.html
new file mode 100644
index 0000000..762bae6
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.html
@@ -0,0 +1,6 @@
+
+
diff --git a/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.sass b/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.sass
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.ts b/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.ts
new file mode 100644
index 0000000..981998d
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/pages/mix/mix-edit/mix-edit.component.ts
@@ -0,0 +1,45 @@
+import {Component} from '@angular/core';
+import {ActivatedRoute, Router} from "@angular/router";
+import {SubscribingComponent} from "../../../../shared/components/subscribing.component";
+import {Material} from "../../../../shared/model/material.model";
+import {MaterialService} from "../../../../material/service/material.service";
+import {MixService} from "../../../services/mix.service";
+
+@Component({
+ selector: 'cre-mix-edit',
+ templateUrl: './mix-edit.component.html',
+ styleUrls: ['./mix-edit.component.sass']
+})
+export class MixEditComponent extends SubscribingComponent {
+ mixId: number | null
+ recipeId: number | null
+ materials: Material[] | null
+
+ constructor(
+ private materialService: MaterialService,
+ private mixService: MixService,
+ router: Router,
+ activatedRoute: ActivatedRoute
+ ) {
+ super(activatedRoute, router)
+ }
+
+ ngOnInit(): void {
+ super.ngOnInit()
+
+ this.mixId = this.urlUtils.parseIntUrlParam('id')
+ this.recipeId = this.urlUtils.parseIntUrlParam('recipeId')
+
+ this.subscribe(
+ this.materialService.getAllForMixUpdate(this.mixId),
+ {next: m => this.materials = m}
+ )
+ }
+
+ submit(values) {
+ this.subscribe(
+ this.mixService.updateWithUnits(this.mixId, values.name, values.materialTypeId, values.mixMaterials, values.units),
+ {next: () => this.urlUtils.navigateTo(`/color/edit/${this.recipeId}`)}
+ )
+ }
+}
diff --git a/src/main/frontend/src/app/modules/colors/services/mix.service.ts b/src/main/frontend/src/app/modules/colors/services/mix.service.ts
new file mode 100644
index 0000000..e33db7e
--- /dev/null
+++ b/src/main/frontend/src/app/modules/colors/services/mix.service.ts
@@ -0,0 +1,75 @@
+import {Injectable} from '@angular/core';
+import {ApiService} from "../../shared/service/api.service";
+import {convertMixMaterialQuantity, UNIT_MILLILITER} from "../../shared/units";
+import {Observable} from "rxjs";
+import {Mix} from "../../shared/model/recipe.model";
+
+@Injectable({
+ providedIn: 'root'
+})
+export class MixService {
+ constructor(
+ private api: ApiService
+ ) {
+ }
+
+ get all(): Observable {
+ return this.api.get('/recipe/mix')
+ }
+
+ getById(id: number): Observable {
+ return this.api.get(`/recipe/mix/${id}`)
+ }
+
+ saveWithUnits(name: string, recipeId: number, materialTypeId: number, mixMaterials: { materialId: number, quantity: number, percents: boolean }[], units: string): Observable {
+ return this.save(name, recipeId, materialTypeId, this.convertMixMaterialsToMl(mixMaterials, units))
+ }
+
+ save(name: string, recipeId: number, materialTypeId: number, mixMaterials: { materialId: number, quantity: number }[]): Observable {
+ const body = {
+ name,
+ recipeId,
+ materialTypeId,
+ mixMaterials: {}
+ }
+ this.appendMixMaterialsToBody(mixMaterials, body)
+ return this.api.post('/recipe/mix', body)
+ }
+
+ updateWithUnits(id: number, name: string, materialTypeId: number, mixMaterials: { materialId: number, quantity: number, percents: boolean }[], units: string): Observable {
+ return this.update(id, name, materialTypeId, this.convertMixMaterialsToMl(mixMaterials, units))
+ }
+
+ update(id: number, name: string, materialTypeId: number, mixMaterials: { materialId: number, quantity: number }[]): Observable {
+ const body = {
+ id,
+ name,
+ materialTypeId,
+ mixMaterials: {}
+ }
+ this.appendMixMaterialsToBody(mixMaterials, body)
+ return this.api.put('/recipe/mix', body)
+ }
+
+ extractMixMaterials(mix: Mix): { materialId: number, quantity: number, percents: boolean }[] {
+ return mix.mixMaterials.map(m => {
+ return {materialId: m.material.id, quantity: m.quantity, percents: m.material.materialType.usePercentages}
+ })
+ }
+
+ private convertMixMaterialsToMl(mixMaterials: { materialId: number, quantity: number, percents: boolean }[], units: string): { materialId: number, quantity: number }[] {
+ return mixMaterials.map(m => {
+ return {
+ materialId: m.materialId,
+ quantity: convertMixMaterialQuantity(m, units, UNIT_MILLILITER)
+ }
+ })
+ }
+
+ private appendMixMaterialsToBody(mixMaterials: { materialId: number, quantity: number }[], body: any) {
+ mixMaterials
+ .filter(m => m.materialId != null && m.quantity != null)
+ .forEach(m => body.mixMaterials[m.materialId] = m.quantity)
+ }
+}
+
diff --git a/src/main/frontend/src/app/modules/colors/services/recipe.service.ts b/src/main/frontend/src/app/modules/colors/services/recipe.service.ts
index c55207a..e8ce843 100644
--- a/src/main/frontend/src/app/modules/colors/services/recipe.service.ts
+++ b/src/main/frontend/src/app/modules/colors/services/recipe.service.ts
@@ -1,9 +1,106 @@
-import { Injectable } from '@angular/core';
+import {Injectable} from '@angular/core';
+import {ApiService} from "../../shared/service/api.service";
+import {Observable} from "rxjs";
+import {Recipe, RecipeStep} from "../../shared/model/recipe.model";
+import {map} from "rxjs/operators";
@Injectable({
providedIn: 'root'
})
export class RecipeService {
+ constructor(
+ private api: ApiService
+ ) {
+ }
- constructor() { }
+ get all(): Observable {
+ return this.api.get('/recipe')
+ }
+
+ get allSortedByCompany(): Observable<{ company: string, recipes: Recipe[] }[]> {
+ return this.all.pipe(map(recipes => {
+ const mapped = []
+ recipes.forEach(r => {
+ if (!mapped[r.company.id]) {
+ mapped[r.company.id] = {company: r.company.name, recipes: []}
+ }
+ mapped[r.company.id].recipes.push(r)
+ })
+ return mapped.filter(e => e != null) // Filter to remove empty elements in the array that appears for some reason
+ }))
+ }
+
+ getById(id: number): Observable {
+ return this.api.get(`/recipe/${id}`)
+ }
+
+ save(name: string, description: string, sample: number, approbationDate: string, remark: string, companyId: number): Observable {
+ const body = {name, description, sample, remark, companyId}
+ if (approbationDate) {
+ // @ts-ignore
+ body.approbationDate = approbationDate
+ }
+ return this.api.post('/recipe', body)
+ }
+
+ update(id: number, name: string, description: string, sample: number, approbationDate: string, remark: string, steps: RecipeStep[] = null) {
+ const body = {id, name, description, sample, remark, steps}
+ if (approbationDate) {
+ // @ts-ignore
+ body.approbationDate = approbationDate
+ }
+ return this.api.put('/recipe', body)
+ }
+
+ saveExplorerModifications(id: number, note: string, mixesLocationChange: Map): Observable {
+ const body = {
+ id,
+ note,
+ mixesLocation: {}
+ }
+ mixesLocationChange.forEach((l, i) => body.mixesLocation[i] = l)
+
+ return this.api.put('/recipe/public', body)
+ }
+
+ deductMixQuantities(recipe: Recipe, mixId: number, quantities: Map): Observable {
+ return this.sendDeductBody(this.buildDeductMixBody(recipe, mixId, quantities))
+ }
+
+ deductQuantities(recipe: Recipe, quantities: Map>): Observable {
+ return this.sendDeductBody(this.buildDeductBody(recipe, quantities))
+ }
+
+ delete(id: number): Observable {
+ return this.api.delete(`/recipe/${id}`)
+ }
+
+ private buildDeductMixBody(recipe: Recipe, mixId: number, quantities: Map): any {
+ const mix = recipe.mixes.filter(m => m.id === mixId)[0]
+ const body = {id: recipe.id, quantities: {}}
+ body.quantities[mixId] = {}
+ const firstMaterial = mix.mixMaterials[0].material.id
+ mix.mixMaterials.forEach(m => {
+ if (quantities && quantities.has(m.material.id)) {
+ body.quantities[mix.id][m.material.id] = quantities.get(m.material.id)
+ } else {
+ let quantity = m.quantity
+ if (m.material.materialType.usePercentages) quantity = body.quantities[mix.id][firstMaterial] * (quantity / 100)
+ body.quantities[mix.id][m.material.id] = quantity
+ }
+ })
+ return body
+ }
+
+ private buildDeductBody(recipe: Recipe, quantities: Map>): any {
+ const body = {id: recipe.id, quantities: {}}
+ recipe.mixes.forEach(mix => {
+ body.quantities[mix.id] = this.buildDeductMixBody(recipe, mix.id, quantities.get(mix.id)).quantities[mix.id]
+ })
+ return body
+ }
+
+ private sendDeductBody(body: any): Observable {
+ return this.api.put('/recipe/deduct', body)
+ }
}
diff --git a/src/main/frontend/src/app/modules/company/pages/add/add.component.ts b/src/main/frontend/src/app/modules/company/pages/add/add.component.ts
index 096f910..fd0db62 100644
--- a/src/main/frontend/src/app/modules/company/pages/add/add.component.ts
+++ b/src/main/frontend/src/app/modules/company/pages/add/add.component.ts
@@ -1,9 +1,9 @@
import {Component} from '@angular/core';
import {CompanyService} from "../../service/company.service";
-import {Validators} from "@angular/forms";
+import {FormBuilder, Validators} from "@angular/forms";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
-import {Router} from "@angular/router";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-add',
@@ -28,9 +28,10 @@ export class AddComponent extends SubscribingComponent {
constructor(
private companyService: CompanyService,
- private router: Router
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
submit(values) {
@@ -39,12 +40,12 @@ export class AddComponent extends SubscribingComponent {
{
next: () => this.router.navigate(['/catalog/company/list']),
error: err => {
- if (err.status == 409) {
+ if (err.status === 409) {
this.errorMessage = `Une bannière avec le nom '${values.name}' existe déjà`
} else {
this.unknownError = true
+ console.log(err)
}
- console.log(err)
}
}
)
diff --git a/src/main/frontend/src/app/modules/company/pages/edit/edit.component.ts b/src/main/frontend/src/app/modules/company/pages/edit/edit.component.ts
index 33bdec1..b828812 100644
--- a/src/main/frontend/src/app/modules/company/pages/edit/edit.component.ts
+++ b/src/main/frontend/src/app/modules/company/pages/edit/edit.component.ts
@@ -2,7 +2,7 @@ import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Company} from "../../../shared/model/company.model";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
-import {Validators} from "@angular/forms";
+import {FormBuilder, Validators} from "@angular/forms";
import {CompanyService} from "../../service/company.service";
import {ActivatedRoute, Router} from "@angular/router";
@@ -30,10 +30,10 @@ export class EditComponent extends SubscribingComponent {
constructor(
private companyService: CompanyService,
- private router: Router,
- private activatedRoute: ActivatedRoute
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/company/pages/list/list.component.ts b/src/main/frontend/src/app/modules/company/pages/list/list.component.ts
index 4f0d118..83c85f0 100644
--- a/src/main/frontend/src/app/modules/company/pages/list/list.component.ts
+++ b/src/main/frontend/src/app/modules/company/pages/list/list.component.ts
@@ -2,6 +2,8 @@ import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {CompanyService} from "../../service/company.service";
import {EmployeePermission} from "../../../shared/model/employee";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-list',
@@ -20,8 +22,10 @@ export class ListComponent extends SubscribingComponent {
}]
constructor(
- private companyService: CompanyService
+ private companyService: CompanyService,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
}
diff --git a/src/main/frontend/src/app/modules/employees/pages/add/add.component.sass b/src/main/frontend/src/app/modules/employees/pages/add/add.component.sass
index e69de29..3c27c57 100644
--- a/src/main/frontend/src/app/modules/employees/pages/add/add.component.sass
+++ b/src/main/frontend/src/app/modules/employees/pages/add/add.component.sass
@@ -0,0 +1,2 @@
+mat-card
+ max-width: 90rem
diff --git a/src/main/frontend/src/app/modules/employees/pages/add/add.component.ts b/src/main/frontend/src/app/modules/employees/pages/add/add.component.ts
index dd757b9..3e8cc51 100644
--- a/src/main/frontend/src/app/modules/employees/pages/add/add.component.ts
+++ b/src/main/frontend/src/app/modules/employees/pages/add/add.component.ts
@@ -1,12 +1,12 @@
import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
-import {FormControl, FormGroup, Validators} from "@angular/forms";
+import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {PermissionsFieldComponent} from "../../../shared/components/permissions-field/permissions-field.component";
import {EmployeeGroup} from "../../../shared/model/employee";
import {Observable, Subject} from "rxjs";
import {GroupService} from "../../../groups/services/group.service";
import {take, takeUntil} from "rxjs/operators";
import {EmployeeService} from "../../services/employee.service";
-import {Router} from "@angular/router";
+import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
@Component({
@@ -30,9 +30,10 @@ export class AddComponent extends SubscribingComponent {
constructor(
private employeeService: EmployeeService,
private groupService: GroupService,
- private router: Router
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.sass b/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.sass
index e69de29..3c27c57 100644
--- a/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.sass
+++ b/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.sass
@@ -0,0 +1,2 @@
+mat-card
+ max-width: 90rem
diff --git a/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.ts b/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.ts
index 10aee43..6199b9c 100644
--- a/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.ts
+++ b/src/main/frontend/src/app/modules/employees/pages/edit/edit.component.ts
@@ -29,11 +29,11 @@ export class EditComponent extends SubscribingComponent {
private accountService: AccountService,
private employeeService: EmployeeService,
private groupService: GroupService,
- private activatedRoute: ActivatedRoute,
- private router: Router,
- private formBuilder: FormBuilder
+ private formBuilder: FormBuilder,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/employees/pages/list/list.component.ts b/src/main/frontend/src/app/modules/employees/pages/list/list.component.ts
index 0e365bc..f5c6f02 100644
--- a/src/main/frontend/src/app/modules/employees/pages/list/list.component.ts
+++ b/src/main/frontend/src/app/modules/employees/pages/list/list.component.ts
@@ -6,6 +6,8 @@ import {takeUntil} from "rxjs/operators";
import {AccountService} from "../../../accounts/services/account.service";
import {animate, state, style, transition, trigger} from "@angular/animations";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-employees',
@@ -27,9 +29,11 @@ export class ListComponent extends SubscribingComponent {
constructor(
private employeeService: EmployeeService,
- private accountService: AccountService
+ private accountService: AccountService,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/employees/pages/password-edit/password-edit.component.ts b/src/main/frontend/src/app/modules/employees/pages/password-edit/password-edit.component.ts
index f0f12d8..eda5cc9 100644
--- a/src/main/frontend/src/app/modules/employees/pages/password-edit/password-edit.component.ts
+++ b/src/main/frontend/src/app/modules/employees/pages/password-edit/password-edit.component.ts
@@ -19,10 +19,10 @@ export class PasswordEditComponent extends SubscribingComponent {
constructor(
private employeeService: EmployeeService,
private formBuilder: FormBuilder,
- private router: Router,
- private activatedRoute: ActivatedRoute
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/groups/pages/add/add.component.sass b/src/main/frontend/src/app/modules/groups/pages/add/add.component.sass
index 5586bfb..d194b62 100644
--- a/src/main/frontend/src/app/modules/groups/pages/add/add.component.sass
+++ b/src/main/frontend/src/app/modules/groups/pages/add/add.component.sass
@@ -1,5 +1,5 @@
mat-card
- width: max-content
+ max-width: 90rem
mat-checkbox
font-size: .8em
diff --git a/src/main/frontend/src/app/modules/groups/pages/add/add.component.ts b/src/main/frontend/src/app/modules/groups/pages/add/add.component.ts
index c448a6e..efa8da0 100644
--- a/src/main/frontend/src/app/modules/groups/pages/add/add.component.ts
+++ b/src/main/frontend/src/app/modules/groups/pages/add/add.component.ts
@@ -1,7 +1,7 @@
import {Component, ViewChild} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {GroupService} from "../../services/group.service";
-import {Router} from "@angular/router";
+import {ActivatedRoute, Router} from "@angular/router";
import {PermissionsFieldComponent} from "../../../shared/components/permissions-field/permissions-field.component";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
@@ -20,9 +20,10 @@ export class AddComponent extends SubscribingComponent {
constructor(
private formBuilder: FormBuilder,
private groupService: GroupService,
- private router: Router
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.sass b/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.sass
index e69de29..3c27c57 100644
--- a/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.sass
+++ b/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.sass
@@ -0,0 +1,2 @@
+mat-card
+ max-width: 90rem
diff --git a/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.ts b/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.ts
index f50737b..0ef4658 100644
--- a/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.ts
+++ b/src/main/frontend/src/app/modules/groups/pages/edit/edit.component.ts
@@ -21,13 +21,13 @@ export class EditComponent extends SubscribingComponent {
private _nameControl: FormControl
constructor(
- private activatedRoute: ActivatedRoute,
- private router: Router,
private accountService: AccountService,
private groupService: GroupService,
- private formBuilder: FormBuilder
+ private formBuilder: FormBuilder,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/groups/pages/list/list.component.ts b/src/main/frontend/src/app/modules/groups/pages/list/list.component.ts
index 7afe55f..822c9d0 100644
--- a/src/main/frontend/src/app/modules/groups/pages/list/list.component.ts
+++ b/src/main/frontend/src/app/modules/groups/pages/list/list.component.ts
@@ -6,6 +6,8 @@ import {takeUntil} from "rxjs/operators";
import {animate, state, style, transition, trigger} from "@angular/animations";
import {AccountService} from "../../../accounts/services/account.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-groups',
@@ -27,9 +29,11 @@ export class ListComponent extends SubscribingComponent {
constructor(
private groupService: GroupService,
- private accountService: AccountService
+ private accountService: AccountService,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
diff --git a/src/main/frontend/src/app/modules/material-type/pages/add/add.component.ts b/src/main/frontend/src/app/modules/material-type/pages/add/add.component.ts
index ac6d7e5..1e18920 100644
--- a/src/main/frontend/src/app/modules/material-type/pages/add/add.component.ts
+++ b/src/main/frontend/src/app/modules/material-type/pages/add/add.component.ts
@@ -1,9 +1,9 @@
import {Component} from '@angular/core';
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
-import {Validators} from "@angular/forms";
+import {FormBuilder, Validators} from "@angular/forms";
import {MaterialTypeService} from "../../service/material-type.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
-import {Router} from "@angular/router";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-add',
@@ -48,9 +48,10 @@ export class AddComponent extends SubscribingComponent {
constructor(
private materialTypeService: MaterialTypeService,
- private router: Router
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
submit(values) {
diff --git a/src/main/frontend/src/app/modules/material-type/pages/edit/edit.component.ts b/src/main/frontend/src/app/modules/material-type/pages/edit/edit.component.ts
index 7f28964..89ad89e 100644
--- a/src/main/frontend/src/app/modules/material-type/pages/edit/edit.component.ts
+++ b/src/main/frontend/src/app/modules/material-type/pages/edit/edit.component.ts
@@ -4,7 +4,7 @@ import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {MaterialTypeService} from "../../service/material-type.service";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
-import {Validators} from "@angular/forms";
+import {FormBuilder, Validators} from "@angular/forms";
@Component({
selector: 'cre-edit',
@@ -44,10 +44,10 @@ export class EditComponent extends SubscribingComponent {
constructor(
private materialTypeService: MaterialTypeService,
- private router: Router,
- private activatedRoute: ActivatedRoute
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit() {
diff --git a/src/main/frontend/src/app/modules/material-type/pages/list/list.component.ts b/src/main/frontend/src/app/modules/material-type/pages/list/list.component.ts
index 0b16d66..a758493 100644
--- a/src/main/frontend/src/app/modules/material-type/pages/list/list.component.ts
+++ b/src/main/frontend/src/app/modules/material-type/pages/list/list.component.ts
@@ -2,6 +2,8 @@ import {Component} from '@angular/core';
import {MaterialTypeService} from "../../service/material-type.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {EmployeePermission} from "../../../shared/model/employee";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-list',
@@ -25,8 +27,10 @@ export class ListComponent extends SubscribingComponent {
]
constructor(
- private materialTypeService: MaterialTypeService
+ private materialTypeService: MaterialTypeService,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
}
diff --git a/src/main/frontend/src/app/modules/material/pages/add/add.component.ts b/src/main/frontend/src/app/modules/material/pages/add/add.component.ts
index 02dce14..b80da7b 100644
--- a/src/main/frontend/src/app/modules/material/pages/add/add.component.ts
+++ b/src/main/frontend/src/app/modules/material/pages/add/add.component.ts
@@ -1,9 +1,9 @@
import {Component} from '@angular/core';
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
-import {Validators} from "@angular/forms";
+import {FormBuilder, Validators} from "@angular/forms";
import {MaterialService} from "../../service/material.service";
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
-import {Router} from "@angular/router";
+import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {map} from "rxjs/operators";
@@ -63,9 +63,10 @@ export class AddComponent extends SubscribingComponent {
constructor(
private materialService: MaterialService,
private materialTypeService: MaterialTypeService,
- private router: Router
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
submit(values) {
diff --git a/src/main/frontend/src/app/modules/material/pages/edit/edit.component.ts b/src/main/frontend/src/app/modules/material/pages/edit/edit.component.ts
index a1165a3..f8d1369 100644
--- a/src/main/frontend/src/app/modules/material/pages/edit/edit.component.ts
+++ b/src/main/frontend/src/app/modules/material/pages/edit/edit.component.ts
@@ -1,6 +1,6 @@
import {Component, ViewChild} from '@angular/core';
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
-import {Validators} from "@angular/forms";
+import {FormBuilder, Validators} from "@angular/forms";
import {map} from "rxjs/operators";
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
import {MaterialService} from "../../service/material.service";
@@ -71,10 +71,10 @@ export class EditComponent extends SubscribingComponent {
constructor(
private materialService: MaterialService,
private materialTypeService: MaterialTypeService,
- private router: Router,
- private activatedRoute: ActivatedRoute,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit() {
@@ -114,8 +114,8 @@ export class EditComponent extends SubscribingComponent {
this.errorMessage = `Un produit avec le nom '${values.name}' existe déjà`
} else {
this.unknownError = true
+ console.error(err)
}
- console.log(err)
}
}
)
@@ -128,7 +128,7 @@ export class EditComponent extends SubscribingComponent {
next: () => this.router.navigate(['/catalog/material/list']),
error: err => {
this.unknownError = true
- console.log(err)
+ console.error(err)
}
}
)
diff --git a/src/main/frontend/src/app/modules/material/pages/list/list.component.ts b/src/main/frontend/src/app/modules/material/pages/list/list.component.ts
index 2d8b608..43c578a 100644
--- a/src/main/frontend/src/app/modules/material/pages/list/list.component.ts
+++ b/src/main/frontend/src/app/modules/material/pages/list/list.component.ts
@@ -3,6 +3,8 @@ import {SubscribingComponent} from "../../../shared/components/subscribing.compo
import {MaterialService} from "../../service/material.service";
import {EmployeePermission} from "../../../shared/model/employee";
import {environment} from "../../../../../environments/environment";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-list',
@@ -39,9 +41,11 @@ export class ListComponent extends SubscribingComponent {
private hasSimdutMap: any = {}
constructor(
- private materialService: MaterialService
+ private materialService: MaterialService,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit() {
diff --git a/src/main/frontend/src/app/modules/material/service/material.service.ts b/src/main/frontend/src/app/modules/material/service/material.service.ts
index 70276d9..fc167fb 100644
--- a/src/main/frontend/src/app/modules/material/service/material.service.ts
+++ b/src/main/frontend/src/app/modules/material/service/material.service.ts
@@ -17,6 +17,14 @@ export class MaterialService {
return this.api.get('/material')
}
+ getAllForMixCreation(recipeId: number): Observable {
+ return this.api.get(`/material/mix/create/${recipeId}`)
+ }
+
+ getAllForMixUpdate(mixId: number): Observable {
+ return this.api.get(`/material/mix/update/${mixId}`)
+ }
+
getById(id: number): Observable {
return this.api.get(`/material/${id}`)
}
diff --git a/src/main/frontend/src/app/modules/shared/components/entity-add/entity-add.component.ts b/src/main/frontend/src/app/modules/shared/components/entity-add/entity-add.component.ts
index 063ec29..e48bda7 100644
--- a/src/main/frontend/src/app/modules/shared/components/entity-add/entity-add.component.ts
+++ b/src/main/frontend/src/app/modules/shared/components/entity-add/entity-add.component.ts
@@ -2,6 +2,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
import {SubscribingComponent} from "../subscribing.component";
import {FormBuilder, FormControl, FormGroup, ValidatorFn} from "@angular/forms";
import {Observable} from "rxjs";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-entity-add',
@@ -19,9 +20,11 @@ export class EntityAddComponent extends SubscribingComponent {
form: FormGroup | null
constructor(
- private formBuilder: FormBuilder
+ private formBuilder: FormBuilder,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit() {
@@ -45,10 +48,6 @@ export class EntityAddComponent extends SubscribingComponent {
getControl(controlName: string): FormControl {
return this.form.controls[controlName] as FormControl
}
-
- test(any) {
- console.log(any)
- }
}
export class FormField {
@@ -61,6 +60,7 @@ export class FormField {
public errorMessages?: FormErrorMessage[],
public valueFn?: (any) => any,
public template?: any,
+ public readonly?: boolean,
// Specifics to some types
public step?: string,
public options$?: Observable<{ value: any, label: string }[]>,
diff --git a/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.html b/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.html
index fabafb1..60bfc00 100644
--- a/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.html
+++ b/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.html
@@ -2,7 +2,7 @@
{{title}}
-
+
Une erreur est survenue
{{customError}}
@@ -32,10 +32,12 @@
-
+
-
+
@@ -44,7 +46,7 @@
let-control="control" let-field="field">
{{field.label}}
-
+
@@ -59,7 +61,7 @@
let-control="control" let-field="field">
{{field.label}}
-
+
{{option.label}}
diff --git a/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.ts b/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.ts
index b13d8f5..8539297 100644
--- a/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.ts
+++ b/src/main/frontend/src/app/modules/shared/components/entity-edit/entity-edit.component.ts
@@ -4,6 +4,7 @@ import {SubscribingComponent} from "../subscribing.component";
import {FormField} from "../entity-add/entity-add.component";
import {EmployeePermission} from "../../model/employee";
import {AccountService} from "../../../accounts/services/account.service";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-entity-edit',
@@ -19,6 +20,7 @@ export class EntityEditComponent extends SubscribingComponent {
@Input() deletePermission: EmployeePermission
@Input() unknownError = false
@Input() customError: string | null
+ @Input() disableButtons: boolean
@Output() submit = new EventEmitter()
@Output() delete = new EventEmitter()
@@ -26,9 +28,11 @@ export class EntityEditComponent extends SubscribingComponent {
constructor(
private accountService: AccountService,
- private formBuilder: FormBuilder
+ private formBuilder: FormBuilder,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit() {
@@ -42,17 +46,21 @@ export class EntityEditComponent extends SubscribingComponent {
}
submitForm() {
- const values = {}
- this.formFields.forEach(f => {
- values[f.name] = this.getControl(f.name).value
- })
- this.submit.emit(values)
+ this.submit.emit(this.values)
}
getControl(controlName: string): FormControl {
return this.form.controls[controlName] as FormControl
}
+ get values(): any {
+ const values = {}
+ this.formFields.forEach(f => {
+ values[f.name] = this.getControl(f.name).value
+ })
+ return values
+ }
+
get canDelete(): boolean {
return this.accountService.hasPermission(this.deletePermission)
}
diff --git a/src/main/frontend/src/app/modules/shared/components/entity-list/entity-list.component.ts b/src/main/frontend/src/app/modules/shared/components/entity-list/entity-list.component.ts
index 3b5d102..93eb95a 100644
--- a/src/main/frontend/src/app/modules/shared/components/entity-list/entity-list.component.ts
+++ b/src/main/frontend/src/app/modules/shared/components/entity-list/entity-list.component.ts
@@ -3,6 +3,8 @@ import {Observable} from "rxjs";
import {SubscribingComponent} from "../subscribing.component";
import {AccountService} from "../../../accounts/services/account.service";
import {EmployeePermission} from "../../model/employee";
+import {FormBuilder} from "@angular/forms";
+import {ActivatedRoute, Router} from "@angular/router";
@Component({
selector: 'cre-entity-list',
@@ -18,9 +20,11 @@ export class EntityListComponent extends SubscribingComponent {
@Input() addPermission: EmployeePermission
constructor(
- private accountService: AccountService
+ private accountService: AccountService,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
hasPermissionToUseButton(button: TableButton): boolean {
diff --git a/src/main/frontend/src/app/modules/shared/components/header/header.component.ts b/src/main/frontend/src/app/modules/shared/components/header/header.component.ts
index cd40f17..65ab5d0 100644
--- a/src/main/frontend/src/app/modules/shared/components/header/header.component.ts
+++ b/src/main/frontend/src/app/modules/shared/components/header/header.component.ts
@@ -1,9 +1,10 @@
import {Component} from '@angular/core';
-import {ResolveEnd, Router} from "@angular/router";
+import {ActivatedRoute, ResolveEnd, Router} from "@angular/router";
import {AppState} from "../../app-state";
import {Employee, EmployeePermission} from "../../model/employee";
import {AccountService} from "../../../accounts/services/account.service";
import {SubscribingComponent} from "../subscribing.component";
+import {FormBuilder} from "@angular/forms";
@Component({
selector: 'cre-header',
@@ -12,9 +13,10 @@ import {SubscribingComponent} from "../subscribing.component";
})
export class HeaderComponent extends SubscribingComponent {
links: HeaderLink[] = [
+ {route: '/color', title: 'Couleurs', requiredPermission: EmployeePermission.VIEW_RECIPE},
{route: '/catalog', title: 'Catalogue', enabled: true},
- new HeaderLink('/employee', 'Employés', EmployeePermission.VIEW_EMPLOYEE),
- new HeaderLink('/group', 'Groupes', EmployeePermission.VIEW_EMPLOYEE_GROUP),
+ {route: '/employee', title: 'Employés', requiredPermission: EmployeePermission.VIEW_EMPLOYEE},
+ {route: '/group', title: 'Groupes', requiredPermission: EmployeePermission.VIEW_EMPLOYEE_GROUP},
{route: '/account/login', title: 'Connexion', enabled: true},
{route: '/account/logout', title: 'Déconnexion', enabled: false},
];
@@ -22,10 +24,11 @@ export class HeaderComponent extends SubscribingComponent {
constructor(
private accountService: AccountService,
- private router: Router,
- private appState: AppState
+ private appState: AppState,
+ router: Router,
+ activatedRoute: ActivatedRoute
) {
- super()
+ super(activatedRoute, router)
}
ngOnInit(): void {
@@ -89,7 +92,7 @@ class HeaderLink {
public route: string,
public title: string,
public requiredPermission?: EmployeePermission,
- public enabled = false
+ public enabled?
) {
}
}
diff --git a/src/main/frontend/src/app/modules/shared/components/subscribing.component.ts b/src/main/frontend/src/app/modules/shared/components/subscribing.component.ts
index 7cd70f3..7e9e805 100644
--- a/src/main/frontend/src/app/modules/shared/components/subscribing.component.ts
+++ b/src/main/frontend/src/app/modules/shared/components/subscribing.component.ts
@@ -1,14 +1,24 @@
import {take, takeUntil} from "rxjs/operators";
import {OnDestroy, OnInit} from "@angular/core";
import {Observable, Subject} from "rxjs";
+import {ActivatedRoute, Router} from "@angular/router";
+import {UrlUtils} from "../utils/url.utils";
export abstract class SubscribingComponent implements OnInit, OnDestroy {
protected subscribers$ = []
protected destroy$ = new Subject()
+ unknownError = false
+
+ protected constructor(
+ protected activatedRoute: ActivatedRoute,
+ protected router: Router,
+ protected urlUtils = new UrlUtils(activatedRoute, router)
+ ) {
+ }
subscribe(observable: Observable, observer, take_count = -1) {
if (!observer.error) {
- observer.error = err => console.log(err)
+ observer.error = err => this.handleObserverError(err)
}
if (take_count >= 0) {
@@ -19,6 +29,24 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy {
this.subscribers$.push(observable.subscribe(observer))
}
+ subscribeEntityByIdFromRoute(service: any, entitySubject: Subject, notFoundRoute: string, paramName: string = 'id') {
+ const id = this.urlUtils.parseIntUrlParam(paramName)
+ this.subscribe(
+ service.getById(id),
+ {
+ next: e => entitySubject.next(e),
+ error: err => {
+ if (err.status === 404) {
+ this.urlUtils.navigateTo(notFoundRoute)
+ } else {
+ this.handleObserverError(err)
+ }
+ }
+ },
+ 1
+ )
+ }
+
ngOnInit(): void {
}
@@ -26,4 +54,17 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy {
this.destroy$.next(true)
this.destroy$.complete()
}
+
+ handleObserverError(error: any) {
+ console.error(error)
+ this.unknownError = true
+ }
+
+ handleNotFoundError(error: any, route: string) {
+ if (error.status === 404) {
+ this.urlUtils.navigateTo(route)
+ } else {
+ this.handleObserverError(error)
+ }
+ }
}
diff --git a/src/main/frontend/src/app/modules/shared/model/recipe.model.ts b/src/main/frontend/src/app/modules/shared/model/recipe.model.ts
new file mode 100644
index 0000000..6a8bada
--- /dev/null
+++ b/src/main/frontend/src/app/modules/shared/model/recipe.model.ts
@@ -0,0 +1,55 @@
+import {Material} from "./material.model";
+import {LocalDate} from "js-joda";
+import {Company} from "./company.model";
+
+export class Recipe {
+ constructor(
+ public id: number,
+ public name: string,
+ public description: string,
+ public sample: number,
+ public approbationDate: LocalDate,
+ public remark: string,
+ public note: string,
+ public company: Company,
+ public mixes: Mix[],
+ public steps: RecipeStep[]
+ ) {
+ }
+}
+
+export class Mix {
+ constructor(
+ public id: number,
+ public mixType: MixType,
+ public mixMaterials: MixMaterial[],
+ public location: string,
+ ) {
+ }
+}
+
+export class MixMaterial {
+ constructor(
+ public id: number,
+ public material: Material,
+ public quantity: number
+ ) {
+ }
+}
+
+class MixType {
+ constructor(
+ public id: number,
+ public name: string,
+ public material: Material
+ ) {
+ }
+}
+
+export class RecipeStep {
+ constructor(
+ public id: number,
+ public message: string
+ ) {
+ }
+}
diff --git a/src/main/frontend/src/app/modules/shared/units.ts b/src/main/frontend/src/app/modules/shared/units.ts
new file mode 100644
index 0000000..8ee70e5
--- /dev/null
+++ b/src/main/frontend/src/app/modules/shared/units.ts
@@ -0,0 +1,32 @@
+export const UNIT_MILLILITER = 'mL'
+export const UNIT_LITER = 'L'
+export const UNIT_GALLON = 'gal'
+
+export const LITER_TO_MILLILITER = 1000
+export const GALLON_TO_MILLILITER = 3785.411784
+
+export const UNIT_RATIOS = {
+ mL: {
+ mL: 1,
+ L: 1 / LITER_TO_MILLILITER,
+ gal: 1 / GALLON_TO_MILLILITER
+ },
+ L: {
+ mL: LITER_TO_MILLILITER,
+ L: 1,
+ gal: LITER_TO_MILLILITER / GALLON_TO_MILLILITER
+ },
+ gal: {
+ mL: GALLON_TO_MILLILITER,
+ L: 1 / (LITER_TO_MILLILITER / GALLON_TO_MILLILITER),
+ gal: 1
+ }
+}
+
+export function convertMixMaterialQuantity(computedQuantity: { percents: boolean, quantity: number }, from: string, to: string): number {
+ return !computedQuantity.percents ? convertQuantity(computedQuantity.quantity, from, to) : computedQuantity.quantity
+}
+
+export function convertQuantity(quantity: number, from: string, to: string): number {
+ return quantity * UNIT_RATIOS[from][to]
+}
diff --git a/src/main/frontend/src/app/modules/shared/utils/url.utils.ts b/src/main/frontend/src/app/modules/shared/utils/url.utils.ts
new file mode 100644
index 0000000..35ff4f1
--- /dev/null
+++ b/src/main/frontend/src/app/modules/shared/utils/url.utils.ts
@@ -0,0 +1,21 @@
+import {ActivatedRoute, Router} from "@angular/router";
+
+export class UrlUtils {
+ constructor(
+ private activatedRoute: ActivatedRoute,
+ private router: Router
+ ) {
+ }
+
+ parseUrlParam(param: string): string {
+ return this.activatedRoute.snapshot.paramMap.get(param)
+ }
+
+ parseIntUrlParam(param: string): number {
+ return parseInt(this.parseUrlParam(param))
+ }
+
+ navigateTo(url: string) {
+ this.router.navigate([url])
+ }
+}
diff --git a/src/main/frontend/src/custom-theme.scss b/src/main/frontend/src/custom-theme.scss
index a44e030..c6a6653 100644
--- a/src/main/frontend/src/custom-theme.scss
+++ b/src/main/frontend/src/custom-theme.scss
@@ -92,6 +92,7 @@ $color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-a
$color-primary: map-get($theme-primary, 500);
$color-accent: map-get($theme-accent, 500);
+$color-warn: map-get($theme-warn, 500);
html, body {
diff --git a/src/main/frontend/src/styles.sass b/src/main/frontend/src/styles.sass
index bb3e444..cee6919 100644
--- a/src/main/frontend/src/styles.sass
+++ b/src/main/frontend/src/styles.sass
@@ -1,8 +1,10 @@
@import "custom-theme"
+@import "~material-design-icons/iconfont/material-icons.css"
mat-card
padding: 0 !important
width: max-content
+ max-width: 50rem
&.x-centered
margin: auto
@@ -22,6 +24,9 @@ mat-card
margin-top: 16px
padding: 0 16px
+ &.no-action
+ padding: 0 24px 16px 24px !important
+
mat-form-field
width: 100%
@@ -38,6 +43,7 @@ mat-card
table
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12)
+ max-width: 90vw
th
background-color: $color-primary
@@ -72,12 +78,62 @@ table
.disabled *
display: none
+mat-expansion-panel.table-title
+ mat-expansion-panel-header
+ background-color: $color-primary
+
+ &:hover, &:focus
+ background-color: $color-primary !important
+
+ mat-panel-title
+ color: $light-primary-text !important
+ font-weight: bold
+ text-transform: uppercase
+
+ mat-panel-description
+ color: $light-primary-text
+
+ mat-form-field
+ display: inline
+
+ &.mat-expanded mat-expansion-panel-header
+ border-bottom-left-radius: 0
+ border-bottom-right-radius: 0
+
+ .mat-expansion-panel-body
+ padding: 0 !important
+
+ table
+ width: 100%
+
+ th
+ border-top-left-radius: 0
+ border-top-right-radius: 0
+
+cre-mix-table.no-top-margin mat-expansion-panel
+ margin-top: 0 !important
+
button
text-transform: uppercase
font-weight: 500
&.mat-accent
- color: white !important
+ color: $light-primary-text !important
+
+mat-form-field
+ &.w-auto
+ .mat-form-field-infix
+ width: auto !important
+
+ &.dark
+ input
+ caret-color: $light-primary-text !important
+
+ mat-label, input
+ color: $light-primary-text
+
+ .mat-form-field-underline, .mat-form-field-ripple
+ background-color: $light-primary-text !important
div.empty
color: $dark-secondary-text
@@ -92,6 +148,13 @@ div.empty
button
margin-left: 1rem
+ &.backward
+ justify-content: flex-start
+
+ button
+ margin-left: 0
+ margin-right: 1rem
+
.alert p
margin-bottom: 0
@@ -112,3 +175,6 @@ div.empty
left: 0
background-color: black
opacity: 0.4
+
+.color-warning
+ color: #fdd835
diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt
index e44627c..1c75639 100644
--- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt
+++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt
@@ -60,7 +60,7 @@ open class MixUpdateDto(
@field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE)
val name: String?,
- val materialType: MaterialType?,
+ val materialTypeId: Long?,
val mixMaterials: Map?
) : EntityDto {
@@ -88,7 +88,7 @@ fun mixSaveDto(
fun mixUpdateDto(
id: Long = 0L,
name: String? = "name",
- materialType: MaterialType? = materialType(),
+ materialTypeId: Long? = 0L,
mixMaterials: Map? = mapOf(),
op: MixUpdateDto.() -> Unit = {}
-) = MixUpdateDto(id, name, materialType, mixMaterials).apply(op)
+) = MixUpdateDto(id, name, materialTypeId, mixMaterials).apply(op)
diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/MixService.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/MixService.kt
index 957139c..2ba3fa7 100644
--- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/MixService.kt
+++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/MixService.kt
@@ -55,17 +55,17 @@ class MixServiceImpl(
override fun update(entity: MixUpdateDto): Mix {
val mix = getById(entity.id)
- if (entity.name != null || entity.materialType != null) {
+ if (entity.name != null || entity.materialTypeId != null) {
mix.mixType = if (mixTypeIsShared(mix.mixType)) {
mixTypeService.createForNameAndMaterialType(
entity.name ?: mix.mixType.name,
- entity.materialType ?: mix.mixType.material.materialType!!
+ if (entity.materialTypeId != null) materialTypeService.getById(entity.materialTypeId) else mix.mixType.material.materialType!!
)
} else {
mixTypeService.updateForNameAndMaterialType(
mix.mixType,
entity.name ?: mix.mixType.name,
- entity.materialType ?: mix.mixType.material.materialType!!
+ if (entity.materialTypeId != null) materialTypeService.getById(entity.materialTypeId) else mix.mixType.material.materialType!!
)
}
}
diff --git a/src/main/resources/thymeleaf/templates/recipe/explore.html b/src/main/resources/thymeleaf/templates/recipe/explore.html
index f926da9..4657d24 100644
--- a/src/main/resources/thymeleaf/templates/recipe/explore.html
+++ b/src/main/resources/thymeleaf/templates/recipe/explore.html
@@ -272,7 +272,7 @@
let i = 0;
$(".mixContainer").each(function () {
- formData[i++] = getMixQuantities($(this));
+ formData[i++] = getMixMaterials($(this));
});
clearNotEnoughClasses();
@@ -284,7 +284,7 @@
$(".useMixSubmit").on({
click: function () {
showConfirm(formatMessage("[[#{inventory.askUseMix}]]"), false, () => {
- let formData = [getMixQuantities($(this).parents(".mixContainer"))];
+ let formData = [getMixMaterials($(this).parents(".mixContainer"))];
clearNotEnoughClasses();
sendPost(formData, "/inventory/use", r => displayNotEnoughReason(r));
@@ -384,7 +384,7 @@
});
}
- function getMixQuantities(mixContainer) {
+ function getMixMaterials(mixContainer) {
mix = $(mixContainer).find(".mix");
const mixId = mix.data("mixid");