From 33761474eca5991ce3feefcb54e24bf5eeac0c92 Mon Sep 17 00:00:00 2001
From: Florent Poittevin <florent.poittevin@unige.ch>
Date: Tue, 11 Feb 2020 17:06:22 +0100
Subject: [PATCH] feat: 1075 explain why file is excluded or ignored

---
 .../deposit-home/deposit-home.routable.ts     | 31 +++++++-
 .../deposit-root/deposit-root.routable.ts     | 35 +++++-----
 .../features/deposit/stores/deposit.action.ts | 33 +++++++++
 .../features/deposit/stores/deposit.state.ts  | 70 +++++++++++++++++++
 ...red-info-excluded-ignored-file.dialog.html | 13 ++++
 ...red-info-excluded-ignored-file.dialog.scss | 14 ++++
 ...hared-info-excluded-ignored-file.dialog.ts | 59 ++++++++++++++++
 ...-datafile-quick-status.presentational.html |  2 +-
 ...-datafile-quick-status.presentational.scss |  6 ++
 ...ed-datafile-quick-status.presentational.ts | 43 ++++++++++--
 src/app/shared/enums/api-action.enum.ts       |  2 +
 .../shared/models/business/file-list.model.ts |  4 ++
 src/app/shared/shared.module.ts               |  4 +-
 src/assets/i18n/de.json                       | 20 +++++-
 src/assets/i18n/en.json                       | 20 +++++-
 src/assets/i18n/fr.json                       | 22 +++++-
 16 files changed, 344 insertions(+), 34 deletions(-)
 create mode 100644 src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.html
 create mode 100644 src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.scss
 create mode 100644 src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.ts
 create mode 100644 src/app/shared/models/business/file-list.model.ts

diff --git a/src/app/features/deposit/components/routables/deposit-home/deposit-home.routable.ts b/src/app/features/deposit/components/routables/deposit-home/deposit-home.routable.ts
index 1a84fbf97..8d676f7db 100644
--- a/src/app/features/deposit/components/routables/deposit-home/deposit-home.routable.ts
+++ b/src/app/features/deposit/components/routables/deposit-home/deposit-home.routable.ts
@@ -4,10 +4,17 @@ import {
 } from "@angular/core";
 import {ActivatedRoute} from "@angular/router";
 import {DepositAction} from "@deposit/stores/deposit.action";
-import {Store} from "@ngxs/store";
+import {
+  Actions,
+  ofActionCompleted,
+  Store,
+} from "@ngxs/store";
 import {SharedAbstractRoutable} from "@shared/components/routables/shared-abstract/shared-abstract.routable";
 import {AppRoutesEnum} from "@shared/enums/routes.enum";
