diff --git a/src/frontend/app/administration/administration-routing.module.ts b/src/frontend/app/administration/administration-routing.module.ts index 55a4988ca7984e56e33d1384b983e36356e0cb72..cd0f8b0afdf186052541587365ddf147bd9a1543 100755 --- a/src/frontend/app/administration/administration-routing.module.ts +++ b/src/frontend/app/administration/administration-routing.module.ts @@ -55,6 +55,8 @@ import { RegisteredMailListComponent } from './registered-mail/registered-mail-l import { RegisteredMailComponent } from './registered-mail/registered-mail.component'; import { SearchAdministrationComponent } from './search/search-administration.component'; import { SsoAdministrationComponent } from './connection/sso/sso-administration.component'; +import { AttachmentTypeAdministrationComponent } from './attachment/attachment-type-administration.component'; +import { AttachmentTypesAdministrationComponent } from './attachment/attachment-types-administration.component'; @NgModule({ @@ -132,6 +134,10 @@ import { SsoAdministrationComponent } from './connection/sso/sso-administration. { path: 'administration/issuingSites/:id', canActivate: [AppGuard], component: IssuingSiteComponent }, { path: 'administration/search', canActivate: [AppGuard], component: SearchAdministrationComponent }, { path: 'administration/connections/sso', canActivate: [AppGuard], component: SsoAdministrationComponent }, + { path: 'administration/attachments/types', canActivate: [AppGuard], component: AttachmentTypesAdministrationComponent }, + { path: 'administration/attachments/types/new', canActivate: [AppGuard], component: AttachmentTypeAdministrationComponent }, + { path: 'administration/attachments/types/:id', canActivate: [AppGuard], component: AttachmentTypeAdministrationComponent }, + { path: 'administration/attachments', redirectTo: 'attachments/types', pathMatch: 'full' }, ]), ], exports: [ diff --git a/src/frontend/app/administration/administration.module.ts b/src/frontend/app/administration/administration.module.ts index 07034f29afef8288cb25406461d6232492d76569..783e0ce7f8cf889bd437f6d5a667cc13e0123f92 100755 --- a/src/frontend/app/administration/administration.module.ts +++ b/src/frontend/app/administration/administration.module.ts @@ -78,6 +78,8 @@ import { RegisteredMailListComponent } from './registered-mail/registered-mail-l import { SearchAdministrationComponent } from './search/search-administration.component'; import { SsoAdministrationComponent } from './connection/sso/sso-administration.component'; import { LifeCycleComponent } from './parameter/lifeCycle/life-cycle.component'; +import { AttachmentTypesAdministrationComponent } from './attachment/attachment-types-administration.component'; +import { AttachmentTypeAdministrationComponent } from './attachment/attachment-type-administration.component'; @NgModule({ @@ -163,7 +165,9 @@ import { LifeCycleComponent } from './parameter/lifeCycle/life-cycle.component'; RegisteredMailListComponent, SearchAdministrationComponent, SsoAdministrationComponent, - LifeCycleComponent + LifeCycleComponent, + AttachmentTypeAdministrationComponent, + AttachmentTypesAdministrationComponent ], entryComponents: [ AccountLinkComponent, diff --git a/src/frontend/app/administration/administration.service.ts b/src/frontend/app/administration/administration.service.ts index 91e78f0c6b77094641b0f992e5ad8fd635ec3870..73ec015faa595d122f9b762ac9e8029fd7b7a77f 100644 --- a/src/frontend/app/administration/administration.service.ts +++ b/src/frontend/app/administration/administration.service.ts @@ -128,6 +128,12 @@ export class AdministrationService { page: 0, field: '' }, + admin_attachments: { + sort: 'label', + sortDirection: 'asc', + page: 0, + field: '' + }, }; dataSource: MatTableDataSource<any>; filterColumns: string[]; diff --git a/src/frontend/app/administration/attachment/attachment-type-administration.component.html b/src/frontend/app/administration/attachment/attachment-type-administration.component.html new file mode 100644 index 0000000000000000000000000000000000000000..305d745234e7be954b0be493877204b07999493d --- /dev/null +++ b/src/frontend/app/administration/attachment/attachment-type-administration.component.html @@ -0,0 +1,65 @@ +<mat-sidenav-container autosize class="maarch-container"> + <mat-sidenav-content> + <div class="bg-head"> + <div class="bg-head-title" [class.customContainerRight]="appService.getViewMode()"> + <div class="bg-head-title-label"> + <header-left></header-left> + </div> + <div class="bg-head-title-tool"> + <header-right></header-right> + </div> + </div> + <div class="bg-head-content" [class.fullContainer]="appService.getViewMode()"> + </div> + </div> + <div class="container" [class.fullContainer]="appService.getViewMode()"> + <div class="container-content"> + <mat-card *ngIf="!loading" class="card-app-content"> + <form class="form-horizontal" (ngSubmit)="onSubmit()" [formGroup]="adminFormGroup"> + <mat-form-field> + <input matInput formControlName="typeId" name="typeId" + title="{{'lang.id' | translate}}" type="text" placeholder="{{'lang.id' | translate}}" + maxlength="255" pattern="^[\w.@-]*$" required> + </mat-form-field> + <mat-form-field> + <input matInput formControlName="label" name="label" + title="{{'lang.label' | translate}}" type="text" + placeholder="{{'lang.label' | translate}}" maxlength="255" required> + </mat-form-field> + <mat-expansion-panel expanded> + <mat-expansion-panel-header> + <mat-panel-title color="primary"> + {{'lang.options' | translate}} + </mat-panel-title> + <mat-panel-description> + </mat-panel-description> + </mat-expansion-panel-header> + <mat-list *ngFor="let idOpt of getOptions()"> + <mat-list-item> + <div mat-line style="display: flex;"> + <mat-slide-toggle [formControlName]="idOpt" [name]="idOpt" + color="primary" [checked]="attachmentType[idOpt].value"> + {{'lang.' + idOpt + '_attachment' | translate}}</mat-slide-toggle> + + <i class="fa fa-question-circle" color="primary" style="cursor: help;" + [title]="'lang.' + idOpt + '_attachmentDesc' | translate"></i> + </div> + <div mat-line *ngIf="attachmentType[idOpt].value && idOpt==='signable'"> + <mat-form-field> + <span mattPrefix color="primary">{{'lang.iconLetter' | translate}} : </span> + <input matInput formControlName="icon" name="icon" type="text" maxlength="1"> + </mat-form-field> + </div> + </mat-list-item> + </mat-list> + </mat-expansion-panel> + <div style="text-align:center;"> + <button mat-raised-button color="primary" type="submit" + [disabled]="!adminFormGroup.valid">{{'lang.save' | translate}}</button> + </div> + </form> + </mat-card> + </div> + </div> + </mat-sidenav-content> +</mat-sidenav-container> \ No newline at end of file diff --git a/src/frontend/app/administration/attachment/attachment-type-administration.component.ts b/src/frontend/app/administration/attachment/attachment-type-administration.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..4cb49c7763db144147c9c0256b5673228d634d7b --- /dev/null +++ b/src/frontend/app/administration/attachment/attachment-type-administration.component.ts @@ -0,0 +1,128 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Router, ActivatedRoute } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationService } from '@service/notification/notification.service'; +import { HeaderService } from '@service/header.service'; +import { AppService } from '@service/app.service'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { finalize, tap, catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { FunctionsService } from '@service/functions.service'; +import { MatDialog } from '@angular/material/dialog'; +@Component({ + templateUrl: 'attachment-type-administration.component.html' +}) +export class AttachmentTypeAdministrationComponent implements OnInit { + + id: string; + creationMode: boolean; + loading: boolean = false; + + adminFormGroup: FormGroup; + + attachmentType: any = { + typeId: new FormControl({ value: '', disabled: false }, [Validators.required]), + label: new FormControl({ value: '', disabled: false }, [Validators.required]), + visible: new FormControl({ value: true, disabled: false }), + emailLink: new FormControl({ value: false, disabled: false }), + signable: new FormControl({ value: false, disabled: false }), + icon: new FormControl({ value: '', disabled: false }), + newVersionDefault: new FormControl({ value: false, disabled: false }), + }; + + constructor( + public translate: TranslateService, + public http: HttpClient, + private route: ActivatedRoute, + private router: Router, + private notify: NotificationService, + private headerService: HeaderService, + public appService: AppService, + public functions: FunctionsService, + public dialog: MatDialog, + private _formBuilder: FormBuilder, + ) { + } + + ngOnInit(): void { + + this.adminFormGroup = this._formBuilder.group(this.attachmentType); + this.loading = true; + console.log(typeof this.attachmentType['signable'].value); + + this.route.params.subscribe(async (params) => { + this.id = params['id']; + if (typeof params['id'] === 'undefined') { + this.headerService.setHeader(this.translate.instant('lang.attachmentTypeCreation')); + this.creationMode = true; + this.loading = false; + } else { + this.creationMode = false; + this.http.get(`../rest/attachmentsTypes/${this.id}`).pipe( + tap((data: any) => { + Object.keys(this.attachmentType).forEach(key => { + this.attachmentType[key].setValue(data[key]); + if (key === 'typeId') { + this.attachmentType[key].disable(); + } + }); + this.headerService.setHeader(this.translate.instant('lang.attachmentTypeModification'), this.attachmentType.label.value); + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + }); + } + + onSubmit() { + if (this.creationMode) { + this.create(); + } else { + this.update(); + } + } + + getOptions() { + return Object.keys(this.attachmentType).filter((id: any) => typeof this.attachmentType[id].value === 'boolean'); + } + + formatData() { + const formattedTag = {}; + Object.keys(this.attachmentType).forEach(element => { + formattedTag[element] = this.attachmentType[element].value; + }); + return formattedTag; + } + + create() { + this.http.post(`../rest/attachmentsTypes`, this.formatData()).pipe( + tap(() => { + this.notify.success(this.translate.instant('lang.attachmentTypeAdded')); + this.router.navigate(['/administration/attachments/types']); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + update() { + + this.http.put(`../rest/attachmentsTypes/${this.id}`, this.formatData()).pipe( + tap(() => { + this.notify.success(this.translate.instant('lang.attachmentTypeUpdated')); + this.router.navigate(['/administration/attachments/types']); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } +} diff --git a/src/frontend/app/administration/attachment/attachment-types-administration.component.html b/src/frontend/app/administration/attachment/attachment-types-administration.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4bfd874e0924d7a117d6e988260fb82db6e29b80 --- /dev/null +++ b/src/frontend/app/administration/attachment/attachment-types-administration.component.html @@ -0,0 +1,80 @@ +<mat-sidenav-container autosize class="maarch-container"> + <ng-template #adminMenuTemplate> + <mat-nav-list> + <h3 mat-subheader>{{'lang.actions' | translate}}</h3> + <a mat-list-item routerLink="/administration/attachments/types/new"> + <mat-icon color="primary" mat-list-icon class="fa fa-plus"></mat-icon> + <p mat-line> + {{'lang.add' | translate}} + </p> + </a> + </mat-nav-list> + </ng-template> + <mat-sidenav-content> + <div class="bg-head"> + <div class="bg-head-title" [class.customContainerRight]="appService.getViewMode()"> + <div class="bg-head-title-label"> + <header-left></header-left> + </div> + <div class="bg-head-title-tool"> + <header-right></header-right> + </div> + </div> + <div class="bg-head-content" [class.fullContainer]="appService.getViewMode()"> + </div> + </div> + <div class="container" [class.fullContainer]="appService.getViewMode()"> + <div class="container-content"> + <mat-card class="card-app-content"> + <div class="row" style="margin:0px;"> + <div class="col-md-6 col-xs-6"> + <mat-form-field> + <input matInput [formControl]="adminService.getFilterField()" placeholder="{{'lang.filterBy' | translate}}"> + </mat-form-field> + </div> + <div class="col-md-6 col-xs-6"> + <mat-paginator #paginator [length]="100" [hidePageSize]="true" [pageSize]="10"> + </mat-paginator> + </div> + </div> + <mat-table #table [dataSource]="adminService.getDataSource()" matSortDisableClear matSort> + <ng-container matColumnDef="id"> + <mat-header-cell *matHeaderCellDef mat-sort-header + [class.hide-for-mobile]="appService.getViewMode()">{{'lang.technicalId' | translate}}</mat-header-cell> + <mat-cell *matCellDef="let element"> + {{element.id}} </mat-cell> + </ng-container> + <ng-container matColumnDef="typeId"> + <mat-header-cell *matHeaderCellDef mat-sort-header style="flex:2;">{{'lang.id' | translate}} + </mat-header-cell> + <mat-cell *matCellDef="let element" style="flex:2;"> + {{element.typeId}} </mat-cell> + </ng-container> + <ng-container matColumnDef="label"> + <mat-header-cell *matHeaderCellDef mat-sort-header style="flex:2;">{{'lang.label' | translate}} + </mat-header-cell> + <mat-cell *matCellDef="let element" style="flex:2;"> + {{element.label}} </mat-cell> + </ng-container> + <ng-container matColumnDef="actions"> + <mat-header-cell *matHeaderCellDef style="flex:1;"></mat-header-cell> + <mat-cell *matCellDef="let element" style="justify-content: flex-end;flex:1;"> + <button mat-icon-button color="warn" matTooltip="{{'lang.delete' | translate}}" + (click)="$event.stopPropagation();delete(element)"> + <mat-icon class="fa fa-trash-alt fa-2x" aria-hidden="true"></mat-icon> + </button> + </mat-cell> + </ng-container> + <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> + <mat-row *matRowDef="let row; columns: displayedColumns;" + routerLink="/administration/attachments/types/{{row.id}}" style="cursor:pointer;" + matTooltip="{{'lang.view' | translate}}"></mat-row> + </mat-table> + <div class="mat-paginator" + style="min-height:48px;display: flex;justify-content: end;align-items: center;padding-right: 20px;"> + {{attachmentsTypes.length}} {{'lang.attachmentsTypes' | translate}}</div> + </mat-card> + </div> + </div> + </mat-sidenav-content> +</mat-sidenav-container> \ No newline at end of file diff --git a/src/frontend/app/administration/attachment/attachment-types-administration.component.scss b/src/frontend/app/administration/attachment/attachment-types-administration.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/frontend/app/administration/attachment/attachment-types-administration.component.ts b/src/frontend/app/administration/attachment/attachment-types-administration.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b1aa77a6330ebcbb85453b0d5f264ffe708a3bf --- /dev/null +++ b/src/frontend/app/administration/attachment/attachment-types-administration.component.ts @@ -0,0 +1,90 @@ +import { Component, OnInit, ViewChild, TemplateRef, ViewContainerRef } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationService } from '@service/notification/notification.service'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { HeaderService } from '@service/header.service'; +import { AppService } from '@service/app.service'; +import { tap, finalize, filter, exhaustMap, catchError, map } from 'rxjs/operators'; +import { ConfirmComponent } from '../../../plugins/modal/confirm.component'; +import { MatDialog } from '@angular/material/dialog'; +import {FunctionsService} from '@service/functions.service'; +import { of } from 'rxjs'; +import { AdministrationService } from '../administration.service'; + +@Component({ + templateUrl: 'attachment-types-administration.component.html' +}) +export class AttachmentTypesAdministrationComponent implements OnInit { + + @ViewChild('adminMenuTemplate', { static: true }) adminMenuTemplate: TemplateRef<any>; + + loading: boolean = true; + + attachmentsTypes: any[] = []; + resultsLength: number = 0; + displayedColumns = ['id', 'typeId', 'label', 'actions']; + filterColumns = ['typeId', 'label']; + + + @ViewChild(MatPaginator, { static: false }) paginator: MatPaginator; + @ViewChild(MatSort, { static: false }) sort: MatSort; + + constructor( + public translate: TranslateService, + public http: HttpClient, + private notify: NotificationService, + private headerService: HeaderService, + public appService: AppService, + public dialog: MatDialog, + public functions: FunctionsService, + public adminService: AdministrationService, + private viewContainerRef: ViewContainerRef + ) { } + + ngOnInit(): void { + this.headerService.setHeader(this.translate.instant('lang.administration') + ' ' + this.translate.instant('lang.attachmentsTypes')); + + this.headerService.injectInSideBarLeft(this.adminMenuTemplate, this.viewContainerRef, 'adminMenu'); + + this.loadList(); + } + + loadList() { + this.loading = true; + this.http.get('../rest/attachmentsTypes').pipe( + map((data: any) => { + const formatData = []; + Object.keys(data.attachmentsTypes).forEach(key => { + formatData.push(data.attachmentsTypes[key]); + }); + return formatData; + }), + tap((data: any) => { + this.attachmentsTypes = data; + this.resultsLength = data.length; + setTimeout(() => { + this.adminService.setDataSource('admin_attachments', this.attachmentsTypes, this.sort, this.paginator, this.filterColumns); + }, 0); + }), + finalize(() => this.loading = false) + ).subscribe(); + } + + delete(item: any) { + const dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: `${this.translate.instant('lang.delete')} "${item.label}"`, msg: this.translate.instant('lang.confirmAction') } }); + dialogRef.afterClosed().pipe( + filter((data: string) => data === 'ok'), + exhaustMap(() => this.http.delete(`../rest/attachmentsTypes/${item.id}`)), + tap(() => { + this.loadList(); + this.notify.success(this.translate.instant('lang.attachmentDeleted')); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } +} diff --git a/src/frontend/service/privileges.service.ts b/src/frontend/service/privileges.service.ts index e10205a1b6504c1d208bfb1692a58cab480e595d..70d2e14a888c962f3d13fcda67e0d8aab022b0da 100755 --- a/src/frontend/service/privileges.service.ts +++ b/src/frontend/service/privileges.service.ts @@ -315,6 +315,16 @@ export class PrivilegeService { 'angular': true, 'hasParams': false }, + { + 'id': 'admin_attachments', + 'label': 'lang.attachments', + 'comment': 'lang.attachmentsDesc', + 'route': '/administration/attachments/types', + 'unit': 'supervision', + 'style': 'fas fa-paperclip', + 'angular': true, + 'hasParams': false + }, ]; private privileges: privilege[] = [ diff --git a/src/lang/lang-fr.json b/src/lang/lang-fr.json index 264b58379817b1ec805fbc137b3e1e731e9ede39..ef577c054c46bd87138df1194e5986880dce8c1b 100644 --- a/src/lang/lang-fr.json +++ b/src/lang/lang-fr.json @@ -2165,5 +2165,21 @@ "tooManySignUser": "Nombre de signataire maximum dépassé", "templateNameMandatory": "Nom du modèle obligatoire", "requiredVisaUser": "{{min}} viseur(s) requis", - "authorizedSignUser": "{{max}} signataire(s) autorisé(s)" + "authorizedSignUser": "{{max}} signataire(s) autorisé(s)", + "visible_attachment": "Afficher le type", + "visible_attachmentDesc": "Sera possible de choisir ce type en création de pièce jointe.", + "emailLink_attachment": "Liaison automatique en envoi de courriel", + "emailLink_attachmentDesc": "Coche automatique les pièces jointes de ce type en envoi de courriel.", + "signable_attachment": "Autoriser la signature", + "signable_attachmentDesc": "Possibilité d'apposer une griffe sur les pièces jointes de ce type dans le parapheur.", + "newVersionDefault_attachment": "Nouvelle version par défaut en modification de pièce jointe", + "newVersionDefault_attachmentDesc": "Création par défaut d'une nouvelle version en modification de pièce jointe de ce type.", + "attachmentTypeCreation": "Création d'un type de pièce jointe", + "attachmentTypeAdded": "Type de pièce jointe ajouté", + "attachmentTypeUpdated": "Type de pièce jointe modifié", + "attachmentTypeDeleted": "Type de pièce jointe supprimé", + "attachmentsTypes": "Type(s) de pièce(s) jointe(s)", + "attachmentTypeModification": "Modification d'un type de pièce jointe", + "iconLetter": "Lettre icône", + "noneAlt": "Aucune" }