From d9b2e719117ce65830e45b5bf182c014e977e57c Mon Sep 17 00:00:00 2001
From: Florent Poittevin <florent.poittevin@unige.ch>
Date: Fri, 28 Jun 2019 09:36:00 +0200
Subject: [PATCH] 590 Upload datafile WIP

---
 .../components/footer/footer.component.scss   |   1 +
 .../file-tree/file-tree.component.html        |  28 ++
 .../file-tree/file-tree.component.scss        |  17 ++
 .../file-tree/file-tree.component.spec.ts     |  25 ++
 .../file-tree/file-tree.component.ts          | 132 ++++++++
 .../file-upload/file-upload.component.html    |  42 +++
 .../file-upload/file-upload.component.scss    |   7 +
 .../file-upload/file-upload.component.spec.ts |  25 ++
 .../file-upload/file-upload.component.ts      |  43 +++
 .../containers/file/file.container.html       |   8 +
 .../containers/file/file.container.scss       |   0
 .../deposit/containers/file/file.container.ts |  36 +++
 src/app/deposit/deposit.action.ts             | 112 ++++++-
 src/app/deposit/deposit.module.ts             |  10 +-
 src/app/deposit/deposit.state.ts              |  79 +++++
 src/app/deposit/models/checksum.model.ts      |   7 +
 .../deposit/models/deposit-data-file.model.ts |  28 ++
 src/app/deposit/models/file-format.model.ts   |   8 +
 .../models/upload-file-status.model.ts        |   6 +
 src/app/deposit/views/detail/detail.view.html |  36 ++-
 src/app/deposit/views/detail/detail.view.ts   |   3 +
 src/app/deposit/views/edit/edit.view.html     |  29 +-
 src/app/deposit/views/edit/edit.view.ts       |   4 +-
 src/app/material.module.ts                    |   3 +-
 .../views/detail/detail.view.html             |   2 +-
 .../views/edit/edit.view.ts                   |   2 +-
 src/app/shared/crud.action.ts                 |  72 +++--
 src/app/shared/enums/api-action.enum.ts       |  39 +++
 src/app/shared/organizational-unit.action.ts  |   1 +
 src/app/shared/services/api.service.ts        |  12 +
 src/app/shared/utils/crud-action.util.ts      |  10 +-
 .../abstract-create/abstract-create.view.ts   |   2 -
 .../abstract-detail/abstract-detail.view.ts   |   4 +
 .../views/abstract-edit/abstract-edit.view.ts |   8 +-
 .../abstract-list/abstract-list.view.html     |   2 +-
 src/assets/i18n/de.json                       |  20 +-
 src/assets/i18n/en.json                       |   8 +
 src/assets/i18n/fr.json                       | 282 +++++++++---------
 src/environments/environment.dlcmtest.ts      |   6 +-
 src/sass/abstracts/_variables.scss            |   3 +
 src/sass/main.scss                            |   6 +
 41 files changed, 954 insertions(+), 214 deletions(-)
 create mode 100644 src/app/deposit/components/file-tree/file-tree.component.html
 create mode 100644 src/app/deposit/components/file-tree/file-tree.component.scss
 create mode 100644 src/app/deposit/components/file-tree/file-tree.component.spec.ts
 create mode 100644 src/app/deposit/components/file-tree/file-tree.component.ts
 create mode 100644 src/app/deposit/components/file-upload/file-upload.component.html
 create mode 100644 src/app/deposit/components/file-upload/file-upload.component.scss
 create mode 100644 src/app/deposit/components/file-upload/file-upload.component.spec.ts
 create mode 100644 src/app/deposit/components/file-upload/file-upload.component.ts
 create mode 100644 src/app/deposit/containers/file/file.container.html
 create mode 100644 src/app/deposit/containers/file/file.container.scss
 create mode 100644 src/app/deposit/containers/file/file.container.ts
 create mode 100644 src/app/deposit/models/checksum.model.ts
 create mode 100644 src/app/deposit/models/deposit-data-file.model.ts
 create mode 100644 src/app/deposit/models/file-format.model.ts
 create mode 100644 src/app/deposit/models/upload-file-status.model.ts

diff --git a/src/app/components/footer/footer.component.scss b/src/app/components/footer/footer.component.scss
index 21cabfd6b..a52b3cbd3 100644
--- a/src/app/components/footer/footer.component.scss
+++ b/src/app/components/footer/footer.component.scss
@@ -14,6 +14,7 @@ $height: 40px;
   display: flex;
   justify-content: center;
   align-items: center;
+  z-index: $index-footer;
 }
 
 .hidden-footer {
diff --git a/src/app/deposit/components/file-tree/file-tree.component.html b/src/app/deposit/components/file-tree/file-tree.component.html
new file mode 100644
index 000000000..b1c91ec7e
--- /dev/null
+++ b/src/app/deposit/components/file-tree/file-tree.component.html
@@ -0,0 +1,28 @@
+<mat-tree *ngIf="dataSource"
+          [dataSource]="dataSource"
+          [treeControl]="treeControl"
+          class="file-tree">
+  <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
+    <li class="mat-tree-node">
+      <!-- use a disabled button to provide padding for tree leaf -->
+      <button mat-icon-button disabled></button>
+      {{node.name}} - {{getStatus(node)}}
+    </li>
+  </mat-tree-node>
+
+  <!-- This is the tree node template for expandable nodes -->
+  <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">
+    <li>
+      <div class="mat-tree-node">
+        <button mat-icon-button matTreeNodeToggle
+                [attr.aria-label]="'toggle ' + node.name">
+          <fa-icon [icon]="treeControl.isExpanded(node) ? 'chevron-down' : 'chevron-right'"></fa-icon>
+        </button>
+        {{node.name}}
+      </div>
+      <ul [class.file-tree-invisible]="!treeControl.isExpanded(node)">
+        <ng-container matTreeNodeOutlet></ng-container>
+      </ul>
+    </li>
+  </mat-nested-tree-node>
+</mat-tree>
diff --git a/src/app/deposit/components/file-tree/file-tree.component.scss b/src/app/deposit/components/file-tree/file-tree.component.scss
new file mode 100644
index 000000000..6ceec9236
--- /dev/null
+++ b/src/app/deposit/components/file-tree/file-tree.component.scss
@@ -0,0 +1,17 @@
+@import "abstracts/variables";
+
+$line-item-size: 30px;
+
+.file-tree-invisible {
+  display: none;
+}
+
+.mat-tree-node {
+  min-height: $line-item-size;
+
+  .mat-icon-button {
+    height: $line-item-size;
+    width: $line-item-size;
+    line-height: $line-item-size;
+  }
+}
diff --git a/src/app/deposit/components/file-tree/file-tree.component.spec.ts b/src/app/deposit/components/file-tree/file-tree.component.spec.ts
new file mode 100644
index 000000000..53a7c6364
--- /dev/null
+++ b/src/app/deposit/components/file-tree/file-tree.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FileTreeComponent } from './file-tree.component';
+
+describe('FileTreeComponent', () => {
+  let component: FileTreeComponent;
+  let fixture: ComponentFixture<FileTreeComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FileTreeComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FileTreeComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/deposit/components/file-tree/file-tree.component.ts b/src/app/deposit/components/file-tree/file-tree.component.ts
new file mode 100644
index 000000000..231b90233
--- /dev/null
+++ b/src/app/deposit/components/file-tree/file-tree.component.ts
@@ -0,0 +1,132 @@
+import {NestedTreeControl} from "@angular/cdk/tree";
+import {ChangeDetectionStrategy, Component, Input} from "@angular/core";
+import {MatTreeNestedDataSource} from "@angular/material";
+import {DepositDataFileModel} from "@app/deposit/models/deposit-data-file.model";
+import {StringUtil} from "@app/shared/utils/string.util";
+
+interface FileNode {
+  name: string;
+  file?: DepositDataFileModel;
+  children?: FileNode[];
+}
+
+@Component({
+  selector: "dlcm-file-tree",
+  templateUrl: "./file-tree.component.html",
+  styleUrls: ["./file-tree.component.scss"],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FileTreeComponent {
+  private _listDepositDataFile: DepositDataFileModel[];
+
+  @Input()
+  readonly: boolean;
+
+  @Input()
+  set listDepositDataFile(val: DepositDataFileModel[]) {
+    this._listDepositDataFile = val;
+    this.adaptListDepositDataFileToTree();
+    this.applyTreeToDataSource();
+  }
+
+  get listDepositDataFile(): DepositDataFileModel[] {
+    return this._listDepositDataFile;
+  }
+
+  fileTree: FileNode[];
+
+  treeControl = new NestedTreeControl<FileNode>(node => node.children);
+  dataSource = new MatTreeNestedDataSource<FileNode>();
+
+  private readonly SEPARATOR = "/";
+  private readonly ROOT = this.SEPARATOR;
+
+  constructor() {
+  }
+
+  adaptListDepositDataFileToTree(): void {
+    this.extractDataFile();
+    const copy = [...this.listDepositDataFile];
+    copy.sort((a, b) => {
+      if (a.relativeLocation < b.relativeLocation) {
+        return -1;
+      }
+      if (a.relativeLocation > b.relativeLocation) {
+        return 1;
+      }
+      return 0;
+    });
+    copy.forEach(dataFile => {
+      this.treatmentDataFile(dataFile);
+    });
+  }
+
+  private extractDataFile(): void {
+    this.fileTree = [];
+  }
+
+  private treatmentDataFile(dataFile): void {
+    const isIntoRootFolder = dataFile.relativeLocation === this.ROOT;
+    if (isIntoRootFolder) {
+      this.addFileToRootDataFile(dataFile);
+    } else {
+      const relativeLocation = dataFile.relativeLocation;
+      const pathParts = relativeLocation.split(this.SEPARATOR).filter(part => part !== StringUtil.stringEmpty);
+      this.recursiveAddFolder(this.fileTree, dataFile, pathParts);
+    }
+  }
+
+  private recursiveAddFolder(levelParent: FileNode[], dataFile: DepositDataFileModel, pathParts: string[]): void {
+    if (pathParts.length === 0) {
+      return;
+    }
+    const firstFolderName = pathParts.splice(0, 1)[0];
+    let parentFileNode = levelParent.find(f => f.name === firstFolderName);
+
+    parentFileNode = this.createLevelIfNotExist(parentFileNode, firstFolderName, levelParent);
+    this.initChildrenAttributIfUndefined(parentFileNode);
+
+    if (pathParts.length === 0) {
+      parentFileNode.children.push(this.getFileNodeFormDataFile(dataFile));
+    } else {
+      this.recursiveAddFolder(parentFileNode.children, dataFile, pathParts);
+    }
+  }
+
+  private createLevelIfNotExist(parentFileNode: FileNode, firstFolderName: string, levelParent: FileNode[]): FileNode {
+    if (parentFileNode === undefined) {
+      parentFileNode = {
+        name: firstFolderName,
+      } as FileNode;
+      levelParent.push(parentFileNode);
+    }
+    return parentFileNode;
+  }
+
+  private initChildrenAttributIfUndefined(parentFileNode) {
+    if (parentFileNode.children === undefined) {
+      parentFileNode.children = [];
+    }
+  }
+
+  private addFileToRootDataFile(dataFile): void {
+    this.fileTree.push(this.getFileNodeFormDataFile(dataFile));
+  }
+
+  private getFileNodeFormDataFile(dataFile): FileNode {
+    return {
+      name: dataFile.fileName,
+      file: dataFile,
+    } as FileNode;
+  }
+
+  applyTreeToDataSource(): void {
+    this.dataSource.data = this.fileTree;
+  }
+
+  hasChild = (_: number, node: FileNode) => !!node.children && node.children.length > 0;
+
+  getStatus(node: FileNode) {
+    return node.file.status;
+  }
+}
diff --git a/src/app/deposit/components/file-upload/file-upload.component.html b/src/app/deposit/components/file-upload/file-upload.component.html
new file mode 100644
index 000000000..4177dac95
--- /dev/null
+++ b/src/app/deposit/components/file-upload/file-upload.component.html
@@ -0,0 +1,42 @@
+<div *ngIf="uploadStatus">
+  <div *ngIf="uploadStatus.status === 'error'">
+    {{uploadStatus.message}}
+  </div>
+  <div *ngIf="uploadStatus.status === 'progress'">
+    <div role="progressbar"
+         [style.width.%]="uploadStatus.progress"
+         aria-valuenow="25"
+         aria-valuemin="0"
+         aria-valuemax="100">
+      {{uploadStatus.progress}}%
+    </div>
+  </div>
+  <div>
+    {{uploadStatus.result}}
+  </div>
+</div>
+
+<form [formGroup]="form"
+      (ngSubmit)="onSubmit()">
+
+  <button mat-button
+          color="accent"
+          type="button"
+          (click)="fileInput.click()">
+    {{'deposit.file.search' | translate}}
+    <input #fileInput
+           class="hide"
+           type="file"
+           [name]="file"
+           (change)="onFileChange($event)"/>
+  </button>
+
+  <span class="file-name">{{form.get(file).value.name}}</span>
+
+  <button [disabled]="!form.valid"
+          mat-button
+          color="primary"
+          type="submit">
+    {{'deposit.file.uploadButton' | translate}}
+  </button>
+</form>
diff --git a/src/app/deposit/components/file-upload/file-upload.component.scss b/src/app/deposit/components/file-upload/file-upload.component.scss
new file mode 100644
index 000000000..5edc659b9
--- /dev/null
+++ b/src/app/deposit/components/file-upload/file-upload.component.scss
@@ -0,0 +1,7 @@
+.hide {
+  display: none;
+}
+
+.file-name {
+  padding: 0 20px;
+}
diff --git a/src/app/deposit/components/file-upload/file-upload.component.spec.ts b/src/app/deposit/components/file-upload/file-upload.component.spec.ts
new file mode 100644
index 000000000..0071ed860
--- /dev/null
+++ b/src/app/deposit/components/file-upload/file-upload.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FileUploadComponent } from './file-upload.component';
+
+describe('FileUploadComponent', () => {
+  let component: FileUploadComponent;
+  let fixture: ComponentFixture<FileUploadComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FileUploadComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FileUploadComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/deposit/components/file-upload/file-upload.component.ts b/src/app/deposit/components/file-upload/file-upload.component.ts
new file mode 100644
index 000000000..3e6c06d90
--- /dev/null
+++ b/src/app/deposit/components/file-upload/file-upload.component.ts
@@ -0,0 +1,43 @@
+import {ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output} from "@angular/core";
+import {FormBuilder, FormGroup, Validators} from "@angular/forms";
+import {UploadFileStatusModel} from "@app/deposit/models/upload-file-status.model";
+import {BaseDirective} from "@app/shared/directives/base.directive";
+
+@Component({
+  selector: "dlcm-file-upload",
+  templateUrl: "./file-upload.component.html",
+  styleUrls: ["./file-upload.component.scss"],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FileUploadComponent extends BaseDirective implements OnInit {
+  @Input()
+  uploadStatus: UploadFileStatusModel;
+
+  @Output()
+  fileUploadEvent: EventEmitter<File>;
+
+  form: FormGroup;
+  readonly file = "file";
+
+  constructor(protected fb: FormBuilder) {
+    super();
+    this.fileUploadEvent = new EventEmitter<File>();
+  }
+
+  ngOnInit() {
+    this.form = this.fb.group({
+      [this.file]: ["", Validators.required],
+    });
+  }
+
+  onFileChange(event) {
+    if (event.target.files.length > 0) {
+      const file: File = event.target.files[0];
+      this.form.get(this.file).setValue(file);
+    }
+  }
+
+  onSubmit() {
+    this.fileUploadEvent.emit(this.form.get(this.file).value as File);
+  }
+}
diff --git a/src/app/deposit/containers/file/file.container.html b/src/app/deposit/containers/file/file.container.html
new file mode 100644
index 000000000..7f66c667b
--- /dev/null
+++ b/src/app/deposit/containers/file/file.container.html
@@ -0,0 +1,8 @@
+<dlcm-file-upload *ngIf="readonly === false"
+                  [uploadStatus]="uploadStatus$ | async"
+                  (fileUploadEvent)="uploadFile($event)"
+></dlcm-file-upload>
+
+<dlcm-file-tree [listDepositDataFile]="listDataFile$ | async"
+                [readonly]="readonly"
+></dlcm-file-tree>
diff --git a/src/app/deposit/containers/file/file.container.scss b/src/app/deposit/containers/file/file.container.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/deposit/containers/file/file.container.ts b/src/app/deposit/containers/file/file.container.ts
new file mode 100644
index 000000000..599c9eb19
--- /dev/null
+++ b/src/app/deposit/containers/file/file.container.ts
@@ -0,0 +1,36 @@
+import {ChangeDetectionStrategy, Component, Input} from "@angular/core";
+import {DepositAction} from "@app/deposit/deposit.action";
+import {DepositStateModel} from "@app/deposit/deposit.state";
+import {DepositDataFileModel} from "@app/deposit/models/deposit-data-file.model";
+import {UploadFileStatusModel} from "@app/deposit/models/upload-file-status.model";
+import {BaseDirective} from "@app/shared/directives/base.directive";
+import {StateEnum} from "@app/shared/enums/state.enum";
+import {Select, Store} from "@ngxs/store";
+import {Observable} from "rxjs";
+
+@Component({
+  selector: "dlcm-file-deposit",
+  templateUrl: "./file.container.html",
+  styleUrls: ["./file.container.scss"],
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class FileContainer extends BaseDirective {
+  @Select((state) => (state[StateEnum.deposit] as DepositStateModel).listDataFile) listDataFile$: Observable<DepositDataFileModel[]>;
+  @Select((state) => (state[StateEnum.deposit] as DepositStateModel).uploadStatus) uploadStatus$: Observable<UploadFileStatusModel>;
+
+  @Input()
+  parentResId: string;
+
+  @Input()
+  readonly: boolean;
+
+  constructor(protected store: Store) {
+
+    super();
+    this.readonly = false;
+  }
+
+  private uploadFile(file: File) {
+    this.store.dispatch(new DepositAction.SendData(this.parentResId, File));
+  }
+}
diff --git a/src/app/deposit/deposit.action.ts b/src/app/deposit/deposit.action.ts
index 5b8944643..b2fc91f30 100644
--- a/src/app/deposit/deposit.action.ts
+++ b/src/app/deposit/deposit.action.ts
@@ -1,5 +1,7 @@
+import {DepositDataFileModel} from "@app/deposit/models/deposit-data-file.model";
 import {DepositsModel} from "@app/generated-api";
 import {CrudAction} from "@app/shared/crud.action";
+import {ApiResourceNameEnum} from "@app/shared/enums/api-resource-name.enum";
 import {StateEnum} from "@app/shared/enums/state.enum";
 import {CollectionTypedModel} from "@app/shared/models/collection-typed.model";
 import {QueryParametersModel} from "@app/shared/models/query-parameters.model";
@@ -33,7 +35,9 @@ export namespace DepositAction {
   }
 
   export class ChangeQueryParameters extends CrudAction.ChangeQueryParameters {
-    static readonly type = StringUtil.format(CrudAction.ChangeQueryParameters.abstractType, state);
+    static get type() {
+      return StringUtil.format(this.abstractType, state);
+    }
 
     constructor(public queryParameters: QueryParametersModel) {
       super(queryParameters);
@@ -159,4 +163,110 @@ export namespace DepositAction {
       super();
     }
   }
+
+  export class SendData {
+    static readonly type = `[${state}] Upload file`;
+
+    // StringUtil.format(CrudAction.GetAllSubResource.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId, public file) {
+      // super(parentId, queryParameters);
+    }
+  }
+
+  export class GetAllSubResourceData extends CrudAction.GetAllSubResource {
+    static readonly type = StringUtil.format(CrudAction.GetAllSubResource.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId, public queryParameters?: QueryParametersModel) {
+      super(parentId, queryParameters);
+    }
+  }
+
+  export class GetAllSubResourceDataSuccess extends CrudAction.GetAllSubResourceSuccess<DepositDataFileModel> {
+    static readonly type = StringUtil.format(CrudAction.GetAllSubResourceSuccess.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public list: CollectionTypedModel<DepositDataFileModel>) {
+      super(list);
+    }
+  }
+
+  export class GetAllSubResourceDataFail extends CrudAction.GetAllSubResourceFail {
+    static readonly type = StringUtil.format(CrudAction.GetAllSubResourceFail.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor() {
+      super();
+    }
+  }
+
+  export class UpdateSubResourceData extends CrudAction.UpdateSubResource {
+    static readonly type = StringUtil.format(CrudAction.UpdateSubResource.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId, public oldResId: string[], newResId: string[]) {
+      super(parentId, oldResId, newResId);
+    }
+  }
+
+  export class UpdateSubResourceDataSuccess extends CrudAction.UpdateSubResourceSuccess {
+    static readonly type = StringUtil.format(CrudAction.UpdateSubResourceSuccess.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId) {
+      super(parentId);
+    }
+  }
+
+  export class UpdateSubResourceDataFail extends CrudAction.UpdateSubResourceFail {
+    static readonly type = StringUtil.format(CrudAction.UpdateSubResourceFail.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId) {
+      super(parentId);
+    }
+  }
+
+  export class CreateSubResourceData<DepositDataFile> extends CrudAction.CreateCompoSubResource<DepositDataFile> {
+    static readonly type = StringUtil.format(CrudAction.CreateCompoSubResource.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId, public model: DepositDataFile) {
+      super(parentId, model);
+    }
+  }
+
+  export class CreateSubResourceDataSuccess extends CrudAction.CreateSubResourceSuccess {
+    static readonly type = StringUtil.format(CrudAction.CreateSubResourceSuccess.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor() {
+      super();
+    }
+  }
+
+  export class CreateSubResourceDataFail extends CrudAction.CreateSubResourceFail {
+    static readonly type = StringUtil.format(CrudAction.CreateSubResourceFail.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor() {
+      super();
+    }
+  }
+
+  export class DeleteSubResourceData extends CrudAction.DeleteSubResource {
+    static readonly type = StringUtil.format(CrudAction.DeleteSubResource.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor(public parentId, public listResId: string[]) {
+      super(parentId, listResId);
+    }
+  }
+
+  export class DeleteSubResourceDataSuccess extends CrudAction.DeleteSubResourceSuccess {
+    static readonly type = StringUtil.format(CrudAction.DeleteSubResourceSuccess.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor() {
+      super();
+    }
+  }
+
+  export class DeleteSubResourceDataFail extends CrudAction.DeleteSubResourceFail {
+    static readonly type = StringUtil.format(CrudAction.DeleteSubResourceFail.abstractType, state, ApiResourceNameEnum.DATAFILE);
+
+    constructor() {
+      super();
+    }
+  }
 }
diff --git a/src/app/deposit/deposit.module.ts b/src/app/deposit/deposit.module.ts
index e47144742..3884cf537 100644
--- a/src/app/deposit/deposit.module.ts
+++ b/src/app/deposit/deposit.module.ts
@@ -1,5 +1,6 @@
 import {NgModule} from "@angular/core";
 import {FormComponent} from "@app/deposit/components/form/form.component";
+import {FileContainer} from "@app/deposit/containers/file/file.container";
 import {DepositRoutingModule} from "@app/deposit/deposit-routing.module";
 import {DepositState} from "@app/deposit/deposit.state";
 import {DeleteDialog} from "@app/deposit/dialogs/delete/delete.dialog";
@@ -11,6 +12,8 @@ import {TranslateModule} from "@ngx-translate/core";
 import {NgxsModule} from "@ngxs/store";
 import {CreateView} from "./views/create/create.view";
 import {DetailView} from "./views/detail/detail.view";
+import { FileTreeComponent } from './components/file-tree/file-tree.component';
+import { FileUploadComponent } from './components/file-upload/file-upload.component';
 
 const views = [
   ListView,
@@ -18,7 +21,9 @@ const views = [
   DetailView,
   EditView,
 ];
-const containers = [];
+const containers = [
+  FileContainer,
+];
 const dialogs = [
   DeleteDialog,
 ];
@@ -32,6 +37,8 @@ const components = [
     ...containers,
     ...dialogs,
     ...components,
+    FileTreeComponent,
+    FileUploadComponent,
   ],
   imports: [
     SharedModule,
@@ -46,7 +53,6 @@ const components = [
   ],
   exports: [
     ...views,
-    ...containers,
   ],
   providers: [
     DepositService,
diff --git a/src/app/deposit/deposit.state.ts b/src/app/deposit/deposit.state.ts
index 01a2a641b..685807a07 100644
--- a/src/app/deposit/deposit.state.ts
+++ b/src/app/deposit/deposit.state.ts
@@ -1,6 +1,10 @@
+import {HttpEventType} from "@angular/common/http";
 import {DepositAction} from "@app/deposit/deposit.action";
+import {DepositDataFileModel} from "@app/deposit/models/deposit-data-file.model";
+import {UploadFileStatusModel} from "@app/deposit/models/upload-file-status.model";
 import {DepositsModel} from "@app/generated-api";
 import {CrudState, CrudStateModel} from "@app/shared/crud.state";
+import {ApiActionEnum} from "@app/shared/enums/api-action.enum";
 import {ApiResourceNameEnum} from "@app/shared/enums/api-resource-name.enum";
 import {PreIngestResourceApiEnum, ResourceApiEnum} from "@app/shared/enums/api.enum";
 import {StateEnum} from "@app/shared/enums/state.enum";
@@ -14,9 +18,13 @@ import {SubmissionPolicyAction} from "@app/shared/submission-policy.action";
 import {NotificationService} from "@app/shared/services/notification.service";
 import {Action, State, StateContext} from "@ngxs/store";
 import {Observable} from "rxjs";
+import {map} from "rxjs/operators";
 
 export interface DepositStateModel extends CrudStateModel<DepositsModel> {
   resourceIsLoading: boolean;
+  listDataFile: DepositDataFileModel[];
+  isLoadingDataFile: boolean;
+  uploadStatus: UploadFileStatusModel;
 }
 
 @State<DepositStateModel>({
@@ -28,6 +36,9 @@ export interface DepositStateModel extends CrudStateModel<DepositsModel> {
     current: null,
     queryParameters: new QueryParametersModel(),
     resourceIsLoading: false,
+    listDataFile: [],
+    isLoadingDataFile: false,
+    uploadStatus: null,
   },
 })
 export class DepositState extends CrudState<DepositsModel> {
@@ -142,4 +153,72 @@ export class DepositState extends CrudState<DepositsModel> {
   deleteFail(ctx: StateContext<DepositStateModel>, action: DepositAction.DeleteFail): void {
     super.deleteFail(ctx, action);
   }
+
+  @Action(DepositAction.GetAllSubResourceData, {cancelUncompleted: true})
+  getAllDataFile(ctx: StateContext<DepositStateModel>, action: DepositAction.GetAllSubResourceData): Observable<CollectionTypedModel<DepositDataFileModel>> {
+    return super.getAllSubResource<DepositDataFileModel>(ctx, action, ApiResourceNameEnum.DATAFILE, action.parentId);
+  }
+
+  @Action(DepositAction.GetAllSubResourceDataSuccess)
+  getAllDataFileSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.GetAllSubResourceDataSuccess): void {
+    ctx.patchState({
+      listDataFile: action.list._data,
+      isLoadingDataFile: false,
+    });
+  }
+
+  @Action(DepositAction.GetAllSubResourceDataFail)
+  getAllDataFileFail(ctx: StateContext<DepositStateModel>, action: DepositAction.GetAllSubResourceDataFail): void {
+    ctx.patchState({
+      isLoadingDataFile: false,
+    });
+  }
+
+  // @Action(DepositAction.CreateSubResourceData, {cancelUncompleted: true})
+  // createDataFile(ctx: StateContext<DepositStateModel>, action: DepositAction.CreateSubResourceData): Observable<string[]> {
+  //   return super.createSubResource(ctx, action, ApiResourceNameEnum.DATAFILE, action.parentId);
+  // }
+
+  @Action(DepositAction.DeleteSubResourceData, {cancelUncompleted: true})
+  deleteDataFile(ctx: StateContext<DepositStateModel>, action: DepositAction.DeleteSubResourceData): Observable<string[]> {
+    return super.deleteSubResource(ctx, action, ApiResourceNameEnum.DATAFILE, action.parentId);
+  }
+
+  @Action(DepositAction.SendData)
+  sendData(ctx: StateContext<DepositStateModel>, action: DepositAction.SendData): Observable<any> {
+    const formData = new FormData();
+    formData.append("file", action.file);
+    return this.apiService.upload(`${PreIngestResourceApiEnum.deposits}/${action.parentId}/${ApiActionEnum.UL}`, action.file)
+      .pipe(
+        map((event) => {
+          switch (event.type) {
+            case HttpEventType.UploadProgress:
+              const progress = Math.round(100 * event.loaded / event.total);
+              ctx.patchState({
+                uploadStatus: {
+                  progress,
+                  status: "progress",
+                },
+              });
+              return;
+            case HttpEventType.Response:
+              return event.body;
+            default:
+              return `Unhandled event: ${event.type}`;
+          }
+        }),
+      );
+    // .pipe(
+    //   tap(() => {
+    //     // ctx.dispatch(CrudActionUtil.createSuccess(this.state));
+    //     console.error("OK");
+    //   }),
+    //   catchError(error => {
+    //     // ctx.dispatch(CrudActionUtil.createFail(this.state));
+    //     console.error("NOT OK");
+    //
+    //     throw error;
+    //   }),
+    // );
+  }
 }
diff --git a/src/app/deposit/models/checksum.model.ts b/src/app/deposit/models/checksum.model.ts
new file mode 100644
index 000000000..662824377
--- /dev/null
+++ b/src/app/deposit/models/checksum.model.ts
@@ -0,0 +1,7 @@
+export interface ChecksumModel {
+  checksumAlgo?: string;
+  checksumType?: string;
+  checksumOrigin?: string;
+  checksum?: string;
+  creationTime?: string;
+}
diff --git a/src/app/deposit/models/deposit-data-file.model.ts b/src/app/deposit/models/deposit-data-file.model.ts
new file mode 100644
index 000000000..b4389823a
--- /dev/null
+++ b/src/app/deposit/models/deposit-data-file.model.ts
@@ -0,0 +1,28 @@
+import {ChecksumModel} from "@app/deposit/models/checksum.model";
+import {FileFormatModel} from "@app/deposit/models/file-format.model";
+import {ChangeInfoModel, DepositsModel} from "@app/generated-api";
+import StatusModelEnum = DepositsModel.StatusModelEnum;
+
+export interface DepositDataFileModel {
+  resId?: string;
+  creation?: ChangeInfoModel;
+  lastUpdate?: ChangeInfoModel;
+  packageId?: string;
+  sourceData?: string;
+  relativeLocation?: string;
+  finalData?: string;
+  fileSize?: number;
+  status?: StatusModelEnum;
+  statusMessage?: string;
+  dataCategory?: string; // Enum get with ApiActionEnum.LIST_DATA_CATEGORY
+  dataType?: string; // Enum get with ApiActionEnum.LIST_DATA_TYPE
+  complianceLevel?: string;
+  fileFormat?: FileFormatModel;
+  virusCheck?: any;
+  fileName?: string;
+  initialPath?: string;
+  smartSize?: string;
+  dataFile?: any; // To check
+  checksums?: ChecksumModel[];
+  available?: boolean;
+}
diff --git a/src/app/deposit/models/file-format.model.ts b/src/app/deposit/models/file-format.model.ts
new file mode 100644
index 000000000..23d3fc49e
--- /dev/null
+++ b/src/app/deposit/models/file-format.model.ts
@@ -0,0 +1,8 @@
+export interface FileFormatModel {
+  contentType?: string;
+  format?: string;
+  version?: string;
+  puid?: string;
+  md5?: string;
+  details?: string;
+}
diff --git a/src/app/deposit/models/upload-file-status.model.ts b/src/app/deposit/models/upload-file-status.model.ts
new file mode 100644
index 000000000..11874aecb
--- /dev/null
+++ b/src/app/deposit/models/upload-file-status.model.ts
@@ -0,0 +1,6 @@
+export interface UploadFileStatusModel {
+  status?: string;
+  progress?: number;
+  message?: string;
+  result?: any;
+}
diff --git a/src/app/deposit/views/detail/detail.view.html b/src/app/deposit/views/detail/detail.view.html
index 32249a57b..d2ef91722 100644
--- a/src/app/deposit/views/detail/detail.view.html
+++ b/src/app/deposit/views/detail/detail.view.html
@@ -1,19 +1,35 @@
 <div class="button-toolbar">
   <button mat-flat-button color="primary" (click)="edit()">{{KEY_EDIT_BUTTON | translate}}</button>
-  <button mat-flat-button color="secondary" (click)="delete()">{{KEY_DELETE_BUTTON | translate}}</button>
+  <button mat-flat-button color="accent" (click)="delete()">{{KEY_DELETE_BUTTON | translate}}</button>
 </div>
 
 <div class="wrapper">
   <div class="spinner-wrapper" *ngIf="isLoading$ | async">
     <mat-spinner></mat-spinner>
   </div>
-  <dlcm-form-deposit *ngIf="(current$| async) != null"
-                     [model]="current$| async"
-                     [languages]="languages$ | async"
-                     [licenses]="licenses$ | async"
-                     [submissionPolicies]="submissionPolicies$ | async"
-                     [preservationPolicies]="preservationPolicies$ | async"
-                     [organizationalUnits]="organizationalUnits$ | async"
-                     [readonly]="true"
-  ></dlcm-form-deposit>
+
+  <mat-tab-group mat-align-tabs="center"
+                 animationDuration="0ms"
+  >
+    <mat-tab [label]="'deposit.tab.details' | translate">
+      <dlcm-form-deposit *ngIf="(current$| async) != null"
+                         [model]="current$| async"
+                         [languages]="languages$ | async"
+                         [licenses]="licenses$ | async"
+                         [submissionPolicies]="submissionPolicies$ | async"
+                         [preservationPolicies]="preservationPolicies$ | async"
+                         [organizationalUnits]="organizationalUnits$ | async"
+                         [readonly]="true"
+      ></dlcm-form-deposit>
+    </mat-tab>
+    <mat-tab [label]="'deposit.tab.datafiles' | translate">
+      <dlcm-file-deposit [parentResId]="getResId()"
+                         [readonly]="true"
+      ></dlcm-file-deposit>
+    </mat-tab>
+  </mat-tab-group>
+
 </div>
+
+
+
diff --git a/src/app/deposit/views/detail/detail.view.ts b/src/app/deposit/views/detail/detail.view.ts
index 18317f9d2..6ee60b4ba 100644
--- a/src/app/deposit/views/detail/detail.view.ts
+++ b/src/app/deposit/views/detail/detail.view.ts
@@ -3,6 +3,7 @@ import {MatDialog} from "@angular/material";
 import {ActivatedRoute} from "@angular/router";
 import {DepositAction} from "@app/deposit/deposit.action";
 import {DepositStateModel} from "@app/deposit/deposit.state";
+import {DepositDataFileModel} from "@app/deposit/models/deposit-data-file.model";
 import {DepositsModel, LanguagesModel, LicensesModel, PreservationPoliciesModel, SubmissionPoliciesModel} from "@app/generated-api";
 import {OrganizationalUnitModule} from "@app/organizational-unit/organizational-unit.module";
 import {StateEnum} from "@app/shared/enums/state.enum";
@@ -30,6 +31,7 @@ export class DetailView extends AbstractDetailView<DepositsModel, DepositStateMo
   @Select((state) => ((state[StateEnum.shared] as SharedStateModel).submissionPolicy as SubmissionPolicyStateModel).list) submissionPolicies$: Observable<SubmissionPoliciesModel[]>;
   @Select((state) => ((state[StateEnum.shared] as SharedStateModel).preservationPolicy as PreservationPolicyStateModel).list) preservationPolicies$: Observable<PreservationPoliciesModel[]>;
   @Select((state) => (state[StateEnum.organizationalUnit] as OrganizationalUnitStateModel).list) organizationalUnits$: Observable<OrganizationalUnitModule[]>;
+  @Select((state) => (state[StateEnum.deposit] as DepositStateModel).listDataFile) listDataFile$: Observable<DepositDataFileModel[]>;
 
   readonly KEY_PARAM_NAME: string = "title";
   readonly KEY_DELETE_BUTTON: string = TRANSLATE("deposit.delete");
@@ -47,5 +49,6 @@ export class DetailView extends AbstractDetailView<DepositsModel, DepositStateMo
   }
 
   getSubResourceWithParentId(id: string): void {
+    this.store.dispatch(new DepositAction.GetAllSubResourceData(id));
   }
 }
diff --git a/src/app/deposit/views/edit/edit.view.html b/src/app/deposit/views/edit/edit.view.html
index d821600f7..f61df1275 100644
--- a/src/app/deposit/views/edit/edit.view.html
+++ b/src/app/deposit/views/edit/edit.view.html
@@ -1,10 +1,21 @@
-<dlcm-form-deposit *ngIf="(current$ | async)"
-                   [model]="current$ | async"
-                   [languages]="languages$ | async"
-                   [licenses]="licenses$ | async"
-                   [submissionPolicies]="submissionPolicies$ | async"
-                   [preservationPolicies]="preservationPolicies$ | async"
-                   [organizationalUnits]="organizationalUnits$ | async"
-                   (submitEvent)="update($event)"
-></dlcm-form-deposit>
 <mat-spinner *ngIf="(isLoading$ | async)"></mat-spinner>
+
+<mat-tab-group mat-align-tabs="center"
+               animationDuration="0ms"
+>
+  <mat-tab [label]="'deposit.tab.details' | translate">
+    <dlcm-form-deposit *ngIf="(current$ | async)"
+                       [model]="current$ | async"
+                       [languages]="languages$ | async"
+                       [licenses]="licenses$ | async"
+                       [submissionPolicies]="submissionPolicies$ | async"
+                       [preservationPolicies]="preservationPolicies$ | async"
+                       [organizationalUnits]="organizationalUnits$ | async"
+                       (submitEvent)="update($event)"
+    ></dlcm-form-deposit>
+  </mat-tab>
+  <mat-tab [label]="'deposit.tab.datafiles' | translate">
+    <dlcm-file-deposit [parentResId]="getResId()"
+    ></dlcm-file-deposit>
+  </mat-tab>
+</mat-tab-group>
diff --git a/src/app/deposit/views/edit/edit.view.ts b/src/app/deposit/views/edit/edit.view.ts
index 7ccd07a95..c45598732 100644
--- a/src/app/deposit/views/edit/edit.view.ts
+++ b/src/app/deposit/views/edit/edit.view.ts
@@ -2,6 +2,7 @@ import {ChangeDetectionStrategy, Component, OnInit} from "@angular/core";
 import {ActivatedRoute} from "@angular/router";
 import {DepositAction} from "@app/deposit/deposit.action";
 import {DepositStateModel} from "@app/deposit/deposit.state";
+import {DepositDataFileModel} from "@app/deposit/models/deposit-data-file.model";
 import {DepositsModel, LanguagesModel, LicensesModel, PreservationPoliciesModel, SubmissionPoliciesModel} from "@app/generated-api";
 import {OrganizationalUnitModule} from "@app/organizational-unit/organizational-unit.module";
 import {StateEnum} from "@app/shared/enums/state.enum";
@@ -39,6 +40,7 @@ export class EditView extends AbstractEditView<DepositsModel, DepositStateModel>
     this.store.dispatch(new OrgUnitAction.GetAll());
   }
 
-  getSubresourceWithParentId(id: string): void {
+  getSubResourceWithParentId(id: string): void {
+    this.store.dispatch(new DepositAction.GetAllSubResourceData(id));
   }
 }
diff --git a/src/app/material.module.ts b/src/app/material.module.ts
index a367f652b..6f7b5a533 100644
--- a/src/app/material.module.ts
+++ b/src/app/material.module.ts
@@ -12,7 +12,7 @@ import {
   MatSnackBarModule,
   MatTabsModule,
   MatToolbarModule,
-  MatTooltipModule,
+  MatTooltipModule, MatTreeModule,
 } from "@angular/material";
 
 const modules = [
@@ -29,6 +29,7 @@ const modules = [
   MatCheckboxModule,
   MatPaginatorModule,
   MatTooltipModule,
+  MatTreeModule,
 ];
 
 @NgModule({
diff --git a/src/app/organizational-unit/views/detail/detail.view.html b/src/app/organizational-unit/views/detail/detail.view.html
index f82503b7a..d540f5bc8 100644
--- a/src/app/organizational-unit/views/detail/detail.view.html
+++ b/src/app/organizational-unit/views/detail/detail.view.html
@@ -1,6 +1,6 @@
 <div class="button-toolbar">
   <button mat-flat-button color="primary" (click)="edit()">{{KEY_EDIT_BUTTON | translate}}</button>
-  <button mat-flat-button color="secondary" (click)="delete()">{{KEY_DELETE_BUTTON | translate}}</button>
+  <button mat-flat-button color="accent" (click)="delete()">{{KEY_DELETE_BUTTON | translate}}</button>
 </div>
 
 <div class="wrapper">
diff --git a/src/app/organizational-unit/views/edit/edit.view.ts b/src/app/organizational-unit/views/edit/edit.view.ts
index 3ada6b749..33864745e 100644
--- a/src/app/organizational-unit/views/edit/edit.view.ts
+++ b/src/app/organizational-unit/views/edit/edit.view.ts
@@ -28,7 +28,7 @@ export class EditView extends AbstractEditView<OrganizationalUnitsModel, Organiz
     super(store, route, StateEnum.organizationalUnit);
   }
 
-  getSubresourceWithParentId(id: string): void {
+  getSubResourceWithParentId(id: string): void {
     this.store.dispatch(new OrgUnitAction.GetAllSubResourceSubmissionPolicies(id));
   }
 
diff --git a/src/app/shared/crud.action.ts b/src/app/shared/crud.action.ts
index d73a256a5..218b337b3 100644
--- a/src/app/shared/crud.action.ts
+++ b/src/app/shared/crud.action.ts
@@ -3,7 +3,7 @@ import {QueryParametersModel} from "@app/shared/models/query-parameters.model";
 
 export namespace CrudAction {
   export class LoadResource {
-    protected static abstractType = "[{0}] Load resource";
+    protected static readonly abstractType = "[{0}] Load resource";
     public static readonly className = "LoadResource";
 
     constructor() {
@@ -11,7 +11,7 @@ export namespace CrudAction {
   }
 
   export class LoadResourceSuccess {
-    protected static abstractType = "[{0}] Load resource Success";
+    protected static readonly abstractType = "[{0}] Load resource Success";
     public static readonly className = "LoadResourceSuccess";
 
     constructor() {
@@ -19,7 +19,7 @@ export namespace CrudAction {
   }
 
   export class LoadResourceFail {
-    protected static abstractType = "[{0}] Load resource Fail";
+    protected static readonly abstractType = "[{0}] Load resource Fail";
     public static readonly className = "LoadResourceFail";
 
     constructor() {
@@ -27,7 +27,7 @@ export namespace CrudAction {
   }
 
   export class ChangeQueryParameters {
-    protected static abstractType = "[{0}] Change Query Parameters";
+    protected static readonly abstractType = "[{0}] Change Query Parameters";
     public static readonly className = "ChangeQueryParameters";
 
     constructor(public queryParameters: QueryParametersModel) {
@@ -35,15 +35,15 @@ export namespace CrudAction {
   }
 
   export class GetAll {
-    protected static abstractType = "[{0}] Get All";
-    public static readonly className = "GetAll";
+    protected static readonly abstractType = "[{0}] Get All";
+    public static readonly className = GetAll.name;
 
     constructor(public queryParameters?: QueryParametersModel) {
     }
   }
 
   export class GetAllSuccess<T> {
-    protected static abstractType = "[{0}] Get All Success";
+    protected static readonly abstractType = "[{0}] Get All Success";
     public static readonly className = "GetAllSuccess";
 
     constructor(public list: CollectionTypedModel<T>) {
@@ -51,7 +51,7 @@ export namespace CrudAction {
   }
 
   export class GetAllFail {
-    protected static abstractType = "[{0}] Get All Fail";
+    protected static readonly abstractType = "[{0}] Get All Fail";
     public static readonly className = "GetAllFail";
 
     constructor() {
@@ -59,7 +59,7 @@ export namespace CrudAction {
   }
 
   export class GetById {
-    protected static abstractType = "[{0}] Get By Id";
+    protected static readonly abstractType = "[{0}] Get By Id";
     public static readonly className = "GetById";
 
     constructor(public id: string) {
@@ -67,7 +67,7 @@ export namespace CrudAction {
   }
 
   export class GetByIdSuccess<T> {
-    protected static abstractType = "[{0}] Get By Id Success";
+    protected static readonly abstractType = "[{0}] Get By Id Success";
     public static readonly className = "GetByIdSuccess";
 
     constructor(public model: T) {
@@ -75,7 +75,7 @@ export namespace CrudAction {
   }
 
   export class GetByIdFail {
-    protected static abstractType = "[{0}] Get By Id Fail";
+    protected static readonly abstractType = "[{0}] Get By Id Fail";
     public static readonly className = "GetByIdFail";
 
     constructor() {
@@ -83,7 +83,7 @@ export namespace CrudAction {
   }
 
   export class Create<T> {
-    protected static abstractType = "[{0}] Create";
+    protected static readonly abstractType = "[{0}] Create";
     public static readonly className = "Create";
 
     constructor(public model: T) {
@@ -91,7 +91,7 @@ export namespace CrudAction {
   }
 
   export class CreateSuccess {
-    protected static abstractType = "[{0}] Create Success";
+    protected static readonly abstractType = "[{0}] Create Success";
     public static readonly className = "CreateSuccess";
 
     constructor() {
@@ -99,7 +99,7 @@ export namespace CrudAction {
   }
 
   export class CreateFail {
-    protected static abstractType = "[{0}] Create Fail";
+    protected static readonly abstractType = "[{0}] Create Fail";
     public static readonly className = "CreateFail";
 
     constructor() {
@@ -107,7 +107,7 @@ export namespace CrudAction {
   }
 
   export class Update<T> {
-    protected static abstractType = "[{0}] Update";
+    protected static readonly abstractType = "[{0}] Update";
     public static readonly className = "Update";
 
     constructor(public model: T) {
@@ -115,7 +115,7 @@ export namespace CrudAction {
   }
 
   export class UpdateSuccess {
-    protected static abstractType = "[{0}] Update Success";
+    protected static readonly abstractType = "[{0}] Update Success";
     public static readonly className = "UpdateSuccess";
 
     constructor() {
@@ -123,7 +123,7 @@ export namespace CrudAction {
   }
 
   export class UpdateFail {
-    protected static abstractType = "[{0}] Update Fail";
+    protected static readonly abstractType = "[{0}] Update Fail";
     public static readonly className = "UpdateFail";
 
     constructor() {
@@ -131,7 +131,7 @@ export namespace CrudAction {
   }
 
   export class Delete {
-    protected static abstractType = "[{0}] Delete";
+    protected static readonly abstractType = "[{0}] Delete";
     public static readonly className = "Delete";
 
     constructor(public resId: string) {
@@ -139,7 +139,7 @@ export namespace CrudAction {
   }
 
   export class DeleteSuccess {
-    protected static abstractType = "[{0}] Delete Success";
+    protected static readonly abstractType = "[{0}] Delete Success";
     public static readonly className = "DeleteSuccess";
 
     constructor() {
@@ -147,7 +147,7 @@ export namespace CrudAction {
   }
 
   export class DeleteFail {
-    protected static abstractType = "[{0}] Delete Fail";
+    protected static readonly abstractType = "[{0}] Delete Fail";
     public static readonly className = "DeleteFail";
 
     constructor() {
@@ -155,7 +155,7 @@ export namespace CrudAction {
   }
 
   export class GetAllSubResource {
-    protected static abstractType = "[{0}] Get All SubResource {1}";
+    protected static readonly abstractType = "[{0}] Get All SubResource {1}";
     public static readonly className = "GetAllSubResource{0}";
 
     constructor(public parentId: string, public queryParameters?: QueryParametersModel) {
@@ -163,7 +163,7 @@ export namespace CrudAction {
   }
 
   export class GetAllSubResourceSuccess<T> {
-    protected static abstractType = "[{0}] Get All SubResource {1} Success";
+    protected static readonly abstractType = "[{0}] Get All SubResource {1} Success";
     public static readonly className = "GetAllSubResource{0}Success";
 
     constructor(public list: CollectionTypedModel<T>) {
@@ -171,15 +171,23 @@ export namespace CrudAction {
   }
 
   export class GetAllSubResourceFail {
-    protected static abstractType = "[{0}] Get All SubResource {1} Fail";
+    protected static readonly abstractType = "[{0}] Get All SubResource {1} Fail";
     public static readonly className = "GetAllSubResource{0}Fail";
 
     constructor() {
     }
   }
 
+  export class CreateCompoSubResource<T> {
+    protected static readonly abstractType = "[{0}] Create Compo SubResource {1}";
+    public static readonly className = "CreateCompoSubResource{0}";
+
+    constructor(public parentId: string, public model: T) {
+    }
+  }
+
   export class CreateSubResource {
-    protected static abstractType = "[{0}] Create SubResource {1}";
+    protected static readonly abstractType = "[{0}] Create SubResource {1}";
     public static readonly className = "CreateSubResource{0}";
 
     constructor(public parentId: string, public listResId: string[]) {
@@ -187,7 +195,7 @@ export namespace CrudAction {
   }
 
   export class CreateSubResourceSuccess {
-    protected static abstractType = "[{0}] Create SubResource {1} Success";
+    protected static readonly abstractType = "[{0}] Create SubResource {1} Success";
     public static readonly className = "CreateSubResource{0}Success";
 
     constructor() {
@@ -195,7 +203,7 @@ export namespace CrudAction {
   }
 
   export class CreateSubResourceFail {
-    protected static abstractType = "[{0}] Create SubResource {1} Fail";
+    protected static readonly abstractType = "[{0}] Create SubResource {1} Fail";
     public static readonly className = "CreateSubResource{0}Fail";
 
     constructor() {
@@ -203,7 +211,7 @@ export namespace CrudAction {
   }
 
   export class DeleteSubResource {
-    protected static abstractType = "[{0}] Delete SubResource {1}";
+    protected static readonly abstractType = "[{0}] Delete SubResource {1}";
     public static readonly className = "DeleteSubResource{0}";
 
     constructor(public parentId: string, public listResId: string[]) {
@@ -211,7 +219,7 @@ export namespace CrudAction {
   }
 
   export class DeleteSubResourceSuccess {
-    protected static abstractType = "[{0}] Delete SubResource {1} Success";
+    protected static readonly abstractType = "[{0}] Delete SubResource {1} Success";
     public static readonly className = "DeleteSubResource{0}Success";
 
     constructor() {
@@ -219,7 +227,7 @@ export namespace CrudAction {
   }
 
   export class DeleteSubResourceFail {
-    protected static abstractType = "[{0}] Delete SubResource {1} Fail";
+    protected static readonly abstractType = "[{0}] Delete SubResource {1} Fail";
     public static readonly className = "DeleteSubResource{0}Fail";
 
     constructor() {
@@ -227,7 +235,7 @@ export namespace CrudAction {
   }
 
   export class UpdateSubResource {
-    protected static abstractType = "[{0}] Update SubResource {1}";
+    protected static readonly abstractType = "[{0}] Update SubResource {1}";
     public static readonly className = "UpdateSubResource{0}";
 
     constructor(public parentId: string, public oldResId: string[], public newResId: string[]) {
@@ -235,7 +243,7 @@ export namespace CrudAction {
   }
 
   export class UpdateSubResourceSuccess {
-    protected static abstractType = "[{0}] Update SubResource {1} Success";
+    protected static readonly abstractType = "[{0}] Update SubResource {1} Success";
     public static readonly className = "UpdateSubResource{0}Success";
 
     constructor(public parentId) {
@@ -243,7 +251,7 @@ export namespace CrudAction {
   }
 
   export class UpdateSubResourceFail {
-    protected static abstractType = "[{0}] Update SubResource {1} Fail";
+    protected static readonly abstractType = "[{0}] Update SubResource {1} Fail";
     public static readonly className = "UpdateSubResource{0}Fail";
 
     constructor(public parentId) {
diff --git a/src/app/shared/enums/api-action.enum.ts b/src/app/shared/enums/api-action.enum.ts
index 967c68053..7d61bbf91 100644
--- a/src/app/shared/enums/api-action.enum.ts
+++ b/src/app/shared/enums/api-action.enum.ts
@@ -18,4 +18,43 @@ export class ApiActionEnum {
   public static REINDEX = "reindex";
   public static CHECK_FIXITY = "check-fixity";
   public static SEARCH_DOI = "search-doi";
+
+  public static SELF = "self";
+  public static MODULE = "module";
+  public static PARENT = "parent";
+  public static STATUS = "status";
+  public static DASHBOARD = "dashboard";
+  public static RESOURCES = "resources";
+  public static LIST = "list";
+  public static FILE = "file";
+  public static COUNT = "count";
+  public static PREVIOUS = "previous";
+  public static NEXT = "next";
+  public static CREATE = "new";
+  public static READ = "get";
+  public static UPDATE = "edit";
+  public static DELETE = "remove";
+  public static VALUES = "values";
+  public static SEARCH = "search";
+  public static LASTCREATED = "lastCreated";
+  public static LASTUPDATE = "lastUpdated";
+  public static RANGE = "searchRange";
+  public static SIZE = "size";
+  public static PAGE = "page";
+  public static SORT = "sort";
+  public static CREATION = "creation.when";
+  public static UPDATED = "lastUpdate.when";
+  public static ERROR = "error";
+  public static UL = "upload";
+  public static UL_ARCHIVE = "upload-archive";
+  public static DL = "download";
+  public static HISTORY = "history";
+  public static RESUME = "resume";
+  public static START = "start";
+  public static ALL = "All";
+  public static VIEW = "view";
+  public static SAVE = "save";
+  public static IMPORT = "import";
+  public static IMPORTED = "imported";
+  public static CLOSE = "close";
 }
diff --git a/src/app/shared/organizational-unit.action.ts b/src/app/shared/organizational-unit.action.ts
index dc5d5acf9..4823f13f6 100644
--- a/src/app/shared/organizational-unit.action.ts
+++ b/src/app/shared/organizational-unit.action.ts
@@ -1,3 +1,4 @@
+import {Type} from "@angular/core";
 import {OrganizationalUnitsModel, SubmissionPoliciesModel} from "@app/generated-api";
 import {CrudAction} from "@app/shared/crud.action";
 import {ApiResourceNameEnum} from "@app/shared/enums/api-resource-name.enum";
diff --git a/src/app/shared/services/api.service.ts b/src/app/shared/services/api.service.ts
index 933958c06..86433ac99 100644
--- a/src/app/shared/services/api.service.ts
+++ b/src/app/shared/services/api.service.ts
@@ -186,6 +186,18 @@ export class ApiService {
     );
   }
 
+  public upload(path: string, data) {
+    let headers = this.defaultHeaders;
+    headers = headers.set("Content-Type", "multipart/form-data; boundary=HereGoes");
+    // headers = headers.set("Accept", "application/json");
+
+    return this.httpClient.post<any>(`${this.configuration.basePath}/${path}`, data, {
+      reportProgress: true,
+      observe: "events",
+      headers,
+    });
+  }
+
   private getQueryParameters(queryParameters: QueryParametersModel, customParameters: { [p: string]: string }) {
     let queryParametersHttp = new HttpParams({encoder: new CustomHttpUrlEncodingCodec()});
     if (queryParameters == null) {
diff --git a/src/app/shared/utils/crud-action.util.ts b/src/app/shared/utils/crud-action.util.ts
index cdae761f2..f49ba5679 100644
--- a/src/app/shared/utils/crud-action.util.ts
+++ b/src/app/shared/utils/crud-action.util.ts
@@ -70,7 +70,7 @@ export class CrudActionUtil {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.GetAll.className, queryParameters);
   }
 
-  static getAllSuccess<T>(state: StateEnum, list: CollectionTypedModel<T>): CrudAction.GetAllSuccess<any> {
+  static getAllSuccess<T>(state: StateEnum, list: CollectionTypedModel<T>): CrudAction.GetAllSuccess<T> {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.GetAllSuccess.className, list);
   }
 
@@ -82,7 +82,7 @@ export class CrudActionUtil {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.GetById.className, id);
   }
 
-  static getByIdSuccess<T>(state: StateEnum, model: T): CrudAction.GetByIdSuccess<any> {
+  static getByIdSuccess<T>(state: StateEnum, model: T): CrudAction.GetByIdSuccess<T> {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.GetByIdSuccess.className, model);
   }
 
@@ -90,7 +90,7 @@ export class CrudActionUtil {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.GetByIdFail.className);
   }
 
-  static create<T>(state: StateEnum, model: T): CrudAction.Create<any> {
+  static create<T>(state: StateEnum, model: T): CrudAction.Create<T> {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.Create.className, model);
   }
 
@@ -102,7 +102,7 @@ export class CrudActionUtil {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.CreateFail.className);
   }
 
-  static update<T>(state: StateEnum, model: T): CrudAction.Update<any> {
+  static update<T>(state: StateEnum, model: T): CrudAction.Update<T> {
     return CrudActionUtil.getInstanceInternal(state, CrudAction.Update.className, model);
   }
 
@@ -123,7 +123,7 @@ export class CrudActionUtil {
   }
 
   static deleteFail(state: StateEnum): CrudAction.DeleteFail {
-    return CrudActionUtil.getInstanceInternal(state, CrudAction.DeleteFail.className);
+    return CrudActionUtil.getInstanceInternal(state, CrudAction.DeleteFail.name);
   }
 
   static getAllSubResource(state: StateEnum, subResource: ApiResourceNameEnum, parentId: string, queryParameters?: QueryParametersModel): CrudAction.GetAllSubResource {
diff --git a/src/app/shared/views/abstract-create/abstract-create.view.ts b/src/app/shared/views/abstract-create/abstract-create.view.ts
index e7835666c..8c5b96a54 100644
--- a/src/app/shared/views/abstract-create/abstract-create.view.ts
+++ b/src/app/shared/views/abstract-create/abstract-create.view.ts
@@ -1,6 +1,4 @@
 import {OnInit} from "@angular/core";
-import {DepositAction} from "@app/deposit/deposit.action";
-import {CrudAction} from "@app/shared/crud.action";
 import {CrudStateModel} from "@app/shared/crud.state";
 import {BaseDirective} from "@app/shared/directives/base.directive";
 import {StateEnum} from "@app/shared/enums/state.enum";
diff --git a/src/app/shared/views/abstract-detail/abstract-detail.view.ts b/src/app/shared/views/abstract-detail/abstract-detail.view.ts
index cdd72fc30..87f4195ec 100644
--- a/src/app/shared/views/abstract-detail/abstract-detail.view.ts
+++ b/src/app/shared/views/abstract-detail/abstract-detail.view.ts
@@ -54,6 +54,10 @@ export abstract class AbstractDetailView<T, U extends CrudStateModel<T>> extends
 
   abstract getSubResourceWithParentId(id: string): void;
 
+  protected getResId(): string {
+    return this.resId;
+  }
+
   edit() {
     this.store.dispatch(new Navigate([CrudRouteUtil.getEditRoute(this.state), this.resId]));
   }
diff --git a/src/app/shared/views/abstract-edit/abstract-edit.view.ts b/src/app/shared/views/abstract-edit/abstract-edit.view.ts
index 12470c3ac..62a2bc714 100644
--- a/src/app/shared/views/abstract-edit/abstract-edit.view.ts
+++ b/src/app/shared/views/abstract-edit/abstract-edit.view.ts
@@ -34,10 +34,14 @@ export abstract class AbstractEditView<T, U extends CrudStateModel<T>> extends B
 
   private getById(id: string) {
     this.store.dispatch(CrudActionUtil.getById(this.state, id));
-    this.getSubresourceWithParentId(id);
+    this.getSubResourceWithParentId(id);
   }
 
-  abstract getSubresourceWithParentId(id: string): void;
+  abstract getSubResourceWithParentId(id: string): void;
+
+  protected getResId(): string {
+    return this.resId;
+  }
 
   update(model: T) {
     this.store.dispatch(CrudActionUtil.update<T>(this.state, model));
diff --git a/src/app/shared/views/abstract-list/abstract-list.view.html b/src/app/shared/views/abstract-list/abstract-list.view.html
index 562864ad2..1138397a8 100644
--- a/src/app/shared/views/abstract-list/abstract-list.view.html
+++ b/src/app/shared/views/abstract-list/abstract-list.view.html
@@ -1,6 +1,6 @@
 <div class="button-toolbar">
   <button mat-flat-button color="primary" (click)="create()">{{KEY_CREATE_BUTTON | translate}}</button>
-  <button mat-flat-button color="secondary" (click)="getAll()">{{KEY_REFRESH_BUTTON | translate}}</button>
+  <button mat-flat-button color="accent" (click)="getAll()">{{KEY_REFRESH_BUTTON | translate}}</button>
 </div>
 
 <div class="wrapper">
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 5bbda5ba1..411371bd8 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -60,6 +60,10 @@
       }
     },
     "edit": "Edit",
+    "file": {
+      "search": "Choose file",
+      "uploadButton": "Upload"
+    },
     "hasEmbargo": "The deposit has an embargo",
     "hint": {
       "author": "Author"
@@ -73,6 +77,10 @@
     "refresh": "Refresh",
     "submissionPolicy": "Submission Policy",
     "submit": "Submit",
+    "tab": {
+      "datafiles": "Data Files",
+      "details": "Details"
+    },
     "table": {
       "header": {
         "creation": {
@@ -136,12 +144,8 @@
   "required": "This field is required",
   "search": "Search",
   "table": {
-    "nodata": "No data has been found"
-  },
-  "tooltip": {
-    "other": "other",
-    "others": "others",
-  "paginator": {
+    "nodata": "No data has been found",
+    "paginator": {
       "firstPage": "First page",
       "itemPerPage": "Items per page",
       "lastPage": "Last page",
@@ -149,5 +153,9 @@
       "ofLabel": "of",
       "previousPage": "Previous page"
     }
+  },
+  "tooltip": {
+    "other": "other",
+    "others": "others"
   }
 }
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 1675c32a4..0f735f92a 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -60,6 +60,10 @@
 			}
 		},
 		"edit": "Edit",
+		"file": {
+			"search": "Choose file",
+			"uploadButton": "Upload"
+		},
 		"hasEmbargo": "The deposit has an embargo",
 		"hint": {
 			"author": "Author"
@@ -73,6 +77,10 @@
 		"refresh": "Refresh",
 		"submissionPolicy": "Submission Policy",
 		"submit": "Submit",
+		"tab": {
+			"datafiles": "Data Files",
+			"details": "Details"
+		},
 		"table": {
 			"header": {
 				"creation": {
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index 3bd9a86b1..1d5af9b9e 100644
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -1,94 +1,102 @@
 {
-  "access": {
-    "organizationalUnitNotFound": "Impossible d'obtenir les données",
-    "table": {
-      "header": {
-        "accessLevel": "Niveau d'Accès",
-        "organizationalUnit": "Unité Organisationnelle",
-        "title": "Titre",
-        "yearPublicationDate": "Année de Publication"
-      }
-    }
-  },
-  "app": {
-    "login": {
-      "loginButton": "Se connecter",
-      "loginMessage": "Vous n'êtes actuellement pas connecté, veuillez vous connecter afin de continuer."
-    },
-    "toolbar": {
-      "deposit": "Dépôts",
-      "home": "Accueil",
-      "login": "Connexion",
-      "logout": "Déconnexion",
-      "organizationalUnit": "Unités Organisationnelles"
-    }
-  },
-  "breadcrumb": {
-    "access": {
-      "root": "Accès",
-      "search": "Recherche"
-    },
-    "deposit": {
-      "create": "Création",
-      "detail": "Détail",
-      "edit": "Modifier",
-      "list": "Liste",
-      "root": "Dépôts"
-    },
-    "home": "Accueil",
-    "organizationalUnit": {
-      "create": "Création",
-      "detail": "Détail",
-      "edit": "Modifier",
-      "list": "Liste",
-      "root": "Unité organisationnelle"
-    }
-  },
-  "deposit": {
-    "accessLevel": "Niveau d'accès",
-    "author": "Auteur",
-    "collectionBegin": "Début de la collecte des données",
-    "collectionEnd": "Fin de la collecte des données",
-    "delete": "Supprimer",
-    "description": "Description",
-    "dialog": {
-      "delete": {
-        "cancel": "Annuler",
-        "confirm": "Oui",
-        "message": "Êtes-vous sûr de vouloir supprimer le dépôt '{{name}}'?",
-        "title": "Confirmer suppresion"
-      }
-    },
-    "edit": "Modifier",
-    "hasEmbargo": "Le dépôt a un embargo",
-    "hint": {
-      "author": "Auteur"
-    },
-    "language": "Langue",
-    "license": "Licence",
-    "new": "Créer dépôt",
-    "organizationUnit": "Unité organisationnelle",
-    "preservationPolicy": "Politique de préservation",
-    "publicationDate": "Date de publication",
-    "refresh": "Rafraichir",
-    "submissionPolicy": "Politique de soumission",
-    "submit": "Envoyer",
-    "table": {
-      "header": {
-        "creation": {
-          "when": "Date de création"
-        },
-        "lastUpdate": {
-          "when": "Dernière mise à jour"
-        },
-        "publicationDate": "Date de publication",
-        "status": "Statut",
-        "title": "Titre"
-      }
-    },
-    "title": "Titre"
-  },
-  "error": {
+	"access": {
+		"organizationalUnitNotFound": "Impossible d'obtenir les données",
+		"table": {
+			"header": {
+				"accessLevel": "Niveau d'Accès",
+				"organizationalUnit": "Unité Organisationnelle",
+				"title": "Titre",
+				"yearPublicationDate": "Année de Publication"
+			}
+		}
+	},
+	"app": {
+		"login": {
+			"loginButton": "Se connecter",
+			"loginMessage": "Vous n'êtes actuellement pas connecté, veuillez vous connecter afin de continuer."
+		},
+		"toolbar": {
+			"deposit": "Dépôts",
+			"home": "Accueil",
+			"login": "Connexion",
+			"logout": "Déconnexion",
+			"organizationalUnit": "Unités Organisationnelles"
+		}
+	},
+	"breadcrumb": {
+		"access": {
+			"root": "Accès",
+			"search": "Recherche"
+		},
+		"deposit": {
+			"create": "Création",
+			"detail": "Détail",
+			"edit": "Modifier",
+			"list": "Liste",
+			"root": "Dépôts"
+		},
+		"home": "Accueil",
+		"organizationalUnit": {
+			"create": "Création",
+			"detail": "Détail",
+			"edit": "Modifier",
+			"list": "Liste",
+			"root": "Unité organisationnelle"
+		}
+	},
+	"deposit": {
+		"accessLevel": "Niveau d'accès",
+		"author": "Auteur",
+		"collectionBegin": "Début de la collecte des données",
+		"collectionEnd": "Fin de la collecte des données",
+		"delete": "Supprimer",
+		"description": "Description",
+		"dialog": {
+			"delete": {
+				"cancel": "Annuler",
+				"confirm": "Oui",
+				"message": "Êtes-vous sûr de vouloir supprimer le dépôt '{{name}}'?",
+				"title": "Confirmer suppresion"
+			}
+		},
+		"edit": "Modifier",
+		"file": {
+			"search": "Choisir fichier",
+			"uploadButton": "Téléverser"
+		},
+		"hasEmbargo": "Le dépôt a un embargo",
+		"hint": {
+			"author": "Auteur"
+		},
+		"language": "Langue",
+		"license": "Licence",
+		"new": "Créer dépôt",
+		"organizationUnit": "Unité organisationnelle",
+		"preservationPolicy": "Politique de préservation",
+		"publicationDate": "Date de publication",
+		"refresh": "Rafraichir",
+		"submissionPolicy": "Politique de soumission",
+		"submit": "Envoyer",
+		"tab": {
+			"datafiles": "Fichiers",
+			"details": "Détails"
+		},
+		"table": {
+			"header": {
+				"creation": {
+					"when": "Date de création"
+				},
+				"lastUpdate": {
+					"when": "Dernière mise à jour"
+				},
+				"publicationDate": "Date de publication",
+				"status": "Statut",
+				"title": "Titre"
+			}
+		},
+		"title": "Titre"
+	},
+	"error": {
     "http": {
       "forbidden": "Vous ne pouvez pas faire cette action",
       "notFound": "La ressource demandée n'existe pas",
@@ -103,51 +111,51 @@
     }
   },
   "organizationalUnit": {
-    "closingDate": "Date de fermeture",
-    "delete": "Supprimer",
-    "description": "Description",
-    "dialog": {
-      "delete": {
-        "cancel": "Annuler",
-        "confirm": "Oui",
-        "message": "Êtes-vous sûr de vouloir supprimer l'unité organisationnelle '{{name}}'?",
-        "title": "Confirmer suppression"
-      }
-    },
-    "edit": "Modifier",
-    "isEmpty": "Est vide",
-    "name": "Nom",
-    "new": "Créer nouvelle unité organisationnelle",
-    "open": "Ouvert",
-    "openingDate": "Date d'ouverture",
-    "refresh": "Rafraichir",
-    "submissionPolicy": "Politique de soumission",
-    "submit": "Envoyer",
-    "table": {
-      "header": {
-        "creation": {
-          "when": "Date de création"
-        },
-        "description": "Description",
-        "name": "Nom"
-      }
-    }
-  },
-  "required": "Ce champ est requis",
-  "search": "Recherche",
-  "table": {
-    "nodata": "Aucune donnée n'a été trouvé",
-    "paginator": {
-      "firstPage": "Première page",
-      "itemPerPage": "Eléments par page",
-      "lastPage": "Dernière page",
-      "nextPage": "Page suivante",
-      "ofLabel": "sur",
-      "previousPage": "Page précédente"
-    }
-  },
-  "tooltip": {
-    "other": "autre",
-    "others": "autres"
-  }
+		"closingDate": "Date de fermeture",
+		"delete": "Supprimer",
+		"description": "Description",
+		"dialog": {
+			"delete": {
+				"cancel": "Annuler",
+				"confirm": "Oui",
+				"message": "Êtes-vous sûr de vouloir supprimer l'unité organisationnelle '{{name}}'?",
+				"title": "Confirmer suppression"
+			}
+		},
+		"edit": "Modifier",
+		"isEmpty": "Est vide",
+		"name": "Nom",
+		"new": "Créer nouvelle unité organisationnelle",
+		"open": "Ouvert",
+		"openingDate": "Date d'ouverture",
+		"refresh": "Rafraichir",
+		"submissionPolicy": "Politique de soumission",
+		"submit": "Envoyer",
+		"table": {
+			"header": {
+				"creation": {
+					"when": "Date de création"
+				},
+				"description": "Description",
+				"name": "Nom"
+			}
+		}
+	},
+	"required": "Ce champ est requis",
+	"search": "Recherche",
+	"table": {
+		"nodata": "Aucune donnée n'a été trouvé",
+		"paginator": {
+			"firstPage": "Première page",
+			"itemPerPage": "Eléments par page",
+			"lastPage": "Dernière page",
+			"nextPage": "Page suivante",
+			"ofLabel": "sur",
+			"previousPage": "Page précédente"
+		}
+	},
+	"tooltip": {
+		"other": "autre",
+		"others": "autres"
+	}
 }
diff --git a/src/environments/environment.dlcmtest.ts b/src/environments/environment.dlcmtest.ts
index baad9d6b8..71a4c2895 100644
--- a/src/environments/environment.dlcmtest.ts
+++ b/src/environments/environment.dlcmtest.ts
@@ -4,10 +4,10 @@ import {defaultEnvironment} from "./environment.defaults";
 export const environment = {
   ...defaultEnvironment,
   theme: ThemeEnum.yareta,
-  oauthTokenEndpoint: "https://dlcmtest.unige.ch/administration/oauth/token",
+  oauthTokenEndpoint: "https://dlcmtest.unige.ch/authorization/oauth/token",
   oauthDummyClientSecret: "12345",
-  oauthClientId: "myClientId",
+  oauthClientId: "nightly-dlcm-ui-client",
   oauthScope: "READ",
   oauthRequireHttps: true,
-  oauthLoginUrl: "https://dlcmtest.unige.ch/administration/oauth/authorize",
+  oauthLoginUrl: "https://dlcmtest.unige.ch/authorization/oauth/authorize",
 };
diff --git a/src/sass/abstracts/_variables.scss b/src/sass/abstracts/_variables.scss
index af1e715e1..937b9dbc2 100644
--- a/src/sass/abstracts/_variables.scss
+++ b/src/sass/abstracts/_variables.scss
@@ -12,3 +12,6 @@ $success: #6acc29;
 $error: #ff0000;
 $warning: #ffa500;
 $info: #15a7ff;
+
+$index-footer: 1;
+$index-modal: 2;
diff --git a/src/sass/main.scss b/src/sass/main.scss
index 668d11f76..cfd2bb2b5 100644
--- a/src/sass/main.scss
+++ b/src/sass/main.scss
@@ -57,3 +57,9 @@ html * {
     border-color: $info;
   }
 }
+
+ul, li {
+  margin-top: 0;
+  margin-bottom: 0;
+  list-style-type: none
+}
-- 
GitLab