show time to restore

This commit is contained in:
Dan Percic 2021-07-07 20:07:47 +03:00
parent 53d8406c23
commit 7990636066
6 changed files with 160 additions and 60 deletions

View File

@ -1,7 +1,10 @@
<section> <section>
<div class="overlay-shadow"></div> <div class="overlay-shadow"></div>
<redaction-page-header pageLabel="trash.label" [showCloseButton]="true"></redaction-page-header> <redaction-page-header
[pageLabel]="'trash.label' | translate"
[showCloseButton]="true"
></redaction-page-header>
<div class="red-content-inner"> <div class="red-content-inner">
<div class="content-container"> <div class="content-container">
@ -20,22 +23,20 @@
}} }}
</span> </span>
<ng-container *ngIf="areSomeEntitiesSelected"> <redaction-circle-button
<redaction-circle-button *ngIf="areSomeEntitiesSelected"
*ngIf="permissionsService.isAdmin()" icon="red:trash"
icon="red:trash" [tooltip]="'trash.bulk.delete' | translate"
tooltip="trash.bulk.delete" type="dark-bg"
type="dark-bg" ></redaction-circle-button>
></redaction-circle-button>
</ng-container>
<div class="actions flex-1"> <!-- <div class="actions flex-1">-->
<redaction-input-with-action <!-- <redaction-input-with-action-->
[form]="searchForm" <!-- [form]="searchForm"-->
[placeholder]="'trash.search'" <!-- [placeholder]="'trash.search' | translate"-->
type="search" <!-- type="search"-->
></redaction-input-with-action> <!-- ></redaction-input-with-action>-->
</div> <!-- </div>-->
</div> </div>
<div <div
@ -50,25 +51,25 @@
[activeSortingOption]="sortingOption" [activeSortingOption]="sortingOption"
[withSort]="true" [withSort]="true"
column="name" column="name"
label="trash.table-col-names.name" [label]="'trash.table-col-names.name' | translate"
></redaction-table-col-name> ></redaction-table-col-name>
<redaction-table-col-name <redaction-table-col-name
class="user-column" class="user-column"
label="trash.table-col-names.owner" [label]="'trash.table-col-names.owner' | translate"
></redaction-table-col-name> ></redaction-table-col-name>
<redaction-table-col-name <redaction-table-col-name
(toggleSort)="toggleSort($event)" (toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption" [activeSortingOption]="sortingOption"
[withSort]="true" [withSort]="true"
column="dateDeleted" column="dateDeleted"
label="trash.table-col-names.deleted-on" [label]="'trash.table-col-names.deleted-on' | translate"
></redaction-table-col-name> ></redaction-table-col-name>
<redaction-table-col-name <redaction-table-col-name
(toggleSort)="toggleSort($event)" (toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption" [activeSortingOption]="sortingOption"
[withSort]="true" [withSort]="true"
column="timeToRestore" column="timeToRestore"
label="trash.table-col-names.time-to-restore" [label]="'trash.table-col-names.time-to-restore' | translate"
></redaction-table-col-name> ></redaction-table-col-name>
<div class="scrollbar-placeholder"></div> <div class="scrollbar-placeholder"></div>
</div> </div>
@ -88,82 +89,75 @@
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar> <cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<div <div
*cdkVirtualFor=" *cdkVirtualFor="
let dw of displayedEntities let entity of displayedEntities
| sortBy: sortingOption.order:sortingOption.column | sortBy: sortingOption.order:sortingOption.column;
trackBy: trackById
" "
class="table-item" class="table-item"
> >
<div (click)="toggleEntitySelected($event, dw)" class="selection-column"> <div (click)="toggleEntitySelected($event, entity)" class="selection-column">
<redaction-round-checkbox <redaction-round-checkbox
[active]="isSelected(dw)" [active]="isSelected(entity)"
></redaction-round-checkbox> ></redaction-round-checkbox>
</div> </div>
<div class="filename"> <div class="filename">
<div <div
class="table-item-title heading" class="table-item-title heading"
[matTooltip]="dw.dossierName" [matTooltip]="entity.dossierName"
matTooltipPosition="above" matTooltipPosition="above"
> >
{{ dw.dossierName }} {{ entity.dossierName }}
</div> </div>
<div class="small-label stats-subtitle"> <div class="small-label stats-subtitle">
<div> <div>
<mat-icon svgIcon="red:template"></mat-icon> <mat-icon svgIcon="red:template"></mat-icon>
{{ getDossierTemplate(dw).name }} {{ getDossierTemplate(entity.dossierTemplateId).name }}
</div> </div>
</div> </div>
<div class="small-label stats-subtitle"> <div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:document"></mat-icon>
{{ dw.files.length }}
</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{ dw.totalNumberOfPages }}
</div>
<div> <div>
<mat-icon svgIcon="red:user"></mat-icon> <mat-icon svgIcon="red:user"></mat-icon>
{{ dw.numberOfMembers }} {{ entity.memberIds.length }}
</div> </div>
<div> <div>
<mat-icon svgIcon="red:calendar"></mat-icon> <mat-icon svgIcon="red:calendar"></mat-icon>
{{ dw.dossier.date | date: 'mediumDate' }} {{ entity.date | date: 'mediumDate' }}
</div> </div>
<div *ngIf="dw.dossier.dueDate"> <div *ngIf="entity.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon> <mat-icon svgIcon="red:lightning"></mat-icon>
{{ dw.dossier.dueDate | date: 'mediumDate' }} {{ entity.dueDate | date: 'mediumDate' }}
</div> </div>
</div> </div>
</div> </div>
<div class="user-column"> <div class="user-column">
<redaction-initials-avatar <redaction-initials-avatar
[userId]="dw.dossier.ownerId" [userId]="entity.ownerId"
[withName]="true" [withName]="true"
></redaction-initials-avatar> ></redaction-initials-avatar>
</div> </div>
<div class="small-label"> <div class="small-label">
{{ dw.dossierDate | date: 'd MMM. yyyy' }} {{ entity.softDeletedTime | date: 'd MMM. yyyy' }}
</div> </div>
<div> <div>
<div class="small-label"> <div class="small-label">
{{ dw.dossier.date | date: 'd MMM. yyyy' }} {{ getRestoreDate(entity.softDeletedTime) | date: 'timeFromNow' }}
</div> </div>
<div class="action-buttons"> <div class="action-buttons">
<redaction-circle-button <redaction-circle-button
*ngIf="permissionsService.isManager()" (action)="restore(entity)"
icon="red:undo" icon="red:undo"
tooltip="trash.action.restore" [tooltip]="'trash.action.restore' | translate"
type="dark-bg" type="dark-bg"
></redaction-circle-button> ></redaction-circle-button>
<redaction-circle-button <redaction-circle-button
*ngIf="permissionsService.isManager()" (action)="hardDelete(entity)"
icon="red:trash" icon="red:trash"
tooltip="trash.action.delete" [tooltip]="'trash.action.delete' | translate"
type="dark-bg" type="dark-bg"
></redaction-circle-button> ></redaction-circle-button>
</div> </div>

View File

@ -1,42 +1,74 @@
import { Component, Injector, OnInit } from '@angular/core'; import { Component, Injector, OnInit } from '@angular/core';
import { BaseListingComponent } from '@shared/base/base-listing.component'; import { BaseListingComponent } from '@shared/base/base-listing.component';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service'; import { Dossier, DossierControllerService, DossierTemplateModel } from '@redaction/red-ui-http';
import { DossierWrapper } from '@state/model/dossier.wrapper'; import { LoadingService } from '../../../../services/loading.service';
import { DossierTemplateModel } from '@redaction/red-ui-http'; import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import * as moment from 'moment';
import { TranslateService } from '@ngx-translate/core';
@Component({ @Component({
templateUrl: './trash-screen.component.html', templateUrl: './trash-screen.component.html',
styleUrls: ['./trash-screen.component.scss'] styleUrls: ['./trash-screen.component.scss']
}) })
export class TrashScreenComponent extends BaseListingComponent<DossierWrapper> implements OnInit { export class TrashScreenComponent extends BaseListingComponent<Dossier> implements OnInit {
readonly itemSize = 85; readonly itemSize = 85;
private readonly _deleteRetentionHours = this._appConfigService.getConfig(
AppConfigKey.DELETE_RETENTION_HOURS
);
protected readonly _searchKey = 'name'; protected readonly _searchKey = 'dossierName';
protected readonly _selectionKey = 'dossierId'; protected readonly _selectionKey = 'dossierId';
protected readonly _sortKey = 'dossier-listing'; protected readonly _sortKey = 'dossier-listing';
constructor( constructor(
private readonly _dialogService: AdminDialogService,
private readonly _appStateService: AppStateService, private readonly _appStateService: AppStateService,
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
protected readonly _injector: Injector protected readonly _injector: Injector,
private readonly _dossierControllerService: DossierControllerService,
private readonly _loadingService: LoadingService,
private readonly _appConfigService: AppConfigService,
private readonly _translateService: TranslateService
) { ) {
super(_injector); super(_injector);
} }
ngOnInit(): void { async ngOnInit(): Promise<void> {
this.loadDossierTemplatesData(); this._loadingService.start();
await this.loadDossierTemplatesData();
this._loadingService.stop();
} }
loadDossierTemplatesData() { async loadDossierTemplatesData(): Promise<void> {
this.allEntities = this._appStateService.allDossiers; this.allEntities = await this._dossierControllerService.getDeletedDossiers().toPromise();
console.log(this.allEntities);
this._executeSearchImmediately(); this._executeSearchImmediately();
} }
getDossierTemplate(dw: DossierWrapper): DossierTemplateModel { getDossierTemplate(dossierTemplateId: string): DossierTemplateModel {
return this._appStateService.getDossierTemplateById(dw.dossier.dossierTemplateId); return this._appStateService.getDossierTemplateById(dossierTemplateId);
}
getRestoreDate(softDeletedTime: string) {
return moment(softDeletedTime).add(5, 'hours').format();
}
async restore(dossier: Dossier) {
this._loadingService.start();
await this._dossierControllerService.restoreDossiers([dossier.dossierId]).toPromise();
this.allEntities = this.allEntities.filter(e => e !== dossier);
this._loadingService.stop();
}
async hardDelete(dossier: Dossier) {
this._loadingService.start();
await this._dossierControllerService.hardDeleteDossiers([dossier.dossierId]).toPromise();
this.allEntities = this.allEntities.filter(e => e !== dossier);
this._loadingService.stop();
}
trackById(index: number, dossier: Dossier) {
return dossier.dossierId;
} }
} }

View File

@ -0,0 +1,62 @@
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
import * as moment from 'moment';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe as BaseDatePipe } from '@angular/common';
const HOURS_IN_A_DAY = 24;
const MINUTES_IN_AN_HOUR = 60;
@Pipe({
name: 'date'
})
export class DatePipe extends BaseDatePipe implements PipeTransform {
constructor(
@Inject(LOCALE_ID) private readonly _locale: string,
private readonly _translateService: TranslateService
) {
super(_locale);
}
transform(value: null | undefined, format?: string, timezone?: string, locale?: string): null;
transform(
value: Date | string | number | null | undefined,
format?: string,
timezone?: string,
locale?: string
): string | null;
transform(value: any, format?: string, timezone?: string, locale?: string): string {
if (format === 'timeFromNow') return this._getTimeFromNow(value);
return super.transform(value, format, timezone, locale);
}
private _getTimeFromNow(item: string) {
const date = moment(item);
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;
if (daysLeft === 0 && hoursLeft === 0 && minutesLeft > 0)
return this._translate('time.less-than-an-hour');
const hoursSuffix = this._translate(`time.hour${hoursLeft === 1 ? '' : 's'}`);
const hoursDisplay = `${hoursLeft} ${hoursSuffix}`;
if (daysLeft === 0 && hoursLeft > 0) return hoursDisplay;
const daysSuffix = this._translate(`time.day${daysLeft === 1 ? '' : 's'}`);
const daysDisplay = `${daysLeft} ${daysSuffix}`;
if (daysLeft > 0 && hoursLeft > 0) return `${daysDisplay} ${hoursDisplay}`;
if (daysLeft > 0) return daysDisplay;
return this._translate(`time.no-time-left`);
}
private _translate(value: string, params?: { [key: string]: string }) {
return this._translateService.instant(value, params);
}
}

