diff --git a/src/app/app.component.html b/src/app/app.component.html index 14ce919..bcd2663 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,5 +1,6 @@ +
diff --git a/src/app/modules/accounts/services/account.service.ts b/src/app/modules/accounts/services/account.service.ts index 6d02458..962df6d 100644 --- a/src/app/modules/accounts/services/account.service.ts +++ b/src/app/modules/accounts/services/account.service.ts @@ -7,6 +7,7 @@ import {environment} from '../../../../environments/environment' import {ApiService} from '../../shared/service/api.service' import {Employee, EmployeePermission} from '../../shared/model/employee' import {ErrorService} from '../../shared/service/error.service' +import {globalLoadingWheel} from '../../shared/components/loading-wheel/loading-wheel.component' @Injectable({ providedIn: 'root' @@ -47,6 +48,7 @@ export class AccountService implements OnDestroy { login(id: number, password: string, success: () => void) { const loginForm = {id, password} + globalLoadingWheel.show() this.http.post(`${environment.apiUrl}/login`, loginForm, { withCredentials: true, observe: 'response' as 'body' @@ -62,7 +64,10 @@ export class AccountService implements OnDestroy { this.setLoggedInEmployeeFromApi() success() }, - error: err => this.errorService.handleError(err) + error: err => { + this.errorService.handleError(err) + globalLoadingWheel.hide() + } }) } @@ -92,7 +97,11 @@ export class AccountService implements OnDestroy { takeUntil(this.destroy$) ) .subscribe({ - next: employee => this.appState.authenticatedEmployee = employee, + next: employee => { + this.appState.authenticatedEmployee = employee + // At this point the loading wheel should be visible + globalLoadingWheel.hide() + }, error: err => this.errorService.handleError(err) }) } diff --git a/src/app/modules/colors/components/images-editor/images-editor.component.ts b/src/app/modules/colors/components/images-editor/images-editor.component.ts index 42c4499..55814bd 100644 --- a/src/app/modules/colors/components/images-editor/images-editor.component.ts +++ b/src/app/modules/colors/components/images-editor/images-editor.component.ts @@ -6,6 +6,7 @@ import {Observable} from 'rxjs' import {RecipeImageService} from '../../services/recipe-image.service' import {environment} from '../../../../../environments/environment' import {ErrorService} from '../../../shared/service/error.service' +import {globalLoadingWheel} from '../../../shared/components/loading-wheel/loading-wheel.component' @Component({ selector: 'cre-images-editor', @@ -36,6 +37,7 @@ export class ImagesEditorComponent extends SubscribingComponent { this.subscribe( this.imageIds$, ids => this.hasImages = ids.length > 0, + false, 1 ) } diff --git a/src/app/modules/colors/components/mix-editor/mix-editor.component.ts b/src/app/modules/colors/components/mix-editor/mix-editor.component.ts index 6420169..cab63ae 100644 --- a/src/app/modules/colors/components/mix-editor/mix-editor.component.ts +++ b/src/app/modules/colors/components/mix-editor/mix-editor.component.ts @@ -1,6 +1,6 @@ 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 {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {MixService} from '../../services/mix.service' import {Observable} from 'rxjs' import {RecipeService} from '../../services/recipe.service' @@ -23,7 +23,7 @@ import {MatSelect} from '@angular/material/select' templateUrl: './mix-editor.component.html', styleUrls: ['./mix-editor.component.sass'] }) -export class MixEditorComponent extends SubscribingComponent { +export class MixEditorComponent extends ErrorHandlingComponent { @ViewChild('mixTable') mixTable: MatTable @ViewChild('deleteConfirmBox') deleteConfirmBox: ConfirmBoxComponent @@ -47,6 +47,16 @@ export class MixEditorComponent extends SubscribingComponent { hoveredMixMaterial: MixMaterial | null columns = ['position', 'material', 'quantity', 'units', 'buttonRemove'] + deleting = false + handledErrorModels = [{ + filter: error => error.status === 409 && !this.deleting, + messageProducer: error => `Un mélange avec le nom '${error.id}' existe déjà dans cette recette` + }, { + filter: error => error.error.status === 409 && this.deleting, + consumer: () => this.deleting = false, + messageProducer: () => 'Ce mélange est utilisé par un ou plusieurs autres mélanges' + }] + constructor( private mixService: MixService, private recipeService: RecipeService, @@ -116,6 +126,7 @@ export class MixEditorComponent extends SubscribingComponent { } delete() { + this.deleting = true this.subscribeAndNavigate(this.mixService.delete(this.mixId), `/color/edit/${this.recipeId}`) } diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.html b/src/app/modules/colors/components/mix-table/mix-table.component.html index ed4576b..a0e1034 100644 --- a/src/app/modules/colors/components/mix-table/mix-table.component.html +++ b/src/app/modules/colors/components/mix-table/mix-table.component.html @@ -6,7 +6,7 @@
- + Casier @@ -15,12 +15,17 @@
- +
- +
@@ -90,9 +95,29 @@ {{units}} + + + + + + + + + + - + + diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.sass b/src/app/modules/colors/components/mix-table/mix-table.component.sass index 0601391..927de1b 100644 --- a/src/app/modules/colors/components/mix-table/mix-table.component.sass +++ b/src/app/modules/colors/components/mix-table/mix-table.component.sass @@ -1,7 +1,7 @@ @import '../../../../../custom-theme' mat-expansion-panel - width: 40rem + width: 48rem margin: 2rem 0 .mix-actions @@ -14,6 +14,9 @@ mat-expansion-panel .low-quantity background-color: #ffb3b3 +.location-input + width: 10rem + ::ng-deep span.mix-calculated-quantity &:first-child color: green diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.ts b/src/app/modules/colors/components/mix-table/mix-table.component.ts index d4f7da4..70baa00 100644 --- a/src/app/modules/colors/components/mix-table/mix-table.component.ts +++ b/src/app/modules/colors/components/mix-table/mix-table.component.ts @@ -8,6 +8,8 @@ import {PtouchPrinter} from '../../ptouchPrint' import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confirm-box.component' import {ErrorService} from '../../../shared/service/error.service' import {AlertService} from '../../../shared/service/alert.service' +import {environment} from '../../../../../environments/environment' +import {MaterialService} from '../../../material/service/material.service' @Component({ selector: 'cre-mix-table', @@ -15,7 +17,7 @@ import {AlertService} from '../../../shared/service/alert.service' styleUrls: ['./mix-table.component.sass'] }) export class MixTableComponent extends SubscribingComponent { - private readonly COLUMNS = ['material', 'materialType', 'quantity', 'quantityCalculated', 'quantityUnits'] + private readonly COLUMNS = ['material', 'materialType', 'quantity', 'quantityCalculated', 'quantityUnits', 'simdut'] private readonly COLUMNS_STATIC = ['material', 'materialType', 'quantityStatic', 'quantityUnits'] @ViewChild('printingConfirmBox') printingConfirmBox: ConfirmBoxComponent @@ -33,11 +35,14 @@ export class MixTableComponent extends SubscribingComponent { mixColumns = this.COLUMNS units = UNIT_MILLILITER computedQuantities: { id: number, percents: boolean, quantity: number }[] = [] + hoveredMixMaterial: MixMaterial | null + hasSimdutMap: any = {} // BPac printer printer: PtouchPrinter | null constructor( + private materialService: MaterialService, private alertService: AlertService, errorService: ErrorService, router: Router, @@ -63,6 +68,12 @@ export class MixTableComponent extends SubscribingComponent { this.units$, u => this.convertQuantities(u) ) + + this.mix.mixMaterials.map(mm => mm.material).forEach(material => this.subscribe( + this.materialService.hasSimdut(material.id), + b => this.hasSimdutMap[material.id] = b + ) + ) } changeLocation(event: any) { @@ -115,6 +126,10 @@ export class MixTableComponent extends SubscribingComponent { return Math.round(quantity * 1000) / 1000 } + openSimdutFile(mixMaterial: MixMaterial) { + window.open(`${environment.apiUrl}/material/${mixMaterial.material.id}/simdut`, '_blank') + } + async print() { const base = this.mix.mixMaterials .map(ma => ma.material) diff --git a/src/app/modules/colors/components/step-table/step-table.component.html b/src/app/modules/colors/components/step-table/step-table.component.html index 9eb22d4..3922552 100644 --- a/src/app/modules/colors/components/step-table/step-table.component.html +++ b/src/app/modules/colors/components/step-table/step-table.component.html @@ -23,11 +23,13 @@ - + + + - + diff --git a/src/app/modules/colors/components/step-table/step-table.component.ts b/src/app/modules/colors/components/step-table/step-table.component.ts index 4038674..f5e3a7e 100644 --- a/src/app/modules/colors/components/step-table/step-table.component.ts +++ b/src/app/modules/colors/components/step-table/step-table.component.ts @@ -13,6 +13,8 @@ export class StepTableComponent { @Input() steps: RecipeStep[] + hoveredStep : RecipeStep | null + addStep() { this.steps.push({id: null, message: ""}) this.stepTable.renderRows() diff --git a/src/app/modules/colors/pages/add/add.component.ts b/src/app/modules/colors/pages/add/add.component.ts index 8fbe040..4b98990 100644 --- a/src/app/modules/colors/pages/add/add.component.ts +++ b/src/app/modules/colors/pages/add/add.component.ts @@ -20,7 +20,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Nom', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Un nom est requis'} ] @@ -30,7 +30,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Description', icon: 'text', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Une description est requise'} ] @@ -41,7 +41,7 @@ export class AddComponent extends ErrorHandlingComponent { icon: 'palette', type: 'color', defaultValue: "#ffffff", - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Une couleur est requise'} ] @@ -53,7 +53,7 @@ export class AddComponent extends ErrorHandlingComponent { min: 0, max: 100, defaultValue: 10, - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Le lustre de la couleur est requis'} ] @@ -86,7 +86,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Bannière', icon: 'domain', type: 'select', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Une bannière est requise'} ], diff --git a/src/app/modules/colors/pages/edit/edit.component.ts b/src/app/modules/colors/pages/edit/edit.component.ts index 31332cc..c4f8a20 100644 --- a/src/app/modules/colors/pages/edit/edit.component.ts +++ b/src/app/modules/colors/pages/edit/edit.component.ts @@ -1,5 +1,5 @@ import {Component, ViewChild} from '@angular/core' -import {ErrorHandlingComponent, SubscribingComponent} from '../../../shared/components/subscribing.component' +import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {Recipe} from '../../../shared/model/recipe.model' import {RecipeService} from '../../services/recipe.service' import {ActivatedRoute, Router} from '@angular/router' @@ -10,7 +10,7 @@ import {AccountService} from '../../../accounts/services/account.service' import {EmployeePermission} from '../../../shared/model/employee' import {EntityEditComponent} from '../../../shared/components/entity-edit/entity-edit.component' import {ImagesEditorComponent} from '../../components/images-editor/images-editor.component' -import {ErrorHandler, ErrorModel, ErrorService} from '../../../shared/service/error.service' +import {ErrorModel, ErrorService} from '../../../shared/service/error.service' import {AlertService} from '../../../shared/service/alert.service' @Component({ @@ -30,7 +30,7 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Nom', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Un nom est requis'} ] @@ -40,7 +40,7 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Description', icon: 'text', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Une description est requise'} ] @@ -50,7 +50,7 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Couleur', icon: 'palette', type: 'color', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Une couleur est requise'} ] diff --git a/src/app/modules/colors/pages/explore/explore.component.html b/src/app/modules/colors/pages/explore/explore.component.html index 89b077b..3cb2ce7 100644 --- a/src/app/modules/colors/pages/explore/explore.component.html +++ b/src/app/modules/colors/pages/explore/explore.component.html @@ -5,11 +5,11 @@
- + - +
diff --git a/src/app/modules/colors/pages/explore/explore.component.ts b/src/app/modules/colors/pages/explore/explore.component.ts index 5f68076..a71ed01 100644 --- a/src/app/modules/colors/pages/explore/explore.component.ts +++ b/src/app/modules/colors/pages/explore/explore.component.ts @@ -88,7 +88,8 @@ export class ExploreComponent extends ErrorHandlingComponent { saveModifications() { this.subscribe( this.recipeService.saveExplorerModifications(this.recipe.id, this.note, this.mixesLocationChanges), - () => this.alertService.pushSuccess('Les modifications ont été enregistrées') + () => this.alertService.pushSuccess('Les modifications ont été enregistrées'), + true ) } @@ -103,7 +104,8 @@ export class ExploreComponent extends ErrorHandlingComponent { performDeductQuantities(observable: Observable) { this.subscribe( observable, - () => this.alertService.pushSuccess('Les quantités des produits utilisés ont été déduites de l\'inventaire') + () => this.alertService.pushSuccess('Les quantités des produits utilisés ont été déduites de l\'inventaire'), + true ) } } diff --git a/src/app/modules/company/pages/add/add.component.ts b/src/app/modules/company/pages/add/add.component.ts index 0579fe3..96a7243 100644 --- a/src/app/modules/company/pages/add/add.component.ts +++ b/src/app/modules/company/pages/add/add.component.ts @@ -1,6 +1,5 @@ import {Component} from '@angular/core' import {CompanyService} from '../../service/company.service' -import {Validators} from '@angular/forms' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {FormField} from '../../../shared/components/entity-add/entity-add.component' import {ActivatedRoute, Router} from '@angular/router' @@ -18,7 +17,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Nom', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Un nom est requis'} ] diff --git a/src/app/modules/company/pages/edit/edit.component.ts b/src/app/modules/company/pages/edit/edit.component.ts index af7ebbf..5037b3e 100644 --- a/src/app/modules/company/pages/edit/edit.component.ts +++ b/src/app/modules/company/pages/edit/edit.component.ts @@ -2,7 +2,6 @@ import {Component} from '@angular/core' import {ErrorHandlingComponent} 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 {CompanyService} from '../../service/company.service' import {ActivatedRoute, Router} from '@angular/router' import {ErrorModel, ErrorService} from '../../../shared/service/error.service' @@ -20,16 +19,20 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Nom', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Un nom est requis'} ] } ] + deleting = false handledErrorModels: ErrorModel[] = [{ - filter: error => error.status === 409, + filter: error => error.status === 409 && !this.deleting, messageProducer: error => `Une bannière avec le nom '${error.error.id}' existe déjà` + }, { + filter: error => error.status === 409 && this.deleting, + messageProducer: () => 'Cette bannière est utilisée par une ou plusieurs recettes' }] constructor( @@ -61,6 +64,7 @@ export class EditComponent extends ErrorHandlingComponent { } delete() { + this.deleting = true this.subscribeAndNavigate( this.companyService.delete(this.company.id), '/catalog/company/list' diff --git a/src/app/modules/employees/pages/add/add.component.html b/src/app/modules/employees/pages/add/add.component.html index 6c528f7..8175a1f 100644 --- a/src/app/modules/employees/pages/add/add.component.html +++ b/src/app/modules/employees/pages/add/add.component.html @@ -1,56 +1,10 @@ - - - Création d'un utilisateur - - -
- - Numéro d'employé - - - - Un numéro d'employé est requis - Le numéro d'employé doit être un nombre - - - - Prénom - - - - Un prénom est requis - - - - Nom - - - - Un nom est requis - - - - Mot de passe - - - - Un mot de passe est requis - Le mot de passe doit comprendre au moins 8 caractères - - - - Groupe - - Aucun - {{group.name}} - - - - -
-
- - - - -
+ + + + + + diff --git a/src/app/modules/employees/pages/add/add.component.sass b/src/app/modules/employees/pages/add/add.component.sass index 3c27c57..f2a4f1c 100644 --- a/src/app/modules/employees/pages/add/add.component.sass +++ b/src/app/modules/employees/pages/add/add.component.sass @@ -1,2 +1,3 @@ -mat-card - max-width: 90rem +cre-entity-add + ::ng-deep mat-card + max-width: 90rem diff --git a/src/app/modules/employees/pages/add/add.component.ts b/src/app/modules/employees/pages/add/add.component.ts index a11377a..97f5f13 100644 --- a/src/app/modules/employees/pages/add/add.component.ts +++ b/src/app/modules/employees/pages/add/add.component.ts @@ -1,13 +1,16 @@ -import {Component, ViewChild} from '@angular/core' -import {FormControl, FormGroup, Validators} from '@angular/forms' -import {PermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' -import {EmployeeGroup} from '../../../shared/model/employee' -import {Observable} from 'rxjs' +import {Component, ContentChildren, ViewChild, ViewContainerRef} from '@angular/core' +import {Validators} from '@angular/forms' +import { + currentPermissionsFieldComponent, + PermissionsFieldComponent +} from '../../../shared/components/permissions-field/permissions-field.component' import {GroupService} from '../../../groups/services/group.service' import {EmployeeService} from '../../services/employee.service' import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorService} from '../../../shared/service/error.service' +import {FormField} from '../../../shared/components/entity-add/entity-add.component' +import {map} from 'rxjs/operators' @Component({ selector: 'cre-add', @@ -15,16 +18,62 @@ import {ErrorService} from '../../../shared/service/error.service' styleUrls: ['./add.component.sass'] }) export class AddComponent extends ErrorHandlingComponent { - @ViewChild('permissionsField', {static: true}) permissionsField: PermissionsFieldComponent + @ViewChild('permissionsTemplateRef', {static: true}) permissionsTemplateRef - form: FormGroup - idControl: FormControl - firstNameControl: FormControl - lastNameControl: FormControl - passwordControl: FormControl - groupControl: FormControl - - group$: Observable | null + formFields: FormField[] = [{ + name: 'id', + label: 'Numéro d\'employé', + icon: 'pound', + type: 'number', + required: true, + validator: Validators.compose([Validators.pattern(new RegExp('^[0-9]+$')), Validators.min(0)]), + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un numéro d\'employé est requis'}, + {conditionFn: errors => errors.pattern, message: 'Le numéro d\'employé doit être un nombre'} + ] + }, { + name: 'firstName', + label: 'Prénom', + icon: 'account', + type: 'text', + required: true, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un prénom est requis'} + ] + }, { + name: 'lastName', + label: 'Nom', + icon: 'account', + type: 'text', + required: true, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un nom est requis'} + ] + }, { + name: 'password', + label: 'Mot de passe', + icon: 'lock', + type: 'password', + required: true, + validator: Validators.minLength(8), + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un mot de passe est requis'}, + {conditionFn: errors => errors.minlength, message: 'Le mot de passe doit comprendre au moins 8 caractères'} + ] + }, { + name: 'groupId', + label: 'Groupe', + icon: 'account-multiple', + type: 'select', + defaultValue: -1, + options$: this.groupService.all.pipe(map(groups => groups.map(g => { + return {value: g.id, label: g.name} + }))) + }, { + name: 'permissions', + label: 'Permissions', + type: 'permissions' + }] constructor( private employeeService: EmployeeService, @@ -36,35 +85,23 @@ export class AddComponent extends ErrorHandlingComponent { super(errorService, activatedRoute, router) } - ngOnInit(): void { + ngOnInit() { super.ngOnInit() - this.group$ = this.groupService.all - - this.idControl = new FormControl(null, Validators.compose([Validators.required, Validators.pattern(new RegExp('^[0-9]+$')), Validators.min(0)])) - this.firstNameControl = new FormControl(null, Validators.required) - this.lastNameControl = new FormControl(null, Validators.required) - this.passwordControl = new FormControl(null, Validators.compose([Validators.required, Validators.minLength(8)])) - this.groupControl = new FormControl(null, Validators.min(0)) - this.form = new FormGroup({ - id: this.idControl, - firstName: this.firstNameControl, - lastName: this.lastNameControl, - password: this.passwordControl, - group: this.groupControl - }) + this.formFields[this.formFields.length - 1].template = this.permissionsTemplateRef } - submit() { - if (this.permissionsField.valid() && this.form.valid) { + submit(values) { + const permissionsField = currentPermissionsFieldComponent + if (permissionsField.valid()) { this.subscribeAndNavigate( this.employeeService.save( - parseInt(this.idControl.value), - this.firstNameControl.value, - this.lastNameControl.value, - this.passwordControl.value, - this.groupControl.value, - this.permissionsField.allEnabledPermissions + values.id, + values.firstName, + values.lastName, + values.password, + values.groupId, + permissionsField.allEnabledPermissions ), '/employee/list' ) diff --git a/src/app/modules/employees/pages/edit/edit.component.html b/src/app/modules/employees/pages/edit/edit.component.html index 9cec7fb..6a5595e 100644 --- a/src/app/modules/employees/pages/edit/edit.component.html +++ b/src/app/modules/employees/pages/edit/edit.component.html @@ -1,50 +1,15 @@ - - - Modification de l'utilisateur #{{employee.id}} - - -
- - Numéro d'employé - - - - Un numéro d'employé est requis - Le numéro d'employé doit être un nombre - - - - Prénom - - - - Un prénom est requis - - - - Nom - - - - Un nom est requis - - - - Groupe - - Aucun - {{group.name}} - - - - -
-
- - - - - - -
+ + + + + + diff --git a/src/app/modules/employees/pages/edit/edit.component.sass b/src/app/modules/employees/pages/edit/edit.component.sass index 3c27c57..f8eace4 100644 --- a/src/app/modules/employees/pages/edit/edit.component.sass +++ b/src/app/modules/employees/pages/edit/edit.component.sass @@ -1,2 +1,3 @@ -mat-card - max-width: 90rem +cre-entity-edit + ::ng-deep mat-card + max-width: 90rem diff --git a/src/app/modules/employees/pages/edit/edit.component.ts b/src/app/modules/employees/pages/edit/edit.component.ts index 27d8478..5ddd2f3 100644 --- a/src/app/modules/employees/pages/edit/edit.component.ts +++ b/src/app/modules/employees/pages/edit/edit.component.ts @@ -1,14 +1,14 @@ -import {Component} from '@angular/core' -import {PermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' -import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms' +import {Component, ViewChild} from '@angular/core' +import {currentPermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' import {EmployeeService} from '../../services/employee.service' import {GroupService} from '../../../groups/services/group.service' import {ActivatedRoute, Router} from '@angular/router' -import {Observable} from 'rxjs' -import {Employee, EmployeeGroup, EmployeePermission} from '../../../shared/model/employee' +import {Employee} from '../../../shared/model/employee' import {AccountService} from '../../../accounts/services/account.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorService} from '../../../shared/service/error.service' +import {FormField} from '../../../shared/components/entity-add/entity-add.component' +import {map} from 'rxjs/operators' @Component({ selector: 'cre-edit', @@ -16,20 +16,52 @@ import {ErrorService} from '../../../shared/service/error.service' styleUrls: ['./edit.component.sass'] }) export class EditComponent extends ErrorHandlingComponent { + @ViewChild('permissionsTemplateRef', {static: true}) permissionsTemplateRef + employee: Employee | null - - group$: Observable | null - - private _idControl: FormControl - private _firstNameControl: FormControl - private _lastNameControl: FormControl - private _groupControl: FormControl + formFields: FormField[] = [{ + name: 'id', + label: 'Numéro d\'employé', + icon: 'pound', + type: 'number', + readonly: true + }, { + name: 'firstName', + label: 'Prénom', + icon: 'account', + type: 'text', + required: true, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un prénom est requis'} + ] + }, { + name: 'lastName', + label: 'Nom', + icon: 'account', + type: 'text', + required: true, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un nom est requis'} + ] + }, { + name: 'groupId', + label: 'Groupe', + icon: 'account-multiple', + type: 'select', + valueFn: employee => employee.group ? employee.group.id : -1, + options$: this.groupService.all.pipe(map(groups => groups.map(g => { + return {value: g.id, label: g.name} + }))) + }, { + name: 'permissions', + label: 'Permissions', + type: 'permissions' + }] constructor( private accountService: AccountService, private employeeService: EmployeeService, private groupService: GroupService, - private formBuilder: FormBuilder, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -46,21 +78,22 @@ export class EditComponent extends ErrorHandlingComponent { '/employee/list' ) - this.group$ = this.groupService.all + this.formFields[this.formFields.length - 1].template = this.permissionsTemplateRef } - submit(permissionsField: PermissionsFieldComponent) { - if (permissionsField.valid() && this.form.valid) { + submit(values) { + const permissionsField = currentPermissionsFieldComponent + if (permissionsField.valid()) { this.subscribe( this.employeeService.update( - parseInt(this.idControl.value), - this.firstNameControl.value, - this.lastNameControl.value, + parseInt(values.id), + values.firstName, + values.lastName, permissionsField.allEnabledPermissions ), () => { - const group = parseInt(this._groupControl.value) - if (!isNaN(group)) { + const group = values.groupId + if (group >= 0) { this.subscribeAndNavigate( this.groupService.addEmployeeToGroup(group, this.employee), '/employee/list' @@ -72,7 +105,7 @@ export class EditComponent extends ErrorHandlingComponent { '/employee/list' ) } else { - this.router.navigate(['/employee/list']) + this.urlUtils.navigateTo('/employee/list') } } } @@ -86,47 +119,4 @@ export class EditComponent extends ErrorHandlingComponent { '/employee/list' ) } - - get form(): FormGroup { - return this.formBuilder.group({ - id: this._idControl, - firstName: this._firstNameControl, - lastName: this._lastNameControl, - group: this._groupControl - }) - } - - get idControl(): FormControl { - this._idControl = this.lazyControl(this._idControl, () => new FormControl({value: this.employee.id, disabled: true}, Validators.compose([Validators.required, Validators.pattern(new RegExp('^[0-9]+$')), Validators.min(0)]))) - return this._idControl - } - - get firstNameControl(): FormControl { - this._firstNameControl = this.lazyControl(this._firstNameControl, () => new FormControl(this.employee.firstName, Validators.required)) - return this._firstNameControl - } - - get lastNameControl(): FormControl { - this._lastNameControl = this.lazyControl(this._lastNameControl, () => new FormControl(this.employee.lastName, Validators.required)) - return this._lastNameControl - } - - get groupControl(): FormControl { - this._groupControl = this.lazyControl(this._groupControl, () => new FormControl(this.employee.group?.id)) - return this._groupControl - } - - private lazyControl(control: FormControl, provider: () => FormControl): FormControl { - if (control) { - return control - } - if (this.employee) { - return provider() - } - return null - } - - get canRemoveEmployee(): boolean { - return this.accountService.hasPermission(EmployeePermission.REMOVE_EMPLOYEE) - } } diff --git a/src/app/modules/employees/pages/list/list.component.html b/src/app/modules/employees/pages/list/list.component.html index 15f11bb..e52139f 100644 --- a/src/app/modules/employees/pages/list/list.component.html +++ b/src/app/modules/employees/pages/list/list.component.html @@ -1,66 +1,13 @@ -
- -
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numéro d'employé{{employee.id}}Nom{{employee.firstName}} {{employee.lastName}}Groupe - {{employee.group.name}} - Aucun - Dernière connexion - {{getDate(employee.lastLoginTime).toLocaleString()}} - Jamais - Permissions - {{employee.permissions.length}} - 0 - - - - - -
- -
-
+ + + diff --git a/src/app/modules/employees/pages/list/list.component.ts b/src/app/modules/employees/pages/list/list.component.ts index 6805ea6..05e7b98 100644 --- a/src/app/modules/employees/pages/list/list.component.ts +++ b/src/app/modules/employees/pages/list/list.component.ts @@ -23,9 +23,22 @@ import {ErrorService} from '../../../shared/service/error.service' }) export class ListComponent extends ErrorHandlingComponent { employees$: Observable - columns = ['id', 'name', 'group', 'permissionCount', 'lastLogin', 'editButton', 'editPasswordButton'] - - expandedElement: Employee | null + columns = [ + {def: 'id', title: 'Numéro d\'employé', valueFn: e => e.id}, + {def: 'name', title: 'Nom', valueFn: e => `${e.firstName} ${e.lastName}`}, + {def: 'group', title: 'Groupe', valueFn: e => e.group ? e.group.name : 'Aucun'}, + {def: 'permissionCount', title: 'Nombre de permissions', valueFn: e => e.permissions.length}, + {def: 'lastLogin', title: 'Dernière connexion', valueFn: e => e.lastLoginTime ? this.getDate(e.lastLoginTime).toLocaleString() : 'Jamais'} + ] + buttons = [{ + text: 'Modifier', + linkFn: employee => `/employee/edit/${employee.id}`, + permission: EmployeePermission.EDIT_EMPLOYEE + }, { + text: 'Modifier mot de passe', + linkFn: employee => `/employee/password/edit/${employee.id}`, + permission: EmployeePermission.EDIT_EMPLOYEE_PASSWORD + }] constructor( private employeeService: EmployeeService, @@ -44,12 +57,4 @@ export class ListComponent extends ErrorHandlingComponent { getDate(dateString: string) { return new Date(dateString) } - - get canEditEmployee(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_EMPLOYEE) - } - - get canEditEmployeePassword(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_EMPLOYEE_PASSWORD) - } } diff --git a/src/app/modules/groups/pages/add/add.component.html b/src/app/modules/groups/pages/add/add.component.html index b00be66..3693e17 100644 --- a/src/app/modules/groups/pages/add/add.component.html +++ b/src/app/modules/groups/pages/add/add.component.html @@ -1,23 +1,10 @@ - - - Création d'un groupe - - -
- - Nom - - - - Un nom est requis - Le nom d'un groupe doit comprendre au moins 3 caractères - - - -
-
- - - - -
+ + + + + + diff --git a/src/app/modules/groups/pages/add/add.component.sass b/src/app/modules/groups/pages/add/add.component.sass index d194b62..f2a4f1c 100644 --- a/src/app/modules/groups/pages/add/add.component.sass +++ b/src/app/modules/groups/pages/add/add.component.sass @@ -1,5 +1,3 @@ -mat-card - max-width: 90rem - -mat-checkbox - font-size: .8em +cre-entity-add + ::ng-deep mat-card + max-width: 90rem diff --git a/src/app/modules/groups/pages/add/add.component.ts b/src/app/modules/groups/pages/add/add.component.ts index a1a30a6..34e7623 100644 --- a/src/app/modules/groups/pages/add/add.component.ts +++ b/src/app/modules/groups/pages/add/add.component.ts @@ -1,10 +1,11 @@ import {Component, ViewChild} from '@angular/core' -import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms' +import {Validators} from '@angular/forms' import {GroupService} from '../../services/group.service' import {ActivatedRoute, Router} from '@angular/router' -import {PermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' +import {currentPermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorService} from '../../../shared/service/error.service' +import {FormField} from '../../../shared/components/entity-add/entity-add.component' @Component({ selector: 'cre-add', @@ -12,13 +13,26 @@ import {ErrorService} from '../../../shared/service/error.service' styleUrls: ['./add.component.sass'] }) export class AddComponent extends ErrorHandlingComponent { - @ViewChild('permissionsField', {static: true}) permissionsField: PermissionsFieldComponent + @ViewChild('permissionsTemplateRef', {static: true}) permissionsTemplateRef - form: FormGroup - nameControl: FormControl + formFields: FormField[] = [{ + name: 'name', + label: 'Nom', + icon: 'account-multiple', + type: 'text', + required: true, + validator: Validators.minLength(3), + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un nom est requis'}, + {conditionFn: errors => errors.minlength, message: 'Le nom d\'un groupe doit comprendre au moins 3 caractères'} + ] + }, { + name: 'permissions', + label: 'Permissions', + type: 'permissions' + }] constructor( - private formBuilder: FormBuilder, private groupService: GroupService, errorService: ErrorService, router: Router, @@ -27,17 +41,17 @@ export class AddComponent extends ErrorHandlingComponent { super(errorService, activatedRoute, router) } - ngOnInit(): void { - this.nameControl = new FormControl(null, Validators.compose([Validators.required, Validators.minLength(3)])) - this.form = this.formBuilder.group({ - name: this.nameControl - }) + ngOnInit() { + super.ngOnInit(); + + this.formFields[this.formFields.length - 1].template = this.permissionsTemplateRef } - submit() { - if (this.form.valid && this.permissionsField.valid()) { + submit(values) { + const permissionsField = currentPermissionsFieldComponent + if (permissionsField.valid()) { this.subscribeAndNavigate( - this.groupService.save(this.nameControl.value, this.permissionsField.allEnabledPermissions), + this.groupService.save(values.name, permissionsField.allEnabledPermissions), '/group/list' ) } diff --git a/src/app/modules/groups/pages/edit/edit.component.html b/src/app/modules/groups/pages/edit/edit.component.html index 5d99d8b..6e47fe1 100644 --- a/src/app/modules/groups/pages/edit/edit.component.html +++ b/src/app/modules/groups/pages/edit/edit.component.html @@ -1,29 +1,15 @@ - - - Modifier le groupe {{group.name}} - - -
- - Nom - - - - Un nom est requis - Le nom d'un groupe doit comprendre au moins 3 caractères - - - -
-
- - - - - - -
+ + + + + diff --git a/src/app/modules/groups/pages/edit/edit.component.sass b/src/app/modules/groups/pages/edit/edit.component.sass index 3c27c57..f8eace4 100644 --- a/src/app/modules/groups/pages/edit/edit.component.sass +++ b/src/app/modules/groups/pages/edit/edit.component.sass @@ -1,2 +1,3 @@ -mat-card - max-width: 90rem +cre-entity-edit + ::ng-deep mat-card + max-width: 90rem diff --git a/src/app/modules/groups/pages/edit/edit.component.ts b/src/app/modules/groups/pages/edit/edit.component.ts index f629334..cdbca83 100644 --- a/src/app/modules/groups/pages/edit/edit.component.ts +++ b/src/app/modules/groups/pages/edit/edit.component.ts @@ -1,12 +1,13 @@ import {Component, ViewChild} from '@angular/core' import {ActivatedRoute, Router} from '@angular/router' -import {EmployeeGroup, EmployeePermission} from '../../../shared/model/employee' +import {EmployeeGroup} from '../../../shared/model/employee' import {GroupService} from '../../services/group.service' -import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms' -import {PermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' +import {Validators} from '@angular/forms' +import {currentPermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' import {AccountService} from '../../../accounts/services/account.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorService} from '../../../shared/service/error.service' +import {FormField} from '../../../shared/components/entity-add/entity-add.component' @Component({ selector: 'cre-edit', @@ -14,16 +15,29 @@ import {ErrorService} from '../../../shared/service/error.service' styleUrls: ['./edit.component.sass'] }) export class EditComponent extends ErrorHandlingComponent { - @ViewChild('permissionsField') permissionsField: PermissionsFieldComponent + @ViewChild('permissionsTemplateRef', {static: true}) permissionsTemplateRef + formFields: FormField[] = [{ + name: 'name', + label: 'Nom', + icon: 'account-multiple', + type: 'text', + required: true, + validator: Validators.minLength(3), + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un nom est requis'}, + {conditionFn: errors => errors.minlength, message: 'Le nom d\'un groupe doit comprendre au moins 3 caractères'} + ] + }, { + name: 'permissions', + label: 'Permissions', + type: 'permissions' + }] group: EmployeeGroup | null - private _nameControl: FormControl - constructor( private accountService: AccountService, private groupService: GroupService, - private formBuilder: FormBuilder, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -39,12 +53,15 @@ export class EditComponent extends ErrorHandlingComponent { group => this.group = group, '/group/list' ) + + this.formFields[this.formFields.length - 1].template = this.permissionsTemplateRef } - submit(): void { - if (this.form.valid && this.permissionsField.valid()) { + submit(values): void { + const permissionsField = currentPermissionsFieldComponent + if (permissionsField.valid()) { this.subscribeAndNavigate( - this.groupService.update(this.group.id, this.nameControl.value, this.permissionsField.allEnabledPermissions), + this.groupService.update(this.group.id, values.name, permissionsField.allEnabledPermissions), '/group/list' ) } @@ -56,29 +73,4 @@ export class EditComponent extends ErrorHandlingComponent { '/group/list' ) } - - get form(): FormGroup { - return this.formBuilder.group({ - name: this.nameControl - }) - } - - get confirmBoxMessage(): string { - return `Voulez-vous vraiment supprimer le groupe ${this.group.name}?` - } - - get nameControl(): FormControl { - if (this._nameControl) { - return this._nameControl - } - if (this.group) { - this._nameControl = new FormControl(this.group.name, Validators.compose([Validators.required, Validators.minLength(3)])) - return this._nameControl - } - return null - } - - get canRemoveGroup(): boolean { - return this.accountService.hasPermission(EmployeePermission.REMOVE_EMPLOYEE_GROUP) - } } diff --git a/src/app/modules/groups/pages/list/list.component.html b/src/app/modules/groups/pages/list/list.component.html index be59f51..4d17df7 100644 --- a/src/app/modules/groups/pages/list/list.component.html +++ b/src/app/modules/groups/pages/list/list.component.html @@ -1,66 +1,13 @@ -
- -
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Nom{{group.name}}Nombre de permissions{{group.permissions.length}}Nombre d'utilisateurs{{group.employeeCount}} - - - - -
- -
-
+ + + diff --git a/src/app/modules/groups/pages/list/list.component.ts b/src/app/modules/groups/pages/list/list.component.ts index e6e1565..dc2e7a2 100644 --- a/src/app/modules/groups/pages/list/list.component.ts +++ b/src/app/modules/groups/pages/list/list.component.ts @@ -1,9 +1,7 @@ import {Component} from '@angular/core' -import {Observable} from 'rxjs' import {GroupService} from '../../services/group.service' import {EmployeeGroup, EmployeePermission} from '../../../shared/model/employee' -import {takeUntil} from 'rxjs/operators' -import {animate, state, style, transition, trigger} from '@angular/animations' +import {map} from 'rxjs/operators' import {AccountService} from '../../../accounts/services/account.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ActivatedRoute, Router} from '@angular/router' @@ -13,20 +11,26 @@ import {AlertService} from '../../../shared/service/alert.service' @Component({ selector: 'cre-groups', templateUrl: './list.component.html', - styleUrls: ['./list.component.sass'], - animations: [ - trigger('detailExpand', [ - state('collapsed', style({height: '0px', minHeight: '0'})), - state('expanded', style({height: '*'})), - transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')) - ]) - ] + styleUrls: ['./list.component.sass'] }) export class ListComponent extends ErrorHandlingComponent { - groups$: Observable + groups$ = this.groupService.all.pipe(map(groups => groups.filter(g => g.id >= 0))) defaultGroup: EmployeeGroup = null - columns = ['name', 'permissionCount', 'employeeCount', 'defaultGroup', 'editGroup'] - expandedElement: EmployeeGroup | null + columns = [ + {def: 'name', title: 'Nom', valueFn: g => g.name}, + {def: 'permissionCount', title: 'Nombre de permissions', valueFn: g => g.permissions.length}, + {def: 'employeeCount', title: 'Nombre d\'utilisateurs', valueFn: g => g.employeeCount} + ] + buttons = [{ + text: 'Définir par défaut', + clickFn: group => this.setDefaultGroup(group), + permission: EmployeePermission.SET_BROWSER_DEFAULT_GROUP, + disabledFn: group => this.isDefaultGroup(group) + }, { + text: 'Modifier', + linkFn: group => `/group/edit/${group.id}`, + permission: EmployeePermission.EDIT_EMPLOYEE_GROUP + }] handledErrorModels: ErrorModel[] = [{ filter: error => error.status === 404, @@ -45,33 +49,22 @@ export class ListComponent extends ErrorHandlingComponent { } ngOnInit(): void { - this.groups$ = this.groupService.all.pipe(takeUntil(this.destroy$)) this.subscribe( this.groupService.defaultGroup, group => this.defaultGroup = group, + true ) } setDefaultGroup(group: EmployeeGroup) { this.subscribe( this.groupService.setDefaultGroup(group), - () => this.defaultGroup = group + () => this.defaultGroup = group, + true ) } isDefaultGroup(group: EmployeeGroup): boolean { return this.defaultGroup && this.defaultGroup.id == group.id } - - get canViewEmployee(): boolean { - return this.accountService.hasPermission(EmployeePermission.VIEW_EMPLOYEE) - } - - get canEditGroup(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_EMPLOYEE_GROUP) - } - - get canSetBrowserDefaultGroup(): boolean { - return this.accountService.hasPermission(EmployeePermission.SET_BROWSER_DEFAULT_GROUP) - } } diff --git a/src/app/modules/groups/services/group.service.ts b/src/app/modules/groups/services/group.service.ts index 7e67017..49bc2ca 100644 --- a/src/app/modules/groups/services/group.service.ts +++ b/src/app/modules/groups/services/group.service.ts @@ -1,7 +1,8 @@ -import {Injectable} from '@angular/core'; -import {ApiService} from "../../shared/service/api.service"; -import {Observable} from "rxjs"; -import {Employee, EmployeeGroup, EmployeePermission} from "../../shared/model/employee"; +import {Injectable} from '@angular/core' +import {ApiService} from '../../shared/service/api.service' +import {Observable} from 'rxjs' +import {Employee, EmployeeGroup, EmployeePermission} from '../../shared/model/employee' +import {tap} from 'rxjs/operators' @Injectable({ providedIn: 'root' @@ -14,10 +15,16 @@ export class GroupService { get all(): Observable { return this.api.get('/employee/group') + .pipe(tap(groups => groups.unshift({ + id: -1, + name: 'Aucun', + permissions: [], + employeeCount: 0 + }))) } getById(id: number): Observable { - return this.api.get(`/employee/group/${id}`); + return this.api.get(`/employee/group/${id}`) } get defaultGroup(): Observable { diff --git a/src/app/modules/material-type/pages/add/add.component.ts b/src/app/modules/material-type/pages/add/add.component.ts index 36b19e4..dd765a1 100644 --- a/src/app/modules/material-type/pages/add/add.component.ts +++ b/src/app/modules/material-type/pages/add/add.component.ts @@ -18,7 +18,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Nom', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: (errors) => errors.required, message: 'Un nom est requis'}, ] @@ -28,7 +28,8 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Préfixe', icon: 'label-variant', type: 'text', - validator: Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(3)]), + required: true, + validator: Validators.compose([Validators.minLength(3), Validators.maxLength(3)]), errorMessages: [ {conditionFn: (errors) => errors.required, message: 'Un préfixe est requis'}, { diff --git a/src/app/modules/material-type/pages/edit/edit.component.ts b/src/app/modules/material-type/pages/edit/edit.component.ts index c110e99..73e8a69 100644 --- a/src/app/modules/material-type/pages/edit/edit.component.ts +++ b/src/app/modules/material-type/pages/edit/edit.component.ts @@ -20,7 +20,7 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Nom', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: (errors) => errors.required, message: 'Un nom est requis'}, ] @@ -30,7 +30,8 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Préfixe', icon: 'label-variant', type: 'text', - validator: Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(3)]), + required: true, + validator: Validators.compose([Validators.minLength(3), Validators.maxLength(3)]), errorMessages: [ {conditionFn: (errors) => errors.required, message: 'Un préfixe est requis'}, { @@ -40,14 +41,19 @@ export class EditComponent extends ErrorHandlingComponent { ] } ] - submittedValues: any | null + deleting = false + submittedValues: any | null handledErrorModels: ErrorModel[] = [{ - filter: error => error.status === 409 && error.error.id === this.submittedValues.name, + filter: error => error.status === 409 && !this.deleting && error.error.id === this.submittedValues.name, messageProducer: error => `Un type de produit avec le nom '${error.error.id}' existe déjà` }, { - filter: error => error.status === 409 && error.error.id === this.submittedValues.prefix, + filter: error => error.status === 409 && !this.deleting && error.error.id === this.submittedValues.prefix, messageProducer: error => `Un type de produit avec le préfixe '${error.error.id}' existe déjà` + }, { + filter: error => error.status === 409 && this.deleting, + consumer: () => this.deleting = true, + messageProducer: () => 'Ce type de produit est utilisé dans une ou plusieurs recettes ou produits' }] constructor( @@ -80,6 +86,7 @@ export class EditComponent extends ErrorHandlingComponent { } delete() { + this.deleting = true this.subscribeAndNavigate( this.materialTypeService.delete(this.materialType.id), '/catalog/materialtype/list' diff --git a/src/app/modules/material/pages/add/add.component.ts b/src/app/modules/material/pages/add/add.component.ts index a895cb6..0faa0c4 100644 --- a/src/app/modules/material/pages/add/add.component.ts +++ b/src/app/modules/material/pages/add/add.component.ts @@ -20,7 +20,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Code', icon: 'form-textbox', type: 'text', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: (errors) => errors.required, message: 'Un code est requis'} ] @@ -30,7 +30,8 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Quantité en inventaire', icon: 'beaker-outline', type: 'number', - validator: Validators.compose([Validators.required, Validators.min(0)]), + required: true, + validator: Validators.min(0), errorMessages: [ {conditionFn: errors => errors.required, message: 'Une quantité en inventaire est requise'}, {conditionFn: errors => errors.min, message: 'La quantité doit être supérieure ou égale à 0'} @@ -42,7 +43,7 @@ export class AddComponent extends ErrorHandlingComponent { label: 'Type de produit', icon: 'shape-outline', type: 'select', - validator: Validators.required, + required: true, errorMessages: [ {conditionFn: errors => errors.required, message: 'Un type de produit est requis'} ], diff --git a/src/app/modules/material/pages/edit/edit.component.ts b/src/app/modules/material/pages/edit/edit.component.ts index 0395e5f..236075d 100644 --- a/src/app/modules/material/pages/edit/edit.component.ts +++ b/src/app/modules/material/pages/edit/edit.component.ts @@ -11,112 +11,119 @@ import {environment} from '../../../../../environments/environment' import {ErrorModel, ErrorService} from '../../../shared/service/error.service' @Component({ - selector: 'cre-edit', - templateUrl: './edit.component.html', - styleUrls: ['./edit.component.sass'] + selector: 'cre-edit', + templateUrl: './edit.component.html', + styleUrls: ['./edit.component.sass'] }) export class EditComponent extends ErrorHandlingComponent { - @ViewChild('simdutTemplate', {static: true}) simdutTemplateRef + @ViewChild('simdutTemplate', {static: true}) simdutTemplateRef - material: Material | null - formFields: FormField[] = [ - { - name: 'name', - label: 'Code', - icon: 'form-textbox', - type: 'text', - validator: Validators.required, - errorMessages: [ - {conditionFn: (errors) => errors.required, message: 'Un code est requis'} - ] - }, - { - name: 'inventoryQuantity', - label: 'Quantité en inventaire', - icon: 'beaker-outline', - type: 'number', - validator: Validators.compose([Validators.required, Validators.min(0)]), - errorMessages: [ - {conditionFn: errors => errors.required, message: 'Une quantité en inventaire est requise'}, - {conditionFn: errors => errors.min, message: 'La quantité doit être supérieure ou égale à 0'} - ], - step: '0.01' - }, - { - name: 'materialType', - label: 'Type de produit', - icon: 'shape-outline', - type: 'select', - validator: Validators.required, - errorMessages: [ - {conditionFn: errors => errors.required, message: 'Un type de produit est requis'} - ], - valueFn: material => material.materialType.id, - options$: this.materialTypeService.all.pipe(map(types => types.map(t => { - return {value: t.id, label: t.name} - }))) - }, - { - name: 'simdutFile', - label: 'Fiche signalitique', - icon: 'file-outline', - type: 'file', - fileType: 'application/pdf' + material: Material | null + formFields: FormField[] = [ + { + name: 'name', + label: 'Code', + icon: 'form-textbox', + type: 'text', + required: true, + errorMessages: [ + {conditionFn: (errors) => errors.required, message: 'Un code est requis'} + ] + }, + { + name: 'inventoryQuantity', + label: 'Quantité en inventaire', + icon: 'beaker-outline', + type: 'number', + required: true, + validator: Validators.min(0), + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Une quantité en inventaire est requise'}, + {conditionFn: errors => errors.min, message: 'La quantité doit être supérieure ou égale à 0'} + ], + step: '0.01' + }, + { + name: 'materialType', + label: 'Type de produit', + icon: 'shape-outline', + type: 'select', + required: true, + errorMessages: [ + {conditionFn: errors => errors.required, message: 'Un type de produit est requis'} + ], + valueFn: material => material.materialType.id, + options$: this.materialTypeService.all.pipe(map(types => types.map(t => { + return {value: t.id, label: t.name} + }))) + }, + { + name: 'simdutFile', + label: 'Fiche signalitique', + icon: 'file-outline', + type: 'file', + fileType: 'application/pdf' + } + ] + hasSimdut = false + selectedSimdutFile: File | null + + deleting = false + handledErrorModels: ErrorModel[] = [{ + filter: error => error.status === 409 && !this.deleting, + messageProducer: error => `Un produit avec le nom '${error.error.id}' existe déjà` + }, { + filter: error => error.status === 409 && this.deleting, + consumer: () => this.deleting = false, + messageProducer: () => `Ce produit est utilisé dans une plusieurs recettes` + }] + + constructor( + private materialService: MaterialService, + private materialTypeService: MaterialTypeService, + errorService: ErrorService, + router: Router, + activatedRoute: ActivatedRoute + ) { + super(errorService, activatedRoute, router) } - ] - hasSimdut = false - selectedSimdutFile: File | null - handledErrorModels: ErrorModel[] = [{ - filter: error => error.status === 409, - messageProducer: error => `Un produit avec le nom '${error.error.id}' existe déjà` - }] + ngOnInit() { + super.ngOnInit() - constructor( - private materialService: MaterialService, - private materialTypeService: MaterialTypeService, - errorService: ErrorService, - router: Router, - activatedRoute: ActivatedRoute - ) { - super(errorService, activatedRoute, router) - } + this.formFields[3].template = this.simdutTemplateRef - ngOnInit() { - super.ngOnInit() + const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id')) + this.subscribeEntityById( + this.materialService, + id, + material => this.material = material, + '/catalog/material/list' + ) - this.formFields[3].template = this.simdutTemplateRef + this.subscribe( + this.materialService.hasSimdut(id), + b => this.hasSimdut = b + ) + } - const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id')) - this.subscribeEntityById( - this.materialService, - id, - material => this.material = material, - '/catalog/material/list' - ) + submit(values) { + this.subscribeAndNavigate( + this.materialService.update(this.material.id, values.name, values.inventoryQuantity, values.materialType, this.selectedSimdutFile), + '/catalog/material/list' + ) + } - this.subscribe( - this.materialService.hasSimdut(id), - b => this.hasSimdut = b - ) - } + delete() { + this.deleting = true + this.subscribeAndNavigate( + this.materialService.delete(this.material.id), + '/catalog/material/list' + ) + } - submit(values) { - this.subscribeAndNavigate( - this.materialService.update(this.material.id, values.name, values.inventoryQuantity, values.materialType, this.selectedSimdutFile), - '/catalog/material/list' - ) - } - - delete() { - this.subscribeAndNavigate( - this.materialService.delete(this.material.id), - '/catalog/material/list' - ) - } - - openSimdutUrl() { - const simdutUrl = environment.apiUrl + `/material/${this.material.id}/simdut` - window.open(simdutUrl, '_blank') - } + openSimdutUrl() { + const simdutUrl = environment.apiUrl + `/material/${this.material.id}/simdut` + window.open(simdutUrl, '_blank') + } } diff --git a/src/app/modules/material/pages/list/list.component.ts b/src/app/modules/material/pages/list/list.component.ts index 7092ac6..96686fa 100644 --- a/src/app/modules/material/pages/list/list.component.ts +++ b/src/app/modules/material/pages/list/list.component.ts @@ -62,6 +62,7 @@ export class ListComponent extends ErrorHandlingComponent { ) }) }, + false, 1 ) } 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 c153826..5d7616e 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 @@
@@ -30,6 +30,10 @@ [ngTemplateOutlet]="sliderTemplate" [ngTemplateOutletContext]="{control: getControl(field.name), field: field}"> + +
@@ -44,7 +48,12 @@ let-control="control" let-field="field"> {{field.label}} - + @@ -57,7 +66,7 @@ - + {{field.label}} @@ -67,7 +76,7 @@ let-control="control" let-field="field"> {{field.label}} - + {{option.label}} @@ -81,7 +90,11 @@ let-control="control" let-field="field"> {{field.label}} - + + 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 3e11984..a3bde83 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 @@ -1,5 +1,5 @@ import {Component, EventEmitter, Input, Output} from '@angular/core' -import {FormBuilder, FormControl, FormGroup, ValidatorFn} from '@angular/forms' +import {FormBuilder, FormControl, FormGroup, ValidatorFn, Validators} from '@angular/forms' import {Observable} from 'rxjs' @Component({ @@ -23,7 +23,8 @@ export class EntityAddComponent { ngOnInit() { const formGroup = {} this.formFields.forEach(f => { - formGroup[f.name] = new FormControl(f.defaultValue, f.validator) + const validator = f.required ? Validators.compose([Validators.required, f.validator]) : f.validator + formGroup[f.name] = new FormControl(f.defaultValue, validator) }) this.form = this.formBuilder.group(formGroup) } @@ -47,6 +48,7 @@ export class FormField { public label?: string, public icon?: string, public type?: string, + public required?: boolean, public validator?: ValidatorFn, public errorMessages?: FormErrorMessage[], public valueFn?: (any) => any, 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 ce2ef3b..83f48fd 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 @@ -21,12 +21,12 @@ [ngTemplateOutletContext]="{control: getControl(field.name), field: field}"> @@ -46,7 +46,12 @@ let-control="control" let-field="field"> {{field.label}} - + @@ -61,7 +66,7 @@ let-control="control" let-field="field"> {{field.label}} - + {{option.label}} @@ -75,7 +80,11 @@ let-control="control" let-field="field"> {{field.label}} - + + diff --git a/src/app/modules/shared/components/entity-list/entity-list.component.html b/src/app/modules/shared/components/entity-list/entity-list.component.html index e0ac245..f444d14 100644 --- a/src/app/modules/shared/components/entity-list/entity-list.component.html +++ b/src/app/modules/shared/components/entity-list/entity-list.component.html @@ -2,7 +2,7 @@
- +
@@ -20,18 +20,40 @@ - + + + + - + + + + +
{{column.title}} - + + + + + +
+ +
diff --git a/src/app/modules/shared/components/entity-list/entity-list.component.ts b/src/app/modules/shared/components/entity-list/entity-list.component.ts index 752a1cb..d5ddb0f 100644 --- a/src/app/modules/shared/components/entity-list/entity-list.component.ts +++ b/src/app/modules/shared/components/entity-list/entity-list.component.ts @@ -2,11 +2,19 @@ import {Component, Input} from '@angular/core' import {Observable} from 'rxjs' import {AccountService} from '../../../accounts/services/account.service' import {EmployeePermission} from '../../model/employee' +import {animate, state, style, transition, trigger} from '@angular/animations' @Component({ selector: 'cre-entity-list', templateUrl: './entity-list.component.html', - styleUrls: ['./entity-list.component.sass'] + styleUrls: ['./entity-list.component.sass'], + animations: [ + trigger('detailExpand', [ + state('collapsed', style({height: '0px', minHeight: '0'})), + state('expanded', style({height: '*'})), + transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')) + ]) + ] }) export class EntityListComponent { @Input() entities$: Observable @@ -15,6 +23,11 @@ export class EntityListComponent { @Input() buttons?: TableButton[] @Input() addLink: string @Input() addPermission: EmployeePermission + @Input() expandable = false + @Input() rowDetailsTemplate + + hoveredEntity: T | null + expandedEntity: T | null constructor( private accountService: AccountService @@ -29,23 +42,43 @@ export class EntityListComponent { return this.accountService.hasPermission(permission) } - openExternalLink(button: TableButton, entity: T) { - let externalLink = null + getRouterLink(button: TableButton, entity: T): string { // @ts-ignore - if (button.link && button.link.externalLink) { + if (button.link && !button.link.externalLink) { // @ts-ignore - externalLink = button.link.externalLink - } else { - const linkFnResult = button.linkFn(entity) + return button.link + } else if (button.linkFn) { + const fnResult = button.linkFn(entity) // @ts-ignore - if (linkFnResult && linkFnResult.externalLink) { + if (!fnResult.externalLink) { // @ts-ignore - externalLink = linkFnResult.externalLink + return fnResult } } + return undefined + } - if (externalLink) { - window.open(externalLink, '_blank') + clickButton(button: TableButton, entity: T) { + if (button.link || button.linkFn) { + let externalLink = null + // @ts-ignore + if (button.link && button.link.externalLink) { + // @ts-ignore + externalLink = button.link.externalLink + } else { + const linkFnResult = button.linkFn(entity) + // @ts-ignore + if (linkFnResult && linkFnResult.externalLink) { + // @ts-ignore + externalLink = linkFnResult.externalLink + } + } + + if (externalLink) { + window.open(externalLink, '_blank') + } + } else if (button.clickFn) { + button.clickFn(entity) } } @@ -85,6 +118,7 @@ export class TableButton { public text: string, public link: string | { externalLink: string } | null, public linkFn: (T) => string | { externalLink: string } | null, + public clickFn: (T) => void, public permission: EmployeePermission | null, public disabledFn: (T) => boolean | null ) { diff --git a/src/app/modules/shared/components/header/header.component.ts b/src/app/modules/shared/components/header/header.component.ts index 9f3585f..ea14e7d 100644 --- a/src/app/modules/shared/components/header/header.component.ts +++ b/src/app/modules/shared/components/header/header.component.ts @@ -46,6 +46,7 @@ export class HeaderComponent extends SubscribingComponent { this._activeLink = data.url } }, + false, 1 ) diff --git a/src/app/modules/shared/components/loading-wheel/loading-wheel.component.html b/src/app/modules/shared/components/loading-wheel/loading-wheel.component.html new file mode 100644 index 0000000..67b0f2f --- /dev/null +++ b/src/app/modules/shared/components/loading-wheel/loading-wheel.component.html @@ -0,0 +1,6 @@ +
+
+
+ +
+
diff --git a/src/app/modules/shared/components/loading-wheel/loading-wheel.component.sass b/src/app/modules/shared/components/loading-wheel/loading-wheel.component.sass new file mode 100644 index 0000000..27c3fcb --- /dev/null +++ b/src/app/modules/shared/components/loading-wheel/loading-wheel.component.sass @@ -0,0 +1,18 @@ +.spinner-wrapper + transition: all 100ms + + &.visible + opacity: 1 !important + visibility: unset !important + + &:not(.visible) + opacity: 0 + visibility: hidden + + .spinner + max-width: max-content + position: fixed + left: 50% + top: 50% + transform: translate(-50%, -50%) + z-index: 11 diff --git a/src/app/modules/shared/components/loading-wheel/loading-wheel.component.ts b/src/app/modules/shared/components/loading-wheel/loading-wheel.component.ts new file mode 100644 index 0000000..0f648f9 --- /dev/null +++ b/src/app/modules/shared/components/loading-wheel/loading-wheel.component.ts @@ -0,0 +1,24 @@ +import {Component} from '@angular/core' + +export let globalLoadingWheel: LoadingWheelComponent | null + +@Component({ + selector: 'cre-loading-wheel', + templateUrl: './loading-wheel.component.html', + styleUrls: ['./loading-wheel.component.sass'] +}) +export class LoadingWheelComponent { + visible = false + + constructor() { + globalLoadingWheel = this + } + + show() { + this.visible = true + } + + hide() { + this.visible = false + } +} diff --git a/src/app/modules/shared/components/permissions-field/permissions-field.component.html b/src/app/modules/shared/components/permissions-field/permissions-field.component.html index cf7ef40..c44ead0 100644 --- a/src/app/modules/shared/components/permissions-field/permissions-field.component.html +++ b/src/app/modules/shared/components/permissions-field/permissions-field.component.html @@ -1,5 +1,5 @@
-

{{title}}

+

{{title}} *

diff --git a/src/app/modules/shared/components/permissions-field/permissions-field.component.ts b/src/app/modules/shared/components/permissions-field/permissions-field.component.ts index 7f3e0d7..59ba164 100644 --- a/src/app/modules/shared/components/permissions-field/permissions-field.component.ts +++ b/src/app/modules/shared/components/permissions-field/permissions-field.component.ts @@ -3,6 +3,10 @@ import {EmployeePermission, mapped_permissions} from "../../model/employee"; import {FormControl} from "@angular/forms"; import {AccountService} from "../../../accounts/services/account.service"; +// The current permissions field component. Workaround to be able to access a permission field in template. (See employee/AddComponent) +// This workaround prevent the use of several permissions field component at the same time. +export let currentPermissionsFieldComponent: PermissionsFieldComponent | null + @Component({ selector: 'cre-permissions-field', templateUrl: './permissions-field.component.html', @@ -35,6 +39,8 @@ export class PermissionsFieldComponent implements OnInit { this.togglePermission(control, true) }) } + + currentPermissionsFieldComponent = this } togglePermission(permission: any, bypassValue?: boolean) { diff --git a/src/app/modules/shared/components/subscribing.component.ts b/src/app/modules/shared/components/subscribing.component.ts index d19ce1b..c73a786 100644 --- a/src/app/modules/shared/components/subscribing.component.ts +++ b/src/app/modules/shared/components/subscribing.component.ts @@ -4,6 +4,7 @@ import {Observable, Subject} from 'rxjs' import {ActivatedRoute, Router} from '@angular/router' import {UrlUtils} from '../utils/url.utils' import {ErrorHandler, ErrorModel, ErrorService} from '../service/error.service' +import {globalLoadingWheel} from './loading-wheel/loading-wheel.component' export abstract class SubscribingComponent implements OnInit, OnDestroy { protected subscribers$ = [] @@ -17,33 +18,48 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy { ) { } - subscribe(observable: Observable, resultConsumer: (T) => void, take_count = -1) { + subscribe(observable: Observable, resultConsumer: (T) => void, showWheel = false, take_count = -1) { if (take_count >= 0) { observable.pipe(take(take_count), takeUntil(this.destroy$)) } else { observable.pipe(takeUntil(this.destroy$)) } + this.showLoadingWheel(showWheel) this.subscribers$.push(observable.subscribe({ - next: resultConsumer, - error: err => this.errorService.handleError(err) + next: t => { + resultConsumer(t) + this.hideLoadingWheel(showWheel) + }, + error: err => { + this.errorService.handleError(err) + this.hideLoadingWheel(showWheel) + } })) } - subscribeAndNavigate(observable: Observable, route: string) { + subscribeAndNavigate(observable: Observable, route: string, showWheel = true) { this.subscribe( observable, () => this.urlUtils.navigateTo(route), + showWheel, 1 ) } - subscribeEntityById(service: any, id: number, resultConsumer: (T) => void, notFoundRoute: string) { + subscribeEntityById(service: any, id: number, resultConsumer: (T) => void, notFoundRoute: string, showWheel = true) { + this.showLoadingWheel(showWheel) this.subscribers$.push(service.getById(id) .pipe(take(1), takeUntil(this.destroy$)) .subscribe({ - next: e => resultConsumer(e), - error: err => this.handleNotFoundError(err, notFoundRoute) + next: e => { + resultConsumer(e) + this.hideLoadingWheel(showWheel) + }, + error: err => { + this.handleNotFoundError(err, notFoundRoute) + this.hideLoadingWheel(showWheel) + } })) } @@ -62,6 +78,18 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy { this.errorService.handleError(error) } } + + protected showLoadingWheel(shouldShowWheel) { + if (shouldShowWheel) { + globalLoadingWheel.show() + } + } + + protected hideLoadingWheel(shouldShowWheel) { + if (shouldShowWheel) { + globalLoadingWheel.hide() + } + } } export abstract class ErrorHandlingComponent extends SubscribingComponent implements ErrorHandler { diff --git a/src/app/modules/shared/model/employee.ts b/src/app/modules/shared/model/employee.ts index 711e30b..3fbb75d 100644 --- a/src/app/modules/shared/model/employee.ts +++ b/src/app/modules/shared/model/employee.ts @@ -3,6 +3,7 @@ export class Employee { public id: number, public firstName: string, public lastName: string, + public explicitPermissions: EmployeePermission[], public permissions: EmployeePermission[], public group?: EmployeeGroup, public lastLoginTime?: Date diff --git a/src/app/modules/shared/service/api.service.ts b/src/app/modules/shared/service/api.service.ts index 3deff1e..5389e7d 100644 --- a/src/app/modules/shared/service/api.service.ts +++ b/src/app/modules/shared/service/api.service.ts @@ -4,10 +4,10 @@ import {Observable, Subject} from 'rxjs' import {environment} from '../../../../environments/environment' import {AppState} from '../app-state' import {Router} from '@angular/router' -import {map, share, takeUntil} from 'rxjs/operators' +import {map, share, takeUntil, tap} from 'rxjs/operators' import {valueOr} from '../utils/utils' import {ErrorService} from './error.service' -import {AccountService} from '../../accounts/services/account.service' +import {globalLoadingWheel} from '../components/loading-wheel/loading-wheel.component' @Injectable({ providedIn: 'root' diff --git a/src/app/modules/shared/shared.module.ts b/src/app/modules/shared/shared.module.ts index 3d8407a..c2325f0 100644 --- a/src/app/modules/shared/shared.module.ts +++ b/src/app/modules/shared/shared.module.ts @@ -28,12 +28,14 @@ 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' +import {MatSliderModule} from '@angular/material/slider' +import {SliderFieldComponent} from './components/slider-field/slider-field.component' +import {LoadingWheelComponent} from './components/loading-wheel/loading-wheel.component' +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner' @NgModule({ - declarations: [HeaderComponent, EmployeeInfoComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent], + declarations: [HeaderComponent, EmployeeInfoComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent, LoadingWheelComponent], exports: [ CommonModule, HttpClientModule, @@ -60,7 +62,8 @@ import { SliderFieldComponent } from './components/slider-field/slider-field.com EntityAddComponent, EntityEditComponent, FileButtonComponent, - GlobalAlertHandlerComponent + GlobalAlertHandlerComponent, + LoadingWheelComponent ], imports: [ MatTabsModule, @@ -76,6 +79,7 @@ import { SliderFieldComponent } from './components/slider-field/slider-field.com MatSelectModule, MatOptionModule, MatSliderModule, + MatProgressSpinnerModule, ReactiveFormsModule, RouterModule, CommonModule, diff --git a/src/styles.sass b/src/styles.sass index 5ece4d2..2e6f3f5 100644 --- a/src/styles.sass +++ b/src/styles.sass @@ -200,7 +200,7 @@ div.empty left: 0 background-color: black opacity: 0.4 - z-index: -1 + z-index: 10 .color-warning color: #fdd835