diff --git a/src/app/modules/colors/components/recipe-info/recipe-info.component.html b/src/app/modules/colors/components/recipe-info/recipe-info.component.html index 1780101..6fd3a80 100644 --- a/src/app/modules/colors/components/recipe-info/recipe-info.component.html +++ b/src/app/modules/colors/components/recipe-info/recipe-info.component.html @@ -1,6 +1,12 @@
-
+

{{recipe.company.name}} - {{recipe.name}}

+
+
{{recipe.gloss}}%
+
diff --git a/src/app/modules/colors/components/recipe-info/recipe-info.component.sass b/src/app/modules/colors/components/recipe-info/recipe-info.component.sass index 00f91b8..b0ca498 100644 --- a/src/app/modules/colors/components/recipe-info/recipe-info.component.sass +++ b/src/app/modules/colors/components/recipe-info/recipe-info.component.sass @@ -3,7 +3,7 @@ color: white padding: 1rem - div + div:not(.recipe-color-circle) margin-right: 3rem p diff --git a/src/app/modules/colors/components/recipe-info/recipe-info.component.ts b/src/app/modules/colors/components/recipe-info/recipe-info.component.ts index 8aeea3f..f87ef6a 100644 --- a/src/app/modules/colors/components/recipe-info/recipe-info.component.ts +++ b/src/app/modules/colors/components/recipe-info/recipe-info.component.ts @@ -1,5 +1,5 @@ -import {AfterViewInit, Component, Input} from '@angular/core'; -import {Recipe} from "../../../shared/model/recipe.model"; +import {AfterViewInit, Component, Input} from '@angular/core' +import {Recipe} from '../../../shared/model/recipe.model' @Component({ selector: 'cre-recipe-info', @@ -13,6 +13,19 @@ export class RecipeInfoComponent implements AfterViewInit { isBPacExtensionInstalled = false ngAfterViewInit(): void { - this.isBPacExtensionInstalled = document.querySelectorAll(".bpac-extension-installed").length > 0 + + this.isBPacExtensionInstalled = document.querySelectorAll('.bpac-extension-installed').length > 0 + } + + get darkColor(): boolean { + // https://stackoverflow.com/questions/12043187/how-to-check-if-hex-color-is-too-black + const c = this.recipe.color.substring(1) // strip # + const rgb = parseInt(c, 16) // convert rrggbb to decimal + const r = (rgb >> 16) & 0xff // extract red + const g = (rgb >> 8) & 0xff // extract green + const b = (rgb >> 0) & 0xff // extract blue + + const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b // per ITU-R BT.709 + return luma < 100 } } diff --git a/src/app/modules/colors/pages/add/add.component.ts b/src/app/modules/colors/pages/add/add.component.ts index c8569c0..8fbe040 100644 --- a/src/app/modules/colors/pages/add/add.component.ts +++ b/src/app/modules/colors/pages/add/add.component.ts @@ -35,6 +35,29 @@ export class AddComponent extends ErrorHandlingComponent { {conditionFn: errors => errors.required, message: 'Une description est requise'} ] }, + { + name: 'color', + label: 'Couleur', + icon: 'palette', + type: 'color', + defaultValue: "#ffffff", + validator: Validators.required, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Une couleur est requise'} + ] + }, + { + name: 'gloss', + label: 'Lustre', + type: 'slider', + min: 0, + max: 100, + defaultValue: 10, + validator: Validators.required, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Le lustre de la couleur est requis'} + ] + }, { name: 'sample', label: 'Échantillon', @@ -85,7 +108,7 @@ export class AddComponent extends ErrorHandlingComponent { submit(values) { this.subscribe( - this.recipeService.save(values.name, values.description, values.sample, values.approbationDate, values.remark, values.company), + this.recipeService.save(values.name, values.description, values.color, values.gloss, values.sample, values.approbationDate, values.remark, values.company), recipe => this.urlUtils.navigateTo(`/color/edit/${recipe.id}`) ) } diff --git a/src/app/modules/colors/pages/edit/edit.component.html b/src/app/modules/colors/pages/edit/edit.component.html index 0dba9d5..3d3705e 100644 --- a/src/app/modules/colors/pages/edit/edit.component.html +++ b/src/app/modules/colors/pages/edit/edit.component.html @@ -3,7 +3,7 @@
- +
diff --git a/src/app/modules/colors/pages/edit/edit.component.ts b/src/app/modules/colors/pages/edit/edit.component.ts index 2cc6289..31332cc 100644 --- a/src/app/modules/colors/pages/edit/edit.component.ts +++ b/src/app/modules/colors/pages/edit/edit.component.ts @@ -45,6 +45,27 @@ export class EditComponent extends ErrorHandlingComponent { {conditionFn: errors => errors.required, message: 'Une description est requise'} ] }, + { + name: 'color', + label: 'Couleur', + icon: 'palette', + type: 'color', + validator: Validators.required, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Une couleur est requise'} + ] + }, + { + name: 'gloss', + label: 'Lustre', + type: 'slider', + min: 0, + max: 100, + validator: Validators.compose([Validators.required, Validators.min(0), Validators.max(100)]), + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Le lustre de la couleur est requis'} + ] + }, { name: 'sample', label: 'Échantillon', @@ -123,7 +144,7 @@ export class EditComponent extends ErrorHandlingComponent { const values = editComponent.values this.submittedValues = values this.subscribeAndNavigate( - this.recipeService.update(this.recipe.id, values.name, values.description, values.sample, values.approbationDate, values.remark, this.recipe.steps), + this.recipeService.update(this.recipe.id, values.name, values.description, values.color, values.gloss, values.sample, values.approbationDate, values.remark, this.recipe.steps), '/color/list' ) } diff --git a/src/app/modules/colors/pages/list/list.component.html b/src/app/modules/colors/pages/list/list.component.html index 3b24baf..72e59aa 100644 --- a/src/app/modules/colors/pages/list/list.component.html +++ b/src/app/modules/colors/pages/list/list.component.html @@ -37,6 +37,18 @@ {{recipe.description}} + + Couleur + +
+ +
+ + + Lustre + {{recipe.gloss}}% + + Échantillon diff --git a/src/app/modules/colors/pages/list/list.component.sass b/src/app/modules/colors/pages/list/list.component.sass index 61985de..e5fb34c 100644 --- a/src/app/modules/colors/pages/list/list.component.sass +++ b/src/app/modules/colors/pages/list/list.component.sass @@ -4,3 +4,6 @@ mat-expansion-panel .button-add margin-top: .8rem + +.recipe-color-circle + box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12) diff --git a/src/app/modules/colors/pages/list/list.component.ts b/src/app/modules/colors/pages/list/list.component.ts index ca2c87d..0476275 100644 --- a/src/app/modules/colors/pages/list/list.component.ts +++ b/src/app/modules/colors/pages/list/list.component.ts @@ -14,7 +14,7 @@ import {ErrorModel, ErrorService} from '../../../shared/service/error.service' }) export class ListComponent extends ErrorHandlingComponent { recipes$ = this.recipeService.allSortedByCompany - tableCols = ['name', 'description', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit'] + tableCols = ['name', 'description', 'color', 'gloss', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit'] searchQuery = "" panelForcedExpanded = false recipesHidden = [] @@ -50,6 +50,18 @@ export class ListComponent extends ErrorHandlingComponent { return (this.searchQuery && this.searchQuery.length > 0) && companyRecipes.map(r => this.recipesHidden[r.id]).filter(r => !r).length <= 0 } + isLightColor(recipe: Recipe): boolean { + // https://stackoverflow.com/questions/12043187/how-to-check-if-hex-color-is-too-black + const c = recipe.color.substring(1) // strip # + const rgb = parseInt(c, 16) // convert rrggbb to decimal + const r = (rgb >> 16) & 0xff // extract red + const g = (rgb >> 8) & 0xff // extract green + const b = (rgb >> 0) & 0xff // extract blue + + const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b // per ITU-R BT.709 + return luma > 200 + } + get hasEditPermission(): boolean { return this.accountService.hasPermission(EmployeePermission.EDIT_RECIPE) } diff --git a/src/app/modules/colors/services/recipe.service.ts b/src/app/modules/colors/services/recipe.service.ts index e8ce843..17e74b7 100644 --- a/src/app/modules/colors/services/recipe.service.ts +++ b/src/app/modules/colors/services/recipe.service.ts @@ -1,8 +1,8 @@ -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"; +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' @@ -34,8 +34,8 @@ export class RecipeService { 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} + save(name: string, description: string, color: string, gloss: number, sample: number, approbationDate: string, remark: string, companyId: number): Observable { + const body = {name, description, color, gloss, sample, remark, companyId} if (approbationDate) { // @ts-ignore body.approbationDate = approbationDate @@ -43,8 +43,8 @@ export class RecipeService { 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} + update(id: number, name: string, description: string, color: string, gloss: number, sample: number, approbationDate: string, remark: string, steps: RecipeStep[] = null) { + const body = {id, name, description, color, gloss, sample, remark, steps} if (approbationDate) { // @ts-ignore body.approbationDate = approbationDate @@ -85,7 +85,9 @@ export class RecipeService { 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) + if (m.material.materialType.usePercentages) { + quantity = body.quantities[mix.id][firstMaterial] * (quantity / 100) + } body.quantities[mix.id][m.material.id] = quantity } }) diff --git a/src/app/modules/shared/components/entity-add/entity-add.component.html b/src/app/modules/shared/components/entity-add/entity-add.component.html index 9e0876e..c153826 100644 --- a/src/app/modules/shared/components/entity-add/entity-add.component.html +++ b/src/app/modules/shared/components/entity-add/entity-add.component.html @@ -6,7 +6,7 @@
@@ -25,6 +25,11 @@ [ngTemplateOutlet]="fileTemplate" [ngTemplateOutletContext]="{control: getControl(field.name), field: field}"> + +
@@ -79,3 +84,13 @@
+ + + + + diff --git a/src/app/modules/shared/components/entity-add/entity-add.component.ts b/src/app/modules/shared/components/entity-add/entity-add.component.ts index 586a6bf..3e11984 100644 --- a/src/app/modules/shared/components/entity-add/entity-add.component.ts +++ b/src/app/modules/shared/components/entity-add/entity-add.component.ts @@ -23,7 +23,7 @@ export class EntityAddComponent { ngOnInit() { const formGroup = {} this.formFields.forEach(f => { - formGroup[f.name] = new FormControl(null, f.validator) + formGroup[f.name] = new FormControl(f.defaultValue, f.validator) }) this.form = this.formBuilder.group(formGroup) } @@ -52,10 +52,13 @@ export class FormField { public valueFn?: (any) => any, public template?: any, public readonly?: boolean, + public defaultValue?: any, // Specifics to some types public step?: string, public options$?: Observable<{ value: any, label: string }[]>, - public fileType?: string + public fileType?: string, + public min?: number, + public max?: number ) { } } diff --git a/src/app/modules/shared/components/entity-edit/entity-edit.component.html b/src/app/modules/shared/components/entity-edit/entity-edit.component.html index a02ea93..ce2ef3b 100644 --- a/src/app/modules/shared/components/entity-edit/entity-edit.component.html +++ b/src/app/modules/shared/components/entity-edit/entity-edit.component.html @@ -6,7 +6,7 @@
@@ -24,6 +24,11 @@ [ngTemplateOutlet]="field.template" [ngTemplateOutletContext]="{control: getControl(field.name), field: field}"> + +
@@ -74,5 +79,14 @@ + + + + diff --git a/src/app/modules/shared/components/slider-field/slider-field.component.html b/src/app/modules/shared/components/slider-field/slider-field.component.html new file mode 100644 index 0000000..1873422 --- /dev/null +++ b/src/app/modules/shared/components/slider-field/slider-field.component.html @@ -0,0 +1,13 @@ +
+

{{label}}

+ + +
diff --git a/src/app/modules/shared/components/slider-field/slider-field.component.sass b/src/app/modules/shared/components/slider-field/slider-field.component.sass new file mode 100644 index 0000000..2fd4b10 --- /dev/null +++ b/src/app/modules/shared/components/slider-field/slider-field.component.sass @@ -0,0 +1,4 @@ +.slider-label + color: rgba(0, 0, 0, 0.54) + font-size: 10px + margin-bottom: -14px diff --git a/src/app/modules/shared/components/slider-field/slider-field.component.ts b/src/app/modules/shared/components/slider-field/slider-field.component.ts new file mode 100644 index 0000000..c227b7e --- /dev/null +++ b/src/app/modules/shared/components/slider-field/slider-field.component.ts @@ -0,0 +1,37 @@ +import {Component, Input, OnInit} from '@angular/core' +import {FormControl} from '@angular/forms' +import {FormField} from '../entity-add/entity-add.component' + +@Component({ + selector: 'cre-slider-field', + templateUrl: './slider-field.component.html', + styleUrls: ['./slider-field.component.sass'] +}) +export class SliderFieldComponent implements OnInit { + @Input() control: FormControl + @Input() field: FormField | null + @Input() label: string + @Input() min: number + @Input() max: number + @Input() step = 1 + @Input() percents = false + @Input() thumbLabel = true + + ngOnInit(): void { + if (this.field) { + if (!this.label) { + this.label = this.field.label + } + if (!this.min) { + this.min = this.field.min + } + if (!this.max) { + this.max = this.field.max + } + } + } + + formatValueForDisplay(value: number): string { + return this.percents ? `${value}%` : value.toString() + } +} diff --git a/src/app/modules/shared/model/recipe.model.ts b/src/app/modules/shared/model/recipe.model.ts index 6a8bada..16dc864 100644 --- a/src/app/modules/shared/model/recipe.model.ts +++ b/src/app/modules/shared/model/recipe.model.ts @@ -7,6 +7,8 @@ export class Recipe { public id: number, public name: string, public description: string, + public color: string, + public gloss: number, public sample: number, public approbationDate: LocalDate, public remark: string, diff --git a/src/app/modules/shared/service/api.service.ts b/src/app/modules/shared/service/api.service.ts index de167a2..4d083d7 100644 --- a/src/app/modules/shared/service/api.service.ts +++ b/src/app/modules/shared/service/api.service.ts @@ -5,7 +5,7 @@ import {environment} from '../../../../environments/environment' import {AppState} from '../app-state' import {Router} from '@angular/router' import {map, share, takeUntil} from 'rxjs/operators' -import {valueOr} from '../utils/optional.utils' +import {valueOr} from '../utils/utils' import {ErrorService} from './error.service' @Injectable({ diff --git a/src/app/modules/shared/shared.module.ts b/src/app/modules/shared/shared.module.ts index 8ea2b4a..3d8407a 100644 --- a/src/app/modules/shared/shared.module.ts +++ b/src/app/modules/shared/shared.module.ts @@ -1,64 +1,67 @@ -import {NgModule} from '@angular/core'; -import {HeaderComponent} from './components/header/header.component'; -import {MatTabsModule} from "@angular/material/tabs"; -import {MatCardModule} from "@angular/material/card"; -import {MatButtonModule} from "@angular/material/button"; -import {MatFormFieldModule} from "@angular/material/form-field"; -import {MatInputModule} from "@angular/material/input"; -import {MatIconModule} from "@angular/material/icon"; -import {ReactiveFormsModule} from "@angular/forms"; -import {EmployeeInfoComponent} from './components/employee-info/employee-info.component'; -import {LabeledIconComponent} from './components/labeled-icon/labeled-icon.component'; -import {MatTableModule} from "@angular/material/table"; -import {CommonModule} from "@angular/common"; -import {HttpClientModule} from "@angular/common/http"; -import {MatCheckboxModule} from "@angular/material/checkbox"; -import {MatListModule} from "@angular/material/list"; -import {ConfirmBoxComponent} from './components/confirm-box/confirm-box.component'; -import {PermissionsListComponent} from './components/permissions-list/permissions-list.component'; -import {MatChipsModule} from "@angular/material/chips"; -import {PermissionsFieldComponent} from "./components/permissions-field/permissions-field.component"; -import {NavComponent} from './components/nav/nav.component'; -import {EntityListComponent} from './components/entity-list/entity-list.component'; -import {RouterModule} from "@angular/router"; -import {EntityAddComponent} from './components/entity-add/entity-add.component'; -import {EntityEditComponent} from './components/entity-edit/entity-edit.component'; -import {MatSelectModule} from "@angular/material/select"; -import {MatOptionModule} from "@angular/material/core"; -import {MaterialFileInputModule} from "ngx-material-file-input"; -import { FileButtonComponent } from './file-button/file-button.component'; -import { GlobalAlertHandlerComponent } from './components/global-alert-handler/global-alert-handler.component'; +import {NgModule} from '@angular/core' +import {HeaderComponent} from './components/header/header.component' +import {MatTabsModule} from '@angular/material/tabs' +import {MatCardModule} from '@angular/material/card' +import {MatButtonModule} from '@angular/material/button' +import {MatFormFieldModule} from '@angular/material/form-field' +import {MatInputModule} from '@angular/material/input' +import {MatIconModule} from '@angular/material/icon' +import {FormsModule, ReactiveFormsModule} from '@angular/forms' +import {EmployeeInfoComponent} from './components/employee-info/employee-info.component' +import {LabeledIconComponent} from './components/labeled-icon/labeled-icon.component' +import {MatTableModule} from '@angular/material/table' +import {CommonModule} from '@angular/common' +import {HttpClientModule} from '@angular/common/http' +import {MatCheckboxModule} from '@angular/material/checkbox' +import {MatListModule} from '@angular/material/list' +import {ConfirmBoxComponent} from './components/confirm-box/confirm-box.component' +import {PermissionsListComponent} from './components/permissions-list/permissions-list.component' +import {MatChipsModule} from '@angular/material/chips' +import {PermissionsFieldComponent} from './components/permissions-field/permissions-field.component' +import {NavComponent} from './components/nav/nav.component' +import {EntityListComponent} from './components/entity-list/entity-list.component' +import {RouterModule} from '@angular/router' +import {EntityAddComponent} from './components/entity-add/entity-add.component' +import {EntityEditComponent} from './components/entity-edit/entity-edit.component' +import {MatSelectModule} from '@angular/material/select' +import {MatOptionModule} from '@angular/material/core' +import {MaterialFileInputModule} from 'ngx-material-file-input' +import {FileButtonComponent} from './file-button/file-button.component' +import {GlobalAlertHandlerComponent} from './components/global-alert-handler/global-alert-handler.component' +import {MatSliderModule} from '@angular/material/slider'; +import { SliderFieldComponent } from './components/slider-field/slider-field.component' @NgModule({ - declarations: [HeaderComponent, EmployeeInfoComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent], - exports: [ - CommonModule, - HttpClientModule, - HeaderComponent, - MatCardModule, - MatButtonModule, - MatFormFieldModule, - MatInputModule, - MatIconModule, - MatTableModule, - MatCheckboxModule, - MatListModule, - MatSelectModule, - MatOptionModule, - MaterialFileInputModule, - ReactiveFormsModule, - LabeledIconComponent, - ConfirmBoxComponent, - PermissionsListComponent, - PermissionsFieldComponent, - NavComponent, - EntityListComponent, - EntityAddComponent, - EntityEditComponent, - FileButtonComponent, - GlobalAlertHandlerComponent - ], + declarations: [HeaderComponent, EmployeeInfoComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent], + exports: [ + CommonModule, + HttpClientModule, + HeaderComponent, + MatCardModule, + MatButtonModule, + MatFormFieldModule, + MatInputModule, + MatIconModule, + MatTableModule, + MatCheckboxModule, + MatListModule, + MatSelectModule, + MatOptionModule, + MatSliderModule, + MaterialFileInputModule, + ReactiveFormsModule, + LabeledIconComponent, + ConfirmBoxComponent, + PermissionsListComponent, + PermissionsFieldComponent, + NavComponent, + EntityListComponent, + EntityAddComponent, + EntityEditComponent, + FileButtonComponent, + GlobalAlertHandlerComponent + ], imports: [ MatTabsModule, MatIconModule, @@ -72,9 +75,11 @@ import { GlobalAlertHandlerComponent } from './components/global-alert-handler/g MatInputModule, MatSelectModule, MatOptionModule, + MatSliderModule, ReactiveFormsModule, RouterModule, - CommonModule + CommonModule, + FormsModule ] }) export class SharedModule { diff --git a/src/app/modules/shared/utils/optional.utils.ts b/src/app/modules/shared/utils/utils.ts similarity index 100% rename from src/app/modules/shared/utils/optional.utils.ts rename to src/app/modules/shared/utils/utils.ts diff --git a/src/styles.sass b/src/styles.sass index dbb5ac2..51e8189 100644 --- a/src/styles.sass +++ b/src/styles.sass @@ -135,6 +135,9 @@ mat-form-field .mat-form-field-underline, .mat-form-field-ripple background-color: $light-primary-text !important +mat-slider + width: 100% + div.empty color: $dark-secondary-text margin: auto @@ -155,6 +158,33 @@ div.empty margin-left: 0 margin-right: 1rem +.recipe-color-circle + color: black + width: 2.2rem + height: 2.2rem + border-radius: 1.1rem + margin-left: 1rem + font-size: 13px + + &.dark-mode + color: white + width: 2.3rem + height: 2.3rem + border: solid 1px white + + //&.light-mode + // color: black + // width: 2.3rem + // height: 2.3rem + // border: solid 1px black + + div + position: absolute + width: 2rem + text-align: center + margin-top: 7px + margin-left: 1px + .alert p margin-bottom: 0