From f7bec4ccdfe875dc36ad22c3096391604155b789 Mon Sep 17 00:00:00 2001
From: Florent Poittevin <florent.poittevin@unige.ch>
Date: Mon, 28 Oct 2019 16:41:23 +0100
Subject: [PATCH] feat: allow to filter by org unit on deposit list

---
 .../deposit-form.presentational.ts            |   7 +-
 .../deposit-list/deposit-list.routable.ts     |  15 ++-
 .../shared-data-table.presentational.html     | 117 ++++++++++--------
 .../shared-data-table.presentational.scss     |  70 +++++++++--
 .../shared-data-table.presentational.ts       |  19 ++-
 ...hable-abstract-content.presentational.scss |   3 +-
 ...searchable-single-select.presentational.ts |  11 ++
 src/app/shared/enums/field-type.enum.ts       |   2 +
 .../shared/models/data-table-columns.model.ts |   5 +
 9 files changed, 174 insertions(+), 75 deletions(-)

diff --git a/src/app/features/deposit/components/presentationals/deposit-form/deposit-form.presentational.ts b/src/app/features/deposit/components/presentationals/deposit-form/deposit-form.presentational.ts
index b7371a3b7..009353e2b 100644
--- a/src/app/features/deposit/components/presentationals/deposit-form/deposit-form.presentational.ts
+++ b/src/app/features/deposit/components/presentationals/deposit-form/deposit-form.presentational.ts
@@ -211,7 +211,12 @@ export class DepositFormPresentational extends SharedAbstractFormPresentational<
     return this.form.get(this.formDefinition.authors).value.includes(this.personConnected.resId);
   }
 
-  orgUnitSelectionChange(orgUnitResId: string): void {
+  orgUnitSelectionChange(orgUnitResId: string | undefined): void {
+    if (isNullOrUndefined(orgUnitResId)) {
+      this.form.get(this.formDefinition.preservationPolicyId).setValue(undefined);
+      this.form.get(this.formDefinition.submissionPolicyId).setValue(undefined);
+      return;
+    }
     this._orgUnitValueBS.next(orgUnitResId);
     let valueChange: boolean = false;
     this.subscribe(this.selectedOrgUnit.pipe(
diff --git a/src/app/features/deposit/components/routables/deposit-list/deposit-list.routable.ts b/src/app/features/deposit/components/routables/deposit-list/deposit-list.routable.ts
index cb60e3e3d..e30942420 100644
--- a/src/app/features/deposit/components/routables/deposit-list/deposit-list.routable.ts
+++ b/src/app/features/deposit/components/routables/deposit-list/deposit-list.routable.ts
@@ -11,10 +11,13 @@ import {Deposit} from "@app/generated-api/model/deposit.model";
 import {SharedAbstractListRoutable} from "@app/shared/components/routables/shared-abstract-list/shared-abstract-list.routable";
 import {FieldTypeEnum} from "@app/shared/enums/field-type.enum";
 import {LocalStateEnum} from "@app/shared/enums/local-state.enum";
+import {appAuthorizedOrganizationalUnitNameSpace} from "@app/stores/authorized-organizational-unit/app-authorized-organizational-unit.action";
+import {AppAuthorizedOrganizationalUnitState} from "@app/stores/authorized-organizational-unit/app-authorized-organizational-unit.state";
 import {TranslateService} from "@ngx-translate/core";
 import {Store} from "@ngxs/store";
 import {
   OrderEnum,
+  ResourceNameSpace,
   TRANSLATE,
 } from "solidify-frontend";
 
@@ -29,6 +32,9 @@ export class DepositListRoutable extends SharedAbstractListRoutable<DepositExten
   readonly KEY_REFRESH_BUTTON: string = TRANSLATE("deposit.refresh");
   readonly KEY_BACK_BUTTON: string | undefined = undefined;
 
+  appAuthorizedOrganizationalUnitNameSpace: ResourceNameSpace = appAuthorizedOrganizationalUnitNameSpace;
+  appAuthorizedOrganizationalUnitState: typeof AppAuthorizedOrganizationalUnitState = AppAuthorizedOrganizationalUnitState;
+
   constructor(protected store: Store,
               protected _changeDetector: ChangeDetectorRef,
               private translate: TranslateService) {
@@ -52,10 +58,13 @@ export class DepositListRoutable extends SharedAbstractListRoutable<DepositExten
       {
         field: "organizationalUnit.name" as any,
         header: TRANSLATE("deposit.table.header.organizationalUnit"),
-        type: FieldTypeEnum.string,
+        type: FieldTypeEnum.searchableSingleSelect,
         order: OrderEnum.none,
-        // isFilterable: true,
+        isFilterable: true,
         // isSortable: true,
+        resourceNameSpace: this.appAuthorizedOrganizationalUnitNameSpace,
+        resourceState: this.appAuthorizedOrganizationalUnitState as any,
+        filterableField: "organizationalUnitId",
       },
       {
         field: "publicationDate",
@@ -84,7 +93,7 @@ export class DepositListRoutable extends SharedAbstractListRoutable<DepositExten
       {
         field: "status",
         header: TRANSLATE("deposit.table.header.status"),
-        type: FieldTypeEnum.string,
+        type: FieldTypeEnum.singleSelect,
         order: OrderEnum.none,
         isFilterable: true,
         isSortable: true,
diff --git a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.html b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.html
index 1cd06e97c..561bc60dd 100644
--- a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.html
+++ b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.html
@@ -12,7 +12,6 @@
       [sortOrder]="defaultSortColumn ? defaultSortColumn?.order : undefined"
   >
     <ng-template pTemplate="header"
-                 let-columns
     >
       <tr>
         <th *ngFor="let col of columns"
@@ -31,62 +30,78 @@
             class="data-table-filter"
         >
           <ng-container *ngIf="col.isFilterable">
-            <div *ngSwitchCase="fieldTypeString"
-                 class="filter-text"
+
+            <div *ngSwitchCase="fieldTypeEnum.searchableSingleSelect"
+                 class="filter-searchable-single-select"
             >
-              <ng-template [ngIf]="col.filterEnum"
-                           [ngIfElse]="inputFilter"
+              <dlcm-shared-searchable-single-select solidifyValidation
+                                                    [resourceNameSpace]="col.resourceNameSpace"
+                                                    [state]="col.resourceState"
+                                                    [ngModel]="getValue(col)"
+                                                    [labelKey]="'name'"
+                                                    [valueKey]="'resId'"
+                                                    (valueChange)="onValueChange(col, $event)"
               >
-                <mat-select #select
-                            [class.no-value]="!select.value"
-                            (selectionChange)="onValueChange(col, $event.value)"
+              </dlcm-shared-searchable-single-select>
+            </div>
+
+            <div *ngSwitchCase="fieldTypeEnum.singleSelect"
+                 class="filter-single-select"
+            >
+              <mat-select #select
+                          [class.no-value]="!select.value"
+                          [value]="getValue(col)"
+                          (selectionChange)="onValueChange(col, $event.value)"
+              >
+                <mat-option>{{'table.filter.all' | translate}}</mat-option>
+                <mat-option *ngFor="let enum of col.filterEnum"
+                            [value]="enum.key"
                 >
-                  <mat-option>{{'table.filter.all' | translate}}</mat-option>
-                  <mat-option *ngFor="let enum of col.filterEnum"
-                              [value]="enum.key"
+                  <ng-template [ngIf]="col.translate"
+                               [ngIfElse]="noTranslate"
                   >
-                    <ng-template [ngIf]="col.translate"
-                                 [ngIfElse]="noTranslate"
-                    >
-                      {{enum.value | translate}}
-                    </ng-template>
-                    <ng-template #noTranslate>
-                      {{enum.value}}
-                    </ng-template>
-                  </mat-option>
-                </mat-select>
-                <button mat-button
-                        *ngIf="select.value"
-                        matSuffix
-                        mat-icon-button
-                        aria-label="Clear"
-                        (click)="select.value='';onValueChange(col, select.value)"
-                >
-                  <fa-icon matSuffix
-                           icon="times"
-                  ></fa-icon>
-                </button>
-              </ng-template>
+                    {{enum.value | translate}}
+                  </ng-template>
+                  <ng-template #noTranslate>
+                    {{enum.value}}
+                  </ng-template>
+                </mat-option>
+              </mat-select>
+              <button mat-button
+                      *ngIf="select.value"
+                      matSuffix
+                      mat-icon-button
+                      aria-label="Clear"
+                      (click)="select.value='';onValueChange(col, select.value)"
+              >
+                <fa-icon matSuffix
+                         icon="times"
+                ></fa-icon>
+              </button>
+            </div>
 
-              <ng-template #inputFilter>
-                <input type="text"
-                       #filter
-                       (keyup)="onValueChange(col, $event.target.value)"
-                       [value]="getValue(col.field)"
-                >
-                <button mat-button
-                        *ngIf="filter.value"
-                        matSuffix
-                        mat-icon-button
-                        aria-label="Clear"
-                        (click)="filter.value='';onValueChange(col, filter.value)"
-                >
-                  <fa-icon matSuffix
-                           icon="times"
-                  ></fa-icon>
-                </button>
-              </ng-template>
+            <div *ngSwitchCase="fieldTypeEnum.string"
+                 class="filter-string"
+            >
+              <input class="filter-input"
+                     type="text"
+                     #filter
+                     (keyup)="onValueChange(col, $event.target.value)"
+                     [value]="getValue(col)"
+              >
+              <button mat-button
+                      *ngIf="filter.value"
+                      matSuffix
+                      mat-icon-button
+                      aria-label="Clear"
+                      (click)="filter.value='';onValueChange(col, filter.value)"
+              >
+                <fa-icon matSuffix
+                         icon="times"
+                ></fa-icon>
+              </button>
             </div>
+
           </ng-container>
         </th>
       </tr>
diff --git a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.scss b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.scss
index 2e41ad075..092193cdb 100644
--- a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.scss
+++ b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.scss
@@ -113,20 +113,36 @@ $inputGroupTextColor: #444444;
 
   .data-table-filter {
     padding: 4px !important;
+    $height-input: 30px;
+
+    @mixin input-filter {
+      background-color: $primary-color-lighter !important;
+      border: 0;
+      border-radius: 5px;
+      padding: 6px 30px 6px 10px;
+      width: 100%;
+      color: $white;
+      height: $height-input;
+    }
+
 
-    .filter-text {
+    .filter-searchable-single-select,
+    .filter-single-select,
+    .filter-string {
       position: relative;
 
-      input, mat-select {
-        background-color: $primary-color-lighter !important;
-        border: 0;
-        border-radius: 5px;
-        padding: 6px 30px 6px 10px;
-        width: 100%;
-        color: $white;
-        height: 30px;
+      .filter-input, mat-select {
+        @include input-filter;
       }
 
+      button {
+        position: absolute;
+        top: -6px;
+        right: -5px;
+      }
+    }
+
+    .filter-single-select {
       mat-select {
         .mat-select-value {
           color: $white;
@@ -142,13 +158,41 @@ $inputGroupTextColor: #444444;
           padding-right: 6px;
         }
       }
+    }
 
-      button {
-        position: absolute;
-        top: -6px;
+    .filter-searchable-single-select {
+      height: $height-input;
+
+      form input {
+        @include input-filter;
+        text-align: left;
+        font-weight: initial;
+        margin: 0 !important;
+      }
+
+      .mat-form-field-wrapper {
+        padding: 0;
+
+        .mat-form-field-infix {
+          padding: 0;
+          border: 0;
+        }
+      }
+
+      .mat-form-field-underline {
+        display: none;
+      }
+
+      .clear {
+        top: -32px;
         right: -5px;
+        width: 40px;
+        height: 40px;
+
+        .mat-button-wrapper {
+          color: white;
+        }
       }
     }
   }
 }
-
diff --git a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts
index 7ba9db52a..eb40cf17b 100644
--- a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts
+++ b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts
@@ -102,7 +102,9 @@ export class SharedDataTablePresentational<T> extends SharedAbstractPresentation
 
   isDatasPresent: boolean;
 
-  fieldTypeString: FieldTypeEnum = FieldTypeEnum.string;
+  get fieldTypeEnum(): typeof FieldTypeEnum {
+    return FieldTypeEnum;
+  }
 
   private readonly _SEPARATOR: string = ".";
 
@@ -175,21 +177,26 @@ export class SharedDataTablePresentational<T> extends SharedAbstractPresentation
   }
 
   onValueChange(col: DataTableColumns, value: string): void {
+    const fieldName = this.getFilterableField(col);
     let queryParameters = ObjectUtil.clone(this.queryParameters);
     if (isEmptyString(value) || isNullOrUndefined(value)) {
-      queryParameters.search.searchItems.delete(col.field);
+      queryParameters.search.searchItems.delete(fieldName);
     } else {
-      MapUtil.addOrUpdate(queryParameters.search.searchItems, col.field, value);
+      MapUtil.addOrUpdate(queryParameters.search.searchItems, fieldName, value);
     }
     queryParameters = QueryParametersUtil.resetToFirstPage(queryParameters);
     this._queryParametersBS.next(queryParameters);
   }
 
-  getValue(field: string): string {
-    const value = this.queryParameters.search.searchItems.get(field);
-    if (value === null || value === undefined || value === "") {
+  getValue(col: DataTableColumns): string | null {
+    const value = this.queryParameters.search.searchItems.get(this.getFilterableField(col));
+    if (value === null || value === undefined || value === StringUtil.stringEmpty) {
       return null;
     }
     return value;
   }
+
+  private getFilterableField(col: DataTableColumns): string {
+    return isNullOrUndefined(col.filterableField) ? col.field : col.filterableField;
+  }
 }
diff --git a/src/app/shared/components/presentationals/shared-searchable-abstract-content/shared-searchable-abstract-content.presentational.scss b/src/app/shared/components/presentationals/shared-searchable-abstract-content/shared-searchable-abstract-content.presentational.scss
index 0e19a2b33..5a247948a 100644
--- a/src/app/shared/components/presentationals/shared-searchable-abstract-content/shared-searchable-abstract-content.presentational.scss
+++ b/src/app/shared/components/presentationals/shared-searchable-abstract-content/shared-searchable-abstract-content.presentational.scss
@@ -15,7 +15,8 @@ $min-height-result: $height-overlay/2 + $height-input;
   transform-origin: 50% 20.5px 0px;
   font-size: 14px;
   opacity: 1;
-  min-width: calc(100% + 32px);
+  //min-width: calc(100% + 32px);
+  min-width: 266px;
   transform: scaleY(1);
 
   height: $height-overlay;
diff --git a/src/app/shared/components/presentationals/shared-searchable-single-select/shared-searchable-single-select.presentational.ts b/src/app/shared/components/presentationals/shared-searchable-single-select/shared-searchable-single-select.presentational.ts
index 190ced136..76d9b75df 100644
--- a/src/app/shared/components/presentationals/shared-searchable-single-select/shared-searchable-single-select.presentational.ts
+++ b/src/app/shared/components/presentationals/shared-searchable-single-select/shared-searchable-single-select.presentational.ts
@@ -94,6 +94,7 @@ export class SharedSearchableSingleSelectPresentational extends SharedAbstractPr
   floatLabel: FloatLabelType;
 
   alreadyInit: boolean = false;
+  noFormControl: boolean;
 
   get formValidationHelper(): typeof FormValidationHelper {
     return FormValidationHelper;
@@ -113,6 +114,10 @@ export class SharedSearchableSingleSelectPresentational extends SharedAbstractPr
   }
 
   ngOnInit(): void {
+    if (isNullOrUndefined(this.formControl)) {
+      this.noFormControl = true;
+      this.formControl = this._fb.control(undefined);
+    }
     if (isTrue(this.alreadyInit)) {
       // When validator is updated, the ngOnInit is call a new time
       // This hack prevent this to happen (case with deposit license validator impacted by access level
@@ -181,6 +186,10 @@ export class SharedSearchableSingleSelectPresentational extends SharedAbstractPr
   }
 
   writeValue(value: string[]): void {
+    if (!isNullOrUndefined(value) && this.noFormControl) {
+      this.formControl.setValue(value);
+      this._dispatchGetActionToRetrieveLabel();
+    }
   }
 
   openOverlay(): void {
@@ -208,6 +217,7 @@ export class SharedSearchableSingleSelectPresentational extends SharedAbstractPr
       tap(value => {
         const valueKey = value[this.valueKey];
         this.propagateChange(valueKey);
+        this.formControl.setValue(valueKey);
         this._valueBS.next(valueKey);
         this.formLabel.get(this.formDefinition.value).setValue(this.labelCallback(value));
         this.computeFloatLabel();
@@ -259,6 +269,7 @@ export class SharedSearchableSingleSelectPresentational extends SharedAbstractPr
   clearValue($event: MouseEvent): void {
     this.formControl.setValue("");
     this.formLabel.get(this.formDefinition.value).setValue(null);
+    this._valueBS.next(undefined);
     $event.stopPropagation();
     this.computeFloatLabel();
   }
diff --git a/src/app/shared/enums/field-type.enum.ts b/src/app/shared/enums/field-type.enum.ts
index fe1d6ea28..c8f751315 100644
--- a/src/app/shared/enums/field-type.enum.ts
+++ b/src/app/shared/enums/field-type.enum.ts
@@ -3,4 +3,6 @@ export enum FieldTypeEnum {
   date,
   datetime,
   number,
+  searchableSingleSelect,
+  singleSelect,
 }
diff --git a/src/app/shared/models/data-table-columns.model.ts b/src/app/shared/models/data-table-columns.model.ts
index ad9fa0bf5..c563a452f 100644
--- a/src/app/shared/models/data-table-columns.model.ts
+++ b/src/app/shared/models/data-table-columns.model.ts
@@ -2,6 +2,8 @@ import {FieldTypeEnum} from "@app/shared/enums/field-type.enum";
 import {
   KeyValue,
   OrderEnum,
+  ResourceNameSpace,
+  ResourceState,
 } from "solidify-frontend";
 
 export interface DataTableColumns<T = any> {
@@ -14,4 +16,7 @@ export interface DataTableColumns<T = any> {
   translate?: boolean;
   isSortable?: boolean;
   isFilterable?: boolean;
+  filterableField?: keyof T & string;
+  resourceNameSpace?: ResourceNameSpace;
+  resourceState?: ResourceState<any>;
 }
-- 
GitLab