View File

@ -38,6 +38,7 @@ import { PopupFilterComponent } from '@shared/components/filters/popup-filter/po
import { AssignUserDropdownComponent } from './components/assign-user-dropdown/assign-user-dropdown.component'; import { AssignUserDropdownComponent } from './components/assign-user-dropdown/assign-user-dropdown.component';
import { InputWithActionComponent } from '@shared/components/input-with-action/input-with-action.component'; import { InputWithActionComponent } from '@shared/components/input-with-action/input-with-action.component';
import { PageHeaderComponent } from './components/page-header/page-header.component'; import { PageHeaderComponent } from './components/page-header/page-header.component';
import { DatePipe } from '@shared/pipes/date.pipe';
const buttons = [ const buttons = [
ChevronButtonComponent, ChevronButtonComponent,
@ -75,6 +76,7 @@ const components = [
const utils = [ const utils = [
HumanizePipe, HumanizePipe,
DatePipe,
SyncWidthDirective, SyncWidthDirective,
HasScrollbarDirective, HasScrollbarDirective,
NavigateLastDossiersScreenDirective NavigateLastDossiersScreenDirective

View File

@ -1404,5 +1404,13 @@
"text-placeholder": "Enter text" "text-placeholder": "Enter text"
}, },
"title": "Watermark" "title": "Watermark"
},
"time": {
"no-time-left": "Time to restore already passed",
"less-than-an-hour": "< 1 hour",
"hour": "hour",
"hours": "hours",
"day": "day",
"days": "days"
} }
} }

View File

@ -19,10 +19,12 @@ export interface Dossier {
dossierTemplateId?: string; dossierTemplateId?: string;
downloadFileTypes?: Array<Dossier.DownloadFileTypesEnum>; downloadFileTypes?: Array<Dossier.DownloadFileTypesEnum>;
dueDate?: string; dueDate?: string;
hardDeletedTime?: string;
memberIds?: Array<string>; memberIds?: Array<string>;
ownerId?: string; ownerId?: string;
reportTemplateIds?: Array<string>; reportTemplateIds?: Array<string>;
reportTypes?: Array<Dossier.ReportTypesEnum>; reportTypes?: Array<Dossier.ReportTypesEnum>;
softDeletedTime?: string;
status?: Dossier.StatusEnum; status?: Dossier.StatusEnum;
watermarkEnabled?: boolean; watermarkEnabled?: boolean;
} }