From 5e3a5bb4933f6516adc5dc457e3791e4d8805de4 Mon Sep 17 00:00:00 2001
From: "florian.azizian" <florian.azizian@maarch.org>
Date: Sun, 16 Feb 2020 23:47:54 +0100
Subject: [PATCH] FEAT #12982 TIME 1:30 signatureBook available + fix note +
 fix create new version +  redirect to a contact

---
 modules/attachments/js/functions.js           | 128 -----------
 modules/visa/css/module.css                   |  85 +------
 src/app/note/models/NoteModel.php             |   5 +-
 .../controllers/ResourceListController.php    |  18 +-
 .../controllers/AutoCompleteController.php    |   6 +-
 ...ministration-redirect-modal.component.html |  12 +-
 .../attachment-page.component.html            |   2 +-
 .../app/notes/notes-list.component.html       |   2 +-
 src/frontend/app/notes/notes.component.ts     |   9 +-
 .../app/signature-book.component.html         |  25 +-
 .../app/signature-book.component.scss         | 213 ++++++++----------
 src/frontend/app/signature-book.component.ts  | 115 +++++-----
 src/frontend/lang/lang-en.ts                  |   5 +
 src/frontend/lang/lang-fr.ts                  |   5 +
 src/frontend/lang/lang-nl.ts                  |   5 +
 15 files changed, 206 insertions(+), 429 deletions(-)

diff --git a/modules/attachments/js/functions.js b/modules/attachments/js/functions.js
index 1805388ee30..2f1c9696fad 100755
--- a/modules/attachments/js/functions.js
+++ b/modules/attachments/js/functions.js
@@ -1,131 +1,3 @@
-
-function showAttachmentsForm(path, width, height) {
-    if(typeof(width)==='undefined'){
-        width = '800';
-    }
-
-    if(typeof(height)==='undefined'){
-        height = '480';
-    }
-    new Ajax.Request(path,
-    {
-        method:'post',
-        parameters: {
-            url : path
-        },
-        onSuccess: function(answer) {
-            eval("response = "+answer.responseText);
-
-            if(response.status == 0){
-                var modal_content = convertToTextVisibleNewLine(response.content);
-                createModalinAttachmentList(modal_content, 'form_attachments', height, width, 'fullscreen');
-                eval(response.exec_js);
-            } else {
-                window.top.$('main_error').innerHTML = response.error;
-            }
-        }
-    });
-}
-
-function modifyAttachmentsForm(path, width, height) {
-    if(typeof(width)==='undefined'){
-        width = '800';
-    }
-
-    if(typeof(height)==='undefined'){
-        height = '480';
-    }
-
-    new Ajax.Request(path,
-    {
-        method:'post',
-        parameters: { url : path
-                    },  
-        onSuccess: function(answer) {
-            eval("response = "+answer.responseText);
-            if(response.status == 0){
-                var modal_content = convertToTextVisibleNewLine(response.content);
-                createModalinAttachmentList(modal_content, 'form_attachments', height, width, 'fullscreen'); 
-                eval(response.exec_js);
-            } else {
-                alert(response.error);
-            }
-        }
-    });
-}
-
-function createModalinAttachmentList(txt, id_mod, height, width, mode_frm){
-    // FIX IE 11
-    if($j('#leftPanelShowDocumentIframe')){
-       $j('#leftPanelShowDocumentIframe').hide(); 
-    }
-    if($j('#rightPanelShowDocumentIframe')){
-       $j('#rightPanelShowDocumentIframe').hide(); 
-    }
-    if(height == undefined || height=='') {
-        height = '100px';
-    }
-
-    if(width == undefined || width=='') {
-        width = '400px';
-    }
-
-    if( mode_frm == 'fullscreen') {
-        width = (screen.availWidth)+'px';
-        height = (screen.availHeight)+'px';
-    }
-
-    if(id_mod && id_mod!='') {
-        id_layer = id_mod+'_layer';
-    } else {
-        id_mod = 'modal';
-        id_layer = 'lb1-layer';
-    }
-    var tmp_width = width;
-    var tmp_height = height;
-    
-    var layer_height = document.body.clientHeight;
-    var layer_width = window.top.window.document.getElementsByTagName('html')[0].offsetWidth - 5;
-
-    
-    var layer = new Element('div', {'id':id_layer, 'class' : 'lb1-layer', 'style' : "display:block;filter:alpha(opacity=70);opacity:.70;z-index:"+get_z_indexes()['layer']+';width :'+ (layer_width)+"px;height:"+layer_height+'px;'});
-
-    if( mode_frm == 'fullscreen') {
-        var fenetre = new Element('div', {'id' :id_mod,'class' : 'modal', 'style' :'top:0px;left:0px;width:'+width+';height:'+height+";z-index:"+get_z_indexes()['modal']+";position:absolute;" });
-    } else {
-        var fenetre = new Element('div', {'id' :id_mod,'class' : 'modal', 'style' :'top:0px;left:0px;'+'width:'+width+';height:'+height+";z-index:"+get_z_indexes()['modal']+";margin-top:0px;margin-left:0px;position:absolute;" });
-    }
-
-    Element.insert(window.top.window.document.body,layer);
-    Element.insert(window.top.window.document.body,fenetre);
-
-    if( mode_frm == 'fullscreen') {
-        navName = BrowserDetect.browser;
-        if (navName == 'Explorer') {
-            if (width == '1080px') {
-                fenetre.style.width = (window.top.window.document.getElementsByTagName('html')[0].offsetWidth - 55)+"px";
-            }
-        } else {
-            //fenetre.style.width = (window.top.window.document.getElementsByTagName('html')[0].offsetWidth - 30)+"px";
-            fenetre.style.width = "98%";
-        }
-        //fenetre.style.height = (window.top.window.document.getElementsByTagName('body')[0].offsetHeight - 20)+"px";
-        fenetre.style.height = "95%";
-    }
-
-    Element.update(fenetre,txt);
-    
-    
-    Event.observe(layer, 'mousewheel', function(event){Event.stop(event);}.bindAsEventListener(), true);
-    Event.observe(layer, 'DOMMouseScroll', function(event){Event.stop(event);}.bindAsEventListener(), false);
-    
-    $j('html',window.top.window.document).scrollTop(0);
-    $j('body',window.top.window.document).scrollTop(0);
-    
-    $j('body',window.top.window.document).css("overflow","hidden");
-    window.top.window.$(id_mod).focus();
-}
-
 function cleanTitle(str) {
     //permet de supprimer les # dans le titre qui bloque l'ouverture de l'applet java
     var res = str.replace(/#/g, " ");
diff --git a/modules/visa/css/module.css b/modules/visa/css/module.css
index 8afdc315ac2..3acaadf9dd0 100755
--- a/modules/visa/css/module.css
+++ b/modules/visa/css/module.css
@@ -12,7 +12,6 @@
 }
 
 #visa_left{
-	/*border:1px solid blue;*/
 	height:95%;
 	width:41%;
 	margin-left:1%;
@@ -27,7 +26,6 @@
 }
 
 #visa_right{