-import {isNullOrUndefined} from "solidify-frontend";
+import {
+  isNullOrUndefined,
+  StoreUtil,
+} from "solidify-frontend";
 
 @Component({
   selector: "dlcm-deposit-home-routable",
@@ -18,11 +25,31 @@ export class DepositHomeRoutable extends SharedAbstractRoutable implements OnIni
   private _orgUnitResId: string;
 
   constructor(private readonly _store: Store,
+              private readonly _actions$: Actions,
               private readonly _route: ActivatedRoute) {
     super();
     this.retrieveCurrentModelWithUrl();
   }
 
+  ngOnInit(): void {
+    this.subscribe(StoreUtil.dispatchParallelActionAndWaitForSubActionsCompletion(this._store, [
+      {
+        action: new DepositAction.GetExcludedListFiles(),
+        subActionCompletions: [
+          this._actions$.pipe(ofActionCompleted(DepositAction.GetExcludedListFilesSuccess)),
+          this._actions$.pipe(ofActionCompleted(DepositAction.GetExcludedListFilesFail)),
+        ],
+      },
+      {
+        action: new DepositAction.GetIgnoredListFiles(),
+        subActionCompletions: [
+          this._actions$.pipe(ofActionCompleted(DepositAction.GetIgnoredListFilesSuccess)),
+          this._actions$.pipe(ofActionCompleted(DepositAction.GetIgnoredListFilesFail)),
+        ],
+      },
+    ]));
+  }
+
   protected retrieveCurrentModelWithUrl(): void {
     this.retrieveResIdFromUrl();
     this._store.dispatch(new DepositAction.SetOrganizationalUnit(this._orgUnitResId));
diff --git a/src/app/features/deposit/components/routables/deposit-root/deposit-root.routable.ts b/src/app/features/deposit/components/routables/deposit-root/deposit-root.routable.ts
index da5756fc3..9d8a82209 100644
--- a/src/app/features/deposit/components/routables/deposit-root/deposit-root.routable.ts
+++ b/src/app/features/deposit/components/routables/deposit-root/deposit-root.routable.ts
@@ -14,6 +14,7 @@ import {
 import {SharedAbstractRoutable} from "@shared/components/routables/shared-abstract/shared-abstract.routable";
 import {RoutesEnum} from "@shared/enums/routes.enum";
 import {UserPreferencesUtil} from "@shared/utils/user-preferences.util";
+import {tap} from "rxjs/operators";
 import {
   isEmptyArray,
   isNullOrUndefined,
@@ -35,7 +36,7 @@ export class DepositRootRoutable extends SharedAbstractRoutable implements OnIni
   }
 
   ngOnInit(): void {
-    StoreUtil.dispatchParallelActionAndWaitForSubActionsCompletion(this._store, [
+    this.subscribe(StoreUtil.dispatchParallelActionAndWaitForSubActionsCompletion(this._store, [
       {
         action: new AppAuthorizedOrganizationalUnitAction.GetAll(),
         subActionCompletions: [
@@ -43,22 +44,24 @@ export class DepositRootRoutable extends SharedAbstractRoutable implements OnIni
           this._actions$.pipe(ofActionCompleted(AppAuthorizedOrganizationalUnitAction.GetAllFail)),
         ],
       },
-    ]).subscribe(success => {
-      if (!success) {
-        return;
-      }
+    ]).pipe(
+      tap(success => {
+        if (!success) {
+          return;
+        }
 
-      const listAuthorizedOrgUnit = ResourceState.listSnapshot(this._store, AppAuthorizedOrganizationalUnitState);
-      if (isEmptyArray(listAuthorizedOrgUnit)) {
-        this.noOrgUnit = true;
-        return;
-      }
-      const preferredOrgUnit = UserPreferencesUtil.getPreferredOrgUnitInDepositMenu(listAuthorizedOrgUnit);
-      if (isNullOrUndefined(preferredOrgUnit)) {
-        return;
-      }
-      this._store.dispatch(new Navigate([RoutesEnum.deposit, preferredOrgUnit.resId, DepositTabStatusEnum.inProgress]));
-    });
+        const listAuthorizedOrgUnit = ResourceState.listSnapshot(this._store, AppAuthorizedOrganizationalUnitState);
+        if (isEmptyArray(listAuthorizedOrgUnit)) {
+          this.noOrgUnit = true;
+          return;
+        }
+        const preferredOrgUnit = UserPreferencesUtil.getPreferredOrgUnitInDepositMenu(listAuthorizedOrgUnit);
+        if (isNullOrUndefined(preferredOrgUnit)) {
+          return;
+        }
+        this._store.dispatch(new Navigate([RoutesEnum.deposit, preferredOrgUnit.resId, DepositTabStatusEnum.inProgress]));
+      }),
+    ));
   }
 }
 
diff --git a/src/app/features/deposit/stores/deposit.action.ts b/src/app/features/deposit/stores/deposit.action.ts
index f96c38f45..bb1a233f7 100644
--- a/src/app/features/deposit/stores/deposit.action.ts
+++ b/src/app/features/deposit/stores/deposit.action.ts
@@ -4,6 +4,7 @@ import {FileUploadWrapper} from "@app/features/deposit/models/file-upload-wrappe
 import {UploadFileStatus} from "@app/features/deposit/models/upload-file-status.model";
 import {Deposit} from "@app/generated-api/model/deposit.model";
 import {LocalStateEnum} from "@app/shared/enums/local-state.enum";
+import {FileListModel} from "@shared/models/business/file-list.model";
 import {Result} from "@shared/models/business/result.model";
 import {
   BaseAction,
@@ -329,6 +330,38 @@ export namespace DepositAction {
     constructor(public isEditable: boolean) {
     }
   }
+
+  export class GetExcludedListFiles extends BaseAction {
+    static readonly type: string = `[${state}] Get Excluded List Files`;
+  }
+
+  export class GetExcludedListFilesSuccess extends BaseSubAction<GetExcludedListFiles> {
+    static readonly type: string = `[${state}] Get Excluded List Files Success`;
+
+    constructor(public parentAction: GetExcludedListFiles, public fileList: FileListModel) {
+      super(parentAction);
+    }
+  }
+
+  export class GetExcludedListFilesFail extends BaseSubAction<GetExcludedListFiles> {
+    static readonly type: string = `[${state}] Get Excluded List Files Fail`;
+  }
+
+  export class GetIgnoredListFiles extends BaseAction {
+    static readonly type: string = `[${state}] Get Ignored List Files`;
+  }
+
+  export class GetIgnoredListFilesSuccess extends BaseSubAction<GetIgnoredListFiles> {
+    static readonly type: string = `[${state}] Get Ignored List Files Success`;
+
+    constructor(public parentAction: GetIgnoredListFiles, public fileList: FileListModel) {
+      super(parentAction);
+    }
+  }
+
+  export class GetIgnoredListFilesFail extends BaseSubAction<GetIgnoredListFiles> {
+    static readonly type: string = `[${state}] Get Ignored List Files Fail`;
+  }
 }
 
 export const depositActionNameSpace: ResourceNameSpace = DepositAction;
diff --git a/src/app/features/deposit/stores/deposit.state.ts b/src/app/features/deposit/stores/deposit.state.ts
index 05ca317a1..49ca54b75 100644
--- a/src/app/features/deposit/stores/deposit.state.ts
+++ b/src/app/features/deposit/stores/deposit.state.ts
@@ -66,6 +66,7 @@ import {
   StateContext,
   Store,
 } from "@ngxs/store";
+import {FileListModel} from "@shared/models/business/file-list.model";
 import {Result} from "@shared/models/business/result.model";
 import {SharedLanguageAction} from "@shared/stores/language/shared-language.action";
 import {defaultStatusHistoryInitValue} from "@shared/stores/status-history/status-history.state";
@@ -118,6 +119,8 @@ export interface DepositStateModel extends ResourceStateModel<DepositExtended> {
   counterTabRejected: number | undefined;
   counterTabInError: number | undefined;
   counterTabAll: number | undefined;
+  excludedFileList: FileListModel | undefined;
+  ignoredFileList: FileListModel | undefined;
   canEdit: boolean;
 }
 
@@ -142,6 +145,8 @@ export interface DepositStateModel extends ResourceStateModel<DepositExtended> {
     counterTabRejected: undefined,
     counterTabInError: undefined,
     counterTabAll: undefined,
+    excludedFileList: undefined,
+    ignoredFileList: undefined,
     canEdit: false,
   },
   children: [
@@ -592,6 +597,7 @@ export class DepositState extends ResourceState<DepositStateModel, DepositExtend
 
   @Action(DepositAction.ReserveDOISuccess)
   reserveDOISuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.ReserveDOISuccess): void {
+    // We want to explicitly set to undefined before set the new value
     ctx.patchState({
       current: undefined,
     });
@@ -696,9 +702,13 @@ export class DepositState extends ResourceState<DepositStateModel, DepositExtend
   @Action(DepositAction.Clean)
   clean(ctx: StateContext<DepositStateModel>, action: DepositAction.Clean): void {
     const organizationalUnit = ctx.getState().organizationalUnit;
+    const ignoredFileList = ctx.getState().ignoredFileList;
+    const excludedFileList = ctx.getState().excludedFileList;
     super.clean(ctx, action);
     ctx.patchState({
       organizationalUnit,
+      ignoredFileList,
+      excludedFileList,
     });
   }
 
@@ -713,4 +723,64 @@ export class DepositState extends ResourceState<DepositStateModel, DepositExtend
       canEdit: action.isEditable,
     });
   }
+
+  @Action(DepositAction.GetExcludedListFiles)
+  getExcludedListFiles(ctx: StateContext<DepositStateModel>, action: DepositAction.GetExcludedListFiles): Observable<FileListModel> {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
+    });
+    return this.apiService.getByIdInPath<FileListModel>(this._urlResource + urlSeparator + ApiActionEnum.LIST_EXCLUDE_FILES)
+      .pipe(
+        tap(fileList => ctx.dispatch(new DepositAction.GetExcludedListFilesSuccess(action, fileList))),
+        catchError(error => {
+          ctx.dispatch(new DepositAction.GetExcludedListFilesFail(action));
+          throw new SolidifyStateError(this, error);
+        }),
+      );
+  }
+
+  @Action(DepositAction.GetExcludedListFilesSuccess)
+  getExcludedListFilesSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.GetExcludedListFilesSuccess): void {
+    ctx.patchState({
+      excludedFileList: action.fileList,
+      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
+    });
+  }
+
+  @Action(DepositAction.GetExcludedListFilesFail)
+  getExcludedListFilesFail(ctx: StateContext<DepositStateModel>, action: DepositAction.GetExcludedListFilesFail): void {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
+    });
+  }
+
+  @Action(DepositAction.GetIgnoredListFiles)
+  getIgnoredListFiles(ctx: StateContext<DepositStateModel>, action: DepositAction.GetIgnoredListFiles): Observable<FileListModel> {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter + 1,
+    });
+    return this.apiService.getByIdInPath<FileListModel>(this._urlResource + urlSeparator + ApiActionEnum.LIST_IGNORE_FILES)
+      .pipe(
+        tap(fileList => ctx.dispatch(new DepositAction.GetIgnoredListFilesSuccess(action, fileList))),
+        catchError(error => {
+          ctx.dispatch(new DepositAction.GetIgnoredListFilesFail(action));
+          throw new SolidifyStateError(this, error);
+        }),
+      );
+  }
+
+  @Action(DepositAction.GetIgnoredListFilesSuccess)
+  getIgnoredListFilesSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.GetIgnoredListFilesSuccess): void {
+    ctx.patchState({
+      ignoredFileList: action.fileList,
+      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
+    });
+  }
+
+  @Action(DepositAction.GetIgnoredListFilesFail)
+  getIgnoredListFilesFail(ctx: StateContext<DepositStateModel>, action: DepositAction.GetIgnoredListFilesFail): void {
+    ctx.patchState({
+      isLoadingCounter: ctx.getState().isLoadingCounter - 1,
+    });
+  }
 }
