diff --git a/src/app/features/admin/funding-agencies/components/routables/admin-funding-agencies-list/admin-funding-agencies-list.routable.ts b/src/app/features/admin/funding-agencies/components/routables/admin-funding-agencies-list/admin-funding-agencies-list.routable.ts index f47f82be5d888ae4c189a9e4077e1440d26fee5c..cb5979a7d9028e8943c4291ddc1a61cbffb0ad08 100644 --- a/src/app/features/admin/funding-agencies/components/routables/admin-funding-agencies-list/admin-funding-agencies-list.routable.ts +++ b/src/app/features/admin/funding-agencies/components/routables/admin-funding-agencies-list/admin-funding-agencies-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-submission-policy-list-routable", @@ -34,7 +37,7 @@ export class AdminFundingAgenciesListRoutable extends SharedAbstractListRoutable field: "acronym", header: TRANSLATE("admin.funding-agencies.table.header.acronym"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminFundingAgenciesListRoutable extends SharedAbstractListRoutable field: "name", header: TRANSLATE("admin.funding-agencies.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/institution/components/routables/admin-institution-list/admin-institution-list.routable.ts b/src/app/features/admin/institution/components/routables/admin-institution-list/admin-institution-list.routable.ts index 518380ade731446069f40d4309e94ec5bb5eaf59..eaf9f6efb0231147f9198226125a74da5e69be35 100644 --- a/src/app/features/admin/institution/components/routables/admin-institution-list/admin-institution-list.routable.ts +++ b/src/app/features/admin/institution/components/routables/admin-institution-list/admin-institution-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-institution-list-routable", @@ -34,7 +37,7 @@ export class AdminInstitutionListRoutable extends SharedAbstractListRoutable<Ins field: "name", header: TRANSLATE("admin.institution.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminInstitutionListRoutable extends SharedAbstractListRoutable<Ins field: "creation.when" as any, header: TRANSLATE("admin.institution.table.header.creation.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminInstitutionListRoutable extends SharedAbstractListRoutable<Ins field: "lastUpdate.when" as any, header: TRANSLATE("admin.institution.table.header.lastUpdate.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/license/components/routables/admin-license-list/admin-license-list.routable.ts b/src/app/features/admin/license/components/routables/admin-license-list/admin-license-list.routable.ts index 590a1c739bc2a62d586063fe148bbf0b4520e132..c0506f3ac4ab24ca1e642036b018bb221c3d18af 100644 --- a/src/app/features/admin/license/components/routables/admin-license-list/admin-license-list.routable.ts +++ b/src/app/features/admin/license/components/routables/admin-license-list/admin-license-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-license-list-routable", @@ -34,7 +37,7 @@ export class AdminLicenseListRoutable extends SharedAbstractListRoutable<License field: "title", header: TRANSLATE("admin.license.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/oauth2-client/components/routables/admin-oauth2-client-list/admin-oauth2-client-list.routable.ts b/src/app/features/admin/oauth2-client/components/routables/admin-oauth2-client-list/admin-oauth2-client-list.routable.ts index 41fb267d013d8466d2f3d591e5c6fb2c0d01cd3f..d7539de07dcaa87dff561573db7915634539069d 100644 --- a/src/app/features/admin/oauth2-client/components/routables/admin-oauth2-client-list/admin-oauth2-client-list.routable.ts +++ b/src/app/features/admin/oauth2-client/components/routables/admin-oauth2-client-list/admin-oauth2-client-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-submission-policy-list-routable", @@ -34,7 +37,7 @@ export class AdminOAuth2ClientListRoutable extends SharedAbstractListRoutable<Oa field: "name", header: TRANSLATE("admin.oauth2.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminOAuth2ClientListRoutable extends SharedAbstractListRoutable<Oa field: "clientId", header: TRANSLATE("admin.oauth2.table.header.clientId"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminOAuth2ClientListRoutable extends SharedAbstractListRoutable<Oa field: "redirectUri", header: TRANSLATE("admin.oauth2.table.header.redirectUri"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/orgunit/components/routables/admin-orgunit-list/admin-orgunit-list.routable.ts b/src/app/features/admin/orgunit/components/routables/admin-orgunit-list/admin-orgunit-list.routable.ts index fb87013fdce68e070e7b31d707dcbefb38e266ac..b8cee1870723f41ea5c87ecce06574d07f4e9f2a 100644 --- a/src/app/features/admin/orgunit/components/routables/admin-orgunit-list/admin-orgunit-list.routable.ts +++ b/src/app/features/admin/orgunit/components/routables/admin-orgunit-list/admin-orgunit-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-orgunit-list-routable", @@ -34,7 +37,7 @@ export class AdminOrgunitListRoutable extends SharedAbstractListRoutable<Organiz field: "name", header: TRANSLATE("admin.organizationalUnit.table.header.name"), type: FieldTypeEnum.string, - order: -1, + order: OrderEnum.descending, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminOrgunitListRoutable extends SharedAbstractListRoutable<Organiz field: "description", header: TRANSLATE("admin.organizationalUnit.table.header.description"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminOrgunitListRoutable extends SharedAbstractListRoutable<Organiz field: "creation.when" as any, header: TRANSLATE("admin.organizationalUnit.table.header.creation.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/person/components/routables/admin-person-list/admin-person-list.routable.ts b/src/app/features/admin/person/components/routables/admin-person-list/admin-person-list.routable.ts index 63420cdeb745f2ce97a5b21acac6e99f1b5dc360..ef7ae198ed9690696bae719c2a39891b962e3a65 100644 --- a/src/app/features/admin/person/components/routables/admin-person-list/admin-person-list.routable.ts +++ b/src/app/features/admin/person/components/routables/admin-person-list/admin-person-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-person-list-routable", @@ -34,7 +37,7 @@ export class AdminPersonListRoutable extends SharedAbstractListRoutable<PersonEx field: "lastName", header: TRANSLATE("admin.person.table.header.lastName"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminPersonListRoutable extends SharedAbstractListRoutable<PersonEx field: "firstName", header: TRANSLATE("admin.person.table.header.firstName"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminPersonListRoutable extends SharedAbstractListRoutable<PersonEx field: "orcid", header: TRANSLATE("admin.person.table.header.orcid"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/preservation-policy/components/routables/admin-preservation-policy-list/admin-preservation-policy-list.routable.ts b/src/app/features/admin/preservation-policy/components/routables/admin-preservation-policy-list/admin-preservation-policy-list.routable.ts index 52498b7fe20e0af997af02a24dc2f4a2e8b0e650..dc21cc7d40e67efae879a834fc709acdc7195c0c 100644 --- a/src/app/features/admin/preservation-policy/components/routables/admin-preservation-policy-list/admin-preservation-policy-list.routable.ts +++ b/src/app/features/admin/preservation-policy/components/routables/admin-preservation-policy-list/admin-preservation-policy-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-preservation-list-routable", @@ -34,7 +37,7 @@ export class AdminPreservationPolicyListRoutable extends SharedAbstractListRouta field: "name", header: TRANSLATE("admin.preservationPolicy.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminPreservationPolicyListRoutable extends SharedAbstractListRouta field: "dispositionApproval", header: TRANSLATE("admin.preservationPolicy.table.header.dispositionApproval"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminPreservationPolicyListRoutable extends SharedAbstractListRouta field: "creation.when" as any, header: TRANSLATE("admin.preservationPolicy.table.header.creation.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -58,7 +61,7 @@ export class AdminPreservationPolicyListRoutable extends SharedAbstractListRouta field: "lastUpdate.when" as any, header: TRANSLATE("admin.preservationPolicy.table.header.lastUpdate.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -66,7 +69,7 @@ export class AdminPreservationPolicyListRoutable extends SharedAbstractListRouta field: "retention", header: TRANSLATE("admin.preservationPolicy.table.header.retention"), type: FieldTypeEnum.number, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: false, }, diff --git a/src/app/features/admin/role/components/routables/admin-role-list/admin-role-list.routable.ts b/src/app/features/admin/role/components/routables/admin-role-list/admin-role-list.routable.ts index 12360c54ba48c1e1b0c07387917f127441371782..a9cbdd16445dff86547e903cbd04e7763eb6f441 100644 --- a/src/app/features/admin/role/components/routables/admin-role-list/admin-role-list.routable.ts +++ b/src/app/features/admin/role/components/routables/admin-role-list/admin-role-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-submission-policy-list-routable", @@ -34,7 +37,7 @@ export class AdminRoleListRoutable extends SharedAbstractListRoutable<Role, Admi field: "name", header: TRANSLATE("admin.roles.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/submission-policy/components/routables/admin-submission-policy-list/admin-submission-policy-list.routable.ts b/src/app/features/admin/submission-policy/components/routables/admin-submission-policy-list/admin-submission-policy-list.routable.ts index 3bfdc9aac77542d9d158635de917ffdb7c1efdd8..4547708d2310dff09538ea3386069ae477dee4a8 100644 --- a/src/app/features/admin/submission-policy/components/routables/admin-submission-policy-list/admin-submission-policy-list.routable.ts +++ b/src/app/features/admin/submission-policy/components/routables/admin-submission-policy-list/admin-submission-policy-list.routable.ts @@ -10,7 +10,10 @@ import {Store} from "@ngxs/store"; import {SharedAbstractListRoutable} from "@shared/components/routables/shared-abstract-list/shared-abstract-list.routable"; import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-submission-policy-list-routable", @@ -34,7 +37,7 @@ export class AdminSubmissionPolicyListRoutable extends SharedAbstractListRoutabl field: "name", header: TRANSLATE("admin.submissionPolicy.table.header.name"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminSubmissionPolicyListRoutable extends SharedAbstractListRoutabl field: "submissionApproval", header: TRANSLATE("admin.submissionPolicy.table.header.submissionApproval"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminSubmissionPolicyListRoutable extends SharedAbstractListRoutabl field: "creation.when" as any, header: TRANSLATE("admin.submissionPolicy.table.header.creation.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -58,7 +61,7 @@ export class AdminSubmissionPolicyListRoutable extends SharedAbstractListRoutabl field: "lastUpdate.when" as any, header: TRANSLATE("admin.submissionPolicy.table.header.lastUpdate.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -66,7 +69,7 @@ export class AdminSubmissionPolicyListRoutable extends SharedAbstractListRoutabl field: "timeToKeep", header: TRANSLATE("admin.submissionPolicy.table.header.timeToKeep"), type: FieldTypeEnum.number, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts b/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts index 678de02c0ff3e0da0c3c4d196c1a420562db45f4..8681446dac678ab1a6107f6ae2bbc827e302076a 100644 --- a/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts +++ b/src/app/features/admin/user/components/routables/admin-user-list/admin-user-list.routable.ts @@ -10,7 +10,10 @@ import {SharedAbstractListRoutable} from "@shared/components/routables/shared-ab import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; import {UserExtended} from "@shared/models/business/user-extended.model"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-admin-user-list-routable", @@ -34,7 +37,7 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend field: "externalUid", header: TRANSLATE("admin.user.table.header.externalUid"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend field: "targetedUid", header: TRANSLATE("admin.user.table.header.targetedUid"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend field: "firstName", header: TRANSLATE("admin.user.table.header.firstName"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -58,7 +61,7 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend field: "lastName", header: TRANSLATE("admin.user.table.header.lastName"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -66,7 +69,7 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend field: "homeOrganization", header: TRANSLATE("admin.user.table.header.homeOrganization"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -74,7 +77,7 @@ export class AdminUserListRoutable extends SharedAbstractListRoutable<UserExtend field: "email", header: TRANSLATE("admin.user.table.header.email"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.html b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.html index bfff181e6da7e063e8721b42e4f5cffac55d2540..70aba14f72819c7dba073e30f7977643139d9fe5 100644 --- a/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.html +++ b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.html @@ -1,30 +1,85 @@ -<div class="form"> - <div *ngIf="!readonly" - class="file-upload" - > - <h1>{{'deposit.file.title.inProgressUpload' | translate}}</h1> - <dlcm-deposit-files-upload-in-progress *ngIf="readonly === false" - [listFilesUploading]="uploadStatusObs | async" - (cancelChange)="cancel($event)" - (retryChange)="retry($event)" - (uploadChange)="upload($event)" - (uploadArchiveChange)="uploadArchive($event)" - ></dlcm-deposit-files-upload-in-progress> +<div *ngIf="!readonly" + class="file-upload" +> + <div class="upload-button-wrapper"> + <button mat-button + color="accent" + type="button" + (click)="openModalUpload()" + > + {{'deposit.file.button.upload' | translate}} + </button> + + <button mat-button + color="accent" + type="button" + (click)="openModalUploadArchive()" + > + {{'deposit.file.button.uploadArchive' | translate}} + </button> </div> + <dlcm-deposit-files-upload-in-progress *ngIf="readonly === false" + [listFilesUploading]="uploadStatusObs | async" + (cancelChange)="cancel($event)" + (retryChange)="retry($event)" + (uploadChange)="upload($event)" + (uploadArchiveChange)="uploadArchive($event)" + ></dlcm-deposit-files-upload-in-progress> +</div> + +<div class="file-uploaded"> <h1>{{'deposit.file.title.filesOfDeposit' | translate}}</h1> - <dlcm-deposit-file-tree *ngIf="(listDataFileObs | async) | isNonEmptyArray; else noFile" - [listDepositDataFile]="listDataFileObs | async" - [isLoading]="isLoadingDataFileObs | async" - [readonly]="readonly" - (resumeChange)="resume($event)" - (deleteChange)="delete($event)" - (downloadChange)="download($event)" - (refreshChange)="refresh()" - (showHistoryChange)="showHistory($event)" - [isLoadingHistory]="isLoadingHistoryObs | async" - ></dlcm-deposit-file-tree> - <ng-template #noFile> - <label>{{'deposit.file.tree.noFile' | translate}}</label> - </ng-template> + <!-- <dlcm-deposit-file-tree *ngIf="(listDataFileObs | async) | isNonEmptyArray; else noFile"--> + <!-- [listDepositDataFile]="listDataFileObs | async"--> + <!-- [listFolders]="listFoldersObs | async"--> + <!-- [isLoading]="isLoadingDataFileObs | async"--> + <!-- [readonly]="readonly"--> + <!-- (openNode)="openNode($event)"--> + <!-- (resumeChange)="resume($event)"--> + <!-- (deleteChange)="delete($event)"--> + <!-- (downloadChange)="download($event)"--> + <!-- (refreshChange)="refresh()"--> + <!-- (showHistoryChange)="showHistory($event)"--> + <!-- [isLoadingHistory]="isLoadingHistoryObs | async"--> + <!-- ></dlcm-deposit-file-tree>--> + + <div class="selector-file-view-mode"> + <a *ngIf="currentFileViewMode === fileViewModeEnum.FolderView" + (click)="selectFolder(undefined)" + >{{'deposit.file.button.flatView' | translate}}</a> + <a *ngIf="currentFileViewMode === fileViewModeEnum.FlatView" + (click)="selectFolder('/')" + >{{'deposit.file.button.showTree' | translate}}</a> + </div> + + <div class="file-viewer"> + <div class="tree-folder" + *ngIf="currentFileViewMode === fileViewModeEnum.FolderView && (listFoldersObs | async) | isNonEmptyArray" + > + <dlcm-deposit-folder-tree + [listFolders]="listFoldersObs | async" + [isLoading]="isLoadingDataFileObs | async" + [currentFolder]="getCurrentFolder(queryParametersObs | async)" + (selectChange)="selectFolder($event)" + ></dlcm-deposit-folder-tree> + </div> + + <div class="wrapper" + [dlcmSpinner]="isLoadingDataFileObs | async" + > + <dlcm-shared-data-table [columns]="columns" + [isLoading]="isLoadingDataFileObs | async" + [datas]="listDataFileObs | async" + [queryParameters]="queryParametersObs | async" + (queryParametersChange)="onQueryParametersEvent($event)" + (selectChange)="showDetail($event)" + > + </dlcm-shared-data-table> + </div> + + <ng-template #noFile> + <label>{{'deposit.file.tree.noFile' | translate}}</label> + </ng-template> + </div> </div> diff --git a/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.scss b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.scss index 5c7ab1cdf99b0660b924d172637fdd368f4eab47..c40fa0474e5f09c82fda2bbfaf324840fa10bae9 100644 --- a/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.scss +++ b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.scss @@ -1,29 +1,28 @@ @import "../../../../../../sass/abstracts/variables"; @import "../../../../../../sass/abstracts/mixins"; -.form { - display: flex; - flex-direction: column; - align-items: center; +:host { + .file-upload { + .upload-button-wrapper { + padding-bottom: 25px; + } - > * { - width: 40%; + padding-bottom: 25px; + } - @include respond-to-breakpoint('xs') { - width: 100%; - } + .file-uploaded { - @include respond-to-breakpoint('sm') { - width: 75%; + .selector-file-view-mode { + padding-bottom: 20px; } - @include respond-to-breakpoint('md') { - width: 50%; - } - } + .file-viewer { + display: flex; - .file-upload { - padding-bottom: 25px; + .tree-folder { + padding-right: 20px; + } + } } } diff --git a/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.ts b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.ts index 8b472e057825603d48a7280304021552599ee494..bb0a4fd4b886b5492f7986423cb5ce8b001ffeef 100644 --- a/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.ts +++ b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.ts @@ -2,8 +2,10 @@ import { ChangeDetectionStrategy, Component, Input, + OnInit, Output, } from "@angular/core"; +import {MatDialog} from "@angular/material/dialog"; import {DepositDataFile} from "@app/features/deposit/models/deposit-data-file.model"; import {FileUploadWrapper} from "@app/features/deposit/models/file-upload-wrapper.model"; import {UploadFileStatus} from "@app/features/deposit/models/upload-file-status.model"; @@ -11,21 +13,44 @@ import {DepositDataFileAction} from "@app/features/deposit/stores/data-file/depo import {DepositAction} from "@app/features/deposit/stores/deposit.action"; import {SharedAbstractContainer} from "@app/shared/components/containers/shared-abstract/shared-abstract.container"; import {LocalStateModel} from "@app/shared/models/local-state.model"; -import {DepositDataFileStatusHistoryAction} from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.action"; +import { + DepositFileDetailDialog, + DepositFileDetailDialogData, +} from "@deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog"; +import {DepositFileUploadArchiveDialog} from "@deposit/components/dialogs/deposit-file-upload-archive/deposit-file-upload-archive.dialog"; +import {DepositFileUploadDialog} from "@deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog"; +import {DepositDataFileState} from "@deposit/stores/data-file/deposit-data-file.state"; import { Select, Store, } from "@ngxs/store"; -import {SharedHistoryDialog} from "@shared/components/dialogs/shared-history/shared-history.dialog"; -import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; +import {ComplianceLevelEnum} from "@shared/enums/business/compliance-level.enum"; +import {DataFileStatusEnum} from "@shared/enums/business/data-file-status.enum"; +import {FieldTypeEnum} from "@shared/enums/field-type.enum"; +import {FileViewModeEnum} from "@shared/enums/file-view-mode.enum"; +import {DataTableColumns} from "@shared/models/data-table-columns.model"; import { BehaviorSubject, Observable, } from "rxjs"; import { + filter, + take, + tap, +} from "rxjs/operators"; +import { + isNonEmptyArray, + isNullOrUndefined, + isUndefined, + ObjectUtil, ObservableUtil, + OrderEnum, + Paging, + QueryParameters, StoreUtil, + TRANSLATE, } from "solidify-frontend"; +import {environment} from "../../../../../../environments/environment"; @Component({ selector: "dlcm-deposit-file-container", @@ -33,12 +58,13 @@ import { styleUrls: ["./deposit-file.container.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DepositFileContainer extends SharedAbstractContainer { +export class DepositFileContainer extends SharedAbstractContainer implements OnInit { @Select((state: LocalStateModel) => state.deposit.deposit_dataFile.list) listDataFileObs: Observable<DepositDataFile[]>; @Select((state: LocalStateModel) => StoreUtil.isLoadingState(state.deposit.deposit_dataFile)) isLoadingDataFileObs: Observable<boolean>; @Select((state: LocalStateModel) => state.deposit.uploadStatus) uploadStatusObs: Observable<UploadFileStatus[]>; @Select((state: LocalStateModel) => StoreUtil.isLoadingState(state.deposit.deposit_dataFile.deposit_dataFile_statusHistory)) isLoadingHistoryObs: Observable<boolean>; - + @Select(DepositDataFileState.listFolders) listFoldersObs: Observable<string[]>; + @Select(DepositDataFileState.queryParameters) queryParametersObs: Observable<QueryParameters>; @Input() parentResId: string; @@ -46,47 +72,244 @@ export class DepositFileContainer extends SharedAbstractContainer { @Input() readonly: boolean = false; + currentFileViewMode: FileViewModeEnum = FileViewModeEnum.FolderView; + + get fileViewModeEnum(): typeof FileViewModeEnum { + return FileViewModeEnum; + } + + private readonly _RELATIVE_LOCATION: string = "relativeLocation"; + columns: DataTableColumns<DepositDataFile>[]; + private readonly _showHistoryBS: BehaviorSubject<DepositDataFile | undefined> = new BehaviorSubject<DepositDataFile | undefined>(undefined); @Output("showHistoryChange") readonly showHistoryObs: Observable<DepositDataFile | undefined> = ObservableUtil.asObservable(this._showHistoryBS); - constructor(protected store: Store) { + constructor(protected readonly _store: Store, + protected readonly _dialog: MatDialog) { super(); } + ngOnInit(): void { + super.ngOnInit(); + this.columns = [ + { + field: "fileName", + header: TRANSLATE("deposit.datafile.table.fileName"), + type: FieldTypeEnum.string, + order: OrderEnum.ascending, + filterableField: "dataFile.relativeLocation" as any, + sortableField: "dataFile.relativeLocation" as any, + isSortable: true, + isFilterable: false, + }, + { + field: "creation.when" as any, + header: TRANSLATE("deposit.datafile.table.creation.when"), + type: FieldTypeEnum.datetime, + order: OrderEnum.none, + isFilterable: false, + isSortable: true, + }, + { + field: "status", + header: TRANSLATE("deposit.datafile.table.status"), + type: FieldTypeEnum.singleSelect, + order: OrderEnum.none, + sortableField: "dataFile.status" as any, + isSortable: true, + isFilterable: true, + translate: true, + filterEnum: [ + { + key: DataFileStatusEnum.IN_ERROR, + value: TRANSLATE("IN_ERROR"), + }, + { + key: DataFileStatusEnum.READY, + value: TRANSLATE("READY"), + }, + { + key: DataFileStatusEnum.RECEIVED, + value: TRANSLATE("RECEIVED"), + }, + { + key: DataFileStatusEnum.CHANGE_RELATIVE_LOCATION, + value: TRANSLATE("CHANGE_RELATIVE_LOCATION"), + }, + { + key: DataFileStatusEnum.TO_PROCESS, + value: TRANSLATE("TO_PROCESS"), + }, + { + key: DataFileStatusEnum.PROCESSED, + value: TRANSLATE("PROCESSED"), + }, + { + key: DataFileStatusEnum.FILE_FORMAT_IDENTIFIED, + value: TRANSLATE("FILE_FORMAT_IDENTIFIED"), + }, + { + key: DataFileStatusEnum.FILE_FORMAT_UNKNOWN, + value: TRANSLATE("FILE_FORMAT_UNKNOWN"), + }, + { + key: DataFileStatusEnum.VIRUS_CHECKED, + value: TRANSLATE("VIRUS_CHECKED"), + }, + { + key: DataFileStatusEnum.CLEANING, + value: TRANSLATE("CLEANING"), + }, + { + key: DataFileStatusEnum.CLEANED, + value: TRANSLATE("CLEANED"), + }, + ], + }, + { + field: "complianceLevel", + header: TRANSLATE("deposit.datafile.table.complianceLevel"), + type: FieldTypeEnum.singleSelect, + order: OrderEnum.none, + sortableField: "dataFile.complianceLevel" as any, + filterableField: "dataFile.complianceLevel" as any, + isSortable: true, + isFilterable: false, + translate: true, + filterEnum: [ + { + key: ComplianceLevelEnum.FULL_COMPLIANCE, + value: TRANSLATE("FULL_COMPLIANCE"), + }, + { + key: ComplianceLevelEnum.AVERAGE_COMPLIANCE, + value: TRANSLATE("AVERAGE_COMPLIANCE"), + }, + { + key: ComplianceLevelEnum.WEAK_COMPLIANCE, + value: TRANSLATE("WEAK_COMPLIANCE"), + }, + { + key: ComplianceLevelEnum.NO_COMPLIANCE, + value: TRANSLATE("NO_COMPLIANCE"), + }, + { + key: ComplianceLevelEnum.NOT_ASSESSED, + value: TRANSLATE("NOT_ASSESSED"), + }, + ], + }, + ]; + } + + onQueryParametersEvent(queryParameters: QueryParameters): void { + this._store.dispatch(new DepositDataFileAction.ChangeQueryParameters(this.parentResId, queryParameters, true)); + // this._changeDetector.detectChanges(); // Allow to display spinner the first time + } + upload($event: FileUploadWrapper): void { - this.store.dispatch(new DepositAction.SendDataFile(this.parentResId, $event)); + this._store.dispatch(new DepositAction.SendDataFile(this.parentResId, $event)); } uploadArchive($event: FileUploadWrapper): void { - this.store.dispatch(new DepositAction.SendDataFile(this.parentResId, $event, true)); + this._store.dispatch(new DepositAction.SendDataFile(this.parentResId, $event, true)); } retry($event: UploadFileStatus): void { - this.store.dispatch(new DepositAction.RetrySendDataFile(this.parentResId, $event)); + this._store.dispatch(new DepositAction.RetrySendDataFile(this.parentResId, $event)); } cancel($event: UploadFileStatus): void { - this.store.dispatch(new DepositAction.MarkAsCancelDataFileSending(this.parentResId, $event)); + this._store.dispatch(new DepositAction.MarkAsCancelDataFileSending(this.parentResId, $event)); } delete($event: DepositDataFile): void { - this.store.dispatch(new DepositDataFileAction.Delete(this.parentResId, $event)); + this._store.dispatch(new DepositDataFileAction.Delete(this.parentResId, $event)); } resume($event: DepositDataFile): void { - this.store.dispatch(new DepositDataFileAction.ResumeDataFile(this.parentResId, $event)); + this._store.dispatch(new DepositDataFileAction.ResumeDataFile(this.parentResId, $event)); } refresh(): void { - this.store.dispatch(new DepositDataFileAction.GetAll(this.parentResId, undefined, true)); + this._store.dispatch(new DepositDataFileAction.GetAll(this.parentResId, undefined, true)); } download($event: DepositDataFile): void { - this.store.dispatch(new DepositDataFileAction.Download(this.parentResId, $event)); + this._store.dispatch(new DepositDataFileAction.Download(this.parentResId, $event)); } showHistory(depositDataFile: DepositDataFile): void { this._showHistoryBS.next(depositDataFile); } + + selectFolder(folderFullName: string | undefined): void { + if (isUndefined(folderFullName)) { + this.currentFileViewMode = FileViewModeEnum.FlatView; + } else { + this.currentFileViewMode = FileViewModeEnum.FolderView; + } + + this.subscribe(this.queryParametersObs.pipe( + take(1), + tap(queryParam => { + const queryParameters = ObjectUtil.clone(queryParam); + queryParameters.search = ObjectUtil.clone(queryParameters.search); + queryParameters.paging = new Paging(); + // queryParameters.search.searchItems = ObjectUtil.clone(queryParameters.search.searchItems); + if (isNullOrUndefined(folderFullName)) { + queryParameters.search.searchItems.delete(this._RELATIVE_LOCATION); + } else { + queryParameters.search.searchItems.set(this._RELATIVE_LOCATION, folderFullName); + } + this._store.dispatch(new DepositDataFileAction.ChangeQueryParameters(this.parentResId, queryParameters, true)); + }), + )); + } + + getCurrentFolder(queryParameters: QueryParameters): string | undefined { + if (isNullOrUndefined(queryParameters) || isNullOrUndefined(queryParameters.search) || isNullOrUndefined(queryParameters.search.searchItems)) { + return undefined; + } + return queryParameters.search.searchItems.get(this._RELATIVE_LOCATION); + } + + showDetail(depositDataFile: DepositDataFile): void { + const dialogRef = this._dialog.open(DepositFileDetailDialog, { + data: { + depositDataFile: depositDataFile, + showHistory: this._showHistoryBS, + depositId: this.parentResId, + } as DepositFileDetailDialogData, + height: environment.modalHeight, + width: environment.modalWidth, + }); + } + + openModalUpload(): void { + const dialogRef = this._dialog.open(DepositFileUploadDialog, { + height: "90%", + width: "90%", + }); + this.subscribe(dialogRef.afterClosed().pipe( + filter((listFilesUploadWrapper: FileUploadWrapper[]) => isNonEmptyArray(listFilesUploadWrapper)), + tap((listFilesUploadWrapper: FileUploadWrapper[]) => { + listFilesUploadWrapper.forEach(f => this.upload(f)); + }), + )); + } + + openModalUploadArchive(): void { + const dialogRef = this._dialog.open(DepositFileUploadArchiveDialog, { + height: "90%", + width: "90%", + }); + this.subscribe(dialogRef.afterClosed().pipe( + filter((fileUploadWrapper: FileUploadWrapper) => !isNullOrUndefined(fileUploadWrapper)), + tap((fileUploadWrapper: FileUploadWrapper) => { + this.upload(fileUploadWrapper); + }), + )); + } } diff --git a/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.html b/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.html index 460860bc7498b943ed1647b9962ec74a7e8824bb..dac936d1b6b9d84460d798301d72e83b3b20d274 100644 --- a/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.html +++ b/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.html @@ -1,10 +1,37 @@ <h1 mat-dialog-title>{{'deposit.file.details.title' | translate:paramMessage}}</h1> <div mat-dialog-content> + + <button mat-button + color="accent" + (click)="delete()" + > + <mat-icon>delete</mat-icon> + {{'deposit.file.button.delete' | translate}} + </button> + + <button *ngIf="data.depositDataFile.status === depositDataFileStateEnum.IN_ERROR" + mat-button + color="accent" + (click)="resume()" + > + <mat-icon>warning</mat-icon> + {{'deposit.file.button.resume' | translate}} + </button> + + <button mat-button + color="accent" + (click)="showHistory()" + > + <fa-icon icon="history"></fa-icon> + {{'deposit.file.button.showHistory' | translate}} + </button> + + <mat-list> <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.createDate' | translate}}</span> - <span class="value">{{data.creation.when | date}}</span> + <span class="value">{{data.depositDataFile.creation.when | date}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -12,7 +39,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.status' | translate}}</span> - <span class="value">{{data.status}}</span> + <span class="value">{{data.depositDataFile.status}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -20,7 +47,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.statusMessage' | translate}}</span> - <span class="value">{{data.statusMessage}}</span> + <span class="value">{{data.depositDataFile.statusMessage}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -28,7 +55,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.category' | translate}}</span> - <span class="value">{{data.dataCategory}}</span> + <span class="value">{{data.depositDataFile.dataCategory}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -36,7 +63,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.type' | translate}}</span> - <span class="value">{{data.dataType}}</span> + <span class="value">{{data.depositDataFile.dataType}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -44,7 +71,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.complianceLevel' | translate}}</span> - <span class="value">{{data.complianceLevel}}</span> + <span class="value">{{data.depositDataFile.complianceLevel}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -52,7 +79,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.smartSize' | translate}}</span> - <span class="value">{{data.smartSize}}</span> + <span class="value">{{data.depositDataFile.smartSize}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -60,7 +87,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.available' | translate}}</span> - <span class="value">{{data.available}}</span> + <span class="value">{{data.depositDataFile.available}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -68,7 +95,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.fileFormat' | translate}}</span> - <span class="value">{{data.fileFormat}}</span> + <span class="value">{{data.depositDataFile.fileFormat}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -76,7 +103,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.virusCheck' | translate}}</span> - <span class="value">{{data.virusCheck}}</span> + <span class="value">{{data.depositDataFile.virusCheck}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -84,7 +111,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.initialPath' | translate}}</span> - <span class="value">{{data.initialPath}}</span> + <span class="value">{{data.depositDataFile.initialPath}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -92,7 +119,7 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.fileSize' | translate}}</span> - <span class="value">{{data.fileSize | filesize}}</span> + <span class="value">{{data.depositDataFile.fileSize | filesize}}</span> </div> </mat-list-item> <mat-divider></mat-divider> @@ -100,7 +127,9 @@ <mat-list-item> <div class="item-content"> <span class="key">{{'deposit.file.detail.data.checksums' | translate}}</span> - <span class="value">{{data.checksums === undefined || data.checksums === null || data.checksums.length === 0 ? 'no' : 'yes'}}</span> + <span class="value">{{data.depositDataFile.checksums === undefined || data.depositDataFile.checksums === null || + data.depositDataFile.checksums.length === 0 ? 'no' : + 'yes'}}</span> </div> </mat-list-item> <mat-divider></mat-divider> diff --git a/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.ts b/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.ts index 9ed0c75e1f9a9a23ec9eda82bb4e7c24b4867076..29474fbd0b7aec7c7ae3d7d14f37f62378da5598 100644 --- a/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.ts +++ b/src/app/features/deposit/components/dialogs/deposit-file-detail/deposit-file-detail.dialog.ts @@ -8,8 +8,23 @@ import { MAT_DIALOG_DATA, MatDialogRef, } from "@angular/material"; -import {DepositDataFile} from "@app/features/deposit/models/deposit-data-file.model"; +import {MatDialog} from "@angular/material/dialog"; +import { + DepositDataFile, + DepositDataFileStateEnum, +} from "@app/features/deposit/models/deposit-data-file.model"; import {SharedAbstractContainer} from "@app/shared/components/containers/shared-abstract/shared-abstract.container"; +import {DepositDataFileAction} from "@deposit/stores/data-file/deposit-data-file.action"; +import { + Select, + Store, +} from "@ngxs/store"; +import {LocalStateModel} from "@shared/models/local-state.model"; +import {StatusHistory} from "@shared/models/status-history.model"; +import { + BehaviorSubject, + Observable, +} from "rxjs"; @Component({ selector: "dlcm-deposit-file-detail-dialog", @@ -20,17 +35,43 @@ import {SharedAbstractContainer} from "@app/shared/components/containers/shared- export class DepositFileDetailDialog extends SharedAbstractContainer implements OnInit { paramMessage: { name: string } = {name: ""}; - constructor(protected dialogRef: MatDialogRef<DepositFileDetailDialog>, - @Inject(MAT_DIALOG_DATA) public data: DepositDataFile) { + @Select((state: LocalStateModel) => state.deposit.deposit_dataFile.deposit_dataFile_statusHistory.history) historyObs: Observable<StatusHistory[]>; + + get depositDataFileStateEnum(): typeof DepositDataFileStateEnum { + return DepositDataFileStateEnum; + } + + constructor(private readonly _store: Store, + private readonly _dialog: MatDialog, + protected dialogRef: MatDialogRef<DepositFileDetailDialog>, + @Inject(MAT_DIALOG_DATA) public data: DepositFileDetailDialogData) { super(); } ngOnInit(): void { super.ngOnInit(); - this.paramMessage.name = this.data.fileName; + this.paramMessage.name = this.data.depositDataFile.fileName; + } + + delete(): void { + this._store.dispatch(new DepositDataFileAction.Delete(this.data.depositId, this.data.depositDataFile)); + } + + resume(): void { + this._store.dispatch(new DepositDataFileAction.ResumeDataFile(this.data.depositId, this.data.depositDataFile)); } close(): void { this.dialogRef.close(); } + + showHistory(): void { + this.data.showHistory.next(this.data.depositDataFile); + } +} + +export interface DepositFileDetailDialogData { + depositId: string; + depositDataFile: DepositDataFile; + showHistory: BehaviorSubject<DepositDataFile>; } diff --git a/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.html b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.html new file mode 100644 index 0000000000000000000000000000000000000000..4dca1d1c560c643489d908d2a0635af6ceaf7b36 --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.html @@ -0,0 +1,96 @@ +<div class="tree-header"> + <label>{{'deposit.file.tree.label' | translate}}</label> + <button mat-icon-button + mat-button + (click)="refresh()" + [matTooltip]="'deposit.file.tree.refresh' | translate" + [matTooltipPosition]="'left'" + [class.spinning]="isLoading" + [disabled]="isLoading" + > + <fa-icon icon="sync-alt"></fa-icon> + </button> +</div> + +<!--*ngIf="dataSource && listFolders"--> + +<mat-tree [dataSource]="dataSource" + [treeControl]="treeControl" +> + <mat-tree-node *matTreeNodeDef="let node" + matTreeNodePadding + > + <button mat-icon-button + disabled + ></button> + {{node.folderName}} + </mat-tree-node> + <mat-tree-node *matTreeNodeDef="let node; when: hasChild" + matTreeNodePadding + > + <button mat-icon-button + [attr.aria-label]="'toggle ' + node.filename" + matTreeNodeToggle + > + <mat-icon class="mat-icon-rtl-mirror"> + {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}} + </mat-icon> + </button> + {{node.folderName}} + <mat-progress-bar *ngIf="node.isLoading" + mode="indeterminate" + class="example-tree-progress-bar" + ></mat-progress-bar> + </mat-tree-node> +</mat-tree> + +<pre>{{dataSource.data | json}}</pre> + + +<!--<mat-tree--> +<!-- [dataSource]="dataSource"--> +<!-- [treeControl]="treeControl"--> +<!-- class="file-tree"--> +<!-->--> +<!-- <mat-tree-node *matTreeNodeDef="let node"--> +<!-- matTreeNodeToggle--> +<!-- >--> +<!-- <li class="mat-tree-node">--> +<!-- leaf--> +<!-- <!– use a disabled button to provide padding for tree leaf –>--> +<!-- <!– <button mat-icon-button–>--> +<!-- <!– disabled–>--> +<!-- <!– ></button>–>--> +<!-- <dlcm-deposit-file [readonly]="readonly"--> +<!-- [depositDataFile]="node.file"--> +<!-- (deleteChange)="delete($event)"--> +<!-- (resumeChange)="resume($event)"--> +<!-- (downloadChange)="download($event)"--> +<!-- (showHistoryChange)="showHistory($event)"--> +<!-- [isLoadingHistory]="isLoadingHistory"--> +<!-- class="file"--> +<!-- >--> +<!-- </dlcm-deposit-file>--> +<!-- </li>--> +<!-- </mat-tree-node>--> + +<!-- <!– This is the tree node template for expandable nodes –>--> +<!-- <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">--> +<!-- <li>--> +<!-- folder--> +<!-- <div class="mat-tree-node">--> +<!-- <button mat-icon-button--> +<!-- mat-button--> +<!-- matTreeNodeToggle--> +<!-- [attr.aria-label]="'toggle ' + node.name"--> +<!-- >--> +<!-- <fa-icon [icon]="treeControl.isExpanded(node) ? 'folder-open' : 'folder'"></fa-icon>--> +<!-- </button>--> +<!-- {{node.item}}--> +<!-- </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/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.scss b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.scss new file mode 100644 index 0000000000000000000000000000000000000000..b8b8a9930361fc796933ead45080eefe989a8b8b --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.scss @@ -0,0 +1,57 @@ +@import "../../../../../../sass/abstracts/variables"; + +$line-item-size: 30px; + +.tree-header { + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + + fa-icon { + font-size: 20px; + } + + button { + transition-property: transform; + transition-duration: 1s; + + position: absolute; + top: -20px; + right: 10px; + + &.spinning { + animation-name: rotate; + animation-duration: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; + } + + @keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + } +} + +.file { + width: 100%; +} + +.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/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.spec.ts b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..feca09477b37479dfddee05dfff402117d2865c1 --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.spec.ts @@ -0,0 +1,34 @@ +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import { + async, + ComponentFixture, + TestBed, +} from "@angular/core/testing"; +import {MaterialModule} from "@app/material.module"; +import {MockTranslatePipe} from "../../../../../../test-helpers/mock-translate.pipe"; + +import {DepositFileTreePresentational} from "./deposit-file-tree.presentational"; + +describe("DepositFileTreePresentational", () => { + let component: DepositFileTreePresentational; + let fixture: ComponentFixture<DepositFileTreePresentational>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MaterialModule], + declarations: [DepositFileTreePresentational, MockTranslatePipe], + schemas: [NO_ERRORS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DepositFileTreePresentational); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.ts b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.ts new file mode 100644 index 0000000000000000000000000000000000000000..1eed7b3fb9613fe74f14504406b766a7edda2e44 --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-file-tree-experiment/deposit-file-tree.presentational.ts @@ -0,0 +1,416 @@ +import { + CollectionViewer, + SelectionChange, +} from "@angular/cdk/collections"; +import {FlatTreeControl} from "@angular/cdk/tree"; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + Injectable, + Input, + OnInit, + Output, +} from "@angular/core"; +import {DepositDataFile} from "@app/features/deposit/models/deposit-data-file.model"; +import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational"; +import { + BehaviorSubject, + merge, + Observable, +} from "rxjs"; +import {map} from "rxjs/operators"; +import { + isNullOrUndefined, + ObservableUtil, +} from "solidify-frontend"; + +interface FileNode { + name: string; + file?: DepositDataFile; + children?: FileNode[]; +} + +@Component({ + selector: "dlcm-deposit-file-tree", + templateUrl: "./deposit-file-tree.presentational.html", + styleUrls: ["./deposit-file-tree.presentational.scss"], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DepositFileTreePresentational extends SharedAbstractPresentational implements OnInit { + private _listDepositDataFile: DepositDataFile[]; + + @Input() + readonly: boolean; + + @Input() + isLoading: boolean; + + @Input() + isLoadingHistory: boolean; + + @Input() + set listDepositDataFile(val: DepositDataFile[]) { + // this._listDepositDataFile = val; + // this.adaptListDepositDataFileToTree(); + // this.applyTreeToDataSource(); + } + + private _listFolders: string[]; + + @Input() + set listFolders(value: string[]) { + this._listFolders = value; + + if (isNullOrUndefined(this.treeControl)) { + // this.treeControl = new FlatTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandable); + } + // let count = 0; + // + // this.dataSource.data = []; + // this._listFolders.forEach(folder => { + // this.dataSource.data.push({ + // item: folder, + // isLoading: false, + // level: count, + // expandable: true, + // }); + // count++; + // }); + // + // this.dataSource.data = [...this.dataSource.data]; + } + + get listFolders(): string[] { + return this._listFolders; + } + + get listDepositDataFile(): DepositDataFile[] { + return this._listDepositDataFile; + } + + private readonly _resumeBS: BehaviorSubject<DepositDataFile | undefined> = new BehaviorSubject<DepositDataFile | undefined>(undefined); + @Output("resumeChange") + readonly resumeObs: Observable<DepositDataFile | undefined> = ObservableUtil.asObservable(this._resumeBS); + + private readonly _deleteBS: BehaviorSubject<DepositDataFile | undefined> = new BehaviorSubject<DepositDataFile | undefined>(undefined); + @Output("deleteChange") + readonly deleteObs: Observable<DepositDataFile | undefined> = ObservableUtil.asObservable(this._deleteBS); + + private readonly _refreshBS: BehaviorSubject<void | undefined> = new BehaviorSubject<void | undefined>(undefined); + @Output("refreshChange") + readonly refreshObs: Observable<void | undefined> = ObservableUtil.asObservable(this._refreshBS); + + private readonly _downloadBS: BehaviorSubject<DepositDataFile | undefined> = new BehaviorSubject<DepositDataFile | undefined>(undefined); + @Output("downloadChange") + readonly downloadObs: Observable<DepositDataFile | undefined> = ObservableUtil.asObservable(this._downloadBS); + + private readonly _showHistoryBS: BehaviorSubject<DepositDataFile | undefined> = new BehaviorSubject<DepositDataFile | undefined>(undefined); + @Output("showHistoryChange") + readonly showHistoryObs: Observable<DepositDataFile | undefined> = ObservableUtil.asObservable(this._showHistoryBS); + + constructor(changeDetector: ChangeDetectorRef) { + super(); + const database: DynamicDatabase = new DynamicDatabase(); + this.treeControl = new FlatTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandable); + this.dataSource = new DynamicDataSource(this.treeControl, database, changeDetector); + + this.dataSource.data = database.initialData(); + } + + treeControl: FlatTreeControl<DynamicFlatNode>; + dataSource: DynamicDataSource; + + getLevel = (node: DynamicFlatNode) => node.level; + + isExpandable = (node: DynamicFlatNode) => node.expandable; + + hasChild = (_: number, _nodeData: DynamicFlatNode) => _nodeData.expandable; + + // fileTree: FileNode[]; + // + // // treeControl: NestedTreeControl<FileNode> = new NestedTreeControl<FileNode>(node => node.children); + // // dataSource: MatTreeNestedDataSource<FileNode> = new MatTreeNestedDataSource<FileNode>(); + // dataSource: MatTreeNestedDataSource<DynamicFlatNode> = new MatTreeNestedDataSource<DynamicFlatNode>(); + // + // private readonly _SEPARATOR: string = "/"; + // private readonly _ROOT: string = this._SEPARATOR; + // + // constructor(public dialog: MatDialog) { + // super(); + // } + // + // ngOnInit(): void { + // if (isNullOrUndefined(this.treeControl)) { + // this.treeControl = new NestedTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandable); + // } + // // this.dataSource = new DynamicDataSource(this.treeControl, database); + // // this.dataSource.data = database.initialData(); + // } + // + // treeControl: FlatTreeControl<DynamicFlatNode>; + // + // // dataSource: DynamicDataSource; + // + // getLevel = (node: DynamicFlatNode) => node.level; + // + // isExpandable = (node: DynamicFlatNode) => node.expandable; + // + // hasChild = (_: number, _nodeData: DynamicFlatNode) => _nodeData.expandable; + // + // 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: DepositDataFile): void { + // const isIntoRootFolder = dataFile.relativeLocation === this._ROOT; + // if (isIntoRootFolder) { + // this.addFileToRootDataFile(dataFile); + // } else { + // const relativeLocation = dataFile.relativeLocation; + // const pathParts = isString(relativeLocation) ? relativeLocation.split(this._SEPARATOR) + // .filter(part => part !== StringUtil.stringEmpty) : []; + // this.recursiveAddFolder(this.fileTree, dataFile, pathParts); + // } + // } + // + // private recursiveAddFolder(levelParent: FileNode[], dataFile: DepositDataFile, 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: FileNode): void { + // if (parentFileNode.children === undefined) { + // parentFileNode.children = []; + // } + // } + // + // private addFileToRootDataFile(dataFile: DepositDataFile): void { + // this.fileTree.push(this.getFileNodeFormDataFile(dataFile)); + // } + // + // private getFileNodeFormDataFile(dataFile: DepositDataFile): 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; + + delete(depositDataFile: DepositDataFile): void { + this._deleteBS.next(depositDataFile); + } + + resume(depositDataFile: DepositDataFile): void { + this._resumeBS.next(depositDataFile); + } + + refresh(): void { + this._refreshBS.next(); + } + + download(depositDataFile: DepositDataFile): void { + this._downloadBS.next(depositDataFile); + } + + showHistory(depositDataFile: DepositDataFile): void { + this._showHistoryBS.next(depositDataFile); + } +} + +export class DynamicFlatNode { + constructor(public folderName: string, + public fullFolderName: string, + public level: number = 1, + public expandable: boolean = false, + public isLoading: boolean = false) {} +} + +export class DynamicDatabase { + dataMap: Map<string, string[]> = new Map<string, string[]>([ + ["Fruits", ["Apple", "Orange", "Banana"]], + ["Vegetables", ["Tomato", "Potato", "Onion"]], + ["Apple", ["Fuji", "Macintosh"]], + ["Onion", ["Yellow", "White", "Purple"]], + ]); + + listFolders: string[] = [ + "/", + "/subfolder", + "/subfolder/test1", + "/subfolder2", + ]; + + listFoldersComputed: DynamicFlatNode[] = []; + + dataFileMap: Map<string, string[]> = new Map<string, string[]>([ + ["/", ["Apple", "Orange", "Banana"]], + ["/subfolder", ["Tomato", "Potato", "Onion"]], + ["/subfolder/test1", ["Fuji", "Macintosh"]], + ["/subfolder/test1/test2", ["fdsaf", "ddd"]], + ["/subfolder2", ["Yellow", "White", "Purple"]], + ]); + + rootLevelNodes: string[] = ["Fruits", "Vegetables"]; + + /** Initial data from database */ + initialData(): DynamicFlatNode[] { + const listFile = this.dataFileMap.get("/").map(f => new DynamicFlatNode(f, f, 1, false)); + + const fileSeparator = "/"; + this.listFolders.forEach(fullFolderName => { + if (isNullOrUndefined(fullFolderName) || fullFolderName === fileSeparator || fullFolderName.indexOf(fileSeparator) === -1) { + return; + } + const level = this.getLevel(fullFolderName); + const folderName = this.getFolderName(fullFolderName); + this.listFoldersComputed.push(new DynamicFlatNode(folderName, fullFolderName, level, true)); + }); + + return [...this.listFoldersComputed.filter(folder => folder.level === 1), ...listFile]; + } + + getFolderName(fullFolderName: string): string { + const fileSeparator = "/"; + const splitted = fullFolderName.split(fileSeparator); + return splitted[splitted.length - 1]; + } + + getLevel(fullFolderName: string): number { + const fileSeparator = "/"; + const splitted = fullFolderName.split(fileSeparator); + return splitted.length - 1; + } + + isInDeep(name: string, desiredDeep: number): boolean { + return name.split("/").length === 2 + desiredDeep; + } + + getChildren(node: DynamicFlatNode): string[] | undefined { + // TODO Make call api here with relativeLocation = node + const folder = this.listFolders.filter(name => name.startsWith(node.fullFolderName) && this.isInDeep(node.fullFolderName, node.level + 1)); + const files = this.dataFileMap.get(node.fullFolderName); + return [...folder, ...files]; + } + + isExpandable(node: string): boolean { + return this.dataMap.has(node); + } +} + +@Injectable() +export class DynamicDataSource { + + dataChange: BehaviorSubject<DynamicFlatNode[]> = new BehaviorSubject<DynamicFlatNode[]>([]); + + get data(): DynamicFlatNode[] { + return this.dataChange.value; + } + + set data(value: DynamicFlatNode[]) { + this._treeControl.dataNodes = value; + this.dataChange.next(value); + } + + constructor(private _treeControl: FlatTreeControl<DynamicFlatNode>, + private _database: DynamicDatabase, + private changeDetector: ChangeDetectorRef) {} + + connect(collectionViewer: CollectionViewer): Observable<DynamicFlatNode[]> { + this._treeControl.expansionModel.onChange.subscribe(change => { + if ((change as SelectionChange<DynamicFlatNode>).added || + (change as SelectionChange<DynamicFlatNode>).removed) { + this.handleTreeControl(change as SelectionChange<DynamicFlatNode>); + } + }); + + return merge(collectionViewer.viewChange, this.dataChange).pipe( + map(() => this.data), + ); + } + + /** Handle expand/collapse behaviors */ + handleTreeControl(change: SelectionChange<DynamicFlatNode>): void { + if (change.added) { + change.added.forEach(node => this.toggleNode(node, true)); + } + if (change.removed) { + change.removed.slice().reverse().forEach(node => this.toggleNode(node, false)); + } + } + + /** + * Toggle the node, remove from display list + */ + toggleNode(node: DynamicFlatNode, expand: boolean): boolean { + const children = this._database.getChildren(node); + const index = this.data.indexOf(node); + if (!children || index < 0) { // If no children, or cannot find the node, no op + return; + } + + node.isLoading = true; + + setTimeout(() => { + if (expand) { + const nodes = children.map(name => new DynamicFlatNode(name, name, node.level + 1, this._database.isExpandable(name))); + this.data.splice(index + 1, 0, ...nodes); + } else { + let count = 0; + for (let i = index + 1; i < this.data.length + && this.data[i].level > node.level; i++, count++) { + } + this.data.splice(index + 1, count); + } + + // notify the change + this.dataChange.next(this.data); + node.isLoading = false; + this.changeDetector.detectChanges(); + }, 1000); + } +} diff --git a/src/app/features/deposit/components/presentationals/deposit-file-tree/deposit-file-tree.presentational.ts b/src/app/features/deposit/components/presentationals/deposit-file-tree/deposit-file-tree.presentational.ts index 54aaa2442644b78161bec5b34893784d184339b5..7be5735c60187934e28f1286713837ba48c4c616 100644 --- a/src/app/features/deposit/components/presentationals/deposit-file-tree/deposit-file-tree.presentational.ts +++ b/src/app/features/deposit/components/presentationals/deposit-file-tree/deposit-file-tree.presentational.ts @@ -3,12 +3,11 @@ import { ChangeDetectionStrategy, Component, Input, + OnInit, Output, } from "@angular/core"; -import { - MatDialog, - MatTreeNestedDataSource, -} from "@angular/material"; +import {MatDialog} from "@angular/material/dialog"; +import {MatTreeNestedDataSource} from "@angular/material/tree"; import {DepositDataFile} from "@app/features/deposit/models/deposit-data-file.model"; import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational"; import { @@ -16,6 +15,7 @@ import { Observable, } from "rxjs"; import { + isNullOrUndefined, isString, ObservableUtil, StringUtil, @@ -33,7 +33,7 @@ interface FileNode { styleUrls: ["./deposit-file-tree.presentational.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DepositFileTreePresentational extends SharedAbstractPresentational { +export class DepositFileTreePresentational extends SharedAbstractPresentational implements OnInit { private _listDepositDataFile: DepositDataFile[]; @Input() @@ -52,6 +52,35 @@ export class DepositFileTreePresentational extends SharedAbstractPresentational this.applyTreeToDataSource(); } + private _listFolders: string[]; + + @Input() + set listFolders(value: string[]) { + this._listFolders = value; + + if (isNullOrUndefined(this.treeControl)) { + // this.treeControl = new FlatTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandable); + } + // let count = 0; + // + // this.dataSource.data = []; + // this._listFolders.forEach(folder => { + // this.dataSource.data.push({ + // item: folder, + // isLoading: false, + // level: count, + // expandable: true, + // }); + // count++; + // }); + // + // this.dataSource.data = [...this.dataSource.data]; + } + + get listFolders(): string[] { + return this._listFolders; + } + get listDepositDataFile(): DepositDataFile[] { return this._listDepositDataFile; } diff --git a/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.html b/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.html index 7a7d30e03a3bfa830dfdb65dba314b6be4978912..47ef1cca2f680dd1a417f66ea6d0425bdb8c88f4 100644 --- a/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.html +++ b/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.html @@ -1,27 +1,17 @@ -<ng-container *ngFor="let fileUploading of listFilesUploading; trackBy: trackByFn"> - <dlcm-deposit-file [uploadStatus]="fileUploading" - (cancelChange)="cancel($event)" - (retryChange)="retry($event)" - ></dlcm-deposit-file> -</ng-container> +<h1>{{'deposit.file.title.inProgressUpload' | translate}}</h1> + +<div *ngIf="getCurrentNumberOfFileInProgress() > 0" + class="file-in-progress" +> + <ng-container *ngFor="let fileUploading of listFilesUploading; trackBy: trackByFn"> + <dlcm-deposit-file [uploadStatus]="fileUploading" + (cancelChange)="cancel($event)" + (retryChange)="retry($event)" + ></dlcm-deposit-file> + </ng-container> +</div> <div *ngIf="getCurrentNumberOfFileInProgress() === 0" class="no-file-in-progress" > {{'deposit.file.inProgress.noFile' | translate}} </div> - -<button mat-button - color="accent" - type="button" - (click)="openModalUpload()" -> - {{'deposit.file.button.upload' | translate}} -</button> - -<button mat-button - color="accent" - type="button" - (click)="openModalUploadArchive()" -> - {{'deposit.file.button.uploadArchive' | translate}} -</button> diff --git a/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.scss b/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.scss index 76faf2927051c27886195597e44e76466e4ef4bf..cde5c40baa7a30992bb9bf6f63dcec84edd46e70 100644 --- a/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.scss +++ b/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.scss @@ -1,5 +1,8 @@ @import "../../../../../../sass/abstracts/variables"; +.file-in-progress { + padding-bottom: 20px; +} + .no-file-in-progress { - padding: 20px 0; } diff --git a/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.ts b/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.ts index de277af56f7ca7548dc4d43858539ebab0dcb571..a08bed21b92c1bc04f9bb9bb9c50b658c5be1c4e 100644 --- a/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.ts +++ b/src/app/features/deposit/components/presentationals/deposit-files-upload-in-progress/deposit-files-upload-in-progress.presentational.ts @@ -6,26 +6,16 @@ import { Output, } from "@angular/core"; import {MatDialog} from "@angular/material"; -import {DepositFileUploadDialog} from "@app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog"; import {FileUploadStatusEnum} from "@app/features/deposit/enums/file-upload-status.enum"; import {FileUploadWrapper} from "@app/features/deposit/models/file-upload-wrapper.model"; import {UploadFileStatus} from "@app/features/deposit/models/upload-file-status.model"; import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational"; import {Guid} from "@app/shared/models/guid.model"; -import {DepositFileUploadArchiveDialog} from "@deposit/components/dialogs/deposit-file-upload-archive/deposit-file-upload-archive.dialog"; import { BehaviorSubject, Observable, } from "rxjs"; -import { - filter, - tap, -} from "rxjs/operators"; -import { - isNonEmptyArray, - isNullOrUndefined, - ObservableUtil, -} from "solidify-frontend"; +import {ObservableUtil} from "solidify-frontend"; @Component({ selector: "dlcm-deposit-files-upload-in-progress", @@ -113,32 +103,6 @@ export class DepositFilesUploadInProgressPresentational extends SharedAbstractPr this._retryBS.next(uploadStatus); } - openModalUpload(): void { - const dialogRef = this.dialog.open(DepositFileUploadDialog, { - height: "90%", - width: "90%", - }); - this.subscribe(dialogRef.afterClosed().pipe( - filter((listFilesUploadWrapper: FileUploadWrapper[]) => isNonEmptyArray(listFilesUploadWrapper)), - tap((listFilesUploadWrapper: FileUploadWrapper[]) => { - listFilesUploadWrapper.forEach(f => this._uploadBS.next(f)); - }), - )); - } - - openModalUploadArchive(): void { - const dialogRef = this.dialog.open(DepositFileUploadArchiveDialog, { - height: "90%", - width: "90%", - }); - this.subscribe(dialogRef.afterClosed().pipe( - filter((fileUploadWrapper: FileUploadWrapper) => !isNullOrUndefined(fileUploadWrapper)), - tap((fileUploadWrapper: FileUploadWrapper) => { - this._uploadArchiveBS.next(fileUploadWrapper); - }), - )); - } - trackByFn(index: number, item: UploadFileStatus): Guid { return item.guid; } diff --git a/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.html b/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.html new file mode 100644 index 0000000000000000000000000000000000000000..2b9fa0874447e57a79c47234e10bb06f782a3de5 --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.html @@ -0,0 +1,50 @@ +<mat-tree *ngIf="dataSource" + [dataSource]="dataSource" + [treeControl]="treeControl" + class="file-tree" +> + <mat-tree-node *matTreeNodeDef="let node" + matTreeNodeToggle + matTreeNodePadding + [matTreeNodePaddingIndent]="indentation" + [class.is-active]="node.fullFolderName === currentFolder" + > + <button mat-icon-button + disabled + > + <fa-icon [icon]="'folder-open'"></fa-icon> + </button> + <a class="selectable" + (click)="select(node)" + > + {{node.folderName}} + </a> + </mat-tree-node> + + <!-- This is the tree node template for expandable nodes --> + <mat-tree-node *matTreeNodeDef="let node; when: hasChild" + matTreeNodePadding + [matTreeNodePaddingIndent]="indentation" + [class.is-active]="node.fullFolderName === currentFolder" + > + <button mat-icon-button + mat-button + matTreeNodeToggle + [attr.aria-label]="'toggle ' + node.folderName" + > + <fa-icon [icon]="treeControl.isExpanded(node) ? 'folder-open' : 'folder'"></fa-icon> + </button> + + <a *ngIf="!node.postCreation; else postCreation" + class="selectable" + (click)="select(node)" + > + {{node.folderName}} + </a> + <ng-template #postCreation> + <span class="intermediate-node"> + {{node.folderName}} + </span> + </ng-template> + </mat-tree-node> +</mat-tree> diff --git a/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.scss b/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.scss new file mode 100644 index 0000000000000000000000000000000000000000..8e509ab086a9e57491686ff31d09441e0705d580 --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.scss @@ -0,0 +1,74 @@ +@import "../../../../../../sass/abstracts/variables"; + + +.selectable { + cursor: pointer; + color: $text-color; +} + +.intermediate-node { + cursor: not-allowed; + color: $light-grey; +} + +.is-active { + > .selectable { + color: $primary-color; + } +} + +$line-item-size: 30px; + +.tree-header { + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + + fa-icon { + font-size: 20px; + } + + button { + transition-property: transform; + transition-duration: 1s; + + position: absolute; + top: -20px; + right: 10px; + + &.spinning { + animation-name: rotate; + animation-duration: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; + } + + @keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + } +} + +.file { + width: 100%; +} + +.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/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.ts b/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.ts new file mode 100644 index 0000000000000000000000000000000000000000..04ec6dcbf508d96c164c08513f85077e50952a09 --- /dev/null +++ b/src/app/features/deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational.ts @@ -0,0 +1,159 @@ +import {FlatTreeControl} from "@angular/cdk/tree"; +import { + ChangeDetectionStrategy, + Component, + Input, + OnInit, + Output, +} from "@angular/core"; +import { + MatTreeFlatDataSource, + MatTreeFlattener, +} from "@angular/material/tree"; +import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational"; +import { + BehaviorSubject, + Observable, +} from "rxjs"; +import { + isNullOrUndefined, + ObservableUtil, +} from "solidify-frontend"; + +export class FlatNode { + constructor(public folderName: string, + public fullFolderName: string, + public level: number = 0, + public expandable: boolean = false, + public postCreation: boolean = false) {} +} + +@Component({ + selector: "dlcm-deposit-folder-tree", + templateUrl: "./deposit-folder-tree.presentational.html", + styleUrls: ["./deposit-folder-tree.presentational.scss"], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DepositFolderTreePresentational extends SharedAbstractPresentational implements OnInit { + private readonly _SEPARATOR: string = "/"; + private readonly _ROOT: string = this._SEPARATOR; + readonly indentation: number = 20; + + @Input() + currentFolder: string | undefined; + + private listNewIntermediateFolder: string[]; + + private readonly _selectBS: BehaviorSubject<string> = new BehaviorSubject<string>(undefined); + @Output("selectChange") + readonly selectObs: Observable<string> = ObservableUtil.asObservable(this._selectBS); + + @Input() + isLoading: boolean; + + private _listFolders: string[]; + + @Input() + set listFolders(value: string[]) { + this._listFolders = value; + if (isNullOrUndefined(this._listFolders)) { + return; + } + const listFoders = [...this._listFolders]; + this.createIntermediateFolders(listFoders); + this.dataSource.data = listFoders; + } + + private _transformer = (fullFolderName: string): FlatNode => ({ + expandable: this.existChild(fullFolderName), + folderName: this.getFolderName(fullFolderName), + fullFolderName: fullFolderName, + level: this.getLevel(fullFolderName), + postCreation: this.isPostCreate(fullFolderName), + }); + + treeControl: FlatTreeControl<FlatNode> = new FlatTreeControl<FlatNode>(node => node.level, node => node.expandable); + treeFlattener: MatTreeFlattener<string, FlatNode> = new MatTreeFlattener(this._transformer, node => node.level, node => node.expandable, node => []/*node.children*/); + dataSource: MatTreeFlatDataSource<string, FlatNode> = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); + + constructor() { + super(); + } + + private createIntermediateFolders(listFolders: string[]): void { + this.listNewIntermediateFolder = []; + listFolders.forEach(path => { + if (path === this._ROOT) { + return; + } + this.recursivelyAddParentIfMissing(this.listNewIntermediateFolder, path); + + // const path.split(this._SEPARATOR); + }); + listFolders.push(...this.listNewIntermediateFolder); + this.sort(listFolders); + } + + private sort(listFolders: string[]): void { + listFolders.sort((a, b) => { + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + return 0; + }); + } + + private recursivelyAddParentIfMissing(listNewIntermediateFolder: string[], path: string): void { + const lastIndexSeparator = path.lastIndexOf(this._SEPARATOR); + if (lastIndexSeparator === 0) { + return; + } + const parentPath = path.substring(0, lastIndexSeparator); + if (!this.listFolders.includes(parentPath)) { + listNewIntermediateFolder.push(parentPath); + this.recursivelyAddParentIfMissing(listNewIntermediateFolder, parentPath); + } + } + + get listFolders(): string[] { + return this._listFolders; + } + + getFolderName(fullFolderName: string): string { + if (fullFolderName === this._ROOT) { + return fullFolderName; + } + const splitted = fullFolderName.split(this._SEPARATOR); + return splitted[splitted.length - 1]; + } + + getLevel(fullFolderName: string): number { + if (fullFolderName === this._SEPARATOR) { + return 0; + } + const splitted = fullFolderName.split(this._SEPARATOR); + return splitted.length - 1; + } + + hasChild = (_: number, node: FlatNode): boolean => node.expandable; + + select(node: FlatNode): void { + this.currentFolder = node.fullFolderName; + this._selectBS.next(node.fullFolderName); + } + + private existChild(currentFullFolderName: string): boolean { + if (currentFullFolderName === this._ROOT) { + return true; + } + const item = this.dataSource.data.find(fullFolderName => fullFolderName.startsWith(currentFullFolderName + this._SEPARATOR)); + return !isNullOrUndefined(item); + } + + private isPostCreate(fullFolderName: string): boolean { + return this.listNewIntermediateFolder.includes(fullFolderName); + } +} diff --git a/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.ts b/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.ts index 5f84d082191abb867452016a5c68b93d4694d59a..5bff95c2756afa7dba7d499f5127409c145e5950 100644 --- a/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.ts +++ b/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.ts @@ -26,7 +26,10 @@ import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; import {StatusHistory} from "@shared/models/status-history.model"; import {SharedOrgUnitAction} from "@shared/stores/organizational-unit/shared-organizational-unit.action"; import {Observable} from "rxjs"; -import {ResourceActionHelper} from "solidify-frontend"; +import { + QueryParameters, + ResourceActionHelper, +} from "solidify-frontend"; @Component({ selector: "dlcm-deposit-file-routable", @@ -69,7 +72,10 @@ export class DepositFileRoutable extends SharedAbstractEditRoutable<DepositExten } getSubResourceWithParentId(id: string): void { - this.store.dispatch(new DepositDataFileAction.GetAll(id, undefined, true)); + const queryParameter = new QueryParameters(); + queryParameter.search.searchItems.set("relativeLocation", "/"); + this.store.dispatch(new DepositDataFileAction.GetAll(id, queryParameter, true)); + this.store.dispatch(new DepositDataFileAction.GetListFolder(id)); } showHistory(depositDataFile: DepositDataFile): void { diff --git a/src/app/features/deposit/deposit.module.ts b/src/app/features/deposit/deposit.module.ts index 9b6165259f7108e12efdb038ddaa7315fbfebf9d..7a886f4518107fff0470403a28a9fbdfc5269cd4 100644 --- a/src/app/features/deposit/deposit.module.ts +++ b/src/app/features/deposit/deposit.module.ts @@ -15,6 +15,7 @@ import {SharedModule} from "@app/shared/shared.module"; import {DepositFileUploadArchiveDialog} from "@deposit/components/dialogs/deposit-file-upload-archive/deposit-file-upload-archive.dialog"; import {DepositPersonAlternativeDialog} from "@deposit/components/dialogs/deposit-person-alternative/deposit-person-alternative.dialog"; import {DepositPersonDialog} from "@deposit/components/dialogs/deposit-person/deposit-person.dialog"; +import {DepositFolderTreePresentational} from "@deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational"; import {DepositFileRoutable} from "@deposit/components/routables/deposit-file/deposit-file.routable"; import {DepositMetadataRoutable} from "@deposit/components/routables/deposit-metadata/deposit-metadata.routable"; import {DepositDataFileStatusHistoryState} from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.state"; @@ -48,6 +49,7 @@ const dialogs = [ const presentationals = [ DepositFormPresentational, DepositFileTreePresentational, + DepositFolderTreePresentational, DepositFilesUploadInProgressPresentational, DepositFilePresentational, ]; @@ -68,7 +70,7 @@ const presentationals = [ DepositDataFileState, DepositPeopleState, DepositStatusHistoryState, - DepositDataFileStatusHistoryState + DepositDataFileStatusHistoryState, ]), ], entryComponents: [ diff --git a/src/app/features/deposit/stores/data-file/deposit-data-file.action.ts b/src/app/features/deposit/stores/data-file/deposit-data-file.action.ts index c4174e2aaaf783c0017247bbedc6c582c5c18074..7012e48bdd35542a918ca557f8ea361138927c3b 100644 --- a/src/app/features/deposit/stores/data-file/deposit-data-file.action.ts +++ b/src/app/features/deposit/stores/data-file/deposit-data-file.action.ts @@ -1,7 +1,10 @@ import {DepositDataFile} from "@app/features/deposit/models/deposit-data-file.model"; import { + BaseAction, + BaseSubAction, CompositionAction, CompositionNameSpace, + QueryParameters, TypeDefaultAction, } from "solidify-frontend"; import {LocalStateEnum} from "../../../../shared/enums/local-state.enum"; @@ -57,6 +60,14 @@ export namespace DepositDataFileAction { export class DeleteFail extends CompositionAction.DeleteFail<DepositDataFile> { } + export class ChangeQueryParameters extends BaseAction { + static readonly type: string = `[${state}] Change Query Parameters`; + + constructor(public parentId: string, public queryParameters: QueryParameters, public keepCurrentContext: boolean = false) { + super(); + } + } + export class ResumeDataFile { static readonly type: string = `[${state}] Resume Data File`; @@ -70,6 +81,30 @@ export namespace DepositDataFileAction { constructor(public parentId: string, public dataFile: DepositDataFile) { } } + + export class GetListFolder extends BaseAction { + static readonly type: string = `[${state}] Get List Folder`; + + constructor(public parentId: string) { + super(); + } + } + + export class GetListFolderSuccess extends BaseSubAction<GetListFolder> { + static readonly type: string = `[${state}] Get List Folder Success`; + + constructor(public parentAction: GetListFolder, public folder: string[]) { + super(parentAction); + } + } + + export class GetListFolderFail extends BaseSubAction<GetListFolder> { + static readonly type: string = `[${state}] Get List Folder Fail`; + + constructor(public parentAction: GetListFolder) { + super(parentAction); + } + } } export const depositDataFileActionNameSpace: CompositionNameSpace = DepositDataFileAction; diff --git a/src/app/features/deposit/stores/data-file/deposit-data-file.state.ts b/src/app/features/deposit/stores/data-file/deposit-data-file.state.ts index fafc375d2b7f289f6032791f9590741091ef1402..19d351f205ee45f5c67e28862f56cd01ea536195 100644 --- a/src/app/features/deposit/stores/data-file/deposit-data-file.state.ts +++ b/src/app/features/deposit/stores/data-file/deposit-data-file.state.ts @@ -20,31 +20,43 @@ import { import { Action, Actions, + Selector, State, StateContext, Store, } from "@ngxs/store"; +import {defaultStatusHistoryInitValue} from "@shared/stores/status-history/status-history.state"; import {saveAs} from "file-saver"; import {Observable} from "rxjs"; import {tap} from "rxjs/internal/operators/tap"; +import {catchError} from "rxjs/operators"; import { ApiService, CompositionState, CompositionStateModel, defaultCompositionStateInitValue, NotificationService, + QueryParameters, + StoreUtil, TRANSLATE, } from "solidify-frontend"; +export const defaultDepositDataFileValue: () => DepositDataFileStateModel = () => + ({ + ...defaultCompositionStateInitValue(), + deposit_dataFile_statusHistory: defaultStatusHistoryInitValue(), + listFolders: [], + }); + export interface DepositDataFileStateModel extends CompositionStateModel<DepositDataFile> { deposit_dataFile_statusHistory: DepositDataFileStatusHistoryStateModel; + listFolders: string[]; } @State<DepositDataFileStateModel>({ name: LocalStateEnum.deposit_dataFile, defaults: { - ...defaultCompositionStateInitValue(), - deposit_dataFile_statusHistory: null, + ...defaultDepositDataFileValue(), }, children: [ DepositDataFileStatusHistoryState, @@ -67,8 +79,23 @@ export class DepositDataFileState extends CompositionState<DepositDataFile> { return PreIngestResourceApiEnum.deposits; } + @Selector() + static listFolders(state: DepositDataFileStateModel): string[] { + return state.listFolders; + } + + @Selector() + static isLoading(state: DepositDataFileStateModel): boolean { + return StoreUtil.isLoadingState(state); + } + + @Selector() + static queryParameters(state: DepositDataFileStateModel): QueryParameters { + return state.queryParameters; + } + @Action(DepositDataFileAction.ResumeDataFile) - resume(ctx: StateContext<DepositDataFileState>, action: DepositDataFileAction.ResumeDataFile): Observable<void> { + resume(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.ResumeDataFile): Observable<void> { return this.apiService.post<void>(`${this._urlResource}/${action.parentId}/${ApiResourceNameEnum.DATAFILE}/${action.dataFile.resId}/${ApiActionEnum.RESUME}`) .pipe( tap(() => { @@ -79,7 +106,7 @@ export class DepositDataFileState extends CompositionState<DepositDataFile> { } @Action(DepositDataFileAction.Download) - download(ctx: StateContext<DepositDataFileState>, action: DepositDataFileAction.Download): void { + download(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.Download): void { let headers = new HttpHeaders(); headers = headers.set("Content-Disposition", "attachment; filename=" + action.dataFile.fileName); @@ -97,4 +124,45 @@ export class DepositDataFileState extends CompositionState<DepositDataFile> { const blob = new Blob([blobContent], {type: "application/octet-stream"}); saveAs(blob, fileName); } + + @Action(DepositDataFileAction.GetListFolder) + getListFolder(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.GetListFolder): Observable<string[]> { + ctx.patchState({ + isLoadingCounter: ctx.getState().isLoadingCounter + 1, + }); + + return this.httpClient.get<string[]>(`${this._urlResource}/${action.parentId}/${ApiResourceNameEnum.DATAFILE}/${ApiActionEnum.LIST_FOLDERS}`) + .pipe( + tap((listFolder: string[]) => { + ctx.dispatch(new DepositDataFileAction.GetListFolderSuccess(action, listFolder)); + }), + catchError(error => { + ctx.dispatch(new DepositDataFileAction.GetListFolderFail(action)); + throw error; + }), + ); + } + + @Action(DepositDataFileAction.GetListFolderSuccess) + getListFolderSuccess(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.GetListFolderSuccess): void { + ctx.patchState({ + isLoadingCounter: ctx.getState().isLoadingCounter - 1, + listFolders: action.folder, + }); + } + + @Action(DepositDataFileAction.GetListFolderFail) + getListFolderFail(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.GetListFolderFail): void { + ctx.patchState({ + isLoadingCounter: ctx.getState().isLoadingCounter - 1, + }); + } + + @Action(DepositDataFileAction.ChangeQueryParameters) + changeQueryParameters(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.ChangeQueryParameters): void { + ctx.patchState({ + queryParameters: action.queryParameters, + }); + ctx.dispatch(new DepositDataFileAction.GetAll(action.parentId, undefined, action.keepCurrentContext)); + } } diff --git a/src/app/features/deposit/stores/deposit.state.ts b/src/app/features/deposit/stores/deposit.state.ts index 7cea789a21d81459d126083813ebdfe3f300710f..4393a367ad80f2ea4baaa55d0112adba11bb01a6 100644 --- a/src/app/features/deposit/stores/deposit.state.ts +++ b/src/app/features/deposit/stores/deposit.state.ts @@ -10,6 +10,7 @@ import {UploadEventModel} from "@app/features/deposit/models/upload-event.model" import {UploadFileStatus} from "@app/features/deposit/models/upload-file-status.model"; import {depositDataFileActionNameSpace} from "@app/features/deposit/stores/data-file/deposit-data-file.action"; import { + defaultDepositDataFileValue, DepositDataFileState, DepositDataFileStateModel, } from "@app/features/deposit/stores/data-file/deposit-data-file.state"; @@ -60,7 +61,6 @@ import { ApiService, CompositionActionHelper, defaultAssociationRemoteStateInitValue, - defaultCompositionStateInitValue, defaultResourceStateInitValue, ErrorDto, HttpStatus, @@ -88,7 +88,7 @@ export interface DepositStateModel extends ResourceStateModel<DepositExtended> { name: LocalStateEnum.deposit, defaults: { ...defaultResourceStateInitValue(), - deposit_dataFile: {...defaultCompositionStateInitValue(), deposit_dataFile_statusHistory: {...defaultStatusHistoryInitValue()}}, + deposit_dataFile: defaultDepositDataFileValue(), deposit_person: {...defaultAssociationRemoteStateInitValue()}, isLoadingDataFile: false, uploadStatus: [], diff --git a/src/app/features/home/components/routables/home-search/home-search.routable.ts b/src/app/features/home/components/routables/home-search/home-search.routable.ts index edd7b30f30c32bb7ef8a8d61da843a70d20327fb..90bee944ad515ce27f4427df9e10b7b9a3f18f42 100644 --- a/src/app/features/home/components/routables/home-search/home-search.routable.ts +++ b/src/app/features/home/components/routables/home-search/home-search.routable.ts @@ -32,6 +32,7 @@ import {filter} from "rxjs/internal/operators/filter"; import {tap} from "rxjs/internal/operators/tap"; import { isNotNullNorUndefined, + OrderEnum, QueryParameters, StoreUtil, TRANSLATE, @@ -74,7 +75,7 @@ export class HomeSearchRoutable extends SharedAbstractPresentational implements field: "organizationalUnit", header: TRANSLATE("access.table.header.organizationalUnit"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, @@ -82,7 +83,7 @@ export class HomeSearchRoutable extends SharedAbstractPresentational implements field: "title", header: TRANSLATE("access.table.header.title"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, @@ -90,7 +91,7 @@ export class HomeSearchRoutable extends SharedAbstractPresentational implements field: "yearPublicationDate", header: TRANSLATE("access.table.header.yearPublicationDate"), type: FieldTypeEnum.number, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, @@ -98,7 +99,7 @@ export class HomeSearchRoutable extends SharedAbstractPresentational implements field: "accessLevel", header: TRANSLATE("access.table.header.accessLevel"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, diff --git a/src/app/features/organizational-unit/components/routables/orgunit-list/orgunit-list.routable.ts b/src/app/features/organizational-unit/components/routables/orgunit-list/orgunit-list.routable.ts index d6f2a4ae88328711e4d909d0affce6542211540d..1e5f79c68a199c9834da2c9186063f6a8ae44622 100644 --- a/src/app/features/organizational-unit/components/routables/orgunit-list/orgunit-list.routable.ts +++ b/src/app/features/organizational-unit/components/routables/orgunit-list/orgunit-list.routable.ts @@ -10,7 +10,10 @@ import {SharedAbstractListRoutable} from "@app/shared/components/routables/share import {FieldTypeEnum} from "@app/shared/enums/field-type.enum"; import {LocalStateEnum} from "@app/shared/enums/local-state.enum"; import {Store} from "@ngxs/store"; -import {TRANSLATE} from "solidify-frontend"; +import { + OrderEnum, + TRANSLATE, +} from "solidify-frontend"; @Component({ selector: "dlcm-orgunit-list-routable", @@ -34,7 +37,7 @@ export class OrgunitListRoutable extends SharedAbstractListRoutable<Organization field: "name", header: TRANSLATE("organizationalUnit.table.header.name"), type: FieldTypeEnum.string, - order: 1, + order: OrderEnum.ascending, isFilterable: true, isSortable: true, }, @@ -42,7 +45,7 @@ export class OrgunitListRoutable extends SharedAbstractListRoutable<Organization field: "description", header: TRANSLATE("organizationalUnit.table.header.description"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, @@ -50,7 +53,7 @@ export class OrgunitListRoutable extends SharedAbstractListRoutable<Organization field: "creation.when" as any, header: TRANSLATE("organizationalUnit.table.header.creation.when"), type: FieldTypeEnum.datetime, - order: 0, + order: OrderEnum.none, isFilterable: true, isSortable: true, }, diff --git a/src/app/shared/components/dialogs/shared-history/shared-history.dialog.ts b/src/app/shared/components/dialogs/shared-history/shared-history.dialog.ts index 1419f8ac95d8c35b7356d4db8b0ac026dcacf482..08aad5c31ee106526fe21b8e8601caf7dec64a1b 100644 --- a/src/app/shared/components/dialogs/shared-history/shared-history.dialog.ts +++ b/src/app/shared/components/dialogs/shared-history/shared-history.dialog.ts @@ -12,6 +12,7 @@ import {FieldTypeEnum} from "@shared/enums/field-type.enum"; import {DataTableColumns} from "@shared/models/data-table-columns.model"; import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; import { + OrderEnum, StringUtil, TRANSLATE, } from "solidify-frontend"; @@ -40,7 +41,7 @@ export class SharedHistoryDialog extends SharedAbstractContainer implements OnIn field: "changeTime", header: TRANSLATE("app.history.table.changeTime"), type: FieldTypeEnum.datetime, - order: 1, + order: OrderEnum.ascending, isSortable: false, isFilterable: false, }, @@ -48,7 +49,7 @@ export class SharedHistoryDialog extends SharedAbstractContainer implements OnIn field: "status", header: TRANSLATE("app.history.table.status"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, @@ -56,7 +57,7 @@ export class SharedHistoryDialog extends SharedAbstractContainer implements OnIn field: "description", header: TRANSLATE("app.history.table.description"), type: FieldTypeEnum.number, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, @@ -64,7 +65,7 @@ export class SharedHistoryDialog extends SharedAbstractContainer implements OnIn field: "createdBy", header: TRANSLATE("app.history.table.createdBy"), type: FieldTypeEnum.string, - order: 0, + order: OrderEnum.none, isSortable: false, isFilterable: false, }, diff --git a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts index 6bc30678cf349254943fd32624311518204cab4f..edaa0399b2f622f3974886f8f168f57e703cb928 100644 --- a/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts +++ b/src/app/shared/components/presentationals/shared-data-table/shared-data-table.presentational.ts @@ -168,7 +168,7 @@ export class SharedDataTablePresentational<T> extends SharedAbstractPresentation private adaptLazyLoadEventToSortModel($event: LazyLoadEvent): Sort { return { - field: $event.sortField, + field: this.getSortableFieldByFieldName($event.sortField), order: $event.sortOrder === -1 ? OrderEnum.descending : OrderEnum.ascending, } as Sort; } @@ -202,4 +202,13 @@ export class SharedDataTablePresentational<T> extends SharedAbstractPresentation private getFilterableField(col: DataTableColumns): string { return isNullOrUndefined(col.filterableField) ? col.field : col.filterableField; } + + private getSortableField(col: DataTableColumns): string { + return isNullOrUndefined(col.sortableField) ? col.field : col.sortableField; + } + + getSortableFieldByFieldName(fieldName: string): string { + const column = this.columns.find(col => col.field === fieldName); + return this.getSortableField(column); + } } diff --git a/src/app/shared/enums/business/compliance-level.enum.ts b/src/app/shared/enums/business/compliance-level.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..f92520f5444fbdf2e8ba27d34addbb84c114a462 --- /dev/null +++ b/src/app/shared/enums/business/compliance-level.enum.ts @@ -0,0 +1,7 @@ +export enum ComplianceLevelEnum { + NOT_ASSESSED = "NOT_ASSESSED", + NO_COMPLIANCE = "NO_COMPLIANCE", + WEAK_COMPLIANCE = "WEAK_COMPLIANCE", + AVERAGE_COMPLIANCE = "AVERAGE_COMPLIANCE", + FULL_COMPLIANCE = "FULL_COMPLIANCE", +} diff --git a/src/app/shared/enums/business/data-file-status.enum.ts b/src/app/shared/enums/business/data-file-status.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..abf370516d25de10633ed5a3755f020a0a737c02 --- /dev/null +++ b/src/app/shared/enums/business/data-file-status.enum.ts @@ -0,0 +1,13 @@ +export enum DataFileStatusEnum { + IN_ERROR = "IN_ERROR", + READY = "READY", + RECEIVED = "RECEIVED", + CHANGE_RELATIVE_LOCATION = "CHANGE_RELATIVE_LOCATION", + TO_PROCESS = "TO_PROCESS", + PROCESSED = "PROCESSED", + FILE_FORMAT_IDENTIFIED = "FILE_FORMAT_IDENTIFIED", + FILE_FORMAT_UNKNOWN = "FILE_FORMAT_UNKNOWN", + VIRUS_CHECKED = "VIRUS_CHECKED", + CLEANING = "CLEANING", + CLEANED = "CLEANED", +} diff --git a/src/app/shared/enums/file-view-mode.enum.ts b/src/app/shared/enums/file-view-mode.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..68fcafe1cf1ef120446d79b4a38f2d2bbd1d41aa --- /dev/null +++ b/src/app/shared/enums/file-view-mode.enum.ts @@ -0,0 +1,4 @@ +export enum FileViewModeEnum { + FlatView, + FolderView, +} diff --git a/src/app/shared/models/data-table-columns.model.ts b/src/app/shared/models/data-table-columns.model.ts index c563a452f05f3d1efe5a9e156e96773d75628aff..d948725b01f72ae0634758d58d467d7e06d65c3a 100644 --- a/src/app/shared/models/data-table-columns.model.ts +++ b/src/app/shared/models/data-table-columns.model.ts @@ -17,6 +17,7 @@ export interface DataTableColumns<T = any> { isSortable?: boolean; isFilterable?: boolean; filterableField?: keyof T & string; + sortableField?: keyof T & string; resourceNameSpace?: ResourceNameSpace; resourceState?: ResourceState<any>; } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 434656ebecd56572bcb383906d92ba54dc6a5795..a4ea3fc2a9928967135dbe750b2e1b49c09367c3 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1,15 +1,30 @@ { "APPROVED": "Approved", + "AVERAGE_COMPLIANCE": "Average compliance", + "CHANGE_RELATIVE_LOCATION": "Change relative location", "CHECKED": "Checked", + "CLEANED": "CLEANED", + "CLEANING": "CLEANING", "COMPLETED": "Completed", "Description is invalid": "Description is invalid", + "FILE_FORMAT_IDENTIFIED": "File format identified", + "FILE_FORMAT_UNKNOWN": "File format unknown", + "FULL_COMPLIANCE": "Full compliance", "IN_ERROR": "In error", "IN_PROGRESS": "In progress", "IN_VALIDATION": "In validation", + "NOT_ASSESSED": "Not assessed", + "NO_COMPLIANCE": "No compliance", "PAUSED": "Paused", + "PROCESSED": "Processed", + "READY": "Ready", + "RECEIVED": "Received", "REJECTED": "Rejected", "SUBMITTED": "Submitted", + "TO_PROCESS": "To process", "URL is invalid": "URL is invalid", + "VIRUS_CHECKED": "Virus checked", + "WEAK_COMPLIANCE": "Weak compliance", "access": { "organizationalUnitNotFound": "Unable to get data", "search": { @@ -650,6 +665,16 @@ "backToEdit": "Return to editing", "collectionBegin": "Data Collection Start Date", "collectionEnd": "Data Collection End Date", + "datafile": { + "table": { + "complianceLevel": "Compliance level", + "creation": { + "when": "Sending date" + }, + "fileName": "File name", + "status": "Status" + } + }, "delete": "Delete", "description": "Description", "dialog": { @@ -667,6 +692,11 @@ }, "file": { "button": { + "delete": "deposit.file.button.delete", + "flatView": "deposit.file.button.flatView", + "resume": "deposit.file.button.resume", + "showHistory": "deposit.file.button.showHistory", + "showTree": "deposit.file.button.showTree", "upload": "Upload files", "uploadArchive": "Upload archive" }, @@ -995,4 +1025,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 434656ebecd56572bcb383906d92ba54dc6a5795..4840c3a4881f7e5f9dc0063fbe19f943886d3114 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1,15 +1,30 @@ { "APPROVED": "Approved", + "AVERAGE_COMPLIANCE": "Average compliance", + "CHANGE_RELATIVE_LOCATION": "Change relative location", "CHECKED": "Checked", + "CLEANED": "CLEANED", + "CLEANING": "CLEANING", "COMPLETED": "Completed", "Description is invalid": "Description is invalid", + "FILE_FORMAT_IDENTIFIED": "File format identified", + "FILE_FORMAT_UNKNOWN": "File format unknown", + "FULL_COMPLIANCE": "Full compliance", "IN_ERROR": "In error", "IN_PROGRESS": "In progress", "IN_VALIDATION": "In validation", + "NOT_ASSESSED": "Not assessed", + "NO_COMPLIANCE": "No compliance", "PAUSED": "Paused", + "PROCESSED": "Processed", + "READY": "Ready", + "RECEIVED": "Received", "REJECTED": "Rejected", "SUBMITTED": "Submitted", + "TO_PROCESS": "To process", "URL is invalid": "URL is invalid", + "VIRUS_CHECKED": "Virus checked", + "WEAK_COMPLIANCE": "Weak compliance", "access": { "organizationalUnitNotFound": "Unable to get data", "search": { @@ -650,6 +665,16 @@ "backToEdit": "Return to editing", "collectionBegin": "Data Collection Start Date", "collectionEnd": "Data Collection End Date", + "datafile": { + "table": { + "complianceLevel": "Compliance level", + "creation": { + "when": "Sending date" + }, + "fileName": "File name", + "status": "Status" + } + }, "delete": "Delete", "description": "Description", "dialog": { @@ -667,6 +692,11 @@ }, "file": { "button": { + "delete": "Delete", + "flatView": "Flat view", + "resume": "Retry", + "showHistory": "Show history", + "showTree": "Folder view", "upload": "Upload files", "uploadArchive": "Upload archive" }, diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 870064e260b2feab33e827cd31d36b92c8f2c426..e628ba1f37835fe767e2875219e4a13f0c262c28 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -1,15 +1,30 @@ { "APPROVED": "Approuvé", + "AVERAGE_COMPLIANCE": "Conformité moyenne", + "CHANGE_RELATIVE_LOCATION": "Changement de position relative", "CHECKED": "Vérifié", + "CLEANED": "Nettoyé", + "CLEANING": "En cours de nettoyage", "COMPLETED": "Complété", "Description is invalid": "La description est invalide", + "FILE_FORMAT_IDENTIFIED": "Format de fichier identifié", + "FILE_FORMAT_UNKNOWN": "Format de fichier inconnue", + "FULL_COMPLIANCE": "Conformité compléte", "IN_ERROR": "En erreur", "IN_PROGRESS": "En cours", "IN_VALIDATION": "En validation", + "NOT_ASSESSED": "Non évalué", + "NO_COMPLIANCE": "Aucune conformité", "PAUSED": "En pause", + "PROCESSED": "Traité", + "READY": "Prêt", + "RECEIVED": "Reçu", "REJECTED": "Rejeté", "SUBMITTED": "Soumis", + "TO_PROCESS": "À traiter", "URL is invalid": "URL invalide", + "VIRUS_CHECKED": "Virus vérifié", + "WEAK_COMPLIANCE": "Conformité faible", "access": { "organizationalUnitNotFound": "Impossible d'obtenir les données", "search": { @@ -650,6 +665,16 @@ "backToEdit": "Retour en édition", "collectionBegin": "Début de la collecte des données", "collectionEnd": "Fin de la collecte des données", + "datafile": { + "table": { + "complianceLevel": "Niveau de conformité", + "creation": { + "when": "Date d'envoi" + }, + "fileName": "Nom du fichier", + "status": "Statut" + } + }, "delete": "Supprimer", "description": "Description", "dialog": { @@ -667,6 +692,11 @@ }, "file": { "button": { + "delete": "Supprimer", + "flatView": "Vue à plat", + "resume": "Réessayer", + "showHistory": "Voir l'historique", + "showTree": "Voir l'arborescence", "upload": "Envoyer fichiers", "uploadArchive": "Envoyer archive" }, @@ -974,7 +1004,7 @@ "filter": { "all": "Tous" }, - "nodata": "Aucune donnée n'a été trouvé", + "nodata": "Aucune donnée n'a été trouvée", "paginator": { "firstPage": "Première page", "itemPerPage": "Eléments par page",