Merge branch '2-creer-un-systeme-de-gestion-des-erreurs-centralise-dans-le-frontend-angular' into 'master'

Resolve "Créer un système de gestion des erreurs centralisé dans le frontend Angular"

Closes #2

See merge request color-recipes-explorer/frontend!4
This commit is contained in:
William Nolin 2021-02-17 05:15:17 +00:00
commit 8c89cd289e
65 changed files with 1049 additions and 937 deletions

View File

@ -1,4 +1,5 @@
<cre-header></cre-header>
<cre-global-alert-handler></cre-global-alert-handler>
<div>
<div class="dark-background"></div>
<router-outlet></router-outlet>

View File

@ -1,9 +1,9 @@
import {Component, Inject, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from "@angular/common";
import {AppState} from "./modules/shared/app-state";
import {Observable} from "rxjs";
import {SubscribingComponent} from "./modules/shared/components/subscribing.component";
import {ActivatedRoute, Router} from "@angular/router";
import {Component, Inject, PLATFORM_ID} from '@angular/core'
import {isPlatformBrowser} from '@angular/common'
import {AppState} from './modules/shared/app-state'
import {SubscribingComponent} from './modules/shared/components/subscribing.component'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from './modules/shared/service/error.service'
@Component({
selector: 'cre-root',
@ -17,21 +17,20 @@ export class AppComponent extends SubscribingComponent {
constructor(
@Inject(PLATFORM_ID) private platformId: object,
private appState: AppState,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
this.isOnline = isPlatformBrowser(this.platformId)
super.ngOnInit();
super.ngOnInit()
this.subscribe(
this.appState.serverOnline$,
{
next: online => this.isServerOnline = online
}
online => this.isServerOnline = online
)
}

View File

@ -4,9 +4,6 @@
<mat-card-title>Connexion au système</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="invalidCredentials" class="alert alert-danger">
<p>Les identifiants entrés sont invalides.</p>
</div>
<mat-form-field>
<mat-label>Numéro d'employé</mat-label>
<input matInput [formControl]="idFormControl" type="text"/>

View File

@ -1,28 +1,38 @@
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {AccountService} from "../../services/account.service";
import {Router} from "@angular/router";
import {Component, OnInit} from '@angular/core'
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'
import {AccountService} from '../../services/account.service'
import {Router} from '@angular/router'
import {ErrorHandler, ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.sass']
})
export class LoginComponent implements OnInit {
export class LoginComponent implements ErrorHandler, OnInit {
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 401,
messageProducer: () => 'Les identifiants entrés sont invalides.'
}, {
filter: error => error.status === 404 || error.status === 403,
consumer: () => console.warn('No default user is defined on this computer')
}]
form: FormGroup
idFormControl: FormControl
passwordFormControl: FormControl
invalidCredentials = false
constructor(
private formBuilder: FormBuilder,
private accountService: AccountService,
private errorService: ErrorService,
private router: Router
) {
}
ngOnInit(): void {
this.errorService.activeErrorHandler = this
if (this.accountService.isLoggedIn()) {
this.router.navigate(['/color'])
}
@ -39,8 +49,7 @@ export class LoginComponent implements OnInit {
this.accountService.login(
this.idFormControl.value,
this.passwordFormControl.value,
() => this.router.navigate(["/color"]),
err => this.invalidCredentials = err.status === 401
() => this.router.navigate(['/color'])
)
}
}

View File

@ -6,6 +6,7 @@ import {HttpClient, HttpResponse} from '@angular/common/http'
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'
@Injectable({
providedIn: 'root'
@ -16,7 +17,8 @@ export class AccountService implements OnDestroy {
constructor(
private http: HttpClient,
private api: ApiService,
private appState: AppState
private appState: AppState,
private errorService: ErrorService
) {
}
@ -38,24 +40,12 @@ export class AccountService implements OnDestroy {
takeUntil(this.destroy$),
).subscribe({
next: employee => this.appState.authenticatedEmployee = employee,
error: err => {
if (err.status === 0 && err.statusText === 'Unknown Error' || err.status === 502) {
this.appState.isServerOnline = false
} else {
this.appState.isServerOnline = true
if (err.status === 404 || err.status === 403) {
console.error('No default user is defined on this computer')
} else {
console.error('An error occurred while authenticating the default user')
console.error(err)
}
}
}
error: err => this.errorService.handleError(err)
})
}
}
login(id: number, password: string, success: () => void, error: (err) => void) {
login(id: number, password: string, success: () => void) {
const loginForm = {id, password}
this.http.post<any>(`${environment.apiUrl}/login`, loginForm, {
withCredentials: true,
@ -72,14 +62,7 @@ export class AccountService implements OnDestroy {
this.setLoggedInEmployeeFromApi()
success()
},
error: err => {
if (err.status === 0 && err.statusText === 'Unknown Error' || err.status === 502) {
this.appState.isServerOnline = false
} else {
this.appState.isServerOnline = true
error(err)
}
}
error: err => this.errorService.handleError(err)
})
}
@ -96,7 +79,7 @@ export class AccountService implements OnDestroy {
this.checkAuthenticationStatus()
success()
},
error: err => console.error(err)
error: err => this.errorService.handleError(err)
})
}
@ -112,10 +95,7 @@ export class AccountService implements OnDestroy {
)
.subscribe({
next: employee => this.appState.authenticatedEmployee = employee,
error: err => {
console.error('Could not get the logged in employee from the API: ')
console.error(err)
}
error: err => this.errorService.handleError(err)
})
}
}

View File