-	/*border:1px solid green;*/
 	height:95%;
 	width:41%;
 	margin-left:1%;
@@ -87,14 +85,9 @@
 
 .listDocsBasket div{
 	width:100%;
-	/*height:15%;*/
 	border-bottom: thin solid black;
 }
 
-.unselectedId{
-	background-color:#F2F2F2;
-}
-
 .selectedId{
 	background-color:#F99830;
 }
@@ -346,11 +339,6 @@
     border-right: solid 1px;
     vertical-align: top;
     padding-left: 10px;
-    /*width:39%;*/
-}
-
-.contentRight{
-    /*width:39%;*/
 }
 
 .contentLeft, .contentRight{
@@ -418,7 +406,7 @@
     margin-bottom: 5px;
 }
 
-.pjDetails,.pjSign,.pjCreate{
+.pjDetails,.pjSign{
     background-color:white;
     display: block;
     width: 97%;
@@ -435,7 +423,7 @@
     height:100px;
 }
 
-.pjSign,.pjCreate{
+.pjSign{
     cursor: pointer;
 }
 
@@ -454,7 +442,6 @@
     background: #135F7F;
     color: white;
     padding:10px;
-    /*border-radius: 25px;*/
     -moz-box-shadow: 0px 0px 10px 0px #656565;
     -webkit-box-shadow: 0px 0px 10px 0px #656565;
     -o-box-shadow: 0px 0px 10px 0px #656565;
@@ -462,29 +449,6 @@
     filter: progid:DXImageTransform.Microsoft.Shadow(color=#656565,Direction=NaN,Strength=10);
 }
 
-.pjCreate{
-    height: auto;
-    white-space: initial;
-}
-
-.pjCreate input,.pjCreate select{    
-    width:80%;
-}
-
-.pjCreateFile{
-    border: dashed 12px #ccc;
-    height: 60%;
-    border-radius: 5px;
-    margin-top: 10px;
-}
-
-.pjCreateFile i{
-    font-size: 100px;
-    opacity: 0.5;
-    margin-top: 20%;
-    cursor: pointer;
-}
-
 .pjDetails div{
     white-space: initial;
 }
@@ -563,7 +527,6 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 }
 
 #labelSignatureBook div{
-    /*width: 100%;*/
     overflow: hidden;
     text-overflow: ellipsis;
     display: block;
@@ -592,32 +555,6 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     color:#666;
     background-color: #F2F2F2;
 }
-/*.titleSignatureBook span{
-    display: inline-block;
-    vertical-align: middle;
-    padding:10px;
-    width: 90%;
-    overflow: hidden;
-    text-overflow: ellipsis;
-
-}
-
-.titleSignatureBook span:nth-child(2){
-    position: absolute;
-    right: 0px;
-    text-align: right;
-    padding-left: 5px;
-    top: -4px;
-}*/
-
-.headerSignatureBook{
-    border: solid 1px black;
-    display: table;
-    margin-bottom: 10px;
-    white-space: nowrap;
-    text-align: center;
-    width: 100%;
-}
 
 .contentSignatureBook{
     border: solid 1px black;
@@ -630,24 +567,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     margin-top: 5px;
 }
 
-.headerSignatureBook .item{
-    border-right: solid 1px black;
-    display: table-cell;
-    padding: 5px;
-    cursor: pointer;
-    width: 5%;
-    vertical-align: middle;
-    background-color: #F2F2F2;
-}
-
-.headerSignatureBook .activeTabSignatureBook i{
-    display: table-cell;
-    vertical-align: middle;
-}
-
 #tabSignatureBook .item.activeTabSignatureBook{
-    /*background: #135F7F;
-    color:white;*/
     -moz-box-shadow: inset 0px 0px 5px 0px #656565;
     -webkit-box-shadow: inset 0px 0px 5px 0px #656565;
     -o-box-shadow: inset 0px 0px 5px 0px #656565;
@@ -768,7 +688,6 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 .ng-table th.filter .input{
     margin:0;display:block;
     width:auto;
-    /*min-height:30px;*/
     font-size: 9px;
     box-sizing:border-box;
 }
diff --git a/src/app/note/models/NoteModel.php b/src/app/note/models/NoteModel.php
index 71af2262d54..154ae72d9c3 100755
--- a/src/app/note/models/NoteModel.php
+++ b/src/app/note/models/NoteModel.php
@@ -210,10 +210,7 @@ class NoteModel
         $user = UserModel::getById(['select' => ['user_id'], 'id' => $aArgs['userId']]);
         $rawUserEntities = EntityModel::getByLogin(['login' => $user['user_id'], 'select' => ['entity_id']]);
 
-        $userEntities = [];
-        foreach ($rawUserEntities as $rawUserEntity) {
-            $userEntities[] = $rawUserEntity['entity_id'];
-        }
+        $userEntities = array_column($rawUserEntities, 'entity_id');
 
         $allNotes = NoteModel::get([
             'select'    => $aArgs['select'],
diff --git a/src/app/resource/controllers/ResourceListController.php b/src/app/resource/controllers/ResourceListController.php
index a80acb38550..0bad972e988 100644
--- a/src/app/resource/controllers/ResourceListController.php
+++ b/src/app/resource/controllers/ResourceListController.php
@@ -405,10 +405,8 @@ class ResourceListController
             'where'     => [$whereClause, 'res_view_letterbox.res_id in (?)'],
             'data'      => [$body['resources']]
         ]);
-        $resourcesInBasket = [];
-        foreach ($resources as $resource) {
-            $resourcesInBasket[] = $resource['res_id'];
-        }
+
+        $resourcesInBasket = array_column($resources, 'res_id');
 
         if (!empty(array_diff($body['resources'], $resourcesInBasket))) {
             return $response->withStatus(403)->withJson(['errors' => 'Resources out of perimeter']);
@@ -511,10 +509,8 @@ class ResourceListController
             'where'     => [$whereClause, 'res_view_letterbox.res_id in (?)'],
             'data'      => [$body['resources']]
         ]);
-        $resourcesInBasket = [];
-        foreach ($resources as $resource) {
-            $resourcesInBasket[] = $resource['res_id'];
-        }
+
+        $resourcesInBasket = array_column($resources, 'res_id');
 
         if (!empty(array_diff($body['resources'], $resourcesInBasket))) {
             return $response->withStatus(403)->withJson(['errors' => 'Resources out of perimeter']);
@@ -583,10 +579,8 @@ class ResourceListController
             'where'     => [$whereClause, 'res_view_letterbox.res_id in (?)'],
             'data'      => [$body['resources']]
         ]);
