Commit 67e5af45 authored by Alex ORLUC's avatar Alex ORLUC
Browse files

FIX #16956 TIME 4 add correspondents suggestion + autocomplete correspondents in otp creation

parent 9e763a2c
......@@ -2,7 +2,7 @@
<div class="fieldsetdContainer" style="flex:2">
<h2>{{'lang.visaWorkflow' | translate}} :</h2>
<div class="fieldsetdContent">
<app-external-visa-workflow #appExternalVisaWorkflow [adminMode]="true" [injectDatas]="injectDatasParam"></app-external-visa-workflow >
<app-external-visa-workflow #appExternalVisaWorkflow [adminMode]="true" [resId]="resIds.length === 1 ? resIds[0] : null" [injectDatas]="injectDatasParam"></app-external-visa-workflow >
<div *ngIf="appExternalVisaWorkflow.checkExternalSignatoryBook().length > 0"
class="alert-message alert-message-danger" role="alert">
<b>{{appExternalVisaWorkflow.checkExternalSignatoryBook().join(', ')}}</b>
......
......@@ -18,6 +18,7 @@ export class MaarchParaphComponent implements OnInit {
@ViewChild('appExternalVisaWorkflow', { static: true }) appExternalVisaWorkflow: ExternalVisaWorkflowComponent;
@Input() resIds: number[] = [];
@Input() resourcesToSign: any[] = [];
@Input() additionalsInfos: any;
@Input() externalSignatoryBookDatas: any;
......
......@@ -44,6 +44,7 @@
[externalSignatoryBookDatas]="externalSignatoryBookDatas">
</app-fast-paraph>
<app-maarch-paraph #maarchParapheur *ngIf="signatoryBookEnabled=='maarchParapheur'"
[resIds]="data.resIds"
[resourcesToSign]="resourcesToSign"
[additionalsInfos]="additionalsInfos"
[externalSignatoryBookDatas]="externalSignatoryBookDatas">
......
......@@ -7,52 +7,95 @@
<div *ngIf="!loading" class="userForm">
<mat-form-field *ngIf="sources.length > 1" appearance="fill">
<mat-label>{{ 'lang.source' | translate }}</mat-label>
<mat-select #source [(ngModel)]="userOTP.sourceId" (selectionChange)="setCurrentSource($event.value)" required>
<mat-option *ngFor="let source of sources" [value]="source.id" >
<mat-select #source [(ngModel)]="userOTP.sourceId" (selectionChange)="setCurrentSource($event.value)"
required>
<mat-option *ngFor="let source of sources" [value]="source.id">
{{ source.label }}
</mat-option>
</mat-select>
</mat-form-field>
<div style="display: flex;margin-bottom: 10px;" *ngIf="searchMode">
<div style="flex: 1;">
<app-contact-autocomplete [exclusion]="'?noEntities=true&noContactsGroups=true'"
[inputMode]="true" style="width:100%;padding-bottom:10px;" (afterSelected)="getContact($event)">
</app-contact-autocomplete>
</div>
<div>
<button mat-icon-button color="primary" [title]="'lang.viewSuggestions' | translate" (click)="searchMode=!searchMode">
<mat-icon class="fa fa-users"></mat-icon>
</button>
</div>
</div>
<ng-container *ngIf="!searchMode">
<div class="formType">
<div class="formType-title">
{{'lang.suggestedCorrespondents' | translate}}
</div>
<div class="formType-content">
<div class="correspondent-list">
<div *ngIf="correspondentShorcuts.length === 0" class="empty">
{{'lang.noSuggestion' | translate}}
</div>
<mat-chip-list>
<mat-chip *ngFor="let shortcut of correspondentShorcuts" [title]="shortcut.title"
(click)="setOtpInfoFromShortcut(shortcut)" style="cursor: pointer;font-size: 12px;min-height: 24px;">
{{shortcut.label}}
</mat-chip>
</mat-chip-list>
</div>
<div>
<button mat-icon-button color="primary" [title]="'lang.searchCorrespondent' | translate" (click)="searchMode=!searchMode">
<mat-icon class="fa fa-search"></mat-icon>
</button>
</div>
</div>
</div>
</ng-container>
<mat-form-field appearance="fill">
<mat-label>{{'lang.firstname' | translate}}</mat-label>
<input matInput #firstname type="text" maxlength="128" [(ngModel)]="userOTP.firstname" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'lang.lastname' | translate}}</mat-label>
<input matInput #lastname type="text" maxlength="128" [(ngModel)]="userOTP.lastname" required>
<input matInput #lastname type="text" maxlength="128" [(ngModel)]="userOTP.lastname" required>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'lang.phoneNumber' | translate}}</mat-label>
<input matInput #phoneNumber type="text" [(ngModel)]="userOTP.phone" pattern="^((\+)33)[1-9](\d{2}){4}$" required>
<mat-hint align="start">{{ 'lang.frFormatPhone' | translate }}</mat-hint>
<input matInput #phoneNumber type="text" [(ngModel)]="userOTP.phone"
(ngModelChange)="formatPhone($event)" pattern="^((\+)33)[1-9](\d{2}){4}$" required>
<mat-hint align="end">{{ 'lang.frFormatPhone' | translate }}</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'lang.email' | translate}}</mat-label>
<input matInput #email type="email" [(ngModel)]="userOTP.email" pattern="(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" required>
<input matInput #email type="email" [(ngModel)]="userOTP.email"
pattern="(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" required>
</mat-form-field>
<mat-label>{{'lang.role' | translate}}</mat-label>
<mat-form-field>
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>{{'lang.role' | translate}}</mat-label>
<input matInput style="display: none;">
<mat-radio-group #role style="display: inline-flex;flex-direction: row;" [(ngModel)]="userOTP.role" required>
<mat-radio-button *ngFor="let role of availableRoles" [value]="role.id" style="margin-right: 20px;" color="primary">
<mat-radio-group #role style="display: flex" [(ngModel)]="userOTP.role" required>
<mat-radio-button *ngFor="let role of availableRoles" [value]="role.id" style="flex:1"
color="primary">
{{ role.label }}
</mat-radio-button>
</mat-radio-group >
</mat-radio-group>
</mat-form-field>
<mat-form-field *ngIf="currentSource.length > 1 && userOTP.role !== 'visa'" appearance="fill">
<mat-label>{{ 'lang.securityMode' | translate }}</mat-label>
<mat-select #securityMode [(ngModel)]="userOTP.security"required>
<mat-option *ngFor="let mode of currentSource" [value]="mode" >
<mat-select #securityMode [(ngModel)]="userOTP.security" required>
<mat-option *ngFor="let mode of currentSource" [value]="mode">
{{ 'lang.' + mode | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<app-maarch-message *ngIf="userOTP.security && userOTP.role !== 'visa'" [mode]="'info'" [content]="'lang.securityModeInfo' | translate : {mode: userOTP.security}"></app-maarch-message>
<app-maarch-message *ngIf="userOTP.security && userOTP.role !== 'visa'" [mode]="'info'"
[content]="'lang.securityModeInfo' | translate : {mode: userOTP.security}"></app-maarch-message>
</div>
</div>
<span class="divider-modal"></span>
<div mat-dialog-actions class="actions">
<button mat-raised-button mat-button color="primary" [disabled]="loading || !isValidForm()" (click)="addOtpUser()">{{ (this.data === null ? 'lang.validate' : 'lang.update') | translate}}</button>
<button mat-raised-button mat-button color="primary" [disabled]="loading || !isValidForm()"
(click)="addOtpUser()">{{ (this.data === null ? 'lang.validate' : 'lang.update') | translate}}</button>
<button mat-raised-button mat-button [mat-dialog-close]="">{{'lang.cancel' | translate}}</button>
</div>
</div>
</div>
\ No newline at end of file
......@@ -8,3 +8,46 @@
height: 100%;
flex-direction:column
}
.formType {
align-items: center;
display: flex;
margin-bottom: 10px;
border-radius: 4px;
border: solid 1px #ccc;
position: relative;
&-title {
white-space: pre;
overflow: hidden;
max-width: 85%;
text-overflow: ellipsis;
z-index: 1;
font-size: 10px;
font-weight: bold;
background: white;
position: absolute;
top: -7px;
left: 10px;
padding: 0px;
margin: 0px;
color: #135f7f;
}
&-content {
display: flex;
align-items: center;
margin-top: 10px;
}
}
.correspondent-list {
max-height: 100px;
overflow: auto;
padding: 10px;
}
.empty {
color: #666;
text-align: center;
font-style: italic;
}
\ No newline at end of file
......@@ -6,10 +6,12 @@ import { FunctionsService } from '@service/functions.service';
import { tap, catchError } from 'rxjs/operators';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { of } from 'rxjs';
import { ContactService } from '@service/contact.service';
@Component({
templateUrl: 'create-external-user.component.html',
styleUrls: ['create-external-user.component.scss'],
providers: [ContactService]
})
export class CreateExternalUserComponent implements OnInit {
......@@ -52,20 +54,30 @@ export class CreateExternalUserComponent implements OnInit {
availableRoles: this.availableRoles.map((role: any) => role.id)
};
correspondentShorcuts: any[] = [];
loading: boolean = true;
searchMode: boolean = false;
constructor(
public translate: TranslateService,
public http: HttpClient,
public functions: FunctionsService,
private dialogRef: MatDialogRef<CreateExternalUserComponent>,
public notify: NotificationService,
private contactService: ContactService,
@Inject(MAT_DIALOG_DATA) public data: any
) { }
async ngOnInit(): Promise<void> {
await this.getConfig();
if (this.data.resId !== null) {
this.getCorrespondents();
} else {
this.searchMode = true;
}
}
getConfig() {
......@@ -74,12 +86,12 @@ export class CreateExternalUserComponent implements OnInit {
tap((data: any) => {
if (data) {
this.sources = data.otp;
this.setCurrentSource(this.data !== null ? this.data.sourceId : this.sources[0].id);
if (this.data === null) {
this.setCurrentSource(this.data.otpInfo !== null ? this.data.otpInfo.sourceId : this.sources[0].id);
if (this.data.otpInfo === null) {
this.userOTP.sourceId = this.sources[0].id;
this.userOTP.type = this.sources[0].type;
} else {
this.userOTP = this.data;
this.userOTP = this.data.otpInfo;
}
}
this.loading = false;
......@@ -113,4 +125,109 @@ export class CreateExternalUserComponent implements OnInit {
this.currentSource = [... new Set(selectedSource.securityModes)];
this.userOTP.security = this.currentSource[0];
}
getContact(item: any) {
if (item.type === 'user') {
this.http.get('../rest/users/' + item.id).pipe(
tap((data: any) => {
this.userOTP.firstname = data.firstname;
this.userOTP.lastname = data.lastname;
this.userOTP.email = data.mail;
this.userOTP.phone = data.phone !== undefined ? data.phone.replace(/( |\.|\-)/g, '').replace('0', '+33') : '';
}),
catchError((err: any) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
} else if (item.type === 'contact') {
this.http.get('../rest/contacts/' + item.id).pipe(
tap((data: any) => {
this.userOTP.firstname = data.firstname;
this.userOTP.lastname = data.lastname;
this.userOTP.email = data.email;
this.userOTP.phone = data.phone.replace(/( |\.|\-)/g, '').replace('0', '+33');
}),
catchError((err: any) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
}
formatPhone(item: any) {
if (item.length > 1 && item[0] === '0') {
this.userOTP.phone = this.userOTP.phone.replace('0', '+33');
}
}
getCorrespondents() {
this.http.get(`../rest/resources/${this.data.resId}?light=true`).pipe(
tap((data: any) => {
if (data.categoryId === 'outgoing') {
data.recipients.forEach((element: any) => {
this.setCorrespondentsShorcuts(element, 'recipient');
});
} else {
data.senders.forEach((element: any) => {
this.setCorrespondentsShorcuts(element, 'sender');
});
}
}),
catchError((err) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
setCorrespondentsShorcuts(item: any, itemCategory: string) {
let objCorr = {};
if (item.type === 'user') {
this.http.get('../rest/users/' + item.id).pipe(
tap((data: any) => {
objCorr = {
title : this.translate.instant('lang.' + itemCategory),
label: this.contactService.formatContact(data),
firstname: data.firstname,
lastname: data.lastname,
email: data.mail,
phone: !this.functions.empty(data.phone) ? data.phone.replace(/( |\.|\-)/g, '').replace('0', '+33') : ''
};
this.correspondentShorcuts.push(objCorr);
}),
catchError((err: any) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
} else if (item.type === 'contact') {
this.http.get('../rest/contacts/' + item.id).pipe(
tap((data: any) => {
objCorr = {
title : this.translate.instant('lang.' + itemCategory),
label: this.contactService.formatContact(data),
firstname: data.firstname,
lastname: data.lastname,
email: data.email,
phone: !this.functions.empty(data.phone) ? data.phone.replace(/( |\.|\-)/g, '').replace('0', '+33') : ''
};
this.correspondentShorcuts.push(objCorr);
}),
catchError((err: any) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
}
setOtpInfoFromShortcut(item: any) {
this.userOTP.firstname = item.firstname;
this.userOTP.lastname = item.lastname;
this.userOTP.email = item.email;
this.userOTP.phone = item.phone;
}
}
......@@ -362,7 +362,7 @@ export class ExternalVisaWorkflowComponent implements OnInit {
panelClass: 'maarch-modal',
disableClose: true,
width: '500px',
data: objToSend
data: { otpInfo : objToSend, resId : this.resId}
});
dialogRef.afterClosed().pipe(
tap(async (data: any) => {
......
......@@ -2506,5 +2506,9 @@
"otp_visa_yousign": "Viseur (yousign)",
"otp_sign_yousign": "Signataire (yousign)",
"source": "Source",
"updateOtp": "Modifier l'utilisateur externe"
"updateOtp": "Modifier l'utilisateur externe",
"suggestedCorrespondents": "Correspondant(s) suggéré(s)",
"searchCorrespondent": "Rechercher un correspondant",
"viewSuggestions": "Voir les suggestions",
"noSuggestion": "Aucune suggestion"
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment