From bfd45a88944be8816136eb0529707e54831c34a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Mon, 9 Aug 2021 16:06:26 +0300 Subject: [PATCH] Trash screen & some other improvements --- .../downloads-list-screen.component.ts | 17 ++- .../screens/trash/trash-screen.component.html | 85 ++++------- .../screens/trash/trash-screen.component.scss | 9 +- .../screens/trash/trash-screen.component.ts | 136 ++++++++++++------ .../dossier-listing-screen.component.ts | 2 +- .../table-header/table-header.component.html | 7 +- apps/red-ui/src/assets/config/config.json | 4 +- .../red-ui/src/assets/icons/general/enter.svg | 7 +- .../src/assets/icons/general/put-back.svg | 1 - 9 files changed, 150 insertions(+), 118 deletions(-) diff --git a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts index cd2ea8848..aaf77cc26 100644 --- a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts +++ b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts @@ -5,6 +5,7 @@ import { DownloadControllerService } from '@redaction/red-ui-http'; import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component'; import { CircleButtonTypes, TableColumnConfig } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { LoadingService } from '@services/loading.service'; @Component({ selector: 'redaction-downloads-list-screen', @@ -27,22 +28,28 @@ export class DownloadsListScreenComponent extends BaseListingComponent d.storageId); + deleteItems(downloads?: DownloadStatusWrapper[]) { + this._loadingService.loadWhile(this._deleteItems(downloads)); + } + + private async _deleteItems(downloads?: DownloadStatusWrapper[]) { + const storageIds = (downloads || this.screenStateService.selectedEntities).map(d => d.storageId); await this._downloadControllerService.deleteDownload({ storageIds }).toPromise(); + this.screenStateService.setSelectedEntities([]); await this._loadData(); } diff --git a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html index ae24d7d02..49d2932ba 100644 --- a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html @@ -5,55 +5,12 @@
-
- - - - {{ 'trash.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }} - - - - - -
- -
-
- - - - - -
-
+
@@ -125,3 +82,23 @@
+ + +
+ + + +
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss index 41c1395e0..9bebde5ca 100644 --- a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss @@ -1,11 +1,12 @@ @import '../../../../../assets/styles/variables'; @import '../../../../../assets/styles/red-mixins'; -.header-item { - padding: 0 24px 0 10px; +.bulk-actions { + display: flex; + align-items: center; - iqser-circle-button:not(:last-child) { - margin-right: 4px !important; + > *:not(:last-child) { + margin-right: 2px; } } diff --git a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts index e26cf9d5d..64c049be6 100644 --- a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts @@ -10,20 +10,49 @@ import { DossiersService } from '../../../dossier/services/dossiers.service'; import { AdminDialogService } from '../../services/admin-dialog.service'; import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { Observable } from 'rxjs'; +import { distinctUntilChanged, map } from 'rxjs/operators'; const HOURS_IN_A_DAY = 24; const MINUTES_IN_AN_HOUR = 60; +interface DossierListItem extends Dossier { + readonly canRestore: boolean; +} + @Component({ templateUrl: './trash-screen.component.html', styleUrls: ['./trash-screen.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [...DefaultListingServices, DossiersService] }) -export class TrashScreenComponent extends BaseListingComponent implements OnInit { - readonly itemSize = 80; - protected readonly _primaryKey = 'dossierName'; +export class TrashScreenComponent extends BaseListingComponent implements OnInit { readonly circleButtonTypes = CircleButtonTypes; + readonly itemSize = 80; + readonly tableColConfigs: TableColConfig[] = [ + { + label: _('trash.table-col-names.name'), + withSort: true, + column: 'dossierName' + }, + { + label: _('trash.table-col-names.owner'), + class: 'user-column' + }, + { + label: _('trash.table-col-names.deleted-on'), + withSort: true, + column: 'softDeletedTime' + }, + { + label: _('trash.table-col-names.time-to-restore'), + withSort: true, + column: 'softDeletedTime' + } + ]; + readonly canRestoreSelected$ = this._canRestoreSelected$; + protected readonly _primaryKey = 'dossierName'; + protected readonly _tableHeaderLabel = 'trash.table-header.title'; private readonly _deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS); constructor( @@ -37,51 +66,22 @@ export class TrashScreenComponent extends BaseListingComponent implemen super(_injector); } + private get _canRestoreSelected$(): Observable { + return this.screenStateService.selectedEntities$.pipe( + map(entities => entities.length && this._canRestore()), + distinctUntilChanged() + ); + } + async ngOnInit(): Promise { - this._loadingService.start(); - - await this.loadDossierTemplatesData(); - - this._loadingService.stop(); - } - - async loadDossierTemplatesData(): Promise { - this.entitiesService.setEntities(await this._dossiersService.getDeleted()); - } - - canRestore(softDeletedTime: string): boolean { - const date = moment(this.getRestoreDate(softDeletedTime)); - const now = new Date(Date.now()); - - const daysLeft = date.diff(now, 'days'); - const hoursFromNow = date.diff(now, 'hours'); - const hoursLeft = hoursFromNow - HOURS_IN_A_DAY * daysLeft; - const minutesFromNow = date.diff(now, 'minutes'); - const minutesLeft = minutesFromNow - HOURS_IN_A_DAY * MINUTES_IN_AN_HOUR * daysLeft; - - return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft > 0; + this._loadingService.loadWhile(this._loadDossiersData()); } getRestoreDate(softDeletedTime: string): string { return moment(softDeletedTime).add(this._deleteRetentionHours, 'hours').toISOString(); } - bulkDelete(dossiers = this.entitiesService.selected) { - this._loadingService.loadWhile(this._hardDelete(dossiers)); - } - - bulkRestore(dossierIds = this.entitiesService.selected.map(d => d.dossierId)) { - this._loadingService.loadWhile(this._restore(dossierIds)); - } - - private async _restore(dossierIds: string[]): Promise { - this._loadingService.start(); - await this._dossiersService.restore(dossierIds); - this._removeFromList(dossierIds); - this._loadingService.stop(); - } - - private async _hardDelete(dossiers: Dossier[]): Promise { + hardDelete(dossiers = this.screenStateService.selectedEntities) { const period = this._appConfigService.getConfig('DELETE_RETENTION_HOURS'); const data = new ConfirmationDialogInput({ title: dossiers.length > 1 ? _('confirmation-dialog.delete-dossier.title-alt') : _('confirmation-dialog.delete-dossier.title'), @@ -94,14 +94,58 @@ export class TrashScreenComponent extends BaseListingComponent implemen translateParams: { dossierName: dossiers[0].dossierName, period: period } }); this._adminDialogService.openDialog('confirm', null, data, async () => { - this._loadingService.start(); - const dossierIds = dossiers.map(d => d.dossierId); - await this._dossiersService.hardDelete(dossierIds); - this._removeFromList(dossierIds); - this._loadingService.stop(); + this._loadingService.loadWhile(this._hardDelete(dossiers)); }); } + restore(dossiers = this.screenStateService.selectedEntities) { + this._loadingService.loadWhile(this._restore(dossiers)); + } + + private async _loadDossiersData(): Promise { + this.screenStateService.setEntities(this._toListItems(await this._dossiersService.getDeleted())); + } + + private _canRestore(dossiers = this.screenStateService.selectedEntities): boolean { + return dossiers.reduce((prev, dossier) => prev && dossier.canRestore, true); + } + + private _canRestoreDossier(dossier: Dossier): boolean { + const date = moment(this.getRestoreDate(dossier.softDeletedTime)); + const now = new Date(Date.now()); + + const daysLeft = date.diff(now, 'days'); + const hoursFromNow = date.diff(now, 'hours'); + const hoursLeft = hoursFromNow - HOURS_IN_A_DAY * daysLeft; + const minutesFromNow = date.diff(now, 'minutes'); + const minutesLeft = minutesFromNow - HOURS_IN_A_DAY * MINUTES_IN_AN_HOUR * daysLeft; + + return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft > 0; + } + + private _toListItems(dossiers: Dossier[]): DossierListItem[] { + return dossiers.map(dossier => this._toListItem(dossier)); + } + + private _toListItem(dossier: Dossier): DossierListItem { + return { + ...dossier, + canRestore: this._canRestoreDossier(dossier) + }; + } + + private async _restore(dossiers: DossierListItem[]): Promise { + const dossierIds = dossiers.map(d => d.dossierId); + await this._dossiersService.restore(dossierIds); + this._removeFromList(dossierIds); + } + + private async _hardDelete(dossiers: DossierListItem[]) { + const dossierIds = dossiers.map(d => d.dossierId); + await this._dossiersService.hardDelete(dossierIds); + this._removeFromList(dossierIds); + } + private _removeFromList(ids: string[]): void { const entities = this.entitiesService.all.filter(e => !ids.includes(e.dossierId)); this.entitiesService.setEntities(entities); diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts index 1632d77ee..d270095ce 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts @@ -41,7 +41,7 @@ export class DossierListingScreenComponent extends BaseListingComponent implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach { - readonly itemSize = 95; + readonly itemSize = 85; protected readonly _primaryKey = 'dossierName'; readonly currentUser = this._userService.currentUser; readonly tableHeaderLabel = _('dossier-listing.table-header.title'); diff --git a/apps/red-ui/src/app/modules/shared/components/table-header/table-header.component.html b/apps/red-ui/src/app/modules/shared/components/table-header/table-header.component.html index 447aaded7..d97625d55 100644 --- a/apps/red-ui/src/app/modules/shared/components/table-header/table-header.component.html +++ b/apps/red-ui/src/app/modules/shared/components/table-header/table-header.component.html @@ -15,7 +15,12 @@ -
+
- - B99F3D5E-879A-47E3-A8C7-84877EE418F3 - + diff --git a/apps/red-ui/src/assets/icons/general/put-back.svg b/apps/red-ui/src/assets/icons/general/put-back.svg index 96da3dae5..90c10a820 100644 --- a/apps/red-ui/src/assets/icons/general/put-back.svg +++ b/apps/red-ui/src/assets/icons/general/put-back.svg @@ -1,7 +1,6 @@ - 711C9D82-CAA8-47BE-954A-A9DA22CE85E6