-        $resourcesInBasket = [];
-        foreach ($resources as $resource) {
-            $resourcesInBasket[] = $resource['res_id'];
-        }
+
+        $resourcesInBasket = array_column($resources, 'res_id');
 
         if (!empty(array_diff($body['resources'], $resourcesInBasket))) {
             return $response->withStatus(403)->withJson(['errors' => 'Resources out of perimeter']);
diff --git a/src/core/controllers/AutoCompleteController.php b/src/core/controllers/AutoCompleteController.php
index a9ac798642c..676a1e6d457 100755
--- a/src/core/controllers/AutoCompleteController.php
+++ b/src/core/controllers/AutoCompleteController.php
@@ -326,9 +326,9 @@ class AutoCompleteController
         $total = count($autocompleteContacts) + count($autocompleteUsers) + count($autocompleteEntities) + count($autocompleteContactsGroups);
         if ($total > self::TINY_LIMIT) {
             $divider = $total / self::TINY_LIMIT;
-            $autocompleteContacts = array_slice($autocompleteContacts, 0, round(count($autocompleteContacts) / $divider));
-            $autocompleteUsers = array_slice($autocompleteUsers, 0, round(count($autocompleteUsers) / $divider));
-            $autocompleteEntities = array_slice($autocompleteEntities, 0, round(count($autocompleteEntities) / $divider));
+            $autocompleteContacts       = array_slice($autocompleteContacts, 0, round(count($autocompleteContacts) / $divider));
+            $autocompleteUsers          = array_slice($autocompleteUsers, 0, round(count($autocompleteUsers) / $divider));
+            $autocompleteEntities       = array_slice($autocompleteEntities, 0, round(count($autocompleteEntities) / $divider));
             $autocompleteContactsGroups = array_slice($autocompleteContactsGroups, 0, round(count($autocompleteContactsGroups) / $divider));
         }
         $autocompleteData = array_merge($autocompleteContacts, $autocompleteUsers, $autocompleteEntities, $autocompleteContactsGroups);
diff --git a/src/frontend/app/administration/contact/list/contacts-list-administration-redirect-modal.component.html b/src/frontend/app/administration/contact/list/contacts-list-administration-redirect-modal.component.html
index 815393826dd..90b4cd45af7 100644
--- a/src/frontend/app/administration/contact/list/contacts-list-administration-redirect-modal.component.html
+++ b/src/frontend/app/administration/contact/list/contacts-list-administration-redirect-modal.component.html
@@ -3,7 +3,7 @@
     <form #redirectConfForm="ngForm">
         <div class="modal-body">
             <div class="alert alert-warning" role="alert">
-                Ce contact est lié à des courriers
+                {{lang.contactLinkedToMails}}
             </div>
             <div class="input-group">
                 <mat-radio-group id="processMode" name="processMode" style="display: inline-flex;flex-direction: column;" [(ngModel)]="this.processMode"
@@ -12,14 +12,14 @@
                         {{lang.delete}}
                     </mat-radio-button>
                     <mat-radio-button value="reaffect" color="primary">
-                        {{lang.reaffectUserRedirect}}
+                        {{lang.reaffectContactRedirect}}
                     </mat-radio-button>
                 </mat-radio-group>
-                <plugin-autocomplete *ngIf="this.processMode == 'reaffect'" [labelPlaceholder]="lang.userReplacement" [labelList]="lang.availableUsers" [routeDatas]="['/rest/autocomplete/correspondents?noUsers=true&noEntities=true&noContactsGroups=true']" [targetSearchKey]="'firstname'" (triggerEvent)="setRedirectUser($event)" singleMode required></plugin-autocomplete>
+                <plugin-autocomplete *ngIf="this.processMode == 'reaffect'" [labelPlaceholder]="lang.contactReplacement" [labelList]="lang.availableContacts" [routeDatas]="['/rest/autocomplete/correspondents?noUsers=true&noEntities=true&noContactsGroups=true']" [targetSearchKey]="'firstname'" (triggerEvent)="setRedirectUser($event)" singleMode required></plugin-autocomplete>
 
-                <div class="alert-message alert-message-info" role="alert" style="max-width: inherit;">
-                    <span *ngIf="this.processMode == 'delete'" [innerHTML]="'Supprimer'"></span>
-                    <span *ngIf="this.processMode == 'reaffect'"><em>Réaffecter</em></span>
+                <div *ngIf="this.processMode" class="alert-message alert-message-info" role="alert" style="max-width: inherit;">
+                    <span *ngIf="this.processMode == 'delete'"><em>{{lang.delete}}</em></span>
+                    <span *ngIf="this.processMode == 'reaffect'"><em>{{lang.reaffect}}</em></span>
                 </div>
             </div>
         </div>
diff --git a/src/frontend/app/attachments/attachments-page/attachment-page.component.html b/src/frontend/app/attachments/attachments-page/attachment-page.component.html
index 5b5046c9803..a6d7566e0a7 100644
--- a/src/frontend/app/attachments/attachments-page/attachment-page.component.html
+++ b/src/frontend/app/attachments/attachments-page/attachment-page.component.html
@@ -224,7 +224,7 @@
         <button mat-raised-button color="primary" *ngIf="!newVersion" (click)="updateAttachment()"
             [disabled]="!loading && (!editMode || !attachFormGroup.valid)">{{lang.validate}}</button>
         <button mat-raised-button color="primary" *ngIf="newVersion" (click)="createNewVersion()"
-            [disabled]="!editMode || !appAttachmentViewer.isEditingTemplate()">{{lang.createNewVersion}}</button>
+            [disabled]="!editMode || appAttachmentViewer.isEditingTemplate()">{{lang.createNewVersion}}</button>
         <button mat-raised-button color="warn" *ngIf=" !loading && attachment.status.value === 'SIGN' && (privilegeService.hasCurrentUserPrivilege('sign_document') || this.headerService.user.id == attachment.typist.value)"
             (click)="deleteSignedVersion()">{{lang.deleteSignedVersion}}</button>
         <button mat-raised-button mat-button [disabled]="loading" [mat-dialog-close]="">{{lang.close}}</button>
diff --git a/src/frontend/app/notes/notes-list.component.html b/src/frontend/app/notes/notes-list.component.html
index 21e2a0062dd..fe2050cc0e8 100644
--- a/src/frontend/app/notes/notes-list.component.html
+++ b/src/frontend/app/notes/notes-list.component.html
@@ -2,10 +2,10 @@
     <mat-spinner style="margin:auto;"></mat-spinner>
 </div>
 <ng-container *ngIf="!loading">
+    <app-note-editor *ngIf="editMode" #noteEditor [addMode]="true" [resIds]="resIds" (refreshNotes)="loadNotes($event)" style="margin: 20px;display: flex;flex-direction: column;"></app-note-editor>
     <div *ngIf="notes.length == 0" style="text-align:center;font-size:24px;font-weight:bold;opacity:0.3;">
         {{lang.noNote}}
     </div>
-    <app-note-editor *ngIf="editMode" #noteEditor [addMode]="true" [resIds]="resIds" (refreshNotes)="loadNotes($event)" style="margin: 20px;display: flex;flex-direction: column;"></app-note-editor>
     <mat-card *ngFor="let note of notes" style="margin-top: 10px;" [ngStyle]="{'background-color': note.entities_restriction ? 'rgba(255, 165, 0, 0.2)' : 'white'}">
         <mat-card-header>
             <div mat-card-avatar><i color="primary" class="fa fa-user"></i></div>
diff --git a/src/frontend/app/notes/notes.component.ts b/src/frontend/app/notes/notes.component.ts
index ba92bfb2514..3ff6c57b5a7 100644
--- a/src/frontend/app/notes/notes.component.ts
+++ b/src/frontend/app/notes/notes.component.ts
@@ -7,6 +7,7 @@ import { of } from 'rxjs';
 import { HeaderService } from '../../service/header.service';
 import { ConfirmComponent } from '../../plugins/modal/confirm.component';
 import { MatDialogRef, MatDialog } from '@angular/material';
+import { FunctionsService } from '../../service/functions.service';
 
 @Component({
     selector: 'app-notes-list',
@@ -34,7 +35,8 @@ export class NotesListComponent implements OnInit {
         public http: HttpClient,
         private notify: NotificationService,
         private headerService: HeaderService,
-        public dialog: MatDialog
+        public dialog: MatDialog,
+        public functions: FunctionsService
     ) { }
 
     ngOnInit(): void {
@@ -65,7 +67,10 @@ export class NotesListComponent implements OnInit {
     }
 
     getRestrictionEntitiesId(entities: any) {
-        return entities.map((entity: any) => entity.item_id[0]);
+        if (!this.functions.empty(entities)) {
+            return entities.map((entity: any) => entity.item_id[0]);
+        }
+        return [];
     }
 
     removeNote(note: any) {
diff --git a/src/frontend/app/signature-book.component.html b/src/frontend/app/signature-book.component.html
index 12bfc5e9c9d..1798b007340 100755
--- a/src/frontend/app/signature-book.component.html
+++ b/src/frontend/app/signature-book.component.html
@@ -1,18 +1,18 @@
 <div *ngIf="loading" class='visaContent'>
     <i class="fa fa-spinner fa-spin fa-5x" style="margin-left: 50%;margin-top: 16%;font-size: 8em"></i>
 </div>
-<div *ngIf="!loading" class='visaContent'>
+<div *ngIf="!loading" class='visaContent' style="margin-top: -64px;">
     <div class="titleSignatureBook">
         <div id="tabSignatureBook">
-            <div *ngIf="signatureBook.documents[0] && signatureBook.documents[0].category_id != 'outgoing'" title="{{lang.mail}}" class="item" [ngClass]="{'activeTabSignatureBook': headerTab == 'document'}" (click)="changeSignatureBookLeftContent(1)">
+            <div *ngIf="signatureBook.documents[0] && signatureBook.documents[0].category_id != 'outgoing'" title="{{lang.mail}}" class="item" [ngClass]="{'activeTabSignatureBook': headerTab == 'document'}" (click)="changeSignatureBookLeftContent('document')">
                 <i class="fa fa-file-alt fa-2x"></i>
             </div>
             <div *ngFor="let module of processTool" title="{{module.label}}" class="item" [ngClass]="{'activeTabSignatureBook': headerTab == module.id}" (click)="changeSignatureBookLeftContent(module.id)">
-                <i [class]="module.icon">
-                    <sup *ngIf="module.count > 0" class="nbRes" style="font-size: 12px;font-weight: bold;">{{module.count}}</sup>
-                </i>
+                <i [class]="module.icon"></i>
+                <i *ngIf="module.count > 0" class="fas fa-circle haveContent"></i>
             </div>
         </div>
+        <div id="labelSignatureBook" title="{{signatureBook.documents[0].title}}" ><div>{{signatureBook.documents[0].alt_id}} : {{signatureBook.documents[0].title}}</div></div>
         <div id="closeSignatureBook">
             <i style="cursor: pointer" (click)="backToBasket()" class="fa fa-times-circle fa-2x"></i>
         </div>
@@ -21,9 +21,9 @@
             <select id="signatureBookActions">
                 <option *ngFor="let option of signatureBook.actions" value="{{option.id}}">{{option.label}}</option>
             </select>
-            <input name="send" id="send" value="{{lang.validate}}" class="button" type="button" (click)="validForm()">
+            <input name="send" id="send" value="{{lang.validate}}" class="button button-form-primary-filled" type="button" (click)="validForm()">
         </div>
-        <div class="others" *ngIf="signatureBook.consigne != ''">
+        <div class="others" *ngIf="!functions.empty(signatureBook.consigne)">
             <span id="consigne">
                 <input type="text" value="{{signatureBook.consigne}}" title="{{signatureBook.consigne}}" readonly="readonly">
             </span>
@@ -181,7 +181,7 @@
                     <div *ngIf="signatureBook.attachments[rightSelectedThumbnail].status == 'TMP' && !signatureBook.attachments[rightSelectedThumbnail].isConverted" [ngStyle]="{'height': showTopRightPanel ? '79%' : '96%'}" class="visaNoPdfWarning">
                         <div style="padding-top: 25%;" [innerHTML]="lang.editingAttachmentInterrupted"></div>
                         <div>
-                            <a title="{{lang.editAttachment}}" (click)="editAttachmentIframe(signatureBook.attachments[rightSelectedThumbnail])">
+                            <a title="{{lang.editAttachment}}" (click)="showAttachment(signatureBook.attachments[rightSelectedThumbnail])">
                                 <i class="fa fa-edit fa-2x" style="cursor:pointer;"></i>
                             </a>
                         </div>
@@ -199,7 +199,10 @@
                         </span>
                     </div>
                 </div>
-                <iframe *ngIf="showAttachmentPanel" [src]="attachmentsViewerLink | safeUrl"></iframe>
+                <app-attachments-list *ngIf="showAttachmentPanel" #appAttachmentsList
+                    [resId]="resId" [target]="'process'"
+                    (reloadBadgeAttachments)="refreshAttachments()" (afterActionAttachment)="refreshAttachments()">
+                </app-attachments-list>
             </div>
             <div *ngIf="!signatureBook.hasWorkflow" class="visaNoWorkflowWarning">
                 <div style="margin-top:200px;" [innerHTML]="noVisaWorkflowNoSignature"></div>
@@ -225,12 +228,12 @@
                     <i class="fa fa-bars fa-2x" ></i>
                 </div>
                 <hr style="background-color:#666;margin-top:0px;"/>
-                <div class="panelRightAddPj" (click)="addAttachmentIframe()" title="{{lang.createAtt}}">
+                <div class="panelRightAddPj" (click)="createAttachment()" title="{{lang.createAtt}}">
                     <i class="fa fa-paperclip fa-2x" ></i>
                     <i class="fa fa-plus" style="position:absolute;"></i>
                 </div>
                 <div *ngIf="signatureBook.attachments[rightSelectedThumbnail]">
-                    <div [ngClass]="{'visaDisabledButton': !signatureBook.attachments[rightSelectedThumbnail].canModify || signatureBook.attachments[rightSelectedThumbnail].status == 'SIGN'}" title="{{lang.updateAtt}}" class="visaPjUp" (click)="editAttachmentIframe(signatureBook.attachments[rightSelectedThumbnail])">
+                    <div [ngClass]="{'visaDisabledButton': !signatureBook.attachments[rightSelectedThumbnail].canModify || signatureBook.attachments[rightSelectedThumbnail].status == 'SIGN'}" title="{{lang.updateAtt}}" class="visaPjUp" (click)="showAttachment(signatureBook.attachments[rightSelectedThumbnail])">
                         <i class="fa fa-edit fa-2x"></i>
                     </div>
                     <div [ngClass]="{'visaDisabledButton': !signatureBook.attachments[rightSelectedThumbnail].canDelete}" title="{{lang.deleteAtt}}" class="visaPjDel" (click)="delAttachment(signatureBook.attachments[rightSelectedThumbnail])">
diff --git a/src/frontend/app/signature-book.component.scss b/src/frontend/app/signature-book.component.scss
index cae6f2ea179..7ec75ada1f9 100755
--- a/src/frontend/app/signature-book.component.scss
+++ b/src/frontend/app/signature-book.component.scss
@@ -1,3 +1,5 @@
+@import "../css/vars.scss";
+
 #modalSaveVisaModel{
 	padding-top:20px;
 	z-index: 1050;
@@ -6,18 +8,14 @@
 	border: 2px solid #000;
 	display: none;
 	position: absolute;
-	background-color: #F2F2F2;
+	background-color: #fbfbfb;
 	left: 40%;
 	top: 10%;
 	text-align: center;
 }
 
-.unselectedId{
-	background-color:#F2F2F2;
-}
-
 .selectedId{
-	background-color:#F99830;
+	background-color:$secondary;
 }
 
 #tab_visaSetWorkflow td{
@@ -77,8 +75,8 @@
 }
 
 .droptarget.currentVis{
-    color : #135F7F;
-    border: solid 2px #135F7F;
+    color : $primary;
+    border: solid 2px $primary;
 }
 
 .visaUserInfo,.visaUserStatus,.visaUserConsigne,.visaUserAction{
@@ -158,6 +156,7 @@
     position: relative;
     height:95vh;
     margin-top: 5px;
+    font-size: 12px;
 }
 
 .visaContent h1{
@@ -210,7 +209,7 @@
 }
 
 .resListContentFrame:hover{
-    background-color: #F99830;
+    background-color: $secondary;
 }
 
 .resListContentFrame{
@@ -232,7 +231,7 @@
     top: 45%;
     padding: 5px;
     cursor: pointer;
-    background: #F2F2F2;
+    background: #fbfbfb;
     border-radius:40px;
 }
 .hideRightContent{
@@ -242,7 +241,7 @@
     top: 45%;
     padding: 5px;
     cursor: pointer;
-    background: #F2F2F2;
+    background: #fbfbfb;
     border-radius:40px;
 }
 
@@ -253,7 +252,7 @@
     top: 45%;
     padding: 5px;
     cursor: pointer;
-    background: #F2F2F2;
+    background: #fbfbfb;
     border-radius:40px;
 }
 
@@ -263,16 +262,13 @@
     vertical-align: top;
     padding-left: 10px;
     /*width:39%;*/
+    display: inline-block;
+    height:100%;
 }
 
 .contentRight{
-    /*width:39%;*/
-}
-
-.contentLeft, .contentRight{
     display: inline-block;
     height:100%;
-    text-align: center;
     vertical-align: top;
 }
 
@@ -310,10 +306,16 @@
     padding-bottom: 20px;
 }
 
