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