Merge branch 'features' into 'master'
Ajout du support complet pour les kits de retouches See merge request color-recipes-explorer/frontend!24
This commit is contained in:
commit
88499935d8
|
@ -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'
|
||||
}]
|
||||
}]
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<Employee>(`${environment.apiUrl}/employee/current`, {withCredentials: true})
|
||||
this.http.get<User>(`${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<any>) => {
|
||||
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>('/employee/current', true)
|
||||
private setLoggedInUserFromApi() {
|
||||
this.api.get<User>('/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()
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" routerLink="/color/edit/{{recipeId}}">Retour</button>
|
||||
<button *ngIf="editionMode && canDeleteMix" mat-raised-button color="warn" (click)="deleteConfirmBox.show()">
|
||||
<button *ngIf="editionMode" mat-raised-button color="warn" (click)="deleteConfirmBox.show()">
|
||||
Supprimer
|
||||
</button>
|
||||
<button mat-raised-button color="accent" [disabled]="!form.valid" (click)="submit()">Enregistrer</button>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
<div class="recipe-info-wrapper d-flex flex-column">
|
||||
<div class="d-flex flex-row">
|
||||
<h3>{{recipe.company.name}} - {{recipe.name}}</h3>
|
||||
<div
|
||||
class="recipe-color-circle"
|
||||
[class.dark-mode]="isDarkColor"
|
||||
[style.backgroundColor]="recipe.color">
|
||||
<div>{{recipe.gloss}}%</div>
|
||||
</div>
|
||||
<cre-color-preview [recipe]="recipe"></cre-color-preview>
|
||||
</div>
|
||||
<div class="d-flex flex-row">
|
||||
<div class="d-flex flex-column">
|
||||
<p>Échantillon #{{recipe.sample}}</p>
|
||||
<p *ngIf="recipe.approbationDate">Approuvée le {{recipe.approbationDate}}</p>
|
||||
<p *ngIf="recipe.approbationDate">Approuvée le {{approbationDate}}</p>
|
||||
<div *ngIf="!recipe.approbationDate" class="recipe-not-approved-wrapper d-flex flex-row">
|
||||
<p>Non approuvée</p>
|
||||
<mat-icon svgIcon="alert" class="color-warning"></mat-icon>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<EmployeeGroup[]>
|
||||
@Input() groups$: Observable<Group[]>
|
||||
@Input() selectedGroupId: number | null
|
||||
|
||||
hoveredStep: RecipeStep | null
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
Enregistrer
|
||||
</button>
|
||||
<button
|
||||
*ngIf="hasDeletePermission"
|
||||
mat-raised-button
|
||||
color="warn"
|
||||
(click)="confirmBoxComponent.show()">
|
||||
|
@ -52,7 +51,7 @@
|
|||
#stepTable
|
||||
[recipe]="recipe"
|
||||
[groups$]="groups$"
|
||||
[selectedGroupId]="loggedInEmployeeGroupId">
|
||||
[selectedGroupId]="loggedInUserGroupId">
|
||||
</cre-step-table>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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<number, RecipeStep[]>): boolean {
|
||||
|
|
|
@ -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<number, string> {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -17,6 +17,10 @@ export class RecipeService {
|
|||
return this.api.get<Recipe[]>('/recipe')
|
||||
}
|
||||
|
||||
getAllByName(name: string): Observable<Recipe[]> {
|
||||
return this.api.get<Recipe[]>(`/recipe?name=${name}`)
|
||||
}
|
||||
|
||||
get allSortedByCompany(): Observable<{ company: string, recipes: Recipe[] }[]> {
|
||||
return this.all.pipe(map(recipes => {
|
||||
const mapped = []
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<EmployeeGroup[]> {
|
||||
return this.api.get<EmployeeGroup[]>('/employee/group')
|
||||
get all(): Observable<Group[]> {
|
||||
return this.api.get<Group[]>('/user/group')
|
||||
}
|
||||
|
||||
get allWithDefault(): Observable<EmployeeGroup[]> {
|
||||
get allWithDefault(): Observable<Group[]> {
|
||||
return this.all
|
||||
.pipe(tap(groups => groups.unshift({
|
||||
id: -1,
|
||||
|
@ -26,41 +26,41 @@ export class GroupService {
|
|||
})))
|
||||
}
|
||||
|
||||
getById(id: number): Observable<EmployeeGroup> {
|
||||
return this.api.get<EmployeeGroup>(`/employee/group/${id}`)
|
||||
getById(id: number): Observable<Group> {
|
||||
return this.api.get<Group>(`/user/group/${id}`)
|
||||
}
|
||||
|
||||
get defaultGroup(): Observable<EmployeeGroup> {
|
||||
return this.api.get<EmployeeGroup>('/employee/group/default')
|
||||
get defaultGroup(): Observable<Group> {
|
||||
return this.api.get<Group>('/user/group/default')
|
||||
}
|
||||
|
||||
setDefaultGroup(value: EmployeeGroup): Observable<void> {
|
||||
return this.api.post<void>(`/employee/group/default/${value.id}`, {})
|
||||
setDefaultGroup(value: Group): Observable<void> {
|
||||
return this.api.post<void>(`/user/group/default/${value.id}`, {})
|
||||
}
|
||||
|
||||
getEmployeesForGroup(id: number): Observable<Employee[]> {
|
||||
return this.api.get<Employee[]>(`/employee/group/${id}/employees`)
|
||||
getUsersForGroup(id: number): Observable<User[]> {
|
||||
return this.api.get<User[]>(`/user/group/${id}/users`)
|
||||
}
|
||||
|
||||
addEmployeeToGroup(id: number, employee: Employee): Observable<void> {
|
||||
return this.api.put<void>(`/employee/group/${id}/${employee.id}`)
|
||||
addUserToGroup(id: number, user: User): Observable<void> {
|
||||
return this.api.put<void>(`/user/group/${id}/${user.id}`)
|
||||
}
|
||||
|
||||
removeEmployeeFromGroup(employee: Employee): Observable<void> {
|
||||
return this.api.delete<void>(`/employee/group/${employee.group.id}/${employee.id}`)
|
||||
removeUserFromGroup(user: User): Observable<void> {
|
||||
return this.api.delete<void>(`/user/group/${user.group.id}/${user.id}`)
|
||||
}
|
||||
|
||||
save(name: string, permissions: EmployeePermission[]): Observable<EmployeeGroup> {
|
||||
save(name: string, permissions: Permission[]): Observable<Group> {
|
||||
const group = {name, permissions}
|
||||
return this.api.post<EmployeeGroup>('/employee/group', group)
|
||||
return this.api.post<Group>('/user/group', group)
|
||||
}
|
||||
|
||||
update(id: number, name: string, permissions: EmployeePermission[]): Observable<EmployeeGroup> {
|
||||
update(id: number, name: string, permissions: Permission[]): Observable<Group> {
|
||||
const group = {id, name, permissions}
|
||||
return this.api.put<EmployeeGroup>('/employee/group', group)
|
||||
return this.api.put<Group>('/user/group', group)
|
||||
}
|
||||
|
||||
delete(id: number): Observable<EmployeeGroup> {
|
||||
return this.api.delete<EmployeeGroup>(`/employee/group/${id}`)
|
||||
delete(id: number): Observable<Group> {
|
||||
return this.api.delete<Group>(`/user/group/${id}`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<boolean>()
|
||||
|
||||
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}`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<div class="d-flex flex-row justify-content-between px-5 py-4">
|
||||
<ng-content select="cre-action-group"></ng-content>
|
||||
</div>
|
|
@ -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 {
|
||||
}
|
|
@ -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 {}
|
|
@ -0,0 +1,3 @@
|
|||
<div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
div
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 1rem
|
|
@ -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 {
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import {Component, Input} from '@angular/core'
|
||||
import {ThemePalette} from '@angular/material/core'
|
||||
|
||||
@Component({
|
||||
selector: 'cre-button',
|
||||
template: `
|
||||
<button mat-raised-button [color]="color" [disabled]="disabled">
|
||||
<ng-content></ng-content>
|
||||
</button>
|
||||
`
|
||||
})
|
||||
export class CreButtonComponent {
|
||||
@Input() color: ThemePalette
|
||||
@Input() disabled = false
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cre-primary-button',
|
||||
template: `
|
||||
<cre-button color="primary" [disabled]="disabled">
|
||||
<ng-content></ng-content>
|
||||
</cre-button>
|
||||
`
|
||||
})
|
||||
export class CrePrimaryButtonComponent {
|
||||
@Input() disabled = false
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cre-accent-button',
|
||||
template: `
|
||||
<cre-button color="accent" [disabled]="disabled">
|
||||
<ng-content></ng-content>
|
||||
</cre-button>
|
||||
`
|
||||
})
|
||||
export class CreAccentButtonComponent {
|
||||
@Input() disabled = false
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cre-warn-button',
|
||||
template: `
|
||||
<cre-button color="warn" [disabled]="disabled">
|
||||
<ng-content></ng-content>
|
||||
</cre-button>
|
||||
`
|
||||
})
|
||||
export class CreWarnButtonComponent {
|
||||
@Input() disabled = false
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<div
|
||||
class="recipe-color-circle mat-elevation-z2"
|
||||
[class.dark-mode]="isDarkColor"
|
||||
[style.backgroundColor]="recipe.color">
|
||||
<div>{{recipe.gloss}}%</div>
|
||||
</div>
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@
|
|||
</mat-card-content>
|
||||
<mat-card-actions *ngIf="!disableButtons">
|
||||
<button mat-raised-button color="primary" [routerLink]="backButtonLink">Retour</button>
|
||||
<button mat-raised-button color="warn" *ngIf="canDelete" (click)="confirmBoxComponent.show()">Supprimer</button>
|
||||
<button mat-raised-button color="warn" (click)="confirmBoxComponent.show()">Supprimer</button>
|
||||
<button mat-raised-button color="accent" [disabled]="form.invalid" (click)="submitForm()">
|
||||
Enregistrer
|
||||
</button>
|
||||
|
|
|
@ -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<any>()
|
||||
|
@ -55,8 +53,4 @@ export class EntityEditComponent {
|
|||
})
|
||||
return values
|
||||
}
|
||||
|
||||
get canDelete(): boolean {
|
||||
return this.accountService.hasPermission(this.deletePermission)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T> {
|
|||
@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<T> {
|
|||
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
|
||||
) {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<ng-content select="cre-form-title"></ng-content>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content [class.no-action]="!hasActions">
|
||||
<form [formGroup]="form">
|
||||
<ng-content select="cre-form-content"></ng-content>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<mat-card-actions *ngIf="hasActions">
|
||||
<ng-content select="cre-form-actions"></ng-content>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
|
@ -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 {}
|
|
@ -0,0 +1,10 @@
|
|||
cre-form
|
||||
display: block
|
||||
|
||||
mat-card
|
||||
width: inherit
|
||||
|
||||
cre-form-actions
|
||||
display: flex
|
||||
flex-direction: row
|
||||
gap: 1rem
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<cre-employee-menu></cre-employee-menu>
|
||||
<cre-user-menu></cre-user-menu>
|
||||
<img
|
||||
class="flex-grow-0"
|
||||
src="assets/logo.png"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Component} from '@angular/core'
|
||||
import {ActivatedRoute, ResolveEnd, Router} from '@angular/router'
|
||||
import {AppState} from '../../app-state'
|
||||
import {Employee, EmployeePermission} from '../../model/employee'
|
||||
import {Permission} from '../../model/user'
|
||||
import {AccountService} from '../../../accounts/services/account.service'
|
||||
import {SubscribingComponent} from '../subscribing.component'
|
||||
import {ErrorService} from '../../service/error.service'
|
||||
|
@ -13,10 +13,10 @@ import {ErrorService} from '../../service/error.service'
|
|||
})
|
||||
export class HeaderComponent extends SubscribingComponent {
|
||||
links: HeaderLink[] = [
|
||||
{route: '/color', title: 'Couleurs', requiredPermission: EmployeePermission.VIEW_RECIPES},
|
||||
{route: '/catalog', title: 'Catalogue', requiredPermission: EmployeePermission.VIEW_CATALOG},
|
||||
{route: '/color', title: 'Couleurs', requiredPermission: Permission.VIEW_RECIPES},
|
||||
{route: '/catalog', title: 'Catalogue', requiredPermission: Permission.VIEW_CATALOG},
|
||||
{route: '/misc', title: 'Autres', enabled: true},
|
||||
{route: '/admin', title: 'Administration', requiredPermission: EmployeePermission.VIEW_USERS},
|
||||
{route: '/admin', title: 'Administration', requiredPermission: Permission.VIEW_USERS},
|
||||
]
|
||||
_activeLink = this.links[0].route
|
||||
|
||||
|
@ -78,7 +78,7 @@ export class HeaderComponent extends SubscribingComponent {
|
|||
}
|
||||
|
||||
if (l.route === '/misc') {
|
||||
l.enabled = this.accountService.hasPermission(EmployeePermission.GENERATE_TOUCH_UP_KIT)
|
||||
l.enabled = this.accountService.hasPermission(Permission.VIEW_TOUCH_UP_KITS)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ class HeaderLink {
|
|||
constructor(
|
||||
public route: string,
|
||||
public title: string,
|
||||
public requiredPermission?: EmployeePermission,
|
||||
public requiredPermission?: Permission,
|
||||
public enabled?
|
||||
) {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
@import "~src/custom-theme"
|
||||
|
||||
.info-banner-wrapper
|
||||
background-color: $color-primary
|
||||
color: $light-primary-text
|
||||
padding: 1rem
|
||||
|
||||
.info-banner-section-wrapper
|
||||
margin-right: 3rem
|
||||
|
||||
h3
|
||||
text-decoration: underline
|
||||
font-weight: bold
|
||||
|
||||
p
|
||||
margin-bottom: 0
|
|
@ -0,0 +1,49 @@
|
|||
import {Component} from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'info-banner',
|
||||
template: `
|
||||
<div class="info-banner-wrapper">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`,
|
||||
styleUrls: ['info-banner.component.sass']
|
||||
})
|
||||
export class InfoBannerComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'info-banner-title',
|
||||
template: `
|
||||
<h3>
|
||||
<ng-content></ng-content>
|
||||
</h3>
|
||||
`,
|
||||
styleUrls: ['info-banner.component.sass']
|
||||
})
|
||||
export class InfoBannerTitleComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'info-banner-content',
|
||||
template: `
|
||||
<div class="info-banner-content-wrapper d-flex flex-row">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`,
|
||||
styleUrls: ['info-banner.component.sass']
|
||||
})
|
||||
export class InfoBannerContentComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'info-banner-section',
|
||||
template: `
|
||||
<div class="info-banner-section-wrapper">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`,
|
||||
styleUrls: ['info-banner.component.sass']
|
||||
})
|
||||
export class InfoBannerSectionComponent {
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<mat-form-field>
|
||||
<mat-label>{{label}}</mat-label>
|
||||
<input
|
||||
*ngIf="!control"
|
||||
matInput
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
[required]="required"
|
||||
[matAutocomplete]="auto"
|
||||
[(ngModel)]="value"
|
||||
(change)="valueChange.emit(value)"/>
|
||||
<input
|
||||
*ngIf="control"
|
||||
matInput
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
[required]="required"
|
||||
[formControl]="control"
|
||||
[matAutocomplete]="auto"/>
|
||||
<mat-icon matSuffix [svgIcon]="icon"></mat-icon>
|
||||
<mat-error *ngIf="control && control.invalid">
|
||||
<span *ngIf="control.errors.required">Ce champ est requis</span>
|
||||
<ng-container *ngIf="errors" [ngTemplateOutlet]="errors"
|
||||
[ngTemplateOutletContext]="{errors: control.errors}"></ng-container>
|
||||
</mat-error>
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<mat-option *ngFor="let option of options | async" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
|
@ -0,0 +1,37 @@
|
|||
<mat-form-field>
|
||||
<mat-label>{{label}}</mat-label>
|
||||
<mat-chip-list
|
||||
[formControl]="control"
|
||||
[required]="required"
|
||||
#chipList>
|
||||
<mat-chip
|
||||
*ngFor="let value of selectedValues"
|
||||
[removable]="true"
|
||||
(removed)="remove(value)">
|
||||
{{value}}
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
|
||||
<input
|
||||
#chipInput
|
||||
matInput
|
||||
[formControl]="inputControl"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
(matChipInputTokenEnd)="add($event)"/>
|
||||
</mat-chip-list>
|
||||
|
||||
<mat-icon matSuffix [svgIcon]="icon"></mat-icon>
|
||||
<mat-error *ngIf="control && control.invalid">
|
||||
<span *ngIf="control.errors.required">Ce champ est requis</span>
|
||||
<ng-container *ngIf="errors" [ngTemplateOutlet]="errors"
|
||||
[ngTemplateOutletContext]="{errors: control.errors}"></ng-container>
|
||||
</mat-error>
|
||||
|
||||
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||
<mat-option *ngFor="let option of filteredOptions | async" [value]="option.key">
|
||||
{{option.display ? option.display : option.value}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
|
@ -0,0 +1,23 @@
|
|||
<mat-form-field>
|
||||
<mat-label>{{label}}</mat-label>
|
||||
<mat-chip-list
|
||||
[formControl]="control"
|
||||
[required]="required"
|
||||
#chipList>
|
||||
<mat-chip
|
||||
*ngFor="let value of selectedValues"
|
||||
[selectable]="true"
|
||||
[removable]="true"
|
||||
(removed)="remove(value)">
|
||||
{{value}}
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
<input
|
||||
[formControl]="inputControl"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
(matChipInputTokenEnd)="add($event)">
|
||||
<mat-icon matSuffix [svgIcon]="icon"></mat-icon>
|
||||
</mat-chip-list>
|
||||
</mat-form-field>
|
|
@ -0,0 +1,22 @@
|
|||
<mat-form-field>
|
||||
<mat-label>{{label}}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
[required]="required"
|
||||
[formControl]="control"
|
||||
[matAutocomplete]="auto">
|
||||
<mat-icon matSuffix [svgIcon]="icon"></mat-icon>
|
||||
|
||||
<mat-error *ngIf="control && control.invalid">
|
||||
<span *ngIf="control.errors.required">Ce champ est requis</span>
|
||||
<ng-container *ngIf="errors" [ngTemplateOutlet]="errors"
|
||||
[ngTemplateOutletContext]="{errors: control.errors}"></ng-container>
|
||||
</mat-error>
|
||||
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<mat-option *ngFor="let option of options | async" [value]="option.key">
|
||||
{{option.value}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
|
@ -0,0 +1,28 @@
|
|||
<mat-form-field>
|
||||
<mat-label>{{label}}</mat-label>
|
||||
<input
|
||||
*ngIf="!control"
|
||||
matInput
|
||||
[type]="type"
|
||||
[step]="step"
|
||||
[placeholder]="placeholder"
|
||||
[(ngModel)]="value"
|
||||
[required]="required"
|
||||
[autocomplete]="autocomplete ? 'on' : 'off'"
|
||||
(change)="valueChange.emit(value)"/>
|
||||
<input
|
||||
*ngIf="control"
|
||||
matInput
|
||||
[type]="type"
|
||||
[step]="step"
|
||||
[placeholder]="placeholder"
|
||||
[required]="required"
|
||||
[formControl]="control"
|
||||
[autocomplete]="autocomplete ? 'on' : 'off'"/>
|
||||
<mat-icon matSuffix [svgIcon]="icon"></mat-icon>
|
||||
<mat-error *ngIf="control && control.invalid">
|
||||
<span *ngIf="control.errors.required">Ce champ est requis</span>
|
||||
<ng-container *ngIf="errors" [ngTemplateOutlet]="errors"
|
||||
[ngTemplateOutletContext]="{errors: control.errors}"></ng-container>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
|
@ -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 {
|
||||
}
|
|
@ -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<any>()
|
||||
|
||||
@ContentChild(TemplateRef) errors: TemplateRef<any>
|
||||
}
|
||||
|
||||
@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<string[]>
|
||||
@Input() value
|
||||
|
||||
@Output() valueChange = new EventEmitter<any>()
|
||||
|
||||
@ContentChild(TemplateRef) errors: TemplateRef<any>
|
||||
}
|
||||
|
||||
@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<ComboBoxEntry[]>
|
||||
|
||||
@ContentChild(TemplateRef) errors: TemplateRef<any>
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cre-chip-combo-box',
|
||||
templateUrl: 'chip-combo-box.html',
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class CreChipComboBoxComponent extends CreChipInputComponent implements OnDestroy {
|
||||
@Input() options: Observable<ComboBoxEntry[]>
|
||||
|
||||
@ContentChild(TemplateRef) errors: TemplateRef<any>
|
||||
@ViewChild('chipInput') chipInput: ElementRef<HTMLInputElement>
|
||||
@ViewChild('auto') matAutocomplete: MatAutocomplete
|
||||
|
||||
filteredOptions: Observable<ComboBoxEntry[]>
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
nav
|
||||
position: relative
|
||||
z-index: 90
|
||||
padding-bottom: 1px
|
||||
|
||||
a
|
||||
opacity: 1
|
||||
|
|
|
@ -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?
|
||||
) {
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<table mat-table [dataSource]="dataSource">
|
||||
<ng-content></ng-content>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="columns"></tr>
|
||||
<tr
|
||||
mat-row
|
||||
*matRowDef="let row; let i = index; columns: columns"
|
||||
[class.cre-row-selected]="interactive && selectedIndex === i"
|
||||
(mouseover)="onRowHover(i)"
|
||||
(click)="onRowSelected(i)">
|
||||
</tr>
|
||||
</table>
|
|
@ -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%)
|
|
@ -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 {
|
||||
}
|
|
@ -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<T> implements AfterContentInit {
|
||||
@ContentChildren(MatHeaderRowDef) headerRowDefs: QueryList<MatHeaderRowDef>
|
||||
@ContentChildren(MatRowDef) rowDefs: QueryList<MatRowDef<T>>
|
||||
@ContentChildren(MatColumnDef) columnDefs: QueryList<MatColumnDef>
|
||||
|
||||
@ContentChildren(CreInteractiveCell, {descendants: true}) interactiveCells: QueryList<CreInteractiveCell>
|
||||
|
||||
@ViewChild(MatTable, {static: true}) table: MatTable<T>
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +1,43 @@
|
|||
<div class="employee-menu-wrapper">
|
||||
<div *ngIf="employee" class="employee-info-wrapper d-flex flex-column" (click)="menuEnabled = !menuEnabled">
|
||||
<div class="user-menu-wrapper">
|
||||
<div *ngIf="user" class="user-info-wrapper d-flex flex-column" (click)="menuEnabled = !menuEnabled">
|
||||
<labeled-icon
|
||||
*ngIf="authenticated"
|
||||
icon="account"
|
||||
label="{{employee.firstName}} {{employee.lastName}}">
|
||||
label="{{user.firstName}} {{user.lastName}}">
|
||||
</labeled-icon>
|
||||
<div class="d-flex flex-row">
|
||||
<labeled-icon
|
||||
*ngIf="authenticated"
|
||||
icon="pound"
|
||||
[label]="employee.id.toString()">
|
||||
[label]="user.id.toString()">
|
||||
</labeled-icon>
|
||||
<labeled-icon
|
||||
*ngIf="employeeInGroup"
|
||||
class="employee-info-group"
|
||||
*ngIf="userInGroup"
|
||||
class="user-info-group"
|
||||
icon="account-multiple"
|
||||
[label]="employee.group.name">
|
||||
[label]="user.group.name">
|
||||
</labeled-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
*ngIf="!authenticated && !employeeInGroup"
|
||||
*ngIf="!authenticated && !userInGroup"
|
||||
(click)="openLogout()">
|
||||
Connexion
|
||||
</button>
|
||||
|
||||
<mat-action-list *ngIf="menuEnabled">
|
||||
<button
|
||||
*ngIf="!authenticated && employeeInGroup"
|
||||
*ngIf="!authenticated && userInGroup"
|
||||
mat-list-item
|
||||
class="employee-menu-item-login"
|
||||
class="user-menu-item-login"
|
||||
(click)="openLogin()">
|
||||
Connexion
|
||||
</button>
|
||||
<button
|
||||
*ngIf="authenticated"
|
||||
mat-list-item
|
||||
class="employee-menu-item-logout"
|
||||
class="user-menu-item-logout"
|
||||
(click)="openLogout()">
|
||||
Déconnexion
|
||||
</button>
|
|
@ -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
|
|
@ -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<boolean>()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<any>
|
||||
) {
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
}]
|
||||
}
|
|
@ -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[]
|
||||
) {
|
||||
|
|
|
@ -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
|
||||
) {
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
}]
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<T>(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}`
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<div class="touchupkit-finish-container" (mouseover)="hover = true" (mouseleave)="hover = false">
|
||||
<div [class.matching]="matchesRecipes">{{finish}}</div>
|
||||
<div class="matching-recipes mat-elevation-z4" *ngIf="matchesRecipes && hover">
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let recipe of matchingRecipes" (click)="openRecipe(recipe)">
|
||||
<span>{{recipe.name}} - {{recipe.company.name}}</span>
|
||||
<cre-color-preview [recipe]="recipe"></cre-color-preview>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
</div>
|
|
@ -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
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<cre-form [formControls]="controls" class="mx-auto" style="width: 50rem">
|
||||
<cre-form-title>Ajouter un kit de retouche</cre-form-title>
|
||||
<cre-form-content>
|
||||
<cre-input [control]="controls.project" label="Project" icon="archive"></cre-input>
|
||||
<cre-input [control]="controls.buggy" label="Chariot" icon="pound"></cre-input>
|
||||
<cre-autocomplete-input
|
||||
[control]="controls.company"
|
||||
[options]="companies$"
|
||||
label="Bannière"
|
||||
icon="domain">
|
||||
</cre-autocomplete-input>
|
||||
<cre-input [control]="controls.quantity" label="Quantité" icon="beaker-outline" type="number">
|
||||
<ng-template let-errors="errors">
|
||||
<span *ngIf="errors && errors.min">La quantité doit être supérieure ou égale à 1</span>
|
||||
</ng-template>
|
||||
</cre-input>
|
||||
<cre-input [control]="controls.shippingDate" label="Date de livraison" icon="calendar" type="date"></cre-input>
|
||||
<cre-chip-combo-box
|
||||
#finishInput
|
||||
[control]="controls.finish"
|
||||
[options]="finish$"
|
||||
label="Fini"
|
||||
icon="flare">
|
||||
</cre-chip-combo-box>
|
||||
<cre-chip-input
|
||||
#materialInput
|
||||
[control]="controls.material"
|
||||
label="Matériel"
|
||||
icon="wall">
|
||||
</cre-chip-input>
|
||||
</cre-form-content>
|
||||
</cre-form>
|
||||
|
||||
<touchupkit-product-editor [products]="touchUpKit?.content"></touchupkit-product-editor>
|
|
@ -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<TouchUpKit>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<mat-card class="mt-5 mx-auto">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Contenu</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<table mat-table [dataSource]="products" style="width: 60rem">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>Nom</th>
|
||||
<td mat-cell *matCellDef="let product; let i = index">
|
||||
<ng-container *ngVar="isFocused(i, product) as focused">
|
||||
<cre-input *ngIf="focused" [(value)]="product.name"></cre-input>
|
||||
<span *ngIf="!focused" class="focused">{{product.name}}</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Description</th>
|
||||
<td mat-cell *matCellDef="let product; let i = index">
|
||||
<ng-container *ngVar="isFocused(i, product) as focused">
|
||||
<cre-input *ngIf="focused" [(value)]="product.description" placeholder="-" [required]="false"></cre-input>
|
||||
<span *ngIf="!focused" class="focused">{{product.description ? product.description : '-'}}</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let product; let i = index">
|
||||
<ng-container *ngVar="isFocused(i, product) as focused">
|
||||
<cre-input *ngIf="focused" type="number" step="1" [(value)]="product.quantity"></cre-input>
|
||||
<span *ngIf="!focused" class="focused">{{product.quantity}}</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="removeButton">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<cre-accent-button (click)="addRow()">Ajouter</cre-accent-button>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let product">
|
||||
<cre-warn-button (click)="removeRow(product)">Retirer</cre-warn-button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="productCols"></tr>
|
||||
<tr
|
||||
mat-row
|
||||
*matRowDef="let product; columns: productCols"
|
||||
(mouseover)="hoveredProduct = product"
|
||||
(click)="selectedProduct = product">
|
||||
</tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
|
@ -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
|
|
@ -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<TouchUpKitProduct>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<cre-action-bar>
|
||||
<cre-action-group>
|
||||
<cre-primary-button routerLink="/misc/touch-up-kit/list">Retour</cre-primary-button>
|
||||
</cre-action-group>
|
||||
<cre-action-group>
|
||||
<cre-accent-button [disabled]="!form.formValid" (click)="form.submit()">Enregistrer</cre-accent-button>
|
||||
</cre-action-group>
|
||||
</cre-action-bar>
|
||||
|
||||
<touchupkit-form #form (submitForm)="submit($event)">
|
||||
</touchupkit-form>
|
|
@ -0,0 +1,19 @@
|
|||
<info-banner>
|
||||
<info-banner-title>{{touchUpKit.project}} - {{touchUpKit.company}}</info-banner-title>
|
||||
<info-banner-content>
|
||||
<info-banner-section>
|
||||
<p>Chariot: {{touchUpKit.buggy}}</p>
|
||||
<p>Quantité: {{touchUpKit.quantity}}</p>
|
||||
<p>Date de livraison: {{shippingDate}}</p>
|
||||
</info-banner-section>
|
||||
<info-banner-section>
|
||||
<p>Fini:
|
||||
<ng-container *ngFor="let finish of touchUpKit.finish; let i = index">
|
||||
<touchupkit-finish [finish]="finish"></touchupkit-finish>
|
||||
<ng-container *ngIf="i < touchUpKit.finish.length - 1"> - </ng-container>
|
||||
</ng-container>
|
||||
</p>
|
||||
<p>Matériel: {{material}}</p>
|
||||
</info-banner-section>
|
||||
</info-banner-content>
|
||||
</info-banner>
|
|
@ -0,0 +1,28 @@
|
|||
<div *ngIf="touchUpKit">
|
||||
<touchupkit-banner [touchUpKit]="touchUpKit"></touchupkit-banner>
|
||||
|
||||
<div class="action-bar backward">
|
||||
<button mat-raised-button color="primary" routerLink="/misc/touch-up-kit">Retour</button>
|
||||
<button mat-raised-button color="accent" (click)="openPdf()">PDF</button>
|
||||
</div>
|
||||
|
||||
<cre-table class="mx-auto" [dataSource]="touchUpKit.content" [columns]="contentTableCols" [interactive]="false">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>Nom</th>
|
||||
<td mat-cell *matCellDef="let product">{{product.name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Description</th>
|
||||
<td mat-cell *matCellDef="let product">
|
||||
<ng-container *ngIf="product.description">{{product.description}}</ng-container>
|
||||
<ng-container *ngIf="!product.description">-</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let product">{{product.quantity}}</td>
|
||||
</ng-container>
|
||||
</cre-table>
|
||||
</div>
|
|
@ -0,0 +1,2 @@
|
|||
info-banner-section p
|
||||
margin-bottom: 0
|
|
@ -0,0 +1,24 @@
|
|||
<ng-container *ngIf="touchUpKit">
|
||||
<cre-action-bar>
|
||||
<cre-action-group>
|
||||
<cre-primary-button routerLink="/misc/touch-up-kit/list">Retour</cre-primary-button>
|
||||
</cre-action-group>
|
||||
<cre-action-group>
|
||||
<cre-warn-button (click)="deleteConfirmBox.show()">Supprimer</cre-warn-button>
|
||||
<cre-accent-button [disabled]="!form.formValid" (click)="form.submit()">Enregistrer</cre-accent-button>
|
||||
</cre-action-group>
|
||||
</cre-action-bar>
|
||||
|
||||
<touchupkit-form
|
||||
#form
|
||||
[touchUpKit]="touchUpKit"
|
||||
(submitForm)="submit($event)">
|
||||
</touchupkit-form>
|
||||
|
||||
<cre-confirm-box
|
||||
#deleteConfirmBox
|
||||
message="Voulez-vous vraiment supprimer le kit de retouche du projet {{touchUpKit.project}}? ({{touchUpKit.buggy}})"
|
||||
(confirm)="delete()">
|
||||
</cre-confirm-box>
|
||||
</ng-container>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<cre-action-bar>
|
||||
<cre-action-group></cre-action-group>
|
||||
<cre-action-group>
|
||||
<cre-accent-button routerLink="/misc/touch-up-kit/add">Ajouter</cre-accent-button>
|
||||
</cre-action-group>
|
||||
</cre-action-bar>
|
||||
|
||||
<cre-table class="mx-auto" [dataSource]="touchUpKits$ | async" [columns]="columns">
|
||||
<ng-container matColumnDef="project">
|
||||
<th mat-header-cell *matHeaderCellDef>Project</th>
|
||||
<td mat-cell *matCellDef="let touchUpKit">{{touchUpKit.project}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="buggy">
|
||||
<th mat-header-cell *matHeaderCellDef>Chariot</th>
|
||||
<td mat-cell *matCellDef="let touchUpKit">{{touchUpKit.buggy}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="company">
|
||||
<th mat-header-cell *matHeaderCellDef>Bannière</th>
|
||||
<td mat-cell *matCellDef="let touchUpKit">{{touchUpKit.company}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="shippingDate">
|
||||
<th mat-header-cell *matHeaderCellDef>Date de livraison</th>
|
||||
<td mat-cell *matCellDef="let touchUpKit">{{touchUpKit.shippingDate}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="pdfButton">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let touchUpKit; let i = index">
|
||||
<cre-accent-button [creInteractiveCell]="i" (click)="openTouchUpKitPdf(touchUpKit)">PDF</cre-accent-button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="detailsButton">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let touchUpKit; let i = index">
|
||||
<cre-accent-button [creInteractiveCell]="i" routerLink="/misc/touch-up-kit/details/{{touchUpKit.id}}">
|
||||
Détails
|
||||
</cre-accent-button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="editButton">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell [class.disabled]="!canEditTouchUpKits" *matCellDef="let touchUpKit; let i = index">
|
||||
<cre-accent-button [creInteractiveCell]="i" routerLink="/misc/touch-up-kit/edit/{{touchUpKit.id}}">
|
||||
Modifier
|
||||
</cre-accent-button>
|
||||
</td>
|
||||
</ng-container>
|
||||
</cre-table>
|
|
@ -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'
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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<TouchUpKit[]> {
|
||||
return this.api.get<TouchUpKit[]>('/touchupkit')
|
||||
}
|
||||
|
||||
getById(id: number): Observable<TouchUpKit> {
|
||||
return this.api.get<TouchUpKit>(`/touchupkit/${id}`)
|
||||
}
|
||||
|
||||
save(touchUpKit: TouchUpKit): Observable<void> {
|
||||
return this.api.post<void>('/touchupkit', touchUpKit)
|
||||
}
|
||||
|
||||
update(touchUpKit: TouchUpKit): Observable<void> {
|
||||
return this.api.put<void>('/touchupkit', touchUpKit)
|
||||
}
|
||||
|
||||
delete(id: number): Observable<void> {
|
||||
return this.api.delete<void>(`/touchupkit/${id}`)
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue