From 8e897cf7b2682a58ed46ee9a8a008502e5d0dde3 Mon Sep 17 00:00:00 2001
From: Alex ORLUC <alex.orluc@maarch.org>
Date: Fri, 6 Nov 2020 12:25:05 +0100
Subject: [PATCH] FEAT #15350 TIME 1:15 fix redirect indexing models modal
 (performance issues)

---
 ...ndexing-models-administration.component.ts |  59 ++---
 .../redirect-indexing-model.component.html    |  88 ++++---
 .../redirect-indexing-model.component.ts      | 214 +++++++++++-------
 3 files changed, 198 insertions(+), 163 deletions(-)

diff --git a/src/frontend/app/administration/indexingModel/indexing-models-administration.component.ts b/src/frontend/app/administration/indexingModel/indexing-models-administration.component.ts
index f29489a8715..de62dd2ea54 100644
--- a/src/frontend/app/administration/indexingModel/indexing-models-administration.component.ts
+++ b/src/frontend/app/administration/indexingModel/indexing-models-administration.component.ts
@@ -11,7 +11,7 @@ import { ConfirmComponent } from '../../../plugins/modal/confirm.component';
 import { MatDialogRef, MatDialog } from '@angular/material/dialog';
 import { FunctionsService } from '@service/functions.service';
 import { of } from 'rxjs';
-import {RedirectIndexingModelComponent} from './redirectIndexingModel/redirect-indexing-model.component';
+import { RedirectIndexingModelComponent } from './redirectIndexingModel/redirect-indexing-model.component';
 import { AdministrationService } from '../administration.service';
 
 @Component({
@@ -23,7 +23,7 @@ export class IndexingModelsAdministrationComponent implements OnInit {
 
     @ViewChild('adminMenuTemplate', { static: true }) adminMenuTemplate: TemplateRef<any>;
 
-    
+
     search: string = null;
 
     indexingModels: any[] = [];
@@ -56,7 +56,7 @@ export class IndexingModelsAdministrationComponent implements OnInit {
 
         this.loading = true;
 
-        this.http.get('../rest/indexingModels?showDisabled=true&admin=true').pipe(
+        this.http.get('../rest/indexingModels?showDisabled=true').pipe(
             map((data: any) => {
                 return data.indexingModels.filter((info: any) => info.private === false);
             }),
@@ -76,46 +76,23 @@ export class IndexingModelsAdministrationComponent implements OnInit {
     }
 
     delete(indexingModel: any) {
+        this.dialogRef = this.dialog.open(RedirectIndexingModelComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { indexingModel: indexingModel } });
 
-        if (indexingModel.used.length === 0) {
-            this.dialogRef = this.dialog.open(ConfirmComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.translate.instant('lang.delete'), msg: this.translate.instant('lang.confirmAction') } });
-
-            this.dialogRef.afterClosed().pipe(
-                filter((data: string) => data === 'ok'),
-                exhaustMap(() => this.http.delete('../rest/indexingModels/' + indexingModel.id)),
-                tap(() => {
-                    for (const i in this.indexingModels) {
-                        if (this.indexingModels[i].id === indexingModel.id) {
-                            this.indexingModels.splice(Number(i), 1);
-                        }
-                    }
-                    this.adminService.setDataSource('admin_indexing_models', this.indexingModels, this.sort, this.paginator, this.filterColumns);
-                    this.notify.success(this.translate.instant('lang.indexingModelDeleted'));
-                }),
-                catchError((err: any) => {
-                    this.notify.handleSoftErrors(err);
-                    return of(false);
-                })
-            ).subscribe();
-        } else {
-            this.dialogRef = this.dialog.open(RedirectIndexingModelComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.translate.instant('lang.delete'), msg: this.translate.instant('lang.confirmAction'), indexingModel: indexingModel } });
-
-            this.dialogRef.afterClosed().pipe(
-                filter((data: string) => data === 'ok'),
-                tap(() => {
-                    for (const i in this.indexingModels) {
-                        if (this.indexingModels[i].id === indexingModel.id) {
-                            this.indexingModels.splice(Number(i), 1);
-                        }
+        this.dialogRef.afterClosed().pipe(
+            filter((data: string) => data === 'ok'),
+            tap(() => {
+                for (const i in this.indexingModels) {
+                    if (this.indexingModels[i].id === indexingModel.id) {
+                        this.indexingModels.splice(Number(i), 1);
                     }
-                    this.adminService.setDataSource('admin_indexing_models', this.indexingModels, this.sort, this.paginator, this.filterColumns);
-                }),
-                catchError((err: any) => {
-                    this.notify.handleSoftErrors(err);
-                    return of(false);
-                })
-            ).subscribe();
-        }
+                }
+                this.adminService.setDataSource('admin_indexing_models', this.indexingModels, this.sort, this.paginator, this.filterColumns);
+            }),
+            catchError((err: any) => {
+                this.notify.handleSoftErrors(err);
+                return of(false);
+            })
+        ).subscribe();
     }
 
     disableIndexingModel(indexingModel: any) {
diff --git a/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.html b/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.html
index 2c99d357baf..2107f9b728a 100644
--- a/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.html
+++ b/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.html
@@ -1,40 +1,54 @@
 <div class="mat-dialog-content-container">
-  <h1 mat-dialog-title>{{'lang.indexingModelReassign' | translate}}</h1>
-  <div mat-dialog-content>
-    <div class="alert-message alert-message-info">
-      {{'lang.indexingModelUsedBy' | translate}}
-      <ul>
-        <li *ngFor="let usage of mainIndexingModel.used">
-          <b>{{usage.count}}</b> {{'lang.mailsWithStatus' | translate}} <b>{{usage.status}}</b>
-        </li>
-      </ul>
-      {{'lang.indexingModelReplaceToDelete' | translate}}
-    </div>
-    <div *ngIf="!loading">
-      <mat-form-field style="padding: 10px; margin-top: 15px">
-        <mat-select name="model" [(ngModel)]="modelIds"
-                    [placeholder]="'lang.indexingModel' | translate" required (selectionChange)="changeModel($event)">
-          <mat-option *ngFor="let model of indexingModels;let i=index" [value]="model.id" [disabled]="model.id === mainIndexingModel.id">
-            {{model.label}}
-          </mat-option>
-        </mat-select>
-      </mat-form-field>
-    </div>
-    <div class="alert-message alert-message-danger" *ngIf="resetFields.length !== 0">
-      {{'lang.indexingModelFieldsReset' | translate}}
-      <ul>
-        <li *ngFor="let field of resetFields">
-          {{field.label}}
-        </li>
-      </ul>
+    <h1 mat-dialog-title>{{title | translate}}</h1>
+    <div mat-dialog-content>
+        <ng-container *ngIf="loading; else elseLoading">
+            <mat-spinner style="margin:auto;"></mat-spinner>
+        </ng-container>
+        <ng-template #elseLoading>
+            <ng-container *ngIf="mainIndexingModel.used.length === 0; else elseTemplate">
+                <div class="alert-message alert-message-info" style="margin: 20px 0;" role="alert"
+                    [innerHTML]="'lang.confirmAction' | translate">
+                </div>
+            </ng-container>
+            <ng-template #elseTemplate>
+                <div class="alert-message alert-message-info">
+                    {{'lang.indexingModelUsedBy' | translate}}
+                    <ul>
+                        <li *ngFor="let usage of mainIndexingModel.used">
+                            <b>{{usage.count}}</b> {{'lang.mailsWithStatus' | translate}} <b>{{usage.status}}</b>
+                        </li>
+                    </ul>
+                    {{'lang.indexingModelReplaceToDelete' | translate}}
+                </div>
+                <div>
+                    <mat-form-field style="padding: 10px; margin-top: 15px">
+                        <mat-select name="model" [(ngModel)]="modelIds" [placeholder]="'lang.indexingModel' | translate"
+                            required (selectionChange)="changeModel($event)">
+                            <mat-option *ngFor="let model of indexingModels;let i=index" [value]="model.id"
+                                [disabled]="model.id === mainIndexingModel.id">
+                                {{model.label}}
+                            </mat-option>
+                        </mat-select>
+                    </mat-form-field>
+                </div>
+                <div class="alert-message alert-message-danger" *ngIf="resetFields.length !== 0">
+                    {{'lang.indexingModelFieldsReset' | translate}}
+                    <ul>
+                        <li *ngFor="let field of resetFields">
+                            {{field.label}}
+                        </li>
+                    </ul>
 
-      {{'lang.confirmAction' | translate}}
-    </div>
-    <span class="divider-modal"></span>
-    <div mat-dialog-actions>
-      <button class="actions" color="primary" mat-raised-button [disabled]="selectedModelId === undefined"
-        (click)="onSubmit()">{{'lang.validate' | translate}}</button>
-      <button class="actions" color="" mat-raised-button (click)="this.dialogRef.close('');">{{'lang.cancel' | translate}}</button>
+                    {{'lang.confirmAction' | translate}}
+                </div>
+            </ng-template>
+        </ng-template>
+        <span class="divider-modal"></span>
+        <div mat-dialog-actions>
+            <button class="actions" color="primary" mat-raised-button [disabled]="!isValid()"
+                (click)="onSubmit()">{{'lang.validate' | translate}}</button>
+            <button class="actions" color="" mat-raised-button
+                (click)="this.dialogRef.close('');">{{'lang.cancel' | translate}}</button>
+        </div>
     </div>
-  </div>
-</div>
+</div>
\ No newline at end of file
diff --git a/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.ts b/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.ts
index b2d4e8107c0..98a6cd8a974 100644
--- a/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.ts
+++ b/src/frontend/app/administration/indexingModel/redirectIndexingModel/redirect-indexing-model.component.ts
@@ -14,15 +14,16 @@ import { NotificationService } from '@service/notification/notification.service'
 })
 export class RedirectIndexingModelComponent implements OnInit {
 
-    
-
+    title: string = 'lang.delete';
     indexingModels: any[] = [];
     modelIds: any[] = [];
 
     selectedModelId: any;
     selectedModelFields: any[];
 
-    mainIndexingModel: any;
+    mainIndexingModel: any = {
+        used: []
+    };
     mainIndexingModelFields: any[];
 
     resetFields: any[] = [];
@@ -89,7 +90,7 @@ export class RedirectIndexingModelComponent implements OnInit {
         },
     ];
 
-    loading: boolean = false;
+    loading: boolean = true;
 
     constructor(
         public translate: TranslateService,
@@ -99,98 +100,117 @@ export class RedirectIndexingModelComponent implements OnInit {
         private notify: NotificationService,
         private sortPipe: SortPipe,
     ) {
-        this.mainIndexingModel = data.indexingModel;
+        this.mainIndexingModel.id = data.indexingModel.id;
     }
 
     async ngOnInit() {
-        this.loadIndexingModels();
+        await this.loadIndexingModelFields();
+
+        if (this.mainIndexingModel.used.length > 0) {
+            this.title = 'lang.indexingModelReassign';
+            await this.loadIndexingModels();
+
+            await this.loadStatuses();
 
-        this.loadStatuses();
+            await this.loadCustomFields();
 
-        this.loadCustomFields();
+            this.formatFields();
+        }
 
-        this.loadIndexingModelFields();
+        this.loading = false;
     }
 
     loadIndexingModels() {
-        this.http.get('../rest/indexingModels').pipe(
-            map((data: any) => {
-                return data.indexingModels.filter((info: any) => info.private === false);
-            }),
-            tap((data: any) => {
-                this.indexingModels = data;
+        return new Promise((resolve) => {
+            this.http.get('../rest/indexingModels').pipe(
+                map((data: any) => {
+                    return data.indexingModels.filter((info: any) => info.private === false);
+                }),
+                tap((data: any) => {
+                    this.indexingModels = data;
 
-                this.sortPipe.transform(this.indexingModels, 'label');
+                    this.sortPipe.transform(this.indexingModels, 'label');
 
-                this.modelIds = this.indexingModels.map(model => model.id);
-            }),
-            finalize(() => this.loading = false),
-            catchError((err: any) => {
-                this.notify.handleErrors(err);
-                return of(false);
-            })
-        ).subscribe();
+                    this.modelIds = this.indexingModels.map(model => model.id);
+                }),
+                finalize(() => resolve(true)),
+                catchError((err: any) => {
+                    this.notify.handleErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
     }
 
     loadIndexingModelFields() {
-        this.http.get('../rest/indexingModels/' + this.mainIndexingModel.id).pipe(
-            tap((data: any) => {
-                this.mainIndexingModelFields = data.indexingModel.fields;
-                this.mainIndexingModelFields = this.mainIndexingModelFields.map(field => {
-                    const availableField = this.availableFields.find(elem => elem.identifier === field.identifier);
+        return new Promise((resolve) => {
+            this.http.get('../rest/indexingModels/' + this.mainIndexingModel.id + '?used=true').pipe(
+                tap((data: any) => {
+                    this.mainIndexingModel.used = data.indexingModel.used;
 
-                    field.label = availableField === undefined ? this.translate.instant('lang.undefined') : availableField.label;
-                    return field;
-                });
-            }),
-            catchError((err: any) => {
-                this.notify.handleErrors(err);
-                return of(false);
-            })
-        ).subscribe();
+                    this.mainIndexingModelFields = data.indexingModel.fields;
+                }),
+                tap(() => resolve(true)),
+                catchError((err: any) => {
+                    this.notify.handleErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
+    }
+
+    formatFields() {
+        this.mainIndexingModelFields = this.mainIndexingModelFields.map(field => {
+            const availableField = this.availableFields.find(elem => elem.identifier === field.identifier);
+            field.label = availableField === undefined ? this.translate.instant('lang.undefined') : availableField.label;
+            return field;
+        });
     }
 
     loadStatuses() {
-        this.http.get('../rest/statuses').pipe(
-            tap((data: any) => {
-                this.statuses = data.statuses;
-
-                this.mainIndexingModel.used.forEach((element: any) => {
-                    const elementStatus = this.statuses.find(status => status.id === element.status);
-                    if (elementStatus !== undefined) {
-                        element.status = elementStatus.label_status;
-                    }
-                });
-            }),
-            catchError((err: any) => {
-                this.notify.handleErrors(err);
-                return of(false);
-            })
-        ).subscribe();
+        return new Promise((resolve) => {
+            this.http.get('../rest/statuses').pipe(
+                tap((data: any) => {
+                    this.statuses = data.statuses;
+
+                    this.mainIndexingModel.used.forEach((element: any) => {
+                        const elementStatus = this.statuses.find(status => status.id === element.status);
+                        if (elementStatus !== undefined) {
+                            element.status = elementStatus.label_status;
+                        }
+                    });
+                }),
+                finalize(() => resolve(true)),
+                catchError((err: any) => {
+                    this.notify.handleErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
     }
 
     loadCustomFields() {
-        this.http.get('../rest/customFields').pipe(
-            tap((data: any) => {
-                data.customFields = data.customFields.map((custom: any) => {
-                    return {
-                        identifier: 'indexingCustomField_' + custom.id,
-                        label: custom.label
-                    };
-                });
-                data.customFields.forEach((custom: any) => {
-                    this.availableFields.push(custom);
-                });
-
-                this.sortPipe.transform(this.availableFields, 'label');
-
-                this.loadIndexingModelFields();
-            }),
-            catchError((err: any) => {
-                this.notify.handleErrors(err);
-                return of(false);
-            })
-        ).subscribe();
+        return new Promise((resolve) => {
+            this.http.get('../rest/customFields').pipe(
+                tap((data: any) => {
+                    data.customFields = data.customFields.map((custom: any) => {
+                        return {
+                            identifier: 'indexingCustomField_' + custom.id,
+                            label: custom.label
+                        };
+                    });
+                    data.customFields.forEach((custom: any) => {
+                        this.availableFields.push(custom);
+                    });
+                    this.sortPipe.transform(this.availableFields, 'label');
+                }),
+                finalize(() => resolve(true)),
+                catchError((err: any) => {
+                    this.notify.handleErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        });
     }
 
     changeModel(event: any) {
@@ -210,16 +230,40 @@ export class RedirectIndexingModelComponent implements OnInit {
         ).subscribe();
     }
 
+    isValid() {
+        if (this.loading) {
+            return false;
+        } else if (this.mainIndexingModel.used.length === 0) {
+            return true;
+        } else {
+            return this.selectedModelId !== undefined;
+        }
+    }
+
     onSubmit() {
-        this.http.request('DELETE', '../rest/indexingModels/' + this.mainIndexingModel.id, { body: { targetId: this.selectedModelId } }).pipe(
-            tap(() => {
-                this.notify.success(this.translate.instant('lang.indexingModelDeleted'));
-                this.dialogRef.close('ok');
-            }),
-            catchError((err: any) => {
-                this.notify.handleSoftErrors(err);
-                return of(false);
-            })
-        ).subscribe();
+        if (this.mainIndexingModel.used.length === 0) {
+            this.http.delete('../rest/indexingModels/' + this.mainIndexingModel.id).pipe(
+                tap(() => {
+                    this.notify.success(this.translate.instant('lang.indexingModelDeleted'));
+                    this.dialogRef.close('ok');
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        } else {
+            this.http.request('DELETE', '../rest/indexingModels/' + this.mainIndexingModel.id, { body: { targetId: this.selectedModelId } }).pipe(
+                tap(() => {
+                    this.notify.success(this.translate.instant('lang.indexingModelDeleted'));
+                    this.dialogRef.close('ok');
+                }),
+                catchError((err: any) => {
+                    this.notify.handleSoftErrors(err);
+                    return of(false);
+                })
+            ).subscribe();
+        }
+
     }
 }
-- 
GitLab