From 9dc8010602a994a4bc3093e8c9384980ec72dee9 Mon Sep 17 00:00:00 2001 From: Florent POITTEVIN <poittevin.florent@gmail.com> Date: Wed, 15 Apr 2020 14:19:37 +0200 Subject: [PATCH] feat: 1253 merge file collection and uploads tabs in one --- .../deposit-collection.container.html} | 13 - .../deposit-collection.container.scss} | 6 +- .../deposit-collection.container.ts} | 48 +- .../deposit-file/deposit-file.container.html | 86 ++++ .../deposit-file/deposit-file.container.scss | 104 ++++ .../deposit-file/deposit-file.container.ts | 473 ++++++++++++++++++ .../deposit-upload.container.html | 65 +++ .../deposit-upload.container.scss | 24 + .../deposit-upload.container.ts} | 66 ++- .../deposit-file-upload.dialog.html | 4 +- .../deposit-file-upload.dialog.ts | 20 +- .../deposit-detail-edit.routable.ts | 95 ++-- .../deposit-file/deposit-file.routable.html | 91 +--- .../deposit-file/deposit-file.routable.scss | 6 +- .../deposit-file/deposit-file.routable.ts | 437 +--------------- .../deposit-upload.routable.html | 31 -- .../deposit-upload.routable.scss | 12 - .../deposit/deposit-routing.module.ts | 33 +- src/app/features/deposit/deposit.module.ts | 13 +- .../deposit/enums/mode-deposit-tab.enum.ts | 5 + .../deposit/helpers/deposit.helper.ts | 46 -- .../collection/deposit-collection.action.ts | 11 +- .../collection/deposit-collection.state.ts | 48 +- .../data-file/deposit-data-file.state.ts | 3 +- .../features/deposit/stores/deposit.action.ts | 4 + .../features/deposit/stores/deposit.state.ts | 24 +- .../preservation/dip/stores/dip.state.ts | 2 +- src/app/shared/enums/routes.enum.ts | 4 +- .../deposit-detail-tab-guard.service.ts | 75 --- src/assets/i18n/de.json | 20 +- src/assets/i18n/en.json | 20 +- src/assets/i18n/fr.json | 22 +- 32 files changed, 1021 insertions(+), 890 deletions(-) rename src/app/features/deposit/components/{routables/deposit-collection/deposit-collection.routable.html => containers/deposit-collection/deposit-collection.container.html} (68%) rename src/app/features/deposit/components/{routables/deposit-collection/deposit-collection.routable.scss => containers/deposit-collection/deposit-collection.container.scss} (82%) rename src/app/features/deposit/components/{routables/deposit-collection/deposit-collection.routable.ts => containers/deposit-collection/deposit-collection.container.ts} (83%) create mode 100644 src/app/features/deposit/components/containers/deposit-file/deposit-file.container.html create mode 100644 src/app/features/deposit/components/containers/deposit-file/deposit-file.container.scss create mode 100644 src/app/features/deposit/components/containers/deposit-file/deposit-file.container.ts create mode 100644 src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.html create mode 100644 src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.scss rename src/app/features/deposit/components/{routables/deposit-upload/deposit-upload.routable.ts => containers/deposit-upload/deposit-upload.container.ts} (68%) delete mode 100644 src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.html delete mode 100644 src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.scss create mode 100644 src/app/features/deposit/enums/mode-deposit-tab.enum.ts delete mode 100644 src/app/shared/guards/deposit-detail-tab-guard.service.ts diff --git a/src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.html b/src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.html similarity index 68% rename from src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.html rename to src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.html index 05f31ad00..1ea8524f3 100644 --- a/src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.html +++ b/src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.html @@ -2,19 +2,6 @@ <div class="header"> <div class="left-part"> <h1 class="title">{{'deposit.aip.title.collection' | translate}}</h1> - <button *ngIf="(canEditObs | async) && (currentObs | async)?.status === depositStatusEnum.INPROGRESS" - mat-button - class="button-add-to-collection" - color="primary" - type="button" - (click)="openModalUpload()" - solidifyShortCuts - (onEnter)="openModalUpload()" - [matTooltip]="'deposit.collection.button.addAip.tooltip' | translate" - > - <mat-icon>publish</mat-icon> - {{'deposit.collection.button.addAip.label' | translate}} - </button> </div> <div class="right-part"> <dlcm-shared-button-refresh [isLoading]="isLoadingCollectionObs | async" diff --git a/src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.scss b/src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.scss similarity index 82% rename from src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.scss rename to src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.scss index 927398331..d4ad9be35 100644 --- a/src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.scss +++ b/src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.scss @@ -1,6 +1,6 @@ -@import "../../../../../../sass/abstracts/variables"; -@import "../../../../../../sass/abstracts/mixins"; -@import "../../../../../shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable"; +@import "src/sass/abstracts/variables"; +@import "src/sass/abstracts/mixins"; +@import "src/app/shared/components/routables/shared-abstract-detail-edit-common/shared-abstract-detail-edit-common.routable"; :host { padding: 0; diff --git a/src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.ts b/src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.ts similarity index 83% rename from src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.ts rename to src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.ts index 633a51e6f..28f5f585e 100644 --- a/src/app/features/deposit/components/routables/deposit-collection/deposit-collection.routable.ts +++ b/src/app/features/deposit/components/containers/deposit-collection/deposit-collection.container.ts @@ -8,10 +8,6 @@ import { MatDialogRef, } from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; -import { - DepositAipUploadDialog, - DepositAipUploadDialogData, -} from "@deposit/components/dialogs/deposit-aip-upload/deposit-aip-upload.dialog"; import {DepositCollectionAction} from "@deposit/stores/collection/deposit-collection.action"; import {DepositCollectionState} from "@deposit/stores/collection/deposit-collection.state"; import {depositActionNameSpace} from "@deposit/stores/deposit.action"; @@ -44,13 +40,9 @@ import {DataTableActions} from "@shared/models/data-table-actions.model"; import {DataTableColumns} from "@shared/models/data-table-columns.model"; import {SecurityService} from "@shared/services/security.service"; import {Observable} from "rxjs"; -import { - filter, - tap, -} from "rxjs/operators"; +import {tap} from "rxjs/operators"; import { AssociationRemoteState, - isNonEmptyArray, isNullOrUndefined, MemoizedUtil, OrderEnum, @@ -61,11 +53,11 @@ import { } from "solidify-frontend"; @Component({ - selector: "dlcm-deposit-collection-routable", - templateUrl: "./deposit-collection.routable.html", - styleUrls: ["./deposit-collection.routable.scss"], + selector: "dlcm-deposit-collection-container", + templateUrl: "./deposit-collection.container.html", + styleUrls: ["./deposit-collection.container.scss"], }) -export class DepositCollectionRoutable extends SharedAbstractDetailEditRoutable<Deposit, DepositStateModel> implements OnInit { +export class DepositCollectionContainer extends SharedAbstractDetailEditRoutable<Deposit, DepositStateModel> implements OnInit { readonly KEY_PARAM_NAME: keyof Deposit & string = undefined; isLoadingCollectionObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositCollectionState); @@ -92,10 +84,6 @@ export class DepositCollectionRoutable extends SharedAbstractDetailEditRoutable< }, ]; - get depositStatusEnum(): typeof Deposit.StatusEnum { - return Deposit.StatusEnum; - } - constructor(protected readonly _store: Store, protected readonly _route: ActivatedRoute, protected readonly _actions$: Actions, @@ -216,32 +204,6 @@ export class DepositCollectionRoutable extends SharedAbstractDetailEditRoutable< this._store.dispatch(new DepositCollectionAction.GoToAip(aip)); } - openModalUpload(): void { - const selected = AssociationRemoteState.selectedSnapshot(this._store, DepositCollectionState); - const dialogRef = this._dialog.open(DepositAipUploadDialog, { - width: "90%", - data: { - listIdAipAlreadyLinked: selected.map(aip => aip.resId), - } as DepositAipUploadDialogData, - }); - this.subscribe(dialogRef.afterClosed().pipe( - filter((listAipIds: string[]) => isNonEmptyArray(listAipIds)), - tap(((listAipIds: string[]) => { - this.addAip(listAipIds); - }), - ))); - } - - addAip(aipIds: string[]): void { - this._store.dispatch(new DepositCollectionAction.Create(this._resId, aipIds)); - this.subscribe(this._actions$.pipe( - ofActionCompleted(DepositCollectionAction.CreateSuccess), - tap(() => { - this._store.dispatch(new DepositCollectionAction.Refresh(this._resId)); - }), - )); - } - deleteAip(_resId: string, aip: Aip): void { this.subscribe(this._actions$.pipe( ofActionCompleted(DepositCollectionAction.DeleteSuccess), 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 new file mode 100644 index 000000000..bb2b81126 --- /dev/null +++ b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.html @@ -0,0 +1,86 @@ +<div class="header"> + <div class="left-part"> + <h1 class="title">{{'deposit.file.title.filesOfDeposit' | translate}}</h1> + <div *ngIf="!(foldersWithIntermediateFoldersObs | async | isNullOrUndefined) && (foldersWithIntermediateFoldersObs | async | isNonEmptyArray)" + class="selector-file-view-mode" + > + <a *ngIf="currentFileViewMode === fileViewModeEnum.FolderView" + (click)="selectFolder(undefined)" + > + <mat-icon class="icon">list</mat-icon> + {{'deposit.file.button.flatView' | translate}} + </a> + <a *ngIf="currentFileViewMode === fileViewModeEnum.FlatView" + (click)="selectFolder('/')" + > + <mat-icon class="icon">folder_open</mat-icon> + {{'deposit.file.button.showTree' | translate}}</a> + </div> + <button *ngIf="(canEditObs | async) && (listDataFileObs | async)?.length > 0" + (click)="selectAndDelete()" + mat-button + type="button" + color="primary" + class="button-select-and-delete" + > + <mat-icon>delete_forever</mat-icon> + {{'deposit.file.button.selectAndDeleteAll' | translate}} + </button> + </div> + <div class="right-part"> + <dlcm-shared-button-refresh [isLoading]="isLoadingDataFileObs | async" + (refresh)="refresh()" + ></dlcm-shared-button-refresh> + </div> +</div> + +<div class="file-viewer" + [solidifyFocusFirstElement]="true" +> + <div class="tree-folder" + *ngIf="currentFileViewMode === fileViewModeEnum.FolderView && (foldersWithIntermediateFoldersObs | async) | isNonEmptyArray" + > + <ng-container *ngIf="(intermediateFoldersObs | async) as intermediateFolders"> + <dlcm-deposit-folder-tree *ngIf="!(intermediateFolders | isNullOrUndefined)" + [foldersWithIntermediateFolders]="foldersWithIntermediateFoldersObs | async" + [intermediateFolders]="intermediateFolders" + [isLoading]="isLoadingDataFileObs | async" + [expandFirstLevel]="true" + [currentFolder]="currentFolderObs | async" + (selectChange)="selectFolder($event)" + (moveChange)="moveDataFile($event)" + (downloadFolder)="downloadFolder($event)" + ></dlcm-deposit-folder-tree> + </ng-container> + </div> + + <div class="file-list-wrapper" + [dlcmSpinner]="isLoadingDataFileObs | async" + > + <dlcm-shared-data-table #dataTablePresentational + *ngIf="columns" + [columns]="columns" + [isLoading]="isLoadingDataFileObs | async" + [datas]="listDataFileObs | async" + [queryParameters]="queryParametersObs | async" + (queryParametersChange)="onQueryParametersEvent($event)" + (queryParametersWithoutRefreshChange)="onQueryParametersEvent($event, false)" + (selectChange)="showDetail($event)" + [columnsSkippedToClear]="columnsToSkippedFilter" + [skipInitialQuery]="true" + [actions]="actions" + [isMultiSelectable]="this.canEditObs | async | isTrue" + [bulkActions]="bulkActions" + [isDraggable]="currentFileViewMode === fileViewModeEnum.FolderView" + [listCdkDropListId]="foldersWithIntermediateFoldersObs | async" + [draggablePreviewColumn]="'fileName'" + [draggablePreviewMatIcon]="'insert_drive_file'" + [preloadRowsForPage]="true" + > + </dlcm-shared-data-table> + </div> + + <ng-template #noFile> + <label>{{'deposit.file.tree.noFile' | translate}}</label> + </ng-template> +</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 new file mode 100644 index 000000000..afa4f5576 --- /dev/null +++ b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.scss @@ -0,0 +1,104 @@ +@import "src/sass/abstracts/variables"; +@import "src/sass/abstracts/mixins"; +@import "src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; + +:host { + padding: 0; + + .header { + display: flex; + justify-content: space-between; + padding-bottom: 10px; + + .left-part, + .right-part { + display: flex; + } + + .left-part { + align-items: center; + + .title { + margin: 0; + } + + .selector-file-view-mode { + display: flex; + padding-left: 20px; + + a { + .icon { + padding-right: 5px; + } + + display: flex; + align-items: center; + } + } + + .button-select-and-delete { + margin-left: 10px; + } + + @include respond-to-smaller-than-breakpoint('sm') { + flex-direction: column; + align-items: self-start; + + .selector-file-view-mode { + padding-left: 0; + } + + .button-select-and-delete { + margin-left: 0; + } + } + } + + .right-part { + .button-refresh { + transition-property: transform; + transition-duration: 1s; + + fa-icon { + font-size: 20px; + } + + &.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-viewer { + display: flex; + + @include respond-to-smaller-than-breakpoint('md') { + flex-wrap: wrap; + .tree-folder { + width: 100%; + padding-bottom: 20px; + } + } + + .tree-folder { + padding-right: 20px; + } + + .file-list-wrapper { + width: 100%; + } + } +} 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 new file mode 100644 index 000000000..630c63289 --- /dev/null +++ b/src/app/features/deposit/components/containers/deposit-file/deposit-file.container.ts @@ -0,0 +1,473 @@ +import {PersonRole} from "@admin/models/person-role.model"; +import { + ChangeDetectorRef, + Component, + OnInit, + ViewChild, +} from "@angular/core"; +import { + MatDialog, + MatDialogRef, +} from "@angular/material/dialog"; +import {ActivatedRoute} from "@angular/router"; +import {AppState} from "@app/stores/app.state"; +import { + DepositFileMoveDialog, + DepositFileMoveDialogData, +} from "@deposit/components/dialogs/deposit-file-move/deposit-file-move.dialog"; +import {DepositDataFileHelper} from "@deposit/helpers/deposit-data-file.helper"; +import {DepositDataFile} from "@deposit/models/deposit-data-file.model"; +import {DepositDataFileAction} from "@deposit/stores/data-file/deposit-data-file.action"; +import {DepositDataFileState} from "@deposit/stores/data-file/deposit-data-file.state"; +import {DepositDataFileStatusHistoryAction} from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.action"; +import {DepositDataFileStatusHistoryState} from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.state"; +import { + DepositAction, + depositActionNameSpace, +} from "@deposit/stores/deposit.action"; +import { + DepositState, + DepositStateModel, +} from "@deposit/stores/deposit.state"; +import {environment} from "@environments/environment"; +import {Deposit} from "@models"; +import { + Actions, + ofActionCompleted, + Select, + Store, +} from "@ngxs/store"; +import { + SharedConfirmDialog, + SharedConfirmDialogData, +} from "@shared/components/dialogs/shared-confirm/shared-confirm.dialog"; +import { + SharedFileDetailDialog, + SharedFileDetailDialogData, +} from "@shared/components/dialogs/shared-file-detail/shared-file-detail.dialog"; +import {SharedHistoryDialog} from "@shared/components/dialogs/shared-history/shared-history.dialog"; +import {SharedPreviewDialogData} from "@shared/components/dialogs/shared-preview/shared-preview-dialog-data.model"; +import {SharedPreviewDialog} from "@shared/components/dialogs/shared-preview/shared-preview.dialog"; +import {SharedDataTablePresentational} from "@shared/components/presentationals/shared-data-table/shared-data-table.presentational"; +import {SharedAbstractDetailEditRoutable} from "@shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; +import {ComplianceLevelEnumHelper} from "@shared/enums/business/compliance-level.enum"; +import { + DataFileStatusEnum, + DataFileStatusEnumHelper, +} from "@shared/enums/business/data-file-status.enum"; +import {DataTableComponentEnum} from "@shared/enums/data-table-component.enum"; +import {FieldTypeEnum} from "@shared/enums/field-type.enum"; +import {FileViewModeEnum} from "@shared/enums/file-view-mode.enum"; +import {LocalStateEnum} from "@shared/enums/local-state.enum"; +import {AppRoutesEnum} from "@shared/enums/routes.enum"; +import {DataTableActions} from "@shared/models/data-table-actions.model"; +import {DataTableBulkActions} from "@shared/models/data-table-bulk-actions.model"; +import {DataTableColumns} from "@shared/models/data-table-columns.model"; +import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; +import {StatusHistory} from "@shared/models/status-history.model"; +import {SecurityService} from "@shared/services/security.service"; +import {Observable} from "rxjs"; +import { + distinctUntilChanged, + take, + tap, +} from "rxjs/operators"; +import { + CompositionState, + isNullOrUndefined, + isTrue, + isUndefined, + MemoizedUtil, + OrderEnum, + QueryParameters, + ResourceActionHelper, + ResourceState, + StringUtil, + TRANSLATE, +} from "solidify-frontend"; + +@Component({ + selector: "dlcm-deposit-file-container", + templateUrl: "./deposit-file.container.html", + styleUrls: ["./deposit-file.container.scss"], +}) +export class DepositFileContainer extends SharedAbstractDetailEditRoutable<Deposit, DepositStateModel> implements OnInit { + isLoadingDataFileObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositDataFileState); + listDataFileObs: Observable<DepositDataFile[]> = CompositionState.list(this._store, DepositDataFileState); + queryParametersObs: Observable<QueryParameters> = CompositionState.queryParameters(this._store, DepositDataFileState); + + foldersObs: Observable<string[]> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.folders); + intermediateFoldersObs: Observable<string[]> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.intermediateFolders); + foldersWithIntermediateFoldersObs: Observable<string[]> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.foldersWithIntermediateFolders); + currentFolderObs: Observable<string> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.currentFolder); + canEditObs: Observable<boolean> = MemoizedUtil.select(this._store, DepositState, state => state.canEdit).pipe( + distinctUntilChanged(), + tap(canEdit => this.actions = [...this.actions]), // Force method computeContext on datatable + ); + + isLoadingHistoryObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositDataFileStatusHistoryState); + dataFileHistoryObs: Observable<StatusHistory[]> = MemoizedUtil.select(this._store, DepositDataFileStatusHistoryState, state => state.history); + queryParametersHistoryObs: Observable<QueryParameters> = MemoizedUtil.select(this._store, DepositDataFileStatusHistoryState, state => state.queryParameters); + + @Select(AppState.currentOrgUnitPerson) currentPersonObs: Observable<PersonRole>; + + @ViewChild("dataTablePresentational") + readonly dataTablePresentational: SharedDataTablePresentational<DepositDataFile>; + + currentFileViewMode: FileViewModeEnum = FileViewModeEnum.FolderView; + + columns: DataTableColumns<DepositDataFile>[]; + + actions: DataTableActions<DepositDataFile>[] = [ + { + logo: "check_circle", + callback: (depositDataFile: DepositDataFile) => this.validate(this._resId, depositDataFile), + placeholder: TRANSLATE("crud.list.action.validate"), + displayOnCondition: (depositDataFile: DepositDataFile) => depositDataFile.status === DataFileStatusEnum.IGNORED_FILE, + }, + { + logo: "file_download", + callback: (depositDataFile: DepositDataFile) => this.downloadDataFile(this._resId, depositDataFile), + placeholder: TRANSLATE("crud.list.action.download"), + displayOnCondition: (depositDataFile: DepositDataFile) => true, + }, + { + logo: "play_circle_filled", + callback: (depositDataFile: DepositDataFile) => this.resumeDataFile(this._resId, depositDataFile), + placeholder: TRANSLATE("deposit.file.button.resume"), + displayOnCondition: (depositDataFile: DepositDataFile) => depositDataFile.status === DataFileStatusEnum.IN_ERROR, + }, + { + logo: "delete", + callback: (depositDataFile: DepositDataFile) => this.deleteDataFile(this._resId, depositDataFile), + placeholder: TRANSLATE("crud.list.action.delete"), + displayOnCondition: (depositDataFile: DepositDataFile) => MemoizedUtil.selectSnapshot(this._store, DepositState, state => state.canEdit), + }, + { + logo: "remove_red_eye", + callback: (depositDataFile: DepositDataFile) => this.showPreview(depositDataFile), + placeholder: TRANSLATE("deposit.file.button.showPreview"), + }, + { + logo: "redo", + callback: (depositDataFile: DepositDataFile) => this.moveDataFileWithDialog(depositDataFile), + placeholder: TRANSLATE("crud.list.action.move"), + displayOnCondition: (depositDataFile: DepositDataFile) => MemoizedUtil.selectSnapshot(this._store, DepositState, state => state.canEdit), + }, + { + logo: "history", + callback: (depositDataFile: DepositDataFile) => this.historyDataFile(this._resId, depositDataFile), + placeholder: TRANSLATE("crud.list.action.history"), + }, + ]; + + bulkActions: DataTableBulkActions[] = [ + { + logo: "delete", + callback: (listId: string[]) => this.bulkDeleteDataFile(this._resId, listId), + label: TRANSLATE("crud.list.bulkAction.deleteSelection"), + displayOnCondition: (listId: string[]) => this.canEditObs, + }, + ]; + + columnsToSkippedFilter: keyof DepositDataFile[] | string[] = [ + DepositDataFileHelper.RELATIVE_LOCATION, + "status", + ]; + + dialogDataFileRef: MatDialogRef<SharedFileDetailDialog<DepositDataFile>>; + + get fileViewModeEnum(): typeof FileViewModeEnum { + return FileViewModeEnum; + } + + readonly KEY_PARAM_NAME: keyof Deposit & string = undefined; + + constructor(protected readonly _store: Store, + protected readonly _route: ActivatedRoute, + protected readonly _actions$: Actions, + protected readonly _changeDetector: ChangeDetectorRef, + protected readonly _dialog: MatDialog, + protected readonly _securityService: SecurityService) { + super(_store, _route, _actions$, _changeDetector, _dialog, LocalStateEnum.deposit, depositActionNameSpace); + } + + ngOnInit(): void { + super.ngOnInit(); + this.getCurrentModelOnParent(); + + 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: false, + 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: StringUtil.stringEmpty, + type: FieldTypeEnum.singleSelect, + order: OrderEnum.none, + sortableField: "dataFile.status" as any, + filterableField: "dataFile.status" as any, + isSortable: false, + isFilterable: false, + translate: false, + component: DataTableComponentEnum.dataFileQuickStatus, + width: "35px", + // filterEnum: ComplianceLevelEnumHelper.getListKeyValue(), + }, + { + 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, + tooltip: (value) => DataFileStatusEnumHelper.getExplanation(value as DataFileStatusEnum), + filterEnum: DataFileStatusEnumHelper.getListKeyValue(), + }, + { + 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, + component: DataTableComponentEnum.conformityLevelStar, + filterEnum: ComplianceLevelEnumHelper.getListKeyValue(), + }, + ]; + } + + private getCurrentModelOnParent(): void { + this._resId = this._route.snapshot.parent.paramMap.get(AppRoutesEnum.paramIdWithoutPrefixParam); + this.getDepositById(this._resId); + } + + private getDepositById(id: string): void { + const depositInState = ResourceState.currentSnapshot(this._store, DepositState); + if (isNullOrUndefined(depositInState) || id !== depositInState.resId) { + this._store.dispatch(ResourceActionHelper.getById(depositActionNameSpace, id, true)); + } + this.getSubResourceWithParentId(id); + } + + getSubResourceWithParentId(id: string): void { + this._store.dispatch(new DepositDataFileAction.GetAll(id, undefined, true)); + this._store.dispatch(new DepositDataFileAction.GetListFolder(id)); + } + + onQueryParametersEvent(queryParameters: QueryParameters, withRefresh: boolean = true): void { + this._store.dispatch(new DepositDataFileAction.ChangeQueryParameters(this._resId, queryParameters, true, withRefresh)); + // this._changeDetector.detectChanges(); // Allow to display spinner the first time + } + + refresh(): void { + this._store.dispatch(new DepositDataFileAction.Refresh(this._resId)); + } + + download($event: DepositDataFile): void { + this._store.dispatch(new DepositDataFileAction.Download(this._resId, $event)); + } + + selectFolder(folderFullName: string | undefined): void { + if (isUndefined(folderFullName)) { + this.currentFileViewMode = FileViewModeEnum.FlatView; + } else { + this.currentFileViewMode = FileViewModeEnum.FolderView; + } + this._store.dispatch(new DepositDataFileAction.ChangeCurrentFolder(folderFullName, true)); + } + + downloadFolder(folderName: string): void { + this._store.dispatch(new DepositAction.Download(this._resId, folderName)); + } + + showDetail(depositDataFile: DepositDataFile): void { + this.dialogDataFileRef = this._dialog.open(SharedFileDetailDialog, { + data: { + dataFile: depositDataFile, + resId: depositDataFile.resId, + parentId: this._resId, + isReadonly: !this.isEdit, + isLoading: this.isLoadingHistoryObs, + statusHistory: this.dataFileHistoryObs, + queryParametersObs: this.queryParametersHistoryObs, + state: DepositDataFileStatusHistoryAction, + name: LocalStateEnum.deposit, + buttons: [ + { + labelToTranslate: TRANSLATE("deposit.file.button.validate"), + color: "primary", + icon: "check_circle", + callback: current => this.validate(this._resId, current), + displayCondition: current => current.status === DataFileStatusEnum.IGNORED_FILE, + order: 0, + }, + { + labelToTranslate: TRANSLATE("deposit.file.button.download"), + color: "primary", + icon: "file_download", + callback: current => this.downloadDataFile(this._resId, current), + order: 1, + }, + { + labelToTranslate: TRANSLATE("deposit.file.button.delete"), + color: "primary", + icon: "delete", + callback: (current) => this.deleteDataFile(this._resId, current), + displayCondition: current => this.canEditObs, + order: 2, + }, + { + labelToTranslate: TRANSLATE("deposit.file.button.resume"), + color: "primary", + icon: "play_circle_filled", + callback: current => this.resumeDataFile(this._resId, current), + displayCondition: current => current.status === DataFileStatusEnum.IN_ERROR, + order: 3, + }, + { + labelToTranslate: TRANSLATE("deposit.file.button.move"), + color: "primary", + icon: "redo", + callback: current => this.moveDataFileWithDialog(current), + displayCondition: current => this.canEditObs, + order: 4, + }, + ], + } as SharedFileDetailDialogData<DepositDataFile>, + width: environment.modalWidth, + }); + } + + bulkDeleteDataFile(parentId: string, listId: string[]): void { + this._store.dispatch(new DepositDataFileAction.DeleteAll(parentId, listId)); + } + + deleteDataFile(parentId: string, dataFile: DepositDataFile): void { + this._store.dispatch(new DepositDataFileAction.Delete(parentId, dataFile)); + this.subscribe(this._actions$.pipe( + ofActionCompleted(DepositDataFileAction.DeleteSuccess), + tap(result => { + if (!result.result.successful) { + return; + } + if (this.dialogDataFileRef) { + this.dialogDataFileRef.close(); + } + }), + )); + } + + moveDataFileWithDialog(dataFile: DepositDataFile): void { + this._dialog.open(DepositFileMoveDialog, { + data: { + parentResId: this._resId, + depositDataFile: dataFile, + } as DepositFileMoveDialogData, + }); + if (!isNullOrUndefined(this.dialogDataFileRef)) { + this.subscribe(this._actions$.pipe( + ofActionCompleted(DepositDataFileAction.MoveSuccess), + take(1), + tap((result) => { + if (isTrue(result.result.successful)) { + this.dialogDataFileRef.close(); + } + }), + )); + } + } + + showPreview(dataFile: DepositDataFile): void { + const previewDialogRef = this._dialog.open(SharedPreviewDialog, { + width: "min-content", + height: "min-content", + data: { + fileInput: { + dataFile: dataFile, + }, + + } as SharedPreviewDialogData, + }); + } + + moveDataFile(dataFile: DepositDataFile): void { + this._store.dispatch(new DepositDataFileAction.Move(this._resId, { + resId: dataFile.resId, + relativeLocation: dataFile.relativeLocation, + })); + } + + validate(parentId: string, dataFile: DepositDataFile): void { + this._store.dispatch(new DepositDataFileAction.Validate(parentId, dataFile)); + } + + downloadDataFile(parentId: string, dataFile: DepositDataFile): void { + this._store.dispatch(new DepositDataFileAction.Download(parentId, dataFile)); + } + + resumeDataFile(parentId: string, dataFile: DepositDataFile): void { + this._store.dispatch(new DepositDataFileAction.ResumeDataFile(parentId, dataFile)); + } + + historyDataFile(parentId: string, dataFile: DepositDataFile): void { + const dialogRef = this._dialog.open(SharedHistoryDialog, { + width: environment.modalWidth, + data: { + parentId: parentId, + resourceResId: dataFile.resId, + name: LocalStateEnum.deposit, + statusHistory: this.dataFileHistoryObs, + isLoading: this.isLoadingHistoryObs, + queryParametersObs: this.queryParametersHistoryObs, + state: DepositDataFileStatusHistoryAction, + } as StatusHistoryDialog, + }); + } + + selectAndDelete(): void { + this.subscribe(this._dialog.open(SharedConfirmDialog, { + data: { + titleToTranslate: TRANSLATE("deposit.file.bulkDelete.title"), + messageToTranslate: TRANSLATE("deposit.file.bulkDelete.message"), + confirmButtonToTranslate: TRANSLATE("deposit.file.bulkDelete.button.confirm"), + cancelButtonToTranslate: TRANSLATE("deposit.file.bulkDelete.button.cancel"), + } as SharedConfirmDialogData, + }).afterClosed().pipe( + tap((isConfirmed: boolean | undefined) => { + if (isNullOrUndefined(isConfirmed)) { + return; + } + if (isTrue(isConfirmed)) { + this.subscribe(this.dataTablePresentational.selectAllResult().pipe( + take(1), + tap(selection => { + this.bulkDeleteDataFile(this._resId, selection); + this.dataTablePresentational.cleanSelection(); + }), + )); + } + }), + )); + } +} diff --git a/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.html b/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.html new file mode 100644 index 000000000..54f976e7d --- /dev/null +++ b/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.html @@ -0,0 +1,65 @@ +<div class="upload-button-wrapper"> + <ng-template [ngIf]="mode === modeDepositTabEnum.UNDEFINED || mode === modeDepositTabEnum.FILE"> + <button dlcmAlternativeButton + solidifyShortCuts + (onEnter)="openModalUpload(dataCategoryEnum.Primary)" + (click)="openModalUpload(dataCategoryEnum.Primary)" + > + <mat-icon>publish</mat-icon> + {{'deposit.file.button.uploadPrimaryData' | translate}} + </button> + </ng-template> + + <ng-template [ngIf]="mode === modeDepositTabEnum.FILE"> + <button mat-button + color="primary" + type="button" + solidifyShortCuts + (onEnter)="openModalUpload(dataCategoryEnum.Secondary)" + (click)="openModalUpload(dataCategoryEnum.Secondary)" + > + <mat-icon>publish</mat-icon> + {{'deposit.file.button.uploadSecondaryData' | translate}} + </button> + + <button mat-button + color="primary" + type="button" + [matTooltip]="'deposit.file.button.uploadArchive.tooltip' | translate" + solidifyShortCuts + (onEnter)="openModalUploadArchive()" + (click)="openModalUploadArchive()" + > + <mat-icon>unarchive</mat-icon> + {{'deposit.file.button.uploadArchive.label' | translate}} + </button> + + <a solidifyShortCuts + (onEnter)="openModalUpload()" + (click)="openModalUpload()" + > + {{'deposit.file.button.uploadAdvanced' | translate}} + </a> + + </ng-template> + + <button *ngIf="mode !== modeDepositTabEnum.FILE && (uploadStatusObs | async).length === 0" + dlcmAlternativeButton + solidifyShortCuts + (click)="openModalAip()" + (onEnter)="openModalAip()" + [matTooltip]="'deposit.collection.button.addAip.tooltip' | translate" + > + <mat-icon>link</mat-icon> + {{'deposit.collection.button.associateArchive' | translate}} + </button> +</div> + +<dlcm-deposit-files-upload-in-progress *ngIf="mode === modeDepositTabEnum.FILE || (uploadStatusObs | async).length > 0" + class="deposit-files-upload-in-progress" + [listFilesUploading]="uploadStatusObs | async" + (cancelChange)="cancel($event)" + (retryChange)="retry($event)" + (uploadChange)="upload($event)" + (uploadArchiveChange)="uploadArchive($event)" +></dlcm-deposit-files-upload-in-progress> diff --git a/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.scss b/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.scss new file mode 100644 index 000000000..bbbfb7f7e --- /dev/null +++ b/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.scss @@ -0,0 +1,24 @@ +@import "src/sass/abstracts/variables"; +@import "src/sass/abstracts/mixins"; +@import "src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; + +:host { + padding: 0; + + .upload-button-wrapper { + display: flex; + flex-wrap: wrap; + padding-bottom: 25px; + justify-content: center; + align-items: center; + + > * { + margin: 5px 10px !important; + } + } + + .deposit-files-upload-in-progress { + display: block; + padding-bottom: 20px; + } +} diff --git a/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.ts b/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.ts similarity index 68% rename from src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.ts rename to src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.ts index 728b2c89e..2d207b7a2 100644 --- a/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.ts +++ b/src/app/features/deposit/components/containers/deposit-upload/deposit-upload.container.ts @@ -2,18 +2,29 @@ import {PersonRole} from "@admin/models/person-role.model"; import { ChangeDetectorRef, Component, + Input, OnInit, ViewChild, } from "@angular/core"; import {MatDialog} from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; import {AppState} from "@app/stores/app.state"; +import { + DepositAipUploadDialog, + DepositAipUploadDialogData, +} from "@deposit/components/dialogs/deposit-aip-upload/deposit-aip-upload.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 { + DepositFileUploadDialog, + DepositFileUploadDialogData, +} from "@deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog"; import {DepositFormPresentational} from "@deposit/components/presentationals/deposit-form/deposit-form.presentational"; +import {ModeDepositTabEnum} from "@deposit/enums/mode-deposit-tab.enum"; import {DepositDataFile} from "@deposit/models/deposit-data-file.model"; import {FileUploadWrapper} from "@deposit/models/file-upload-wrapper.model"; import {UploadFileStatus} from "@deposit/models/upload-file-status.model"; +import {DepositCollectionAction} from "@deposit/stores/collection/deposit-collection.action"; +import {DepositCollectionState} from "@deposit/stores/collection/deposit-collection.state"; import {DepositDataFileAction} from "@deposit/stores/data-file/deposit-data-file.action"; import {DepositDataFileState} from "@deposit/stores/data-file/deposit-data-file.state"; import { @@ -27,10 +38,12 @@ import { import {Deposit} from "@models"; import { Actions, + ofActionCompleted, Select, Store, } from "@ngxs/store"; import {SharedAbstractDetailEditRoutable} from "@shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; +import {DataCategoryEnum} from "@shared/enums/data-category.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; import {AppRoutesEnum} from "@shared/enums/routes.enum"; import {SecurityService} from "@shared/services/security.service"; @@ -40,6 +53,7 @@ import { tap, } from "rxjs/operators"; import { + AssociationRemoteState, CompositionState, isNonEmptyArray, isNullOrUndefined, @@ -48,11 +62,11 @@ import { } from "solidify-frontend"; @Component({ - selector: "dlcm-deposit-file-routable", - templateUrl: "./deposit-upload.routable.html", - styleUrls: ["./deposit-upload.routable.scss"], + selector: "dlcm-deposit-upload-container", + templateUrl: "./deposit-upload.container.html", + styleUrls: ["./deposit-upload.container.scss"], }) -export class DepositUploadRoutable extends SharedAbstractDetailEditRoutable<Deposit, DepositStateModel> implements OnInit { +export class DepositUploadContainer extends SharedAbstractDetailEditRoutable<Deposit, DepositStateModel> implements OnInit { queryParametersObs: Observable<QueryParameters> = CompositionState.queryParameters(this._store, DepositDataFileState); uploadStatusObs: Observable<UploadFileStatus[]> = MemoizedUtil.select(this._store, DepositState, state => state.uploadStatus); @@ -63,6 +77,17 @@ export class DepositUploadRoutable extends SharedAbstractDetailEditRoutable<Depo readonly KEY_PARAM_NAME: keyof Deposit & string = undefined; + get dataCategoryEnum(): typeof DataCategoryEnum { + return DataCategoryEnum; + } + + get modeDepositTabEnum(): typeof ModeDepositTabEnum { + return ModeDepositTabEnum; + } + + @Input() + mode: ModeDepositTabEnum; + constructor(protected readonly _store: Store, protected readonly _route: ActivatedRoute, protected readonly _actions$: Actions, @@ -112,9 +137,12 @@ export class DepositUploadRoutable extends SharedAbstractDetailEditRoutable<Depo this._store.dispatch(new DepositDataFileAction.Download(this._resId, $event)); } - openModalUpload(): void { + openModalUpload(dataCategoryEnum?: DataCategoryEnum): void { const dialogRef = this._dialog.open(DepositFileUploadDialog, { width: "500px", + data: { + dataCategoryEnum: dataCategoryEnum, + } as DepositFileUploadDialogData, }); this.subscribe(dialogRef.afterClosed().pipe( filter((listFilesUploadWrapper: FileUploadWrapper[]) => isNonEmptyArray(listFilesUploadWrapper)), @@ -135,4 +163,30 @@ export class DepositUploadRoutable extends SharedAbstractDetailEditRoutable<Depo }), )); } + + openModalAip(): void { + const selected = AssociationRemoteState.selectedSnapshot(this._store, DepositCollectionState); + const dialogRef = this._dialog.open(DepositAipUploadDialog, { + width: "90%", + data: { + listIdAipAlreadyLinked: selected.map(aip => aip.resId), + } as DepositAipUploadDialogData, + }); + this.subscribe(dialogRef.afterClosed().pipe( + filter((listAipIds: string[]) => isNonEmptyArray(listAipIds)), + tap(((listAipIds: string[]) => { + this.addAip(listAipIds); + }), + ))); + } + + addAip(aipIds: string[]): void { + this._store.dispatch(new DepositCollectionAction.Create(this._resId, aipIds)); + this.subscribe(this._actions$.pipe( + ofActionCompleted(DepositCollectionAction.CreateSuccess), + tap(() => { + this._store.dispatch(new DepositCollectionAction.Refresh(this._resId)); + }), + )); + } } diff --git a/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.html b/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.html index 132bd0e3b..13613e84e 100644 --- a/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.html +++ b/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.html @@ -10,7 +10,9 @@ <mat-form-field *ngIf="getFormControl(formDefinition.dataCategory) as fd"> <mat-label>{{'deposit.file.modal.form.dataCategory' | translate }}</mat-label> - <mat-select [formControl]="fd"> + <mat-select [formControl]="fd" + [disabled]="data?.dataCategoryEnum | isNotNullNorUndefined" + > <mat-option *ngFor="let dataCategory of dataCategoryHelper.getListDataCategory()" [value]="dataCategory" > diff --git a/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.ts b/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.ts index 481e79241..a0d15183b 100644 --- a/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.ts +++ b/src/app/features/deposit/components/dialogs/deposit-file-upload/deposit-file-upload.dialog.ts @@ -1,17 +1,23 @@ import { ChangeDetectionStrategy, Component, + Inject, OnInit, } from "@angular/core"; import {FormBuilder} from "@angular/forms"; -import {MatDialogRef} from "@angular/material/dialog"; +import { + MAT_DIALOG_DATA, + MatDialogRef, +} from "@angular/material/dialog"; import {FileUploadWrapper} from "@app/features/deposit/models/file-upload-wrapper.model"; import {DataCategoryHelper} from "@app/shared/helpers/data-category.helper"; import { AbstractDepositFileUploadDialog, AbstractDepositFileUploadFormComponentFormDefinition, } from "@deposit/components/dialogs/abstract-deposit-file-upload/abstract-deposit-file-upload.dialog"; +import {DataCategoryEnum} from "@shared/enums/data-category.enum"; import { + isNotNullNorUndefined, isNullOrUndefined, PropertyName, } from "solidify-frontend"; @@ -31,13 +37,19 @@ export class DepositFileUploadDialog extends AbstractDepositFileUploadDialog imp } constructor(protected _dialogRef: MatDialogRef<DepositFileUploadDialog>, - protected _fb: FormBuilder) { + protected _fb: FormBuilder, + @Inject(MAT_DIALOG_DATA) public data: DepositFileUploadDialogData) { super(_dialogRef, _fb); } ngOnInit(): void { super.ngOnInit(); this.form.addControl(this.formDefinition.subDirectory, this._fb.control("")); + + if (isNotNullNorUndefined(this.data.dataCategoryEnum)) { + const dataCategoryFormControl = this.form.get(this.formDefinition.dataCategory); + dataCategoryFormControl.setValue(this.data.dataCategoryEnum); + } } onFileChange(event: Event & any): void { @@ -108,3 +120,7 @@ class FileWrapper { file: File; isInError: boolean; } + +export interface DepositFileUploadDialogData { + dataCategoryEnum?: DataCategoryEnum; +} \ No newline at end of file diff --git a/src/app/features/deposit/components/routables/deposit-detail-edit/deposit-detail-edit.routable.ts b/src/app/features/deposit/components/routables/deposit-detail-edit/deposit-detail-edit.routable.ts index f0a47da16..1c48c6a84 100644 --- a/src/app/features/deposit/components/routables/deposit-detail-edit/deposit-detail-edit.routable.ts +++ b/src/app/features/deposit/components/routables/deposit-detail-edit/deposit-detail-edit.routable.ts @@ -23,7 +23,7 @@ import { import {LocalStateEnum} from "@app/shared/enums/local-state.enum"; import {AppState} from "@app/stores/app.state"; import {AppOrgUnitPersonRoleAction} from "@app/stores/organizational-unit-person-role/app-organizational-unit-person-role.action"; -import {DepositFormPresentational} from "@deposit/components/presentationals/deposit-form/deposit-form.presentational"; +import {ModeDepositTabEnum} from "@deposit/enums/mode-deposit-tab.enum"; import {DepositHelper} from "@deposit/helpers/deposit.helper"; import {DepositService} from "@deposit/services/deposit.service"; import {DepositCollectionAction} from "@deposit/stores/collection/deposit-collection.action"; @@ -67,7 +67,6 @@ import { distinctUntilChanged, filter, flatMap, - map, take, } from "rxjs/operators"; import { @@ -91,15 +90,15 @@ export class DepositDetailEditRoutable extends SharedAbstractDetailEditRoutable< isLoadingHistoryObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositStatusHistoryState); isLoadingObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositState); queryParametersObs: Observable<QueryParameters> = MemoizedUtil.select(this._store, DepositStatusHistoryState, state => state.queryParameters); - isLoadingFileObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositDataFileState); numberFilesNotReady: Observable<number | undefined> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.numberFilesNotReady); numberFilesObs: Observable<number | undefined> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.numberFiles); - isLoadingCollectionObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositCollectionState); numberCollectionObs: Observable<number | undefined> = MemoizedUtil.select(this._store, DepositCollectionState, state => state.numberCollections); @Select(AppState.currentOrgUnitPersonRoleResId) currentPersonRoleObs: Observable<string[]>; @Select(AppState.currentOrgUnitPerson) currentPersonObs: Observable<PersonRole>; @Select(AppState.currentUserApplicationRolesResId) currentUserApplicationRoleObs: Observable<string[]>; + depositModeTabEnumObs: Observable<ModeDepositTabEnum> = MemoizedUtil.select(this._store, DepositState, state => state.depositModeTabEnum); + @ViewChild("matTabGroup") readonly matTabGroup: MatTabGroup; @@ -109,71 +108,18 @@ export class DepositDetailEditRoutable extends SharedAbstractDetailEditRoutable< listTabs: Tab[] = [ { - id: "METADATA", + id: TabEnum.METADATA, suffixUrl: DepositRoutesEnum.metadata, icon: "assignment", titleToTranslate: TRANSLATE("deposit.tab.details"), route: () => [...this.rootUrl, DepositRoutesEnum.metadata], }, { - id: "FILES", - suffixUrl: DepositRoutesEnum.files, + id: TabEnum.FILES, + suffixUrl: DepositRoutesEnum.data, icon: "insert_drive_file", - titleToTranslate: TRANSLATE("deposit.tab.datafiles"), - route: () => [...this.rootUrl, DepositRoutesEnum.files], - conditionDisplay: () => combineLatest([this.isLoadingFileObs, this.isLoadingCollectionObs, this.numberFilesObs, this.numberCollectionObs]) - .pipe( - filter(([isLoadingFile, isLoadingCollection, numberFile, numberCollection]) => - this.current && this.current.status === Deposit.StatusEnum.INPROGRESS ? !isNullOrUndefined(numberCollection) - : !isNullOrUndefined(numberFile)), - map(([isLoadingFile, isLoadingCollection, numberFile, numberCollection]) => { - if (!isNullOrUndefined(this.current) && this.current.status === Deposit.StatusEnum.INPROGRESS) { - return (isNullOrUndefined(numberCollection) && !isLoadingCollection) || numberCollection === 0; - } - return (isNullOrUndefined(numberFile) && !isLoadingFile) || numberFile > 0; - }), - ), - }, - { - id: "COLLECTION", - suffixUrl: DepositRoutesEnum.collection, - icon: "collections_bookmark", - titleToTranslate: TRANSLATE("deposit.tab.collection"), - route: () => [...this.rootUrl, DepositRoutesEnum.collection], - conditionDisplay: () => combineLatest([this.isLoadingFileObs, this.isLoadingCollectionObs, this.numberFilesObs, this.numberCollectionObs]) - .pipe( - filter(([isLoadingFile, isLoadingCollection, numberFile, numberCollection]) => - this.current && this.current.status === Deposit.StatusEnum.INPROGRESS ? !isNullOrUndefined(numberFile) - : !isNullOrUndefined(numberCollection)), - map(([isLoadingFile, isLoadingCollection, numberFile, numberCollection]) => { - if (!isNullOrUndefined(this.current) && this.current.status === Deposit.StatusEnum.INPROGRESS) { - return (isNullOrUndefined(numberFile) && !isLoadingFile) || numberFile === 0; - } - return (isNullOrUndefined(numberCollection) && !isLoadingCollection) || numberCollection > 0; - }), - ), - }, - { - id: "UPLOAD", - suffixUrl: DepositRoutesEnum.upload, - icon: "publish", - titleToTranslate: TRANSLATE("deposit.tab.upload"), - route: () => [...this.rootUrl, DepositRoutesEnum.upload], - conditionDisplay: () => combineLatest([this.canDoAlterationActionsBS.asObservable(), this.isLoadingFileObs, this.isLoadingCollectionObs, this.numberFilesObs, this.numberCollectionObs]) - .pipe( - distinctUntilChanged(), - filter(([isLoadingFile, isLoadingCollection, numberFile, numberCollection]) => - this.current && this.current.status === Deposit.StatusEnum.INPROGRESS ? !isNullOrUndefined(numberCollection) : true), - map(([canDoAlteration, isLoadingFile, isLoadingCollection, numberFile, numberCollection]) => { - if (isFalse(canDoAlteration)) { - return false; - } - if (!isNullOrUndefined(this.current) && this.current.status === Deposit.StatusEnum.INPROGRESS) { - return (isNullOrUndefined(numberCollection) && !isLoadingCollection) || numberCollection === 0; - } - return false; - }), - ), + titleToTranslate: TRANSLATE("deposit.tab.data"), + route: () => [...this.rootUrl, DepositRoutesEnum.data], }, ]; @@ -273,6 +219,21 @@ export class DepositDetailEditRoutable extends SharedAbstractDetailEditRoutable< this.retrieveCurrentModelWithUrl(); this._computeCurrentUserRight(); this._computeEditableField(); + + this.subscribe(this.depositModeTabEnumObs.pipe(distinctUntilChanged()), mode => { + const tab = this.listTabs.find(t => t.id === TabEnum.FILES); + if (mode === ModeDepositTabEnum.COLLECTION) { + tab.icon = "collections_bookmark"; + tab.titleToTranslate = TRANSLATE("deposit.tab.collection"); + } else if (mode === ModeDepositTabEnum.FILE) { + tab.icon = "insert_drive_file"; + tab.titleToTranslate = TRANSLATE("deposit.tab.datafiles"); + } else { + tab.icon = "insert_drive_file"; + tab.titleToTranslate = TRANSLATE("deposit.tab.data"); + } + this.listTabs = [...this.listTabs]; + }); } ngOnDestroy(): void { @@ -424,9 +385,8 @@ export class DepositDetailEditRoutable extends SharedAbstractDetailEditRoutable< getSubResourceWithParentId(id: string): void { this._store.dispatch(new DepositPersonAction.GetAll(id)); - this._store.dispatch(new DepositDataFileAction.GetNumberFilesNotReady(id)); - this._store.dispatch(new DepositDataFileAction.GetNumberFiles(id)); - this._store.dispatch(new DepositCollectionAction.GetNumberCollections(id)); + this._store.dispatch(new DepositDataFileAction.Refresh(id)); + this._store.dispatch(new DepositCollectionAction.Refresh(id)); } submit(): void { @@ -498,3 +458,8 @@ export class DepositDetailEditRoutable extends SharedAbstractDetailEditRoutable< this._store.dispatch(new DepositAction.Download(this._resId)); } } + +enum TabEnum { + METADATA = "METADATA", + FILES = "FILES", +} diff --git a/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.html b/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.html index bb2b81126..1eeea4bf1 100644 --- a/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.html +++ b/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.html @@ -1,86 +1,5 @@ -<div class="header"> - <div class="left-part"> - <h1 class="title">{{'deposit.file.title.filesOfDeposit' | translate}}</h1> - <div *ngIf="!(foldersWithIntermediateFoldersObs | async | isNullOrUndefined) && (foldersWithIntermediateFoldersObs | async | isNonEmptyArray)" - class="selector-file-view-mode" - > - <a *ngIf="currentFileViewMode === fileViewModeEnum.FolderView" - (click)="selectFolder(undefined)" - > - <mat-icon class="icon">list</mat-icon> - {{'deposit.file.button.flatView' | translate}} - </a> - <a *ngIf="currentFileViewMode === fileViewModeEnum.FlatView" - (click)="selectFolder('/')" - > - <mat-icon class="icon">folder_open</mat-icon> - {{'deposit.file.button.showTree' | translate}}</a> - </div> - <button *ngIf="(canEditObs | async) && (listDataFileObs | async)?.length > 0" - (click)="selectAndDelete()" - mat-button - type="button" - color="primary" - class="button-select-and-delete" - > - <mat-icon>delete_forever</mat-icon> - {{'deposit.file.button.selectAndDeleteAll' | translate}} - </button> - </div> - <div class="right-part"> - <dlcm-shared-button-refresh [isLoading]="isLoadingDataFileObs | async" - (refresh)="refresh()" - ></dlcm-shared-button-refresh> - </div> -</div> - -<div class="file-viewer" - [solidifyFocusFirstElement]="true" -> - <div class="tree-folder" - *ngIf="currentFileViewMode === fileViewModeEnum.FolderView && (foldersWithIntermediateFoldersObs | async) | isNonEmptyArray" - > - <ng-container *ngIf="(intermediateFoldersObs | async) as intermediateFolders"> - <dlcm-deposit-folder-tree *ngIf="!(intermediateFolders | isNullOrUndefined)" - [foldersWithIntermediateFolders]="foldersWithIntermediateFoldersObs | async" - [intermediateFolders]="intermediateFolders" - [isLoading]="isLoadingDataFileObs | async" - [expandFirstLevel]="true" - [currentFolder]="currentFolderObs | async" - (selectChange)="selectFolder($event)" - (moveChange)="moveDataFile($event)" - (downloadFolder)="downloadFolder($event)" - ></dlcm-deposit-folder-tree> - </ng-container> - </div> - - <div class="file-list-wrapper" - [dlcmSpinner]="isLoadingDataFileObs | async" - > - <dlcm-shared-data-table #dataTablePresentational - *ngIf="columns" - [columns]="columns" - [isLoading]="isLoadingDataFileObs | async" - [datas]="listDataFileObs | async" - [queryParameters]="queryParametersObs | async" - (queryParametersChange)="onQueryParametersEvent($event)" - (queryParametersWithoutRefreshChange)="onQueryParametersEvent($event, false)" - (selectChange)="showDetail($event)" - [columnsSkippedToClear]="columnsToSkippedFilter" - [skipInitialQuery]="true" - [actions]="actions" - [isMultiSelectable]="this.canEditObs | async | isTrue" - [bulkActions]="bulkActions" - [isDraggable]="currentFileViewMode === fileViewModeEnum.FolderView" - [listCdkDropListId]="foldersWithIntermediateFoldersObs | async" - [draggablePreviewColumn]="'fileName'" - [draggablePreviewMatIcon]="'insert_drive_file'" - [preloadRowsForPage]="true" - > - </dlcm-shared-data-table> - </div> - - <ng-template #noFile> - <label>{{'deposit.file.tree.noFile' | translate}}</label> - </ng-template> -</div> +<dlcm-deposit-upload-container *ngIf="(canEditObs | async) && (currentObs | async)?.status === depositStatusEnum.INPROGRESS" + [mode]="depositModeTabEnumObs | async" +></dlcm-deposit-upload-container> +<dlcm-deposit-file-container *ngIf="(depositModeTabEnumObs | async) === modeDepositTabEnum.FILE"></dlcm-deposit-file-container> +<dlcm-deposit-collection-container *ngIf="(depositModeTabEnumObs | async) === modeDepositTabEnum.COLLECTION"></dlcm-deposit-collection-container> diff --git a/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.scss b/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.scss index be71debf6..afa4f5576 100644 --- a/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.scss +++ b/src/app/features/deposit/components/routables/deposit-file/deposit-file.routable.scss @@ -1,6 +1,6 @@ -@import "../../../../../../sass/abstracts/variables"; -@import "../../../../../../sass/abstracts/mixins"; -@import "../../../../../shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; +@import "src/sass/abstracts/variables"; +@import "src/sass/abstracts/mixins"; +@import "src/app/shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; :host { padding: 0; 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 a6cc76f66..408f6d74b 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 @@ -3,88 +3,27 @@ import { ChangeDetectorRef, Component, OnInit, - ViewChild, } from "@angular/core"; -import { - MatDialog, - MatDialogRef, -} from "@angular/material/dialog"; +import {MatDialog} from "@angular/material/dialog"; import {ActivatedRoute} from "@angular/router"; import {AppState} from "@app/stores/app.state"; -import { - DepositFileMoveDialog, - DepositFileMoveDialogData, -} from "@deposit/components/dialogs/deposit-file-move/deposit-file-move.dialog"; -import {DepositDataFileHelper} from "@deposit/helpers/deposit-data-file.helper"; -import {DepositDataFile} from "@deposit/models/deposit-data-file.model"; -import {DepositDataFileAction} from "@deposit/stores/data-file/deposit-data-file.action"; -import {DepositDataFileState} from "@deposit/stores/data-file/deposit-data-file.state"; -import {DepositDataFileStatusHistoryAction} from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.action"; -import {DepositDataFileStatusHistoryState} from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.state"; -import { - DepositAction, - depositActionNameSpace, -} from "@deposit/stores/deposit.action"; +import {ModeDepositTabEnum} from "@deposit/enums/mode-deposit-tab.enum"; +import {depositActionNameSpace} from "@deposit/stores/deposit.action"; import { DepositState, DepositStateModel, } from "@deposit/stores/deposit.state"; -import {environment} from "@environments/environment"; import {Deposit} from "@models"; import { Actions, - ofActionCompleted, Select, Store, } from "@ngxs/store"; -import { - SharedConfirmDialog, - SharedConfirmDialogData, -} from "@shared/components/dialogs/shared-confirm/shared-confirm.dialog"; -import { - SharedFileDetailDialog, - SharedFileDetailDialogData, -} from "@shared/components/dialogs/shared-file-detail/shared-file-detail.dialog"; -import {SharedHistoryDialog} from "@shared/components/dialogs/shared-history/shared-history.dialog"; -import {SharedPreviewDialogData} from "@shared/components/dialogs/shared-preview/shared-preview-dialog-data.model"; -import {SharedPreviewDialog} from "@shared/components/dialogs/shared-preview/shared-preview.dialog"; -import {SharedDataTablePresentational} from "@shared/components/presentationals/shared-data-table/shared-data-table.presentational"; import {SharedAbstractDetailEditRoutable} from "@shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; -import {ComplianceLevelEnumHelper} from "@shared/enums/business/compliance-level.enum"; -import { - DataFileStatusEnum, - DataFileStatusEnumHelper, -} from "@shared/enums/business/data-file-status.enum"; -import {DataTableComponentEnum} from "@shared/enums/data-table-component.enum"; -import {FieldTypeEnum} from "@shared/enums/field-type.enum"; -import {FileViewModeEnum} from "@shared/enums/file-view-mode.enum"; import {LocalStateEnum} from "@shared/enums/local-state.enum"; -import {AppRoutesEnum} from "@shared/enums/routes.enum"; -import {DataTableActions} from "@shared/models/data-table-actions.model"; -import {DataTableBulkActions} from "@shared/models/data-table-bulk-actions.model"; -import {DataTableColumns} from "@shared/models/data-table-columns.model"; -import {StatusHistoryDialog} from "@shared/models/status-history-dialog.model"; -import {StatusHistory} from "@shared/models/status-history.model"; import {SecurityService} from "@shared/services/security.service"; import {Observable} from "rxjs"; -import { - distinctUntilChanged, - take, - tap, -} from "rxjs/operators"; -import { - CompositionState, - isNullOrUndefined, - isTrue, - isUndefined, - MemoizedUtil, - OrderEnum, - QueryParameters, - ResourceActionHelper, - ResourceState, - StringUtil, - TRANSLATE, -} from "solidify-frontend"; +import {MemoizedUtil} from "solidify-frontend"; @Component({ selector: "dlcm-deposit-file-routable", @@ -92,96 +31,20 @@ import { styleUrls: ["./deposit-file.routable.scss"], }) export class DepositFileRoutable extends SharedAbstractDetailEditRoutable<Deposit, DepositStateModel> implements OnInit { - isLoadingDataFileObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositDataFileState); - listDataFileObs: Observable<DepositDataFile[]> = CompositionState.list(this._store, DepositDataFileState); - queryParametersObs: Observable<QueryParameters> = CompositionState.queryParameters(this._store, DepositDataFileState); - - foldersObs: Observable<string[]> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.folders); - intermediateFoldersObs: Observable<string[]> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.intermediateFolders); - foldersWithIntermediateFoldersObs: Observable<string[]> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.foldersWithIntermediateFolders); - currentFolderObs: Observable<string> = MemoizedUtil.select(this._store, DepositDataFileState, state => state.currentFolder); - canEditObs: Observable<boolean> = MemoizedUtil.select(this._store, DepositState, state => state.canEdit).pipe( - distinctUntilChanged(), - tap(canEdit => this.actions = [...this.actions]), // Force method computeContext on datatable - ); - - isLoadingHistoryObs: Observable<boolean> = MemoizedUtil.isLoading(this._store, DepositDataFileStatusHistoryState); - dataFileHistoryObs: Observable<StatusHistory[]> = MemoizedUtil.select(this._store, DepositDataFileStatusHistoryState, state => state.history); - queryParametersHistoryObs: Observable<QueryParameters> = MemoizedUtil.select(this._store, DepositDataFileStatusHistoryState, state => state.queryParameters); - @Select(AppState.currentOrgUnitPerson) currentPersonObs: Observable<PersonRole>; - @ViewChild("dataTablePresentational") - readonly dataTablePresentational: SharedDataTablePresentational<DepositDataFile>; - - currentFileViewMode: FileViewModeEnum = FileViewModeEnum.FolderView; - - columns: DataTableColumns<DepositDataFile>[]; - - actions: DataTableActions<DepositDataFile>[] = [ - { - logo: "check_circle", - callback: (depositDataFile: DepositDataFile) => this.validate(this._resId, depositDataFile), - placeholder: TRANSLATE("crud.list.action.validate"), - displayOnCondition: (depositDataFile: DepositDataFile) => depositDataFile.status === DataFileStatusEnum.IGNORED_FILE, - }, - { - logo: "file_download", - callback: (depositDataFile: DepositDataFile) => this.downloadDataFile(this._resId, depositDataFile), - placeholder: TRANSLATE("crud.list.action.download"), - displayOnCondition: (depositDataFile: DepositDataFile) => true, - }, - { - logo: "play_circle_filled", - callback: (depositDataFile: DepositDataFile) => this.resumeDataFile(this._resId, depositDataFile), - placeholder: TRANSLATE("deposit.file.button.resume"), - displayOnCondition: (depositDataFile: DepositDataFile) => depositDataFile.status === DataFileStatusEnum.IN_ERROR, - }, - { - logo: "delete", - callback: (depositDataFile: DepositDataFile) => this.deleteDataFile(this._resId, depositDataFile), - placeholder: TRANSLATE("crud.list.action.delete"), - displayOnCondition: (depositDataFile: DepositDataFile) => MemoizedUtil.selectSnapshot(this._store, DepositState, state => state.canEdit), - }, - { - logo: "remove_red_eye", - callback: (depositDataFile: DepositDataFile) => this.showPreview(depositDataFile), - placeholder: TRANSLATE("deposit.file.button.showPreview"), - }, - { - logo: "redo", - callback: (depositDataFile: DepositDataFile) => this.moveDataFileWithDialog(depositDataFile), - placeholder: TRANSLATE("crud.list.action.move"), - displayOnCondition: (depositDataFile: DepositDataFile) => MemoizedUtil.selectSnapshot(this._store, DepositState, state => state.canEdit), - }, - { - logo: "history", - callback: (depositDataFile: DepositDataFile) => this.historyDataFile(this._resId, depositDataFile), - placeholder: TRANSLATE("crud.list.action.history"), - }, - ]; + canEditObs: Observable<boolean> = MemoizedUtil.select(this._store, DepositState, state => state.canEdit); + depositModeTabEnumObs: Observable<ModeDepositTabEnum> = MemoizedUtil.select(this._store, DepositState, state => state.depositModeTabEnum); - bulkActions: DataTableBulkActions[] = [ - { - logo: "delete", - callback: (listId: string[]) => this.bulkDeleteDataFile(this._resId, listId), - label: TRANSLATE("crud.list.bulkAction.deleteSelection"), - displayOnCondition: (listId: string[]) => this.canEditObs, - }, - ]; - - columnsToSkippedFilter: keyof DepositDataFile[] | string[] = [ - DepositDataFileHelper.RELATIVE_LOCATION, - "status", - ]; - - dialogDataFileRef: MatDialogRef<SharedFileDetailDialog<DepositDataFile>>; + get depositStatusEnum(): typeof Deposit.StatusEnum { + return Deposit.StatusEnum; + } - get fileViewModeEnum(): typeof FileViewModeEnum { - return FileViewModeEnum; + get modeDepositTabEnum(): typeof ModeDepositTabEnum { + return ModeDepositTabEnum; } - readonly KEY_PARAM_NAME: keyof Deposit & string = undefined; + readonly KEY_PARAM_NAME: keyof Deposit & string; constructor(protected readonly _store: Store, protected readonly _route: ActivatedRoute, @@ -194,280 +57,12 @@ export class DepositFileRoutable extends SharedAbstractDetailEditRoutable<Deposi ngOnInit(): void { super.ngOnInit(); - this.getCurrentModelOnParent(); - - 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: false, - 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: StringUtil.stringEmpty, - type: FieldTypeEnum.singleSelect, - order: OrderEnum.none, - sortableField: "dataFile.status" as any, - filterableField: "dataFile.status" as any, - isSortable: false, - isFilterable: false, - translate: false, - component: DataTableComponentEnum.dataFileQuickStatus, - width: "35px", - // filterEnum: ComplianceLevelEnumHelper.getListKeyValue(), - }, - { - 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, - tooltip: (value) => DataFileStatusEnumHelper.getExplanation(value as DataFileStatusEnum), - filterEnum: DataFileStatusEnumHelper.getListKeyValue(), - }, - { - 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, - component: DataTableComponentEnum.conformityLevelStar, - filterEnum: ComplianceLevelEnumHelper.getListKeyValue(), - }, - ]; - } - - private getCurrentModelOnParent(): void { - this._resId = this._route.snapshot.parent.paramMap.get(AppRoutesEnum.paramIdWithoutPrefixParam); - this.getDepositById(this._resId); - } - - private getDepositById(id: string): void { - const depositInState = ResourceState.currentSnapshot(this._store, DepositState); - if (isNullOrUndefined(depositInState) || id !== depositInState.resId) { - this._store.dispatch(ResourceActionHelper.getById(depositActionNameSpace, id, true)); - } - this.getSubResourceWithParentId(id); - } - - getSubResourceWithParentId(id: string): void { - this._store.dispatch(new DepositDataFileAction.GetAll(id, undefined, true)); - this._store.dispatch(new DepositDataFileAction.GetListFolder(id)); - } - - onQueryParametersEvent(queryParameters: QueryParameters, withRefresh: boolean = true): void { - this._store.dispatch(new DepositDataFileAction.ChangeQueryParameters(this._resId, queryParameters, true, withRefresh)); - // this._changeDetector.detectChanges(); // Allow to display spinner the first time - } - - refresh(): void { - this._store.dispatch(new DepositDataFileAction.Refresh(this._resId)); + this.retrieveResIdFromUrl(); + this.getSubResourceWithParentId(this._resId); } - download($event: DepositDataFile): void { - this._store.dispatch(new DepositDataFileAction.Download(this._resId, $event)); - } - - selectFolder(folderFullName: string | undefined): void { - if (isUndefined(folderFullName)) { - this.currentFileViewMode = FileViewModeEnum.FlatView; - } else { - this.currentFileViewMode = FileViewModeEnum.FolderView; - } - this._store.dispatch(new DepositDataFileAction.ChangeCurrentFolder(folderFullName, true)); - } - - downloadFolder(folderName: string): void { - this._store.dispatch(new DepositAction.Download(this._resId, folderName)); - } - - showDetail(depositDataFile: DepositDataFile): void { - this.dialogDataFileRef = this._dialog.open(SharedFileDetailDialog, { - data: { - dataFile: depositDataFile, - resId: depositDataFile.resId, - parentId: this._resId, - isReadonly: !this.isEdit, - isLoading: this.isLoadingHistoryObs, - statusHistory: this.dataFileHistoryObs, - queryParametersObs: this.queryParametersHistoryObs, - state: DepositDataFileStatusHistoryAction, - name: LocalStateEnum.deposit, - buttons: [ - { - labelToTranslate: TRANSLATE("deposit.file.button.validate"), - color: "primary", - icon: "check_circle", - callback: current => this.validate(this._resId, current), - displayCondition: current => current.status === DataFileStatusEnum.IGNORED_FILE, - order: 0, - }, - { - labelToTranslate: TRANSLATE("deposit.file.button.download"), - color: "primary", - icon: "file_download", - callback: current => this.downloadDataFile(this._resId, current), - order: 1, - }, - { - labelToTranslate: TRANSLATE("deposit.file.button.delete"), - color: "primary", - icon: "delete", - callback: (current) => this.deleteDataFile(this._resId, current), - displayCondition: current => this.canEditObs, - order: 2, - }, - { - labelToTranslate: TRANSLATE("deposit.file.button.resume"), - color: "primary", - icon: "play_circle_filled", - callback: current => this.resumeDataFile(this._resId, current), - displayCondition: current => current.status === DataFileStatusEnum.IN_ERROR, - order: 3, - }, - { - labelToTranslate: TRANSLATE("deposit.file.button.move"), - color: "primary", - icon: "redo", - callback: current => this.moveDataFileWithDialog(current), - displayCondition: current => this.canEditObs, - order: 4, - }, - ], - } as SharedFileDetailDialogData<DepositDataFile>, - width: environment.modalWidth, - }); - } - - bulkDeleteDataFile(parentId: string, listId: string[]): void { - this._store.dispatch(new DepositDataFileAction.DeleteAll(parentId, listId)); - } - - deleteDataFile(parentId: string, dataFile: DepositDataFile): void { - this._store.dispatch(new DepositDataFileAction.Delete(parentId, dataFile)); - this.subscribe(this._actions$.pipe( - ofActionCompleted(DepositDataFileAction.DeleteSuccess), - tap(result => { - if (!result.result.successful) { - return; - } - if (this.dialogDataFileRef) { - this.dialogDataFileRef.close(); - } - }), - )); - } - - moveDataFileWithDialog(dataFile: DepositDataFile): void { - this._dialog.open(DepositFileMoveDialog, { - data: { - parentResId: this._resId, - depositDataFile: dataFile, - } as DepositFileMoveDialogData, - }); - if (!isNullOrUndefined(this.dialogDataFileRef)) { - this.subscribe(this._actions$.pipe( - ofActionCompleted(DepositDataFileAction.MoveSuccess), - take(1), - tap((result) => { - if (isTrue(result.result.successful)) { - this.dialogDataFileRef.close(); - } - }), - )); - } - } - - showPreview(dataFile: DepositDataFile): void { - const previewDialogRef = this._dialog.open(SharedPreviewDialog, { - width: "min-content", - height: "min-content", - data: { - fileInput: { - dataFile: dataFile, - }, - - } as SharedPreviewDialogData, - }); - } - - moveDataFile(dataFile: DepositDataFile): void { - this._store.dispatch(new DepositDataFileAction.Move(this._resId, { - resId: dataFile.resId, - relativeLocation: dataFile.relativeLocation, - })); - } - - validate(parentId: string, dataFile: DepositDataFile): void { - this._store.dispatch(new DepositDataFileAction.Validate(parentId, dataFile)); - } - - downloadDataFile(parentId: string, dataFile: DepositDataFile): void { - this._store.dispatch(new DepositDataFileAction.Download(parentId, dataFile)); - } - - resumeDataFile(parentId: string, dataFile: DepositDataFile): void { - this._store.dispatch(new DepositDataFileAction.ResumeDataFile(parentId, dataFile)); + protected getSubResourceWithParentId(id: string): void { } +} - historyDataFile(parentId: string, dataFile: DepositDataFile): void { - const dialogRef = this._dialog.open(SharedHistoryDialog, { - width: environment.modalWidth, - data: { - parentId: parentId, - resourceResId: dataFile.resId, - name: LocalStateEnum.deposit, - statusHistory: this.dataFileHistoryObs, - isLoading: this.isLoadingHistoryObs, - queryParametersObs: this.queryParametersHistoryObs, - state: DepositDataFileStatusHistoryAction, - } as StatusHistoryDialog, - }); - } - selectAndDelete(): void { - this.subscribe(this._dialog.open(SharedConfirmDialog, { - data: { - titleToTranslate: TRANSLATE("deposit.file.bulkDelete.title"), - messageToTranslate: TRANSLATE("deposit.file.bulkDelete.message"), - confirmButtonToTranslate: TRANSLATE("deposit.file.bulkDelete.button.confirm"), - cancelButtonToTranslate: TRANSLATE("deposit.file.bulkDelete.button.cancel"), - } as SharedConfirmDialogData, - }).afterClosed().pipe( - tap((isConfirmed: boolean | undefined) => { - if (isNullOrUndefined(isConfirmed)) { - return; - } - if (isTrue(isConfirmed)) { - this.subscribe(this.dataTablePresentational.selectAllResult().pipe( - take(1), - tap(selection => { - this.bulkDeleteDataFile(this._resId, selection); - this.dataTablePresentational.cleanSelection(); - }), - )); - } - }), - )); - } -} diff --git a/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.html b/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.html deleted file mode 100644 index 93993abd8..000000000 --- a/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.html +++ /dev/null @@ -1,31 +0,0 @@ -<div class="upload-button-wrapper"> - <button mat-button - color="primary" - type="button" - solidifyShortCuts - (onEnter)="openModalUpload()" - (click)="openModalUpload()" - > - <mat-icon>publish</mat-icon> - {{'deposit.file.button.upload' | translate}} - </button> - - <button mat-button - color="primary" - type="button" - [matTooltip]="'deposit.file.button.uploadArchive.tooltip' | translate" - solidifyShortCuts - (onEnter)="openModalUploadArchive()" - (click)="openModalUploadArchive()" - > - <mat-icon>unarchive</mat-icon> - {{'deposit.file.button.uploadArchive.label' | translate}} - </button> -</div> - -<dlcm-deposit-files-upload-in-progress [listFilesUploading]="uploadStatusObs | async" - (cancelChange)="cancel($event)" - (retryChange)="retry($event)" - (uploadChange)="upload($event)" - (uploadArchiveChange)="uploadArchive($event)" -></dlcm-deposit-files-upload-in-progress> diff --git a/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.scss b/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.scss deleted file mode 100644 index 6af0cbe4d..000000000 --- a/src/app/features/deposit/components/routables/deposit-upload/deposit-upload.routable.scss +++ /dev/null @@ -1,12 +0,0 @@ -@import "../../../../../../sass/abstracts/variables"; -@import "../../../../../../sass/abstracts/mixins"; -@import "../../../../../shared/components/routables/shared-abstract-detail-edit/shared-abstract-detail-edit.routable"; - -:host { - padding: 0; - - .upload-button-wrapper { - padding-bottom: 25px; - text-align: center; - } -} diff --git a/src/app/features/deposit/deposit-routing.module.ts b/src/app/features/deposit/deposit-routing.module.ts index b214ae937..2c4f7058a 100644 --- a/src/app/features/deposit/deposit-routing.module.ts +++ b/src/app/features/deposit/deposit-routing.module.ts @@ -10,19 +10,16 @@ import { DepositRoutesEnum, } from "@app/shared/enums/routes.enum"; import {DlcmRoutes} from "@app/shared/models/dlcm-route.model"; -import {DepositCollectionRoutable} from "@deposit/components/routables/deposit-collection/deposit-collection.routable"; import {DepositDetailEditRoutable} from "@deposit/components/routables/deposit-detail-edit/deposit-detail-edit.routable"; import {DepositFileRoutable} from "@deposit/components/routables/deposit-file/deposit-file.routable"; import {DepositHomeRoutable} from "@deposit/components/routables/deposit-home/deposit-home.routable"; import {DepositMetadataRoutable} from "@deposit/components/routables/deposit-metadata/deposit-metadata.routable"; import {DepositRootRoutable} from "@deposit/components/routables/deposit-root/deposit-root.routable"; -import {DepositUploadRoutable} from "@deposit/components/routables/deposit-upload/deposit-upload.routable"; import { DepositTabStatusEnum, DepositTabStatusName, } from "@deposit/enums/deposit-tab-status.enum"; import {DepositState} from "@deposit/stores/deposit.state"; -import {DepositDetailTabGuardService} from "@shared/guards/deposit-detail-tab-guard.service"; import {DepositRoleGuardDetailService} from "@shared/guards/deposit-role-guard-detail.service"; import {DepositRoleGuardEditService} from "@shared/guards/deposit-role-guard-edit.service"; import {CanDeactivateGuard} from "@shared/services/can-deactivate-guard.service"; @@ -105,10 +102,6 @@ const routes: DlcmRoutes = [ { path: DepositRoutesEnum.metadata, component: DepositMetadataRoutable, - data: { - breadcrumb: TRANSLATE("breadcrumb.deposit.metadata"), - noBreadcrumbLink: true, - }, children: [ { path: DepositRoutesEnum.edit, @@ -121,32 +114,8 @@ const routes: DlcmRoutes = [ ], }, { - path: DepositRoutesEnum.files, + path: DepositRoutesEnum.data, component: DepositFileRoutable, - data: { - breadcrumb: TRANSLATE("breadcrumb.deposit.file"), - noBreadcrumbLink: true, - }, - canActivate: [DepositDetailTabGuardService], - }, - { - path: DepositRoutesEnum.collection, - component: DepositCollectionRoutable, - data: { - breadcrumb: TRANSLATE("breadcrumb.deposit.collection"), - noBreadcrumbLink: true, - }, - canActivate: [DepositDetailTabGuardService], - }, - { - path: DepositRoutesEnum.upload, - component: DepositUploadRoutable, - data: { - breadcrumb: TRANSLATE("breadcrumb.deposit.upload"), - noBreadcrumbLink: true, - }, - canActivate: [DepositRoleGuardEditService], - canDeactivate: [CanDeactivateGuard], }, ], }, diff --git a/src/app/features/deposit/deposit.module.ts b/src/app/features/deposit/deposit.module.ts index b2cc51117..835151d34 100644 --- a/src/app/features/deposit/deposit.module.ts +++ b/src/app/features/deposit/deposit.module.ts @@ -9,6 +9,9 @@ import {DepositDataFileState} from "@app/features/deposit/stores/data-file/depos import {DepositState} from "@app/features/deposit/stores/deposit.state"; import {DepositPeopleState} from "@app/features/deposit/stores/people/deposit-people.state"; import {SharedModule} from "@app/shared/shared.module"; +import {DepositCollectionContainer} from "@deposit/components/containers/deposit-collection/deposit-collection.container"; +import {DepositFileContainer} from "@deposit/components/containers/deposit-file/deposit-file.container"; +import {DepositUploadContainer} from "@deposit/components/containers/deposit-upload/deposit-upload.container"; import {DepositAipUploadDialog} from "@deposit/components/dialogs/deposit-aip-upload/deposit-aip-upload.dialog"; import {DepositFileMoveDialog} from "@deposit/components/dialogs/deposit-file-move/deposit-file-move.dialog"; import {DepositFileUploadArchiveDialog} from "@deposit/components/dialogs/deposit-file-upload-archive/deposit-file-upload-archive.dialog"; @@ -17,12 +20,10 @@ import {DepositPersonAlternativeDialog} from "@deposit/components/dialogs/deposi import {DepositPersonDialog} from "@deposit/components/dialogs/deposit-person/deposit-person.dialog"; import {DepositBannerPresentational} from "@deposit/components/presentationals/deposit-banner/deposit-banner.presentational"; import {DepositFolderTreePresentational} from "@deposit/components/presentationals/deposit-folder-tree/deposit-folder-tree.presentational"; -import {DepositCollectionRoutable} from "@deposit/components/routables/deposit-collection/deposit-collection.routable"; import {DepositFileRoutable} from "@deposit/components/routables/deposit-file/deposit-file.routable"; import {DepositHomeRoutable} from "@deposit/components/routables/deposit-home/deposit-home.routable"; import {DepositMetadataRoutable} from "@deposit/components/routables/deposit-metadata/deposit-metadata.routable"; import {DepositRootRoutable} from "@deposit/components/routables/deposit-root/deposit-root.routable"; -import {DepositUploadRoutable} from "@deposit/components/routables/deposit-upload/deposit-upload.routable"; import {DepositAipState} from "@deposit/stores/aip/deposit-aip.state"; import {DepositAuthorizedOrganizationalUnitState} from "@deposit/stores/authorized-organizational-unit/deposit-authorized-organizational-unit.state"; import {DepositCollectionState} from "@deposit/stores/collection/deposit-collection.state"; @@ -44,11 +45,13 @@ const routables = [ DepositCreateRoutable, DepositDetailEditRoutable, DepositFileRoutable, - DepositUploadRoutable, DepositMetadataRoutable, - DepositCollectionRoutable, ]; -const containers = []; +const containers = [ + DepositUploadContainer, + DepositFileContainer, + DepositCollectionContainer, +]; const dialogs = [ DepositDeleteDialog, DepositFileUploadDialog, diff --git a/src/app/features/deposit/enums/mode-deposit-tab.enum.ts b/src/app/features/deposit/enums/mode-deposit-tab.enum.ts new file mode 100644 index 000000000..1d6cc1ff9 --- /dev/null +++ b/src/app/features/deposit/enums/mode-deposit-tab.enum.ts @@ -0,0 +1,5 @@ +export enum ModeDepositTabEnum { + UNDEFINED, + FILE, + COLLECTION, +} diff --git a/src/app/features/deposit/helpers/deposit.helper.ts b/src/app/features/deposit/helpers/deposit.helper.ts index d1073820a..d1f368638 100644 --- a/src/app/features/deposit/helpers/deposit.helper.ts +++ b/src/app/features/deposit/helpers/deposit.helper.ts @@ -1,20 +1,7 @@ import {ActivatedRoute} from "@angular/router"; -import {DepositTabEnum} from "@deposit/enums/deposit-tab.enum"; import {DepositRoutesEnum} from "@shared/enums/routes.enum"; export class DepositHelper { - static computeIsReadonly(route: ActivatedRoute): boolean { - if (route.snapshot.parent.url.length > 0) { - const mode = route.snapshot.parent.url[0].path; - if (mode === DepositRoutesEnum.edit) { - return false; - } else if (mode === DepositRoutesEnum.create) { - return false; - } - } - return true; - } - static getTabRouteSelected(route: ActivatedRoute): DepositRoutesEnum | undefined { const children = route.snapshot.children; if (children.length > 0 && children[0].url.length > 0) { @@ -23,37 +10,4 @@ export class DepositHelper { } return undefined; } - - static getTabSelectedIndexWithRoute(tabRouteSelected: DepositRoutesEnum): DepositTabEnum | undefined { - if (tabRouteSelected === DepositRoutesEnum.metadata) { - return DepositTabEnum.metadata; - } - if (tabRouteSelected === DepositRoutesEnum.files) { - return DepositTabEnum.file; - } - if (tabRouteSelected === DepositRoutesEnum.upload) { - return DepositTabEnum.upload; - } - if (tabRouteSelected === DepositRoutesEnum.collection) { - return DepositTabEnum.collection; - } - return undefined; - } - - static getTabRouteSelectedWithTabIndex(tabIndexSelected: DepositTabEnum): DepositRoutesEnum | undefined { - if (tabIndexSelected === DepositTabEnum.metadata) { - return DepositRoutesEnum.metadata; - } - if (tabIndexSelected === DepositTabEnum.file) { - return DepositRoutesEnum.files; - } - if (tabIndexSelected === DepositTabEnum.upload) { - return DepositRoutesEnum.upload; - } - if (tabIndexSelected === DepositTabEnum.collection) { - return DepositRoutesEnum.collection; - } - return undefined; - } - } diff --git a/src/app/features/deposit/stores/collection/deposit-collection.action.ts b/src/app/features/deposit/stores/collection/deposit-collection.action.ts index 33097316e..42e9496ba 100644 --- a/src/app/features/deposit/stores/collection/deposit-collection.action.ts +++ b/src/app/features/deposit/stores/collection/deposit-collection.action.ts @@ -99,13 +99,22 @@ export namespace DepositCollectionAction { } } - export class Refresh { + export class Refresh extends BaseAction { static readonly type: string = `[${state}] Refresh`; constructor(public parentId: string) { + super(); } } + export class RefreshSuccess extends BaseSubAction<Refresh> { + static readonly type: string = `[${state}] Refresh Success`; + } + + export class RefreshFail extends BaseSubAction<Refresh> { + static readonly type: string = `[${state}] Refresh Fail`; + } + export class AddAip { static readonly type: string = `[${state}] Add Aip`; diff --git a/src/app/features/deposit/stores/collection/deposit-collection.state.ts b/src/app/features/deposit/stores/collection/deposit-collection.state.ts index 6c372b5fe..d82783f76 100644 --- a/src/app/features/deposit/stores/collection/deposit-collection.state.ts +++ b/src/app/features/deposit/stores/collection/deposit-collection.state.ts @@ -3,12 +3,14 @@ import { DepositCollectionAction, depositCollectionActionNameSpace, } from "@deposit/stores/collection/deposit-collection.action"; +import {DepositAction} from "@deposit/stores/deposit.action"; import {environment} from "@environments/environment"; import {Aip} from "@models"; import {Navigate} from "@ngxs/router-plugin"; import { Action, Actions, + ofActionCompleted, State, StateContext, Store, @@ -34,17 +36,16 @@ import { NotificationService, QueryParameters, SolidifyStateError, + StoreUtil, } from "solidify-frontend"; export const defaultDepositCollectionValue: () => DepositCollectionStateModel = () => ({ ...defaultAssociationRemoteStateInitValue(), - listAips: [], numberCollections: undefined, }); export interface DepositCollectionStateModel extends AssociationRemoteStateModel<Aip> { - listAips: string[]; numberCollections: number | undefined; } @@ -80,8 +81,47 @@ export class DepositCollectionState extends AssociationRemoteState<DepositCollec @Action(DepositCollectionAction.Refresh) refresh(ctx: StateContext<DepositCollectionStateModel>, action: DepositCollectionAction.Refresh): void { - ctx.dispatch(new DepositCollectionAction.GetAll(action.parentId, undefined, true)); - ctx.dispatch(new DepositCollectionAction.GetNumberCollections(action.parentId)); + ctx.patchState({ + isLoadingCounter: ctx.getState().isLoadingCounter + 1, + }); + + StoreUtil.dispatchParallelActionAndWaitForSubActionsCompletion(ctx, [ + { + action: new DepositCollectionAction.GetAll(action.parentId, undefined, true), + subActionCompletions: [ + this.actions$.pipe(ofActionCompleted(DepositCollectionAction.GetAllSuccess)), + this.actions$.pipe(ofActionCompleted(DepositCollectionAction.GetAllFail)), + ], + }, + { + action: new DepositCollectionAction.GetNumberCollections(action.parentId), + subActionCompletions: [ + this.actions$.pipe(ofActionCompleted(DepositCollectionAction.GetNumberCollectionsSuccess)), + this.actions$.pipe(ofActionCompleted(DepositCollectionAction.GetNumberCollectionsFail)), + ], + }, + ]).subscribe(success => { + if (success) { + ctx.dispatch(new DepositCollectionAction.RefreshSuccess(action)); + } else { + ctx.dispatch(new DepositCollectionAction.RefreshFail(action)); + } + }); + } + + @Action(DepositCollectionAction.RefreshSuccess) + refreshSuccess(ctx: StateContext<DepositCollectionStateModel>, action: DepositCollectionAction.RefreshSuccess): void { + ctx.patchState({ + isLoadingCounter: ctx.getState().isLoadingCounter - 1, + }); + ctx.dispatch(new DepositAction.ComputeModeTab()); + } + + @Action(DepositCollectionAction.RefreshFail) + refreshFail(ctx: StateContext<DepositCollectionStateModel>, action: DepositCollectionAction.RefreshFail): void { + ctx.patchState({ + isLoadingCounter: ctx.getState().isLoadingCounter - 1, + }); } @Action(DepositCollectionAction.GoToAip) 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 25d820df0..d291c7e45 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 @@ -19,6 +19,7 @@ import { DepositDataFileStatusHistoryState, DepositDataFileStatusHistoryStateModel, } from "@deposit/stores/data-file/status-history/deposit-data-file-status-history.state"; +import {DepositAction} from "@deposit/stores/deposit.action"; import {DepositState} from "@deposit/stores/deposit.state"; import {environment} from "@environments/environment"; import { @@ -199,6 +200,7 @@ export class DepositDataFileState extends CompositionState<DepositDataFileStateM ctx.patchState({ isLoadingCounter: ctx.getState().isLoadingCounter - 1, }); + ctx.dispatch(new DepositAction.ComputeModeTab()); } @Action(DepositDataFileAction.RefreshFail) @@ -228,7 +230,6 @@ export class DepositDataFileState extends CompositionState<DepositDataFileStateM } } - @Action(DepositDataFileAction.GetListFolder) getListFolder(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.GetListFolder): Observable<string[]> { ctx.patchState({ diff --git a/src/app/features/deposit/stores/deposit.action.ts b/src/app/features/deposit/stores/deposit.action.ts index a2154b981..e8c23394f 100644 --- a/src/app/features/deposit/stores/deposit.action.ts +++ b/src/app/features/deposit/stores/deposit.action.ts @@ -376,6 +376,10 @@ export namespace DepositAction { super(); } } + + export class ComputeModeTab extends BaseAction { + static readonly type: string = `[${state}] Can Create`; + } } export const depositActionNameSpace: ResourceNameSpace = DepositAction; diff --git a/src/app/features/deposit/stores/deposit.state.ts b/src/app/features/deposit/stores/deposit.state.ts index 102f4df6c..27f4aa55d 100644 --- a/src/app/features/deposit/stores/deposit.state.ts +++ b/src/app/features/deposit/stores/deposit.state.ts @@ -35,6 +35,7 @@ import { import {AppAction} from "@app/stores/app.action"; import {AppAuthorizedOrganizationalUnitState} from "@app/stores/authorized-organizational-unit/app-authorized-organizational-unit.state"; import {DepositFormPresentational} from "@deposit/components/presentationals/deposit-form/deposit-form.presentational"; +import {ModeDepositTabEnum} from "@deposit/enums/mode-deposit-tab.enum"; import { DepositAipState, DepositAipStateModel, @@ -132,6 +133,7 @@ export interface DepositStateModel extends ResourceStateModel<Deposit> { canEdit: boolean; canCreate: boolean; formPresentational: DepositFormPresentational | undefined; + depositModeTabEnum: ModeDepositTabEnum; } @Injectable() @@ -161,6 +163,7 @@ export interface DepositStateModel extends ResourceStateModel<Deposit> { canEdit: false, canCreate: false, formPresentational: undefined, + depositModeTabEnum: ModeDepositTabEnum.UNDEFINED, }, children: [ DepositDataFileState, @@ -332,7 +335,7 @@ export class DepositState extends ResourceState<DepositStateModel, Deposit> { createSuccess(ctx: StateContext<DepositStateModel>, action: DepositAction.CreateSuccess): void { super.createSuccess(ctx, action); ctx.dispatch([ - new Navigate([RoutesEnum.deposit, action.model.organizationalUnitId, DepositRoutesEnum.detail, action.model.resId, DepositRoutesEnum.upload]), + new Navigate([RoutesEnum.deposit, action.model.organizationalUnitId, DepositRoutesEnum.detail, action.model.resId, DepositRoutesEnum.data]), new DepositAction.SetOrganizationalUnit(action.model.organizationalUnitId), ]); } @@ -364,7 +367,7 @@ export class DepositState extends ResourceState<DepositStateModel, Deposit> { const depositId = action.model.resId; ctx.dispatch([ new DepositPersonAction.GetAll(depositId), - new Navigate([RoutesEnum.deposit, action.model.organizationalUnitId, DepositRoutesEnum.detail, action.model.resId, DepositRoutesEnum.files]), + new Navigate([RoutesEnum.deposit, action.model.organizationalUnitId, DepositRoutesEnum.detail, action.model.resId, DepositRoutesEnum.data]), new DepositAction.SetOrganizationalUnit(action.model.organizationalUnitId), ]); } @@ -845,4 +848,21 @@ export class DepositState extends ResourceState<DepositStateModel, Deposit> { }), ); } + + @Action(DepositAction.ComputeModeTab) + computeModeTab(ctx: StateContext<DepositStateModel>, action: DepositAction.ComputeModeTab): void { + let mode = ModeDepositTabEnum.UNDEFINED; + + const numberCollection = ctx.getState()?.deposit_collection?.numberCollections; + const numberFiles = ctx.getState()?.deposit_dataFile?.numberFiles; + if (numberCollection > 0) { + mode = ModeDepositTabEnum.COLLECTION; + } else if (numberFiles > 0) { + mode = ModeDepositTabEnum.FILE; + } + + ctx.patchState({ + depositModeTabEnum: mode, + }); + } } diff --git a/src/app/features/preservation/dip/stores/dip.state.ts b/src/app/features/preservation/dip/stores/dip.state.ts index 8e929bde5..e60679ddd 100644 --- a/src/app/features/preservation/dip/stores/dip.state.ts +++ b/src/app/features/preservation/dip/stores/dip.state.ts @@ -121,7 +121,7 @@ export class DipState extends ResourceState<DipStateModel, Dip> { super.updateSuccess(ctx, action); ctx.dispatch([ - new Navigate([RoutesEnum.preservationDipDetail, action.model.resId, DepositRoutesEnum.files]), + new Navigate([RoutesEnum.preservationDipDetail, action.model.resId, DepositRoutesEnum.data]), ]); } diff --git a/src/app/shared/enums/routes.enum.ts b/src/app/shared/enums/routes.enum.ts index 853382680..5b12b2088 100644 --- a/src/app/shared/enums/routes.enum.ts +++ b/src/app/shared/enums/routes.enum.ts @@ -32,9 +32,7 @@ export enum DepositRoutesEnum { detail = "detail", edit = "edit", metadata = "metadata", - files = "files", - upload = "upload", - collection = "collection", + data = "data", paramTab = ":tab", paramTabWithoutPrefix = "tab", } diff --git a/src/app/shared/guards/deposit-detail-tab-guard.service.ts b/src/app/shared/guards/deposit-detail-tab-guard.service.ts deleted file mode 100644 index 534c5f5e9..000000000 --- a/src/app/shared/guards/deposit-detail-tab-guard.service.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {Injectable} from "@angular/core"; -import { - ActivatedRouteSnapshot, - CanActivate, - Router, -} from "@angular/router"; -import {DepositAction} from "@app/features/deposit/stores/deposit.action"; -import {DepositState} from "@deposit/stores/deposit.state"; -import {Deposit} from "@models"; -import {Store} from "@ngxs/store"; -import { - AppRoutesEnum, - DepositRoutesEnum, -} from "@shared/enums/routes.enum"; -import {SecurityService} from "@shared/services/security.service"; -import {Observable} from "rxjs"; -import { - distinctUntilChanged, - filter, - map, -} from "rxjs/operators"; -import { - ApiService, - isNullOrUndefined, - ResourceState, - RoutingUtil, -} from "solidify-frontend"; - -@Injectable({ - providedIn: "root", -}) -export class DepositDetailTabGuardService implements CanActivate { - constructor(public router: Router, - private readonly _store: Store, - public apiService: ApiService, - private readonly _securityService: SecurityService) { - } - - canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean { - const path = route.url[0].path; - let depositId: string = route.parent.parent.params[AppRoutesEnum.paramIdWithoutPrefixParam]; - if (isNullOrUndefined(depositId)) { - depositId = route.parent.params[AppRoutesEnum.paramIdWithoutPrefixParam]; - } - // const depositInState = ResourceState.currentSnapshot(this._store, DepositState); - // if (!isNullOrUndefined(depositInState) && depositId === depositInState.resId) { - // return this._canGoToThisTabRoute(depositInState, path, route); - // } - this._store.dispatch(new DepositAction.GetById(depositId, true)); - return ResourceState.current(this._store, DepositState).pipe( - distinctUntilChanged(), - filter(deposit => !isNullOrUndefined(deposit) && deposit.resId === depositId), - map(deposit => this._canGoToThisTabRoute(deposit, path, route)), - ); - } - - private _canGoToThisTabRoute(deposit: Deposit, path: string, route: ActivatedRouteSnapshot): boolean { - if (deposit.dataFileNumber > 0 && deposit.collectionSize > 0) { - return true; - } - if (path === DepositRoutesEnum.files && deposit.collectionSize > 0) { - const url = RoutingUtil.rebuildUrl(route.pathFromRoot); - url[url.length - 1] = DepositRoutesEnum.collection; - this.router.navigate(url); - return false; - } - if (path === DepositRoutesEnum.collection && deposit.dataFileNumber > 0) { - const url = RoutingUtil.rebuildUrl(route.pathFromRoot); - url[url.length - 1] = DepositRoutesEnum.files; - this.router.navigate(url); - return false; - } - return true; - } -} diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index da8f3aba1..922bf57f3 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -1138,13 +1138,9 @@ "root": "Contributor" }, "deposit": { - "collection": "Collection", "create": "Create", "edit": "Edit", - "file": "Data files", - "metadata": "Metadata", - "root": "Deposits", - "upload": "Uploads" + "root": "Deposits" }, "home": "Home", "myOrder": { @@ -1290,9 +1286,9 @@ "collection": { "button": { "addAip": { - "label": "Add archives", - "tooltip": "Add existing archives to collection" - } + "tooltip": "Associate existing archives to collection" + }, + "associateArchive": "Associate archive" } }, "collectionBegin": "Data Collection Start Date", @@ -1341,11 +1337,13 @@ "showHistory": "Show history", "showPreview": "Show preview", "showTree": "Folder view", - "upload": "Upload files", + "uploadAdvanced": "Advanced mode", "uploadArchive": { "label": "Structured upload", "tooltip": "Uploading a zip file in this manner will decompress its contents while retaining its folder structure" }, + "uploadPrimaryData": "Upload primary data", + "uploadSecondaryData": "Upload secondary data", "validate": "Do not ignore" }, "detail": { @@ -1549,9 +1547,9 @@ "submit": "Submit", "tab": { "collection": "Collection", + "data": "Data", "datafiles": "Files", - "details": "Metadata", - "upload": "Uploads" + "details": "Metadata" }, "table": { "header": { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0928337ed..4d467990e 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1138,13 +1138,9 @@ "root": "Contributor" }, "deposit": { - "collection": "Collection", "create": "Create", "edit": "Edit", - "file": "Data files", - "metadata": "Metadata", - "root": "Deposits", - "upload": "Uploads" + "root": "Deposits" }, "home": "Home", "myOrder": { @@ -1290,9 +1286,9 @@ "collection": { "button": { "addAip": { - "label": "Add archives", - "tooltip": "Add existing archives to collection" - } + "tooltip": "Associate existing archives to collection" + }, + "associateArchive": "Associate archive" } }, "collectionBegin": "Data Collection Start Date", @@ -1341,11 +1337,13 @@ "showHistory": "Show history", "showPreview": "Show preview", "showTree": "Folder view", - "upload": "Upload files", + "uploadAdvanced": "Advanced mode", "uploadArchive": { "label": "Structured upload", "tooltip": "Uploading a zip file in this manner will decompress its contents while retaining its folder structure" }, + "uploadPrimaryData": "Upload primary data", + "uploadSecondaryData": "Upload secondary data", "validate": "Do not ignore" }, "detail": { @@ -1549,9 +1547,9 @@ "submit": "Submit", "tab": { "collection": "Collection", + "data": "Data", "datafiles": "Files", - "details": "Metadata", - "upload": "Uploads" + "details": "Metadata" }, "table": { "header": { diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 98d2eec06..3dc5ebdd7 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -1138,13 +1138,9 @@ "root": "Contributeur-rice" }, "deposit": { - "collection": "Collection", "create": "Créer", "edit": "Modifier", - "file": "Fichiers", - "metadata": "Métadonnées", - "root": "Dépôts", - "upload": "Envois" + "root": "Dépôts" }, "home": "Accueil", "myOrder": { @@ -1290,9 +1286,9 @@ "collection": { "button": { "addAip": { - "label": "Ajouter archive", - "tooltip": "Ajouter une archive existante à la collection" - } + "tooltip": "Associer une archive existante à la collection" + }, + "associateArchive": "Associer archive" } }, "collectionBegin": "Début de la collecte des données", @@ -1341,11 +1337,13 @@ "showHistory": "Voir l'historique", "showPreview": "Voir prévisualisation", "showTree": "Vue dossier", - "upload": "Envoyer fichiers", + "uploadAdvanced": "Mode avancé", "uploadArchive": { - "label": "Envoyer fichiers en lot", + "label": "Envoyer données en lot", "tooltip": "Le téléchargement d'un fichier zip de cette manière décompressera son contenu tout en conservant sa structure de dossiers" }, + "uploadPrimaryData": "Envoyer données primaires", + "uploadSecondaryData": "Envoyer données secondaires", "validate": "Ne pas ignore" }, "detail": { @@ -1549,9 +1547,9 @@ "submit": "Enregistrer", "tab": { "collection": "Collection", + "data": "Données", "datafiles": "Fichiers", - "details": "Métadonnées", - "upload": "Envois" + "details": "Métadonnées" }, "table": { "header": { -- GitLab