diff --git a/src/app/external/messageExchange/controllers/SendMessageExchangeController.php b/src/app/external/messageExchange/controllers/SendMessageExchangeController.php index afff1d8bf884e62615d0ce5efa0ca3eaee7e18df..306e74d4e3623e083cf5eb3f93b98ed1792f2d95 100755 --- a/src/app/external/messageExchange/controllers/SendMessageExchangeController.php +++ b/src/app/external/messageExchange/controllers/SendMessageExchangeController.php @@ -38,7 +38,7 @@ class SendMessageExchangeController public function getInitialization(Request $request, Response $response) { $rawEntities = EntityModel::getWithUserEntities([ - 'select' => ['entities.entity_label', 'entities.business_id'], + 'select' => ['entities.id', 'entities.entity_label', 'entities.business_id'], 'where' => ['users_entities.user_id = ?', 'business_id is not null', 'business_id != ?'], 'data' => [$GLOBALS['userId'], ''] ]); @@ -46,6 +46,7 @@ class SendMessageExchangeController $entities = []; foreach ($rawEntities as $key => $entity) { $entities[] = [ + 'id' => $entity['id'], 'label' => $entity['entity_label'], 'm2m' => $entity['business_id'] ]; diff --git a/src/frontend/app/app-routing.module.ts b/src/frontend/app/app-routing.module.ts index e825c5c0029fb5bbb02c3bc46dc9445c1d8e1dad..80e2512c44f80dd8f31026ad62dc94fde0d83f25 100755 --- a/src/frontend/app/app-routing.module.ts +++ b/src/frontend/app/app-routing.module.ts @@ -10,13 +10,13 @@ import { BasketListComponent } from './list/basket-list.component'; import { SignatureBookComponent } from './signature-book.component'; import { SaveNumericPackageComponent } from './save-numeric-package.component'; import { PrintSeparatorComponent } from './separator/print-separator/print-separator.component'; -import { AppGuard, AfterProcessGuard } from '../service/app.guard'; +import { AppGuard, AfterProcessGuard } from '../service/app.guard'; import { FolderDocumentListComponent } from './folder/document-list/folder-document-list.component'; import { IndexationComponent } from './indexation/indexation.component'; import { ForgotPasswordComponent } from './login/forgotPassword/forgotPassword.component'; import { UpdatePasswordComponent } from './login/updatePassword/updatePassword.component'; -import { ProcessComponent } from './process/process.component'; -import {FollowedDocumentListComponent} from "./home/followed-list/followed-document-list.component"; +import { ProcessComponent } from './process/process.component'; +import {FollowedDocumentListComponent} from "./home/followed-list/followed-document-list.component"; @NgModule({ imports: [ diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 9120ab3ca1d5aaf7852af60116e49ab21a85d6e7..00125d46a6e2cdd50b99c768fe4c1516a3abc6c1 100755 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -107,6 +107,7 @@ import { LinkResourceModalComponent } from './linkedResource/linkResourceModal/l import { DocumentViewerModalComponent } from './viewer/modal/document-viewer-modal.component'; import { SentResourceListComponent } from './sentResource/sent-resource-list.component'; import { SentResourcePageComponent } from './sentResource/sent-resource-page/sent-resource-page.component'; +import { SentNumericPackagePageComponent } from './sentResource/sent-numeric-package-page/sent-numeric-package-page.component'; @@ -202,7 +203,8 @@ import { SentResourcePageComponent } from './sentResource/sent-resource-page/sen DocumentViewerModalComponent, SentResourceListComponent, SentResourcePageComponent, - PrintedFolderModalComponent + PrintedFolderModalComponent, + SentNumericPackagePageComponent, ], entryComponents: [ InfoChangePasswordModalComponent, @@ -246,7 +248,8 @@ import { SentResourcePageComponent } from './sentResource/sent-resource-page/sen LinkResourceModalComponent, DocumentViewerModalComponent, SentResourcePageComponent, - PrintedFolderModalComponent + PrintedFolderModalComponent, + SentNumericPackagePageComponent ], providers: [ FiltersListService, FoldersService, ActionsService, PrivilegeService ], bootstrap: [ AppComponent ] diff --git a/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.html b/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.html new file mode 100644 index 0000000000000000000000000000000000000000..93255924147e84a1c15e6eef006242347ff1a836 --- /dev/null +++ b/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.html @@ -0,0 +1,169 @@ +<div class="mat-dialog-content-container"> + <h1 mat-dialog-title> + <span style="flex: 1;"> + {{lang.sendNumericPackage}} + </span> + <button [title]="lang.close" mat-icon-button (click)="saveDraft()"> + <mat-icon class="fa fa-times"></mat-icon> + </button></h1> + <mat-dialog-content class="modal-container"> + <div *ngIf="loading" class="loading"> + <mat-spinner style="margin:auto;"></mat-spinner> + </div> + <div *ngIf="canManageMail()" class="alert-message alert-message-info" + [innerHTML]="lang.sendNumericPackageInfo | scan: [maarch2maarchUrl]"></div> + <mat-form-field> + <mat-label class="attachLabel">{{lang.sender}}</mat-label> + <input *ngIf="!canManageMail()" matInput [value]="currentSender.label" readonly> + <mat-select *ngIf="canManageMail()" [compareWith]="this.compareSenders" [(ngModel)]="currentSender" + required> + <mat-option *ngFor="let sender of availableSenders | sortBy: 'label'" [value]="sender"> + {{sender.label}} ({{sender.m2m}}) + </mat-option> + </mat-select> + </mat-form-field> + <mat-form-field> + <mat-label class="attachLabel">{{lang.recipient}} *</mat-label> + <mat-chip-list id="recipients-list" #recipientsList> + <mat-chip class="recipients" *ngFor="let recipient of recipients" [selectable]="selectable" + [removable]="canManageMail()" (removed)="remove(recipient, 'recipients')" + (click)="remove(recipient, 'recipients')"> + <span style="flex:1;"> + <div> + {{recipient.label}} - <b>{{recipient.m2m}}</b> + </div> + <div class="attachLabel"> + ({{lang.communicationMean}} : {{recipient.communicationMeans}}) + </div> + </span> + <mat-icon matChipRemove class="fa fa-times" *ngIf="canManageMail()"></mat-icon> + </mat-chip> + <input #recipientsInput [formControl]="recipientsCtrl" [matChipInputFor]="recipientsList" + [matAutocomplete]="autoEmails" (focus)="resetAutocomplete()" required> + </mat-chip-list> + <mat-autocomplete #autoEmails="matAutocomplete" (optionSelected)="addRecipient($event.option.value)"> + <mat-option *ngFor="let option of filteredEmails | async" [value]="option" class="m2mRecipientList"> + <div> + {{option.label}} - <b>{{option.m2m}}</b> + </div> + <div class="attachLabel"> + ({{lang.communicationMean}} : {{option.communicationMeans}}) + </div> + </mat-option> + </mat-autocomplete> + </mat-form-field> + <mat-form-field> + <mat-label class="attachLabel">{{lang.mailSubject}}</mat-label> + <input matInput [readonly]="!canManageMail()" [(ngModel)]="numericPackage.object" maxlength="70" required> + <button mat-icon-button matSuffix *ngFor="let keyVal of emailAttachTool | keyvalue" + [disabled]="!canManageMail()" [title]="emailAttachTool[keyVal.key].title" + (click)="$event.stopPropagation();currentEmailAttachTool=keyVal.key;" + [matMenuTriggerFor]="emailAttachListMenu"> + <mat-icon class="{{emailAttachTool[keyVal.key].icon}}" + [class.activeButton]="isSelectedAttachType(keyVal.key)" color="primary"> + </mat-icon> + </button> + <mat-menu #emailAttachListMenu="matMenu" [class]="'attachListMenu'"> + <ng-container *ngFor="let keyVal of emailAttachTool | keyvalue"> + <ng-container *ngIf="keyVal.key === currentEmailAttachTool"> + <button mat-menu-item style="line-height: normal;" disableRipple + *ngFor="let attach of emailAttachTool[keyVal.key].list" + [disabled]="isSelectedAttach(attach,keyVal.key)" + (click)="$event.stopPropagation();toggleAttach(attach,keyVal.key,'original');"> + <span (click)="$event.stopPropagation();toggleAttach(attach,keyVal.key,'original')" + [title]="attach.label"> + <div style="font-size: 10px;opacity: 0.5;">{{attach.chrono}} - {{attach.typeLabel}} + ({{attach.creator}})</div> + {{attach.label | shorten: 45: '...'}} - {{attach.format}} + </span> + </button> + </ng-container> + </ng-container> + </mat-menu> + </mat-form-field> + <mat-form-field *ngIf="!canManageMail()"> + <mat-label class="attachLabel">{{lang.reference}}</mat-label> + <input matInput [value]="reference" readonly> + </mat-form-field> + <div class="attachMsg" + *ngIf="numericPackage.mainExchangeDoc === null && emailAttach.length === 0 && canManageMail()"> + {{lang.attachItemToNumericPackage}} + <i class="fas fa-arrow-up"></i> + </div> + <div style="overflow: auto;max-height: 300px;"> + <mat-list *ngIf="numericPackage.mainExchangeDoc !== null"> + <h3 mat-subheader class="attachLabel">{{lang.mainDocNumericPackage}}</h3> + <mat-list-item class="numericPackageAttach" *ngIf="numericPackage.mainExchangeDoc !== null"> + <p mat-line class="numericPackageAttachItem"> + <span + style="overflow: hidden;text-overflow: ellipsis;">{{numericPackage.mainExchangeDoc.label}}</span> + <span class="badge">{{numericPackage.mainExchangeDoc.typeLabel}}</span> + <span class="subInfo">.{{numericPackage.mainExchangeDoc.format}}</span> + <button mat-icon-button color="warn" (click)="numericPackage.mainExchangeDoc=null"> + <mat-icon class="fa fa-trash"></mat-icon> + </button> + </p> + </mat-list-item> + </mat-list> + <mat-divider *ngIf="emailAttach.length > 0"></mat-divider> + <mat-list *ngIf="emailAttach.length > 0"> + <h3 mat-subheader class="attachLabel">{{lang.attachmentsNumericPackage}}</h3> + <mat-list-item class="numericPackageAttach" *ngFor="let attach of emailAttach; let i=index"> + <p mat-line class="numericPackageAttachItem"> + <span style="overflow: hidden;text-overflow: ellipsis;" + [title]="attach.label">{{attach.label}}</span> + <span class="badge">{{attach.typeLabel}}</span> + <span class="subInfo">.{{attach.format}}</span> + <button mat-icon-button color="warn" (click)="removeAttach(i)"> + <mat-icon class="fa fa-trash"></mat-icon> + </button> + </p> + </mat-list-item> + </mat-list> + </div> + <!--<div class="models" *ngIf="canManageMail()"> + <plugin-select-search *ngIf="availableEmailModels.length > 0" [label]="lang.emailModel" + [placeholderLabel]="lang.emailModel" [datas]="availableEmailModels" + [formControlSelect]="templateEmailListForm" (afterSelected)="mergeEmailTemplate($event)"> + </plugin-select-search> + + <plugin-select-search #templateList *ngIf="availableSignEmailModels.length > 0" + [label]="lang.emailSignatures" [placeholderLabel]="lang.emailSignatures" + [datas]="availableSignEmailModels" [formControlSelect]="emailSignListForm" + (afterSelected)="mergeSignEmailTemplate($event)"> + </plugin-select-search> + </div>--> + <ng-container *ngIf="!canManageMail()"> + <div class="col-md-8"> + + </div> + <div class="col-md-4"> + <button mat-button color="primary" style="width: 180px;white-space: initial;line-height: 20px;padding: 10px;"> + <i class="fas fa-file-download" style="font-size: 40px;"></i><br/> + <span>TÊlÊcharger le pli numÊrique</span> + </button> + </div> + </ng-container> + + <div style="padding-top: 10px;"> + <mat-form-field appearance="outline"> + <mat-label class="attachLabel">{{lang.note}}</mat-label> + <textarea matInput [placeholder]="lang.addNoteToNumericPackage" [(ngModel)]="numericPackage.content" + matTextareaAutosize style="padding:0" [readonly]="!canManageMail()"></textarea> + </mat-form-field> + <app-document-viewer *ngIf="pdfMode" #appDocumentViewer + style="display:block;height:400px;width:100%;overflow: auto;" [editMode]="false" + [base64]="emailContent"> + </app-document-viewer> + </div> + </mat-dialog-content> + <span class="divider-modal"></span> + <div mat-dialog-actions class="actions"> + <button mat-raised-button color="primary" + *ngIf="canManageMail() && privilegeService.hasCurrentUserPrivilege('sendmail')" (click)="onSubmit()" + [disabled]="recipients.length === 0 || numericPackage.mainExchangeDoc === null || loading">{{lang.send}}</button> + <button mat-raised-button color="warn" (click)="deleteEmail()" + *ngIf="data.emailId && data.emailType === 'email' && !loading" + [disabled]="headerService.user.id !== emailCreatorId">{{lang.delete}}</button> + </div> +</div> \ No newline at end of file diff --git a/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.scss b/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..ca379c5adc58d02bfdac2bff324da35a8f18a3ab --- /dev/null +++ b/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.scss @@ -0,0 +1,93 @@ +@import '../../../css/vars.scss'; + +.loading { + display: flex; + height: 100%; + position: absolute; + z-index: 2; + width: 100%; + top: 0; + left: 0; + background: white; +} + +.models { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 20px; + padding-top: 10px; +} + +.activeButton { + color: $secondary; + font-weight: bold; +} + +.attachLabel { + color: $primary; +} + +::ng-deep.attachListMenu { + width: auto; + max-width: initial; + min-height: auto; +} + + +.numericPackageAttachItem { + font-size: 14px; + display: grid !important; + grid-template-columns: 1fr 150px 50px 50px; + grid-gap: 10px; + align-items: center; + + .subInfo { + color: $primary; + font-size: 80%; + } +} + +.m2mRecipientList { + line-height: initial; + font-size: 14px; +} + +.recipients { + font-size: 14px !important; + padding: 10px !important; + width: 100%; + min-height: auto; + height: auto; + border-radius: 5px; + cursor: pointer; +} + +.attachMsg { + font-size: 20px; + padding: 30px; + opacity: 0.5; + font-weight: bold; + position: relative; + text-align: center; + + i { + position: absolute; + top: 0px; + right: 40px; + transform: rotate(29deg); + } +} + +.badge { + overflow: hidden; + text-overflow: ellipsis; + font-size: 10px !important; + color: white; + font-weight: normal; + border-radius: .25em; + background: $primary; +} + +.alert-message { + min-width: 100%; +} \ No newline at end of file diff --git a/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.ts b/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3848b0aba7407b0f7a1adda1c72df68ab7d8558d --- /dev/null +++ b/src/frontend/app/sentResource/sent-numeric-package-page/sent-numeric-package-page.component.ts @@ -0,0 +1,545 @@ +import { COMMA } from '@angular/cdk/keycodes'; +import { Component, OnInit, Inject, ElementRef, ViewChild } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { LANG } from '../../translate.component'; +import { NotificationService } from '../../notification.service'; +import { Observable, of } from 'rxjs'; +import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { switchMap, catchError, filter, exhaustMap, tap, debounceTime, distinctUntilChanged, finalize, map } from 'rxjs/operators'; +import { FormControl } from '@angular/forms'; +import { FunctionsService } from '../../../service/functions.service'; +import { ContactService } from '../../../service/contact.service'; +import { AppService } from '../../../service/app.service'; +import { ConfirmComponent } from '../../../plugins/modal/confirm.component'; +import { PrivilegeService } from '../../../service/privileges.service'; +import { HeaderService } from '../../../service/header.service'; +import { StripTagsPipe } from 'ngx-pipes'; + +declare var angularGlobals: any; +declare function $j(selector: any): any; + +@Component({ + selector: 'app-sent-numeric-package-page', + templateUrl: './sent-numeric-package-page.component.html', + styleUrls: ['./sent-numeric-package-page.component.scss'], + providers: [ContactService, AppService, StripTagsPipe], +}) +export class SentNumericPackagePageComponent implements OnInit { + + + lang: any = LANG; + loading: boolean = true; + + readonly separatorKeysCodes: number[] = [COMMA]; + + availableEmailModels: any[] = []; + availableSignEmailModels: any[] = []; + + resourceData: any = null; + availableSenders: any[] = []; + currentSender: any = {}; + + recipients: any[] = []; + + copies: any[] = []; + + invisibleCopies: any[] = []; + + recipientsCtrl: FormControl = new FormControl(); + + emailSignListForm = new FormControl(); + templateEmailListForm = new FormControl(); + + filteredEmails: Observable<string[]>; + + showCopies: boolean = false; + showInvisibleCopies: boolean = false; + + emailCreatorId: number = null; + emailId: number = null; + emailStatus: string = 'WAITING'; + emailContent: string = ''; + currentEmailAttachTool: string = ''; + emailAttachTool: any = { + document: { + icon: 'fa fa-file', + title: this.lang.attachMainDocument, + list: [] + }, + notes: { + icon: 'fas fa-pen-square', + title: this.lang.attachNote, + list: [] + }, + attachments: { + icon: 'fa fa-paperclip', + title: this.lang.attachAttachment, + list: [] + }, + }; + emailAttach: any = []; + + numericPackage: any = { + mainExchangeDoc: null, + "object": '', + "contacts": [], + "joinFile": [], + "joinAttachment": [], + "notes": [], + "content": "", + "senderEmail": null + } + + communicationType: string = null; + reference: string = null; + + maarch2maarchUrl: string = `https://docs.maarch.org/gitbook/html/MaarchCourrier/${angularGlobals.applicationVersion.split('.')[0] + '.' + angularGlobals.applicationVersion.split('.')[1]}/guat/guat_exploitation/maarch2maarch.html`; + + @ViewChild('recipientsInput', { static: true }) recipientsInput: ElementRef<HTMLInputElement>; + + constructor( + public http: HttpClient, + private notify: NotificationService, + @Inject(MAT_DIALOG_DATA) public data: any, + public dialog: MatDialog, + public dialogRef: MatDialogRef<SentNumericPackagePageComponent>, + public functions: FunctionsService, + private contactService: ContactService, + public privilegeService: PrivilegeService, + public headerService: HeaderService, + private stringPipe: StripTagsPipe, + ) { } + + async ngOnInit(): Promise<void> { + + await this.getAttachElements(); + + if (this.data.emailId) { + await this.getNumericPackageData(this.data.emailId); + } + + if (this.canManageMail()) { + this.initEmailModelsList(); + this.initM2MList(); + this.initSignEmailModelsList(); + + await this.getResourceData(); + await this.getM2MSenders(); + + } + this.loading = false; + } + + isBadEmailFormat(email: string) { + const regex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/g; + + return email.trim().match(regex) === null; + } + + closeModal(state: string = '') { + this.dialogRef.close(state); + } + + addRecipient(item: any) { + this.recipients.push(item); + this.recipientsInput.nativeElement.value = ''; + this.recipientsCtrl.setValue(''); + } + + mergeEmailTemplate(templateId: any) { + + this.templateEmailListForm.reset(); + + this.http.post(`../../rest/templates/${templateId}/mergeEmail`, { data: { resId: this.data.resId } }).pipe( + tap((data: any) => { + var textArea = document.createElement('textarea'); + textArea.innerHTML = data.mergedDocument; + this.emailContent += this.stringPipe.transform(textArea.value); + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + mergeSignEmailTemplate(templateId: any) { + + this.emailSignListForm.reset(); + + this.http.get(`../../rest/currentUser/emailSignatures/${templateId}`).pipe( + tap((data: any) => { + var textArea = document.createElement('textarea'); + textArea.innerHTML = data.mergedDocument; + this.emailContent += this.stringPipe.transform(textArea.value); + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + remove(item: any, type: string): void { + if (this.canManageMail()) { + const index = this[type].indexOf(item); + + if (index >= 0) { + this[type].splice(index, 1); + } + } + } + + getNumericPackageData(emailId: number) { + return new Promise((resolve) => { + this.http.get(`../../rest/messageExchanges/${emailId}`).pipe( + map((data: any) => data.messageExchange), + tap((data: any) => { + this.emailCreatorId = data.userId; + + this.recipients = [{ + label: data.recipent, + m2m: data.recipent + }]; + + this.currentSender.label = data.sender; + this.numericPackage.object = data.object; + this.emailStatus = data.status.toUpperCase(); + this.numericPackage.content = data.body; + this.communicationType = data.communicationType; + this.reference = data.reference; + + Object.keys(data.document).forEach(element => { + if (['id', 'isLinked', 'original'].indexOf(element) === -1) { + data.document[element].forEach((dataAttach: any) => { + const elem = this.emailAttachTool[element].list.filter((item: any) => item.id === dataAttach.id || item.id === dataAttach); + if (elem.length > 0) { + this.emailAttach[element] = elem.map((item: any) => { + return { + ...item, + format: dataAttach.original || dataAttach.original === undefined ? item.format : 'pdf', + original: dataAttach.original, + size: dataAttach.original || dataAttach.original === undefined ? item.size : item.convertedDocument.size + } + }) + } + }); + } else if (element === 'isLinked' && data.document.isLinked === true) { + this.emailAttach.document.isLinked = true; + this.emailAttach.document.original = data.document.original; + this.emailAttach.document.size = this.emailAttach.document.original ? this.emailAttachTool.document.list[0].size : this.emailAttachTool.document.list[0].convertedDocument.size + } + }); + + resolve(true); + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + getResourceData() { + return new Promise((resolve) => { + this.http.get(`../../rest/resources/${this.data.resId}?light=true`).pipe( + tap((data: any) => { + this.resourceData = data; + this.numericPackage.object = this.resourceData.subject; + + resolve(true); + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + setSender(id: number) { + this.http.get(`../../rest/contacts/${id}`).pipe( + tap((data: any) => { + if (!this.functions.empty(data.email)) { + this.recipients.push( + { + label: this.contactService.formatContact(data), + email: data.email + } + ) + } + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + getM2MSenders() { + return new Promise((resolve) => { + this.http.get('../../rest/messageExchangesInitialization').pipe( + tap((data: any) => { + this.availableSenders = data.entities; + this.currentSender = this.availableSenders[0] + resolve(true); + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + getAttachElements() { + return new Promise((resolve) => { + this.http.get(`../../rest/resources/${this.data.resId}/emailsInitialization`).pipe( + tap((data: any) => { + Object.keys(data).forEach(element => { + if (element === 'resource') { + this.emailAttachTool.document.list = []; + if (!this.functions.empty(data[element])) { + this.emailAttachTool.document.list = [data[element]]; + } + } else { + this.emailAttachTool[element].list = data[element].map((item: any) => { + return { + ...item, + original: item.original !== undefined ? item.original : true, + title: item.chrono !== undefined ? `${item.chrono} - ${item.label} (${item.typeLabel})` : `${item.label} (${item.typeLabel})` + } + }); + } + }); + resolve(true); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + initM2MList() { + this.recipientsCtrl.valueChanges.pipe( + filter(value => value !== null), + debounceTime(300), + tap((value) => { + if (value.length === 0) { + this.filteredEmails = of([]); + } + }), + filter(value => value.length > 2), + distinctUntilChanged(), + switchMap(data => this.http.get('../../rest/autocomplete/contacts/m2m', { params: { "search": data } })), + tap((data: any) => { + data = data.map((contact: any) => { + return { + ...contact, + address: this.contactService.formatContact(contact), + label: this.contactService.formatContact(contact) + } + }); + this.filteredEmails = of(data); + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + + initEmailModelsList() { + this.http.get(`../../rest/resources/${this.data.resId}/emailTemplates`).pipe( + tap((data: any) => { + this.availableEmailModels = data.templates; + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + initSignEmailModelsList() { + this.http.get(`../../rest/currentUser/emailSignatures`).pipe( + tap((data: any) => { + this.availableSignEmailModels = data.emailSignatures; + }), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + resetAutocomplete() { + this.filteredEmails = of([]); + } + + onSubmit() { + this.loading = true; + this.emailStatus = 'WAITING'; + if (this.data.emailId === null) { + if (this.numericPackage.object === '') { + const dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.lang.confirm, msg: this.lang.warnEmptySubject } }); + + dialogRef.afterClosed().pipe( + filter((data: string) => data === 'ok'), + tap(() => { + this.createEmail(true); + }) + ).subscribe(); + } else { + this.createEmail(true); + } + + } else { + this.updateEmail(true); + } + } + + createEmail(closeModal: boolean = true) { + this.http.post(`../../rest/resources/${this.data.resId}/messageExchange`, this.formatNumericPackage()).pipe( + tap(() => { + this.notify.success(`Pli numÊrique envoyÊ`); + + this.closeModal('success'); + }), + finalize(() => this.loading = false), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + deleteEmail() { + const dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.lang.delete, msg: this.lang.confirmAction } }); + + dialogRef.afterClosed().pipe( + filter((data: string) => data === 'ok'), + exhaustMap(() => this.http.delete(`../../rest/emails/${this.data.emailId}`)), + tap(() => { + this.notify.success(this.lang.emailDeleted); + this.closeModal('success'); + }), + finalize(() => this.loading = false), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + updateEmail(closeModal: boolean = true) { + this.http.put(`../../rest/emails/${this.data.emailId}`, this.formatNumericPackage()).pipe( + tap(() => { + if (this.emailStatus === 'DRAFT') { + // this.notify.success(this.lang.draftUpdated); + } else { + this.notify.success(`Pli numÊrique envoyÊ`); + } + + if (closeModal) { + this.closeModal('success'); + } + }), + finalize(() => this.loading = false), + catchError((err) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + saveDraft() { + this.closeModal(); + /*if (this.canManageMail()) { + this.emailStatus = 'DRAFT'; + if (this.data.emailId === null) { + if (!this.functions.empty(tinymce.get('emailSignature').getContent())) { + this.createEmail(true); + } else { + this.closeModal(); + } + } else { + this.updateEmail(true); + } + } else { + this.closeModal(); + }*/ + } + + toggleAttach(item: any, type: string, mode: string) { + if (this.numericPackage.mainExchangeDoc === null) { + this.numericPackage.mainExchangeDoc = { + ...item, + typeLabel: item.typeLabel !== undefined ? item.typeLabel : this.lang.mainDocument, + type: type + } + } else { + this.emailAttach.push({ + ...item, + typeLabel: item.typeLabel !== undefined ? item.typeLabel : this.lang.mainDocument, + type: type + }); + } + } + + removeAttach(index: number) { + this.emailAttach.splice(index, 1); + } + + formatNumericPackage() { + const numericPackage: any = {}; + if (this.numericPackage.mainExchangeDoc !== null) { + let typeDoc = 'res_letterbox'; + if (this.numericPackage.mainExchangeDoc.type === 'attachments') { + typeDoc = 'res_attachments'; + } else if (this.numericPackage.mainExchangeDoc.type === 'notes') { + typeDoc = 'notes'; + } + numericPackage.joinFile = [parseInt(this.numericPackage.mainExchangeDoc.id)]; + numericPackage.mainExchangeDoc = `${typeDoc}__${this.numericPackage.mainExchangeDoc.id}` + } + numericPackage.object = this.numericPackage.object; + numericPackage.content = this.numericPackage.content; + numericPackage.contacts = this.recipients.map(recipient => recipient.id); + numericPackage.joinAttachment = this.emailAttach.filter((attach: any) => attach.type === 'attachments').map((attach: any) => attach.id); + numericPackage.notes = this.emailAttach.filter((attach: any) => attach.type === 'notes').map((attach: any) => attach.id); + numericPackage.senderEmail = this.currentSender.id; + + return numericPackage; + } + + isSelectedAttach(item: any, type: string) { + return this.emailAttach.filter((attach: any) => attach.id === item.id && attach.type === type).length > 0 || (this.numericPackage.mainExchangeDoc !== null && this.numericPackage.mainExchangeDoc.id === item.id && type === this.numericPackage.mainExchangeDoc.type); + } + + isSelectedAttachType(type: string) { + return this.emailAttach.filter((attach: any) => attach.type === type).length > 0 || (this.numericPackage.mainExchangeDoc !== null && type === this.numericPackage.mainExchangeDoc.type); + } + + canManageMail() { + if ((this.data.emailId === null) || (this.emailStatus !== 'SENT' && this.headerService.user.id === this.emailCreatorId)) { + this.recipientsCtrl.enable(); + return true; + } else { + this.recipientsCtrl.disable(); + return false; + } + } + + compareSenders(sender1: any, sender2: any) { + return (sender1.label === sender2.label || ((sender1.label === null || sender2.label === null) && (sender1.entityId === null || sender2.entityId === null))) && sender1.entityId === sender2.entityId && sender1.email === sender2.email; + } +} diff --git a/src/frontend/app/sentResource/sent-resource-list.component.html b/src/frontend/app/sentResource/sent-resource-list.component.html index 4c1b61d14bc729d849919152a81373c533ea2532..0e4be918caa01c573ee398cf5c59e319e249b739 100644 --- a/src/frontend/app/sentResource/sent-resource-list.component.html +++ b/src/frontend/app/sentResource/sent-resource-list.component.html @@ -16,17 +16,20 @@ </div> <mat-table #table [dataSource]="dataSource" matSort matSortActive="creationDate" matSortDirection="desc"> <ng-container matColumnDef="creationDate"> - <mat-cell *matCellDef="let row" class="dataLine" [class.canManage]="row.canManage" style="flex: 1;padding: 0px;flex-direction: column;" (click)="openPromptMail(row)"> + <mat-cell *matCellDef="let row" class="dataLine" [class.canManage]="row.canManage" style="flex: 1;padding: 0px;flex-direction: column;" (click)="open(row)"> <div class="subinfo"> - <span style="flex:1" [title]="row.creationDate | fullDate"> - {{lang.createdAlt}} : <b>{{row.creationDate | timeAgo : 'full'}}</b> + <span *ngIf="(row.sendDate === null && row.status === 'SENT' && row.type == 'm2m_ARCHIVETRANSFER') || row.type != 'm2m_ARCHIVETRANSFER'" style="flex:1" [title]="row.creationDate | fullDate"> + {{row.type == 'm2m_ARCHIVETRANSFER' ? lang.sent : lang.createdAlt}} : <b>{{row.creationDate | timeAgo : 'full'}}</b> </span> <span *ngIf="row.sendDate !== null" style="flex:1;color:green" [title]="row.sendDate | fullDate"> - {{lang.sent}} : <b>{{row.sendDate | timeAgo : 'full'}}</b> + {{row.type == 'm2m_ARCHIVETRANSFER' ? 'Reçu' : lang.sent}} : <b>{{row.sendDate | timeAgo : 'full'}}</b> </span> <span *ngIf="row.status === 'DRAFT'" style="flex:1;color:orange"> {{lang.draft}} </span> + <span *ngIf="row.sendDate === null && row.status === 'SENT'" style="flex:1;color:orange"> + En attente de rÊception + </span> <span *ngIf="row.status === 'WAITING'" style="flex:1;color:orange;font-weight: bold;"> {{lang.emailSendInProgress}} </span> @@ -36,7 +39,7 @@ <span *ngIf="row.status !== 'ERROR' && row.type == 'acknowledgementReceipt' && functions.empty(row.sendDate)" style="flex:1;color:orange"> {{lang.manualSendingAR}} </span> - <span style="flex:1" *ngIf="!functions.empty(row.operationDate)" [title]="row.operationDate | fullDate"> + <span style="flex:1;color:green" *ngIf="!functions.empty(row.operationDate)" [title]="row.operationDate | fullDate"> {{lang.delivery}} : <b>{{row.operationDate | timeAgo : 'full'}}</b> </span> </div> @@ -68,4 +71,7 @@ <button mat-fab *ngIf="privilegeService.hasCurrentUserPrivilege('sendmail')" class="addSentResource" color="primary" [title]="lang.sendElement" (click)="openPromptMail()"> <mat-icon style="height: auto;width: auto;" class="fa fa-plus"></mat-icon> </button> + <button mat-fab *ngIf="privilegeService.hasCurrentUserPrivilege('sendmail')" class="addSentResource" color="primary" [title]="lang.sendNumericPackage" (click)="openPromptNumericPackage()"> + <mat-icon style="height: auto;width: auto;font-size: 20px;" class="fas fa-route"></mat-icon> + </button> </ng-template> diff --git a/src/frontend/app/sentResource/sent-resource-list.component.ts b/src/frontend/app/sentResource/sent-resource-list.component.ts index f822e7ec550e846d51ac1ac1ddb6019bfc0459f9..12253cce3957603bc84b08d75e0e49b8f3951419 100644 --- a/src/frontend/app/sentResource/sent-resource-list.component.ts +++ b/src/frontend/app/sentResource/sent-resource-list.component.ts @@ -8,6 +8,7 @@ import { map, catchError, filter, tap } from 'rxjs/operators'; import { FunctionsService } from '../../service/functions.service'; import { PrivilegeService } from '../../service/privileges.service'; import { SentResourcePageComponent } from './sent-resource-page/sent-resource-page.component'; +import { SentNumericPackagePageComponent } from './sent-numeric-package-page/sent-numeric-package-page.component'; @Component({ selector: 'app-sent-resource-list', @@ -169,11 +170,11 @@ export class SentResourceListComponent implements OnInit { type: 'm2m_ARCHIVETRANSFER', typeColor: '#F99830', desc: this.lang.m2m_ARCHIVETRANSFER, - status: item.status, + status: item.status.toUpperCase(), hasAttach: false, hasNote: false, hasMainDoc: false, - canManage: false + canManage: true } }); return data.messageExchanges; @@ -245,6 +246,15 @@ export class SentResourceListComponent implements OnInit { this.dataSource.filter = ev.value; } + open(row: any = {id: null, type: null}) { + + if (row.type === 'm2m_ARCHIVETRANSFER') { + this.openPromptNumericPackage(row); + } else { + this.openPromptMail(row); + } + } + openPromptMail(row: any = {id: null, type: null}) { let title = this.lang.sendElement; @@ -254,7 +264,34 @@ export class SentResourceListComponent implements OnInit { } if (row.canManage || row.id === null) { - const dialogRef = this.dialog.open(SentResourcePageComponent, { panelClass: 'maarch-modal', disableClose: true, data: { title: title, resId: this.resId, emailId: row.id, emailType: row.type } }); + const dialogRef = this.dialog.open(SentResourcePageComponent, { panelClass: 'maarch-modal', width:'60vw', disableClose: true, data: { title: title, resId: this.resId, emailId: row.id, emailType: row.type } }); + + dialogRef.afterClosed().pipe( + filter((data: any) => data.state === 'success' || data === 'success'), + tap(() => { + this.refreshEmailList(); + setTimeout(() => { + this.refreshWaitingElements(); + }, 5000); + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + } + + openPromptNumericPackage(row: any = {id: null, type: null}) { + + let title = this.lang.sendElement; + + if (row.id !== null) { + title = this.lang[row.type]; + } + + if (row.canManage || row.id === null) { + const dialogRef = this.dialog.open(SentNumericPackagePageComponent, { panelClass: 'maarch-modal', width:'60vw', disableClose: true, data: { title: title, resId: this.resId, emailId: row.id } }); dialogRef.afterClosed().pipe( filter((data: any) => data.state === 'success' || data === 'success'), diff --git a/src/frontend/app/sentResource/sent-resource-page/sent-resource-page.component.ts b/src/frontend/app/sentResource/sent-resource-page/sent-resource-page.component.ts index 21e3cb872aeb3054768d0868ff6daeb882cc3ebc..ca45fdc0a7be2773e64f81beb0a37b22e82a0c72 100644 --- a/src/frontend/app/sentResource/sent-resource-page/sent-resource-page.component.ts +++ b/src/frontend/app/sentResource/sent-resource-page/sent-resource-page.component.ts @@ -128,7 +128,7 @@ export class SentResourcePageComponent implements OnInit { this.setDefaultInfo(); } } - this.loading = false; + //this.loading = false; setTimeout(() => { this.initMce(); }, 0); @@ -137,9 +137,14 @@ export class SentResourcePageComponent implements OnInit { initMce() { tinymce.init({ selector: "textarea#emailSignature", + setup: (editor: any) => { + editor.on('init', (e: any) => { + console.log('The Editor has initialized.'); + this.loading = false; + }); + }, readonly: this.emailStatus === 'SENT', - height : '400', - width : '1100', + height: '400', suffix: '.min', language: this.lang.langISO.replace('-', '_'), language_url: `../../node_modules/tinymce-i18n/langs/${this.lang.langISO.replace('-', '_')}.js`, diff --git a/src/frontend/lang/lang-en.ts b/src/frontend/lang/lang-en.ts index 048f2816db2781fc862d10105859d0f5282021cf..091a1bc84bbbc43c85e616941ffc3d74545f4f42 100755 --- a/src/frontend/lang/lang-en.ts +++ b/src/frontend/lang/lang-en.ts @@ -1594,5 +1594,13 @@ export const LANG_EN = { "diffNoDestmsg" : "No <b>assignee</b> defined, you <b>can't</b> modify the diffusion list.", "noDest" : "No <b>assignee</b> defined", "noPrivileges" : "Insufficient privileges", - "mustDeleteUsersWithNoPrivileges" : "You must delete users with insufficient privileges", + "mustDeleteUsersWithNoPrivileges" : "You must delete users with insufficient privileges", + "sendNumericPackage" : "Send a numeric package", + "sendNumericPackageInfo" : "You can send mail and attachments between <b> two instances </b> of Maarch Courrier. <br /> <br /> You will receive <b> an acknowledgment of receipt </b> and you will be able <b> follow the progress </b> of the mail at the recipient. (see <a href=\"{0}\" target=\"_blank\"> Maarch2GEC </a> for more information).", + "mailSubject" : "Mail subject", + "attachItemToNumericPackage" : "Attach an element to sending mail", + "note" : "Note", + "addNoteToNumericPackage" : "Add a note Ajouter to sending mail", + "mainDocNumericPackage" : "Main document of sending mail", + "attachmentsNumericPackage" : "Attachments of sending mail", }; diff --git a/src/frontend/lang/lang-fr.ts b/src/frontend/lang/lang-fr.ts index 24843307aea482deb1eecbf4bd523ac35a9f3600..5c671db83d1a17da73199aa70c84846c9824102b 100755 --- a/src/frontend/lang/lang-fr.ts +++ b/src/frontend/lang/lang-fr.ts @@ -1292,6 +1292,7 @@ export const LANG_FR = { "openedInExternalModal" : "Ouvert dans une fenÃĒtre", "showMore" : "Voir plus", "notesAlt" : "Annotations", + "note" : "Annotation", "mailsSentAlt" : "ÃlÊments envoyÊs", "newsFeed" : "Fil d'actualitÊ", "sendError" : "Erreur d'envoi", @@ -1633,5 +1634,12 @@ export const LANG_FR = { "diffNoDestmsg" : "Aucun <b>atttributaire</b> dÊfini, modification de la liste <b>impossible</b>.", "noDest" : "Aucun <b>atttributaire</b> dÊfini", "noPrivileges" : "Privilèges insuffisants", - "mustDeleteUsersWithNoPrivileges" : "Veuillez supprimer les personnes dont les privilèges sont insuffisants", + "mustDeleteUsersWithNoPrivileges" : "Veuillez supprimer les personnes dont les privilèges sont insuffisants", + "sendNumericPackage" : "Envoyer un pli numÊrique", + "sendNumericPackageInfo" : "Vous pouvez envoyer un courrier et des pièces jointes entre <b>deux instances</b> de Maarch Courrier.<br /><br />Vous reçeverez <b>un accusÊ de rÊception</b> et vous pourrez <b>suivre l'avancement</b> du courrier chez le destinataire. (voir <a href=\"{0}\" target=\"_blank\">Maarch2GEC</a> pour plus d'informations).", + "mailSubject" : "Objet du courrier", + "attachItemToNumericPackage" : "Attacher un document / une pièce jointe au courrier à transmettre", + "addNoteToNumericPackage" : "Ajouter une annotation au courrier à transmettre", + "mainDocNumericPackage" : "Document principal du courrier à transmettre", + "attachmentsNumericPackage" : "Pièces attachÊs du courrier à transmettre", }; diff --git a/src/frontend/lang/lang-nl.ts b/src/frontend/lang/lang-nl.ts index 88224385af1a1dd6d7b0f1fcbe3bdf362cc11b1f..9a3db7f24b21cb48607d1a44bb57501ccc69ecf7 100755 --- a/src/frontend/lang/lang-nl.ts +++ b/src/frontend/lang/lang-nl.ts @@ -1620,4 +1620,12 @@ export const LANG_NL = { "noDest" : "No <b>assignee</b> defined", //_TO_TRANSLATE "noPrivileges" : "Insufficient privileges", //_TO_TRANSLATE "mustDeleteUsersWithNoPrivileges" : "You must delete users with insufficient privileges", //_TO_TRANSLATE + "sendNumericPackage" : "Send a numeric package", //_TO_TRANSLATE + "sendNumericPackageInfo" : "You can send mail and attachments between <b> two instances </b> of Maarch Courrier. <br /> <br /> You will receive <b> an acknowledgment of receipt </b> and you will be able <b> follow the progress </b> of the mail at the recipient. (see <a href=\"{0}\" target=\"_blank\"> Maarch2GEC </a> for more information).", //_TO_TRANSLATE + "mailSubject" : "Mail subject", //_TO_TRANSLATE + "attachItemToNumericPackage" : "Attach an element to sending mail", //_TO_TRANSLATE + "note" : "Note", //_TO_TRANSLATE + "addNoteToNumericPackage" : "Add a note Ajouter to sending mail", //_TO_TRANSLATE + "mainDocNumericPackage" : "Main document of sending mail", //_TO_TRANSLATE + "attachmentsNumericPackage" : "Attachments of sending mail", //_TO_TRANSLATE };