-.panelRight,.contentRight .contentShow{
+.contentRight .contentShow{
     display: inline-block;
    height: 91vh;
 }
+
+.panelRight{
+    display: inline-block;
+    height: 92vh;
+}
+
 .contentLeft .contentShow{
     height: 95vh;
     overflow: auto;
@@ -328,16 +330,16 @@
 .contentRight .contentShow{
     position: relative;
     vertical-align: top;
-    width:98%;
+    width:100%;
 }
 .pjDetails,.pjDoc{
     margin-bottom: 5px;
 }
 
-.pjDetails,.pjSign,.pjCreate{
+.pjDetails,.pjSign{
     background-color:white;
     display: block;
-    width: 97%;
+    width: 99%;
     text-align: left;
     -moz-box-shadow: inset 0px 0px 5px 0px #656565;
     -webkit-box-shadow: inset 0px 0px 5px 0px #656565;
@@ -351,7 +353,7 @@
     height:100px;
 }
 
-.pjSign,.pjCreate{
+.pjSign{
     cursor: pointer;
 }
 
@@ -367,10 +369,9 @@
     margin-top: 2px;
     text-align: center;
     cursor: pointer;
-    background: #135F7F;
+    background: $primary;
     color: white;
     padding:10px;
-    /*border-radius: 25px;*/
     -moz-box-shadow: 0px 0px 10px 0px #656565;
     -webkit-box-shadow: 0px 0px 10px 0px #656565;
     -o-box-shadow: 0px 0px 10px 0px #656565;
@@ -378,31 +379,16 @@
     filter: progid:DXImageTransform.Microsoft.Shadow(color=#656565,Direction=NaN,Strength=10);
 }
 
-.pjCreate{
-    height: auto;
+.pjDetails div{
     white-space: initial;
 }
 
-.pjCreate input,.pjCreate select{    
-    width:80%;
-}
-
-.pjCreateFile{
-    border: dashed 12px #ccc;
-    height: 60%;
-    border-radius: 5px;
-    margin-top: 10px;
-}
-
-.pjCreateFile i{
-    font-size: 100px;
-    opacity: 0.5;
-    margin-top: 20%;
-    cursor: pointer;
-}
-
-.pjDetails div{
-    white-space: initial;
+.pjDetails {
+    color: #666;
+    font-family: Verdana,Geneva,Arial,Helvetica,sans-serif;
+    font-size: 12px;
+    font-weight: normal;
+    letter-spacing: 0.02em;
 }
 
 .pjDoc{
@@ -426,7 +412,7 @@
 }
 
 img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
-    border: solid 2px #135F7F;
+    border: solid 2px $primary;
     cursor:pointer;
 }
 
@@ -453,13 +439,22 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
    width:17%;
 }
 
+#signatureBookActions {
+    background: white;
+    border: solid 1px $primary;
+    color: black;
+    font: 400 13.3333px Arial;
+}
+
 .titleSignatureBook{
+    height: 38px;
+    font-size: 12px;
     display: block;
     width:100%;
     border: solid 1px black;
     vertical-align: middle;
     font-weight: bold;
-    background: #135F7F;
+    background: $primary;
     color: white;
     text-transform : uppercase;
     white-space: nowrap;
@@ -468,7 +463,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 #closeSignatureBook{
     float:right;
     padding:5px;
-    width:2%;
+    width:47px;
     text-align: center;
 }
 #labelSignatureBook{
@@ -500,70 +495,27 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     padding: 5px;
     cursor: pointer;
     vertical-align: middle;
-    width: 40px;
+    width: 60px;
     text-align: center;
     padding-left: 10px;
     padding-right: 10px;
     padding-bottom: 7px;
     color:#666;
-    background-color: #F2F2F2;
-}
-/*.titleSignatureBook span{
-    display: inline-block;
-    vertical-align: middle;
-    padding:10px;
-    width: 90%;
-    overflow: hidden;
-    text-overflow: ellipsis;
-
-}
-
-.titleSignatureBook span:nth-child(2){
-    position: absolute;
-    right: 0px;
-    text-align: right;
-    padding-left: 5px;
-    top: -4px;
-}*/
-
-.headerSignatureBook{
-    border: solid 1px black;
-    display: table;
-    margin-bottom: 10px;
-    white-space: nowrap;
-    text-align: center;
-    width: 100%;
+    background-color: #fbfbfb;
 }
 
 .contentSignatureBook{
     border: solid 1px black;
+    border-top: none;
     display: block;
     width: 100%;
-    height:91vh;
+    height:93vh;
     white-space: nowrap;
     overflow: hidden;
-    background-color: #F2F2F2;
-    margin-top: 5px;
-}
-
-.headerSignatureBook .item{
-    border-right: solid 1px black;
-    display: table-cell;
-    padding: 5px;
-    cursor: pointer;
-    width: 5%;
-    vertical-align: middle;
-    background-color: #F2F2F2;
-}
-
-.headerSignatureBook .activeTabSignatureBook i{
-    display: table-cell;
-    vertical-align: middle;
+    background-color: #fbfbfb;
 }
 
 #tabSignatureBook .item.activeTabSignatureBook{
-    /*background: #135F7F;
-    color:white;*/
     -moz-box-shadow: inset 0px 0px 5px 0px #656565;
     -webkit-box-shadow: inset 0px 0px 5px 0px #656565;
     -o-box-shadow: inset 0px 0px 5px 0px #656565;
@@ -573,7 +525,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 }
 
 .resListContentFrameSelected{
-    background-color: #F99830;
+    background-color: $secondary;
     
 }
 
@@ -582,14 +534,14 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     -webkit-box-shadow: inset 0px 0px 5px 0px #656565;
     -o-box-shadow: inset 0px 0px 5px 0px #656565;
     box-shadow: inset 0px 0px 5px 0px #656565;
-    background-color: #F99830;
+    background-color: $secondary;
 }
 .panelSelectedThumbnail{
     -moz-box-shadow: inset 0px 0px 5px 0px #656565;
     -webkit-box-shadow: inset 0px 0px 5px 0px #656565;
     -o-box-shadow: inset 0px 0px 5px 0px #656565;
     box-shadow: inset 0px 0px 5px 0px #656565;
-    background-color: #F99830;
+    background-color: $secondary;
     color: #666 !important;
 }
 