diff --git a/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.html b/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.html
new file mode 100644
index 000000000..46e2110b3
--- /dev/null
+++ b/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.html
@@ -0,0 +1,13 @@
+<dlcm-shared-base-info-dialog [titleToTranslate]="titleToTranslate">
+  <span class="message">{{messageToTranslate | translate}}</span>
+  <ng-container *ngIf="(fileListObs | async) as fileList">
+    <h3>{{'deposit.file.dialog.excludedIgnoredFile.categeroy.files' | translate}}</h3>
+    <ul class="list">
+      <li *ngFor="let file of fileList.files">{{file}}</li>
+    </ul>
+    <h3>{{'deposit.file.dialog.excludedIgnoredFile.categeroy.puids' | translate}}</h3>
+    <ul class="list">
+      <li *ngFor="let puid of fileList.puids">{{puid}}</li>
+    </ul>
+  </ng-container>
+</dlcm-shared-base-info-dialog>
diff --git a/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.scss b/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.scss
new file mode 100644
index 000000000..f43681170
--- /dev/null
+++ b/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.scss
@@ -0,0 +1,14 @@
+@import "../sass/abstracts/mixins";
+@import "../sass/abstracts/variables";
+
+:host {
+  .list {
+    padding-left: 16px;
+
+    > li {
+      list-style: square;
+      line-height: 20px;
+      padding-left: 10px;
+    }
+  }
+}
diff --git a/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.ts b/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.ts
new file mode 100644
index 000000000..4c34238df
--- /dev/null
+++ b/src/app/shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog.ts
@@ -0,0 +1,59 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  Inject,
+  OnInit,
+} from "@angular/core";
+import {
+  MAT_DIALOG_DATA,
+  MatDialogRef,
+} from "@angular/material/dialog";
+import {DepositState} from "@deposit/stores/deposit.state";
+import {Store} from "@ngxs/store";
+import {SharedAbstractContainer} from "@shared/components/containers/shared-abstract/shared-abstract.container";
+import {DataFileStatusEnum} from "@shared/enums/business/data-file-status.enum";
+import {FileListModel} from "@shared/models/business/file-list.model";
+import {Observable} from "rxjs";
+import {
+  MemoizedUtil,
+  TRANSLATE,
+} from "solidify-frontend";
+
+@Component({
+  selector: "dlcm-shared-info-excluded-ignored-file-dialog",
+  templateUrl: "./shared-info-excluded-ignored-file.dialog.html",
+  styleUrls: ["./shared-info-excluded-ignored-file.dialog.scss"],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class SharedInfoExcludedIgnoredFileDialog extends SharedAbstractContainer implements OnInit {
+  fileListObs: Observable<FileListModel>;
+
+  titleToTranslate: string = "";
+  messageToTranslate: string = "";
+
+  constructor(private readonly _store: Store,
+              protected dialogRef: MatDialogRef<SharedInfoExcludedIgnoredFileDialog>,
+              @Inject(MAT_DIALOG_DATA) public data: SharedInfoExcludedIgnoredFileDialogData) {
+    super();
+  }
+
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (this.data.dataFileStatusEnum === DataFileStatusEnum.IGNORED_FILE) {
+      this.fileListObs = MemoizedUtil.select(this._store, DepositState, state => state.ignoredFileList);
+      this.titleToTranslate = TRANSLATE("deposit.file.dialog.excludedIgnoredFile.title.ignored");
+      this.messageToTranslate = TRANSLATE("deposit.file.dialog.excludedIgnoredFile.message.ignored");
+    } else if (this.data.dataFileStatusEnum === DataFileStatusEnum.EXCLUDED_FILE) {
+      this.fileListObs = MemoizedUtil.select(this._store, DepositState, state => state.excludedFileList);
+      this.titleToTranslate = TRANSLATE("deposit.file.dialog.excludedIgnoredFile.title.excluded");
+      this.messageToTranslate = TRANSLATE("deposit.file.dialog.excludedIgnoredFile.message.excluded");
+    } else {
+      this.dialogRef.close();
+    }
+  }
+}
+
+export interface SharedInfoExcludedIgnoredFileDialogData {
+  dataFileStatusEnum: DataFileStatusEnum;
+}
diff --git a/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.html b/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.html
index b160f02ce..3d441713e 100644
--- a/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.html
+++ b/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.html
@@ -1,4 +1,4 @@
-<mat-icon *ngIf="classes === PENDING"
+<mat-icon *ngIf="classes.includes(CLASS_PENDING)"
           class="icon"
 >autorenew
 </mat-icon>
diff --git a/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.scss b/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.scss
index 47cf7a674..864d1976c 100644
--- a/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.scss
+++ b/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.scss
@@ -2,6 +2,12 @@
 
 $size: 20px;
 :host {
+  cursor: initial;
+
+  &.can-see-detail {
+    cursor: pointer;
+  }
+
   .icon {
     font-size: $size;
     color: $white;
diff --git a/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.ts b/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.ts
index 2291683e6..d770d0cbc 100644
--- a/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.ts
+++ b/src/app/shared/components/presentationals/shared-datafile-quick-status/shared-datafile-quick-status.presentational.ts
@@ -2,8 +2,14 @@ import {
   ChangeDetectionStrategy,
   Component,
   HostBinding,
+  HostListener,
   Input,
 } from "@angular/core";
+import {MatDialog} from "@angular/material/dialog";
+import {
+  SharedInfoExcludedIgnoredFileDialog,
+  SharedInfoExcludedIgnoredFileDialogData,
+} from "@shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog";
 import {SharedAbstractPresentational} from "@shared/components/presentationals/shared-abstract/shared-abstract.presentational";
 import {DataFileStatusEnum} from "@shared/enums/business/data-file-status.enum";
 import {TRANSLATE} from "solidify-frontend";
@@ -16,7 +22,16 @@ import {TRANSLATE} from "solidify-frontend";
 })
 
 export class SharedDatafileQuickStatusPresentational extends SharedAbstractPresentational {
+  private readonly CLASS_READY: string = "ready";
+  private readonly CLASS_ERROR: string = "error";
+  private readonly CLASS_WARNING: string = "warning";
+  private readonly CLASS_CLEANED: string = "cleaned";
+  private readonly CLASS_CAN_SEE_DETAIL: string = "can-see-detail";
+
+  readonly CLASS_PENDING: string = "pending";
+
   _value: DataFileStatusEnum;
+
   @Input()
   set value(value: DataFileStatusEnum) {
     this._value = value;
@@ -27,33 +42,47 @@ export class SharedDatafileQuickStatusPresentational extends SharedAbstractPrese
     return this._value;
   }
 
+  constructor(private readonly _dialog: MatDialog) {
+    super();
+  }
+
   toolipToTranslate: string | undefined;
 
   @HostBinding("class")
   classes: string;
 
-  readonly PENDING: string = "pending";
+  @HostListener("click", ["$event"]) click(mouseEvent: MouseEvent): void {
+    mouseEvent.stopPropagation();
+    if (this.value !== DataFileStatusEnum.EXCLUDED_FILE && this.value !== DataFileStatusEnum.IGNORED_FILE) {
+      return;
+    }
+    this._dialog.open(SharedInfoExcludedIgnoredFileDialog, {
+      data: {
+        dataFileStatusEnum: this.value,
+      } as SharedInfoExcludedIgnoredFileDialogData,
+    });
+  }
 
   getColor(): string {
     switch (this.value) {
       case DataFileStatusEnum.READY:
         this.toolipToTranslate = TRANSLATE("dataFileQuickStatus.ready");
-        return "ready";
+        return this.CLASS_READY;
       case DataFileStatusEnum.IN_ERROR:
         this.toolipToTranslate = TRANSLATE("dataFileQuickStatus.inError");
-        return "error";
+        return this.CLASS_ERROR;
       case DataFileStatusEnum.EXCLUDED_FILE:
         this.toolipToTranslate = TRANSLATE("dataFileQuickStatus.excludedFile");
-        return "error";
+        return this.CLASS_ERROR + " " + this.CLASS_CAN_SEE_DETAIL;
       case DataFileStatusEnum.IGNORED_FILE:
         this.toolipToTranslate = TRANSLATE("dataFileQuickStatus.ignoredFile");
-        return "warning";
+        return this.CLASS_WARNING + " " + this.CLASS_CAN_SEE_DETAIL;
       case DataFileStatusEnum.CLEANED:
         this.toolipToTranslate = TRANSLATE("dataFileQuickStatus.cleaned");
-        return "cleaned";
+        return this.CLASS_CLEANED;
       default:
         this.toolipToTranslate = TRANSLATE("dataFileQuickStatus.pending");
-        return this.PENDING;
+        return this.CLASS_PENDING;
     }
   }
 }
diff --git a/src/app/shared/enums/api-action.enum.ts b/src/app/shared/enums/api-action.enum.ts
index 9200315dc..9788e9441 100644
--- a/src/app/shared/enums/api-action.enum.ts
+++ b/src/app/shared/enums/api-action.enum.ts
@@ -9,6 +9,8 @@ export class ApiActionEnum {
   public static LIST_OAUTH2_GRANT_TYPE: string = "list-grant-types";
   public static LIST_STATUS: string = "list-status";
   public static LIST_FOLDERS: string = "list-folders";
+  public static LIST_EXCLUDE_FILES: string = "list-exclude-files";
+  public static LIST_IGNORE_FILES: string = "list-ignore-files";
 
   public static ARCHIVAL_UNITS: string = "archivalUnits";
   public static ARCHIVAL_COLLECTIONS: string = "archivalCollections";
diff --git a/src/app/shared/models/business/file-list.model.ts b/src/app/shared/models/business/file-list.model.ts
new file mode 100644
index 000000000..a0873b7bd
--- /dev/null
+++ b/src/app/shared/models/business/file-list.model.ts
@@ -0,0 +1,4 @@
+export interface FileListModel {
+  files: string[];
+  puids: string[];
+}
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 28d3695b0..e56d1e28e 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -33,13 +33,14 @@ import {
 import {fas} from "@fortawesome/free-solid-svg-icons";
 import {TranslateModule} from "@ngx-translate/core";
 import {NgxsModule} from "@ngxs/store";
-import {SharedTabsContainer} from "@shared/components/containers/shared-tabs/shared-tabs.container";
 import {SharedOrganizationalUnitNameContainer} from "@shared/components/containers/shared-organizational-unit-name/shared-organizational-unit-name.container";
+import {SharedTabsContainer} from "@shared/components/containers/shared-tabs/shared-tabs.container";
 import {SharedAipDetailDialog} from "@shared/components/dialogs/shared-aip-detail/shared-aip-detail.dialog";
 import {SharedBaseActionDialog} from "@shared/components/dialogs/shared-base-action/shared-base-action.dialog";
 import {SharedBaseInfoDialog} from "@shared/components/dialogs/shared-base-info/shared-base-info.dialog";
 import {SharedFileDetailDialog} from "@shared/components/dialogs/shared-file-detail/shared-file-detail.dialog";
 import {SharedHistoryDialog} from "@shared/components/dialogs/shared-history/shared-history.dialog";
+import {SharedInfoExcludedIgnoredFileDialog} from "@shared/components/dialogs/shared-info-excluded-ignored-file/shared-info-excluded-ignored-file.dialog";
 import {AipStatusNamePresentational} from "@shared/components/presentationals/aip-status-name/aip-status-name.presentational";
 import {AipStatusOrgunitNamePresentational} from "@shared/components/presentationals/aip-status-orgunit-name/aip-status-orgunit-name.presentational";
 import {AipStatusSummaryPresentational} from "@shared/components/presentationals/aip-status-summary/aip-status-summary.presentational";
@@ -92,6 +93,7 @@ const dialogs = [
   SharedAipDetailDialog,
   SharedBaseActionDialog,
   SharedBaseInfoDialog,
+  SharedInfoExcludedIgnoredFileDialog,
 ];
 const contents = [
   SharedSearchableSingleSelectContentPresentational,
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 4270635f8..054f4386a 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -972,8 +972,8 @@
   "dataFileQuickStatus": {
     "cleaned": "File cleaned",
     "excludedFile": "File to remove",
-    "ignoredFile": "File to not ignore or remove",
-    "inError": "In error. File to resume or remove",
+    "ignoredFile": "File to not ignore or remove. Click to see the reason",
+    "inError": "In error. File to resume or remove. Click to see the reason",
     "pending": "Processing",
     "ready": "No action to do"
   },
@@ -1101,6 +1101,22 @@
         },
         "title": "Details of file \"{{name}}\""
       },
+      "dialog": {
+        "excludedIgnoredFile": {
+          "categeroy": {
+            "files": "Files name",
+            "puids": "Puids"
+          },
+          "message": {
+            "excluded": "This file is excluded because it falls within one of the criteria defined below :",
+            "ignored": "This file is ignored because it falls within one of the criteria defined below :"
+          },
+          "title": {
+            "excluded": "Reason for file exclusion",
+            "ignored": "Reason for ignoring the file"
+          }
+        }
+      },
       "inProgress": {
         "noFile": "There is currently no file upload in progress."
       },
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 4270635f8..054f4386a 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -972,8 +972,8 @@
   "dataFileQuickStatus": {
     "cleaned": "File cleaned",
     "excludedFile": "File to remove",
-    "ignoredFile": "File to not ignore or remove",
-    "inError": "In error. File to resume or remove",
+    "ignoredFile": "File to not ignore or remove. Click to see the reason",
+    "inError": "In error. File to resume or remove. Click to see the reason",
     "pending": "Processing",
     "ready": "No action to do"
   },
@@ -1101,6 +1101,22 @@
         },
         "title": "Details of file \"{{name}}\""
       },
+      "dialog": {
+        "excludedIgnoredFile": {
+          "categeroy": {
+            "files": "Files name",
+            "puids": "Puids"
+          },
+          "message": {
+            "excluded": "This file is excluded because it falls within one of the criteria defined below :",
+            "ignored": "This file is ignored because it falls within one of the criteria defined below :"
+          },
+          "title": {
+            "excluded": "Reason for file exclusion",
+            "ignored": "Reason for ignoring the file"
+          }
+        }
+      },
       "inProgress": {
         "noFile": "There is currently no file upload in progress."
       },
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index 2a5830d00..b8a64afa4 100644
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -971,9 +971,9 @@
   },
   "dataFileQuickStatus": {
     "cleaned": "Fichier nettoyé",
-    "excludedFile": "Fichier à supprimer",
-    "ignoredFile": "Fichier à ne pas ignorer ou supprimer",
-    "inError": "En erreur de traitement. Fichier à relancer ou supprimer.",
+    "excludedFile": "Fichier à supprimer. Cliquer pour voir la raison",
+    "ignoredFile": "Fichier à ne pas ignorer ou supprimer. Cliquer pour voir la raison",
+    "inError": "En erreur de traitement. Fichier à relancer ou supprimer",
     "pending": "En cours de traitement",
     "ready": "Aucune action à effectuer"
   },
@@ -1101,6 +1101,22 @@
         },
         "title": "Détails du fichier \"{{name}}\""
       },
+      "dialog": {
+        "excludedIgnoredFile": {
+          "categeroy": {
+            "files": "Nom de fichiers",
+            "puids": "Puids"
+          },
+          "message": {
+            "excluded": "Ce fichier est exclu car il entre dans un des critères définis ci dessous :",
+            "ignored": "Ce fichier est ignoré car il entre dans un des critères définis ci dessous :"
+          },
+          "title": {
+            "excluded": "Motif de l'exclusion du fichier",
+            "ignored": "Motif de l'ignorance du fichier"
+          }
+        }
+      },
       "inProgress": {
         "noFile": "Il n'y a pour l'instant aucun envoi de fichier en cours."
       },
-- 
GitLab