@ -1,10 +1,11 @@
import {Component, Input} from '@angular/core';
import {Recipe} from "../../../shared/model/recipe.model";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {ActivatedRoute, Router} from "@angular/router";
import {Observable} from "rxjs";
import {RecipeImageService} from "../../services/recipe-image.service";
import {environment} from "../../../../../environments/environment";
import {Component, Input} from '@angular/core'
import {Recipe} from '../../../shared/model/recipe.model'
import {SubscribingComponent} from '../../../shared/components/subscribing.component'
import {ActivatedRoute, Router} from '@angular/router'
import {Observable} from 'rxjs'
import {RecipeImageService} from '../../services/recipe-image.service'
import {environment} from '../../../../../environments/environment'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-images-editor',
@ -20,10 +21,11 @@ export class ImagesEditorComponent extends SubscribingComponent {
constructor(
private recipeImageService: RecipeImageService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
@ -36,7 +38,7 @@ export class ImagesEditorComponent extends SubscribingComponent {
const image = event.target.files[0]
this.subscribe(
this.recipeImageService.save(image, this.recipe.id),
{next: () => this.loadImagesIds()}
() => this.loadImagesIds()
)
}
@ -47,7 +49,7 @@ export class ImagesEditorComponent extends SubscribingComponent {
delete(imageId: number) {
this.subscribe(
this.recipeImageService.delete(imageId, this.recipe.id),
{next: () => this.loadImagesIds()}
() => this.loadImagesIds()
)
}

View File

@ -1,20 +1,21 @@
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
import {Mix, MixMaterial, Recipe} from "../../../shared/model/recipe.model";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {MixService} from "../../services/mix.service";
import {Observable} from "rxjs";
import {RecipeService} from "../../services/recipe.service";
import {Material} from "../../../shared/model/material.model";
import {MaterialService} from "../../../material/service/material.service";
import {MaterialType} from "../../../shared/model/materialtype.model";
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {UNIT_MILLILITER} from "../../../shared/units";
import {MatTable} from "@angular/material/table";
import {ActivatedRoute, Router} from "@angular/router";
import {ConfirmBoxComponent} from "../../../shared/components/confirm-box/confirm-box.component";
import {AccountService} from "../../../accounts/services/account.service";
import {EmployeePermission} from "../../../shared/model/employee";
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
import {Mix, MixMaterial, Recipe} from '../../../shared/model/recipe.model'
import {SubscribingComponent} from '../../../shared/components/subscribing.component'
import {MixService} from '../../services/mix.service'
import {Observable} from 'rxjs'
import {RecipeService} from '../../services/recipe.service'
import {Material} from '../../../shared/model/material.model'
import {MaterialService} from '../../../material/service/material.service'
import {MaterialType} from '../../../shared/model/materialtype.model'
import {MaterialTypeService} from '../../../material-type/service/material-type.service'
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'
import {UNIT_MILLILITER} from '../../../shared/units'
import {MatTable} from '@angular/material/table'
import {ActivatedRoute, Router} from '@angular/router'
import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confirm-box.component'
import {AccountService} from '../../../accounts/services/account.service'
import {EmployeePermission} from '../../../shared/model/employee'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-mix-editor',
@ -29,7 +30,7 @@ export class MixEditorComponent extends SubscribingComponent {
@Input() recipeId: number | null
@Input() materials: Material[]
@Output() save = new EventEmitter<any>();
@Output() save = new EventEmitter<any>()
mix: Mix | null
recipe: Recipe | null
@ -52,43 +53,43 @@ export class MixEditorComponent extends SubscribingComponent {
private materialTypeService: MaterialTypeService,
private accountService: AccountService,
private formBuilder: FormBuilder,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit();
super.ngOnInit()
this.mixId = this.urlUtils.parseIntUrlParam('id')
if (this.mixId) {
this.editionMode = true
}
this.subscribe(
this.recipeService.getById(this.recipeId),
{
next: r => {
this.recipe = r
if (this.editionMode) {
this.subscribe(
this.mixService.getById(this.mixId),
{
next: m => {
this.mix = m
this.mixMaterials = this.mixService.extractMixMaterials(this.mix)
this.generateForm()
}, error: err => this.handleNotFoundError(err, '/color/list')
}
)
} else {
this.mixMaterials.push({})
this.generateForm()
}
},
error: err => this.handleNotFoundError(err, '/color/list')
}
this.subscribeEntityById(
this.recipeService,
this.recipeId,
r => {
this.recipe = r
if (this.editionMode) {
this.subscribeEntityById(
this.mixService,
this.mixId,
m => {
this.mix = m
this.mixMaterials = this.mixService.extractMixMaterials(this.mix)
this.generateForm()
},
'/color/list'
)
} else {
this.mixMaterials.push({})
this.generateForm()
}
},
'/color/list'
)
this.materialTypes$ = this.materialTypeService.all
}
@ -118,8 +119,10 @@ export class MixEditorComponent extends SubscribingComponent {
}
materialUsePercentages(mixMaterial: any) {
if (!mixMaterial.materialId) return null
const material = this.getMaterialFromId(mixMaterial.materialId);
if (!mixMaterial.materialId) {
return null
}
const material = this.getMaterialFromId(mixMaterial.materialId)
mixMaterial.percents = material && material.materialType.usePercentages
return mixMaterial.percents
}

View File

@ -1,11 +1,13 @@
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
import {Mix, MixMaterial, Recipe} from "../../../shared/model/recipe.model";
import {Subject} from "rxjs";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {convertMixMaterialQuantity, UNIT_MILLILITER} from "../../../shared/units";
import {ActivatedRoute, Router} from "@angular/router";
import {PtouchPrinter} from "../../ptouchPrint"
import {ConfirmBoxComponent} from "../../../shared/components/confirm-box/confirm-box.component";
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
import {Mix, MixMaterial, Recipe} from '../../../shared/model/recipe.model'
import {Subject} from 'rxjs'
import {SubscribingComponent} from '../../../shared/components/subscribing.component'
import {convertMixMaterialQuantity, UNIT_MILLILITER} from '../../../shared/units'
import {ActivatedRoute, Router} from '@angular/router'
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'
@Component({
selector: 'cre-mix-table',
@ -23,7 +25,6 @@ export class MixTableComponent extends SubscribingComponent {
@Input() units$: Subject<string>
@Input() deductErrorBody
@Input() editionMode: boolean
@Input() printingError = 2
@Output() locationChange = new EventEmitter<{ id: number, location: string }>()
@Output() quantityChange = new EventEmitter<{ id: number, materialId: number, quantity: number }>()
@Output() deduct = new EventEmitter<void>()
@ -37,14 +38,16 @@ export class MixTableComponent extends SubscribingComponent {
printer: PtouchPrinter | null
constructor(
private alertService: AlertService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit();
super.ngOnInit()
if (this.editionMode) {
this.mixColumns = this.COLUMNS_STATIC
@ -58,9 +61,7 @@ export class MixTableComponent extends SubscribingComponent {
this.subscribe(
this.units$,
{
next: u => this.convertQuantities(u)
}
u => this.convertQuantities(u)
)
}
@ -90,7 +91,9 @@ export class MixTableComponent extends SubscribingComponent {
}
getTotalQuantity(index: number = -1): number {
if (index === -1) index = this.computedQuantities.length - 1
if (index === -1) {
index = this.computedQuantities.length - 1
}
let totalQuantity = 0
for (let i = 0; i <= index; i++) {
totalQuantity += this.calculateQuantity(i)
@ -121,16 +124,32 @@ export class MixTableComponent extends SubscribingComponent {
return
}
this.printer = new PtouchPrinter({
template: "Couleur",
template: 'Couleur',
lines: [
{name: "color", value: this.recipe.name},
{name: "banner", value: this.recipe.company.name},
{name: "base", value: base.name},
{name: "description", value: this.recipe.description}
{name: 'color', value: this.recipe.name},
{name: 'banner', value: this.recipe.company.name},
{name: 'base', value: base.name},
{name: 'description', value: this.recipe.description}
]
})
const errorCode = await this.printer.print()
this.printingErrorChange.emit(errorCode)
const exitCode = await this.printer.print()
switch (exitCode) {
case PrintingExitCode.PrintingInProgress:
this.alertService.pushSuccess('Impression en cours. Cette opération peut prendre quelques secondes')
break
case PrintingExitCode.ExtensionNotInstalled:
this.alertService.pushError('L\'extension b-Pac n\'est pas installée')
break
case PrintingExitCode.NoBaseFound:
this.alertService.pushError('Il n\'y a pas de base dans ce mélange')
break
case PrintingExitCode.GenericPrintingError:
this.alertService.pushError('Une erreur est survenue durant l\'impression')
break
default:
this.errorService.pushUnknownError()
break
}
}
private emitQuantityChangeEvent(index: number) {
@ -158,3 +177,11 @@ export class MixTableComponent extends SubscribingComponent {
return this.computedQuantities[0].quantity * (computedQuantity.quantity / 100)
}
}
enum PrintingExitCode {
PrintingInProgress = 1,
ExtensionNotInstalled = -1,
NoBaseFound = 98,
GenericPrintingError = 99
}

View File

@ -13,8 +13,7 @@
[editionMode]="editionMode"
(quantityChange)="quantityChange.emit($event)"
(locationChange)="locationChange.emit($event)"
(deduct)="deduct.emit(mix.id)"
[(printingError)]="printingError">>
(deduct)="deduct.emit(mix.id)">
</cre-mix-table>
</ng-container>
</mat-card-content>

View File

@ -11,7 +11,6 @@ export class MixesCardComponent {
@Input() recipe: Recipe
@Input() units$: Subject<string>
@Input() deductErrorBody: any
@Input() printingError = 2
@Input() editionMode = false
@Output() locationChange = new EventEmitter<{ id: number, location: string }>()

View File

@ -1,8 +1,6 @@
<cre-entity-add
title="Création d'une recette"
backButtonLink="/color/list"
[unknownError]="unknownError"
[customError]="errorMessage"
[formFields]="formFields"
(submit)="submit($event)">
</cre-entity-add>

View File

@ -1,18 +1,19 @@
import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {RecipeService} from "../../services/recipe.service";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {FormBuilder, Validators} from "@angular/forms";
import {CompanyService} from "../../../company/service/company.service";
import {map} from "rxjs/operators";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {RecipeService} from '../../services/recipe.service'
import {FormField} from '../../../shared/components/entity-add/entity-add.component'
import {Validators} from '@angular/forms'
import {CompanyService} from '../../../company/service/company.service'
import {map} from 'rxjs/operators'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.sass']
})
export class AddComponent extends SubscribingComponent {
export class AddComponent extends ErrorHandlingComponent {
formFields: FormField[] = [
{
name: 'name',
@ -71,28 +72,21 @@ export class AddComponent extends SubscribingComponent {
})))
}
]
unknownError = false
errorMessage: string | null
constructor(
private recipeService: RecipeService,
private companyService: CompanyService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
submit(values) {
this.subscribe(
this.recipeService.save(values.name, values.description, values.sample, values.approbationDate, values.remark, values.company),
{
next: recipe => this.urlUtils.navigateTo(`/color/edit/${recipe.id}`),
error: err => {
this.unknownError = true
console.error(err)
}
}
recipe => this.urlUtils.navigateTo(`/color/edit/${recipe.id}`)
)
}
}

View File

@ -1,11 +1,4 @@
<div *ngIf="recipe">
<div *ngIf="!recipe.mixes" class="alert alert-warning m-3">
<p>Il n'y a aucun mélange dans cette recette</p>
</div>
<div *ngIf="recipe.steps.length <= 0" class="alert alert-warning m-3">
<p>Il n'y a aucune étape dans cette recette</p>
</div>
<div class="action-bar backward">
<div class="d-flex flex-column">
<div class="mt-1 pb-2">
@ -33,8 +26,6 @@
deleteConfirmMessage="Voulez-vous vraiment supprimer la couleur {{recipe.name}}?"
[entity]="recipe"
[formFields]="formFields"
[unknownError]="unknownError"
[customError]="errorMessage"
[disableButtons]="true"
[noTopMargin]="true">
</cre-entity-edit>

View File

@ -1,22 +1,24 @@
import {Component, ViewChild} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Recipe} from "../../../shared/model/recipe.model";
import {RecipeService} from "../../services/recipe.service";
import {ActivatedRoute, Router} from "@angular/router";
import {Validators} from "@angular/forms";
import {Subject} from "rxjs";
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
import {AccountService} from "../../../accounts/services/account.service";
import {EmployeePermission} from "../../../shared/model/employee";
import {EntityEditComponent} from "../../../shared/components/entity-edit/entity-edit.component";
import {ImagesEditorComponent} from "../../components/images-editor/images-editor.component";
import {Component, ViewChild} from '@angular/core'
import {ErrorHandlingComponent, SubscribingComponent} from '../../../shared/components/subscribing.component'
import {Recipe} from '../../../shared/model/recipe.model'
import {RecipeService} from '../../services/recipe.service'
import {ActivatedRoute, Router} from '@angular/router'
import {Validators} from '@angular/forms'
import {Subject} from 'rxjs'
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from '../../../shared/units'
import {AccountService} from '../../../accounts/services/account.service'
import {EmployeePermission} from '../../../shared/model/employee'
import {EntityEditComponent} from '../../../shared/components/entity-edit/entity-edit.component'
import {ImagesEditorComponent} from '../../components/images-editor/images-editor.component'
import {ErrorHandler, ErrorModel, ErrorService} from '../../../shared/service/error.service'
import {AlertService} from '../../../shared/service/alert.service'
@Component({
selector: 'cre-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.sass']
})
export class EditComponent extends SubscribingComponent {
export class EditComponent extends ErrorHandlingComponent {
readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
@ViewChild('imagesEditor') imagesEditor: ImagesEditorComponent
@ -75,36 +77,41 @@ export class EditComponent extends SubscribingComponent {
valueFn: recipe => recipe.company.name,
}
]
unknownError = false
errorMessage: string | null
units$ = new Subject<string>()
submittedValues: any | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409,
messageProducer: () => `Une couleur avec le nom '${this.submittedValues.name}' et la bannière '${this.recipe.company.name}' existe déjà`
}]
constructor(
private recipeService: RecipeService,
private accountService: AccountService,
private alertService: AlertService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit();
super.ngOnInit()
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribe(
this.recipeService.getById(id),
{
next: recipe => this.recipe = recipe,
error: err => {
if (err.status === 404) {
this.router.navigate(['/color/list'])
} else {
this.unknownError = true
}
this.subscribeEntityById(
this.recipeService,
parseInt(this.activatedRoute.snapshot.paramMap.get('id')),
recipe => {
this.recipe = recipe
if (this.recipe.mixes.length == 0) {
this.alertService.pushWarning('Il n\'y a aucun mélange dans cette recette')
}
if (this.recipe.steps.length == 0) {
this.alertService.pushWarning('Il n\'y a aucune étape dans cette recette')
}
},
1
'/color/list'
)
}
@ -114,28 +121,17 @@ export class EditComponent extends SubscribingComponent {
submit(editComponent: EntityEditComponent) {
const values = editComponent.values
this.subscribe(
this.submittedValues = values
this.subscribeAndNavigate(
this.recipeService.update(this.recipe.id, values.name, values.description, values.sample, values.approbationDate, values.remark, this.recipe.steps),
{
next: () => this.router.navigate(['/color/list']),
error: err => {
if (err.status === 409) {
this.errorMessage = `Une couleur avec le nom '${values.name}' et la bannière '${this.recipe.company.name}' existe déjà`
} else {
this.unknownError = true
console.error(err)
}
}
}
'/color/list'
)
}
delete() {
this.subscribe(
this.subscribeAndNavigate(
this.recipeService.delete(this.recipe.id),
{
next: () => this.router.navigate(['/color/list'])
}
'/color/list'
)
}

View File

@ -1,26 +1,6 @@
<div *ngIf="recipe">
<cre-recipe-info [recipe]="recipe" [hasModifications]="hasModifications"></cre-recipe-info>
<div *ngIf="error" class="alert alert-danger m-3">
<p *ngIf="error === ERROR_UNKNOWN">Une erreur est survenue</p>
<p *ngIf="error === ERROR_DEDUCT">Certains produit ne sont pas en quantité suffisante dans l'inventaire</p>
</div>
<div *ngIf="printingError != 2 && printingError != 1" class="alert alert-danger m-3">
<p *ngIf="printingError === -1">L'extension b-Pac n'est pas installée</p>
<p *ngIf="printingError === 98">Il n'y a pas de base dans ce mélange</p>
<p *ngIf="printingError === 99">Une erreur est survenue pendant l'impression</p>
</div>
<div *ngIf="success" class="alert alert-success m-3">
<p *ngIf="success === SUCCESS_SAVE">Les modifications ont été enregistrées</p>
<p *ngIf="success === SUCCESS_DEDUCT">Les quantités des produits utilisés ont été déduites de l'inventaire</p>
</div>
<div *ngIf="printingError === 1" class="alert alert-success m-3">
<p>Impression en cours. Cette opération peut prendre quelques secondes.</p>
</div>
<div class="action-bar backward d-flex flex-row">
<div class="d-flex flex-column">
<div class="mt-1 pb-2">
@ -51,8 +31,7 @@
[units$]="units$"
(quantityChange)="changeQuantity($event)"
(locationChange)="changeMixLocation($event)"
(deduct)="deductMixQuantities($event)"
[(printingError)]="printingError">
(deduct)="deductMixQuantities($event)">
</cre-mixes-card>
</div>

View File

@ -1,63 +1,65 @@
import {Component} from '@angular/core';
import {RecipeService} from "../../services/recipe.service";
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Recipe} from "../../../shared/model/recipe.model";
import {Observable, Subject} from "rxjs";
import {UNIT_GALLON, UNIT_LITER, UNIT_MILLILITER} from "../../../shared/units";
import {Component} from '@angular/core'
import {RecipeService} from '../../services/recipe.service'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {Recipe} from '../../../shared/model/recipe.model'
import {Observable, Subject} from 'rxjs'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
import {AlertService} from '../../../shared/service/alert.service'
import {GlobalAlertHandlerComponent} from '../../../shared/components/global-alert-handler/global-alert-handler.component'
@Component({
selector: 'cre-explore',
templateUrl: './explore.component.html',
styleUrls: ['./explore.component.sass']
})
export class ExploreComponent extends SubscribingComponent {
readonly unitConstants = {UNIT_MILLILITER, UNIT_LITER, UNIT_GALLON}
export class ExploreComponent extends ErrorHandlingComponent {
recipe: Recipe | null
error: string
deductErrorBody = {}
success: string
units$ = new Subject<string>()
hasModifications = false
note: string | null
quantitiesChanges = new Map<number, Map<number, number>>()
mixesLocationChanges = new Map<number, string>()
printingError = 2
// Errors
readonly ERROR_UNKNOWN = 'unknown'
readonly ERROR_DEDUCT = 'deduct'
// Success
readonly SUCCESS_SAVE = 'save'
readonly SUCCESS_DEDUCT = 'deduct'
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409,
consumer: error => this.deductErrorBody = error.error,
messageProducer: () => 'Certains produit ne sont pas en quantité suffisante dans l\'inventaire'
}]
constructor(
private recipeService: RecipeService,
private alertService: AlertService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
super.ngOnInit()
GlobalAlertHandlerComponent.extraTopMarginMultiplier = 2.5
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribe(
this.recipeService.getById(id),
{
next: r => {
this.recipe = r
this.note = r.note
},
error: err => this.handleNotFoundError(err, '/colors/list')
this.subscribeEntityById(
this.recipeService,
id,
r => {
this.recipe = r
this.note = r.note
},
1
'/colors/list'
)
}
ngOnDestroy() {
super.ngOnDestroy();
GlobalAlertHandlerComponent.extraTopMarginMultiplier = 0
}
changeUnits(unit: string) {
this.units$.next(unit)
}
@ -68,7 +70,9 @@ export class ExploreComponent extends SubscribingComponent {
}
changeQuantity(event: { id: number, materialId: number, quantity: number }) {
if (!this.quantitiesChanges.has(event.id)) this.quantitiesChanges.set(event.id, new Map<number, number>())
if (!this.quantitiesChanges.has(event.id)) {
this.quantitiesChanges.set(event.id, new Map<number, number>())
}
this.quantitiesChanges.get(event.id).set(event.materialId, event.quantity)
}
@ -80,19 +84,7 @@ export class ExploreComponent extends SubscribingComponent {
saveModifications() {
this.subscribe(
this.recipeService.saveExplorerModifications(this.recipe.id, this.note, this.mixesLocationChanges),
{
next: () => {
this.hasModifications = false
this.error = null
this.success = this.SUCCESS_SAVE
},
error: err => {
this.success = null
this.error = this.ERROR_UNKNOWN
console.error(err)
}
},
1
() => this.alertService.pushSuccess('Les modifications ont été enregistrées')
)
}
@ -107,23 +99,7 @@ export class ExploreComponent extends SubscribingComponent {
performDeductQuantities(observable: Observable<void>) {
this.subscribe(
observable,
{
next: () => {
this.error = null
this.success = this.SUCCESS_DEDUCT
},
error: err => {
this.success = null
if (err.status === 409) { // There is not enough of one or more materials in the inventory
this.error = this.ERROR_DEDUCT
this.deductErrorBody = err.error
} else {
this.error = this.ERROR_UNKNOWN
console.error(err)
}
}
},
1
() => this.alertService.pushSuccess('Les quantités des produits utilisés ont été déduites de l\'inventaire')
)
}
}

View File

@ -1,30 +1,34 @@
import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {RecipeService} from "../../services/recipe.service";
import {EmployeePermission} from "../../../shared/model/employee";
import {AccountService} from "../../../accounts/services/account.service";
import {Recipe} from "../../../shared/model/recipe.model";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {RecipeService} from '../../services/recipe.service'
import {EmployeePermission} from '../../../shared/model/employee'
import {AccountService} from '../../../accounts/services/account.service'
import {Recipe} from '../../../shared/model/recipe.model'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.sass']
})
export class ListComponent extends SubscribingComponent {
export class ListComponent extends ErrorHandlingComponent {
recipes$ = this.recipeService.allSortedByCompany
tableCols = ['name', 'description', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit']
searchQuery = ""
panelForcedExpanded = false
recipesHidden = []
handledErrorModels: ErrorModel[]
constructor(
private recipeService: RecipeService,
private accountService: AccountService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {

View File

@ -1,26 +1,30 @@
import {Component} from '@angular/core';
import {Material} from "../../../../shared/model/material.model";
import {MaterialService} from "../../../../material/service/material.service";
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../../shared/components/subscribing.component";
import {MixService} from "../../../services/mix.service";
import {Component} from '@angular/core'
import {Material} from '../../../../shared/model/material.model'
import {MaterialService} from '../../../../material/service/material.service'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent, SubscribingComponent} from '../../../../shared/components/subscribing.component'
import {MixService} from '../../../services/mix.service'
import {ErrorHandler, ErrorModel, ErrorService} from '../../../../shared/service/error.service'
@Component({
selector: 'cre-mix-add',
templateUrl: './mix-add.component.html',
styleUrls: ['./mix-add.component.sass']
})
export class MixAddComponent extends SubscribingComponent {
export class MixAddComponent extends ErrorHandlingComponent {
recipeId: number | null
materials: Material[] | null
handledErrorModels: ErrorModel[]
constructor(
private materialService: MaterialService,
private mixService: MixService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
@ -30,14 +34,14 @@ export class MixAddComponent extends SubscribingComponent {
this.subscribe(
this.materialService.getAllForMixCreation(this.recipeId),
{next: m => this.materials = m}
m => this.materials = m
)
}
submit(values) {
this.subscribe(
this.subscribeAndNavigate(
this.mixService.saveWithUnits(values.name, values.recipeId, values.materialTypeId, values.mixMaterials, values.units),
{next: () => this.urlUtils.navigateTo(`/color/edit/${this.recipeId}`)}
`/color/edit/${this.recipeId}`
)
}
}

View File

@ -1,27 +1,31 @@
import {Component} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../../shared/components/subscribing.component";
import {Material} from "../../../../shared/model/material.model";
import {MaterialService} from "../../../../material/service/material.service";
import {MixService} from "../../../services/mix.service";
import {Component} from '@angular/core'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent} from '../../../../shared/components/subscribing.component'
import {Material} from '../../../../shared/model/material.model'
import {MaterialService} from '../../../../material/service/material.service'
import {MixService} from '../../../services/mix.service'
import {ErrorModel, ErrorService} from '../../../../shared/service/error.service'
@Component({
selector: 'cre-mix-edit',
templateUrl: './mix-edit.component.html',
styleUrls: ['./mix-edit.component.sass']
})
export class MixEditComponent extends SubscribingComponent {
export class MixEditComponent extends ErrorHandlingComponent {
mixId: number | null
recipeId: number | null
materials: Material[] | null
handledErrorModels: ErrorModel[]
constructor(
private materialService: MaterialService,
private mixService: MixService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
@ -32,14 +36,14 @@ export class MixEditComponent extends SubscribingComponent {
this.subscribe(
this.materialService.getAllForMixUpdate(this.mixId),
{next: m => this.materials = m}
m => this.materials = m
)
}
submit(values) {
this.subscribe(
this.subscribeAndNavigate(
this.mixService.updateWithUnits(this.mixId, values.name, values.materialTypeId, values.mixMaterials, values.units),
{next: () => this.urlUtils.navigateTo(`/color/edit/${this.recipeId}`)}
`/color/edit/${this.recipeId}`
)
}
}

View File

@ -1,8 +1,6 @@
<cre-entity-add
title="Création d'une bannière"
backButtonLink="/catalog/company/list"
[unknownError]="unknownError"
[customError]="errorMessage"
[formFields]="formFields"
(submit)="submit($event)">
</cre-entity-add>

View File

@ -1,16 +1,17 @@
import {Component} from '@angular/core';
import {CompanyService} from "../../service/company.service";
import {FormBuilder, Validators} from "@angular/forms";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {ActivatedRoute, Router} from "@angular/router";
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'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.sass']
})
export class AddComponent extends SubscribingComponent {
export class AddComponent extends ErrorHandlingComponent {
formFields: FormField[] = [
{
name: 'name',
@ -23,31 +24,31 @@ export class AddComponent extends SubscribingComponent {
]
}
]
unknownError = false
errorMessage: string | null
submittedValues: any | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409,
messageProducer: () => `Une bannière avec le nom '${this.submittedValues.name}' existe déjà`
}]
constructor(
private companyService: CompanyService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit()
}
submit(values) {
this.subscribe(
this.submittedValues = values
this.subscribeAndNavigate(
this.companyService.save(values.name),
{
next: () => this.router.navigate(['/catalog/company/list']),
error: err => {
if (err.status === 409) {
this.errorMessage = `Une bannière avec le nom '${values.name}' existe déjà`
} else {
this.unknownError = true
console.log(err)
}
}
}
'/catalog/company/list'
)
}
}

View File

@ -6,8 +6,6 @@
deletePermission="REMOVE_COMPANY"
[entity]="company"
[formFields]="formFields"
[unknownError]="unknownError"
[customError]="errorMessage"
(submit)="submit($event)"
(delete)="delete()">
</cre-entity-edit>

View File

@ -1,17 +1,18 @@
import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Company} from "../../../shared/model/company.model";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {FormBuilder, Validators} from "@angular/forms";
import {CompanyService} from "../../service/company.service";
import {ActivatedRoute, Router} from "@angular/router";
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'
@Component({
selector: 'cre-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.sass']
})
export class EditComponent extends SubscribingComponent {
export class EditComponent extends ErrorHandlingComponent {
company: Company | null
formFields: FormField[] = [
{
@ -25,64 +26,44 @@ export class EditComponent extends SubscribingComponent {
]
}
]
unknownError = false
errorMessage: string | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409,
messageProducer: error => `Une bannière avec le nom '${error.error.id}' existe déjà`
}]
constructor(
private companyService: CompanyService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
super.ngOnInit()
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribe(
this.companyService.getById(id),
{
next: company => this.company = company,
error: err => {
if (err.status == 404) {
this.router.navigate(['/catalog/company/list'])
} else {
this.unknownError = true
}
}
},
1
this.subscribeEntityById(
this.companyService,
id,
company => this.company = company,
'/catalog/company/list'
)
}
submit(values) {
this.subscribe(
this.subscribeAndNavigate(
this.companyService.update(this.company.id, values.name),
{
next: () => this.router.navigate(['/catalog/company/list']),
error: err => {
if (err.status == 409) {
this.errorMessage = `Une bannière avec le nom '${values.name}' existe déjà`
} else {
this.unknownError = true
}
console.log(err)
}
}
'/catalog/company/list'
)
}
delete() {
this.subscribe(
this.subscribeAndNavigate(
this.companyService.delete(this.company.id),
{
next: () => this.router.navigate(['/catalog/company/list']),
error: err => {
this.unknownError = true
console.log(err)
}
}
'/catalog/company/list'
)
}
}

View File

@ -1,16 +1,16 @@
import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {CompanyService} from "../../service/company.service";
import {EmployeePermission} from "../../../shared/model/employee";
import {FormBuilder} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {CompanyService} from '../../service/company.service'
import {EmployeePermission} from '../../../shared/model/employee'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.sass']
})
export class ListComponent extends SubscribingComponent {
export class ListComponent extends ErrorHandlingComponent {
companies$ = this.companyService.all
columns = [
{def: 'name', title: 'Nom', valueFn: c => c.name}
@ -23,9 +23,10 @@ export class ListComponent extends SubscribingComponent {
constructor(
private companyService: CompanyService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
}

View File

@ -3,10 +3,6 @@
<mat-card-title>Création d'un employé</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="unknownError" class="alert alert-danger">
<p>Une erreur est survenue</p>
</div>
<form [formGroup]="form">
<mat-form-field>
<mat-label>Numéro d'employé</mat-label>

View File

@ -1,20 +1,20 @@
import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {PermissionsFieldComponent} from "../../../shared/components/permissions-field/permissions-field.component";
import {EmployeeGroup} from "../../../shared/model/employee";
import {Observable, Subject} from "rxjs";
import {GroupService} from "../../../groups/services/group.service";
import {take, takeUntil} from "rxjs/operators";
import {EmployeeService} from "../../services/employee.service";
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
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 {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'
@Component({
selector: 'cre-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.sass']
})
export class AddComponent extends SubscribingComponent {
export class AddComponent extends ErrorHandlingComponent {
@ViewChild('permissionsField', {static: true}) permissionsField: PermissionsFieldComponent
form: FormGroup
@ -23,17 +23,17 @@ export class AddComponent extends SubscribingComponent {
lastNameControl: FormControl
passwordControl: FormControl
groupControl: FormControl
unknownError = false
group$: Observable<EmployeeGroup[]> | null
constructor(
private employeeService: EmployeeService,
private groupService: GroupService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
@ -57,7 +57,7 @@ export class AddComponent extends SubscribingComponent {
submit() {
if (this.permissionsField.valid() && this.form.valid) {
this.subscribe(
this.subscribeAndNavigate(
this.employeeService.save(
parseInt(this.idControl.value),
this.firstNameControl.value,
@ -66,13 +66,7 @@ export class AddComponent extends SubscribingComponent {
this.groupControl.value,
this.permissionsField.allEnabledPermissions
),
{
next: () => this.router.navigate(['/employee/list']),
error: err => {
console.error(err)
this.unknownError = true
}
}
'/employee/list'
)
}
}

View File

@ -3,10 +3,6 @@
<mat-card-title>Modification de l'employé #{{employee.id}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="unknownError" class="alert alert-danger">
<p>Une erreur est survenue</p>
</div>
<form [formGroup]="form">
<mat-form-field>
<mat-label>Numéro d'employé</mat-label>

View File

@ -1,22 +1,22 @@
import {Component} from '@angular/core';
import {PermissionsFieldComponent} from "../../../shared/components/permissions-field/permissions-field.component";
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
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 {AccountService} from "../../../accounts/services/account.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Component} from '@angular/core'
import {PermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component'
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'
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 {AccountService} from '../../../accounts/services/account.service'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.sass']
})
export class EditComponent extends SubscribingComponent {
export class EditComponent extends ErrorHandlingComponent {
employee: Employee | null
unknownError = false
group$: Observable<EmployeeGroup[]> | null
@ -30,27 +30,20 @@ export class EditComponent extends SubscribingComponent {
private employeeService: EmployeeService,
private groupService: GroupService,
private formBuilder: FormBuilder,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
const employeeId = this.activatedRoute.snapshot.paramMap.get("id")
this.subscribe(
this.employeeService.get(parseInt(employeeId)),
{
next: employee => this.employee = employee,
error: err => {
if (err.status === 404) {
this.router.navigate(['/employee/list'])
} else {
this.unknownError = true
}
}
},
1
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribeEntityById(
this.employeeService,
id,
employee => this.employee = employee,
'/employee/list'
)
this.group$ = this.groupService.all
@ -65,40 +58,22 @@ export class EditComponent extends SubscribingComponent {
this.lastNameControl.value,
permissionsField.allEnabledPermissions
),
{
next: () => {
const group = parseInt(this._groupControl.value)
if (!isNaN(group)) {
this.subscribe(
this.groupService.addEmployeeToGroup(group, this.employee),
{
next: () => this.router.navigate(['/employee/list']),
error: err => {
console.error(err)
this.unknownError = true
}
}
() => {
const group = parseInt(this._groupControl.value)
if (!isNaN(group)) {
this.subscribeAndNavigate(
this.groupService.addEmployeeToGroup(group, this.employee),
'/employee/list'
)
} else {
if (this.employee.group) {
this.subscribeAndNavigate(
this.groupService.removeEmployeeFromGroup(this.employee),
'/employee/list'
)
} else {
if (this.employee.group) {
this.subscribe(
this.groupService.removeEmployeeFromGroup(this.employee),
{
next: () => this.router.navigate(['/employee/list']),
error: err => {
console.error(err)
this.unknownError = true
}
}
)
} else {
this.router.navigate(['/employee/list'])
}
this.router.navigate(['/employee/list'])
}
},
error: err => {
console.error(err)
this.unknownError = true
}
}
)
@ -106,15 +81,9 @@ export class EditComponent extends SubscribingComponent {
}
delete() {
this.subscribe(
this.subscribeAndNavigate(
this.employeeService.delete(this.employee.id),
{
next: () => this.router.navigate(['/employee/list']),
error: err => {
this.unknownError = true
console.error(err)
}
}
'/employee/list'
)
}
@ -148,7 +117,9 @@ export class EditComponent extends SubscribingComponent {
}
private lazyControl(control: FormControl, provider: () => FormControl): FormControl {
if (control) return control
if (control) {
return control
}
if (this.employee) {
return provider()
}

View File

@ -1,13 +1,13 @@
import {Component} from '@angular/core';
import {Observable} from "rxjs";
import {EmployeeService} from "../../services/employee.service";
import {Employee, EmployeePermission} from "../../../shared/model/employee";
import {takeUntil} from "rxjs/operators";
import {AccountService} from "../../../accounts/services/account.service";
import {animate, state, style, transition, trigger} from "@angular/animations";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {FormBuilder} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {Observable} from 'rxjs'
import {EmployeeService} from '../../services/employee.service'
import {Employee, EmployeePermission} from '../../../shared/model/employee'
import {takeUntil} from 'rxjs/operators'
import {AccountService} from '../../../accounts/services/account.service'
import {animate, state, style, transition, trigger} from '@angular/animations'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-employees',
@ -21,7 +21,7 @@ import {ActivatedRoute, Router} from "@angular/router";
])
]
})
export class ListComponent extends SubscribingComponent {
export class ListComponent extends ErrorHandlingComponent {
employees$: Observable<Employee[]>
columns = ['id', 'name', 'group', 'permissionCount', 'lastLogin', 'editButton', 'editPasswordButton']
@ -30,10 +30,11 @@ export class ListComponent extends SubscribingComponent {
constructor(
private employeeService: EmployeeService,
private accountService: AccountService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {

View File

@ -1,16 +1,17 @@
import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {EmployeeService} from "../../services/employee.service";
import {Employee} from "../../../shared/model/employee";
import {ActivatedRoute, Router} from "@angular/router";
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {Component} from '@angular/core'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {EmployeeService} from '../../services/employee.service'
import {Employee} from '../../../shared/model/employee'
import {ActivatedRoute, Router} from '@angular/router'
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-password-edit',
templateUrl: './password-edit.component.html',
styleUrls: ['./password-edit.component.sass']
})
export class PasswordEditComponent extends SubscribingComponent {
export class PasswordEditComponent extends ErrorHandlingComponent {
employee: Employee | null
form: FormGroup
@ -19,19 +20,20 @@ export class PasswordEditComponent extends SubscribingComponent {
constructor(
private employeeService: EmployeeService,
private formBuilder: FormBuilder,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
const employeeId = this.activatedRoute.snapshot.paramMap.get('id')
this.subscribe(
this.employeeService.get(parseInt(employeeId)),
{
next: employee => this.employee = employee
}
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribeEntityById(
this.employeeService,
id,
employee => this.employee = employee,
'/employee/list'
)
this.form = this.formBuilder.group({
@ -41,11 +43,9 @@ export class PasswordEditComponent extends SubscribingComponent {
submit() {
if (this.form.valid) {
this.subscribe(
this.subscribeAndNavigate(
this.employeeService.updatePassword(this.employee.id, this.passwordControl.value),
{
next: () => this.router.navigate(['/employee/list'])
}
'/employee/list'
)
}
}

View File

@ -16,7 +16,7 @@ export class EmployeeService {
return this.api.get<Employee[]>('/employee')
}
get(id: number): Observable<Employee> {
getById(id: number): Observable<Employee> {
return this.api.get<Employee>(`/employee/${id}`)
}

View File

@ -1,22 +1,20 @@
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {Employee, EmployeeGroup, EmployeePermission} from "../../../shared/model/employee";
import {GroupService} from "../../services/group.service";
import {Observable, Subject} from "rxjs";
import {takeUntil} from "rxjs/operators";
import {AccountService} from "../../../accounts/services/account.service";
import {Component, Input, OnInit} from '@angular/core'
import {Employee, EmployeeGroup, EmployeePermission} from '../../../shared/model/employee'
import {GroupService} from '../../services/group.service'
import {AccountService} from '../../../accounts/services/account.service'
import {Observable} from 'rxjs'
@Component({
selector: 'cre-employees-list',
templateUrl: './employees-list.component.html',
styleUrls: ['./employees-list.component.sass']
})
export class EmployeesListComponent implements OnInit, OnDestroy {
export class EmployeesListComponent implements OnInit {
@Input() group: EmployeeGroup
employees$: Observable<Employee[]> | null
columns = ['id', 'firstName', 'lastName', 'edit']
private _destroy$ = new Subject<boolean>()
constructor(
private accountService: AccountService,
private groupService: GroupService
@ -24,12 +22,7 @@ export class EmployeesListComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this.employees$ = this.groupService.getEmployeesForGroup(this.group.id).pipe(takeUntil(this._destroy$))
}
ngOnDestroy(): void {
this._destroy$.next(true)
this._destroy$.complete()
this.employees$ = this.groupService.getEmployeesForGroup(this.group.id)
}
get canEditEmployee(): boolean {

View File

@ -3,10 +3,6 @@
<mat-card-title>Création d'un groupe</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="unknownError" class="alert alert-danger">
<p>Une erreur est survenue</p>
</div>
<form [formGroup]="form">
<mat-form-field class="pb-3">
<mat-label>Nom</mat-label>

View File

@ -1,29 +1,30 @@
import {Component, ViewChild} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, 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 {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Component, ViewChild} from '@angular/core'
import {FormBuilder, FormControl, FormGroup, 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 {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.sass']
})
export class AddComponent extends SubscribingComponent {
export class AddComponent extends ErrorHandlingComponent {
@ViewChild('permissionsField', {static: true}) permissionsField: PermissionsFieldComponent
form: FormGroup
nameControl: FormControl
unknownError = false
constructor(
private formBuilder: FormBuilder,
private groupService: GroupService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
@ -35,15 +36,9 @@ export class AddComponent extends SubscribingComponent {
submit() {
if (this.form.valid && this.permissionsField.valid()) {
this.subscribe(
this.subscribeAndNavigate(
this.groupService.save(this.nameControl.value, this.permissionsField.allEnabledPermissions),
{
next: () => this.router.navigate(['/group/list']),
error: err => {
this.unknownError = true
console.log(err)
}
}
'/group/list'
)
}
}

View File

@ -3,10 +3,6 @@
<mat-card-title>Modifier le groupe {{group.name}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="unknownError" class="alert alert-danger">
<p>Une erreur est survenue</p>
</div>
<form [formGroup]="form">
<mat-form-field>
<mat-label>Nom</mat-label>

View File

@ -1,22 +1,22 @@
import {Component, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {EmployeeGroup, EmployeePermission} 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 {AccountService} from "../../../accounts/services/account.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Component, ViewChild} from '@angular/core'
import {ActivatedRoute, Router} from '@angular/router'
import {EmployeeGroup, EmployeePermission} 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 {AccountService} from '../../../accounts/services/account.service'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.sass']
})
export class EditComponent extends SubscribingComponent {
export class EditComponent extends ErrorHandlingComponent {
@ViewChild('permissionsField') permissionsField: PermissionsFieldComponent
group: EmployeeGroup | null
unknownError = false
private _nameControl: FormControl
@ -24,54 +24,36 @@ export class EditComponent extends SubscribingComponent {
private accountService: AccountService,
private groupService: GroupService,
private formBuilder: FormBuilder,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
const groupId = this.activatedRoute.snapshot.paramMap.get("id")
this.subscribe(
this.groupService.get(parseInt(groupId)),
{
next: group => this.group = group,
error: err => {
if (err.status === 404) {
this.router.navigate(['/group/list'])
} else {
this.unknownError = true
}
}
}
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribeEntityById(
this.groupService,
id,
group => this.group = group,
'/group/list'
)
}
submit(): void {
if (this.form.valid && this.permissionsField.valid()) {
this.subscribe(
this.subscribeAndNavigate(
this.groupService.update(this.group.id, this.nameControl.value, this.permissionsField.allEnabledPermissions),
{
next: () => this.router.navigate(['/group/list']),
error: err => {
this.unknownError = true
console.log(err)
}
}
'/group/list'
)
}
}
delete() {
this.subscribe(
this.subscribeAndNavigate(
this.groupService.delete(this.group.id),
{
next: () => this.router.navigate(['/group/list']),
error: err => {
this.unknownError = true
console.log(err)
}
}
'/group/list'
)
}
@ -86,7 +68,9 @@ export class EditComponent extends SubscribingComponent {
}
get nameControl(): FormControl {
if (this._nameControl) return this._nameControl
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

View File

@ -1,13 +1,14 @@
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 {AccountService} from "../../../accounts/services/account.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {FormBuilder} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
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 {AccountService} from '../../../accounts/services/account.service'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
import {AlertService} from '../../../shared/service/alert.service'
@Component({
selector: 'cre-groups',
@ -21,40 +22,40 @@ import {ActivatedRoute, Router} from "@angular/router";
])
]
})
export class ListComponent extends SubscribingComponent {
export class ListComponent extends ErrorHandlingComponent {
groups$: Observable<EmployeeGroup[]>
defaultGroup: EmployeeGroup = null
columns = ['name', 'permissionCount', 'employeeCount', 'defaultGroup', 'editGroup']
expandedElement: EmployeeGroup | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 404,
consumer: () => this.alertService.pushWarning('Aucun groupe par défaut n\'a été défini sur cet ordinateur')
}]
constructor(
private groupService: GroupService,
private accountService: AccountService,
private alertService: AlertService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
this.groups$ = this.groupService.all.pipe(takeUntil(this.destroy$))
this.subscribe(
this.groupService.defaultGroup,
{
next: g => this.defaultGroup = g,
error: err => {
if (err.status === 404) {
console.error('No default group is defined on this computer')
}
}
}
group => this.defaultGroup = group,
)
}
setDefaultGroup(group: EmployeeGroup) {
this.subscribe(
this.groupService.setDefaultGroup(group),
{next: () => this.defaultGroup = group}
() => this.defaultGroup = group
)
}

View File

@ -16,7 +16,7 @@ export class GroupService {
return this.api.get<EmployeeGroup[]>('/employee/group')
}
get(id: number): Observable<EmployeeGroup> {
getById(id: number): Observable<EmployeeGroup> {
return this.api.get<EmployeeGroup>(`/employee/group/${id}`);
}

View File

@ -1,8 +1,6 @@
<cre-entity-add
title="Création d'un type de produit"
backButtonLink="/catalog/materialtype/list"
[unknownError]="unknownError"
[customError]="errorMessage"
[formFields]="formFields"
(submit)="submit($event)">
</cre-entity-add>

View File

@ -1,16 +1,17 @@
import {Component} from '@angular/core';
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {FormBuilder, Validators} from "@angular/forms";
import {MaterialTypeService} from "../../service/material-type.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {FormField} from '../../../shared/components/entity-add/entity-add.component'
import {Validators} from '@angular/forms'
import {MaterialTypeService} from '../../service/material-type.service'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.sass']
})
export class AddComponent extends SubscribingComponent {
export class AddComponent extends ErrorHandlingComponent {
formFields: FormField[] = [
{
name: 'name',
@ -43,33 +44,30 @@ export class AddComponent extends SubscribingComponent {
type: 'checkbox'
}
]
unknownError = false
errorMessage: string | null
submittedValues: any | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409 && 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,
messageProducer: error => `Un type de produit avec le préfixe '${error.error.id}' existe déjà`
}]
constructor(
private materialTypeService: MaterialTypeService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
submit(values) {
this.subscribe(
this.submittedValues = values
this.subscribeAndNavigate(
this.materialTypeService.save(values.name, values.prefix, values.usePercentages),
{
next: () => this.router.navigate(['/catalog/materialtype/list']),
error: err => {
if (err.status == 409 && err.error.id === values.name) {
this.errorMessage = `Un type de produit avec le nom '${values.name}' existe déjà`
} else if (err.status == 409 && err.error.id === values.prefix) {
this.errorMessage = `Un type de produit avec le préfixe '${values.prefix}' exists déjà`
} else {
this.unknownError = true
}
console.log(err)
}
}
'/catalog/materialtype/list'
)
}
}

View File

@ -6,8 +6,6 @@
deletePermission="REMOVE_MATERIAL_TYPE"
[entity]="materialType"
[formFields]="formFields"
[unknownError]="unknownError"
[customError]="errorMessage"
(submit)="submit($event)"
(delete)="delete()">
</cre-entity-edit>

View File

@ -1,17 +1,18 @@
import {Component} from '@angular/core';
import {MaterialType} from "../../../shared/model/materialtype.model";
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {MaterialTypeService} from "../../service/material-type.service";
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {FormBuilder, Validators} from "@angular/forms";
import {Component} from '@angular/core'
import {MaterialType} from '../../../shared/model/materialtype.model'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {MaterialTypeService} from '../../service/material-type.service'
import {FormField} from '../../../shared/components/entity-add/entity-add.component'
import {Validators} from '@angular/forms'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.sass']
})
export class EditComponent extends SubscribingComponent {
export class EditComponent extends ErrorHandlingComponent {
materialType: MaterialType | null
formFields: FormField[] = [
{
@ -39,66 +40,49 @@ export class EditComponent extends SubscribingComponent {
]
}
]
unknownError = false
errorMessage: string | null
submittedValues: any | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409 && 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,
messageProducer: error => `Un type de produit avec le préfixe '${error.error.id}' existe déjà`
}]
constructor(
private materialTypeService: MaterialTypeService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit()
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribe(
this.materialTypeService.get(id),
{
next: materialType => this.materialType = materialType,
error: err => {
if (err.status === 404) {
this.router.navigate(['/employee/list'])
} else {
this.unknownError = true
}
}
},
1
this.subscribeEntityById(
this.materialTypeService,
id,
materialType => this.materialType = materialType,
'/employee/list'
)
}
submit(values) {
this.subscribe(
this.submittedValues = values
this.subscribeAndNavigate(
this.materialTypeService.update(this.materialType.id, values.name, values.prefix),
{
next: () => this.router.navigate(['/catalog/materialtype/list']),
error: err => {
if (err.status == 409 && err.error.id === values.name) {
this.errorMessage = `Un type de produit avec le nom '${values.name}' existe déjà`
} else if (err.status == 409 && err.error.id === values.prefix) {
this.errorMessage = `Un type de produit avec le préfixe '${values.prefix}' exists déjà`
} else {
this.unknownError = true
}
console.log(err)
}
}
'/catalog/materialtype/list'
)
}
delete() {
this.subscribe(
this.subscribeAndNavigate(
this.materialTypeService.delete(this.materialType.id),
{
next: () => this.router.navigate(['/catalog/materialtype/list']),
error: err => {
this.unknownError = true
console.log(err)
}
}
'/catalog/materialtype/list'
)
}
}

View File

@ -1,16 +1,16 @@
import {Component} from '@angular/core';
import {MaterialTypeService} from "../../service/material-type.service";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {EmployeePermission} from "../../../shared/model/employee";
import {FormBuilder} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {MaterialTypeService} from '../../service/material-type.service'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {EmployeePermission} from '../../../shared/model/employee'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.sass']
})
export class ListComponent extends SubscribingComponent {
export class ListComponent extends ErrorHandlingComponent {
materialTypes$ = this.materialTypeService.all
columns = [
{def: 'name', title: 'Nom', valueFn: t => t.name},
@ -28,9 +28,10 @@ export class ListComponent extends SubscribingComponent {
constructor(
private materialTypeService: MaterialTypeService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
}

View File

@ -16,7 +16,7 @@ export class MaterialTypeService {
return this.api.get<MaterialType[]>('/materialtype')
}
get(id: number): Observable<MaterialType> {
getById(id: number): Observable<MaterialType> {
return this.api.get<MaterialType>(`/materialtype/${id}`)
}

View File

@ -1,8 +1,6 @@
<cre-entity-add
title="Création d'un produit"
backButtonLink="/catalog/material/list"
[unknownError]="unknownError"
[customError]="errorMessage"
[formFields]="formFields"
(submit)="submit($event)">
</cre-entity-add>

View File

@ -1,18 +1,19 @@
import {Component} from '@angular/core';
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {FormBuilder, Validators} from "@angular/forms";
import {MaterialService} from "../../service/material.service";
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {map} from "rxjs/operators";
import {Component} from '@angular/core'
import {FormField} from '../../../shared/components/entity-add/entity-add.component'
import {Validators} from '@angular/forms'
import {MaterialService} from '../../service/material.service'
import {MaterialTypeService} from '../../../material-type/service/material-type.service'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {map} from 'rxjs/operators'
import {ErrorModel, ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.sass']
})
export class AddComponent extends SubscribingComponent {
export class AddComponent extends ErrorHandlingComponent {
formFields: FormField[] = [
{
name: 'name',
@ -57,32 +58,26 @@ export class AddComponent extends SubscribingComponent {
fileType: 'application/pdf'
}
]
unknownError = false
errorMessage: string | null
handledErrorModels: ErrorModel[] = [{
filter: error => error.status === 409,
messageProducer: error => `Un produit avec le nom '${error.error.id}' existe déjà`
}]
constructor(
private materialService: MaterialService,
private materialTypeService: MaterialTypeService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
submit(values) {
this.subscribe(
this.subscribeAndNavigate(
this.materialService.save(values.name, values.inventoryQuantity, values.materialType, values.simdutFile),
{
next: () => this.router.navigate(['/catalog/material/list']),
error: err => {
if (err.status == 409) {
this.errorMessage = `Un produit avec le nom '${values.name}' existe déjà`
} else {
this.unknownError = true
}
console.log(err)
}
}
'/catalog/material/list'
)
}
}

View File

@ -6,8 +6,6 @@
deletePermission="REMOVE_MATERIAL"
[entity]="material"
[formFields]="formFields"
[unknownError]="unknownError"
[customError]="errorMessage"
(submit)="submit($event)"
(delete)="delete()">
</cre-entity-edit>
@ -25,11 +23,10 @@
Voir la fiche signalitique
</button>
<cre-file-button
#simdutFileButton
color="accent"
label="Modifier la fiche signalitique"
accept="application/pdf"
(change)="selectedSimdutFile = $event.target.files[0]">
(change)="selectedSimdutFile = $event.target.files[0]">
</cre-file-button>
</div>
</ng-template>

View File

@ -1,21 +1,21 @@
import {Component, ViewChild} from '@angular/core';
import {FormField} from "../../../shared/components/entity-add/entity-add.component";
import {Validators} from "@angular/forms";
import {map} from "rxjs/operators";
import {MaterialTypeService} from "../../../material-type/service/material-type.service";
import {MaterialService} from "../../service/material.service";
import {ActivatedRoute, Router} from "@angular/router";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {Material} from "../../../shared/model/material.model";
import {environment} from "../../../../../environments/environment";
import {FileButtonComponent} from "../../../shared/file-button/file-button.component";
import {Component, ViewChild} from '@angular/core'
import {FormField} from '../../../shared/components/entity-add/entity-add.component'
import {Validators} from '@angular/forms'
import {map} from 'rxjs/operators'
import {MaterialTypeService} from '../../../material-type/service/material-type.service'
import {MaterialService} from '../../service/material.service'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {Material} from '../../../shared/model/material.model'
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']
})
export class EditComponent extends SubscribingComponent {
export class EditComponent extends ErrorHandlingComponent {
@ViewChild('simdutTemplate', {static: true}) simdutTemplateRef
material: Material | null
@ -64,79 +64,59 @@ export class EditComponent extends SubscribingComponent {
fileType: 'application/pdf'
}
]
unknownError = false
errorMessage: string | null
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à`
}]
constructor(
private materialService: MaterialService,
private materialTypeService: MaterialTypeService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit();
super.ngOnInit()
this.formFields[3].template = this.simdutTemplateRef
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
this.subscribe(
this.materialService.getById(id),
{
next: material => this.material = material,
error: err => {
if (err.status === 404) {
this.router.navigate(['/catalog/material/list'])
} else {
this.unknownError = true
}
}
},
1
this.subscribeEntityById(
this.materialService,
id,
material => this.material = material,
'/catalog/material/list'
)
this.subscribe(
this.materialService.hasSimdut(id),
{next: b => this.hasSimdut = b}
b => this.hasSimdut = b
)
}
submit(values) {
this.subscribe(
this.subscribeAndNavigate(
this.materialService.update(this.material.id, values.name, values.inventoryQuantity, values.materialType, this.selectedSimdutFile),
{
next: () => this.router.navigate(['/catalog/material/list']),
error: err => {
if (err.status == 409) {
this.errorMessage = `Un produit avec le nom '${values.name}' existe déjà`
} else {
this.unknownError = true
console.error(err)
}
}
}
'/catalog/material/list'
)
}
delete() {
this.subscribe(
this.subscribeAndNavigate(
this.materialService.delete(this.material.id),
{
next: () => this.router.navigate(['/catalog/material/list']),
error: err => {
this.unknownError = true
console.error(err)
}
}
'/catalog/material/list'
)
}
openSimdutUrl() {
const simdutUrl = environment.apiUrl + `/material/${this.material.id}/simdut`
window.open(simdutUrl, "_blank")
window.open(simdutUrl, '_blank')
}
}

View File

@ -1,17 +1,17 @@
import {Component} from '@angular/core';
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {MaterialService} from "../../service/material.service";
import {EmployeePermission} from "../../../shared/model/employee";
import {environment} from "../../../../../environments/environment";
import {FormBuilder} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {Component} from '@angular/core'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {MaterialService} from '../../service/material.service'
import {EmployeePermission} from '../../../shared/model/employee'
import {environment} from '../../../../../environments/environment'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
@Component({
selector: 'cre-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.sass']
})
export class ListComponent extends SubscribingComponent {
export class ListComponent extends ErrorHandlingComponent {
materials$ = this.materialService.allNotMixType
columns = [
{def: 'name', title: 'Code', valueFn: t => t.name},
@ -42,26 +42,25 @@ export class ListComponent extends SubscribingComponent {
constructor(
private materialService: MaterialService,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit() {
super.ngOnInit();
super.ngOnInit()
this.subscribe(
this.materials$,
{
next: mArray => {
mArray.forEach(m => {
this.subscribe(
this.materialService.hasSimdut(m.id),
{ next: b => this.hasSimdutMap[m.id] = b }
)
})
}
mArray => {
mArray.forEach(m => {
this.subscribe(
this.materialService.hasSimdut(m.id),
b => this.hasSimdutMap[m.id] = b
)
})
},
1
)

View File

@ -3,11 +3,6 @@
<mat-card-title>{{title}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="unknownError || customError" class="alert alert-danger">
<p *ngIf="unknownError">Une erreur est survenue</p>
<p *ngIf="customError">{{customError}}</p>
</div>
<form *ngIf="form" [formGroup]="form">
<ng-container *ngFor="let field of formFields">
<ng-container

View File

@ -1,30 +1,23 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {SubscribingComponent} from "../subscribing.component";
import {FormBuilder, FormControl, FormGroup, ValidatorFn} from "@angular/forms";
import {Observable} from "rxjs";
import {ActivatedRoute, Router} from "@angular/router";
import {Component, EventEmitter, Input, Output} from '@angular/core'
import {FormBuilder, FormControl, FormGroup, ValidatorFn} from '@angular/forms'
import {Observable} from 'rxjs'
@Component({
selector: 'cre-entity-add',
templateUrl: './entity-add.component.html',
styleUrls: ['./entity-add.component.sass']
})
export class EntityAddComponent extends SubscribingComponent {
export class EntityAddComponent {
@Input() title: string
@Input() backButtonLink: string
@Input() unknownError: boolean = false
@Input() customError: string | null
@Input() formFields: FormField[]
@Output() submit = new EventEmitter<any>()
form: FormGroup | null
constructor(
private formBuilder: FormBuilder,
router: Router,
activatedRoute: ActivatedRoute
private formBuilder: FormBuilder
) {
super(activatedRoute, router)
}
ngOnInit() {
@ -33,8 +26,6 @@ export class EntityAddComponent extends SubscribingComponent {
formGroup[f.name] = new FormControl(null, f.validator)
})
this.form = this.formBuilder.group(formGroup)
super.ngOnInit();
}
submitForm() {

View File

@ -3,11 +3,6 @@
<mat-card-title>{{title}}</mat-card-title>
</mat-card-header>
<mat-card-content [class.no-action]="disableButtons">
<div *ngIf="unknownError || customError" class="alert alert-danger">
<p *ngIf="unknownError">Une erreur est survenue</p>
<p *ngIf="customError">{{customError}}</p>
</div>
<form [formGroup]="form">
<ng-container *ngFor="let field of formFields">
<ng-container

View File

@ -1,25 +1,21 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
import {SubscribingComponent} from "../subscribing.component";
import {FormField} from "../entity-add/entity-add.component";
import {EmployeePermission} from "../../model/employee";
import {AccountService} from "../../../accounts/services/account.service";
import {ActivatedRoute, Router} from "@angular/router";
import {Component, EventEmitter, Input, Output} from '@angular/core'
import {FormBuilder, FormControl, FormGroup} from '@angular/forms'
import {FormField} from '../entity-add/entity-add.component'
import {EmployeePermission} from '../../model/employee'
import {AccountService} from '../../../accounts/services/account.service'
@Component({
selector: 'cre-entity-edit',
templateUrl: './entity-edit.component.html',
styleUrls: ['./entity-edit.component.sass']
})
export class EntityEditComponent extends SubscribingComponent {
export class EntityEditComponent {
@Input() entity: any
@Input() title: string
@Input() deleteConfirmMessage: string
@Input() backButtonLink: string
@Input() formFields: FormField[]
@Input() deletePermission: EmployeePermission
@Input() unknownError = false
@Input() customError: string | null
@Input() disableButtons: boolean
@Input() noTopMargin = false
@Output() submit = new EventEmitter<any>()
@ -29,21 +25,19 @@ export class EntityEditComponent extends SubscribingComponent {
constructor(
private accountService: AccountService,
private formBuilder: FormBuilder,
router: Router,
activatedRoute: ActivatedRoute
private formBuilder: FormBuilder
) {
super(activatedRoute, router)
}
ngOnInit() {
const formGroup = {}
this.formFields.forEach(f => {
formGroup[f.name] = new FormControl({value: f.valueFn ? f.valueFn(this.entity) : this.entity[f.name], disabled: f.readonly}, f.validator)
formGroup[f.name] = new FormControl({
value: f.valueFn ? f.valueFn(this.entity) : this.entity[f.name],
disabled: f.readonly
}, f.validator)
})
this.form = this.formBuilder.group(formGroup)
super.ngOnInit();
}
submitForm() {

View File

@ -1,17 +1,14 @@
import {Component, Input} from '@angular/core';
import {Observable} from "rxjs";
import {SubscribingComponent} from "../subscribing.component";
import {AccountService} from "../../../accounts/services/account.service";
import {EmployeePermission} from "../../model/employee";
import {FormBuilder} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {Component, Input} from '@angular/core'
import {Observable} from 'rxjs'
import {AccountService} from '../../../accounts/services/account.service'
import {EmployeePermission} from '../../model/employee'
@Component({
selector: 'cre-entity-list',
templateUrl: './entity-list.component.html',
styleUrls: ['./entity-list.component.sass']
})
export class EntityListComponent<T> extends SubscribingComponent {
export class EntityListComponent<T> {
@Input() entities$: Observable<T>
@Input() columns: TableColumn[]
@Input() icons: TableIcon[]
@ -20,11 +17,8 @@ export class EntityListComponent<T> extends SubscribingComponent {
@Input() addPermission: EmployeePermission
constructor(
private accountService: AccountService,
router: Router,
activatedRoute: ActivatedRoute
private accountService: AccountService
) {
super(activatedRoute, router)
}
hasPermissionToUseButton(button: TableButton): boolean {
@ -50,7 +44,9 @@ export class EntityListComponent<T> extends SubscribingComponent {
}
}
if (externalLink) window.open(externalLink, "_blank")
if (externalLink) {
window.open(externalLink, '_blank')
}
}
get tableCols(): string[] {

View File

@ -0,0 +1,11 @@
<div class="alerts-wrapper" [style.margin-top.rem]="extraTopMarginSize">
<div
*ngFor="let alert of alertBuffer"
[@fadeIn]
class="alert"
[class.alert-success]="alert.alert.type === SUCCESS_ALERT_TYPE"
[class.alert-warning]="alert.alert.type === WARNING_ALERT_TYPE"
[class.alert-danger]="alert.alert.type === ERROR_ALERT_TYPE">
{{alert.alert.message}}
</div>
</div>

View File

@ -0,0 +1,28 @@
@import '../../../../../custom-theme'
.alerts-wrapper
position: absolute
width: 100vw
&.subnav-margin
margin-top: 3rem
.alert
width: max-content
margin: 1rem auto
z-index: 50
.alert-danger
color: map-get($theme-error, 900)
border-color: map-get($theme-error, 200)
background-color: map-get($theme-error, 100)
.alert-warning
color: map-get($theme-warning, 900)
border-color: map-get($theme-warning, 400)
background-color: map-get($theme-warning, 100)
.alert-success
color: map-get($theme-accent, 900)
border-color: map-get($theme-accent, 400)
background-color: map-get($theme-accent, 100)

View File

@ -0,0 +1,33 @@
import {Component} from '@angular/core'
import {AlertHandlerComponent, AlertService, AlertType} from '../../service/alert.service'
import {animate, state, style, transition, trigger} from '@angular/animations'
@Component({
selector: 'cre-global-alert-handler',
templateUrl: './global-alert-handler.component.html',
styleUrls: ['./global-alert-handler.component.sass'],
animations: [
trigger('fadeIn', [
state('void', style({
opacity: 0,
transform: 'scale(0.5)'
})),
transition('void <=> *', animate(100))
])
]
})
export class GlobalAlertHandlerComponent extends AlertHandlerComponent {
public static extraTopMarginMultiplier = 0
readonly SUCCESS_ALERT_TYPE = AlertType.Success
readonly WARNING_ALERT_TYPE = AlertType.Warning
readonly ERROR_ALERT_TYPE = AlertType.Error
constructor(alertService: AlertService) {
super(alertService)
}
get extraTopMarginSize(): number {
return GlobalAlertHandlerComponent.extraTopMarginMultiplier * 3
}
}

View File

@ -1,10 +1,10 @@
import {Component} from '@angular/core';
import {ActivatedRoute, ResolveEnd, Router} from "@angular/router";
import {AppState} from "../../app-state";
import {Employee, EmployeePermission} from "../../model/employee";
import {AccountService} from "../../../accounts/services/account.service";
import {SubscribingComponent} from "../subscribing.component";
import {FormBuilder} from "@angular/forms";
import {Component} from '@angular/core'
import {ActivatedRoute, ResolveEnd, Router} from '@angular/router'
import {AppState} from '../../app-state'
import {Employee, EmployeePermission} from '../../model/employee'
import {AccountService} from '../../../accounts/services/account.service'
import {SubscribingComponent} from '../subscribing.component'
import {ErrorService} from '../../service/error.service'
@Component({
selector: 'cre-header',
@ -19,44 +19,45 @@ export class HeaderComponent extends SubscribingComponent {
{route: '/group', title: 'Groupes', requiredPermission: EmployeePermission.VIEW_EMPLOYEE_GROUP},
{route: '/account/login', title: 'Connexion', enabled: true},
{route: '/account/logout', title: 'Déconnexion', enabled: false},
];
]
_activeLink = this.links[0].route
constructor(
private accountService: AccountService,
private appState: AppState,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(activatedRoute, router)
super(errorService, activatedRoute, router)
}
ngOnInit(): void {
super.ngOnInit()
this.accountService.checkAuthenticationStatus()
// Gets the current route
this.subscribe(
this.router.events,
{
next: data => {
if (data instanceof ResolveEnd) this._activeLink = data.url
data => {
if (data instanceof ResolveEnd) {
this._activeLink = data.url
}
},
1
)
// Auth status
this.accountService.checkAuthenticationStatus()
this.updateEnabledLinks(this.appState.isAuthenticated, this.appState.authenticatedEmployee)
this.subscribe(
this.appState.authenticatedUser$,
{next: authentication => this.updateEnabledLinks(authentication.authenticated, authentication.authenticatedUser)}
authentication => this.updateEnabledLinks(authentication.authenticated, authentication.authenticatedUser)
)
super.ngOnInit()
}
ngOnDestroy(): void {
this.accountService.logout(() => {
console.log("Successfully logged out")
console.log('Successfully logged out')
})
super.ngOnDestroy()
@ -77,7 +78,7 @@ export class HeaderComponent extends SubscribingComponent {
this.links.forEach(l => {
if (l.requiredPermission) {
l.enabled = employee && employee.permissions.indexOf(l.requiredPermission) >= 0;
l.enabled = employee && employee.permissions.indexOf(l.requiredPermission) >= 0
}
})
}

View File

@ -1,60 +1,50 @@
import {take, takeUntil} from "rxjs/operators";
import {OnDestroy, OnInit} from "@angular/core";
import {Observable, Subject} from "rxjs";
import {ActivatedRoute, Router} from "@angular/router";
import {UrlUtils} from "../utils/url.utils";
import {take, takeUntil} from 'rxjs/operators'
import {OnDestroy, OnInit} from '@angular/core'
import {Observable, Subject} from 'rxjs'
import {ActivatedRoute, Router} from '@angular/router'
import {UrlUtils} from '../utils/url.utils'
import {ErrorHandler, ErrorModel, ErrorService} from '../service/error.service'
export abstract class SubscribingComponent implements OnInit, OnDestroy {
protected subscribers$ = []
protected destroy$ = new Subject<boolean>()
unknownError = false
protected constructor(
protected errorService: ErrorService,
protected activatedRoute: ActivatedRoute,
protected router: Router,
protected urlUtils = new UrlUtils(activatedRoute, router)
) {
}
subscribe<T>(observable: Observable<T>, observer, take_count = -1) {
if (!observer.error) {
observer.error = err => this.handleObserverError(err)
}
subscribe<T>(observable: Observable<T>, resultConsumer: (T) => void, take_count = -1) {
if (take_count >= 0) {
observable.pipe(take(take_count), takeUntil(this.destroy$))
} else {
observable.pipe(takeUntil(this.destroy$))
}
this.subscribers$.push(observable.subscribe(observer))
this.subscribers$.push(observable.subscribe({
next: resultConsumer,
error: err => this.errorService.handleError(err)
}))
}
subscribeAndNavigate(observable: Observable<any>, route: string) {
this.subscribe(
observable,
{
next: () => this.urlUtils.navigateTo(route)
},
() => this.urlUtils.navigateTo(route),
1
)
}
subscribeEntityByIdFromRoute<T>(service: any, entitySubject: Subject<T>, notFoundRoute: string, paramName: string = 'id') {
const id = this.urlUtils.parseIntUrlParam(paramName)
this.subscribe(
service.getById(id),
{
next: e => entitySubject.next(e),
error: err => {
if (err.status === 404) {
this.urlUtils.navigateTo(notFoundRoute)
} else {
this.handleObserverError(err)
}
}
},
1
)
subscribeEntityById<T>(service: any, id: number, resultConsumer: (T) => void, notFoundRoute: string) {
this.subscribers$.push(service.getById(id)
.pipe(take(1), takeUntil(this.destroy$))
.subscribe({
next: e => resultConsumer(e),
error: err => this.handleNotFoundError(err, notFoundRoute)
}))
}
ngOnInit(): void {
@ -65,16 +55,24 @@ export abstract class SubscribingComponent implements OnInit, OnDestroy {
this.destroy$.complete()
}
handleObserverError(error: any) {
console.error(error)
this.unknownError = true
}
handleNotFoundError(error: any, route: string) {
if (error.status === 404) {
this.urlUtils.navigateTo(route)
} else {
this.handleObserverError(error)
this.errorService.handleError(error)
}
}
}
export abstract class ErrorHandlingComponent extends SubscribingComponent implements ErrorHandler {
handledErrorModels: ErrorModel[] = []
protected constructor(
errorService: ErrorService,
activatedRoute: ActivatedRoute,
router: Router
) {
super(errorService, activatedRoute, router)
this.errorService.activeErrorHandler = this
}
}

View File

@ -0,0 +1,112 @@
import {Injectable, OnDestroy} from '@angular/core'
import {interval, Subject} from 'rxjs'
import {takeUntil} from 'rxjs/operators'
@Injectable({
providedIn: 'root'
})
export class AlertService {
private alertQueue: Alert[] = []
private activeHandler: AlertHandlerComponent
pushSuccess(message: String) {
this.enqueue(AlertType.Success, message)
}
pushWarning(message: String) {
this.enqueue(AlertType.Warning, message)
}
pushError(message: String) {
this.enqueue(AlertType.Error, message)
}
set activeAlertHandlerComponent(handler: AlertHandlerComponent) {
this.activeHandler = handler
}
private enqueue(type: AlertType, message: String) {
this.alertQueue.unshift({type, message})
}
dequeue(): Alert {
return this.alertQueue.pop()
}
}
/**
* An alert handler component is a component that will show the alerts pushed by the alert system to the user.
*/
export abstract class AlertHandlerComponent implements OnDestroy {
protected static readonly DEFAULT_ALERT_BUFFER_SIZE = 3
protected static readonly DEFAULT_ALERT_DURATION = 5
alertBuffer = new Array<{ alert: Alert, time: number }>()
protected alertBufferSize: number = AlertHandlerComponent.DEFAULT_ALERT_BUFFER_SIZE
protected alertDuration: number = AlertHandlerComponent.DEFAULT_ALERT_DURATION
protected alertDurationCounter = 0
private destroy$ = new Subject<boolean>()
protected constructor(
protected alertService: AlertService
) {
alertService.activeAlertHandlerComponent = this
interval(1000)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: () => {
this.alertDurationCounter++
if (!this.isBufferEmpty) {
this.alertBuffer
.filter(a => a.time + this.alertDuration < this.alertDurationCounter)
.map(a => this.alertBuffer.indexOf(a))
.forEach(i => this.alertBuffer.splice(i, 1))
}
if (!this.isBufferFull) {
this.pullAlert()
}
}
})
}
ngOnDestroy(): void {
this.destroy$.next(true)
this.destroy$.complete()
}
pullAlert() {
const nextAlert = this.alertService.dequeue()
if (nextAlert) {
this.alertBuffer.unshift({alert: nextAlert, time: this.alertDurationCounter})
}
}
get isBufferEmpty(): boolean {
return this.alertBuffer.length == 0
}
get isBufferFull(): boolean {
return this.alertBuffer.length >= this.alertBufferSize
}
}
/**
* An alert is a message that will be shown to the user.
*
* An alert can be a success alert, a warning alert of an error alert.
*/
class Alert {
constructor(
public type: AlertType,
public message: String
) {
}
}
export enum AlertType {
Success,
Warning,
Error
}

View File

@ -6,6 +6,7 @@ import {AppState} from '../app-state'
import {Router} from '@angular/router'
import {map, share, takeUntil} from 'rxjs/operators'
import {valueOr} from '../utils/optional.utils'
import {ErrorService} from './error.service'
@Injectable({
providedIn: 'root'
@ -14,6 +15,7 @@ export class ApiService implements OnDestroy {
private _destroy$ = new Subject<boolean>()
constructor(
private errorService: ErrorService,
private http: HttpClient,
private appState: AppState,
private router: Router
@ -85,15 +87,6 @@ export class ApiService implements OnDestroy {
: requestFn(httpOptions)
.pipe(takeUntil(this._destroy$), map(r => r.body), share())
const errorCheckSubscription = result$.subscribe({
next: () => this.appState.isServerOnline = true,
error: err => {
console.error(err)
errorCheckSubscription.unsubscribe()
this.appState.isServerOnline = !(err.status === 0 && err.statusText === 'Unknown Error' || err.status === 502)
}
})
return result$
}

View File

@ -0,0 +1,110 @@
import {Injectable} from '@angular/core'
import {AlertService} from './alert.service'
import {AppState} from '../app-state'
@Injectable({
providedIn: 'root'
})
export class ErrorService {
private static readonly UNKNOWN_ERROR_MESSAGE = 'Une erreur inconnue est survenue'
private defaultHandledErrorModels: ErrorModel[] = [{
filter: error => error.status === 0 && error.statusText === 'Unknown Error' || error.status === 502,
consumer: () => this.appState.isServerOnline = false
}, {
filter: error => error.status === 400,
consumer: error => console.error(error),
messageProducer: () => 'Certaines informations dans la requête étaient invalides'
}, {
filter: error => error.status === 401,
messageProducer: () => 'Vous devez être connecté pour effectuer cette action'
}, {
filter: error => error.status === 403,
messageProducer: () => 'Vous n\'avez pas la permission d\'effectuer cette action'
}, {
filter: error => error.status === 404,
messageProducer: () => 'La resource demandée n\'a pas été trouvée'
}, {
filter: error => error.status === 409,
messageProducer: () => 'Un conflit avec une autre resource s\'est produit'
}, {
filter: error => error.status === 500,
messageProducer: () => ErrorService.UNKNOWN_ERROR_MESSAGE
}]
private activeHandler: ErrorHandler
constructor(
private alertService: AlertService,
private appState: AppState
) {
}
handleError(error: any) {
let matchingModels
if (this.activeHandler) {
matchingModels = this.activeHandler.handledErrorModels.filter(m => m.filter(error)) // Find error models whose filter matches the current error
} else {
console.warn('An error occurred but no handler was set')
}
if (!matchingModels || matchingModels.length == 0) { // If none are found, search in defaults handlers
matchingModels = this.defaultHandledErrorModels.filter(m => m.filter(error))
}
if (!matchingModels || matchingModels.length == 0) { // If still none are found, handle as an unknown error
this.consumeUnknownError(error)
return
}
matchingModels.forEach(m => {
if (m.consumer || m.messageProducer) {
if (m.consumer) {
m.consumer(error)
}
if (m.messageProducer) {
this.alertService.pushError(m.messageProducer(error))
}
} else {
console.error('An error model has no consumer or message')
}
})
}
consumeUnknownError(error: any) {
console.error(error)
this.pushUnknownError()
}
pushUnknownError() {
this.alertService.pushError(ErrorService.UNKNOWN_ERROR_MESSAGE)
}
set activeErrorHandler(handler: ErrorHandler) {
this.activeHandler = handler
}
}
/**
* An error handler, defining models of errors that this type will handle
*/
export interface ErrorHandler {
handledErrorModels: ErrorModel[]
}
/**
* An error model define how errors matching its filter will be handled.
*
* The consumer will consume matching errors when they occurs.
* The message producer returns a string that will be pushed to the alert system.
*
* To work correctly a model must define at least one handler (consumer or message producer).
*/
export class ErrorModel {
constructor(
public filter: (error: any) => Boolean,
public consumer?: (error: any) => void,
public messageProducer?: (error: any) => String
) {
}
}

View File

@ -27,10 +27,11 @@ 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';
@NgModule({
declarations: [HeaderComponent, EmployeeInfoComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent],
declarations: [HeaderComponent, EmployeeInfoComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent],
exports: [
CommonModule,
HttpClientModule,
@ -55,7 +56,8 @@ import { FileButtonComponent } from './file-button/file-button.component';
EntityListComponent,
EntityAddComponent,
EntityEditComponent,
FileButtonComponent
FileButtonComponent,
GlobalAlertHandlerComponent
],
imports: [
MatTabsModule,

View File

@ -1,16 +1,25 @@
import {Component} from '@angular/core';
import {NavLink} from "../../modules/shared/components/nav/nav.component";
import {EmployeePermission} from "../../modules/shared/model/employee";
import {Component, OnDestroy, OnInit} from '@angular/core'
import {NavLink} from '../../modules/shared/components/nav/nav.component'
import {EmployeePermission} from '../../modules/shared/model/employee'
import {GlobalAlertHandlerComponent} from '../../modules/shared/components/global-alert-handler/global-alert-handler.component'
@Component({
selector: 'cre-inventory-page',
templateUrl: './catalog.component.html',
styleUrls: ['./catalog.component.sass']
})
export class CatalogComponent {
export class CatalogComponent implements OnInit, OnDestroy {
links: NavLink[] = [
{route: '/catalog/materialtype', title: 'Types de produit', permission: EmployeePermission.VIEW_MATERIAL_TYPE},
{route: '/catalog/material', title: 'Produits', permission: EmployeePermission.VIEW_MATERIAL},
{route: '/catalog/company', title: 'Bannières', permission: EmployeePermission.VIEW_COMPANY}
]
ngOnInit(): void {
GlobalAlertHandlerComponent.extraTopMarginMultiplier = 1
}
ngOnDestroy(): void {
GlobalAlertHandlerComponent.extraTopMarginMultiplier = 0
}
}

View File

@ -78,12 +78,44 @@ $theme-accent: mat-palette((
A700 : #000000,
)
));
$theme-warning: mat-palette((
50 : #fff8e4,
100 : #feefbd,
200 : #fee491,
300 : #fdd964,
400 : #fcd043,
500 : #fcc822,
600 : #fcc21e,
700 : #fbbb19,
800 : #fbb414,
900 : #faa70c,
A100 : #ffffff,
A200 : #fffaf1,
A400 : #ffe6be,
A700 : #ffdca4,
contrast: (
50 : #000000,
100 : #000000,
200 : #000000,
300 : #000000,
400 : #000000,
500 : #000000,
600 : #000000,
700 : #000000,
800 : #000000,
900 : #000000,
A100 : #000000,
A200 : #000000,
A400 : #000000,
A700 : #000000,
)
));
// The warn palette is optional (defaults to red).
$theme-warn: mat-palette($mat-red);
$theme-error: mat-palette($mat-red);
// Create the theme object (a Sass map containing all of the palettes).
$color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-accent, $theme-warn);
$color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-accent, $theme-error);
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
@ -92,7 +124,7 @@ $color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-a
$color-primary: map-get($theme-primary, 500);
$color-accent: map-get($theme-accent, 500);
$color-warn: map-get($theme-warn, 500);
$color-warn: map-get($theme-error, 500);
html, body {