@@ -599,7 +551,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     white-space: nowrap !important;
     overflow: hidden;
     text-overflow: ellipsis;
-    padding: 5px;
+    padding-left: 10px;
 }
 
 .infoPj label{
@@ -701,7 +653,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     background-color: transparent;
     border-collapse: collapse;
     border-spacing: 0;
-    border-bottom: solid 1px #135F7F;
+    border-bottom: solid 1px $primary;
 }
 
 .ng-table td{
@@ -710,7 +662,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 }
 
 .ng-table th{
-    border-bottom: solid 1px #135F7F;
+    border-bottom: solid 1px $primary;
 }
 
 .ng-table-counts{
@@ -719,14 +671,14 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 
 .ng-table-pagination li{
     display: inline-block;
-    border: solid 1px #135F7F;
+    border: solid 1px $primary;
     padding: 5px;
     margin:5px;
     border-radius: 10px;
 }
 
 .ng-table-pagination li.active{
-    background : #135F7F;
+    background : $primary;
     color: white;
 }
 
@@ -735,7 +687,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 }
 
 .ng-table-pagination li:hover{
-    background : #135F7F;
+    background : $primary;
     color: white;
 }
 
@@ -770,7 +722,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     bottom: 0px;
     background: white;
     margin-bottom: 5px;
