diff --git a/apps/red-ui/src/app/app.component.html b/apps/red-ui/src/app/app.component.html index dbdf8372d..5735353bf 100644 --- a/apps/red-ui/src/app/app.component.html +++ b/apps/red-ui/src/app/app.component.html @@ -1,2 +1,2 @@ - + 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 04d84bd9e..764603479 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 { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { LoadingService } from '@services/loading.service'; +import { timer } from 'rxjs'; @Component({ selector: 'redaction-downloads-list-screen', @@ -35,6 +36,9 @@ export class DownloadsListScreenComponent extends ListingComponent { + await this._loadData(); + }); } downloadItem(download: DownloadStatusWrapper) { 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 fc0578ad8..9b5a836fa 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 @@ -74,7 +74,7 @@ export class TrashScreenComponent extends ListingComponent impl hardDelete(dossiers = this.entitiesService.selected) { const data = new ConfirmationDialogInput({ - title: dossiers.length > 1 ? _('confirmation-dialog.delete-dossier.title-alt') : _('confirmation-dialog.delete-dossier.title'), + title: _('confirmation-dialog.delete-dossier.title'), titleColor: TitleColors.PRIMARY, question: _('confirmation-dialog.delete-dossier.question'), // details: _('confirmation-dialog.delete-dossier.details'), @@ -83,7 +83,7 @@ export class TrashScreenComponent extends ListingComponent impl denyText: _('confirmation-dialog.delete-dossier.deny-text'), translateParams: { dossierName: dossiers[0].dossierName, - period: this._deleteRetentionHours + dossiersCount: dossiers.length } }); this._adminDialogService.openDialog('confirm', null, data, async () => { diff --git a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html index e1431f031..c5d74ae01 100644 --- a/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/comments/comments.component.html @@ -1,6 +1,6 @@
-
+
{{ getOwnerName(comment) }} {{ comment.date | date: 'commentDate' }}
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.ts index e678d0a62..f30a06a44 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.ts @@ -38,7 +38,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa get changed() { for (const attr of this.attributes) { - if (this.isDate(attr)) { + if (this.isDate(attr) && attr.value) { if (!moment(attr.value).isSame(moment(this.currentAttrValue(attr)))) { return true; } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html new file mode 100644 index 000000000..942ff9740 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html @@ -0,0 +1,76 @@ + +
+
+ + + + +
+
+ +
+
+ {{ file.filename }} +
+
+
+ + {{ file.numberOfPages }} +
+
+
{{ file.softDeleted | date: 'exactDate' }}
+
+
{{ file.restoreDate | date: 'timeFromNow' }}
+
+ + + +
+
+
+
+
+ + +
+ + + +
+
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss new file mode 100644 index 000000000..b2e5fe998 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss @@ -0,0 +1,41 @@ +@import '../../../../../../assets/styles/variables'; +@import '../../../../../../assets/styles/red-mixins'; + +.instructions { + color: $grey-7; + flex: 1; + text-align: end; +} + +cdk-virtual-scroll-viewport { + height: calc(100% - 81px); + + ::ng-deep.cdk-virtual-scroll-content-wrapper { + grid-template-columns: auto 3fr 1fr 2fr 2fr 11px; + + .table-item > div { + height: 50px; + + &.filename span { + @include line-clamp(1); + } + + &.stats-subtitle > div { + width: fit-content; + } + } + } + + &.has-scrollbar:hover ::ng-deep.cdk-virtual-scroll-content-wrapper { + grid-template-columns: auto 3fr 1fr 2fr 2fr; + } +} + +.bulk-actions { + display: flex; + align-items: center; + + > *:not(:last-child) { + margin-right: 2px; + } +} diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts new file mode 100644 index 000000000..8794c15a5 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts @@ -0,0 +1,141 @@ +import { Component, EventEmitter, Injector, Input, OnInit, Output } from '@angular/core'; +import { EditDossierSectionInterface } from '../edit-dossier-section.interface'; +import { DossierWrapper } from '@state/model/dossier.wrapper'; +import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui'; +import { FileManagementControllerService, FileStatus, StatusControllerService } from '@redaction/red-ui-http'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { LoadingService } from '@services/loading.service'; +import * as moment from 'moment'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; +import { getLeftDateTime } from '@utils/functions'; +import { Observable } from 'rxjs'; +import { distinctUntilChanged, map } from 'rxjs/operators'; +import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; +import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; +import { AppStateService } from '@state/app-state.service'; + +interface FileListItem extends FileStatus { + readonly canRestore: boolean; + readonly restoreDate: string; +} + +@Component({ + selector: 'redaction-edit-dossier-deleted-documents', + templateUrl: './edit-dossier-deleted-documents.component.html', + styleUrls: ['./edit-dossier-deleted-documents.component.scss'], + providers: [...DefaultListingServices] +}) +export class EditDossierDeletedDocumentsComponent extends ListingComponent implements EditDossierSectionInterface, OnInit { + @Input() dossierWrapper: DossierWrapper; + @Output() updateDossier = new EventEmitter(); + readonly changed = false; + readonly canRestoreSelected$ = this._canRestoreSelected$; + disabled: boolean; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.name') }, + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.pages') }, + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.deleted-on') }, + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.time-to-restore') } + ]; + readonly tableHeaderLabel = _('edit-dossier-dialog.deleted-documents.table-header.label'); + readonly itemSize = 50; + readonly circleButtonTypes = CircleButtonTypes; + readonly deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS); + protected readonly _primaryKey = 'fileId'; + + constructor( + protected readonly _injector: Injector, + private readonly _statusController: StatusControllerService, + private readonly _fileManagementController: FileManagementControllerService, + private readonly _appStateService: AppStateService, + private readonly _loadingService: LoadingService, + private readonly _appConfigService: AppConfigService, + private readonly _dialogService: DossiersDialogService + ) { + super(_injector); + } + + private get _canRestoreSelected$(): Observable { + return this.entitiesService.selected$.pipe( + map(entities => entities.length && !entities.find(file => !file.canRestore)), + distinctUntilChanged() + ); + } + + hardDelete(files = this.entitiesService.selected) { + const data = new ConfirmationDialogInput({ + title: _('confirmation-dialog.permanently-delete-file.title'), + titleColor: TitleColors.PRIMARY, + question: _('confirmation-dialog.permanently-delete-file.question'), + confirmationText: _('confirmation-dialog.permanently-delete-file.confirmation-text'), + requireInput: true, + denyText: _('confirmation-dialog.permanently-delete-file.deny-text'), + translateParams: { + fileName: files[0].filename, + filesCount: files.length + } + }); + this._dialogService.openDialog('confirm', null, data, async () => { + this._loadingService.loadWhile(this._hardDelete(files)); + }); + } + + async ngOnInit() { + this._loadingService.start(); + const files = await this._statusController.getDeletedFileStatus(this.dossierWrapper.dossierId).toPromise(); + this.entitiesService.setEntities(this._toListItems(files)); + this._loadingService.stop(); + } + + revert() {} + + save() {} + + restore(files = this.entitiesService.selected) { + this._loadingService.loadWhile(this._restore(files)); + } + + private async _restore(files: FileListItem[]): Promise { + const fileIds = files.map(f => f.fileId); + await this._fileManagementController.restoreFiles(fileIds, this.dossierWrapper.dossierId).toPromise(); + this._removeFromList(fileIds); + await this._appStateService.reloadActiveDossierFiles(); + this.updateDossier.emit(); + } + + private async _hardDelete(files: FileListItem[]) { + const fileIds = files.map(f => f.fileId); + await this._fileManagementController.hardDeleteFile(this.dossierWrapper.dossierId, fileIds).toPromise(); + this._removeFromList(fileIds); + this.updateDossier.emit(); + } + + private _removeFromList(ids: string[]): void { + const entities = this.entitiesService.all.filter(e => !ids.includes(e.fileId)); + this.entitiesService.setEntities(entities); + this.entitiesService.setSelected([]); + } + + private _toListItems(files: FileStatus[]): FileListItem[] { + return files.map(file => this._toListItem(file)); + } + + private _toListItem(file: FileStatus): FileListItem { + const restoreDate = this._getRestoreDate(file.softDeleted); + return { + ...file, + restoreDate, + canRestore: this._canRestoreFile(restoreDate) + }; + } + + private _canRestoreFile(restoreDate: string): boolean { + const { daysLeft, hoursLeft, minutesLeft } = getLeftDateTime(restoreDate); + + return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft > 0; + } + + private _getRestoreDate(softDeletedTime: string): string { + return moment(softDeletedTime).add(this.deleteRetentionHours, 'hours').toISOString(); + } +} diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html index d73c7d9bb..d0819f642 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.html @@ -14,7 +14,7 @@ >
-
+
{{ activeNavItem.title | translate }} @@ -58,9 +58,15 @@ *ngIf="activeNav === 'dossierAttributes'" [dossierWrapper]="dossierWrapper" > + +
-
+