diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 97a4b48ab788d8890e731d6ba5aae06f80d39c5c..47497af1cd23e5c6707c4b77e91a05f183254c40 100755 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -107,6 +107,7 @@ import { LinkedResourceListComponent } from './linkedResource/linked-resource-li import { LinkResourceModalComponent } from './linkedResource/linkResourceModal/link-resource-modal.component'; import { DocumentViewerModalComponent } from './viewer/modal/document-viewer-modal.component'; +import { SendedResourceListComponent } from './sendedResource/sended-resource-list.component'; @@ -203,7 +204,8 @@ import { DocumentViewerModalComponent } from './viewer/modal/document-viewer-mod SearchAdvListComponent, LinkedResourceListComponent, LinkResourceModalComponent, - DocumentViewerModalComponent + DocumentViewerModalComponent, + SendedResourceListComponent ], entryComponents: [ ConfirmModalComponent, diff --git a/src/frontend/app/process/process.component.html b/src/frontend/app/process/process.component.html index 0a75007aba266110a53bb14603c1d1eef8ea29cf..4c3900e7c5f0e20f3a12787be78f648de15043ca 100644 --- a/src/frontend/app/process/process.component.html +++ b/src/frontend/app/process/process.component.html @@ -9,7 +9,8 @@ <div class="processTool"> <div class="processTool-module jiggle" *ngFor="let module of processTool" [class.processTool-module-active]="module.id === currentTool" matRipple - (click)="isToolEnabled(module.id) ? changeTab(module.id) : false" [class.tool-disabled]="!isToolEnabled(module.id)"> + (click)="isToolEnabled(module.id) ? changeTab(module.id) : false" + [class.tool-disabled]="!isToolEnabled(module.id)"> <i *ngIf="module.count > 0" class="fas fa-circle haveContent"></i> <i [class]="module.icon"></i> <span>{{module.label}}</span> @@ -77,11 +78,15 @@ [resId]="currentResourceInformations.resId"> </app-notes-list> <app-linked-resource-list *ngIf="currentTool === 'linkedResources' && !loading" #appLinkedResourceList - [resId]="currentResourceInformations.resId" (reloadBadgeLinkedResources)="refreshBadge($event,'linkedResources')"> + [resId]="currentResourceInformations.resId" + (reloadBadgeLinkedResources)="refreshBadge($event,'linkedResources')"> </app-linked-resource-list> <app-diffusions-list *ngIf="currentTool === 'diffusionList' && !loading" #appDiffusionsList [adminMode]="false" [resId]="currentResourceInformations.resId" [expanded]="true"> </app-diffusions-list> + <app-sended-resource-list *ngIf="currentTool === 'mails' && !loading" #appSendedResource + [resId]="currentResourceInformations.resId"> + </app-sended-resource-list> <app-visa-workflow *ngIf="currentTool === 'visaCircuit' && !loading" #appVisaWorkflow [resId]="currentResourceInformations.resId" [adminMode]="privilegeService.hasCurrentUserPrivilege('config_visa_workflow')"></app-visa-workflow> @@ -210,9 +215,14 @@ </div> </ng-template> </div> - <div style="position: absolute;right: 0px;top: 0px;height: 100%;display: flex;flex-direction: column;justify-content: center;"> - <button mat-icon-button *ngFor="let inteKey of integrationsInfo | keyvalue" [matTooltip]="lang[inteKey.key]" (click)="toggleIntegration(inteKey.key)"> - <mat-icon [class]="inteKey.value.icon" [class.checked]="currentResourceInformations.integrations[inteKey.key]" [class.unchecked]="!currentResourceInformations.integrations[inteKey.key]"></mat-icon> + <div + style="position: absolute;right: 0px;top: 0px;height: 100%;display: flex;flex-direction: column;justify-content: center;"> + <button mat-icon-button *ngFor="let inteKey of integrationsInfo | keyvalue" + [matTooltip]="lang[inteKey.key]" (click)="toggleIntegration(inteKey.key)"> + <mat-icon [class]="inteKey.value.icon" + [class.checked]="currentResourceInformations.integrations[inteKey.key]" + [class.unchecked]="!currentResourceInformations.integrations[inteKey.key]"> + </mat-icon> </button> </div> </div> @@ -229,7 +239,8 @@ <div class="content"> <app-document-viewer #appDocumentViewer style="height:100%;width:100%;" [editMode]="canEditData" [resId]="currentResourceInformations.resId" - [title]="currentResourceInformations.chrono + ' - ' + currentResourceInformations.subject" [sidenavLeft]="sidenavLeft"> + [title]="currentResourceInformations.chrono + ' - ' + currentResourceInformations.subject" + [sidenavLeft]="sidenavLeft"> </app-document-viewer> </div> </div> @@ -258,10 +269,14 @@ [resId]="currentResourceInformations.resId"> </app-notes-list> <app-linked-resource-list *ngIf="modal.id === 'linkedResources' && !loading" #appLinkedResourceList - [resId]="currentResourceInformations.resId" (reloadBadgeLinkedResources)="refreshBadge($event,'linkedResources')"></app-linked-resource-list> + [resId]="currentResourceInformations.resId" + (reloadBadgeLinkedResources)="refreshBadge($event,'linkedResources')"></app-linked-resource-list> <app-diffusions-list *ngIf="modal.id === 'diffusionList' && !loading" #appDiffusionsList [adminMode]="false" [resId]="currentResourceInformations.resId" [expanded]="true"> </app-diffusions-list> + <app-sended-resource-list *ngIf="modal.id === 'mails' && !loading" #appSendedResource + [resId]="currentResourceInformations.resId"> + </app-sended-resource-list> <app-visa-workflow *ngIf="modal.id === 'visaCircuit' && !loading" [adminMode]="privilegeService.hasCurrentUserPrivilege('config_visa_workflow')" #appVisaWorkflow [resId]="currentResourceInformations.resId"> diff --git a/src/frontend/app/sendedResource/sended-resource-list.component.html b/src/frontend/app/sendedResource/sended-resource-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e8ea2806c61aabe3a62b3de8414ffcbc742c2661 --- /dev/null +++ b/src/frontend/app/sendedResource/sended-resource-list.component.html @@ -0,0 +1,66 @@ +<ng-container *ngIf="loading; else elseLoading"> + <div class="loading"> + <mat-spinner></mat-spinner> + </div> +</ng-container> +<ng-template #elseLoading> + <div class="row"> + <div class="col-md-12" style="padding-bottom: 10px;"> + <mat-button-toggle-group *ngIf="sendedResources.length > 0" class="filterTypes" (change)="filterType($event)"> + <mat-button-toggle [checked]="currentFilter === ''" [value]="''">Tous</mat-button-toggle> + <mat-button-toggle *ngFor="let type of filterTypes | sortBy : 'label'" + [checked]="currentFilter === type.id" [value]="type.id">{{type.label}} + </mat-button-toggle> + </mat-button-toggle-group> + </div> + </div> + <mat-table #table [dataSource]="dataSource" matSort matSortActive="creationDate" matSortDirection="desc"> + <ng-container matColumnDef="creationDate"> + <mat-cell *matCellDef="let row" class="dataLine" style="flex: 1;padding: 0px;flex-direction: column;"> + <div class="subinfo"> + <span style="flex:1" [title]="row.creationDate | fullDate"> + Créé : <b>{{row.creationDate | timeAgo : 'full'}}</b> + </span> + <span *ngIf="row.sendDate !== null" style="flex:1;color:green" [title]="row.sendDate | fullDate"> + Envoyé : <b>{{row.sendDate | timeAgo : 'full'}}</b> + </span> + <span *ngIf="row.status === 'DRAFT'" style="flex:1;color:orange"> + Brouillon + </span> + <span *ngIf="row.sendDate === null" style="flex:1;color:red"> + Non envoyé + </span> + <span style="flex:1" *ngIf="!functions.empty(row.sendBackDate)" [title]="row.creationDate | fullDate"> + Pris en charge : <b>{{row.creationDate | timeAgo : 'full'}}</b> + </span> + </div> + <div style="display: grid;grid-template-columns: 100px 1fr 170px;width: 100%;grid-gap: 10px;align-items: center;"> + <div class="dateType"> + <span class="type"> + <span class="badge" [style.background]="row.typeColor">{{lang[row.type]}}</span> + <div class="attach"> + <i *ngIf="row.hasMainDoc" class="fas fa-file" title="Document attaché"></i> + <i *ngIf="row.hasAttach" class="fas fa-paperclip" title="Pièce(s) jointe(s) attachée(s)"></i> + <i *ngIf="row.hasNote" class="fas fa-pen-square" title="Note(s) attachée(s)"></i> + </div> + </span> + </div> + <div class="desc"> + + {{row.desc}} + + </div> + <div class="contact"> + <span *ngIf="row.sender" style="white-space: pre;overflow: hidden;text-overflow: ellipsis;"> + De : {{row.sender}} + </span> + <span style="white-space: pre;overflow: hidden;text-overflow: ellipsis;"> + Pour : {{row.recipients}} + </span> + </div> + </div> + </mat-cell> + </ng-container> + <mat-row *matRowDef="let row; columns: displayedColumns;" style="cursor: pointer;"></mat-row> + </mat-table> +</ng-template> \ No newline at end of file diff --git a/src/frontend/app/sendedResource/sended-resource-list.component.scss b/src/frontend/app/sendedResource/sended-resource-list.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..282d79f38cfa8aa0b1d387ebe1b02f8fcb6bcf46 --- /dev/null +++ b/src/frontend/app/sendedResource/sended-resource-list.component.scss @@ -0,0 +1,112 @@ +@import '../../css/vars.scss'; + + +.dateType { + width: 100px; + display: flex; + flex-direction: column; + font-size: 10px !important; + justify-content: center; + text-align: center; + + .type { + .badge { + font-size: 10px !important; + color: white; + font-weight: normal; + width: 100px; + } + + } + + .date { + opacity: 0.5; + } +} + +.desc { + font-size: 13px; + flex: 1; + display: flex; + flex-direction: column; +} + +.error { + color: red; +} + +.success { + color: green; +} + +.contact { + font-size: 10px; + flex-direction: column; + display: flex; +} + +.filterTypes { + overflow: auto; + overflow-y: hidden; + width: auto; + max-width: 100%; + margin-left: 10px; + + ::ng-deep.mat-button-toggle-checked { + background: $secondary; + color: white; + font-weight: bold; + height: 25px; + font-size: 10px; + + .mat-button-toggle-label-content { + line-height: 0px; + } + + .mat-button-toggle-button { + height: 100%; + } + } + + :not(::ng-deep.mat-button-toggle-checked) { + ::ng-deep.mat-button-toggle-button { + color: $primary; + font-weight: bold; + height: 25px; + font-size: 10px; + + .mat-button-toggle-label-content { + line-height: 0px; + } + } + } +} + +.subinfo { + padding: 5px; + display: flex; + font-size: 10px; + opacity: 0.5; + width: 100%; +} + +.dataLine { + transition: all 0.5s; + + &:hover { + background: rgba($primary, 0.1); + transition: all 0.5s; + } +} + +.attach { + font-size: 13px; + display: flex; + padding: 5px; + color: $primary; + + i { + flex: 1; + cursor: help; + } +} \ No newline at end of file diff --git a/src/frontend/app/sendedResource/sended-resource-list.component.ts b/src/frontend/app/sendedResource/sended-resource-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..584961041663bf9347384f8491c6c4a7b1efb547 --- /dev/null +++ b/src/frontend/app/sendedResource/sended-resource-list.component.ts @@ -0,0 +1,165 @@ +import { Component, OnInit, ViewChild, EventEmitter, ElementRef, Input } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { LANG } from '../translate.component'; +import { NotificationService } from '../notification.service'; +import { HeaderService } from '../../service/header.service'; +import { Observable, merge, Subject, of as observableOf, of } from 'rxjs'; +import { MatPaginator, MatSort, MatDialog, MatTableDataSource } from '@angular/material'; +import { takeUntil, startWith, switchMap, map, catchError, filter, exhaustMap, tap, debounceTime, distinctUntilChanged, finalize } from 'rxjs/operators'; +import { FormControl } from '@angular/forms'; +import { FunctionsService } from '../../service/functions.service'; +import { LatinisePipe } from 'ngx-pipes'; +import { PrivilegeService } from '../../service/privileges.service'; + +@Component({ + selector: 'app-sended-resource-list', + templateUrl: "sended-resource-list.component.html", + styleUrls: ['sended-resource-list.component.scss'], +}) +export class SendedResourceListComponent implements OnInit { + + lang: any = LANG; + loading: boolean = true; + + filtersChange = new EventEmitter(); + + + dataSource: any; + displayedColumns: string[] = ['creationDate']; + + sendedResources: any[] = []; + + resultsLength = 0; + + typeColor = { + startDate: '#b5cfd8', + endDate: '#7393a7', + actions: '#7d5ba6', + systemActions: '#7d5ba6', + users: '#009dc5', + }; + + currentFilter: string = ''; + filterTypes: any[] = []; + + + @Input('resId') resId: number = null; + + @ViewChild(MatSort, { static: false }) sort: MatSort; + + constructor( + public http: HttpClient, + private notify: NotificationService, + private headerService: HeaderService, + public dialog: MatDialog, + public functions: FunctionsService, + private latinisePipe: LatinisePipe, + public privilegeService: PrivilegeService) { } + + async ngOnInit(): Promise<void> { + this.sendedResources = []; + await this.initAcknowledgementReceipList(); + await this.initEmailList(); + this.initFilter(); + + setTimeout(() => { + this.dataSource = new MatTableDataSource(this.sendedResources); + this.dataSource.sort = this.sort; + }, 0); + + this.loading = false; + + } + + initAcknowledgementReceipList() { + return new Promise((resolve, reject) => { + this.http.get(`../../rest/resources/${this.resId}/acknowledgementReceipts`).pipe( + map((data: any) => { + data = data.map((item: any) => { + return { + id: item.id, + sender : false, + recipients : item.format === 'html' ? item.contact.email : `${item.contact.firstname} ${item.contact.lastname}`, + creationDate : item.creationDate, + sendDate : item.sendDate, + type: 'acknowledgementReceipt', + typeColor: '#7d5ba6', + desc: item.format === 'html' ? this.lang.ARelectronic : this.lang.ARPaper, + status : item.format === 'html' && item.sendDate === null ? 'ERROR' : 'SENT', + hasAttach : false, + hasNote : false, + hasMainDoc : false + } + }) + return data; + }), + tap((data: any) => { + this.sendedResources = this.sendedResources.concat(data); + + resolve(true); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + initEmailList() { + return new Promise((resolve, reject) => { + this.http.get(`../../rest/resources/${this.resId}/emails`).pipe( + map((data: any) => { + data.emails = data.emails.map((item: any) => { + return { + id: item.id, + sender : item.sender.email, + recipients : item.recipients, + creationDate : item.creation_date, + sendDate : item.send_date, + type: 'email', + typeColor: '#5bc0de', + desc: item.object, + status : item.status, + hasAttach : !this.functions.empty(item.document.attachments), + hasNote : !this.functions.empty(item.document.notes), + hasMainDoc : item.document.isLinked + } + }) + return data.emails; + }), + tap((data: any) => { + this.sendedResources = this.sendedResources.concat(data); + + resolve(true); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + initFilter() { + this.sendedResources.forEach((element: any) => { + if (this.filterTypes.filter(type => type.id === element.type).length === 0) { + this.filterTypes.push( { + id: element.type, + label: this.lang[element.type] + }); + } + }); + } + + processPostData(data: any) { + return data; + } + + filterType(ev: any) { + this.currentFilter = ev.value; + this.dataSource.filter = ev.value; + } +} \ No newline at end of file diff --git a/src/frontend/lang/lang-fr.ts b/src/frontend/lang/lang-fr.ts index 02ee216032df84f51f20d7ba90421ebca37e993a..690e57c19c37a3fddf30edd59db83f6962048547 100755 --- a/src/frontend/lang/lang-fr.ts +++ b/src/frontend/lang/lang-fr.ts @@ -1515,7 +1515,7 @@ export const LANG_FR = { "onlyofficeEditDenied": "Le document avec l'extension", "onlyofficeEditDenied2": "ne peut pas être édité avec onlyoffice", "NOTE_version": "Version annotée", - "SIGN_version": "Version original", + "SIGN_version": "Version originale", "versions": "Versions", "itemRemovedFromVisaTemplate": "Les utilisateurs suivants ont été retirés car ils n'ont pas le droit de viser ou signer", "itemRemovedFromAvisTemplate": "Les utilisateurs suivants ont été retirés car ils n'ont pas le droit de donner un avis",