From e509307454103223e9f7e707ddb5dc95520f209a Mon Sep 17 00:00:00 2001
From: Alex ORLUC <alex.orluc@maarch.org>
Date: Thu, 27 Feb 2020 22:32:16 +0100
Subject: [PATCH] FEAT #13217 TIME 1:30 link avis / visa component in entities
 admin

---
 ...ffusionModel-administration.component.html |   4 +-
 .../entities-administration.component.html    |  42 +-
 .../entities-administration.component.ts      | 422 ++++++++++--------
 .../app/avis/avis-workflow.component.html     |   4 +-
 .../app/avis/avis-workflow.component.ts       |  51 ++-
 .../diffusions/diffusions-list.component.html |   2 +-
 .../app/visa/visa-workflow.component.html     |   6 +-
 .../app/visa/visa-workflow.component.ts       |  12 +-
 8 files changed, 280 insertions(+), 263 deletions(-)

diff --git a/src/frontend/app/administration/diffusionModel/diffusionModel-administration.component.html b/src/frontend/app/administration/diffusionModel/diffusionModel-administration.component.html
index 486eab5aed5..8192dfb0692 100755
--- a/src/frontend/app/administration/diffusionModel/diffusionModel-administration.component.html
+++ b/src/frontend/app/administration/diffusionModel/diffusionModel-administration.component.html
@@ -51,9 +51,9 @@
             <mat-nav-list>
                 <mat-tab-group>
                     <mat-tab label="{{lang[diffusionModel.type]}}">
-                        <app-visa-workflow *ngIf="diffusionModel.type === 'visaCircuit'" [adminMode]="true"
+                        <app-visa-workflow *ngIf="diffusionModel.type === 'visaCircuit'" [showListModels]="false" [showComment]="false" [adminMode]="true"
                             #appVisaWorkflow></app-visa-workflow>
-                        <app-avis-workflow *ngIf="diffusionModel.type === 'opinionCircuit'" [adminMode]="true"
+                        <app-avis-workflow *ngIf="diffusionModel.type === 'opinionCircuit'" [showListModels]="false" [adminMode]="true"
                             #appAvisWorkflow></app-avis-workflow>
                     </mat-tab>
                 </mat-tab-group>
diff --git a/src/frontend/app/administration/entity/entities-administration.component.html b/src/frontend/app/administration/entity/entities-administration.component.html
index 80b408f9707..75910adc74e 100755
--- a/src/frontend/app/administration/entity/entities-administration.component.html
+++ b/src/frontend/app/administration/entity/entities-administration.component.html
@@ -218,7 +218,7 @@
                             </div>
                             <div class="form-group" *ngIf="currentEntity.entity_id">
                                 <div class="col-md-12 text-center" style="padding:10px;">
-                                    <button mat-raised-button color="primary" (click)="saveDiffList()" [disabled]="appDiffusionsList.hasEmptyDest()">{{lang.save}}</button>
+                                    <button mat-raised-button color="primary" (click)="saveDiffList()" [disabled]="appDiffusionsList.hasEmptyDest() || !appDiffusionsList.isModified()">{{lang.save}}</button>
                                     <button mat-raised-button color="default" (click)="appDiffusionsList.loadListModel(currentEntity.id)" [disabled]="!appDiffusionsList.isModified()">{{lang.cancel}}</button>
                                     <button *ngIf="!appDiffusionsList.isEmptyList() && currentEntity.listTemplate.id" mat-raised-button color="warn" (click)="deleteDiffList()">{{lang.delete}}</button>
                                 </div>
@@ -228,26 +228,12 @@
                     <mat-tab label="{{lang.visaWorkflow}}" *ngIf="!creationMode">
                         <div class="row" style="margin:0px;" id="visaCircuitContent">
                             <div class="col-md-12">