-    width : 50px;
+    width : 80px;
     -webkit-transition: width 0.5s; /* Safari */
     transition: width 0.5s;
     overflow:auto !important;
@@ -794,7 +746,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     transition: height 0.5s;
 }
 .pjSign:hover img:hover{
-    border: solid 2px #135F7F;
+    border: solid 2px $primary;
 }
 .pjSign:hover {
     width : 95%;
@@ -806,12 +758,12 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 }
 
 .panelRightListPj{
-    padding: 5px;
+    padding: 3px;
     margin-bottom: 5px;
     cursor: pointer;
 }
 .panelRightListPj:hover{
-    color: #135F7F;
+    color: $primary;
 }
 
 .panelRightAddPj{
@@ -820,8 +772,8 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     margin: auto;
     margin-bottom: 10px; 
     font-size: 10px;
-    height:20px;
-    width:20px;
+    height:40px;
+    width:40px;
     cursor: pointer;
     padding: 10px;
     background-color: #16a765;
@@ -834,16 +786,12 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     filter: progid:DXImageTransform.Microsoft.Shadow(color=#656565, Direction=NaN, Strength=10);
 }
 
-.panelRightAddPj i{
-    margin-left: -5px;
-}
-
 .visaPjUp{
     margin: auto;
     margin-bottom: 10px; 
     font-size: 10px;
-    height:20px;
-    width:20px;
+    height:40px;
+    width:40px;
     cursor: pointer;
     padding: 10px;
     background-color: #4285f4;
@@ -858,9 +806,8 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 .visaPjDel{
     margin: auto;
     font-size: 10px;
-    margin-bottom: 5px; 
-    height:20px;
-    width:20px;
+    height:40px;
+    width:40px;
     cursor: pointer;
     padding: 10px;
     background-color: #d14836;
@@ -880,7 +827,7 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
     font-size: 40px;
 }
 .visaNoWorkflowWarning{
-    height: 96%;
+    height: 97%;
     width:100%;
     font-size: 40px;
     border: dashed;
@@ -920,8 +867,26 @@ img.panelSelectedThumbnail,img:hover.panelSelectedThumbnail{
 }
 
 .nbRes{
-    background: #F99830;
+    background: $secondary;
     color: white;
     padding: 3px;
     border-radius: 7px;
-}
\ No newline at end of file
+}
+
+.haveContent {
+    font-size: 9px !important;
+    color: $secondary;
+    margin-left: 2px;
+}
+
+.button-form-primary-filled {
+    background: $primary;
+    border: solid 1px white;
+    color: white;
+    border-radius: 30px;
+    padding-left: 20px;
+    padding-right: 20px;
+    -webkit-transition: all 0.1s;
+    transition: all 0.1s;
+    margin-left: 5px;
+}
diff --git a/src/frontend/app/signature-book.component.ts b/src/frontend/app/signature-book.component.ts
index af5b7a05cb2..d1039a9a70f 100755
--- a/src/frontend/app/signature-book.component.ts
+++ b/src/frontend/app/signature-book.component.ts
@@ -4,18 +4,19 @@ import { DomSanitizer } from '@angular/platform-browser';
 import { Router, ActivatedRoute } from '@angular/router';
 import { LANG } from './translate.component';
 import { NotificationService } from './notification.service';
-import { tap, catchError } from 'rxjs/operators';
+import { tap, catchError, filter } from 'rxjs/operators';
 import { of } from 'rxjs';
 import { PrivilegeService } from '../service/privileges.service';
+import { MatDialogRef, MatDialog } from '@angular/material';
+import { AttachmentCreateComponent } from './attachments/attachment-create/attachment-create.component';
+import { FunctionsService } from '../service/functions.service';
+import { AttachmentPageComponent } from './attachments/attachments-page/attachment-page.component';
 
 declare function lockDocument(resId: number) : void;
 declare function unlockDocument(resId: number) : void;
 declare function valid_action_form(a1: string, a2: string, a3: string, a4: number, a5: string, a6: string, a7: string, a8: string, a9: boolean, a10: any) : void;
 declare function $j(selector: string) : any;
-declare function showAttachmentsForm(path: string) : void;
-declare function modifyAttachmentsForm(path: string, width: string, height: string) : void;
 declare function setSessionForSignatureBook(resId: any) : void;
-// declare function triggerAngular(route: string) : void;
 
 declare var angularGlobals : any;
 
@@ -68,8 +69,7 @@ export class SignatureBookComponent implements OnInit {
 
     leftContentWidth            : string    = "44%";
     rightContentWidth           : string    = "44%";
-
-    attachmentsViewerLink       : string    = "";
+    dialogRef: MatDialogRef<any>;
     
     processTool: any[] = [
         {
@@ -104,19 +104,10 @@ export class SignatureBookComponent implements OnInit {
         private router: Router, 
         private zone: NgZone, 
         private notify: NotificationService,
-        public privilegeService: PrivilegeService
+        public privilegeService: PrivilegeService,
+        public dialog: MatDialog,
+        public functions: FunctionsService
     ) {
-        
-        // $j("head style").remove();
-        // if ($j("link[href='merged_css.php']").length == 0) {
-        //     var head = document.getElementsByTagName('head')[0];
-        //     var link = document.createElement('link');
-        //     link.rel = 'stylesheet';
-        //     link.href = 'merged_css.php';
-        //     link.type = 'text/css';
-        //     link.media = 'screen';
-        //     head.insertBefore(link,head.children[5])
-        // }
         window['angularSignatureBookComponent'] = {
             componentAfterAttach: (value: string) => this.processAfterAttach(value),
             componentAfterAction: () => this.processAfterAction()
@@ -124,13 +115,7 @@ export class SignatureBookComponent implements OnInit {
         (<any>window).pdfWorkerSrc = '../../node_modules/pdfjs-dist/build/pdf.worker.min.js';
     }
 
-    prepareSignatureBook() {
-        $j('main-header').remove();
-        $j('#container').width("99%");
-    }
-
     ngOnInit() : void {
-        this.prepareSignatureBook();
         this.coreUrl = angularGlobals.coreUrl;
 
         this.loading = true;
@@ -164,7 +149,6 @@ export class SignatureBookComponent implements OnInit {
                     this.showTopLeftPanel       = false;
                     this.showTopRightPanel      = false;
                     this.showAttachmentPanel    = false;
-                    // this.attachmentsViewerLink = "index.php?display=true&module=attachments&page=frame_list_attachments&resId=" + this.resId + "&noModification=true&template_selected=documents_list_attachments_simple&load&attach_type_exclude=converted_pdf,print_folder";
 
                     this.leftContentWidth  = "44%";
                     this.rightContentWidth = "44%";
@@ -192,6 +176,7 @@ export class SignatureBookComponent implements OnInit {
                         }
                     }, 0);
                     this.loadBadges();
+                    this.loadActions();
                 }, (err) => {
                     this.notify.error(err.error.errors);
                     setTimeout(() => {
@@ -199,12 +184,15 @@ export class SignatureBookComponent implements OnInit {
                     }, 2000);
 
                 });
-            this.http.get("../../rest/resourcesList/users/" + this.userId + "/groups/" + this.groupId + "/baskets/" + this.basketId + "/actions?resId=" + this.resId)
-                .subscribe((data : any) => {
-                    this.signatureBook.actions = data.actions;
-                }, (err) => {
-                    this.notify.error(err.error.errors);
-                });
+        });
+    }
+    
+    loadActions() {
+        this.http.get("../../rest/resourcesList/users/" + this.userId + "/groups/" + this.groupId + "/baskets/" + this.basketId + "/actions?resId=" + this.resId)
+        .subscribe((data : any) => {
+            this.signatureBook.actions = data.actions;
+        }, (err) => {
+            this.notify.error(err.error.errors);
         });
     }
 
@@ -254,13 +242,11 @@ export class SignatureBookComponent implements OnInit {
             this.rightViewerLink = "";
         }
         this.rightSelectedThumbnail = index;
-        //this.reloadViewerRight();
     }
 
     changeLeftViewer(index: number) {
         this.leftViewerLink = this.signatureBook.documents[index].viewerLink;
         this.leftSelectedThumbnail = index;
-        //this.reloadViewerLeft();
     }
 
     displayPanel(panel: string) {
@@ -277,7 +263,7 @@ export class SignatureBookComponent implements OnInit {
             } else {
                 this.rightContentWidth = "48%";
                 this.leftContentWidth = "48%";
-                $j("#hideLeftContent").css('background', '#F2F2F2');
+                $j("#hideLeftContent").css('background', '#fbfbfb');
             }
         } else if (panel == "RESLEFT") {
             this.showResLeftPanel = !this.showResLeftPanel;
@@ -365,19 +351,6 @@ export class SignatureBookComponent implements OnInit {
         }
     }
 
-    addAttachmentIframe() {
-        // showAttachmentsForm('index.php?display=true&module=attachments&page=attachments_content&docId=' + this.resId);
-    }
-
-    editAttachmentIframe(attachment: any) {
-        if (attachment.canModify && attachment.status != "SIGN") {
-            var resId: number;
-            resId = attachment.res_id;
-
-            // modifyAttachmentsForm('index.php?display=true&module=attachments&page=attachments_content&id=' + resId + '&relation=' + attachment.relation + '&docId=' + this.resId, '98%', 'auto');
-        }
-    }
-
     delAttachment(attachment: any) {
         if (attachment.canDelete) {
             if (this.signatureBook.attachments.length <= 1) {
@@ -386,13 +359,15 @@ export class SignatureBookComponent implements OnInit {
                 var r = confirm('Voulez-vous vraiment supprimer la pièce jointe ?');
             }
             if (r) {
-                var resId: number;
-                resId = attachment.res_id;
-
-                // this.http.get('index.php?display=true&module=attachments&page=del_attachment&id=' + resId + '&relation=' + attachment.relation + '&docId=' + this.resId + '&rest=true')
-                //     .subscribe(() => {
-                //         this.refreshAttachments('del');
-                //     });
+                this.http.delete('../../rest/attachments/' + attachment.res_id).pipe(
+                    tap(() => {
+                        this.refreshAttachments('del');
+                    }),
+                    catchError((err: any) => {
+                        this.notify.handleErrors(err);
+                        return of(false);
+                    })
+                ).subscribe();
             }
         }
     }
@@ -526,4 +501,36 @@ export class SignatureBookComponent implements OnInit {
         ).subscribe();
     }
 
+    createAttachment() {
+        this.dialogRef = this.dialog.open(AttachmentCreateComponent, { disableClose: true, panelClass: 'modal-container', height: '90vh', width: '90vw', data: { resIdMaster: this.resId } });
+
+        this.dialogRef.afterClosed().pipe(
+            filter((data: string) => data === 'success'),
+            tap(() => {
+                this.refreshAttachments('add');
+            }),
+            catchError((err: any) => {
+                this.notify.handleErrors(err);
+                return of(false);
+            })
+        ).subscribe();
+    }
+
+    showAttachment(attachment: any) {
+        if (attachment.canModify && attachment.status != "SIGN") {
+            this.dialogRef = this.dialog.open(AttachmentPageComponent, { height: '99vh', width: '99vw', panelClass: 'modal-container', disableClose: true, data: { resId: attachment.res_id} });
+    
+            this.dialogRef.afterClosed().pipe(
+                filter((data: string) => data === 'success'),
+                tap(() => {
+                    this.refreshAttachments('edit');
+                }),
+                catchError((err: any) => {
+                    this.notify.handleErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        }
+    }
+
 }
diff --git a/src/frontend/lang/lang-en.ts b/src/frontend/lang/lang-en.ts
index 7eee7c01c1b..6bd3d3603a3 100755
--- a/src/frontend/lang/lang-en.ts
+++ b/src/frontend/lang/lang-en.ts
@@ -1493,6 +1493,11 @@ export const LANG_EN = {
     "cannotCloseMails" : "Some mails cannot be closed",
     "followingFieldsAreEmpty" : "Following fields are empty",
     "checkEmptyFields" : "Requisite fields to make this action",
+    "contactLinkedToMails" : "Contact linked to mails",
+    "reaffect" : "Reaffect",
+    "reaffectContactRedirect" : "Reaffect to a contact",
+    "contactReplacement" : "Replacement contact",
+    "availableContacts" : "Available contact",
     "sent" :  "Sent",
     "notSent" :  "Not sent",
     "delivery" :  "Delivery",
diff --git a/src/frontend/lang/lang-fr.ts b/src/frontend/lang/lang-fr.ts
index b310599ce5d..c8b72172793 100755
--- a/src/frontend/lang/lang-fr.ts
+++ b/src/frontend/lang/lang-fr.ts
@@ -1534,6 +1534,11 @@ export const LANG_FR = {
     "cannotCloseMails" : "Certains courriers ne peuvent pas être clôturés",
     "followingFieldsAreEmpty" : "Les champs suivants sont vides",
     "checkEmptyFields" : "Champs requis pour effectuer cette action",
+    "contactLinkedToMails" : "Ce contact est lié à des courriers",
+    "reaffect" : "Réaffecter",
+    "reaffectContactRedirect" : "Réaffecter à un contact",
+    "contactReplacement" : "Contact remplaçant",
+    "availableContacts" : "Contact(s) disponible(s)",
     "sent" :  "Envoyé",
     "notSent" :  "Non envoyé",
     "delivery" :  "Pris en charge",
diff --git a/src/frontend/lang/lang-nl.ts b/src/frontend/lang/lang-nl.ts
index d6a10874063..0ac9ed47c4d 100755
--- a/src/frontend/lang/lang-nl.ts
+++ b/src/frontend/lang/lang-nl.ts
@@ -1518,6 +1518,11 @@ export const LANG_NL = {
     "cannotCloseMails" : "Some mails cannot be closed", //_TO_TRANSLATE
     "followingFieldsAreEmpty" : "Following fields are empty", //_TO_TRANSLATE
     "checkEmptyFields" : "Requisite fields to make this action", //_TO_TRANSLATE
+    "contactLinkedToMails" : "Contact linked to mails", //_TO_TRANSLATE
+    "reaffect" : "Reaffect", //_TO_TRANSLATE
+    "reaffectContactRedirect" : "Reaffect to a contact", //_TO_TRANSLATE
+    "contactReplacement" : "Replacement contact", //_TO_TRANSLATE
+    "availableContacts" : "Available contact", //_TO_TRANSLATE
     "sent" :  "Sent", //_TO_TRANSLATE
     "notSent" :  "Not sent", //_TO_TRANSLATE
     "delivery" :  "Delivery", //_TO_TRANSLATE
-- 
GitLab