diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index be757b7..7057e34 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,7 +3,6 @@ import {Routes, RouterModule} from '@angular/router' import {CatalogComponent} from './pages/catalog/catalog.component' import {AdministrationComponent} from './pages/administration/administration.component' import {MiscComponent} from './pages/others/misc.component' -import {TouchupkitComponent} from './pages/others/touchupkit/touchupkit.component' const routes: Routes = [{ @@ -49,12 +48,12 @@ const routes: Routes = [{ path: 'misc', component: MiscComponent, children: [{ - path: 'touchupkit', - component: TouchupkitComponent + path: 'touch-up-kit', + loadChildren: () => import('./modules/touch-up-kit/touch-up-kit.module').then(m => m.TouchUpKitModule) }, { path: '', pathMatch: 'full', - redirectTo: 'touchupkit' + redirectTo: 'touch-up-kit' }] }] diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9de024d..c90abde 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,30 +1,34 @@ -import {DomSanitizer} from '@angular/platform-browser'; -import {NgModule} from '@angular/core'; +import {DomSanitizer} from '@angular/platform-browser' +import {NgModule} from '@angular/core' -import {AppRoutingModule} from './app-routing.module'; -import {AppComponent} from './app.component'; -import {MatIconRegistry} from "@angular/material/icon"; -import {SharedModule} from "./modules/shared/shared.module"; -import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; -import {CatalogComponent} from './pages/catalog/catalog.component'; -import {CompanyModule} from './modules/company/company.module'; -import { AdministrationComponent } from './pages/administration/administration.component'; -import { MiscComponent } from './pages/others/misc.component'; -import { TouchupkitComponent } from './pages/others/touchupkit/touchupkit.component'; +import {AppRoutingModule} from './app-routing.module' +import {AppComponent} from './app.component' +import {MatIconRegistry} from '@angular/material/icon' +import {SharedModule} from './modules/shared/shared.module' +import {BrowserAnimationsModule} from '@angular/platform-browser/animations' +import {CatalogComponent} from './pages/catalog/catalog.component' +import {CompanyModule} from './modules/company/company.module' +import {AdministrationComponent} from './pages/administration/administration.component' +import {MiscComponent} from './pages/others/misc.component' +import {CreTablesModule} from './modules/shared/components/tables/tables.module' +import {CreButtonsModule} from './modules/shared/components/buttons/buttons.module' +import {TouchUpKitModule} from './modules/touch-up-kit/touch-up-kit.module' @NgModule({ declarations: [ AppComponent, CatalogComponent, AdministrationComponent, - MiscComponent, - TouchupkitComponent + MiscComponent ], imports: [ AppRoutingModule, SharedModule, BrowserAnimationsModule, - CompanyModule + CompanyModule, + CreTablesModule, + CreButtonsModule, + TouchUpKitModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/modules/accounts/pages/login/login.component.ts b/src/app/modules/accounts/pages/login/login.component.ts index 8f5443a..1802a7c 100644 --- a/src/app/modules/accounts/pages/login/login.component.ts +++ b/src/app/modules/accounts/pages/login/login.component.ts @@ -4,6 +4,7 @@ import {AccountService} from '../../services/account.service' import {ActivatedRoute, Router} from '@angular/router' import {ErrorService} from '../../../shared/service/error.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-login', @@ -18,11 +19,13 @@ export class LoginComponent extends ErrorHandlingComponent implements OnInit { constructor( private formBuilder: FormBuilder, private accountService: AccountService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Connexion' } ngOnInit(): void { diff --git a/src/app/modules/accounts/services/account.service.ts b/src/app/modules/accounts/services/account.service.ts index 3af265f..dbf3362 100644 --- a/src/app/modules/accounts/services/account.service.ts +++ b/src/app/modules/accounts/services/account.service.ts @@ -5,7 +5,7 @@ import {AppState} from '../../shared/app-state' 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 {User, Permission} from '../../shared/model/user' import {ErrorService} from '../../shared/service/error.service' import {globalLoadingWheel} from '../../shared/components/loading-wheel/loading-wheel.component' import {AlertService} from '../../shared/service/alert.service' @@ -35,15 +35,15 @@ export class AccountService implements OnDestroy { } checkAuthenticationStatus() { - if (!this.appState.authenticatedEmployee) { + if (!this.appState.authenticatedUser) { // Try to get current default group user - this.http.get(`${environment.apiUrl}/employee/current`, {withCredentials: true}) + this.http.get(`${environment.apiUrl}/user/current`, {withCredentials: true}) .pipe( take(1), takeUntil(this.destroy$), ).subscribe( { - next: employee => this.appState.authenticatedEmployee = employee, + next: user => this.appState.authenticatedUser = user, error: err => { if (err.status === 404 || err.status === 403) { console.warn('No default user is defined on this computer') @@ -70,7 +70,7 @@ export class AccountService implements OnDestroy { next: (response: HttpResponse) => { this.appState.authenticationExpiration = parseInt(response.headers.get('X-Authentication-Expiration')) this.appState.isAuthenticated = true - this.setLoggedInEmployeeFromApi() + this.setLoggedInUserFromApi() success() }, error: err => { @@ -91,7 +91,7 @@ export class AccountService implements OnDestroy { ) .subscribe({ next: () => { - this.appState.resetAuthenticatedEmployee() + this.appState.resetAuthenticatedUser() this.checkAuthenticationStatus() success() }, @@ -99,19 +99,19 @@ export class AccountService implements OnDestroy { }) } - hasPermission(permission: EmployeePermission): boolean { - return this.appState.authenticatedEmployee && this.appState.authenticatedEmployee.permissions.indexOf(permission) >= 0 + hasPermission(permission: Permission): boolean { + return this.appState.authenticatedUser && this.appState.authenticatedUser.permissions.indexOf(permission) >= 0 } - private setLoggedInEmployeeFromApi() { - this.api.get('/employee/current', true) + private setLoggedInUserFromApi() { + this.api.get('/user/current', true) .pipe( take(1), takeUntil(this.destroy$) ) .subscribe({ - next: employee => { - this.appState.authenticatedEmployee = employee + next: user => { + this.appState.authenticatedUser = user // At this point the loading wheel should be visible globalLoadingWheel.hide() }, diff --git a/src/app/modules/colors/components/mix-editor/mix-editor.component.html b/src/app/modules/colors/components/mix-editor/mix-editor.component.html index 7417836..a89926b 100644 --- a/src/app/modules/colors/components/mix-editor/mix-editor.component.html +++ b/src/app/modules/colors/components/mix-editor/mix-editor.component.html @@ -28,7 +28,7 @@ - diff --git a/src/app/modules/colors/components/mix-editor/mix-editor.component.ts b/src/app/modules/colors/components/mix-editor/mix-editor.component.ts index 1ebd7bc..d4eee9e 100644 --- a/src/app/modules/colors/components/mix-editor/mix-editor.component.ts +++ b/src/app/modules/colors/components/mix-editor/mix-editor.component.ts @@ -19,7 +19,6 @@ 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({ @@ -190,10 +189,6 @@ export class MixEditorComponent extends ErrorHandlingComponent { }) } - get canDeleteMix() { - return this.accountService.hasPermission(EmployeePermission.REMOVE_RECIPES) - } - private generateForm() { this.nameControl = new FormControl(this.mix ? this.mix.mixType.name : null, Validators.required) this.materialTypeControl = new FormControl(this.mix ? this.mix.mixType.material.materialType.id : null, Validators.required) diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.ts b/src/app/modules/colors/components/mix-table/mix-table.component.ts index fa8da6d..853c054 100644 --- a/src/app/modules/colors/components/mix-table/mix-table.component.ts +++ b/src/app/modules/colors/components/mix-table/mix-table.component.ts @@ -10,7 +10,7 @@ import {ErrorService} from '../../../shared/service/error.service' import {AlertService} from '../../../shared/service/alert.service' import {environment} from '../../../../../environments/environment' import {MaterialService} from '../../../material/service/material.service' -import {EmployeePermission} from '../../../shared/model/employee' +import {Permission} from '../../../shared/model/user' import {AccountService} from '../../../accounts/services/account.service' import {Material, openSimdut} from '../../../shared/model/material.model' @@ -175,15 +175,15 @@ export class MixTableComponent extends SubscribingComponent { } get canPrintMixes(): boolean { - return this.accountService.hasPermission(EmployeePermission.PRINT_MIXES) + return this.accountService.hasPermission(Permission.PRINT_MIXES) } get canDeductFromInventory(): boolean { - return this.accountService.hasPermission(EmployeePermission.DEDUCT_FROM_INVENTORY) + return this.accountService.hasPermission(Permission.DEDUCT_FROM_INVENTORY) } get canEditRecipesPublicData(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_RECIPES_PUBLIC_DATA) + return this.accountService.hasPermission(Permission.EDIT_RECIPES_PUBLIC_DATA) } private emitQuantityChangeEvent(index: number) { diff --git a/src/app/modules/colors/components/recipe-info/recipe-info.component.html b/src/app/modules/colors/components/recipe-info/recipe-info.component.html index 5e4831a..ba42447 100644 --- a/src/app/modules/colors/components/recipe-info/recipe-info.component.html +++ b/src/app/modules/colors/components/recipe-info/recipe-info.component.html @@ -1,17 +1,12 @@

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

-
-
{{recipe.gloss}}%
-
+

Échantillon #{{recipe.sample}}

-

Approuvée le {{recipe.approbationDate}}

+

Approuvée le {{approbationDate}}

Non approuvée

diff --git a/src/app/modules/colors/components/recipe-info/recipe-info.component.ts b/src/app/modules/colors/components/recipe-info/recipe-info.component.ts index 35145bc..6a619d7 100644 --- a/src/app/modules/colors/components/recipe-info/recipe-info.component.ts +++ b/src/app/modules/colors/components/recipe-info/recipe-info.component.ts @@ -1,5 +1,6 @@ import {AfterViewInit, Component, Input} from '@angular/core' import {getRecipeLuma, recipeApprobationExpired, Recipe} from '../../../shared/model/recipe.model' +import {formatDate} from '../../../shared/utils/utils' @Component({ selector: 'cre-recipe-info', @@ -13,15 +14,14 @@ export class RecipeInfoComponent implements AfterViewInit { isBPacExtensionInstalled = false ngAfterViewInit(): void { - this.isBPacExtensionInstalled = document.querySelectorAll('.bpac-extension-installed').length > 0 } + get approbationDate(): string { + return formatDate(this.recipe.approbationDate) + } + get isApprobationExpired(): boolean { return recipeApprobationExpired(this.recipe) } - - get isDarkColor(): boolean { - return getRecipeLuma(this.recipe) < 100 - } } diff --git a/src/app/modules/colors/components/step-table/step-table.component.ts b/src/app/modules/colors/components/step-table/step-table.component.ts index 3d92d34..1c471b3 100644 --- a/src/app/modules/colors/components/step-table/step-table.component.ts +++ b/src/app/modules/colors/components/step-table/step-table.component.ts @@ -2,7 +2,7 @@ import {Component, Input} from '@angular/core' import {Recipe, RecipeStep, recipeStepsForGroupId, sortRecipeSteps} from '../../../shared/model/recipe.model' import {MatTable} from '@angular/material/table' import {Observable} from 'rxjs' -import {EmployeeGroup} from '../../../shared/model/employee' +import {Group} from '../../../shared/model/user' @Component({ selector: 'cre-step-table', @@ -13,7 +13,7 @@ export class StepTableComponent { readonly columns = ['position', 'buttonsPosition', 'message', 'buttonRemove'] @Input() recipe: Recipe - @Input() groups$: Observable + @Input() groups$: Observable @Input() selectedGroupId: number | null hoveredStep: RecipeStep | null diff --git a/src/app/modules/colors/pages/add/add.component.ts b/src/app/modules/colors/pages/add/add.component.ts index f79b33f..e5bcf7e 100644 --- a/src/app/modules/colors/pages/add/add.component.ts +++ b/src/app/modules/colors/pages/add/add.component.ts @@ -7,6 +7,7 @@ import {CompanyService} from '../../../company/service/company.service' import {map} from 'rxjs/operators' import {ActivatedRoute, Router} from '@angular/router' import {ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-add', @@ -96,14 +97,21 @@ export class AddComponent extends ErrorHandlingComponent { } ] + errorHandlers = [{ + filter: error => error.type === `exists-recipe-company-name`, + messageProducer: error => `Une couleur avec le nom ${error.name} existe déjà pour la bannière ${error.company}` + }] + constructor( private recipeService: RecipeService, private companyService: CompanyService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = "Nouvelle couleur" } submit(values) { diff --git a/src/app/modules/colors/pages/edit/edit.component.html b/src/app/modules/colors/pages/edit/edit.component.html index 6e15d0a..81461f8 100644 --- a/src/app/modules/colors/pages/edit/edit.component.html +++ b/src/app/modules/colors/pages/edit/edit.component.html @@ -11,7 +11,6 @@ Enregistrer
diff --git a/src/app/modules/colors/pages/edit/edit.component.ts b/src/app/modules/colors/pages/edit/edit.component.ts index 5382f21..ab663dc 100644 --- a/src/app/modules/colors/pages/edit/edit.component.ts +++ b/src/app/modules/colors/pages/edit/edit.component.ts @@ -7,7 +7,6 @@ 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, ErrorService} from '../../../shared/service/error.service' @@ -131,6 +130,8 @@ export class EditComponent extends ErrorHandlingComponent { parseInt(this.activatedRoute.snapshot.paramMap.get('id')), recipe => { this.recipe = recipe + this.appState.title = `${recipe.name} (Modifications)` + if (recipeMixCount(this.recipe) == 0) { this.alertService.pushWarning('Il n\'y a aucun mélange dans cette recette') } @@ -168,12 +169,8 @@ export class EditComponent extends ErrorHandlingComponent { ) } - get hasDeletePermission(): boolean { - return this.accountService.hasPermission(EmployeePermission.REMOVE_RECIPES) - } - - get loggedInEmployeeGroupId(): number { - return this.appState.authenticatedEmployee.group?.id + get loggedInUserGroupId(): number { + return this.appState.authenticatedUser.group?.id } private stepsPositionsAreValid(steps: Map): boolean { diff --git a/src/app/modules/colors/pages/explore/explore.component.ts b/src/app/modules/colors/pages/explore/explore.component.ts index e112d4b..1510aa8 100644 --- a/src/app/modules/colors/pages/explore/explore.component.ts +++ b/src/app/modules/colors/pages/explore/explore.component.ts @@ -12,7 +12,8 @@ import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confir import {GroupService} from '../../../groups/services/group.service' import {AppState} from '../../../shared/app-state' import {AccountService} from '../../../accounts/services/account.service' -import {EmployeePermission} from '../../../shared/model/employee' +import {Permission} from '../../../shared/model/user' +import {Title} from '@angular/platform-browser' @Component({ selector: 'cre-explore', @@ -60,7 +61,7 @@ export class ExploreComponent extends ErrorHandlingComponent { super.ngOnInit() GlobalAlertHandlerComponent.extraTopMarginMultiplier = 2.5 - this.selectedGroupId = this.loggedInEmployeeGroupId + this.selectedGroupId = this.loggedInUserGroupId const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id')) this.subscribeEntityById( @@ -68,6 +69,7 @@ export class ExploreComponent extends ErrorHandlingComponent { id, r => { this.recipe = r + this.appState.title = r.name if (recipeMixCount(this.recipe) <= 0 || recipeStepCount(this.recipe) <= 0) { this.alertService.pushWarning('Cette recette n\'est pas complète') @@ -132,8 +134,8 @@ export class ExploreComponent extends ErrorHandlingComponent { ) } - get loggedInEmployeeGroupId(): number { - return this.appState.authenticatedEmployee.group?.id + get loggedInUserGroupId(): number { + return this.appState.authenticatedUser.group?.id } get selectedGroupNote(): string { @@ -148,7 +150,7 @@ export class ExploreComponent extends ErrorHandlingComponent { } get canEditRecipesPublicData(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_RECIPES_PUBLIC_DATA) + return this.accountService.hasPermission(Permission.EDIT_RECIPES_PUBLIC_DATA) } private get mappedUpdatedNotes(): Map { diff --git a/src/app/modules/colors/pages/list/list.component.ts b/src/app/modules/colors/pages/list/list.component.ts index 1b42eb4..cda46fb 100644 --- a/src/app/modules/colors/pages/list/list.component.ts +++ b/src/app/modules/colors/pages/list/list.component.ts @@ -1,11 +1,12 @@ import {ChangeDetectorRef, Component} from '@angular/core' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {RecipeService} from '../../services/recipe.service' -import {EmployeePermission} from '../../../shared/model/employee' +import {Permission} from '../../../shared/model/user' import {AccountService} from '../../../accounts/services/account.service' import {getRecipeLuma, Recipe, recipeApprobationExpired} from '../../../shared/model/recipe.model' import {ActivatedRoute, Router} from '@angular/router' import {ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-list', @@ -23,6 +24,7 @@ export class ListComponent extends ErrorHandlingComponent { private recipeService: RecipeService, private accountService: AccountService, private cdRef: ChangeDetectorRef, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -32,6 +34,7 @@ export class ListComponent extends ErrorHandlingComponent { ngOnInit() { super.ngOnInit() + this.appState.title = "Explorateur" this.subscribe( this.recipeService.allSortedByCompany, @@ -63,7 +66,7 @@ export class ListComponent extends ErrorHandlingComponent { } get hasEditPermission(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_RECIPES) + return this.accountService.hasPermission(Permission.EDIT_RECIPES) } private recipeMatchesSearchQuery(recipe: Recipe) { diff --git a/src/app/modules/colors/services/recipe.service.ts b/src/app/modules/colors/services/recipe.service.ts index 5e80f25..1cda0ce 100644 --- a/src/app/modules/colors/services/recipe.service.ts +++ b/src/app/modules/colors/services/recipe.service.ts @@ -17,6 +17,10 @@ export class RecipeService { return this.api.get('/recipe') } + getAllByName(name: string): Observable { + return this.api.get(`/recipe?name=${name}`) + } + get allSortedByCompany(): Observable<{ company: string, recipes: Recipe[] }[]> { return this.all.pipe(map(recipes => { const mapped = [] diff --git a/src/app/modules/company/pages/add/add.component.ts b/src/app/modules/company/pages/add/add.component.ts index 83d3ff7..37fbe4c 100644 --- a/src/app/modules/company/pages/add/add.component.ts +++ b/src/app/modules/company/pages/add/add.component.ts @@ -4,6 +4,7 @@ import {ErrorHandlingComponent} from '../../../shared/components/subscribing.com import {FormField} from '../../../shared/components/entity-add/entity-add.component' import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-add', @@ -32,11 +33,13 @@ export class AddComponent extends ErrorHandlingComponent { constructor( private companyService: CompanyService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Nouvelle bannière' } ngOnInit() { diff --git a/src/app/modules/company/pages/edit/edit.component.html b/src/app/modules/company/pages/edit/edit.component.html index cd9b72b..c25607d 100644 --- a/src/app/modules/company/pages/edit/edit.component.html +++ b/src/app/modules/company/pages/edit/edit.component.html @@ -3,7 +3,6 @@ title="Modifier la bannière {{company.name}}" deleteConfirmMessage="Voulez-vous vraiment supprimer la bannière {{company.name}}?" backButtonLink="/catalog/company/list" - deletePermission="REMOVE_COMPANIES" [entity]="company" [formFields]="formFields" (submit)="submit($event)" diff --git a/src/app/modules/company/pages/edit/edit.component.ts b/src/app/modules/company/pages/edit/edit.component.ts index 8564f8f..b536b39 100644 --- a/src/app/modules/company/pages/edit/edit.component.ts +++ b/src/app/modules/company/pages/edit/edit.component.ts @@ -5,6 +5,7 @@ import {FormField} from '../../../shared/components/entity-add/entity-add.compon import {CompanyService} from '../../service/company.service' import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandler, ErrorHandlerComponent, ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-edit', @@ -39,6 +40,7 @@ export class EditComponent extends ErrorHandlingComponent { constructor( private companyService: CompanyService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -53,7 +55,10 @@ export class EditComponent extends ErrorHandlingComponent { this.subscribeEntityById( this.companyService, id, - company => this.company = company + company => { + this.appState.title = `${company.name} (Modifications)` + this.company = company + } ) } diff --git a/src/app/modules/company/pages/list/list.component.ts b/src/app/modules/company/pages/list/list.component.ts index d817a86..6104358 100644 --- a/src/app/modules/company/pages/list/list.component.ts +++ b/src/app/modules/company/pages/list/list.component.ts @@ -1,9 +1,10 @@ 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 {Permission} from '../../../shared/model/user' import {ActivatedRoute, Router} from '@angular/router' import {ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-list', @@ -18,15 +19,17 @@ export class ListComponent extends ErrorHandlingComponent { buttons = [{ text: 'Modifier', linkFn: t => `/catalog/company/edit/${t.id}`, - permission: EmployeePermission.EDIT_COMPANIES + permission: Permission.EDIT_COMPANIES }] constructor( private companyService: CompanyService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Bannières' } } diff --git a/src/app/modules/groups/pages/add/add.component.ts b/src/app/modules/groups/pages/add/add.component.ts index e632fb9..4c39fe2 100644 --- a/src/app/modules/groups/pages/add/add.component.ts +++ b/src/app/modules/groups/pages/add/add.component.ts @@ -6,6 +6,7 @@ import {currentPermissionsFieldComponent} from '../../../shared/components/permi import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' import {FormField} from '../../../shared/components/entity-add/entity-add.component' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-add', @@ -33,17 +34,19 @@ export class AddComponent extends ErrorHandlingComponent { }] errorHandlers: ErrorHandler[] = [{ - filter: error => error.type === 'exists-employeegroup-name', + filter: error => error.type === 'exists-group-name', messageProducer: error => `Un groupe avec le nom '${error.name}' existe déjà` }] constructor( private groupService: GroupService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Nouveau groupe' } ngOnInit() { diff --git a/src/app/modules/groups/pages/edit/edit.component.html b/src/app/modules/groups/pages/edit/edit.component.html index 9e12e69..3870f9e 100644 --- a/src/app/modules/groups/pages/edit/edit.component.html +++ b/src/app/modules/groups/pages/edit/edit.component.html @@ -2,7 +2,6 @@ *ngIf="group" title="Modifier le groupe {{group.name}}" backButtonLink="/admin/group/list" - deletePermission="REMOVE_USERS" deleteConfirmMessage="Voulez-vous vraiment supprimer le groupe {{group.name}}?" [entity]="group" [formFields]="formFields" diff --git a/src/app/modules/groups/pages/edit/edit.component.ts b/src/app/modules/groups/pages/edit/edit.component.ts index 0b4c121..57740fa 100644 --- a/src/app/modules/groups/pages/edit/edit.component.ts +++ b/src/app/modules/groups/pages/edit/edit.component.ts @@ -1,6 +1,6 @@ import {Component, ViewChild} from '@angular/core' import {ActivatedRoute, Router} from '@angular/router' -import {EmployeeGroup} from '../../../shared/model/employee' +import {Group} from '../../../shared/model/user' import {GroupService} from '../../services/group.service' import {Validators} from '@angular/forms' import {currentPermissionsFieldComponent} from '../../../shared/components/permissions-field/permissions-field.component' @@ -8,6 +8,7 @@ import {AccountService} from '../../../accounts/services/account.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' import {FormField} from '../../../shared/components/entity-add/entity-add.component' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-edit', @@ -33,19 +34,20 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Permissions', type: 'permissions' }] - group: EmployeeGroup | null + group: Group | null errorHandlers: ErrorHandler[] = [{ - filter: error => error.type === 'notfound-employeegroup-id', + filter: error => error.type === 'notfound-group-id', consumer: error => this.urlUtils.navigateTo('/admin/group/list') }, { - filter: error => error.type === 'exists-employeegroup-name', + filter: error => error.type === 'exists-group-name', messageProducer: error => `Un groupe avec le nom '${error.name}' existe déjà` }] constructor( private accountService: AccountService, private groupService: GroupService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -58,7 +60,10 @@ export class EditComponent extends ErrorHandlingComponent { this.subscribeEntityById( this.groupService, id, - group => this.group = group + group => { + this.appState.title = `${group.name} (Modifications)` + this.group = group + } ) this.formFields[this.formFields.length - 1].template = this.permissionsTemplateRef diff --git a/src/app/modules/groups/pages/list/list.component.ts b/src/app/modules/groups/pages/list/list.component.ts index d8a08e9..effdcf6 100644 --- a/src/app/modules/groups/pages/list/list.component.ts +++ b/src/app/modules/groups/pages/list/list.component.ts @@ -1,11 +1,12 @@ import {Component} from '@angular/core' import {GroupService} from '../../services/group.service' -import {EmployeeGroup, EmployeePermission} from '../../../shared/model/employee' +import {Group, Permission} from '../../../shared/model/user' import {AccountService} from '../../../accounts/services/account.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' import {AlertService} from '../../../shared/service/alert.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-groups', @@ -14,7 +15,7 @@ import {AlertService} from '../../../shared/service/alert.service' }) export class ListComponent extends ErrorHandlingComponent { groups$ = this.groupService.all - defaultGroup: EmployeeGroup = null + defaultGroup: Group = null columns = [ {def: 'name', title: 'Nom', valueFn: g => g.name}, {def: 'permissionCount', title: 'Nombre de permissions', valueFn: g => g.permissions.length} @@ -26,7 +27,7 @@ export class ListComponent extends ErrorHandlingComponent { }, { text: 'Modifier', linkFn: group => `/admin/group/edit/${group.id}`, - permission: EmployeePermission.EDIT_USERS + permission: Permission.EDIT_USERS }] errorHandlers: ErrorHandler[] = [{ @@ -38,11 +39,13 @@ export class ListComponent extends ErrorHandlingComponent { private groupService: GroupService, private accountService: AccountService, private alertService: AlertService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Groupes' } ngOnInit(): void { @@ -53,7 +56,7 @@ export class ListComponent extends ErrorHandlingComponent { ) } - setDefaultGroup(group: EmployeeGroup) { + setDefaultGroup(group: Group) { this.subscribe( this.groupService.setDefaultGroup(group), () => this.defaultGroup = group, @@ -61,7 +64,7 @@ export class ListComponent extends ErrorHandlingComponent { ) } - isDefaultGroup(group: EmployeeGroup): boolean { + isDefaultGroup(group: Group): boolean { return this.defaultGroup && this.defaultGroup.id == group.id } } diff --git a/src/app/modules/groups/services/group.service.ts b/src/app/modules/groups/services/group.service.ts index 565332c..56fbccb 100644 --- a/src/app/modules/groups/services/group.service.ts +++ b/src/app/modules/groups/services/group.service.ts @@ -1,7 +1,7 @@ import {Injectable} from '@angular/core' import {ApiService} from '../../shared/service/api.service' import {Observable} from 'rxjs' -import {Employee, EmployeeGroup, EmployeePermission} from '../../shared/model/employee' +import {User, Group, Permission} from '../../shared/model/user' import {tap} from 'rxjs/operators' @Injectable({ @@ -13,11 +13,11 @@ export class GroupService { ) { } - get all(): Observable { - return this.api.get('/employee/group') + get all(): Observable { + return this.api.get('/user/group') } - get allWithDefault(): Observable { + get allWithDefault(): Observable { return this.all .pipe(tap(groups => groups.unshift({ id: -1, @@ -26,41 +26,41 @@ export class GroupService { }))) } - getById(id: number): Observable { - return this.api.get(`/employee/group/${id}`) + getById(id: number): Observable { + return this.api.get(`/user/group/${id}`) } - get defaultGroup(): Observable { - return this.api.get('/employee/group/default') + get defaultGroup(): Observable { + return this.api.get('/user/group/default') } - setDefaultGroup(value: EmployeeGroup): Observable { - return this.api.post(`/employee/group/default/${value.id}`, {}) + setDefaultGroup(value: Group): Observable { + return this.api.post(`/user/group/default/${value.id}`, {}) } - getEmployeesForGroup(id: number): Observable { - return this.api.get(`/employee/group/${id}/employees`) + getUsersForGroup(id: number): Observable { + return this.api.get(`/user/group/${id}/users`) } - addEmployeeToGroup(id: number, employee: Employee): Observable { - return this.api.put(`/employee/group/${id}/${employee.id}`) + addUserToGroup(id: number, user: User): Observable { + return this.api.put(`/user/group/${id}/${user.id}`) } - removeEmployeeFromGroup(employee: Employee): Observable { - return this.api.delete(`/employee/group/${employee.group.id}/${employee.id}`) + removeUserFromGroup(user: User): Observable { + return this.api.delete(`/user/group/${user.group.id}/${user.id}`) } - save(name: string, permissions: EmployeePermission[]): Observable { + save(name: string, permissions: Permission[]): Observable { const group = {name, permissions} - return this.api.post('/employee/group', group) + return this.api.post('/user/group', group) } - update(id: number, name: string, permissions: EmployeePermission[]): Observable { + update(id: number, name: string, permissions: Permission[]): Observable { const group = {id, name, permissions} - return this.api.put('/employee/group', group) + return this.api.put('/user/group', group) } - delete(id: number): Observable { - return this.api.delete(`/employee/group/${id}`) + delete(id: number): Observable { + return this.api.delete(`/user/group/${id}`) } } diff --git a/src/app/modules/material-type/pages/add/add.component.ts b/src/app/modules/material-type/pages/add/add.component.ts index 9b2fe76..1c2b1ff 100644 --- a/src/app/modules/material-type/pages/add/add.component.ts +++ b/src/app/modules/material-type/pages/add/add.component.ts @@ -5,6 +5,7 @@ import {MaterialTypeService} from '../../service/material-type.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-add', @@ -56,11 +57,13 @@ export class AddComponent extends ErrorHandlingComponent { constructor( private materialTypeService: MaterialTypeService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Nouveau type de produit' } submit(values) { diff --git a/src/app/modules/material-type/pages/edit/edit.component.html b/src/app/modules/material-type/pages/edit/edit.component.html index f817426..b02be31 100644 --- a/src/app/modules/material-type/pages/edit/edit.component.html +++ b/src/app/modules/material-type/pages/edit/edit.component.html @@ -3,7 +3,6 @@ title="Modifier le group {{materialType.name}}" deleteConfirmMessage="Voulez-vous vraiment supprimer le type de produit {{materialType.name}}?" backButtonLink="/catalog/materialtype/list" - deletePermission="REMOVE_MATERIAL_TYPES" [entity]="materialType" [formFields]="formFields" (submit)="submit($event)" diff --git a/src/app/modules/material-type/pages/edit/edit.component.ts b/src/app/modules/material-type/pages/edit/edit.component.ts index fcf3755..8db8933 100644 --- a/src/app/modules/material-type/pages/edit/edit.component.ts +++ b/src/app/modules/material-type/pages/edit/edit.component.ts @@ -6,6 +6,7 @@ import {MaterialTypeService} from '../../service/material-type.service' import {FormField} from '../../../shared/components/entity-add/entity-add.component' import {Validators} from '@angular/forms' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-edit', @@ -58,6 +59,7 @@ export class EditComponent extends ErrorHandlingComponent { constructor( private materialTypeService: MaterialTypeService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -72,7 +74,10 @@ export class EditComponent extends ErrorHandlingComponent { this.subscribeEntityById( this.materialTypeService, id, - materialType => this.materialType = materialType + materialType => { + this.appState.title = `${materialType.name} (Modifications)` + this.materialType = materialType + } ) } diff --git a/src/app/modules/material-type/pages/list/list.component.ts b/src/app/modules/material-type/pages/list/list.component.ts index c729638..2556388 100644 --- a/src/app/modules/material-type/pages/list/list.component.ts +++ b/src/app/modules/material-type/pages/list/list.component.ts @@ -1,9 +1,10 @@ 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 {Permission} from '../../../shared/model/user' import {ActivatedRoute, Router} from '@angular/router' import {ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-list', @@ -21,17 +22,19 @@ export class ListComponent extends ErrorHandlingComponent { { text: 'Modifier', linkFn: t => `/catalog/materialtype/edit/${t.id}`, - permission: EmployeePermission.EDIT_MATERIAL_TYPES, + permission: Permission.EDIT_MATERIAL_TYPES, disabledFn: t => t.systemType } ] constructor( private materialTypeService: MaterialTypeService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Types de produit' } } diff --git a/src/app/modules/material/pages/add/add.component.ts b/src/app/modules/material/pages/add/add.component.ts index cfc3eef..d5e710a 100644 --- a/src/app/modules/material/pages/add/add.component.ts +++ b/src/app/modules/material/pages/add/add.component.ts @@ -6,7 +6,8 @@ import {MaterialTypeService} from '../../../material-type/service/material-type. import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {map} from 'rxjs/operators' -import {ErrorHandler, ErrorHandlerComponent, ErrorService} from '../../../shared/service/error.service' +import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-add', @@ -68,11 +69,13 @@ export class AddComponent extends ErrorHandlingComponent { constructor( private materialService: MaterialService, private materialTypeService: MaterialTypeService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Nouveau produit' } submit(values) { diff --git a/src/app/modules/material/pages/edit/edit.component.html b/src/app/modules/material/pages/edit/edit.component.html index e409b00..e7be59e 100644 --- a/src/app/modules/material/pages/edit/edit.component.html +++ b/src/app/modules/material/pages/edit/edit.component.html @@ -3,7 +3,6 @@ title="Modifier le produit {{material.name}}" deleteConfirmMessage="Voulez-vous vraiment supprimer le produit {{material.name}}?" backButtonLink="/catalog/material/list" - deletePermission="REMOVE_MATERIALS" [entity]="material" [formFields]="formFields" (submit)="submit($event)" diff --git a/src/app/modules/material/pages/edit/edit.component.ts b/src/app/modules/material/pages/edit/edit.component.ts index ed7332c..aadc5d7 100644 --- a/src/app/modules/material/pages/edit/edit.component.ts +++ b/src/app/modules/material/pages/edit/edit.component.ts @@ -8,6 +8,7 @@ import {ActivatedRoute, Router} from '@angular/router' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {Material, openSimdut} from '../../../shared/model/material.model' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-edit', @@ -80,6 +81,7 @@ export class EditComponent extends ErrorHandlingComponent { constructor( private materialService: MaterialService, private materialTypeService: MaterialTypeService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -96,7 +98,10 @@ export class EditComponent extends ErrorHandlingComponent { this.subscribeEntityById( this.materialService, id, - material => this.material = material + material => { + this.appState.title = `${material.name} (Modifications)` + this.material = material + } ) } diff --git a/src/app/modules/material/pages/inventory/inventory.component.ts b/src/app/modules/material/pages/inventory/inventory.component.ts index f706215..aafe764 100644 --- a/src/app/modules/material/pages/inventory/inventory.component.ts +++ b/src/app/modules/material/pages/inventory/inventory.component.ts @@ -1,7 +1,7 @@ import {Component, ViewChild} from '@angular/core' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {MaterialService} from '../../service/material.service' -import {EmployeePermission} from '../../../shared/model/employee' +import {Permission} from '../../../shared/model/user' import {ActivatedRoute, Router} from '@angular/router' import {ErrorService} from '../../../shared/service/error.service' import {Material, openSimdut} from '../../../shared/model/material.model' @@ -11,6 +11,7 @@ import {MatSort} from '@angular/material/sort' import {MatTableDataSource} from '@angular/material/table' import {MaterialTypeService} from '../../../material-type/service/material-type.service' import {InventoryService} from '../../service/inventory.service' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-list', @@ -38,11 +39,13 @@ export class InventoryComponent extends ErrorHandlingComponent { private materialTypeService: MaterialTypeService, private inventoryService: InventoryService, private accountService: AccountService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Inventaire' } ngOnInit() { @@ -116,10 +119,10 @@ export class InventoryComponent extends ErrorHandlingComponent { } get canEditMaterial(): boolean { - return this.accountService.hasPermission(EmployeePermission.EDIT_MATERIALS) + return this.accountService.hasPermission(Permission.EDIT_MATERIALS) } get canAddToInventory(): boolean { - return this.accountService.hasPermission(EmployeePermission.ADD_TO_INVENTORY) + return this.accountService.hasPermission(Permission.ADD_TO_INVENTORY) } } diff --git a/src/app/modules/shared/app-state.ts b/src/app/modules/shared/app-state.ts index 772bd25..f850b3e 100644 --- a/src/app/modules/shared/app-state.ts +++ b/src/app/modules/shared/app-state.ts @@ -1,39 +1,46 @@ -import {Injectable} from "@angular/core"; -import {Employee} from "./model/employee"; -import {Subject} from "rxjs"; +import {Injectable} from '@angular/core' +import {User} from './model/user' +import {Subject} from 'rxjs' +import {Title} from '@angular/platform-browser' @Injectable({ providedIn: 'root' }) export class AppState { - private readonly KEY_AUTHENTICATED = "authenticated" - private readonly KEY_AUTHENTICATION_EXPIRATION = "authentication-expiration" - private readonly KEY_LOGGED_IN_EMPLOYEE = "logged-in-employee" + private readonly KEY_AUTHENTICATED = 'authenticated' + private readonly KEY_AUTHENTICATION_EXPIRATION = 'authentication-expiration' + private readonly KEY_LOGGED_IN_USER = 'logged-in-user' - authenticatedUser$ = new Subject<{ authenticated: boolean, authenticatedUser: Employee }>() + authenticatedUser$ = new Subject<{ authenticated: boolean, authenticatedUser: User }>() serverOnline$ = new Subject() - resetAuthenticatedEmployee() { + constructor( + private titleService: Title + ) { + } + + resetAuthenticatedUser() { this.isAuthenticated = false this.authenticationExpiration = -1 - this.authenticatedEmployee = null - + this.authenticatedUser = null } set isServerOnline(isOnline: boolean) { - if (!isOnline) this.authenticatedEmployee = null - this.serverOnline$.next(isOnline); + if (!isOnline) { + this.authenticatedUser = null + } + this.serverOnline$.next(isOnline) } get isAuthenticated(): boolean { - return sessionStorage.getItem(this.KEY_AUTHENTICATED) === "true" + return sessionStorage.getItem(this.KEY_AUTHENTICATED) === 'true' } set isAuthenticated(value: boolean) { sessionStorage.setItem(this.KEY_AUTHENTICATED, value.toString()) this.authenticatedUser$.next({ authenticated: value, - authenticatedUser: this.authenticatedEmployee + authenticatedUser: this.authenticatedUser }) } @@ -45,20 +52,24 @@ export class AppState { sessionStorage.setItem(this.KEY_AUTHENTICATION_EXPIRATION, value.toString()) } - get authenticatedEmployee(): Employee { - const employeeString = sessionStorage.getItem(this.KEY_LOGGED_IN_EMPLOYEE) - return employeeString ? JSON.parse(employeeString) : null + get authenticatedUser(): User { + const userString = sessionStorage.getItem(this.KEY_LOGGED_IN_USER) + return userString ? JSON.parse(userString) : null } - set authenticatedEmployee(value: Employee) { + set authenticatedUser(value: User) { if (value === null) { - sessionStorage.removeItem(this.KEY_LOGGED_IN_EMPLOYEE) + sessionStorage.removeItem(this.KEY_LOGGED_IN_USER) } else { - sessionStorage.setItem(this.KEY_LOGGED_IN_EMPLOYEE, JSON.stringify(value)) + sessionStorage.setItem(this.KEY_LOGGED_IN_USER, JSON.stringify(value)) } this.authenticatedUser$.next({ authenticated: this.isAuthenticated, authenticatedUser: value }) } + + set title(value: string) { + this.titleService.setTitle(`CRE: ${value}`) + } } diff --git a/src/app/modules/shared/components/action-bar/action-bar.html b/src/app/modules/shared/components/action-bar/action-bar.html new file mode 100644 index 0000000..4bad334 --- /dev/null +++ b/src/app/modules/shared/components/action-bar/action-bar.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/modules/shared/components/action-bar/action-bar.module.ts b/src/app/modules/shared/components/action-bar/action-bar.module.ts new file mode 100644 index 0000000..5188c1b --- /dev/null +++ b/src/app/modules/shared/components/action-bar/action-bar.module.ts @@ -0,0 +1,15 @@ +import {NgModule} from '@angular/core' +import {CreActionBar, CreActionGroup} from './action-bar' + +@NgModule({ + exports: [ + CreActionBar, + CreActionGroup + ], + declarations: [ + CreActionBar, + CreActionGroup + ] +}) +export class CreActionBarModule { +} diff --git a/src/app/modules/shared/components/action-bar/action-bar.ts b/src/app/modules/shared/components/action-bar/action-bar.ts new file mode 100644 index 0000000..506bed9 --- /dev/null +++ b/src/app/modules/shared/components/action-bar/action-bar.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core' + +@Component({ + selector: 'cre-action-group', + templateUrl: 'action-group.html', + styleUrls: ['action-group.sass'] +}) +export class CreActionGroup {} + +@Component({ + selector: 'cre-action-bar', + templateUrl: 'action-bar.html' +}) +export class CreActionBar {} diff --git a/src/app/modules/shared/components/action-bar/action-group.html b/src/app/modules/shared/components/action-bar/action-group.html new file mode 100644 index 0000000..dfdbcc7 --- /dev/null +++ b/src/app/modules/shared/components/action-bar/action-group.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/modules/shared/components/action-bar/action-group.sass b/src/app/modules/shared/components/action-bar/action-group.sass new file mode 100644 index 0000000..4c50bf7 --- /dev/null +++ b/src/app/modules/shared/components/action-bar/action-group.sass @@ -0,0 +1,4 @@ +div + display: flex + flex-direction: row + gap: 1rem diff --git a/src/app/modules/shared/components/buttons/buttons.module.ts b/src/app/modules/shared/components/buttons/buttons.module.ts new file mode 100644 index 0000000..1dd694f --- /dev/null +++ b/src/app/modules/shared/components/buttons/buttons.module.ts @@ -0,0 +1,22 @@ +import {NgModule} from '@angular/core' +import {CreAccentButtonComponent, CreButtonComponent, CrePrimaryButtonComponent, CreWarnButtonComponent} from './buttons' +import {MatButtonModule} from '@angular/material/button' + +@NgModule({ + declarations: [ + CreButtonComponent, + CrePrimaryButtonComponent, + CreAccentButtonComponent, + CreWarnButtonComponent + ], + imports: [ + MatButtonModule + ], + exports: [ + CrePrimaryButtonComponent, + CreAccentButtonComponent, + CreWarnButtonComponent + ] +}) +export class CreButtonsModule { +} diff --git a/src/app/modules/shared/components/buttons/buttons.ts b/src/app/modules/shared/components/buttons/buttons.ts new file mode 100644 index 0000000..7b3679e --- /dev/null +++ b/src/app/modules/shared/components/buttons/buttons.ts @@ -0,0 +1,51 @@ +import {Component, Input} from '@angular/core' +import {ThemePalette} from '@angular/material/core' + +@Component({ + selector: 'cre-button', + template: ` + + ` +}) +export class CreButtonComponent { + @Input() color: ThemePalette + @Input() disabled = false +} + +@Component({ + selector: 'cre-primary-button', + template: ` + + + + ` +}) +export class CrePrimaryButtonComponent { + @Input() disabled = false +} + +@Component({ + selector: 'cre-accent-button', + template: ` + + + + ` +}) +export class CreAccentButtonComponent { + @Input() disabled = false +} + +@Component({ + selector: 'cre-warn-button', + template: ` + + + + ` +}) +export class CreWarnButtonComponent { + @Input() disabled = false +} diff --git a/src/app/modules/shared/components/color-preview/color-preview.html b/src/app/modules/shared/components/color-preview/color-preview.html new file mode 100644 index 0000000..76b6bf6 --- /dev/null +++ b/src/app/modules/shared/components/color-preview/color-preview.html @@ -0,0 +1,6 @@ +
+
{{recipe.gloss}}%
+
diff --git a/src/app/modules/shared/components/color-preview/color-preview.sass b/src/app/modules/shared/components/color-preview/color-preview.sass new file mode 100644 index 0000000..28343ed --- /dev/null +++ b/src/app/modules/shared/components/color-preview/color-preview.sass @@ -0,0 +1,20 @@ +.recipe-color-circle + color: black + width: 2.2rem + height: 2.2rem + border-radius: 1.1rem + margin-left: 1rem + font-size: 13px + + &.dark-mode + color: white + width: 2.3rem + height: 2.3rem + border: solid 1px white + + div + position: absolute + width: 2rem + text-align: center + margin-top: 7px + margin-left: 1px diff --git a/src/app/modules/shared/components/color-preview/color-preview.ts b/src/app/modules/shared/components/color-preview/color-preview.ts new file mode 100644 index 0000000..d713ec2 --- /dev/null +++ b/src/app/modules/shared/components/color-preview/color-preview.ts @@ -0,0 +1,15 @@ +import {getRecipeLuma, Recipe} from '../../model/recipe.model' +import {Component, Input} from '@angular/core' + +@Component({ + selector: 'cre-color-preview', + templateUrl: 'color-preview.html', + styleUrls: ['color-preview.sass'] +}) +export class CreColorPreview { + @Input() recipe: Recipe + + get isDarkColor(): boolean { + return getRecipeLuma(this.recipe) < 100 + } +} diff --git a/src/app/modules/shared/components/entity-edit/entity-edit.component.html b/src/app/modules/shared/components/entity-edit/entity-edit.component.html index 83f48fd..d1bc6df 100644 --- a/src/app/modules/shared/components/entity-edit/entity-edit.component.html +++ b/src/app/modules/shared/components/entity-edit/entity-edit.component.html @@ -34,7 +34,7 @@ - + diff --git a/src/app/modules/shared/components/entity-edit/entity-edit.component.ts b/src/app/modules/shared/components/entity-edit/entity-edit.component.ts index 41864b1..6f29e5b 100644 --- a/src/app/modules/shared/components/entity-edit/entity-edit.component.ts +++ b/src/app/modules/shared/components/entity-edit/entity-edit.component.ts @@ -1,7 +1,6 @@ -import {ChangeDetectorRef, Component, EventEmitter, Input, Output} from '@angular/core' +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({ @@ -15,7 +14,6 @@ export class EntityEditComponent { @Input() deleteConfirmMessage: string @Input() backButtonLink: string @Input() formFields: FormField[] - @Input() deletePermission: EmployeePermission @Input() disableButtons: boolean @Input() noTopMargin = false @Output() submit = new EventEmitter() @@ -55,8 +53,4 @@ export class EntityEditComponent { }) return values } - - get canDelete(): boolean { - return this.accountService.hasPermission(this.deletePermission) - } } diff --git a/src/app/modules/shared/components/entity-list/entity-list.component.ts b/src/app/modules/shared/components/entity-list/entity-list.component.ts index d5ddb0f..115ad50 100644 --- a/src/app/modules/shared/components/entity-list/entity-list.component.ts +++ b/src/app/modules/shared/components/entity-list/entity-list.component.ts @@ -1,7 +1,7 @@ import {Component, Input} from '@angular/core' import {Observable} from 'rxjs' import {AccountService} from '../../../accounts/services/account.service' -import {EmployeePermission} from '../../model/employee' +import {Permission} from '../../model/user' import {animate, state, style, transition, trigger} from '@angular/animations' @Component({ @@ -22,7 +22,7 @@ export class EntityListComponent { @Input() icons: TableIcon[] @Input() buttons?: TableButton[] @Input() addLink: string - @Input() addPermission: EmployeePermission + @Input() addPermission: Permission @Input() expandable = false @Input() rowDetailsTemplate @@ -38,7 +38,7 @@ export class EntityListComponent { return !button.permission || this.hasPermission(button.permission) } - hasPermission(permission: EmployeePermission): boolean { + hasPermission(permission: Permission): boolean { return this.accountService.hasPermission(permission) } @@ -119,7 +119,7 @@ export class TableButton { public link: string | { externalLink: string } | null, public linkFn: (T) => string | { externalLink: string } | null, public clickFn: (T) => void, - public permission: EmployeePermission | null, + public permission: Permission | null, public disabledFn: (T) => boolean | null ) { } diff --git a/src/app/modules/shared/components/forms/form.html b/src/app/modules/shared/components/forms/form.html new file mode 100644 index 0000000..1d6e89b --- /dev/null +++ b/src/app/modules/shared/components/forms/form.html @@ -0,0 +1,15 @@ + + + + + + + +
+ +
+
+ + + +
diff --git a/src/app/modules/shared/components/forms/forms.module.ts b/src/app/modules/shared/components/forms/forms.module.ts new file mode 100644 index 0000000..2ee4a91 --- /dev/null +++ b/src/app/modules/shared/components/forms/forms.module.ts @@ -0,0 +1,28 @@ +import {NgModule} from '@angular/core' +import {CreFormActions, CreFormComponent, CreFormContent, CreFormTitle} from './forms' +import {MatCardModule} from '@angular/material/card' +import {CommonModule} from '@angular/common' +import {MatButtonModule} from '@angular/material/button' +import {ReactiveFormsModule} from '@angular/forms' + +@NgModule({ + declarations: [ + CreFormComponent, + CreFormTitle, + CreFormContent, + CreFormActions + ], + exports: [ + CreFormComponent, + CreFormTitle, + CreFormContent, + CreFormActions + ], + imports: [ + MatCardModule, + CommonModule, + MatButtonModule, + ReactiveFormsModule + ] +}) +export class CreFormsModule {} diff --git a/src/app/modules/shared/components/forms/forms.sass b/src/app/modules/shared/components/forms/forms.sass new file mode 100644 index 0000000..fffedc8 --- /dev/null +++ b/src/app/modules/shared/components/forms/forms.sass @@ -0,0 +1,10 @@ +cre-form + display: block + + mat-card + width: inherit + + cre-form-actions + display: flex + flex-direction: row + gap: 1rem diff --git a/src/app/modules/shared/components/forms/forms.ts b/src/app/modules/shared/components/forms/forms.ts new file mode 100644 index 0000000..5770d23 --- /dev/null +++ b/src/app/modules/shared/components/forms/forms.ts @@ -0,0 +1,51 @@ +import {Component, ContentChild, Directive, Input, OnInit, ViewEncapsulation} from '@angular/core' +import {FormBuilder, FormGroup} from '@angular/forms' + +@Directive({ + selector: 'cre-form-title' +}) +export class CreFormTitle { +} + +@Directive({ + selector: 'cre-form-content' +}) +export class CreFormContent { +} + +@Directive({ + selector: 'cre-form-actions' +}) +export class CreFormActions { + +} + +@Component({ + selector: 'cre-form', + templateUrl: './form.html', + styleUrls: ['forms.sass'], + encapsulation: ViewEncapsulation.None +}) +export class CreFormComponent implements OnInit { + @ContentChild(CreFormActions) formActions: CreFormActions + @Input() formControls: { [key: string]: any } + + form: FormGroup + + constructor( + private formBuilder: FormBuilder + ) { + } + + ngOnInit(): void { + this.form = this.formBuilder.group(this.formControls) + } + + get hasActions(): boolean { + return this.formActions === true + } + + get invalid(): boolean { + return !this.form || this.form.invalid + } +} diff --git a/src/app/modules/shared/components/header/header.component.html b/src/app/modules/shared/components/header/header.component.html index 51aabfa..7a2bb30 100644 --- a/src/app/modules/shared/components/header/header.component.html +++ b/src/app/modules/shared/components/header/header.component.html @@ -16,7 +16,7 @@
- + + +
+ `, + styleUrls: ['info-banner.component.sass'] +}) +export class InfoBannerComponent { +} + +@Component({ + selector: 'info-banner-title', + template: ` +

+ +

+ `, + styleUrls: ['info-banner.component.sass'] +}) +export class InfoBannerTitleComponent { +} + +@Component({ + selector: 'info-banner-content', + template: ` +
+ +
+ `, + styleUrls: ['info-banner.component.sass'] +}) +export class InfoBannerContentComponent { +} + +@Component({ + selector: 'info-banner-section', + template: ` +
+ +
+ `, + styleUrls: ['info-banner.component.sass'] +}) +export class InfoBannerSectionComponent { +} diff --git a/src/app/modules/shared/components/info-banner/info-banner.module.ts b/src/app/modules/shared/components/info-banner/info-banner.module.ts new file mode 100644 index 0000000..4afef82 --- /dev/null +++ b/src/app/modules/shared/components/info-banner/info-banner.module.ts @@ -0,0 +1,24 @@ +import {NgModule} from '@angular/core' +import { + InfoBannerComponent, + InfoBannerContentComponent, + InfoBannerSectionComponent, + InfoBannerTitleComponent +} from './info-banner.component' + +@NgModule({ + declarations: [ + InfoBannerComponent, + InfoBannerTitleComponent, + InfoBannerContentComponent, + InfoBannerSectionComponent + ], + exports: [ + InfoBannerComponent, + InfoBannerTitleComponent, + InfoBannerContentComponent, + InfoBannerSectionComponent + ] +}) +export class InfoBannerModule { +} diff --git a/src/app/modules/shared/components/inputs/autocomplete-input.html b/src/app/modules/shared/components/inputs/autocomplete-input.html new file mode 100644 index 0000000..bc21b24 --- /dev/null +++ b/src/app/modules/shared/components/inputs/autocomplete-input.html @@ -0,0 +1,31 @@ + + {{label}} + + + + + Ce champ est requis + + + + + {{option}} + + + diff --git a/src/app/modules/shared/components/inputs/chip-combo-box.html b/src/app/modules/shared/components/inputs/chip-combo-box.html new file mode 100644 index 0000000..443ff0c --- /dev/null +++ b/src/app/modules/shared/components/inputs/chip-combo-box.html @@ -0,0 +1,37 @@ + + {{label}} + + + {{value}} + cancel + + + + + + + + Ce champ est requis + + + + + + {{option.display ? option.display : option.value}} + + + diff --git a/src/app/modules/shared/components/inputs/chip-input.html b/src/app/modules/shared/components/inputs/chip-input.html new file mode 100644 index 0000000..4faf8f5 --- /dev/null +++ b/src/app/modules/shared/components/inputs/chip-input.html @@ -0,0 +1,23 @@ + + {{label}} + + + {{value}} + cancel + + + + + diff --git a/src/app/modules/shared/components/inputs/combo-box.html b/src/app/modules/shared/components/inputs/combo-box.html new file mode 100644 index 0000000..5170f8c --- /dev/null +++ b/src/app/modules/shared/components/inputs/combo-box.html @@ -0,0 +1,22 @@ + + {{label}} + + + + + Ce champ est requis + + + + + + {{option.value}} + + + diff --git a/src/app/modules/shared/components/inputs/input.html b/src/app/modules/shared/components/inputs/input.html new file mode 100644 index 0000000..d7ef1cd --- /dev/null +++ b/src/app/modules/shared/components/inputs/input.html @@ -0,0 +1,28 @@ + + {{label}} + + + + + Ce champ est requis + + + diff --git a/src/app/modules/shared/components/inputs/inputs.module.ts b/src/app/modules/shared/components/inputs/inputs.module.ts new file mode 100644 index 0000000..d28ce15 --- /dev/null +++ b/src/app/modules/shared/components/inputs/inputs.module.ts @@ -0,0 +1,46 @@ +import {NgModule} from '@angular/core' +import { + CreAutocompleteInputComponent, + CreChipComboBoxComponent, + CreChipInputComponent, + CreComboBoxComponent, + CreInputComponent +} from './inputs' +import {MatInputModule} from '@angular/material/input' +import {MatIconModule} from '@angular/material/icon' +import {FormsModule, ReactiveFormsModule} from '@angular/forms' +import {CommonModule} from '@angular/common' +import {MatCardModule} from '@angular/material/card' +import {MatListModule} from '@angular/material/list' +import {MatAutocompleteModule} from '@angular/material/autocomplete' +import {MatChipsModule} from '@angular/material/chips' + +@NgModule({ + declarations: [ + CreInputComponent, + CreChipInputComponent, + CreAutocompleteInputComponent, + CreComboBoxComponent, + CreChipComboBoxComponent + ], + imports: [ + MatInputModule, + MatAutocompleteModule, + MatIconModule, + ReactiveFormsModule, + CommonModule, + MatCardModule, + MatListModule, + MatChipsModule, + FormsModule, + ], + exports: [ + CreInputComponent, + CreComboBoxComponent, + CreChipComboBoxComponent, + CreChipInputComponent, + CreAutocompleteInputComponent + ] +}) +export class CreInputsModule { +} diff --git a/src/app/modules/shared/components/inputs/inputs.ts b/src/app/modules/shared/components/inputs/inputs.ts new file mode 100644 index 0000000..1a64d73 --- /dev/null +++ b/src/app/modules/shared/components/inputs/inputs.ts @@ -0,0 +1,190 @@ +import { + Component, + ContentChild, + ElementRef, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core' +import {AbstractControl, FormControl, ValidationErrors, ValidatorFn} from '@angular/forms' +import {COMMA, ENTER} from '@angular/cdk/keycodes' +import {Observable, Subject} from 'rxjs' +import {map, takeUntil} from 'rxjs/operators' +import {MatChipInputEvent} from '@angular/material/chips' +import {MatAutocomplete, MatAutocompleteSelectedEvent} from '@angular/material/autocomplete' + +@Component({ + selector: 'cre-input', + templateUrl: 'input.html', + encapsulation: ViewEncapsulation.None +}) +export class CreInputComponent { + @Input() control: FormControl | null + @Input() type = 'text' + @Input() label: string + @Input() icon: string + @Input() required = true + @Input() autocomplete = true + @Input() placeholder: string | null + @Input() step = 1 + @Input() value + + @Output() valueChange = new EventEmitter() + + @ContentChild(TemplateRef) errors: TemplateRef +} + +@Component({ + selector: 'cre-autocomplete-input', + templateUrl: 'autocomplete-input.html', + encapsulation: ViewEncapsulation.None +}) +export class CreAutocompleteInputComponent { + @Input() control: FormControl | null + @Input() label: string + @Input() icon: string + @Input() required = true + @Input() options: Observable + @Input() value + + @Output() valueChange = new EventEmitter() + + @ContentChild(TemplateRef) errors: TemplateRef +} + +@Component({ + selector: 'cre-chip-input', + templateUrl: 'chip-input.html', + encapsulation: ViewEncapsulation.None +}) +export class CreChipInputComponent implements OnInit { + @Input() control: FormControl + @Input() label: string + @Input() icon: string + @Input() required = true + + inputControl = new FormControl() + + readonly separatorKeysCodes = [ENTER, COMMA] + + selectedValues = [] + + ngOnInit(): void { + if (this.control.value) { + this.selectedValues = this.control.value + } + } + + add(event: MatChipInputEvent): void { + const input = event.input + const value = event.value + + if ((value || '').trim()) { + this.selectedValues.push(value.trim()) + } + + if (input) { + input.value = '' + } + + this.inputControl.setValue(null) + this.control.setValue(this.selectedValues) + } + + remove(value): void { + const index = this.selectedValues.indexOf(value) + + if (index >= 0) { + this.selectedValues.splice(index, 1) + this.control.setValue(this.selectedValues) + } + } +} + +@Component({ + selector: 'cre-combo-box', + templateUrl: 'combo-box.html', + encapsulation: ViewEncapsulation.None +}) +export class CreComboBoxComponent { + @Input() control: FormControl + @Input() label: string + @Input() icon: string + @Input() required = true + @Input() options: Observable + + @ContentChild(TemplateRef) errors: TemplateRef +} + +@Component({ + selector: 'cre-chip-combo-box', + templateUrl: 'chip-combo-box.html', + encapsulation: ViewEncapsulation.None +}) +export class CreChipComboBoxComponent extends CreChipInputComponent implements OnDestroy { + @Input() options: Observable + + @ContentChild(TemplateRef) errors: TemplateRef + @ViewChild('chipInput') chipInput: ElementRef + @ViewChild('auto') matAutocomplete: MatAutocomplete + + filteredOptions: Observable + + private _options: ComboBoxEntry[] + private _destroy$ = new Subject() + + ngOnInit() { + super.ngOnInit() + + this.options.pipe(takeUntil(this._destroy$)) + .subscribe({next: options => this._options = options}) + + this.filteredOptions = this.inputControl.valueChanges.pipe( + map((query: string | null) => query ? this._filter(query) : this._options.slice()) + ) + } + + ngOnDestroy(): void { + this._destroy$.complete() + } + + selected(event: MatAutocompleteSelectedEvent) { + this.selectedValues.push(this.findValueByKey(event.option.value)) + this.chipInput.nativeElement.value = '' + this.inputControl.setValue(null) + this.control.setValue(this.selectedValues) + } + + get empty(): boolean { + return this.selectedValues.length <= 0 + } + + private _filter(query: string): ComboBoxEntry[] { + const filterValue = query.toString().toLowerCase() + return this._options.filter(option => option.value.toString().toLowerCase().indexOf(filterValue) === 0) + } + + private findValueByKey(key: any): any { + return this._options.filter(o => o.key === key)[0].value + } +} + +export class ComboBoxEntry { + constructor( + public key: any, + public value: any, + public display?: any + ) { + } +} + +export function chipListRequired(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + return !control.value || control.value.length <= 0 ? {required: true} : null + } +} diff --git a/src/app/modules/shared/components/nav/nav.component.sass b/src/app/modules/shared/components/nav/nav.component.sass index 05f5622..26455a3 100644 --- a/src/app/modules/shared/components/nav/nav.component.sass +++ b/src/app/modules/shared/components/nav/nav.component.sass @@ -3,7 +3,6 @@ nav position: relative z-index: 90 - padding-bottom: 1px a opacity: 1 diff --git a/src/app/modules/shared/components/nav/nav.component.ts b/src/app/modules/shared/components/nav/nav.component.ts index 8de709b..7741af3 100644 --- a/src/app/modules/shared/components/nav/nav.component.ts +++ b/src/app/modules/shared/components/nav/nav.component.ts @@ -1,5 +1,5 @@ import {Component, Input, OnDestroy, OnInit} from '@angular/core'; -import {Employee, EmployeePermission} from "../../model/employee"; +import {User, Permission} from "../../model/user"; import {AccountService} from "../../../accounts/services/account.service"; import {ActivatedRoute, Router} from "@angular/router"; import {takeUntil} from "rxjs/operators"; @@ -27,7 +27,7 @@ export class NavComponent implements OnInit, OnDestroy { ngOnInit(): void { this._activeLink = this.router.url - this.updateEnabledLinks(this.appState.authenticatedEmployee) + this.updateEnabledLinks(this.appState.authenticatedUser) this.appState.authenticatedUser$ .pipe(takeUntil(this._destroy$)) @@ -54,10 +54,10 @@ export class NavComponent implements OnInit, OnDestroy { return this._activeLink } - private updateEnabledLinks(employee: Employee) { + private updateEnabledLinks(user: User) { this.links.forEach(l => { if (l.permission) { - l.enabled = employee && employee.permissions.indexOf(l.permission) >= 0; + l.enabled = user && user.permissions.indexOf(l.permission) >= 0; } }) } @@ -67,7 +67,7 @@ export class NavLink { constructor( public route: string, public title: string, - public permission?: EmployeePermission, + public permission?: Permission, public enabled? ) { } diff --git a/src/app/modules/shared/components/permissions-field/permissions-field.component.ts b/src/app/modules/shared/components/permissions-field/permissions-field.component.ts index 5a265c6..1b2b80d 100644 --- a/src/app/modules/shared/components/permissions-field/permissions-field.component.ts +++ b/src/app/modules/shared/components/permissions-field/permissions-field.component.ts @@ -1,9 +1,9 @@ import {Component, Input, OnInit, ViewChild} from '@angular/core'; -import {EmployeePermission, mapped_permissions} from "../../model/employee"; +import {Permission, mapped_permissions} from "../../model/user"; import {FormControl} from "@angular/forms"; import {AccountService} from "../../../accounts/services/account.service"; -// The current permissions field component. Workaround to be able to access a permission field in template. (See employee/AddComponent) +// The current permissions field component. Workaround to be able to access a permission field in template. (See user/AddComponent) // This workaround prevent the use of several permissions field component at the same time. export let currentPermissionsFieldComponent: PermissionsFieldComponent | null @@ -13,7 +13,7 @@ export let currentPermissionsFieldComponent: PermissionsFieldComponent | null styleUrls: ['./permissions-field.component.sass'] }) export class PermissionsFieldComponent implements OnInit { - @Input() enabledPermissions: EmployeePermission[] + @Input() enabledPermissions: Permission[] @Input() title = 'Permissions' @Input() required = true @@ -29,12 +29,11 @@ export class PermissionsFieldComponent implements OnInit { ngOnInit(): void { this.mapPermissions('view') this.mapPermissions('edit') - this.mapPermissions('remove') this.mapPermissions('other') if (this.enabledPermissions) { this.enabledPermissions.forEach(p => { - if (EmployeePermission[p]) { + if (Permission[p]) { const control = this.findPermissionControl(p) control.control.setValue(true) this.togglePermission(control, true) @@ -74,7 +73,7 @@ export class PermissionsFieldComponent implements OnInit { return false } - get allEnabledPermissions(): EmployeePermission[] { + get allEnabledPermissions(): Permission[] { return this.allPermissionControls().filter(p => p.control.value).map(p => p.permission) } @@ -96,7 +95,7 @@ export class PermissionsFieldComponent implements OnInit { return Object.values(this.permissionControls).flatMap(p => p) } - private findPermissionControl(permission: EmployeePermission): any { + private findPermissionControl(permission: Permission): any { return this.allPermissionControls().filter(p => p.permission === permission)[0] } diff --git a/src/app/modules/shared/components/permissions-list/permissions-list.component.ts b/src/app/modules/shared/components/permissions-list/permissions-list.component.ts index b63ac16..18ddb0e 100644 --- a/src/app/modules/shared/components/permissions-list/permissions-list.component.ts +++ b/src/app/modules/shared/components/permissions-list/permissions-list.component.ts @@ -1,5 +1,5 @@ import {Component, Input, OnInit} from '@angular/core' -import {Employee, EmployeeGroup, EmployeePermission, mapped_permissions} from '../../model/employee' +import {User, Group, Permission, mapped_permissions} from '../../model/user' @Component({ selector: 'cre-permissions-list', @@ -7,8 +7,8 @@ import {Employee, EmployeeGroup, EmployeePermission, mapped_permissions} from '. styleUrls: ['./permissions-list.component.sass'] }) export class PermissionsListComponent { - @Input() employee: Employee - @Input() group: EmployeeGroup + @Input() user: User + @Input() group: Group // @ts-ignore private _permissions = Object.values(mapped_permissions).flatMap(p => p) @@ -16,12 +16,12 @@ export class PermissionsListComponent { constructor() { } - get permissions(): EmployeePermission[] { + get permissions(): Permission[] { // @ts-ignore - return this.filterPermissions(this.employee ? this.employee.permissions : this.group.permissions) + return this.filterPermissions(this.user ? this.user.permissions : this.group.permissions) } - private filterPermissions(permissions: EmployeePermission[]) { + private filterPermissions(permissions: Permission[]) { return this._permissions.filter(p => permissions.indexOf(p.permission) >= 0).map(p => p.description) } } diff --git a/src/app/modules/shared/components/tables/table.html b/src/app/modules/shared/components/tables/table.html new file mode 100644 index 0000000..46560ec --- /dev/null +++ b/src/app/modules/shared/components/tables/table.html @@ -0,0 +1,12 @@ + + + + + + +
diff --git a/src/app/modules/shared/components/tables/table.sass b/src/app/modules/shared/components/tables/table.sass new file mode 100644 index 0000000..4458b6e --- /dev/null +++ b/src/app/modules/shared/components/tables/table.sass @@ -0,0 +1,12 @@ +@import "~src/custom-theme" + +cre-table + display: block + width: max-content + + tr + &:hover + background-color: darken(white, 5%) + + &.cre-row-selected + background-color: darken(white, 10%) diff --git a/src/app/modules/shared/components/tables/tables.module.ts b/src/app/modules/shared/components/tables/tables.module.ts new file mode 100644 index 0000000..2e9576d --- /dev/null +++ b/src/app/modules/shared/components/tables/tables.module.ts @@ -0,0 +1,21 @@ +import {NgModule} from '@angular/core' +import {MatTableModule} from '@angular/material/table' +import {CommonModule} from '@angular/common' +import {CreInteractiveCell, CreTable} from './tables' + +@NgModule({ + declarations: [ + CreTable, + CreInteractiveCell + ], + imports: [ + MatTableModule, + CommonModule + ], + exports: [ + CreTable, + CreInteractiveCell, + ] +}) +export class CreTablesModule { +} diff --git a/src/app/modules/shared/components/tables/tables.ts b/src/app/modules/shared/components/tables/tables.ts new file mode 100644 index 0000000..59cec5c --- /dev/null +++ b/src/app/modules/shared/components/tables/tables.ts @@ -0,0 +1,83 @@ +import { + AfterContentInit, + Component, + ContentChildren, + Directive, + HostBinding, + Input, + QueryList, + ViewChild, + ViewEncapsulation +} from '@angular/core' +import {MatColumnDef, MatHeaderRowDef, MatRowDef, MatTable} from '@angular/material/table' + +@Directive({ + selector: '[creInteractiveCell]' +}) +export class CreInteractiveCell implements AfterContentInit { + @Input('creInteractiveCell') index: number + + @HostBinding() hidden = true + + private _selectedIndex = 0 + private _hoverIndex = 0 + + ngAfterContentInit(): void { + this.hidden = this.isHidden + } + + set hoverIndex(index: number) { + this._hoverIndex = index + this.hidden = this.isHidden + } + + set selectedIndex(index: number) { + this._selectedIndex = index + this.hidden = this.isHidden + } + + get isHidden(): boolean { + return this._hoverIndex !== this.index && this._selectedIndex !== this.index + } +} + +@Component({ + selector: 'cre-table', + templateUrl: 'table.html', + styleUrls: ['table.sass'], + encapsulation: ViewEncapsulation.None +}) +export class CreTable implements AfterContentInit { + @ContentChildren(MatHeaderRowDef) headerRowDefs: QueryList + @ContentChildren(MatRowDef) rowDefs: QueryList> + @ContentChildren(MatColumnDef) columnDefs: QueryList + + @ContentChildren(CreInteractiveCell, {descendants: true}) interactiveCells: QueryList + + @ViewChild(MatTable, {static: true}) table: MatTable + + @Input() columns: string[] + @Input() dataSource: T[] + @Input() interactive = true + + selectedIndex = 0 + + ngAfterContentInit(): void { + this.columnDefs.forEach(columnDef => this.table.addColumnDef(columnDef)) + this.rowDefs.forEach(rowDef => this.table.addRowDef(rowDef)) + this.headerRowDefs.forEach(headerRowDef => this.table.addHeaderRowDef(headerRowDef)) + } + + onRowHover(index: number) { + if (this.interactive) { + this.interactiveCells.forEach(cell => cell.hoverIndex = index) + } + } + + onRowSelected(index: number) { + if (this.interactive) { + this.selectedIndex = index + this.interactiveCells.forEach(cell => cell.selectedIndex = index) + } + } +} diff --git a/src/app/modules/shared/components/employee-info/employee-menu.component.html b/src/app/modules/shared/components/user-info/user-menu.component.html similarity index 55% rename from src/app/modules/shared/components/employee-info/employee-menu.component.html rename to src/app/modules/shared/components/user-info/user-menu.component.html index 4830558..d758d48 100644 --- a/src/app/modules/shared/components/employee-info/employee-menu.component.html +++ b/src/app/modules/shared/components/user-info/user-menu.component.html @@ -1,43 +1,43 @@ -
-
+
+ diff --git a/src/app/modules/shared/components/employee-info/employee-menu.component.sass b/src/app/modules/shared/components/user-info/user-menu.component.sass similarity index 93% rename from src/app/modules/shared/components/employee-info/employee-menu.component.sass rename to src/app/modules/shared/components/user-info/user-menu.component.sass index 7da5a0c..d3ffcd7 100644 --- a/src/app/modules/shared/components/employee-info/employee-menu.component.sass +++ b/src/app/modules/shared/components/user-info/user-menu.component.sass @@ -4,14 +4,14 @@ p, labeled-icon margin: 0 color: $light-primary-text -.employee-info-wrapper +.user-info-wrapper margin: 1rem 1rem 0 cursor: pointer &:hover labeled-icon text-decoration: underline -.employee-info-group +.user-info-group margin-left: 0.7rem mat-action-list diff --git a/src/app/modules/shared/components/employee-info/employee-menu.component.ts b/src/app/modules/shared/components/user-info/user-menu.component.ts similarity index 70% rename from src/app/modules/shared/components/employee-info/employee-menu.component.ts rename to src/app/modules/shared/components/user-info/user-menu.component.ts index 70263c3..d04c28a 100644 --- a/src/app/modules/shared/components/employee-info/employee-menu.component.ts +++ b/src/app/modules/shared/components/user-info/user-menu.component.ts @@ -1,20 +1,20 @@ import {Component, OnDestroy, OnInit} from '@angular/core' import {AppState} from '../../app-state' -import {Employee} from '../../model/employee' +import {User} from '../../model/user' import {Subject} from 'rxjs' import {takeUntil} from 'rxjs/operators' import {UrlUtils} from '../../utils/url.utils' import {ActivatedRoute, Router} from '@angular/router' @Component({ - selector: 'cre-employee-menu', - templateUrl: './employee-menu.component.html', - styleUrls: ['./employee-menu.component.sass'] + selector: 'cre-user-menu', + templateUrl: './user-menu.component.html', + styleUrls: ['./user-menu.component.sass'] }) -export class EmployeeMenuComponent implements OnInit, OnDestroy { +export class UserMenuComponent implements OnInit, OnDestroy { authenticated = false - employee: Employee = null - employeeInGroup = false + user: User = null + userInGroup = false menuEnabled = false private destroy$ = new Subject() @@ -29,7 +29,7 @@ export class EmployeeMenuComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.authenticationState(this.appState.isAuthenticated, this.appState.authenticatedEmployee) + this.authenticationState(this.appState.isAuthenticated, this.appState.authenticatedUser) this.appState.authenticatedUser$ .pipe(takeUntil(this.destroy$)) .subscribe({ @@ -52,11 +52,11 @@ export class EmployeeMenuComponent implements OnInit, OnDestroy { this.menuEnabled = false } - private authenticationState(authenticated: boolean, employee: Employee) { + private authenticationState(authenticated: boolean, user: User) { this.authenticated = authenticated - this.employee = employee - if (this.employee != null) { - this.employeeInGroup = this.employee.group != null + this.user = user + if (this.user != null) { + this.userInGroup = this.user.group != null } } } diff --git a/src/app/modules/shared/directives/var.directive.ts b/src/app/modules/shared/directives/var.directive.ts new file mode 100644 index 0000000..9aad369 --- /dev/null +++ b/src/app/modules/shared/directives/var.directive.ts @@ -0,0 +1,25 @@ +import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core' + +@Directive({ + selector: '[ngVar]' +}) +export class VarDirective { + context: any = {} + + constructor( + private vcRef: ViewContainerRef, + private templateRef: TemplateRef + ) { + } + + @Input() + set ngVar(context: any) { + this.context.$implicit = this.context.ngVar = context + this.updateView() + } + + private updateView() { + this.vcRef.clear() + this.vcRef.createEmbeddedView(this.templateRef, this.context) + } +} diff --git a/src/app/modules/shared/model/employee.ts b/src/app/modules/shared/model/employee.ts deleted file mode 100644 index 601676a..0000000 --- a/src/app/modules/shared/model/employee.ts +++ /dev/null @@ -1,132 +0,0 @@ -export class Employee { - constructor( - public id: number, - public firstName: string, - public lastName: string, - public explicitPermissions: EmployeePermission[], - public permissions: EmployeePermission[], - public group?: EmployeeGroup, - public lastLoginTime?: Date - ) { - } -} - -export class EmployeeGroup { - constructor( - public id: number, - public name: string, - public permissions: EmployeePermission[] - ) { - } -} - -export enum EmployeePermission { - VIEW_RECIPES = 'VIEW_RECIPES', - VIEW_USERS = 'VIEW_USERS', - VIEW_CATALOG = 'VIEW_CATALOG', - - EDIT_RECIPES_PUBLIC_DATA = 'EDIT_RECIPES_PUBLIC_DATA', - EDIT_RECIPES = 'EDIT_RECIPES', - EDIT_MATERIALS = 'EDIT_MATERIALS', - EDIT_MATERIAL_TYPES = 'EDIT_MATERIAL_TYPES', - EDIT_COMPANIES = 'EDIT_COMPANIES', - EDIT_USERS = 'EDIT_USERS', - EDIT_CATALOG = 'EDIT_CATALOG', - - REMOVE_RECIPES = 'REMOVE_RECIPES', - REMOVE_MATERIALS = 'REMOVE_MATERIALS', - REMOVE_MATERIAL_TYPES = 'REMOVE_MATERIAL_TYPES', - REMOVE_COMPANIES = 'REMOVE_COMPANIES', - REMOVE_USERS = 'REMOVE_USERS', - REMOVE_CATALOG = 'REMOVE_CATALOG', - - PRINT_MIXES = 'PRINT_MIXES', - ADD_TO_INVENTORY = 'ADD_TO_INVENTORY', - DEDUCT_FROM_INVENTORY = 'DEDUCT_FROM_INVENTORY', - GENERATE_TOUCH_UP_KIT = 'GENERATE_TOUCH_UP_KIT', - - ADMIN = 'ADMIN' -} - -export const mapped_permissions = { - view: [ - {permission: EmployeePermission.VIEW_RECIPES, description: 'Voir les recettes', impliedPermissions: []}, - {permission: EmployeePermission.VIEW_CATALOG, description: 'Voir le catalogue', impliedPermissions: []}, - {permission: EmployeePermission.VIEW_USERS, description: 'Voir les utilisateurs', impliedPermissions: []}, - ], - edit: [{ - permission: EmployeePermission.EDIT_RECIPES_PUBLIC_DATA, - description: 'Modifier les données publiques des recettes', - impliedPermissions: [EmployeePermission.VIEW_RECIPES] - }, { - permission: EmployeePermission.EDIT_RECIPES, - description: 'Modifier les recettes', - impliedPermissions: [EmployeePermission.EDIT_RECIPES_PUBLIC_DATA] - }, { - permission: EmployeePermission.EDIT_MATERIALS, - description: 'Modifier les produits', - impliedPermissions: [EmployeePermission.VIEW_CATALOG] - }, { - permission: EmployeePermission.EDIT_MATERIAL_TYPES, - description: 'Modifier les types de produit', - impliedPermissions: [EmployeePermission.VIEW_CATALOG] - }, { - permission: EmployeePermission.EDIT_COMPANIES, - description: 'Modifier les bannières', - impliedPermissions: [EmployeePermission.VIEW_CATALOG] - }, { - permission: EmployeePermission.EDIT_CATALOG, - description: 'Modifier le catalogue', - impliedPermissions: [EmployeePermission.EDIT_MATERIALS, EmployeePermission.EDIT_MATERIAL_TYPES, EmployeePermission.EDIT_COMPANIES] - }, { - permission: EmployeePermission.EDIT_USERS, - description: 'Modifier les utilisateurs', - impliedPermissions: [EmployeePermission.VIEW_USERS] - }], - remove: [{ - permission: EmployeePermission.REMOVE_RECIPES, - description: 'Supprimer des recettes', - impliedPermissions: [EmployeePermission.EDIT_RECIPES] - }, { - permission: EmployeePermission.REMOVE_MATERIALS, - description: 'Supprimer des produits', - impliedPermissions: [EmployeePermission.EDIT_MATERIALS] - }, { - permission: EmployeePermission.REMOVE_MATERIAL_TYPES, - description: 'Supprimer des types de produit', - impliedPermissions: [EmployeePermission.EDIT_MATERIAL_TYPES] - }, { - permission: EmployeePermission.REMOVE_COMPANIES, - description: 'Supprimer des bannières', - impliedPermissions: [EmployeePermission.EDIT_COMPANIES] - }, { - permission: EmployeePermission.REMOVE_CATALOG, - description: 'Supprimer dans le catalogue', - impliedPermissions: [EmployeePermission.REMOVE_MATERIALS, EmployeePermission.REMOVE_MATERIAL_TYPES, EmployeePermission.REMOVE_COMPANIES] - }, { - permission: EmployeePermission.REMOVE_USERS, - description: 'Supprimer des utilisateurs', - impliedPermissions: [EmployeePermission.EDIT_USERS] - }], - other: [{ - permission: EmployeePermission.PRINT_MIXES, - description: 'Imprimer les mélanges (bPac)', - impliedPermissions: [EmployeePermission.VIEW_RECIPES] - }, { - permission: EmployeePermission.ADD_TO_INVENTORY, - description: 'Ajouter dans l\'inventaire', - impliedPermissions: [EmployeePermission.VIEW_CATALOG] - }, { - permission: EmployeePermission.DEDUCT_FROM_INVENTORY, - description: 'Déduire dans l\'inventaire', - impliedPermissions: [EmployeePermission.VIEW_RECIPES] - }, { - permission: EmployeePermission.GENERATE_TOUCH_UP_KIT, - description: 'Générer un PDF de kit de retouche', - impliedPermissions: [] - }, { - permission: EmployeePermission.ADMIN, - description: 'Administrateur', - impliedPermissions: [EmployeePermission.EDIT_CATALOG, EmployeePermission.REMOVE_RECIPES, EmployeePermission.REMOVE_USERS, EmployeePermission.REMOVE_CATALOG, EmployeePermission.PRINT_MIXES, EmployeePermission.ADD_TO_INVENTORY, EmployeePermission.DEDUCT_FROM_INVENTORY] - }] -} diff --git a/src/app/modules/shared/model/recipe.model.ts b/src/app/modules/shared/model/recipe.model.ts index 7e84e00..b1137c4 100644 --- a/src/app/modules/shared/model/recipe.model.ts +++ b/src/app/modules/shared/model/recipe.model.ts @@ -1,7 +1,7 @@ import {Material} from './material.model' import {LocalDate} from 'js-joda' import {Company} from './company.model' -import {EmployeeGroup} from './employee' +import {Group} from './user' export class Recipe { constructor( @@ -11,7 +11,7 @@ export class Recipe { public color: string, public gloss: number, public sample: number, - public approbationDate: LocalDate, + public approbationDate: string, public remark: string, public company: Company, public mixes: Mix[], @@ -24,7 +24,7 @@ export class Recipe { export class RecipeGroupInformation { constructor( public id: number, - public group: EmployeeGroup, + public group: Group, public note: string, public steps: RecipeStep[] ) { diff --git a/src/app/modules/shared/model/touch-up-kit.model.ts b/src/app/modules/shared/model/touch-up-kit.model.ts new file mode 100644 index 0000000..df625e5 --- /dev/null +++ b/src/app/modules/shared/model/touch-up-kit.model.ts @@ -0,0 +1,24 @@ +export class TouchUpKit { + constructor( + public id: number, + public project: string, + public buggy: string, + public company: string, + public quantity: number, + public shippingDate: string, + public finish: string[], + public material: string[], + public content: TouchUpKitProduct[] + ) { + } +} + +export class TouchUpKitProduct { + constructor( + public id: number, + public name: string, + public description: string | null, + public quantity: number + ) { + } +} diff --git a/src/app/modules/shared/model/user.ts b/src/app/modules/shared/model/user.ts new file mode 100644 index 0000000..5c08ff9 --- /dev/null +++ b/src/app/modules/shared/model/user.ts @@ -0,0 +1,103 @@ +export class User { + constructor( + public id: number, + public firstName: string, + public lastName: string, + public explicitPermissions: Permission[], + public permissions: Permission[], + public group?: Group, + public lastLoginTime?: Date + ) { + } +} + +export class Group { + constructor( + public id: number, + public name: string, + public permissions: Permission[] + ) { + } +} + +export enum Permission { + VIEW_RECIPES = 'VIEW_RECIPES', + VIEW_USERS = 'VIEW_USERS', + VIEW_CATALOG = 'VIEW_CATALOG', + + EDIT_RECIPES_PUBLIC_DATA = 'EDIT_RECIPES_PUBLIC_DATA', + EDIT_RECIPES = 'EDIT_RECIPES', + EDIT_MATERIALS = 'EDIT_MATERIALS', + EDIT_MATERIAL_TYPES = 'EDIT_MATERIAL_TYPES', + EDIT_COMPANIES = 'EDIT_COMPANIES', + EDIT_USERS = 'EDIT_USERS', + EDIT_CATALOG = 'EDIT_CATALOG', + + VIEW_TOUCH_UP_KITS = 'VIEW_TOUCH_UP_KITS', + EDIT_TOUCH_UP_KITS = 'EDIT_TOUCH_UP_KITS', + + PRINT_MIXES = 'PRINT_MIXES', + ADD_TO_INVENTORY = 'ADD_TO_INVENTORY', + DEDUCT_FROM_INVENTORY = 'DEDUCT_FROM_INVENTORY', + + ADMIN = 'ADMIN' +} + +export const mapped_permissions = { + view: [ + {permission: Permission.VIEW_RECIPES, description: 'Voir les recettes', impliedPermissions: []}, + {permission: Permission.VIEW_CATALOG, description: 'Voir le catalogue', impliedPermissions: []}, + {permission: Permission.VIEW_USERS, description: 'Voir les utilisateurs', impliedPermissions: []}, + {permission: Permission.VIEW_TOUCH_UP_KITS, description: 'Voir les kits de retouche', impliedPermissions: []} + ], + edit: [{ + permission: Permission.EDIT_RECIPES_PUBLIC_DATA, + description: 'Modifier les données publiques des recettes', + impliedPermissions: [Permission.VIEW_RECIPES] + }, { + permission: Permission.EDIT_RECIPES, + description: 'Modifier les recettes', + impliedPermissions: [Permission.EDIT_RECIPES_PUBLIC_DATA] + }, { + permission: Permission.EDIT_MATERIALS, + description: 'Modifier les produits', + impliedPermissions: [Permission.VIEW_CATALOG] + }, { + permission: Permission.EDIT_MATERIAL_TYPES, + description: 'Modifier les types de produit', + impliedPermissions: [Permission.VIEW_CATALOG] + }, { + permission: Permission.EDIT_COMPANIES, + description: 'Modifier les bannières', + impliedPermissions: [Permission.VIEW_CATALOG] + }, { + permission: Permission.EDIT_CATALOG, + description: 'Modifier le catalogue', + impliedPermissions: [Permission.EDIT_MATERIALS, Permission.EDIT_MATERIAL_TYPES, Permission.EDIT_COMPANIES] + }, { + permission: Permission.EDIT_USERS, + description: 'Modifier les utilisateurs', + impliedPermissions: [Permission.VIEW_USERS] + }, { + permission: Permission.EDIT_TOUCH_UP_KITS, + description: 'Modifier les kits de retouche', + impliedPermissions: [Permission.VIEW_TOUCH_UP_KITS] + }], + other: [{ + permission: Permission.PRINT_MIXES, + description: 'Imprimer les mélanges (bPac)', + impliedPermissions: [Permission.VIEW_RECIPES] + }, { + permission: Permission.ADD_TO_INVENTORY, + description: 'Ajouter dans l\'inventaire', + impliedPermissions: [Permission.VIEW_CATALOG] + }, { + permission: Permission.DEDUCT_FROM_INVENTORY, + description: 'Déduire dans l\'inventaire', + impliedPermissions: [Permission.VIEW_RECIPES] + }, { + permission: Permission.ADMIN, + description: 'Administrateur', + impliedPermissions: [Permission.EDIT_RECIPES, Permission.EDIT_CATALOG, Permission.EDIT_USERS, Permission.EDIT_TOUCH_UP_KITS, Permission.PRINT_MIXES, Permission.ADD_TO_INVENTORY, Permission.DEDUCT_FROM_INVENTORY] + }] +} diff --git a/src/app/modules/shared/service/api.service.ts b/src/app/modules/shared/service/api.service.ts index 5389e7d..7c39b28 100644 --- a/src/app/modules/shared/service/api.service.ts +++ b/src/app/modules/shared/service/api.service.ts @@ -78,7 +78,7 @@ export class ApiService implements OnDestroy { console.error('httpOptions need to be specified to use credentials in HTTP methods.') } } else { - this.appState.resetAuthenticatedEmployee() + this.appState.resetAuthenticatedUser() this.navigateToLogin() } } @@ -92,7 +92,7 @@ export class ApiService implements OnDestroy { private checkAuthenticated(): boolean { return (this.appState.isAuthenticated && Date.now() <= this.appState.authenticationExpiration) || - (this.appState.authenticatedEmployee && this.appState.authenticatedEmployee.group != null) + (this.appState.authenticatedUser && this.appState.authenticatedUser.group != null) } private navigateToLogin() { diff --git a/src/app/modules/shared/service/error.service.ts b/src/app/modules/shared/service/error.service.ts index 6189683..fd045b2 100644 --- a/src/app/modules/shared/service/error.service.ts +++ b/src/app/modules/shared/service/error.service.ts @@ -121,5 +121,5 @@ export class ErrorHandler { } function isServerOfflineError(response: any): boolean { - return response.status === 0 && response.statusText === 'Unknown Error' + return response.status === 0 || response.status === 502 } diff --git a/src/app/modules/shared/shared.module.ts b/src/app/modules/shared/shared.module.ts index 549c6a8..1d7d62e 100644 --- a/src/app/modules/shared/shared.module.ts +++ b/src/app/modules/shared/shared.module.ts @@ -7,7 +7,7 @@ import {MatFormFieldModule} from '@angular/material/form-field' import {MatInputModule} from '@angular/material/input' import {MatIconModule} from '@angular/material/icon' import {FormsModule, ReactiveFormsModule} from '@angular/forms' -import {EmployeeMenuComponent} from './components/employee-info/employee-menu.component' +import {UserMenuComponent} from './components/user-info/user-menu.component' import {LabeledIconComponent} from './components/labeled-icon/labeled-icon.component' import {MatTableModule} from '@angular/material/table' import {CommonModule} from '@angular/common' @@ -32,10 +32,13 @@ import {MatSliderModule} from '@angular/material/slider' import {SliderFieldComponent} from './components/slider-field/slider-field.component' import {LoadingWheelComponent} from './components/loading-wheel/loading-wheel.component' import {MatProgressSpinnerModule} from '@angular/material/progress-spinner' - +import {InfoBannerModule} from './components/info-banner/info-banner.module' +import {CreFormsModule} from './components/forms/forms.module' +import {VarDirective} from './directives/var.directive' +import {CreColorPreview} from './components/color-preview/color-preview' @NgModule({ - declarations: [HeaderComponent, EmployeeMenuComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent, LoadingWheelComponent], + declarations: [VarDirective, HeaderComponent, UserMenuComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent, LoadingWheelComponent, CreColorPreview], exports: [ CommonModule, HttpClientModule, @@ -64,7 +67,11 @@ import {MatProgressSpinnerModule} from '@angular/material/progress-spinner' FileButtonComponent, GlobalAlertHandlerComponent, LoadingWheelComponent, - RouterModule + RouterModule, + InfoBannerModule, + CreFormsModule, + VarDirective, + CreColorPreview ], imports: [ MatTabsModule, diff --git a/src/app/modules/shared/utils/utils.ts b/src/app/modules/shared/utils/utils.ts index 266f1e1..e9eaab1 100644 --- a/src/app/modules/shared/utils/utils.ts +++ b/src/app/modules/shared/utils/utils.ts @@ -1,4 +1,8 @@ /** Returns [value] if it is not null or [or]. */ +import {DateTimeFormatter, LocalDate} from 'js-joda' +import {TouchUpKit} from '../model/touch-up-kit.model' +import {environment} from '../../../../environments/environment' + export function valueOr(value: T, or: T): T { return value ? value : or } @@ -14,7 +18,27 @@ export function openJpg(url: string) { openUrl(url, MEDIA_TYPE_JPG) } -function openUrl(url: string, mediaType: string) { - const encodedUrl = `${url}&mediaType=${encodeURIComponent(mediaType)}` - window.open(encodedUrl, '_blank') +export function openTouchUpKit(touchUpKit: TouchUpKit) { + openRawUrl(`${environment.apiUrl}/touchupkit/pdf?project=${touchUpKit.project}`) +} + +export function openUrl(url: string, mediaType: string) { + openRawUrl(`${url}&mediaType=${encodeURIComponent(mediaType)}`) +} + +export function openRawUrl(url: string) { + window.open(url, '_blank') +} + +const dateFormatter = DateTimeFormatter + .ofPattern('dd-MM-yyyy') + +export function formatDate(date: string): string { + return LocalDate.parse(date).format(dateFormatter) +} + +export function reduceDashes(arr: string[]): string { + return arr.reduce((acc, cur) => { + return `${acc} - ${cur}` + }) } diff --git a/src/app/modules/touch-up-kit/components/finish.html b/src/app/modules/touch-up-kit/components/finish.html new file mode 100644 index 0000000..18e7dbd --- /dev/null +++ b/src/app/modules/touch-up-kit/components/finish.html @@ -0,0 +1,11 @@ +
+
{{finish}}
+
+ + + {{recipe.name}} - {{recipe.company.name}} + + + +
+
diff --git a/src/app/modules/touch-up-kit/components/finish.sass b/src/app/modules/touch-up-kit/components/finish.sass new file mode 100644 index 0000000..899e060 --- /dev/null +++ b/src/app/modules/touch-up-kit/components/finish.sass @@ -0,0 +1,28 @@ +@import '~src/custom-theme' + +.touchupkit-finish-container + display: inline-block + position: relative + +.matching + text-decoration: underline + +.matching-recipes + position: absolute + top: 1.5em + transform: translateX(-25%) + width: max-content + display: inline-block + background-color: white + color: $dark-primary-text + border-radius: 4px + +mat-list + padding-top: 0 + +mat-list-item:hover + background-color: darken(white, 5%) + cursor: pointer + +cre-color-preview + display: inline-block diff --git a/src/app/modules/touch-up-kit/components/finish.ts b/src/app/modules/touch-up-kit/components/finish.ts new file mode 100644 index 0000000..d91f01b --- /dev/null +++ b/src/app/modules/touch-up-kit/components/finish.ts @@ -0,0 +1,42 @@ +import {Component, Input} from '@angular/core' +import {SubscribingComponent} from '../../shared/components/subscribing.component' +import {RecipeService} from '../../colors/services/recipe.service' +import {ErrorService} from '../../shared/service/error.service' +import {ActivatedRoute, Router} from '@angular/router' +import {Recipe} from '../../shared/model/recipe.model' + +@Component({ + selector: 'touchupkit-finish', + templateUrl: 'finish.html', + styleUrls: ['finish.sass'] +}) +export class TouchUpKitFinish extends SubscribingComponent { + @Input() finish: string + + hover = false + matchesRecipes = false + matchingRecipes: Recipe[] | null + + constructor( + private recipeService: RecipeService, + errorService: ErrorService, + router: Router, + activatedRoute: ActivatedRoute + ) { + super(errorService, activatedRoute, router) + } + + ngOnInit(): void { + this.subscribe( + this.recipeService.getAllByName(this.finish), + recipes => { + this.matchesRecipes = recipes.length > 0 + this.matchingRecipes = recipes + } + ) + } + + openRecipe(recipe: Recipe) { + window.open(`/color/explore/${recipe.id}`, '_blank') + } +} diff --git a/src/app/modules/touch-up-kit/components/form.html b/src/app/modules/touch-up-kit/components/form.html new file mode 100644 index 0000000..999b820 --- /dev/null +++ b/src/app/modules/touch-up-kit/components/form.html @@ -0,0 +1,34 @@ + + Ajouter un kit de retouche + + + + + + + + La quantité doit être supérieure ou égale à 1 + + + + + + + + + + + diff --git a/src/app/modules/touch-up-kit/components/form.ts b/src/app/modules/touch-up-kit/components/form.ts new file mode 100644 index 0000000..6340f72 --- /dev/null +++ b/src/app/modules/touch-up-kit/components/form.ts @@ -0,0 +1,92 @@ +import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core' +import {chipListRequired, ComboBoxEntry, CreChipComboBoxComponent} from '../../shared/components/inputs/inputs' +import {CreFormComponent} from '../../shared/components/forms/forms' +import {TouchUpKitProductEditor} from './product-editor' +import {FormControl, Validators} from '@angular/forms' +import {RecipeService} from '../../colors/services/recipe.service' +import {CompanyService} from '../../company/service/company.service' +import {ErrorService} from '../../shared/service/error.service' +import {ActivatedRoute, Router} from '@angular/router' +import {TouchUpKit, TouchUpKitProduct} from '../../shared/model/touch-up-kit.model' +import {SubscribingComponent} from '../../shared/components/subscribing.component' +import {map} from 'rxjs/operators' + +@Component({ + selector: 'touchupkit-form', + templateUrl: 'form.html' +}) +export class TouchUpKitForm extends SubscribingComponent { + @ViewChild('finishInput') finishInput: CreChipComboBoxComponent + @ViewChild('materialInput') materialInput: CreChipComboBoxComponent + @ViewChild(CreFormComponent) form: CreFormComponent + @ViewChild(TouchUpKitProductEditor) contentEditor: TouchUpKitProductEditor + + @Input() touchUpKit: TouchUpKit | null + + controls: any + finish$ = this.recipeService.all.pipe( + map(recipes => recipes.map(recipe => new ComboBoxEntry(recipe.id, recipe.name, `${recipe.name} - ${recipe.company.name}`))) + ) + companies$ = this.companyService.all.pipe( + map(companies => companies.map(company => company.name)) + ) + + @Output() submitForm = new EventEmitter() + + constructor( + private recipeService: RecipeService, + private companyService: CompanyService, + errorService: ErrorService, + activatedRoute: ActivatedRoute, + router: Router, + ) { + super(errorService, activatedRoute, router) + } + + ngOnInit() { + super.ngOnInit() + + this.controls = { + project: new FormControl(this.touchUpKit?.project, Validators.required), + buggy: new FormControl(this.touchUpKit?.buggy, Validators.required), + company: new FormControl(this.touchUpKit?.company, Validators.required), + quantity: new FormControl(this.touchUpKit?.quantity, Validators.compose([Validators.required, Validators.min(1)])), + shippingDate: new FormControl(this.touchUpKit?.shippingDate, Validators.required), + finish: new FormControl(this.touchUpKit?.finish, chipListRequired()), + material: new FormControl(this.touchUpKit?.material, chipListRequired()) + } + } + + submit() { + this.submitForm.emit({ + id: this.touchUpKit ? this.touchUpKit.id : null, + project: this.controls.project.value, + buggy: this.controls.buggy.value, + company: this.controls.company.value, + quantity: this.controls.quantity.value, + shippingDate: this.controls.shippingDate.value, + finish: this.selectedFinish, + material: this.selectedMaterial, + content: this.touchUpKitContent + }) + } + + get selectedFinish(): string[] { + return this.finishInput.selectedValues + } + + get selectedMaterial(): string[] { + return this.materialInput.selectedValues + } + + get touchUpKitContent(): TouchUpKitProduct[] { + return this.contentEditor.products + } + + get formValid(): boolean { + return this.form && !this.form.invalid && + !this.finishInput.empty && + !this.materialInput.empty && + !this.contentEditor.empty + } +} diff --git a/src/app/modules/touch-up-kit/components/product-editor.html b/src/app/modules/touch-up-kit/components/product-editor.html new file mode 100644 index 0000000..477335b --- /dev/null +++ b/src/app/modules/touch-up-kit/components/product-editor.html @@ -0,0 +1,55 @@ + + + Contenu + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nom + + + {{product.name}} + + Description + + + {{product.description ? product.description : '-'}} + + Quantité + + + {{product.quantity}} + + + Ajouter + + Retirer +
+
+
diff --git a/src/app/modules/touch-up-kit/components/product-editor.sass b/src/app/modules/touch-up-kit/components/product-editor.sass new file mode 100644 index 0000000..eaad423 --- /dev/null +++ b/src/app/modules/touch-up-kit/components/product-editor.sass @@ -0,0 +1,25 @@ +@import '../../../../custom-theme' + +touchupkit-product-editor + .mat-form-field-label-wrapper + opacity: 0 + + cre-input, span.focused + display: inline-block + width: 15rem + + span.focused + white-space: nowrap + overflow: hidden + + // Content card + mat-card-content + margin: 0 + padding: 0 + + table + border: none + box-shadow: none + + th + border-radius: 0 !important diff --git a/src/app/modules/touch-up-kit/components/product-editor.ts b/src/app/modules/touch-up-kit/components/product-editor.ts new file mode 100644 index 0000000..ea2d840 --- /dev/null +++ b/src/app/modules/touch-up-kit/components/product-editor.ts @@ -0,0 +1,57 @@ +import {Component, Input, OnInit, ViewChild, ViewEncapsulation} from '@angular/core' +import {TouchUpKitProduct} from '../../shared/model/touch-up-kit.model' +import {MatTable} from '@angular/material/table' + +@Component({ + selector: 'touchupkit-product-editor', + templateUrl: 'product-editor.html', + styleUrls: ['product-editor.sass'], + encapsulation: ViewEncapsulation.None +}) +export class TouchUpKitProductEditor implements OnInit { + productCols = ['name', 'description', 'quantity', 'removeButton'] + + @Input() products: TouchUpKitProduct[] + + @ViewChild(MatTable) table: MatTable + hoveredProduct: TouchUpKitProduct | null + selectedProduct: TouchUpKitProduct | null + + ngOnInit(): void { + if (!this.products) { + this.products = [this.emptyProduct] + } + } + + addRow() { + const newProduct = this.emptyProduct + + this.products.push(newProduct) + this.table.renderRows() + this.selectedProduct = newProduct + } + + removeRow(product: TouchUpKitProduct) { + this.products = this.products.filter(p => p !== product) + this.table.renderRows() + } + + isFocused(index: number, product: TouchUpKitProduct): boolean { + return (!this.hoveredProduct && index === 0) || + this.hoveredProduct === product || + this.selectedProduct === product + } + + get empty(): boolean { + return this.products.length <= 0 + } + + get emptyProduct(): TouchUpKitProduct { + return { + id: null, + name: '', + description: '', + quantity: 1 + } + } +} diff --git a/src/app/modules/touch-up-kit/pages/add.html b/src/app/modules/touch-up-kit/pages/add.html new file mode 100644 index 0000000..be52732 --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/add.html @@ -0,0 +1,11 @@ + + + Retour + + + Enregistrer + + + + + diff --git a/src/app/modules/touch-up-kit/pages/banner.html b/src/app/modules/touch-up-kit/pages/banner.html new file mode 100644 index 0000000..4da19bc --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/banner.html @@ -0,0 +1,19 @@ + + {{touchUpKit.project}} - {{touchUpKit.company}} + + +

Chariot: {{touchUpKit.buggy}}

+

Quantité: {{touchUpKit.quantity}}

+

Date de livraison: {{shippingDate}}

+
+ +

Fini: + + + - + +

+

Matériel: {{material}}

+
+
+
diff --git a/src/app/modules/touch-up-kit/pages/details.html b/src/app/modules/touch-up-kit/pages/details.html new file mode 100644 index 0000000..99bbb6c --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/details.html @@ -0,0 +1,28 @@ +
+ + +
+ + +
+ + + + Nom + {{product.name}} + + + + Description + + {{product.description}} + - + + + + + Quantité + {{product.quantity}} + + +
diff --git a/src/app/modules/touch-up-kit/pages/details.sass b/src/app/modules/touch-up-kit/pages/details.sass new file mode 100644 index 0000000..caf74e0 --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/details.sass @@ -0,0 +1,2 @@ +info-banner-section p + margin-bottom: 0 diff --git a/src/app/modules/touch-up-kit/pages/edit.html b/src/app/modules/touch-up-kit/pages/edit.html new file mode 100644 index 0000000..7fd4938 --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/edit.html @@ -0,0 +1,24 @@ + + + + Retour + + + Supprimer + Enregistrer + + + + + + + + + + diff --git a/src/app/modules/touch-up-kit/pages/list.html b/src/app/modules/touch-up-kit/pages/list.html new file mode 100644 index 0000000..e5fc496 --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/list.html @@ -0,0 +1,53 @@ + + + + Ajouter + + + + + + Project + {{touchUpKit.project}} + + + + Chariot + {{touchUpKit.buggy}} + + + + Bannière + {{touchUpKit.company}} + + + + Date de livraison + {{touchUpKit.shippingDate}} + + + + + + PDF + + + + + + + + Détails + + + + + + + + + Modifier + + + + diff --git a/src/app/modules/touch-up-kit/pages/touchupkit.ts b/src/app/modules/touch-up-kit/pages/touchupkit.ts new file mode 100644 index 0000000..2f6b60c --- /dev/null +++ b/src/app/modules/touch-up-kit/pages/touchupkit.ts @@ -0,0 +1,165 @@ +import {Component, Input} from '@angular/core' +import {TouchUpKit} from '../../shared/model/touch-up-kit.model' +import {formatDate, openTouchUpKit, reduceDashes} from '../../shared/utils/utils' +import {ErrorHandlingComponent} from '../../shared/components/subscribing.component' +import {TouchUpKitService} from '../service/touch-up-kit.service' +import {AccountService} from '../../accounts/services/account.service' +import {ErrorService} from '../../shared/service/error.service' +import {ActivatedRoute, Router} from '@angular/router' +import {Permission} from '../../shared/model/user' +import {RecipeService} from '../../colors/services/recipe.service' +import {AppState} from '../../shared/app-state' + +@Component({ + selector: 'touchupkit-banner', + templateUrl: 'banner.html', + styles: [ + 'p { margin-bottom: 0 }' + ] +}) +export class TouchUpKitBanner { + @Input() touchUpKit: TouchUpKit + + get shippingDate(): string { + return formatDate(this.touchUpKit.shippingDate) + } + + get material(): string { + return reduceDashes(this.touchUpKit.material) + } +} + +@Component({ + selector: 'touchupkit-list', + templateUrl: './list.html' +}) +export class TouchUpKitList extends ErrorHandlingComponent { + touchUpKits$ = this.touchUpKitService.all + columns = ['project', 'buggy', 'company', 'shippingDate', 'pdfButton', 'detailsButton', 'editButton'] + + constructor( + private touchUpKitService: TouchUpKitService, + private accountService: AccountService, + private appState: AppState, + errorService: ErrorService, + router: Router, + activatedRoute: ActivatedRoute + ) { + super(errorService, activatedRoute, router) + this.appState.title = 'Kits de retouche' + } + + openTouchUpKitPdf(touchUpKit: TouchUpKit) { + openTouchUpKit(touchUpKit) + } + + get canEditTouchUpKits(): boolean { + return this.accountService.hasPermission(Permission.EDIT_TOUCH_UP_KITS) + } +} + +@Component({ + selector: 'touchupkit-details', + templateUrl: 'details.html' +}) +export class TouchUpKitDetails extends ErrorHandlingComponent { + touchUpKit: TouchUpKit | null + contentTableCols = ['name', 'description', 'quantity'] + + constructor( + private touchUpKitService: TouchUpKitService, + private recipeService: RecipeService, + private appState: AppState, + errorService: ErrorService, + activatedRoute: ActivatedRoute, + router: Router + ) { + super(errorService, activatedRoute, router) + } + + ngOnInit() { + super.ngOnInit() + + this.subscribeEntityById( + this.touchUpKitService, + this.urlUtils.parseIntUrlParam('id'), + t => { + this.appState.title = `${t.project} - ${t.buggy}` + this.touchUpKit = t + } + ) + } + + openPdf() { + openTouchUpKit(this.touchUpKit) + } +} + +@Component({ + selector: 'touchupkit-add', + templateUrl: 'add.html' +}) +export class TouchUpKitAdd extends ErrorHandlingComponent { + constructor( + private touchUpKitService: TouchUpKitService, + private appState: AppState, + errorService: ErrorService, + activatedRoute: ActivatedRoute, + router: Router, + ) { + super(errorService, activatedRoute, router) + this.appState.title = 'Nouveau kit de retouche' + } + + submit(touchUpKit) { + this.subscribeAndNavigate( + this.touchUpKitService.save(touchUpKit), + '/misc/touch-up-kit/list' + ) + } +} + +@Component({ + selector: 'touchupkit-edit', + templateUrl: 'edit.html' +}) +export class TouchUpKitEdit extends ErrorHandlingComponent { + touchUpKit: TouchUpKit | null + + constructor( + private touchUpKitService: TouchUpKitService, + private appState: AppState, + errorService: ErrorService, + activatedRoute: ActivatedRoute, + router: Router + ) { + super(errorService, activatedRoute, router) + } + + ngOnInit() { + super.ngOnInit() + + this.subscribeEntityById( + this.touchUpKitService, + this.urlUtils.parseIntUrlParam('id'), + touchUpKit => { + this.appState.title = `${touchUpKit.project} - ${touchUpKit.buggy} (Modifications)` + this.touchUpKit = touchUpKit + } + ) + } + + submit(touchUpKit) { + this.subscribeAndNavigate( + this.touchUpKitService.update(touchUpKit), + '/misc/touch-up-kit/list' + ) + } + + delete() { + this.subscribeAndNavigate( + this.touchUpKitService.delete(this.touchUpKit.id), + '/misc/touch-up-kit/list' + ) + } +} diff --git a/src/app/modules/touch-up-kit/service/touch-up-kit.service.ts b/src/app/modules/touch-up-kit/service/touch-up-kit.service.ts new file mode 100644 index 0000000..18aa7b2 --- /dev/null +++ b/src/app/modules/touch-up-kit/service/touch-up-kit.service.ts @@ -0,0 +1,34 @@ +import {Injectable} from '@angular/core' +import {ApiService} from '../../shared/service/api.service' +import {Observable} from 'rxjs' +import {TouchUpKit, TouchUpKitProduct} from '../../shared/model/touch-up-kit.model' + +@Injectable({ + providedIn: 'root' +}) +export class TouchUpKitService { + constructor( + private api: ApiService + ) { + } + + get all(): Observable { + return this.api.get('/touchupkit') + } + + getById(id: number): Observable { + return this.api.get(`/touchupkit/${id}`) + } + + save(touchUpKit: TouchUpKit): Observable { + return this.api.post('/touchupkit', touchUpKit) + } + + update(touchUpKit: TouchUpKit): Observable { + return this.api.put('/touchupkit', touchUpKit) + } + + delete(id: number): Observable { + return this.api.delete(`/touchupkit/${id}`) + } +} diff --git a/src/app/modules/touch-up-kit/touch-up-kit-routing.module.ts b/src/app/modules/touch-up-kit/touch-up-kit-routing.module.ts new file mode 100644 index 0000000..ce570bf --- /dev/null +++ b/src/app/modules/touch-up-kit/touch-up-kit-routing.module.ts @@ -0,0 +1,28 @@ +import {NgModule} from '@angular/core' +import {RouterModule, Routes} from '@angular/router' +import {TouchUpKitAdd, TouchUpKitDetails, TouchUpKitEdit, TouchUpKitList} from './pages/touchupkit' + +const routes: Routes = [{ + path: 'list', + component: TouchUpKitList +}, { + path: 'details/:id', + component: TouchUpKitDetails +}, { + path: 'add', + component: TouchUpKitAdd +}, { + path: 'edit/:id', + component: TouchUpKitEdit +}, { + path: '', + pathMatch: 'full', + redirectTo: 'list' +}] + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class TouchUpKitRoutingModule { +} diff --git a/src/app/modules/touch-up-kit/touch-up-kit.module.ts b/src/app/modules/touch-up-kit/touch-up-kit.module.ts new file mode 100644 index 0000000..067f3d4 --- /dev/null +++ b/src/app/modules/touch-up-kit/touch-up-kit.module.ts @@ -0,0 +1,41 @@ +import {NgModule} from '@angular/core' +import {CommonModule} from '@angular/common' +import {TouchUpKitRoutingModule} from './touch-up-kit-routing.module' +import {SharedModule} from '../shared/shared.module' +import {CreInputsModule} from '../shared/components/inputs/inputs.module' +import {CreButtonsModule} from '../shared/components/buttons/buttons.module' +import {TouchUpKitProductEditor} from './components/product-editor' +import {FormsModule} from '@angular/forms' +import {CreActionBarModule} from '../shared/components/action-bar/action-bar.module' +import {TouchUpKitForm} from './components/form' +import {CreTablesModule} from '../shared/components/tables/tables.module' +import {TouchUpKitAdd, TouchUpKitBanner, TouchUpKitDetails, TouchUpKitEdit, TouchUpKitList} from './pages/touchupkit' +import {TouchUpKitFinish} from './components/finish' +import {MatTooltipModule} from '@angular/material/tooltip' + + +@NgModule({ + declarations: [ + TouchUpKitForm, + TouchUpKitProductEditor, + TouchUpKitBanner, + TouchUpKitFinish, + TouchUpKitList, + TouchUpKitDetails, + TouchUpKitAdd, + TouchUpKitEdit + ], + imports: [ + CommonModule, + TouchUpKitRoutingModule, + SharedModule, + CreInputsModule, + CreButtonsModule, + FormsModule, + CreActionBarModule, + CreTablesModule, + MatTooltipModule + ] +}) +export class TouchUpKitModule { +} diff --git a/src/app/modules/users/pages/add/add.component.ts b/src/app/modules/users/pages/add/add.component.ts index 44d8a71..0470abe 100644 --- a/src/app/modules/users/pages/add/add.component.ts +++ b/src/app/modules/users/pages/add/add.component.ts @@ -8,6 +8,7 @@ import {ErrorHandlingComponent} from '../../../shared/components/subscribing.com import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' import {FormField} from '../../../shared/components/entity-add/entity-add.component' import {map} from 'rxjs/operators' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-add', @@ -73,21 +74,23 @@ export class AddComponent extends ErrorHandlingComponent { }] errorHandlers: ErrorHandler[] = [{ - filter: error => error.type === 'exists-employee-id', + filter: error => error.type === 'exists-user-id', messageProducer: error => `Un utilisateur avec l'identifiant ${error.id} existe déjà` }, { - filter: error => error.type === 'exists-employee-fullName', + filter: error => error.type === 'exists-user-fullName', messageProducer: error => `Un utilisateur nommé '${error.fullName}' existe déjà` }] constructor( - private employeeService: UserService, + private userService: UserService, private groupService: GroupService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Nouvel utilisateur' } ngOnInit() { @@ -99,14 +102,13 @@ export class AddComponent extends ErrorHandlingComponent { submit(values) { const permissionsField = currentPermissionsFieldComponent if (permissionsField.valid()) { - const groupId = values.groupId >= 0 ? values.groupId : null this.subscribeAndNavigate( - this.employeeService.save( + this.userService.save( values.id, values.firstName, values.lastName, values.password, - groupId, + values.groupId, permissionsField.allEnabledPermissions ), '/admin/user/list' diff --git a/src/app/modules/users/pages/edit/edit.component.html b/src/app/modules/users/pages/edit/edit.component.html index dcc5afe..00b475b 100644 --- a/src/app/modules/users/pages/edit/edit.component.html +++ b/src/app/modules/users/pages/edit/edit.component.html @@ -1,15 +1,14 @@ - + diff --git a/src/app/modules/users/pages/edit/edit.component.ts b/src/app/modules/users/pages/edit/edit.component.ts index f835a4a..387ab00 100644 --- a/src/app/modules/users/pages/edit/edit.component.ts +++ b/src/app/modules/users/pages/edit/edit.component.ts @@ -3,12 +3,13 @@ import {currentPermissionsFieldComponent} from '../../../shared/components/permi import {UserService} from '../../services/user.service' import {GroupService} from '../../../groups/services/group.service' import {ActivatedRoute, Router} from '@angular/router' -import {Employee} from '../../../shared/model/employee' +import {User} from '../../../shared/model/user' import {AccountService} from '../../../accounts/services/account.service' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' import {FormField} from '../../../shared/components/entity-add/entity-add.component' import {map} from 'rxjs/operators' +import {AppState} from '../../../shared/app-state' @Component({ selector: 'cre-edit', @@ -18,7 +19,7 @@ import {map} from 'rxjs/operators' export class EditComponent extends ErrorHandlingComponent { @ViewChild('permissionsTemplateRef', {static: true}) permissionsTemplateRef - employee: Employee | null + user: User | null formFields: FormField[] = [{ name: 'id', label: 'Numéro d\'utilisateur', @@ -48,7 +49,7 @@ export class EditComponent extends ErrorHandlingComponent { label: 'Groupe', icon: 'account-multiple', type: 'select', - valueFn: employee => employee.group ? employee.group.id : -1, + valueFn: user => user.group ? user.group.id : -1, options$: this.groupService.allWithDefault.pipe(map(groups => groups.map(g => { return {value: g.id, label: g.name} }))) @@ -59,17 +60,18 @@ export class EditComponent extends ErrorHandlingComponent { }] errorHandlers: ErrorHandler[] = [{ - filter: error => error.type === 'notfound-employee-id', + filter: error => error.type === 'notfound-user-id', consumer: error => this.urlUtils.navigateTo('/admin/user/list') }, { - filter: error => error.type === 'exists-employee-fullName', + filter: error => error.type === 'exists-user-fullName', messageProducer: error => `Un utilisateur nommé '${error.fullName}' existe déjà` }] constructor( private accountService: AccountService, - private employeeService: UserService, + private userService: UserService, private groupService: GroupService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute @@ -80,9 +82,12 @@ export class EditComponent extends ErrorHandlingComponent { ngOnInit(): void { const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id')) this.subscribeEntityById( - this.employeeService, + this.userService, id, - employee => this.employee = employee + user => { + this.appState.title = `${user.firstName} ${user.lastName} (Modifications)` + this.user = user + } ) this.formFields[this.formFields.length - 1].template = this.permissionsTemplateRef @@ -92,7 +97,7 @@ export class EditComponent extends ErrorHandlingComponent { const permissionsField = currentPermissionsFieldComponent if (permissionsField.valid()) { this.subscribe( - this.employeeService.update( + this.userService.update( parseInt(values.id), values.firstName, values.lastName, @@ -108,7 +113,7 @@ export class EditComponent extends ErrorHandlingComponent { delete() { this.subscribeAndNavigate( - this.employeeService.delete(this.employee.id), + this.userService.delete(this.user.id), '/admin/user/list' ) } diff --git a/src/app/modules/users/pages/list/list.component.html b/src/app/modules/users/pages/list/list.component.html index bb5d917..8d99f7f 100644 --- a/src/app/modules/users/pages/list/list.component.html +++ b/src/app/modules/users/pages/list/list.component.html @@ -1,13 +1,13 @@ + [rowDetailsTemplate]="userDetailsTemplate"> - - + + diff --git a/src/app/modules/users/pages/list/list.component.ts b/src/app/modules/users/pages/list/list.component.ts index dd680dd..7cae5c6 100644 --- a/src/app/modules/users/pages/list/list.component.ts +++ b/src/app/modules/users/pages/list/list.component.ts @@ -1,16 +1,17 @@ import {Component} from '@angular/core' import {Observable} from 'rxjs' import {UserService} from '../../services/user.service' -import {Employee, EmployeePermission} from '../../../shared/model/employee' +import {User, Permission} from '../../../shared/model/user' 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' +import {AppState} from '../../../shared/app-state' @Component({ - selector: 'cre-employees', + selector: 'cre-users', templateUrl: './list.component.html', styleUrls: ['./list.component.sass'], animations: [ @@ -22,7 +23,7 @@ import {ErrorService} from '../../../shared/service/error.service' ] }) export class ListComponent extends ErrorHandlingComponent { - employees$: Observable + users$: Observable columns = [ {def: 'id', title: 'Numéro d\'utilisateur', valueFn: e => e.id}, {def: 'name', title: 'Nom', valueFn: e => `${e.firstName} ${e.lastName}`}, @@ -32,26 +33,28 @@ export class ListComponent extends ErrorHandlingComponent { ] buttons = [{ text: 'Modifier', - linkFn: employee => `/admin/user/edit/${employee.id}`, - permission: EmployeePermission.EDIT_USERS + linkFn: user => `/admin/user/edit/${user.id}`, + permission: Permission.EDIT_USERS }, { text: 'Modifier mot de passe', - linkFn: employee => `/admin/user/password/edit/${employee.id}`, - permission: EmployeePermission.EDIT_USERS + linkFn: user => `/admin/user/password/edit/${user.id}`, + permission: Permission.EDIT_USERS }] constructor( - private employeeService: UserService, + private userService: UserService, private accountService: AccountService, + private appState: AppState, errorService: ErrorService, router: Router, activatedRoute: ActivatedRoute ) { super(errorService, activatedRoute, router) + this.appState.title = 'Utilisateurs' } ngOnInit(): void { - this.employees$ = this.employeeService.all.pipe(takeUntil(this.destroy$)) + this.users$ = this.userService.all.pipe(takeUntil(this.destroy$)) } getDate(dateString: string) { diff --git a/src/app/modules/users/pages/password-edit/password-edit.component.html b/src/app/modules/users/pages/password-edit/password-edit.component.html index 70411dc..e53857c 100644 --- a/src/app/modules/users/pages/password-edit/password-edit.component.html +++ b/src/app/modules/users/pages/password-edit/password-edit.component.html @@ -1,7 +1,7 @@ - +
- Modification du mot de passe de l'utilisateur #{{employee.id}} + Modification du mot de passe de l'utilisateur #{{user.id}} diff --git a/src/app/modules/users/pages/password-edit/password-edit.component.ts b/src/app/modules/users/pages/password-edit/password-edit.component.ts index 65684c8..293fa78 100644 --- a/src/app/modules/users/pages/password-edit/password-edit.component.ts +++ b/src/app/modules/users/pages/password-edit/password-edit.component.ts @@ -1,7 +1,7 @@ import {Component} from '@angular/core' import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component' import {UserService} from '../../services/user.service' -import {Employee} from '../../../shared/model/employee' +import {User} from '../../../shared/model/user' import {ActivatedRoute, Router} from '@angular/router' import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms' import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' @@ -12,18 +12,18 @@ import {ErrorHandler, ErrorService} from '../../../shared/service/error.service' styleUrls: ['./password-edit.component.sass'] }) export class PasswordEditComponent extends ErrorHandlingComponent { - employee: Employee | null + user: User | null form: FormGroup passwordControl = new FormControl(null, Validators.compose([Validators.required, Validators.minLength(8)])) errorHandlers: ErrorHandler[] = [{ - filter: error => error.type === 'notfound-employee-id', + filter: error => error.type === 'notfound-user-id', consumer: error => this.urlUtils.navigateTo('/admin/user/list') }] constructor( - private employeeService: UserService, + private userService: UserService, private formBuilder: FormBuilder, errorService: ErrorService, router: Router, @@ -35,9 +35,9 @@ export class PasswordEditComponent extends ErrorHandlingComponent { ngOnInit(): void { const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id')) this.subscribeEntityById( - this.employeeService, + this.userService, id, - employee => this.employee = employee + user => this.user = user ) this.form = this.formBuilder.group({ @@ -48,7 +48,7 @@ export class PasswordEditComponent extends ErrorHandlingComponent { submit() { if (this.form.valid) { this.subscribeAndNavigate( - this.employeeService.updatePassword(this.employee.id, this.passwordControl.value), + this.userService.updatePassword(this.user.id, this.passwordControl.value), '/admin/user/list' ) } diff --git a/src/app/modules/users/services/user.service.ts b/src/app/modules/users/services/user.service.ts index 3047bb6..4a93cf0 100644 --- a/src/app/modules/users/services/user.service.ts +++ b/src/app/modules/users/services/user.service.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {ApiService} from "../../shared/service/api.service"; -import {Employee, EmployeePermission} from "../../shared/model/employee"; +import {User, Permission} from "../../shared/model/user"; import {Observable} from "rxjs"; @Injectable({ @@ -12,29 +12,29 @@ export class UserService { ) { } - get all(): Observable { - return this.api.get('/employee') + get all(): Observable { + return this.api.get('/user') } - getById(id: number): Observable { - return this.api.get(`/employee/${id}`) + getById(id: number): Observable { + return this.api.get(`/user/${id}`) } - save(id: number, firstName: string, lastName: string, password: string, group: number, permissions: EmployeePermission[]): Observable { - const employee = {id, firstName, lastName, password, groupId: group, permissions} - return this.api.post('/employee', employee) + save(id: number, firstName: string, lastName: string, password: string, group: number, permissions: Permission[]): Observable { + const user = {id, firstName, lastName, password, groupId: group >= 0 ? group : null, permissions} + return this.api.post('/user', user) } - update(id: number, firstName: string, lastName: string, group: number, permissions: EmployeePermission[]): Observable { - const employee = {id, firstName, lastName, groupId: group, permissions} - return this.api.put('/employee', employee) + update(id: number, firstName: string, lastName: string, group: number, permissions: Permission[]): Observable { + const user = {id, firstName, lastName, groupId: group >= 0 ? group : null, permissions} + return this.api.put('/user', user) } updatePassword(id: number, password: string): Observable { - return this.api.put(`/employee/${id}/password`, password, true, {contentType: 'text/plain'}) + return this.api.put(`/user/${id}/password`, password, true, {contentType: 'text/plain'}) } delete(id: number): Observable { - return this.api.delete(`/employee/${id}`) + return this.api.delete(`/user/${id}`) } } diff --git a/src/app/pages/administration/administration.component.ts b/src/app/pages/administration/administration.component.ts index 44683f8..3406db0 100644 --- a/src/app/pages/administration/administration.component.ts +++ b/src/app/pages/administration/administration.component.ts @@ -1,7 +1,7 @@ import {Component} from '@angular/core' import {SubMenuComponent} from '../../modules/shared/components/sub-menu/sub-menu.component' import {NavLink} from '../../modules/shared/components/nav/nav.component' -import {EmployeePermission} from '../../modules/shared/model/employee' +import {Permission} from '../../modules/shared/model/user' @Component({ selector: 'cre-administration', @@ -10,7 +10,7 @@ import {EmployeePermission} from '../../modules/shared/model/employee' }) export class AdministrationComponent extends SubMenuComponent { links: NavLink[] = [ - {route: '/admin/user', title: 'Utilisateurs', permission: EmployeePermission.VIEW_USERS}, - {route: '/admin/group', title: 'Groupes', permission: EmployeePermission.VIEW_USERS}, + {route: '/admin/user', title: 'Utilisateurs', permission: Permission.VIEW_USERS}, + {route: '/admin/group', title: 'Groupes', permission: Permission.VIEW_USERS}, ] } diff --git a/src/app/pages/catalog/catalog.component.ts b/src/app/pages/catalog/catalog.component.ts index 30981b0..2ad1840 100644 --- a/src/app/pages/catalog/catalog.component.ts +++ b/src/app/pages/catalog/catalog.component.ts @@ -1,6 +1,6 @@ import {Component} from '@angular/core' import {NavLink} from '../../modules/shared/components/nav/nav.component' -import {EmployeePermission} from '../../modules/shared/model/employee' +import {Permission} from '../../modules/shared/model/user' import {SubMenuComponent} from '../../modules/shared/components/sub-menu/sub-menu.component' @Component({ @@ -10,8 +10,8 @@ import {SubMenuComponent} from '../../modules/shared/components/sub-menu/sub-men }) export class CatalogComponent extends SubMenuComponent { links: NavLink[] = [ - {route: '/catalog/materialtype', title: 'Types de produit', permission: EmployeePermission.VIEW_CATALOG}, - {route: '/catalog/material', title: 'Inventaire', permission: EmployeePermission.VIEW_CATALOG}, - {route: '/catalog/company', title: 'Bannières', permission: EmployeePermission.VIEW_CATALOG} + {route: '/catalog/materialtype', title: 'Types de produit', permission: Permission.VIEW_CATALOG}, + {route: '/catalog/material', title: 'Inventaire', permission: Permission.VIEW_CATALOG}, + {route: '/catalog/company', title: 'Bannières', permission: Permission.VIEW_CATALOG} ] } diff --git a/src/app/pages/others/misc.component.ts b/src/app/pages/others/misc.component.ts index d6f99ea..03d63a8 100644 --- a/src/app/pages/others/misc.component.ts +++ b/src/app/pages/others/misc.component.ts @@ -1,7 +1,7 @@ import {Component} from '@angular/core' import {SubMenuComponent} from '../../modules/shared/components/sub-menu/sub-menu.component' import {NavLink} from '../../modules/shared/components/nav/nav.component' -import {EmployeePermission} from '../../modules/shared/model/employee' +import {Permission} from '../../modules/shared/model/user' @Component({ selector: 'cre-others', @@ -10,6 +10,6 @@ import {EmployeePermission} from '../../modules/shared/model/employee' }) export class MiscComponent extends SubMenuComponent{ links: NavLink[] = [ - {route: '/misc/touchupkit', title: 'Kits de retouche', permission: EmployeePermission.GENERATE_TOUCH_UP_KIT} + {route: '/misc/touch-up-kit', title: 'Kits de retouche', permission: Permission.VIEW_TOUCH_UP_KITS} ] } diff --git a/src/app/pages/others/touchupkit/touchupkit.component.html b/src/app/pages/others/touchupkit/touchupkit.component.html deleted file mode 100644 index 0218a25..0000000 --- a/src/app/pages/others/touchupkit/touchupkit.component.html +++ /dev/null @@ -1,23 +0,0 @@ - - - Génération d'un kit de retouche - - - - - Job - - - - - - - - - diff --git a/src/app/pages/others/touchupkit/touchupkit.component.sass b/src/app/pages/others/touchupkit/touchupkit.component.sass deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/pages/others/touchupkit/touchupkit.component.ts b/src/app/pages/others/touchupkit/touchupkit.component.ts deleted file mode 100644 index 1ffe1e8..0000000 --- a/src/app/pages/others/touchupkit/touchupkit.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {Component, OnInit} from '@angular/core' -import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms' -import {TouchupkitService} from '../../../modules/shared/service/touchupkit.service' - -@Component({ - selector: 'cre-touchupkit', - templateUrl: './touchupkit.component.html', - styleUrls: ['./touchupkit.component.sass'] -}) -export class TouchupkitComponent implements OnInit { - formGroup: FormGroup - jobControl: FormControl - - constructor( - private touchUpKitService: TouchupkitService, - private formBuilder: FormBuilder - ) { - } - - ngOnInit(): void { - this.jobControl = new FormControl(null, Validators.required) - this.formGroup = this.formBuilder.group({ - job: this.jobControl - }) - } - - submit() { - this.touchUpKitService.generateJobPdfDocument(this.jobControl.value) - } -} diff --git a/src/styles.sass b/src/styles.sass index 146b03d..5570317 100644 --- a/src/styles.sass +++ b/src/styles.sass @@ -5,7 +5,6 @@ mat-card padding: 0 !important width: max-content - max-width: 50rem &.x-centered margin: auto @@ -168,27 +167,6 @@ div.empty margin-left: 0 margin-right: 1rem -.recipe-color-circle - color: black - width: 2.2rem - height: 2.2rem - border-radius: 1.1rem - margin-left: 1rem - font-size: 13px - - &.dark-mode - color: white - width: 2.3rem - height: 2.3rem - border: solid 1px white - - div - position: absolute - width: 2rem - text-align: center - margin-top: 7px - margin-left: 1px - .alert p margin-bottom: 0