diff --git a/src/frontend/app/actions/actions.service.ts b/src/frontend/app/actions/actions.service.ts index 67dd15744ef46af54198f334488f6548a2ed37bc..8369bb91bf5e76c5af650b47792dbcad1e435935 100644 --- a/src/frontend/app/actions/actions.service.ts +++ b/src/frontend/app/actions/actions.service.ts @@ -28,6 +28,7 @@ import { Router } from '@angular/router'; import { SendSignatureBookActionComponent } from './visa-send-signature-book-action/send-signature-book-action.component'; import { ContinueVisaCircuitActionComponent } from './visa-continue-circuit-action/continue-visa-circuit-action.component'; import { SendAvisWorkflowComponent } from './avis-workflow-send-action/send-avis-workflow-action.component'; +import { ContinueAvisCircuitActionComponent } from './avis-continue-circuit-action/continue-avis-circuit-action.component'; @Injectable() export class ActionsService { @@ -751,4 +752,28 @@ export class ActionsService { }) ).subscribe(); } + + continueOpinionCircuitAction(options: any = null) { + const dialogRef = this.dialog.open(ContinueAvisCircuitActionComponent, { + autoFocus: false, + disableClose: true, + data: this.setDatasActionToSend() + }); + dialogRef.afterClosed().pipe( + tap((data: any) => { + this.unlockResourceAfterActionModal(data); + }), + filter((data: string) => data === 'success'), + tap((result: any) => { + this.endAction(result); + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + } diff --git a/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.html b/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a53d4a660380e6b6435fcae4885855e564df588a --- /dev/null +++ b/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.html @@ -0,0 +1,53 @@ +<h1 mat-dialog-title>{{data.action.label}}</h1> +<div mat-dialog-content> + <div *ngIf="loading" class="loading" style="display:flex;height:100%;"> + <mat-spinner style="margin:auto;"></mat-spinner> + </div> + <mat-sidenav-container autosize style="height:100%;"> + <mat-sidenav-content style="background: white;padding:10px;"> + <div> + {{lang.makeActionOn}} + <b *ngIf="data.resIds.length === 0" color="primary" class="highlight">{{lang.currentIndexingMail}}</b> + <b *ngIf="data.resIds.length == 1" color="primary" class="highlight">{{data.resource.chrono}}</b> + <b *ngIf="data.resIds.length > 1" color="primary" class="highlight">{{data.resIds.length}} + {{lang.elements}}</b> ? + </div> + <div class="alert-message alert-message-info" + *ngIf="data.resIds.length == 1 && appAvisWorkflow !== undefined && appAvisWorkflow.getNextAvisUser() !== ''" + role="alert" style="margin-top: 30px;" + [innerHTML]="lang.sendToDocTo + ' <b>' + appAvisWorkflow.getNextAvisUser().labelToDisplay + '</b>'"> + </div> + + <div class="alert-message alert-message-info" + *ngIf="data.resIds.length == 1 && appAvisWorkflow !== undefined && appAvisWorkflow.getNextAvisUser() === '' && !noResourceToProcess" + role="alert" style="margin-top: 30px;" [innerHTML]="lang.endWorkflow"></div> + + <div *ngIf="resourcesErrors.length > 0" class="alert-message alert-message-danger mailList" role="alert"> + <p> + {{lang.canNotMakeAction}} : + </p> + <ul> + <li *ngFor="let ressource of resourcesErrors"> + <b>{{ressource.alt_identifier}}</b> : {{lang[ressource.reason]}} + </li> + </ul> + </div> + <div *ngIf="resourcesWarnings.length > 0" class="alert-message alert-message-info mailList" role="alert"> + <ul style="margin: 0;padding-bottom: 0px;"> + <li *ngFor="let ressource of resourcesWarnings"> + <b>{{ressource.alt_identifier}}</b> : {{lang[ressource.reason]}} + </li> + </ul> + </div> + <app-note-editor #noteEditor [resIds]="data.resIds"></app-note-editor> + <app-avis-workflow *ngIf="data.resIds.length == 1" [adminMode]="false" [resId]="data.resIds[0]" + #appAvisWorkflow> + </app-avis-workflow> + </mat-sidenav-content> + </mat-sidenav-container> +</div> +<div mat-dialog-actions class="actions"> + <button mat-raised-button mat-button color="primary" [disabled]="loading || !isValidAction()" + (click)="onSubmit()">{{lang.validate}}</button> + <button mat-raised-button mat-button [disabled]="loading" [mat-dialog-close]="">{{lang.cancel}}</button> +</div> \ No newline at end of file diff --git a/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.scss b/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..9424211bb4af9027d454165e7417bf86fb23c6eb --- /dev/null +++ b/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.scss @@ -0,0 +1,128 @@ +@import '../../../css/vars.scss'; + +.fullHeight { + height: 70vh; +} + +.fullWidth { + width: 70vw; +} + +.highlight { + font-size: 110%; +} + +.loading { + display: flex; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #ffffffb3; + z-index: 2; + overflow: hidden; +} + +.mailList { + ul { + font-size: 12px; + max-height: 100px; + overflow: auto; + padding-left: 25px; + padding-right: 5px; + padding-bottom: 10px; + margin-top: 10px; + } + + p { + //font-size: 18px; + margin: 0; + text-decoration: underline; + } + + b { + font-size: 120%; + } +} + +.formType { + align-items: center; + display: flex; + margin: 10px; + border-radius: 4px; + border: solid 1px #ccc; + position: relative; + padding: 10px; + + &-title { + white-space: pre; + overflow: hidden; + max-width: 85%; + text-overflow: ellipsis; + z-index: 1; + font-size: 10px; + font-weight: bold; + background: white; + position: absolute; + top: -7px; + left: 10px; + padding: 0px; + margin: 0px; + color: #135f7f; + } + + ::ng-deep.mat-form-field-suffix { + color: $secondary; + font-size: 15px; + top: 0; + } + + ::ng-deep.mat-form-field-wrapper { + padding: 0; + } +} + +.priceContent { + display: flex; + align-items: center; + justify-content: flex-end; + width: 100%; + + &-label { + text-align: right; + color: $primary; + flex: 1; + justify-content: flex-end; + display: flex; + padding-right: 10px; + } + + .mat-form-field { + width: 90px !important; + + input { + font-weight: bold; + user-select: none; + } + } +} + +.priceInfo { + padding-right: 20px; + font-size: 10px; + opacity: 0.5; + width: 100%; +} + +.pjList { + display: flex; + width: 100%; + overflow: auto; + flex-direction: column; + background: #666; + + img { + margin: 10px; + } +} \ No newline at end of file diff --git a/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.ts b/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..65b6524f5e0a68116d4bbbc8a7704648307211cd --- /dev/null +++ b/src/frontend/app/actions/avis-continue-circuit-action/continue-avis-circuit-action.component.ts @@ -0,0 +1,96 @@ +import { Component, OnInit, Inject, ViewChild } from '@angular/core'; +import { LANG } from '../../translate.component'; +import { NotificationService } from '../../notification.service'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { HttpClient } from '@angular/common/http'; +import { NoteEditorComponent } from '../../notes/note-editor.component'; +import { tap, finalize, catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { FunctionsService } from '../../../service/functions.service'; +import { AvisWorkflowComponent } from '../../avis/avis-workflow.component'; + +@Component({ + templateUrl: "continue-avis-circuit-action.component.html", + styleUrls: ['continue-avis-circuit-action.component.scss'], +}) +export class ContinueAvisCircuitActionComponent implements OnInit { + + lang: any = LANG; + loading: boolean = false; + + resourcesWarnings: any[] = []; + resourcesErrors: any[] = []; + + noResourceToProcess: boolean = null; + + @ViewChild('noteEditor', { static: true }) noteEditor: NoteEditorComponent; + @ViewChild('appAvisWorkflow', { static: false }) appAvisWorkflow: AvisWorkflowComponent; + + constructor( + public http: HttpClient, + private notify: NotificationService, + public dialogRef: MatDialogRef<ContinueAvisCircuitActionComponent>, + @Inject(MAT_DIALOG_DATA) public data: any, + public functions: FunctionsService) { } + + async ngOnInit(): Promise<void> { + this.loading = true; + await this.checkAvisCircuit(); + this.loading = false; + } + + checkAvisCircuit() { + this.resourcesErrors = []; + this.resourcesWarnings = []; + + // TO DO : WAIT BACK + return new Promise((resolve, reject) => { + this.http.post('../../rest/resourcesList/users/' + this.data.userId + '/groups/' + this.data.groupId + '/baskets/' + this.data.basketId + '/actions/' + this.data.action.id + '/checkContinueVisaCircuit', { resources: this.data.resIds }) + .subscribe((data: any) => { + if (!this.functions.empty(data.resourcesInformations.warning)) { + this.resourcesWarnings = data.resourcesInformations.warning; + } + + if(!this.functions.empty(data.resourcesInformations.error)) { + this.resourcesErrors = data.resourcesInformations.error; + this.noResourceToProcess = this.resourcesErrors.length === this.data.resIds.length; + } + resolve(true); + }, (err: any) => { + this.notify.handleSoftErrors(err); + }); + }); + } + + async onSubmit() { + const realResSelected: number[] = this.data.resIds.filter((resId: any) => this.resourcesErrors.map(resErr => resErr.res_id).indexOf(resId) === -1); + this.executeAction(realResSelected); + } + + executeAction(realResSelected: number[]) { + const noteContent: string = `[avis] ${this.noteEditor.getNoteContent()}`; + this.http.put(this.data.processActionRoute, {resources : realResSelected, note : noteContent}).pipe( + tap((data: any) => { + if (!data) { + this.dialogRef.close('success'); + } + if (data && data.errors != null) { + this.notify.error(data.errors); + } + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + isValidAction() { + if (!this.noResourceToProcess && !this.functions.empty(this.noteEditor.getNoteContent())) { + return true; + } else { + return false; + } + } +} diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 3e345ef6fce9760b7fedaf5db5763290fac6b01b..1b57cea8ba85505867c0b3cfd6dcda662eb20710 100755 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -57,10 +57,9 @@ import { RedirectActionComponent } from './actions/redirect-action import { SendShippingActionComponent } from './actions/send-shipping-action/send-shipping-action.component'; import { redirectInitiatorEntityActionComponent } from './actions/redirect-initiator-entity-action/redirect-initiator-entity-action.component'; import { closeMailWithAttachmentsOrNotesActionComponent } from './actions/close-mail-with-attachments-or-notes-action/close-mail-with-attachments-or-notes-action.component'; -import { SendSignatureBookActionComponent } from './actions/send-signature-book-action/send-signature-book-action.component'; -import { ContinueVisaCircuitActionComponent } from './actions/continue-visa-circuit-action/continue-visa-circuit-action.component'; import { SendSignatureBookActionComponent } from './actions/visa-send-signature-book-action/send-signature-book-action.component'; import { ContinueVisaCircuitActionComponent } from './actions/visa-continue-circuit-action/continue-visa-circuit-action.component'; +import { ContinueAvisCircuitActionComponent } from './actions/avis-continue-circuit-action/continue-avis-circuit-action.component'; import { SendAvisWorkflowComponent } from './actions/avis-workflow-send-action/send-avis-workflow-action.component'; import { FiltersListComponent } from './list/filters/filters-list.component'; @@ -156,6 +155,7 @@ import { AddAvisModelModalComponent } from './avis/addAvisModel/add-avis-model-m closeMailWithAttachmentsOrNotesActionComponent, SendSignatureBookActionComponent, ContinueVisaCircuitActionComponent, + ContinueAvisCircuitActionComponent, SendAvisWorkflowComponent, ActionsListComponent, PrintSeparatorComponent, @@ -208,6 +208,7 @@ import { AddAvisModelModalComponent } from './avis/addAvisModel/add-avis-model-m SendExternalSignatoryBookActionComponent, SendExternalNoteBookActionComponent, ContinueVisaCircuitActionComponent, + ContinueAvisCircuitActionComponent, ProcessActionComponent, RedirectActionComponent, SendShippingActionComponent, diff --git a/src/frontend/app/avis/avis-workflow.component.html b/src/frontend/app/avis/avis-workflow.component.html index 9c9f734707ebd0afbd6985d66c62361020e27d0a..f038dd701d563a583e9d646c08e6b130cf88f946 100644 --- a/src/frontend/app/avis/avis-workflow.component.html +++ b/src/frontend/app/avis/avis-workflow.component.html @@ -50,6 +50,8 @@ [cdkDragDisabled]="!adminMode || !functions.empty(diffusion.process_date)" [class.notDraggable]="!adminMode || !functions.empty(diffusion.process_date)" [class.notEditable]="!adminMode" [class.processed]="diffusion.process_date != null"> + <mat-icon *ngIf="getCurrentAvisUserIndex() === i && !adminMode" class="fa fa-chevron-right fa-2x" mat-list-icon color="accent"> + </mat-icon> <mat-icon [ngClass]="{'fa fa-user fa-2x': functions.empty(diffusion.picture),'avatar': !functions.empty(diffusion.picture)}" mat-list-icon color="primary" diff --git a/src/frontend/app/avis/avis-workflow.component.ts b/src/frontend/app/avis/avis-workflow.component.ts index 8cb910893ba53f49e86ef5f80731fa5fc6ef75f5..e5dca9b2dffcb2663e8e554afa0770a8f7dea78b 100644 --- a/src/frontend/app/avis/avis-workflow.component.ts +++ b/src/frontend/app/avis/avis-workflow.component.ts @@ -292,6 +292,37 @@ export class AvisWorkflowComponent implements OnInit { return this.avisWorkflow.items; } + getCurrentAvisUserIndex() { + + const index = this.avisWorkflow.items.map((item: any) => item.listinstance_id).indexOf(this.getLastAvisUser().listinstance_id); + + return (index + 1); + } + + getFirstAvisUser() { + return !this.functions.empty(this.avisWorkflow.items[0]) ? this.avisWorkflow.items[0] : ''; + } + + getCurrentAvisUser() { + + const index = this.avisWorkflow.items.map((item: any) => item.listinstance_id).indexOf(this.getLastAvisUser().listinstance_id); + + return !this.functions.empty(this.avisWorkflow.items[index + 1]) ? this.avisWorkflow.items[index + 1] : ''; + } + + getNextAvisUser() { + + const index = this.avisWorkflow.items.map((item: any) => item.listinstance_id).indexOf(this.getLastAvisUser().listinstance_id); + + return !this.functions.empty(this.avisWorkflow.items[index + 2]) ? this.avisWorkflow.items[index + 2] : ''; + } + + getLastAvisUser() { + let arrOnlyProcess = this.avisWorkflow.items.filter((item: any) => !this.functions.empty(item.process_date)); + + return !this.functions.empty(arrOnlyProcess[arrOnlyProcess.length - 1]) ? arrOnlyProcess[arrOnlyProcess.length - 1] : ''; + } + saveAvisWorkflow(resIds: number[] = [this.resId]) { return new Promise((resolve, reject) => { if (this.avisWorkflow.items.length === 0) { diff --git a/src/frontend/lang/lang-nl.ts b/src/frontend/lang/lang-nl.ts index 9236084fed4369f750b074b3b43fcc1365fb3372..c08f33ac01caab0ca0889ac795c173338fd2b066 100755 --- a/src/frontend/lang/lang-nl.ts +++ b/src/frontend/lang/lang-nl.ts @@ -1440,7 +1440,6 @@ export const LANG_NL = { "endWorkflow": "You will end this workflow", //_TO_TRANSLATE "hasNoAttachmentsNotes": "The following letters have no annotation or attachments", //_TO_TRANSLATE "closeMailAddNoteMandatory": "Some letters have neither annotation nor attachments. Please enter an annotation below, or add an attachment to the letters.", //_TO_TRANSLATE - "endWorkflow": "You will end the workflow", //_TO_TRANSLATE "endedCircuit": "Workflow ended", //_TO_TRANSLATE "userHasntSigned": "The assignee hasn't signed any document", //_TO_TRANSLATE "noCircuitAvailable": "No workflow defined", //_TO_TRANSLATE