-                                <plugin-autocomplete [labelPlaceholder]="lang.addVisaSignUser" [labelList]="lang.availableUsers" [routeDatas]="['/rest/autocomplete/users/circuit?circuit=visa']" [targetSearchKey]="'idToDisplay'" [subInfoKey]="'descriptionToDisplay'" (triggerEvent)="addElemListModelVisa($event)"></plugin-autocomplete>
-                                <mat-list *ngIf="currentEntity.visaCircuit">
-                                    <span dnd-sortable-container [dropZones]="['boxers-zone']" [sortableData]="currentEntity.visaCircuit.items">
-                                        <mat-list-item disableRipple="true" *ngFor="let template of currentEntity.visaCircuit.items; let i = index" title="{{lang.move}}"
-                                            dnd-sortable [sortableIndex]="i" (onDropSuccess)="updateDiffListVisa(template)">
-                                            <mat-icon color="primary" [class]="template.mode == 'visa' ? 'fa fa-user' : 'fa fa-certificate'" mat-list-icon></mat-icon>
-                                            <p mat-line>{{template.idToDisplay}}
-                                                <small style="opacity:0.5;">{{template.descriptionToDisplay}}</small>
-                                            </p>
-                                            <button matTooltip="{{lang.delete}}" mat-icon-button color="warn" (click)="removeDiffListVisa(template,i)">
-                                                <mat-icon class="fa fa-times"></mat-icon>
-                                            </button>
-                                        </mat-list-item>
-                                    </span>
-                                </mat-list>
+                                <app-visa-workflow [adminMode]="true" [showListModels]="false" [showComment]="false" #appVisaWorkflow></app-visa-workflow>
                             </div>
                             <div class="form-group" *ngIf="currentEntity.entity_id">
                                 <div class="col-md-12 text-center" style="padding:10px;">
-                                    <button mat-raised-button color="primary" (click)="saveDiffListVisa()" [disabled]="!this.visaCircuitModified">{{lang.save}}</button>
-                                    <button mat-raised-button color="default" (click)="loadEntity(this.currentEntity.entity_id)" [disabled]="!this.visaCircuitModified">{{lang.cancel}}</button>
+                                    <button mat-raised-button color="primary" (click)="saveDiffListVisa()" [disabled]="!appVisaWorkflow.isModified()">{{lang.save}}</button>
+                                    <button mat-raised-button color="default" (click)="appVisaWorkflow.loadListModel(currentEntity.id)"  [disabled]="!appVisaWorkflow.isModified()">{{lang.cancel}}</button>
                                 </div>
                             </div>
                         </div>
@@ -255,26 +241,12 @@
                     <mat-tab label="{{lang.avis}}" *ngIf="!creationMode">
                         <div class="row" style="margin:0px;" id="opinionCircuitContent">
                             <div class="col-md-12">
-                                <plugin-autocomplete [labelPlaceholder]="lang.addUser" [labelList]="lang.availableUsers" [routeDatas]="['/rest/autocomplete/users/circuit?circuit=opinion']" [targetSearchKey]="'idToDisplay'" [subInfoKey]="'descriptionToDisplay'" (triggerEvent)="addElemListModelOpinion($event)"></plugin-autocomplete>
-                                <mat-list *ngIf="currentEntity.opinionCircuit">
-                                    <span dnd-sortable-container [dropZones]="['boxers-zone']" [sortableData]="currentEntity.opinionCircuit.items">
-                                        <mat-list-item disableRipple="true" *ngFor="let template of currentEntity.opinionCircuit.items; let i = index" title="{{lang.move}}"
-                                            dnd-sortable [sortableIndex]="i">
-                                            <mat-icon color="primary" [class]="'fa fa-user'" mat-list-icon></mat-icon>
-                                            <p mat-line>{{template.idToDisplay}}
-                                                <small style="opacity:0.5;">{{template.descriptionToDisplay}}</small>
-                                            </p>
-                                            <button matTooltip="{{lang.delete}}" mat-icon-button color="warn" (click)="removeDiffListOpinion(template,i)">
-                                                <mat-icon class="fa fa-times"></mat-icon>
-                                            </button>
-                                        </mat-list-item>
-                                    </span>
-                                </mat-list>
+                                <app-avis-workflow [adminMode]="true" [showListModels]="false" #appAvisWorkflow></app-avis-workflow>
                             </div>
                             <div class="form-group" *ngIf="currentEntity.entity_id">
                                 <div class="col-md-12 text-center" style="padding:10px;">
-                                    <button mat-raised-button color="primary" (click)="saveDiffListOpinion()" [disabled]="!this.opinionCircuitModified">{{lang.save}}</button>
-                                    <button mat-raised-button color="default" (click)="loadEntity(this.currentEntity.entity_id)" [disabled]="!this.opinionCircuitModified">{{lang.cancel}}</button>
+                                    <button mat-raised-button color="primary" (click)="saveDiffListOpinion()" [disabled]="!appAvisWorkflow.isModified()">{{lang.save}}</button>
+                                    <button mat-raised-button color="default" (click)="appAvisWorkflow.loadListModel(currentEntity.id)" [disabled]="!appAvisWorkflow.isModified()">{{lang.cancel}}</button>
                                 </div>
                             </div>
                         </div>
