diff --git a/src/frontend/app/administration/group/group-administration.component.html b/src/frontend/app/administration/group/group-administration.component.html index b587345ab5d11fd13c0a377fdb91a39d11d97be7..6325c66399c7418136a2db0a9ff0f50ca8b3a587 100755 --- a/src/frontend/app/administration/group/group-administration.component.html +++ b/src/frontend/app/administration/group/group-administration.component.html @@ -80,24 +80,7 @@ <mat-tab label="{{lang.relatedUsers}}" *ngIf="!creationMode"> <div class="row" style="margin:0px;"> <div class="col-md-12" *ngIf="group.canAdminUsers" style="padding:5px;"> - <mat-form-field> - <span matPrefix><mat-icon class="fa fa-user-plus" color="primary"></mat-icon> </span> - <input class="autocompleteSearch" #autocompleteFilter placeholder="{{lang.linkUser}}" type="text" matInput [matAutocomplete]="auto" - [formControl]="userCtrl"> - <mat-autocomplete #auto="matAutocomplete"> - <mat-option *ngFor="let user of filteredUsers | async" [value]="user.idToDisplay" (click)="linkUser(user)"> - <p mat-line style="margin:0;"> - <span class="col-xm-1" style="padding-right:5px;"> - <mat-icon color="primary" [class]="user.type == 'entity' ? 'fa fa-sitemap fa-2x' : 'fa fa-user fa-2x'" style="margin-right:0px;"></mat-icon> - </span> - <span class="col-xm-11"> - {{ user.idToDisplay }} - <small>{{ user.otherInfo }}</small> - </span> - </p> - </mat-option> - </mat-autocomplete> - </mat-form-field> + <plugin-autocomplete [labelPlaceholder]="lang.linkUser" [labelList]="lang.availableUsers" [routeDatas]="'/rest/autocomplete/users'" [targetSearchKey]="'idToDisplay'" [subInfoKey]="'id'" (triggerEvent)="linkUser($event)"></plugin-autocomplete> <hr/> </div> <div class="col-md-6 col-xs-6"> diff --git a/src/frontend/app/administration/group/group-administration.component.ts b/src/frontend/app/administration/group/group-administration.component.ts index 74168ffefa8de4ee3327cd0e4ed85606e7bf6cee..a33f6b149bb049392dc368b764ddf3231fb96fb5 100755 --- a/src/frontend/app/administration/group/group-administration.component.ts +++ b/src/frontend/app/administration/group/group-administration.component.ts @@ -7,8 +7,6 @@ import { NotificationService } from '../../notification.service'; import { HeaderService } from '../../../service/header.service'; import { MatPaginator, MatTableDataSource, MatSort, MatSidenav} from '@angular/material'; -import { AutoCompletePlugin } from '../../../plugins/autocomplete.plugin'; - declare function $j(selector: any) : any; declare const angularGlobals : any; @@ -17,7 +15,7 @@ declare const angularGlobals : any; templateUrl: "group-administration.component.html", providers : [NotificationService] }) -export class GroupAdministrationComponent extends AutoCompletePlugin implements OnInit { +export class GroupAdministrationComponent implements OnInit { /*HEADER*/ @ViewChild('snav') public sidenavLeft : MatSidenav; @ViewChild('snav2') public sidenavRight : MatSidenav; @@ -57,7 +55,6 @@ export class GroupAdministrationComponent extends AutoCompletePlugin implements } constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher,public http: HttpClient, private route: ActivatedRoute, private router: Router, private notify: NotificationService, private headerService: HeaderService) { - super(http, ['adminUsers']); $j("link[href='merged_css.php']").remove(); this.mobileQuery = media.matchMedia('(max-width: 768px)'); this._mobileQueryListener = () => changeDetectorRef.detectChanges(); @@ -137,13 +134,11 @@ export class GroupAdministrationComponent extends AutoCompletePlugin implements } linkUser(newUser:any) { - this.userCtrl.setValue(''); - $j('.autocompleteSearch').blur(); var groupReq = { "groupId" : this.group.group_id, "role" : this.group.role }; - this.http.post(this.coreUrl + "rest/users/" + newUser.id + "/groups", groupReq) + this.http.post(this.coreUrl + "rest/users/" + newUser.serialId + "/groups", groupReq) .subscribe(() => { var displayName = newUser.idToDisplay.split(" "); var user = { diff --git a/src/frontend/app/administration/group/indexing/indexing-administration.component.scss b/src/frontend/app/administration/group/indexing/indexing-administration.component.scss index 75b16112ed867f7a9a388aeef93c4f89dddb78f9..08db799bed293ccfc7f1c2e0f9d8ba62b2c3e366 100644 --- a/src/frontend/app/administration/group/indexing/indexing-administration.component.scss +++ b/src/frontend/app/administration/group/indexing/indexing-administration.component.scss @@ -1,7 +1,3 @@ -/deep/.mat-form-field-appearance-outline .mat-form-field-outline-thick { - color: #666; -} - .selectedActionList { width: 100%; } diff --git a/src/frontend/lang/lang-en.ts b/src/frontend/lang/lang-en.ts index 7c00b0eb1e145de4e06540e9f07437f1ba2e3ec5..c4a25a3943359189e810ca88aa7e20d5104f0163 100755 --- a/src/frontend/lang/lang-en.ts +++ b/src/frontend/lang/lang-en.ts @@ -1030,5 +1030,10 @@ export const LANG_EN = { "updateInProgress" : "Update in progress ...", "updateInfo" : "This action cannot be undone! Do not make any actions during process.", "isAvailable" : "is available", - "updateWarn" : "Update is not possible.<br/>These files are modified : <br/>" + "updateWarn" : "Update is not possible.<br/>These files are modified : <br/>", + "chooseValue" : "Choose a value", + "availableValues" : "Available values", + "noAvailableValue" : "No available value", + "autocompleteInfo" : "The criteria must contain at least <b>3 letters</b>", + "availableUsers" : "Available users", }; diff --git a/src/frontend/lang/lang-fr.ts b/src/frontend/lang/lang-fr.ts index 3992bcb34d9b7b81ea399ac6a60d0e124afb1a99..6a2327244eda0695f5dc89f98fbc6f516ac6f76d 100755 --- a/src/frontend/lang/lang-fr.ts +++ b/src/frontend/lang/lang-fr.ts @@ -1063,5 +1063,10 @@ export const LANG_FR = { "updateInProgress" : "Mise à jour en cours ...", "updateInfo" : "Cette action est irréversible ! Il est préférable de ne pas faire d'actions durant le processus et qu'aucun utilisateur ne soit connecté.", "isAvailable" : "est disponible", - "updateWarn" : "<b>Impossible</b> d'effectuer la mise à jour. Voici les fichiers modifiés :<br/><br/>" + "updateWarn" : "<b>Impossible</b> d'effectuer la mise à jour. Voici les fichiers modifiés :<br/><br/>", + "chooseValue" : "Choisissez une valeur", + "availableValues" : "Valeur(s) disponible(s)", + "noAvailableValue" : "Aucun élément disponible", + "autocompleteInfo" : "La recherche doit contenir au minimum <b>3 caractères</b>", + "availableUsers" : "Utilisateur(s) disponible(s)", }; \ No newline at end of file diff --git a/src/frontend/lang/lang-nl.ts b/src/frontend/lang/lang-nl.ts index 57b79471769122a9cfb7ea3f4627fe76b34afd4b..40866eb569a7955985373b56dfe28511c2bcc13e 100755 --- a/src/frontend/lang/lang-nl.ts +++ b/src/frontend/lang/lang-nl.ts @@ -1056,5 +1056,10 @@ export const LANG_NL = { "updateInProgress" : "Update in progress ...", //_TO_TRANSLATE "updateInfo" : "This action cannot be undone! Do not make any actions during process.", //_TO_TRANSLATE "isAvailable" : "is available", //_TO_TRANSLATE - "updateWarn" : "Update is not possible.<br/>These files are modified : <br/>" + "updateWarn" : "Update is not possible.<br/>These files are modified : <br/>", //_TO_TRANSLATE + "chooseValue" : "Choose a value", //_TO_TRANSLATE + "availableValues" : "Available values", //_TO_TRANSLATE + "noAvailableValue" : "No available value", //_TO_TRANSLATE + "autocompleteInfo" : "The criteria must contain at least <b>3 letters</b>", //_TO_TRANSLATE + "availableUsers" : "Available users", //_TO_TRANSLATE }; diff --git a/src/frontend/plugins/autocomplete/autocomplete.component.html b/src/frontend/plugins/autocomplete/autocomplete.component.html index dba5ab6ffcbf1355715facfadcfffe662f725566..e78b74f5d003da028fcd09a25c968f215da06c4d 100644 --- a/src/frontend/plugins/autocomplete/autocomplete.component.html +++ b/src/frontend/plugins/autocomplete/autocomplete.component.html @@ -1,14 +1,18 @@ <form> - <mat-form-field appearance="outline"> - <input type="text" #autoCompleteInput [placeholder]="placeholder" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto" (focus)="myControl.setValue('')"> + <mat-form-field appearance="outline"> + <mat-icon color="primary" class="fa fa-search" matPrefix></mat-icon> + <input type="text" #autoCompleteInput [placeholder]="placeholder" aria-label="Number" matInput [formControl]="myControl" + [matAutocomplete]="auto" (focus)="resetAutocomplete()"> <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selectOpt($event)"> - <mat-optgroup [label]="optGroupLabel" *ngIf="options.length > 0"> - <mat-option *ngFor="let option of filteredOptions | async | sortBy: key" [value]="option"> - {{option[key]}} - </mat-option> + <mat-optgroup [label]="optGroupLabel" *ngIf="options.length > 0 && !loading"> + <mat-option *ngFor="let option of filteredOptions | async | sortBy: key" [value]="option"> + <span color="primary">{{option[key]}}</span> <small>{{option[subInfoKey]}}</small> + </mat-option> </mat-optgroup> - <mat-option *ngIf="options.length === 0" disabled> - Aucun élément disponible + <mat-option *ngIf="options.length === 0 && !loading" disabled [innerHTML]="listInfo"> + </mat-option> + <mat-option *ngIf="loading" disabled> + <mat-spinner diameter="20"></mat-spinner> </mat-option> </mat-autocomplete> </mat-form-field> diff --git a/src/frontend/plugins/autocomplete/autocomplete.component.scss b/src/frontend/plugins/autocomplete/autocomplete.component.scss index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c1033720f9b990664abe011d7593b0f0e2803b71 100644 --- a/src/frontend/plugins/autocomplete/autocomplete.component.scss +++ b/src/frontend/plugins/autocomplete/autocomplete.component.scss @@ -0,0 +1,15 @@ +@import '../../css/vars.scss'; + +/deep/.mat-form-field-appearance-outline .mat-form-field-outline-thick { + color: $primary; +} + +/deep/.mat-form-field-prefix { + top: 0px !important; +} +.mat-option { + span { + font-size: 110%; + font-weight: bold; + } +} \ No newline at end of file diff --git a/src/frontend/plugins/autocomplete/autocomplete.component.ts b/src/frontend/plugins/autocomplete/autocomplete.component.ts index 6fe36de0d4a088687dab4dba9713e011573c1857..85a8611599e481f54b6308ddf12ef351f860ef12 100644 --- a/src/frontend/plugins/autocomplete/autocomplete.component.ts +++ b/src/frontend/plugins/autocomplete/autocomplete.component.ts @@ -1,9 +1,11 @@ import { Component, OnInit } from '@angular/core'; import { FormControl } from '@angular/forms'; import { Input, EventEmitter, Output, ViewChild, ElementRef } from '@angular/core'; -import { Observable } from 'rxjs'; -import { map, startWith } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { map, startWith, debounceTime, filter, distinctUntilChanged, switchMap, tap } from 'rxjs/operators'; import { LatinisePipe } from 'ngx-pipes'; +import { LANG } from '../../app/translate.component'; +import { HttpClient } from '@angular/common/http'; @Component({ selector: 'plugin-autocomplete', @@ -11,25 +13,42 @@ import { LatinisePipe } from 'ngx-pipes'; styleUrls: ['autocomplete.component.scss'], }) export class PluginAutocomplete implements OnInit { + lang: any = LANG; myControl = new FormControl(); + loading = false; + + listInfo: string; @Input('datas') options: any; + @Input('routeDatas') routeDatas: string; @Input('labelPlaceholder') placeholder: string; @Input('labelList') optGroupLabel: string; @Input('targetSearchKey') key: string; + @Input('subInfoKey') subInfoKey: string; @Output('triggerEvent') selectedOpt = new EventEmitter(); @ViewChild('autoCompleteInput') autoCompleteInput: ElementRef; filteredOptions: Observable<string[]>; - constructor(private latinisePipe: LatinisePipe + constructor( + public http: HttpClient, + private latinisePipe: LatinisePipe ) { } ngOnInit() { - this.optGroupLabel = this.optGroupLabel === undefined ? 'Valeurs disponibles' : this.optGroupLabel; - this.placeholder = this.placeholder === undefined ? 'Choisissez une valeur' : this.placeholder; + this.optGroupLabel = this.optGroupLabel === undefined ? this.lang.availableValues : this.optGroupLabel; + this.placeholder = this.placeholder === undefined ? this.lang.chooseValue : this.placeholder; + if (this.routeDatas !== undefined) { + this.initAutocompleteRoute(); + } else { + this.initAutocompleteData(); + } + } + + initAutocompleteData() { + this.listInfo = this.lang.noAvailableValue; this.filteredOptions = this.myControl.valueChanges .pipe( startWith(''), @@ -37,12 +56,39 @@ export class PluginAutocomplete implements OnInit { ); } + initAutocompleteRoute() { + this.listInfo = this.lang.autocompleteInfo; + this.options = []; + this.myControl.valueChanges + .pipe( + debounceTime(300), + filter(value => value.length > 2), + distinctUntilChanged(), + tap(() => this.loading = true), + switchMap((data: any) => this.http.get('../..' + this.routeDatas, { params: { "search": data } })), + tap((data: any) => { + this.listInfo = data.length === 0 ? this.lang.noAvailableValue : ''; + this.options = data; + this.filteredOptions = of(this.options); + this.loading = false; + }) + ).subscribe(); + } + selectOpt(ev: any) { - this.myControl.setValue(''); + this.resetAutocomplete(); this.autoCompleteInput.nativeElement.blur(); this.selectedOpt.emit(ev.option.value); } + resetAutocomplete() { + this.myControl.setValue(''); + if (this.routeDatas !== undefined) { + this.options = []; + this.listInfo = this.lang.autocompleteInfo; + } + } + private _filter(value: string): string[] { if (typeof value === 'string') { const filterValue = this.latinisePipe.transform(value.toLowerCase());