diff --git a/src/frontend/app/administration/entity/entities-administration.component.ts b/src/frontend/app/administration/entity/entities-administration.component.ts
index b1401c9aca5..549d716f9d6 100755
--- a/src/frontend/app/administration/entity/entities-administration.component.ts
+++ b/src/frontend/app/administration/entity/entities-administration.component.ts
@@ -16,6 +16,8 @@ import { tap, catchError, filter, exhaustMap } from 'rxjs/operators';
 import { of } from 'rxjs';
 import { FunctionsService } from '../../../service/functions.service';
 import { ConfirmComponent } from '../../../plugins/modal/confirm.component';
+import { VisaWorkflowComponent } from '../../visa/visa-workflow.component';
+import { AvisWorkflowComponent } from '../../avis/avis-workflow.component';
 
 declare function $j(selector: any): any;
 
@@ -59,6 +61,8 @@ export class EntitiesAdministrationComponent implements OnInit {
     @ViewChild('tableUsers', { static: true }) sortUsers: MatSort;
     @ViewChild('tableTemplates', { static: true }) sortTemplates: MatSort;
     @ViewChild('appDiffusionsList', { static: false }) appDiffusionsList: DiffusionsListComponent;
+    @ViewChild('appVisaWorkflow', { static: false }) appVisaWorkflow: VisaWorkflowComponent;
+    @ViewChild('appAvisWorkflow', { static: false }) appAvisWorkflow: AvisWorkflowComponent;
     applyFilterUsers(filterValue: string) {
         filterValue = filterValue.trim();
         filterValue = filterValue.toLowerCase();
@@ -81,119 +85,157 @@ export class EntitiesAdministrationComponent implements OnInit {
         public functions: FunctionsService
     ) { }
 
-    ngOnInit(): void {
+    async ngOnInit(): Promise<void> {
         this.headerService.setHeader(this.lang.administration + ' ' + this.lang.entities);
         window['MainHeaderComponent'].setSnav(this.sidenavLeft);
         window['MainHeaderComponent'].setSnavRight(null);
 
         this.loading = true;
-        this.http.get("../../rest/entityTypes")
-            .subscribe((data: any) => {
-                this.entityTypeList = data['types'];
-            }, (err: any) => {
-                this.notify.error(err.error.errors);
-            });
-        this.http.get("../../rest/listTemplates/types/entity_id/roles")
-            .subscribe((data: any) => {
-                this.listTemplateRoles = data['roles'];
-            }, (err: any) => {
-                this.notify.error(err.error.errors);
-            });
 
-        this.http.get("../../rest/entities")
-            .subscribe((data: any) => {
-                this.entities = data['entities'];
-                this.loading = false;
-
-                setTimeout(() => {
-                    $j('#jstree').jstree({
-                        "checkbox": {
-                            'deselect_all': true,
-                            "three_state": false //no cascade selection
-                        },
-                        'core': {
-                            force_text: true,
-                            'themes': {
-                                'name': 'proton',
-                                'responsive': true
-                            },
-                            'multiple': false,
-                            'data': this.entities,
-                            "check_callback": function (operation: any, node: any, node_parent: any, node_position: any, more: any) {
-                                if (operation == 'move_node') {
-                                    if (node_parent.id == '#') {
-                                        return false;
-                                    } else if (!node_parent.original.allowed) {
-                                        return false;
-                                    } else {
-                                        return true;
-                                    }
-                                }
-                            }
-                        },
-                        "dnd": {
-                            is_draggable: function (nodes: any) {
-                                var i = 0;
-                                var j = nodes.length;
-                                for (; i < j; i++) {
-                                    if (!nodes[i].original.allowed) {
-                                        return false;
-                                    }
-                                }
+        await this.getEntityTypes();
+        await this.getRoles();
+        await this.getEntities();
+
+        this.loading = false;
+
+        this.initEntitiesTree();
+
+    }
+
+    initEntitiesTree() {
+        setTimeout(() => {
+            $j('#jstree').jstree({
+                "checkbox": {
+                    'deselect_all': true,
+                    "three_state": false //no cascade selection
+                },
+                'core': {
+                    force_text: true,
+                    'themes': {
+                        'name': 'proton',
+                        'responsive': true
+                    },
+                    'multiple': false,
+                    'data': this.entities,
+                    "check_callback": function (operation: any, node: any, node_parent: any, node_position: any, more: any) {
+                        if (operation == 'move_node') {
+                            if (node_parent.id == '#') {
+                                return false;
+                            } else if (!node_parent.original.allowed) {
+                                return false;
+                            } else {
                                 return true;
                             }
-                        },
-                        "plugins": ["checkbox", "search", "dnd", "sort"]
-                    });
-                    $j('#jstree').jstree('select_node', this.entities[0]);
-                    var to: any = false;
-                    $j('#jstree_search').keyup(function () {
-                        if (to) { clearTimeout(to); }
-                        to = setTimeout(function () {
-                            var v = $j('#jstree_search').val();
-                            $j('#jstree').jstree(true).search(v);
-                        }, 250);
-                    });
-                    $j('#jstree')
-                        // listen for event
-                        .on('select_node.jstree', (e: any, data: any) => {
-                            if (this.sidenavRight.opened == false) {
-                                this.sidenavRight.open();
-                            }
-                            if (this.creationMode == true) {
-                                this.currentEntity.parent_entity_id = data.node.id;
-                            } else {
-                                if (this.newEntity == true) {
-                                    this.loadEntity(this.currentEntity.entity_id);
-                                    this.newEntity = false;
-                                } else {
-                                    this.loadEntity(data.node.id);
-                                }
+                        }
+                    }
+                },
+                "dnd": {
+                    is_draggable: function (nodes: any) {
+                        var i = 0;
+                        var j = nodes.length;
+                        for (; i < j; i++) {
+                            if (!nodes[i].original.allowed) {
+                                return false;
                             }
+                        }
+                        return true;
+                    }
+                },
+                "plugins": ["checkbox", "search", "dnd", "sort"]
+            });
+            $j('#jstree').jstree('select_node', this.entities[0]);
+            var to: any = false;
+            $j('#jstree_search').keyup(function () {
+                if (to) { clearTimeout(to); }
+                to = setTimeout(function () {
+                    var v = $j('#jstree_search').val();
+                    $j('#jstree').jstree(true).search(v);
+                }, 250);
+            });
+            $j('#jstree')
+                // listen for event
+                .on('select_node.jstree', (e: any, data: any) => {
+                    if (this.sidenavRight.opened == false) {
+                        this.sidenavRight.open();
+                    }
+                    if (this.creationMode == true) {
+                        this.currentEntity.parent_entity_id = data.node.id;
+                    } else {
+                        if (this.newEntity == true) {
+                            this.loadEntity(this.currentEntity.entity_id);
+                            this.newEntity = false;
+                        } else {
+                            this.loadEntity(data.node.id);
+                        }
+                    }
 
-                        }).on('deselect_node.jstree', (e: any, data: any) => {
+                }).on('deselect_node.jstree', (e: any, data: any) => {
 
-                            this.sidenavRight.close();
+                    this.sidenavRight.close();
 
-                        }).on('move_node.jstree', (e: any, data: any) => {
+                }).on('move_node.jstree', (e: any, data: any) => {
 
 
-                            if (this.currentEntity.parent_entity_id != this.currentEntity.entity_id) {
-                                this.currentEntity.parent_entity_id = data.parent;
-                            }
-                            this.moveEntity();
-                        })
-                        // create the instance
-                        .jstree();
-
-                    $j(document).on('dnd_start.vakata', (e: any, data: any) => {
-                        $j('#jstree').jstree('deselect_all');
-                        $j('#jstree').jstree('select_node', data.data.nodes[0]);
-                    });
-                }, 0);
-            }, () => {
-                location.href = "index.php";
+                    if (this.currentEntity.parent_entity_id != this.currentEntity.entity_id) {
+                        this.currentEntity.parent_entity_id = data.parent;
+                    }
+                    this.moveEntity();
+                })
+                // create the instance
+                .jstree();
+
+            $j(document).on('dnd_start.vakata', (e: any, data: any) => {
+                $j('#jstree').jstree('deselect_all');
+                $j('#jstree').jstree('select_node', data.data.nodes[0]);
             });
+        }, 0);
+    }
+
+    getEntityTypes() {
+        return new Promise((resolve, reject) => {
+            this.http.get(`../../rest/entityTypes`).pipe(
+                tap((data: any) => {
+                    this.entityTypeList = data['types'];
+                    resolve(true);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
+    }
+
+    getRoles() {
+        return new Promise((resolve, reject) => {
+            this.http.get(`../../rest/listTemplates/types/entity_id/roles`).pipe(
+                tap((data: any) => {
+                    this.listTemplateRoles = data['roles'];
+                    resolve(true);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
+       
+    }
+
+    getEntities() {
+        return new Promise((resolve, reject) => {
+            this.http.get(`../../rest/entities`).pipe(
+                tap((data: any) => {
+                    this.entities = data['entities'];
+                    resolve(true);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
+       
     }
 
     loadEntity(entity_id: any) {
@@ -204,6 +246,8 @@ export class EntitiesAdministrationComponent implements OnInit {
                 this.currentEntity = data['entity'];
 
                 this.appDiffusionsList.loadListModel(this.currentEntity.id);
+                this.appVisaWorkflow.loadListModel(this.currentEntity.id);
+                this.appAvisWorkflow.loadListModel(this.currentEntity.id);
 
                 if (this.currentEntity.visaCircuit) {
                     this.idVisaCircuit = this.currentEntity.visaCircuit.id;
@@ -499,7 +543,7 @@ export class EntitiesAdministrationComponent implements OnInit {
                     this.notify.handleSoftErrors(err);
                     return of(false);
                 })
-            ).subscribe()
+            ).subscribe();
         } else {
             this.http.post(`../../rest/listTemplates?admin=true`, newDiffList).pipe(
                 tap((data: any) => {
@@ -511,7 +555,7 @@ export class EntitiesAdministrationComponent implements OnInit {
                     this.notify.handleSoftErrors(err);
                     return of(false);
                 })
-            ).subscribe()
+            ).subscribe();
         }
     }
 
@@ -532,121 +576,111 @@ export class EntitiesAdministrationComponent implements OnInit {
         ).subscribe();
     }
 
-    updateDiffListVisa(template: any): any {
-        this.visaCircuitModified = true;
-        this.currentEntity.visaCircuit.items.forEach((listModel: any, i: number) => {
-            listModel.sequence = i;
-            if (i == (this.currentEntity.visaCircuit.items.length - 1)) {
-                listModel.mode = "sign";
-            } else {
-                listModel.mode = "visa";
-            }
-        });
-    }
-
     saveDiffListVisa() {
-        this.visaCircuitModified = false;
         const newDiffList = {
             "title": this.currentEntity.entity_id,
             "description": this.currentEntity.entity_id,
             "type": "visaCircuit",
             "entityId": this.currentEntity.id,
-            "items": Array()
-        };
-        if (this.idVisaCircuit == null) {
-            this.currentEntity.visaCircuit.items.forEach((listModel: any, i: number) => {
-                listModel.sequence = i;
-                if (i == (this.currentEntity.visaCircuit.items.length - 1)) {
-                    listModel.mode = "sign";
-                } else {
-                    listModel.mode = "visa";
-                }
-                newDiffList.items.push(listModel);
-            });
-            this.http.post("../../rest/listTemplates?admin=true", newDiffList)
-                .subscribe((data: any) => {
-                    this.idVisaCircuit = data.id;
-                    this.notify.success(this.lang.diffusionModelUpdated);
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
-        } else if (this.currentEntity.visaCircuit.items.length > 0) {
-            this.currentEntity.visaCircuit.items.forEach((listModel: any, i: number) => {
-                listModel.sequence = i;
-                if (i == (this.currentEntity.visaCircuit.items.length - 1)) {
-                    listModel.mode = "sign";
-                } else {
-                    listModel.mode = "visa";
+            "items": this.appVisaWorkflow.getWorkflow().map((item: any, index : number) => {
+                return {
+                    "id": item.item_id,
+                    "type": item.item_type,
+                    "mode": item.requested_signature ? 'sign' : 'visa',
+                    "sequence": index
                 }
-                newDiffList.items.push(listModel);
-            });
-            this.http.put("../../rest/listTemplates/" + this.idVisaCircuit, newDiffList)
-                .subscribe(() => {
-                    this.notify.success(this.lang.diffusionModelUpdated);
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
-        } else {
-            this.http.delete("../../rest/listTemplates/" + this.idVisaCircuit)
-                .subscribe(() => {
+            })
+        };
+
+        if (this.functions.empty(newDiffList.items)) {
+            this.http.delete(`../../rest/listTemplates/${this.idVisaCircuit}`).pipe(
+                tap(() => {
                     this.idVisaCircuit = null;
                     this.notify.success(this.lang.diffusionModelDeleted);
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
+                    this.appVisaWorkflow.loadListModel(this.currentEntity.id);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        } else if (!this.functions.empty(this.idVisaCircuit)) {
+            this.http.put(`../../rest/listTemplates/${this.idVisaCircuit}`, newDiffList).pipe(
+                tap(() => {
+                    this.notify.success(this.lang.diffusionModelUpdated);
+                    this.appVisaWorkflow.loadListModel(this.currentEntity.id);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        } else {
+            this.http.post(`../../rest/listTemplates?admin=true`, newDiffList).pipe(
+                tap((data: any) => {
+                    this.idVisaCircuit = data.id;
+                    this.notify.success(this.lang.diffusionModelUpdated);
+                    this.appVisaWorkflow.loadListModel(this.currentEntity.id);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
         }
     }
 
     saveDiffListOpinion() {
-        this.opinionCircuitModified = false;
         const newDiffList = {
             "title": this.currentEntity.entity_id,
             "description": this.currentEntity.entity_id,
             "type": "opinionCircuit",
             "entityId": this.currentEntity.id,
-            "items": Array()
+            "items": this.appAvisWorkflow.getWorkflow().map((item: any, index : number) => {
+                return {
+                    "id": item.item_id,
+                    "type": item.item_type,
+                    "mode": 'avis',
+                    "sequence": index
+                }
+            })
         };
-        if (this.idOpinionCircuit == null) {
-            newDiffList.items = this.currentEntity.opinionCircuit.items;
-            this.http.post("../../rest/listTemplates?admin=true", newDiffList)
-                .subscribe((data: any) => {
-                    this.idOpinionCircuit = data.id;
-                    this.notify.success(this.lang.diffusionModelUpdated);
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
-        } else if (this.currentEntity.opinionCircuit.items.length > 0) {
-            newDiffList.items = this.currentEntity.opinionCircuit.items;
-            this.http.put("../../rest/listTemplates/" + this.idOpinionCircuit, newDiffList)
-                .subscribe(() => {
-                    this.notify.success(this.lang.diffusionModelUpdated);
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
-        } else {
-            this.http.delete("../../rest/listTemplates/" + this.idOpinionCircuit)
-                .subscribe(() => {
+
+        if (this.functions.empty(newDiffList.items)) {
+            this.http.delete(`../../rest/listTemplates/${this.idOpinionCircuit}`).pipe(
+                tap(() => {
                     this.idOpinionCircuit = null;
                     this.notify.success(this.lang.diffusionModelDeleted);
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
-        }
-    }
-
-    removeDiffListVisa(template: any, i: number): any {
-        this.visaCircuitModified = true;
-        this.currentEntity.visaCircuit.items.splice(i, 1);
-
-        if (this.currentEntity.visaCircuit.items.length > 0) {
-            this.currentEntity.visaCircuit.items.forEach((listModel: any, i: number) => {
-                listModel.sequence = i;
-                if (i == (this.currentEntity.visaCircuit.items.length - 1)) {
-                    listModel.mode = "sign";
-                } else {
-                    listModel.mode = "visa";
-                }
-            });
+                    this.appAvisWorkflow.loadListModel(this.currentEntity.id);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        } else if (!this.functions.empty(this.idOpinionCircuit)) {
+            this.http.put(`../../rest/listTemplates/${this.idOpinionCircuit}`, newDiffList).pipe(
+                tap(() => {
+                    this.notify.success(this.lang.diffusionModelUpdated);
+                    this.appAvisWorkflow.loadListModel(this.currentEntity.id);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        } else {
+            this.http.post(`../../rest/listTemplates?admin=true`, newDiffList).pipe(
+                tap((data: any) => {
+                    this.idOpinionCircuit = data.id;
+                    this.notify.success(this.lang.diffusionModelUpdated);
+                    this.appAvisWorkflow.loadListModel(this.currentEntity.id);
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
         }
     }
 
diff --git a/src/frontend/app/avis/avis-workflow.component.html b/src/frontend/app/avis/avis-workflow.component.html
index 76af5f1360e..178f1c601f3 100644
--- a/src/frontend/app/avis/avis-workflow.component.html
+++ b/src/frontend/app/avis/avis-workflow.component.html
@@ -36,7 +36,7 @@
                 </mat-option>
             </mat-optgroup>
         </mat-autocomplete>
-        <button mat-icon-button matSuffix *ngIf="avisWorkflow.items.length > 0" color="primary"
+        <button mat-icon-button matSuffix *ngIf="avisWorkflow.items.length > 0 && showListModels" color="primary"
             (click)="$event.stopPropagation();openPromptSaveModel()">
             <mat-icon class="fa fa-plus"></mat-icon>
         </button>
@@ -46,7 +46,7 @@
         <div class="emptyContent" *ngIf="avisWorkflow.items.length === 0">
             {{lang.noPerson}}
         </div>
-        <mat-list-item *ngFor="let diffusion of avisWorkflow.items;let i=index" cdkDrag class="columns workflow"
+        <mat-list-item disableRipple *ngFor="let diffusion of avisWorkflow.items;let i=index" cdkDrag class="columns workflow"
             [cdkDragDisabled]="!adminMode || !functions.empty(diffusion.process_date)"
             [class.notDraggable]="!adminMode || !functions.empty(diffusion.process_date)"
             [class.notEditable]="!adminMode" [class.processed]="diffusion.process_date != null">
diff --git a/src/frontend/app/avis/avis-workflow.component.ts b/src/frontend/app/avis/avis-workflow.component.ts
index 0d50a4f258a..d5b043b062c 100644
--- a/src/frontend/app/avis/avis-workflow.component.ts
+++ b/src/frontend/app/avis/avis-workflow.component.ts
@@ -45,6 +45,8 @@ export class AvisWorkflowComponent implements OnInit {
     @Input('adminMode') adminMode: boolean;
     @Input('resId') resId: number = null;
 
+    @Input('showListModels') showListModels: boolean = true;
+
     @Input('mode') mode: 'parallel' | 'circuit' = 'circuit';
 
     @ViewChild('searchAvisUserInput', { static: false }) searchAvisUserInput: ElementRef;
@@ -86,7 +88,7 @@ export class AvisWorkflowComponent implements OnInit {
     loadAvisRoles() {
         return new Promise((resolve, reject) => {
             this.http.get(`../../rest/roles`).pipe(
-                tap((data:any) => {
+                tap((data: any) => {
                     this.availableRoles = data.roles.filter((role: any) => ['avis', 'avis_copy', 'avis_info'].indexOf(role.id) > -1).map((role: any) => {
                         return {
                             id: role.id,
@@ -104,7 +106,7 @@ export class AvisWorkflowComponent implements OnInit {
     }
 
     getRoleLabel(id: string) {
-        return this.availableRoles.filter(role => role.id === id) [0].label;
+        return this.availableRoles.filter(role => role.id === id)[0].label;
     }
 
     loadListModel(entityId: number) {
@@ -121,8 +123,9 @@ export class AvisWorkflowComponent implements OnInit {
                             item_entity: item.descriptionToDisplay,
                         }
                     });
-                    this.loading = false;
                 }
+                this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items));
+                this.loading = false;
             });
     }
 
@@ -233,7 +236,9 @@ export class AvisWorkflowComponent implements OnInit {
         if (this.avisModelListNotLoaded) {
             await this.loadAvisUsersList();
 
-            await this.loadAvisModelList();
+            if (this.showListModels) {
+                await this.loadAvisModelList();
+            }
 
             this.searchAvisUser.reset();
 
@@ -300,7 +305,7 @@ export class AvisWorkflowComponent implements OnInit {
                 })
             ).subscribe();
         });
-        
+
     }
 
     loadParallelWorkflow(resId: number) {
@@ -309,22 +314,22 @@ export class AvisWorkflowComponent implements OnInit {
         this.avisWorkflow.items = [];
         return new Promise((resolve, reject) => {
             this.http.get("../../rest/resources/" + resId + "/parallelOpinion")
-            .subscribe((data: any) => {
-                data.forEach((element: any) => {
-                    this.avisWorkflow.items.push(
-                        {
-                            ...element,
-                            difflist_type: 'entity_id'
-                        });
+                .subscribe((data: any) => {
+                    data.forEach((element: any) => {
+                        this.avisWorkflow.items.push(
+                            {
+                                ...element,
+                                difflist_type: 'entity_id'
+                            });
+                    });
+                    this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items));
+                    this.loading = false;
+                    resolve(true);
+                }, (err: any) => {
+                    this.notify.handleErrors(err);
                 });
-                this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items));
-                this.loading = false;
-                resolve(true);
-            }, (err: any) => {
-                this.notify.handleErrors(err);
-            });
         });
-        
+
     }
 
     loadDefaultWorkflow(resId: number) {
@@ -364,7 +369,7 @@ export class AvisWorkflowComponent implements OnInit {
         return this.avisWorkflow.items.length;
     }
 
-    changeRole(role: any, i : number) {
+    changeRole(role: any, i: number) {
         this.avisWorkflow.items[i].item_mode = role.id;
     }
 
@@ -417,7 +422,7 @@ export class AvisWorkflowComponent implements OnInit {
                         return of(false);
                     })
                 ).subscribe();
-            } else {     
+            } else {
                 const arrAvis = resIds.map(resId => {
                     return {
                         resId: resId,
@@ -474,8 +479,8 @@ export class AvisWorkflowComponent implements OnInit {
                         resolve(true);
                     })
                 ).subscribe();
-            } 
-        });        
+            }
+        });
     }
 
     resetWorkflow() {
diff --git a/src/frontend/app/diffusions/diffusions-list.component.html b/src/frontend/app/diffusions/diffusions-list.component.html
index f712fff85ba..4a8d0536d23 100644
--- a/src/frontend/app/diffusions/diffusions-list.component.html
+++ b/src/frontend/app/diffusions/diffusions-list.component.html
@@ -16,7 +16,7 @@
                 </mat-panel-title>
             </mat-expansion-panel-header>
             <mat-list>
-                <mat-list-item class="item-diffusion-list"
+                <mat-list-item disableRipple class="item-diffusion-list"
                     *ngFor="let diffusion of diffList[role.id].items;let i=index">
                     <mat-icon mat-list-icon color="primary" class="fa"
                         [class.fa-user]="diffusion.item_type === 'user_id' || diffusion.item_type === 'user'"
diff --git a/src/frontend/app/visa/visa-workflow.component.html b/src/frontend/app/visa/visa-workflow.component.html
index 95475d501b3..f5aec0cda25 100644
--- a/src/frontend/app/visa/visa-workflow.component.html
+++ b/src/frontend/app/visa/visa-workflow.component.html
@@ -36,7 +36,7 @@
                 </mat-option>
             </mat-optgroup>
         </mat-autocomplete>
-        <button mat-icon-button matSuffix *ngIf="visaWorkflow.items.length > 0" color="primary"
+        <button mat-icon-button matSuffix *ngIf="visaWorkflow.items.length > 0 && showListModels" color="primary"
             (click)="$event.stopPropagation();openPromptSaveModel()">
             <mat-icon class="fa fa-plus"></mat-icon>
         </button>
@@ -50,7 +50,7 @@
         <div class="emptyContent" *ngIf="visaWorkflow.items.length === 0">
             {{lang.noPerson}}
         </div>
-        <mat-list-item *ngFor="let diffusion of visaWorkflow.items;let i=index" cdkDrag class="columns workflow"
+        <mat-list-item disableRipple *ngFor="let diffusion of visaWorkflow.items;let i=index" cdkDrag class="columns workflow"
             [cdkDragDisabled]="!adminMode || !functions.empty(diffusion.process_date)"
             [class.notDraggable]="!adminMode || !functions.empty(diffusion.process_date)"
             [class.notEditable]="!adminMode"
@@ -85,7 +85,7 @@
                     <div class="workflowLineSubLabel">
                         {{diffusion.item_entity}}
                     </div>
-                    <div class="workflowLineSubLabel" *ngIf="(!linkedToMaarchParapheur && adminMode && functions.empty(diffusion.process_date)) || (!linkedToMaarchParapheur && getCurrentVisaUserIndex() === i && !functions.empty(diffusion.process_comment))">
+                    <div class="workflowLineSubLabel" *ngIf="showComment && ((!linkedToMaarchParapheur && adminMode && functions.empty(diffusion.process_date)) || (!linkedToMaarchParapheur && getCurrentVisaUserIndex() === i && !functions.empty(diffusion.process_comment)))">
                         <mat-form-field>
                             <input matInput class="comment" [disabled]="!adminMode || diffusion.process_date != null" [placeholder]="lang.visaNote" [(ngModel)]="diffusion.process_comment">
                         </mat-form-field>
diff --git a/src/frontend/app/visa/visa-workflow.component.ts b/src/frontend/app/visa/visa-workflow.component.ts
index fd74dcdcdd2..30231442e9b 100644
--- a/src/frontend/app/visa/visa-workflow.component.ts
+++ b/src/frontend/app/visa/visa-workflow.component.ts
@@ -43,6 +43,9 @@ export class VisaWorkflowComponent implements OnInit {
     @Input('adminMode') adminMode: boolean;
     @Input('resId') resId: number = null;
 
+    @Input('showListModels') showListModels: boolean = true;
+    @Input('showComment') showComment: boolean = true;
+
     @Input('linkedToMaarchParapheur') linkedToMaarchParapheur: boolean = false;
 
     @ViewChild('searchVisaSignUserInput', { static: false }) searchVisaSignUserInput: ElementRef;
@@ -95,13 +98,14 @@ export class VisaWorkflowComponent implements OnInit {
                             requested_signature: item.item_mode !== 'visa'
                         }
                     });
-                    this.loading = false;
                 }
                 this.visaWorkflow.items.forEach((element: any, key: number) => {
                     if (!this.functions.empty(element['externalId'])) {
                         this.getMaarchParapheurUserAvatar(element.externalId.maarchParapheur, key);
                     }
                 });
+                this.visaWorkflowClone = JSON.parse(JSON.stringify(this.visaWorkflow.items));
+                this.loading = false;
                 resolve(true);
             });
         });
@@ -215,7 +219,9 @@ export class VisaWorkflowComponent implements OnInit {
             if (this.visaModelListNotLoaded) {
                 await this.loadVisaSignUsersList();
 
-                await this.loadVisaModelList();
+                if (this.showListModels) {
+                    await this.loadVisaModelList();
+                }
                 
                 this.searchVisaSignUser.reset();
 
@@ -569,7 +575,7 @@ export class VisaWorkflowComponent implements OnInit {
         }
     }
 
-    isModified() {
+    isModified() {        
         return !(this.loading || JSON.stringify(this.visaWorkflow.items) === JSON.stringify(this.visaWorkflowClone));
     }
 }
-- 
GitLab