RED-1817: fixed access permissions to settings screens

This commit is contained in:
Adina Țeudan 2021-07-15 12:24:16 +03:00
parent d30e74e14c
commit 1c81469e6b
18 changed files with 149 additions and 308 deletions

View File

@ -111,14 +111,19 @@ const routes = [
{ path: '', redirectTo: 'dictionaries', pathMatch: 'full' }
]
}
]
],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_MANAGER', 'RED_ADMIN']
}
},
{
path: 'users',
component: UserListingScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_USER_ADMIN']
}
},
{
@ -126,7 +131,8 @@ const routes = [
component: LicenseInformationScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_ADMIN']
}
},
{
@ -134,7 +140,8 @@ const routes = [
component: DigitalSignatureScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_ADMIN']
}
},
{
@ -142,7 +149,8 @@ const routes = [
component: AuditScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_ADMIN']
}
},
{
@ -150,7 +158,8 @@ const routes = [
component: SmtpConfigScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_ADMIN']
}
},
{

View File

@ -4,7 +4,8 @@
*ngIf="
(!item.onlyAdmin || permissionsService.isAdmin()) &&
(!item.onlyDevMode || userPreferenceService.areDevFeaturesEnabled) &&
(!item.userManagerOnly || permissionsService.canManageUsers())
(!item.userManagerOnly || permissionsService.canManageUsers()) &&
(!item.onlyManager || permissionsService.isManager())
"
[routerLinkActiveOptions]="{ exact: false }"
[routerLink]="prefix + item.screen"

View File

@ -16,12 +16,13 @@ export class AdminSideNavComponent {
screen: string;
onlyDevMode?: boolean;
onlyAdmin?: boolean;
onlyManager?: boolean;
userManagerOnly?: boolean;
label?: string;
}[];
} = {
settings: [
{ screen: 'dossier-templates', onlyAdmin: true },
{ screen: 'dossier-templates', onlyManager: true },
{ screen: 'digital-signature', onlyAdmin: true },
{ screen: 'license-info', label: 'license-information', onlyAdmin: true },
{ screen: 'audit', onlyAdmin: true },
@ -33,9 +34,9 @@ export class AdminSideNavComponent {
{ screen: 'rules', onlyDevMode: true, label: 'rule-editor' },
{ screen: 'default-colors' },
{ screen: 'watermark' },
{ screen: 'file-attributes', onlyAdmin: true },
{ screen: 'dossier-attributes', onlyAdmin: true },
{ screen: 'reports', onlyAdmin: true, onlyDevMode: true }
{ screen: 'file-attributes' },
{ screen: 'dossier-attributes' },
{ screen: 'reports', onlyDevMode: true }
]
};

View File

@ -7,10 +7,7 @@
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'file-attributes-csv-import.table-header.title'
| translate: { length: allEntities.length }
}}
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: allEntities.length } }}
</span>
<ng-container *ngIf="areSomeEntitiesSelected$ | async">
@ -49,11 +46,7 @@
</mat-menu>
<mat-menu #typeMenu="matMenu" class="padding-bottom-8">
<button
(click)="setAttributeForSelection('type', type)"
*ngFor="let type of typeOptions"
mat-menu-item
>
<button (click)="setAttributeForSelection('type', type)" *ngFor="let type of typeOptions" mat-menu-item>
{{ 'file-attribute-types.' + type | translate }}
</button>
</mat-menu>
@ -63,14 +56,9 @@
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
class="name"
label="file-attributes-csv-import.table-col-names.name"
></redaction-table-col-name>
<redaction-table-col-name class="name" label="file-attributes-csv-import.table-col-names.name"></redaction-table-col-name>
<redaction-table-col-name
label="file-attributes-csv-import.table-col-names.type"
></redaction-table-col-name>
<redaction-table-col-name label="file-attributes-csv-import.table-col-names.type"></redaction-table-col-name>
<redaction-table-col-name
class="flex-center"
@ -89,11 +77,7 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length === 0"
icon="red:attribute"
screen="file-attributes-csv-import"
></redaction-empty-state>
<redaction-empty-state *ngIf="noData" icon="red:attribute" screen="file-attributes-csv-import"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<!-- Table lines -->
@ -110,10 +94,7 @@
<div *ngIf="!field.editingName">
{{ field.name }}
</div>
<form
(submit)="field.editingName = false; field.name = field.temporaryName"
*ngIf="field.editingName"
>
<form (submit)="field.editingName = false; field.name = field.temporaryName" *ngIf="field.editingName">
<div class="red-input-group w-200">
<input [(ngModel)]="field.temporaryName" name="name" />
</div>
@ -156,10 +137,7 @@
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
</div>
<div class="center">
<redaction-round-checkbox
(click)="togglePrimary(field)"
[active]="field.primaryAttribute"
></redaction-round-checkbox>
<redaction-round-checkbox (click)="togglePrimary(field)" [active]="field.primaryAttribute"></redaction-round-checkbox>
</div>
<div class="actions-container">
<div class="action-buttons">

View File

@ -35,7 +35,7 @@
<redaction-circle-button
(action)="openConfirmDeleteAttributeDialog($event)"
*ngIf="areSomeEntitiesSelected$ | async"
*ngIf="permissionsService.isAdmin() && areSomeEntitiesSelected$ | async"
icon="red:trash"
tooltip="dossier-attributes-listing.bulk.delete"
type="dark-bg"
@ -50,6 +50,7 @@
<redaction-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="permissionsService.isAdmin()"
icon="red:plus"
text="dossier-attributes-listing.add-new"
type="primary"
@ -81,17 +82,13 @@
<redaction-empty-state
(action)="openAddEditAttributeDialog($event)"
*ngIf="(allEntities$ | async)?.length === 0"
[showButton]="true"
*ngIf="noData"
[showButton]="permissionsService.isAdmin()"
icon="red:attribute"
screen="dossier-attributes-listing"
></redaction-empty-state>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async)?.length === 0"
screen="dossier-attributes-listing"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch" screen="dossier-attributes-listing" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<div
@ -114,7 +111,7 @@
{{ 'dossier-attribute-types.' + attribute.type | translate }}
</div>
<div class="actions-container">
<div class="action-buttons">
<div *ngIf="permissionsService.isAdmin()" class="action-buttons">
<redaction-circle-button
(action)="openAddEditAttributeDialog($event, attribute)"
icon="red:edit"

View File

@ -9,6 +9,7 @@ import { ScreenNames, SortingService } from '../../../../services/sorting.servic
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { PermissionsService } from '@services/permissions.service';
@Component({
templateUrl: './dossier-attributes-listing-screen.component.html',
@ -22,7 +23,8 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService,
private readonly _dossierAttributesService: DossierAttributesControllerService
private readonly _dossierAttributesService: DossierAttributesControllerService,
readonly permissionsService: PermissionsService
) {
super(_injector);
this._searchService.setSearchKey('label');

View File

@ -23,17 +23,12 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'dossier-templates-listing.table-header.title'
| translate: { length: (displayedEntities$ | async).length }
}}
{{ 'dossier-templates-listing.table-header.title' | translate: { length: (displayedEntities$ | async).length } }}
</span>
<ng-container *ngIf="areSomeEntitiesSelected$ | async">
@ -55,10 +50,7 @@
<redaction-icon-button
(action)="openAddDossierTemplateDialog()"
*ngIf="
permissionsService.isAdmin() &&
userPreferenceService.areDevFeaturesEnabled
"
*ngIf="permissionsService.isAdmin() && userPreferenceService.areDevFeaturesEnabled"
icon="red:plus"
text="dossier-templates-listing.add-new"
type="primary"
@ -66,11 +58,7 @@
</div>
</div>
<div
[class.no-data]="(allEntities$ | async)?.length === 0"
class="table-header"
redactionSyncWidth="table-item"
>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -101,37 +89,20 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length === 0"
icon="red:template"
screen="dossier-templates-listing"
></redaction-empty-state>
<redaction-empty-state *ngIf="noData" icon="red:template" screen="dossier-templates-listing"></redaction-empty-state>
<redaction-empty-state
*ngIf="
(allEntities$ | async)?.length && (displayedEntities$ | async).length === 0
"
screen="dossier-templates-listing"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch" screen="dossier-templates-listing" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
*cdkVirtualFor="
let dossierTemplate of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column
let dossierTemplate of displayedEntities$ | async | sortBy: sortingOption.order:sortingOption.column
"
[routerLink]="[dossierTemplate.dossierTemplateId, 'dictionaries']"
class="table-item pointer"
>
<div
(click)="toggleEntitySelected($event, dossierTemplate)"
class="selection-column"
>
<redaction-round-checkbox
[active]="isSelected(dossierTemplate)"
></redaction-round-checkbox>
<div (click)="toggleEntitySelected($event, dossierTemplate)" class="selection-column">
<redaction-round-checkbox [active]="isSelected(dossierTemplate)"></redaction-round-checkbox>
</div>
<div>
@ -142,19 +113,14 @@
<div>
<mat-icon svgIcon="red:dictionary"></mat-icon>
{{
'dossier-templates-listing.dictionaries'
| translate
: { length: dossierTemplate.dictionariesCount }
'dossier-templates-listing.dictionaries' | translate: { length: dossierTemplate.dictionariesCount }
}}
</div>
</div>
</div>
<div class="user-column">
<redaction-initials-avatar
[userId]="dossierTemplate.createdBy"
[withName]="true"
></redaction-initials-avatar>
<redaction-initials-avatar [userId]="dossierTemplate.createdBy" [withName]="true"></redaction-initials-avatar>
</div>
<div class="small-label">
{{ dossierTemplate.dateAdded | date: 'd MMM. yyyy' }}

View File

@ -25,22 +25,17 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'file-attributes-listing.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length }
}}
{{ 'file-attributes-listing.table-header.title' | translate: { length: (displayedEntities$ | async)?.length } }}
</span>
<redaction-circle-button
(click)="openConfirmDeleteAttributeDialog($event)"
*ngIf="areSomeEntitiesSelected$ | async"
*ngIf="permissionsService.isAdmin() && areSomeEntitiesSelected$ | async"
icon="red:trash"
tooltip="file-attributes-listing.bulk-actions.delete"
type="dark-bg"
@ -57,6 +52,7 @@
<redaction-circle-button
(action)="fileInput.click()"
*ngIf="permissionsService.isAdmin()"
icon="red:upload"
tooltip="file-attributes-listing.upload-csv"
tooltipPosition="above"
@ -65,6 +61,7 @@
<redaction-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="permissionsService.isAdmin()"
icon="red:plus"
text="file-attributes-listing.add-new"
type="primary"
@ -72,11 +69,7 @@
</div>
</div>
<div
[class.no-data]="(allEntities$ | async)?.length === 0"
class="table-header"
redactionSyncWidth="table-item"
>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -118,26 +111,14 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length === 0"
icon="red:attribute"
screen="file-attributes-listing"
></redaction-empty-state>
<redaction-empty-state *ngIf="noData" icon="red:attribute" screen="file-attributes-listing"></redaction-empty-state>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async)?.length === 0"
screen="file-attributes-listing"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch" screen="file-attributes-listing" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div
*cdkVirtualFor="
let attribute of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column
"
*cdkVirtualFor="let attribute of displayedEntities$ | async | sortBy: sortingOption.order:sortingOption.column"
class="table-item"
>
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">
@ -165,7 +146,7 @@
<redaction-round-checkbox *ngIf="attribute.primaryAttribute" [active]="true" [size]="18"></redaction-round-checkbox>
</div>
<div class="actions-container">
<div class="action-buttons">
<div *ngIf="permissionsService.isAdmin()" class="action-buttons">
<redaction-circle-button
(action)="openAddEditAttributeDialog($event, attribute)"
icon="red:edit"

View File

@ -46,6 +46,7 @@
<div class="heading" translate="reports-screen.report-documents"></div>
<redaction-circle-button
(action)="fileInput.click()"
*ngIf="permissionsService.isAdmin()"
icon="red:upload"
tooltip="reports-screen.upload-document"
></redaction-circle-button>
@ -53,7 +54,7 @@
<div
(click)="fileInput.click()"
*ngIf="!availableTemplates?.length"
*ngIf="permissionsService.isAdmin() && !availableTemplates?.length"
class="template upload-button"
translate="reports-screen.upload-document"
></div>
@ -70,6 +71,7 @@
></redaction-circle-button>
<redaction-circle-button
(action)="deleteTemplate(template)"
*ngIf="permissionsService.isAdmin()"
[iconSize]="12"
[size]="18"
icon="red:trash"

View File

@ -21,6 +21,7 @@
align-items: center;
padding-left: 8px;
margin-bottom: 8px;
min-height: 34px;
}
.template {

View File

@ -5,6 +5,7 @@ import { ReportTemplate, ReportTemplateControllerService } from '@redaction/red-
import { download } from '../../../../utils/file-download-utils';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { PermissionsService } from '../../../../services/permissions.service';
@Component({
selector: 'redaction-reports-screen',
@ -22,7 +23,8 @@ export class ReportsScreenComponent implements OnInit {
private readonly _appStateService: AppStateService,
private readonly _reportTemplateService: ReportTemplateControllerService,
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService
private readonly _loadingService: LoadingService,
readonly permissionsService: PermissionsService
) {
this._appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}

View File

@ -1,10 +1,7 @@
<section>
<div class="overlay-shadow"></div>
<redaction-page-header
[pageLabel]="'trash.label' | translate"
[showCloseButton]="true"
></redaction-page-header>
<redaction-page-header [pageLabel]="'trash.label' | translate" [showCloseButton]="true"></redaction-page-header>
<div class="red-content-inner">
<div class="content-container">
@ -13,105 +10,80 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'trash.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length }
}}
{{ 'trash.table-header.title' | translate: { length: (displayedEntities$ | async)?.length } }}
</span>
<redaction-circle-button
(action)="bulkRestore()"
*ngIf="areSomeEntitiesSelected$ | async"
icon="red:put-back"
[tooltip]="'trash.bulk.restore' | translate"
icon="red:put-back"
type="dark-bg"
></redaction-circle-button>
<redaction-circle-button
(action)="bulkDelete()"
*ngIf="areSomeEntitiesSelected$ | async"
icon="red:trash"
[tooltip]="'trash.bulk.delete' | translate"
icon="red:trash"
type="dark-bg"
></redaction-circle-button>
</div>
<div
[class.no-data]="(allEntities$ | async)?.length === 0"
class="table-header"
redactionSyncWidth="table-item"
>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[label]="'trash.table-col-names.name' | translate"
[withSort]="true"
column="name"
[label]="'trash.table-col-names.name' | translate"
></redaction-table-col-name>
<redaction-table-col-name
class="user-column"
[label]="'trash.table-col-names.owner' | translate"
class="user-column"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[label]="'trash.table-col-names.deleted-on' | translate"
[withSort]="true"
column="dateDeleted"
[label]="'trash.table-col-names.deleted-on' | translate"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[label]="'trash.table-col-names.time-to-restore' | translate"
[withSort]="true"
column="timeToRestore"
[label]="'trash.table-col-names.time-to-restore' | translate"
></redaction-table-col-name>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length === 0"
icon="red:template"
screen="trash"
></redaction-empty-state>
<redaction-empty-state *ngIf="noData" icon="red:template" screen="trash"></redaction-empty-state>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async)?.length === 0"
screen="trash"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch" screen="trash" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="
let entity of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column;
let entity of displayedEntities$ | async | sortBy: sortingOption.order:sortingOption.column;
trackBy: trackById
"
class="table-item"
>
<div (click)="toggleEntitySelected($event, entity)" class="selection-column">
<redaction-round-checkbox
[active]="isSelected(entity)"
></redaction-round-checkbox>
<redaction-round-checkbox [active]="isSelected(entity)"></redaction-round-checkbox>
</div>
<div class="filename">
<div
class="table-item-title heading"
[matTooltip]="entity.dossierName"
matTooltipPosition="above"
>
<div [matTooltip]="entity.dossierName" class="table-item-title heading" matTooltipPosition="above">
{{ entity.dossierName }}
</div>
<div class="small-label stats-subtitle">
@ -131,10 +103,7 @@
</div>
<div class="user-column">
<redaction-initials-avatar
[userId]="entity.ownerId"
[withName]="true"
></redaction-initials-avatar>
<redaction-initials-avatar [userId]="entity.ownerId" [withName]="true"></redaction-initials-avatar>
</div>
<div class="small-label">
@ -148,15 +117,15 @@
<div class="action-buttons">
<redaction-circle-button
(action)="bulkRestore([entity.dossierId])"
icon="red:put-back"
[tooltip]="'trash.action.restore' | translate"
icon="red:put-back"
type="dark-bg"
></redaction-circle-button>
<redaction-circle-button
(action)="bulkDelete([entity.dossierId])"
icon="red:trash"
[tooltip]="'trash.action.delete' | translate"
icon="red:trash"
type="dark-bg"
></redaction-circle-button>
</div>

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { Dossier, DossierTemplateModel, StatusControllerService } from '@redaction/red-ui-http';
import { Dossier, StatusControllerService } from '@redaction/red-ui-http';
import { LoadingService } from '../../../../services/loading.service';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import * as moment from 'moment';
@ -11,7 +11,6 @@ import { ScreenStateService } from '../../../shared/services/screen-state.servic
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { tap } from 'rxjs/operators';
@Component({
templateUrl: './trash-screen.component.html',
@ -21,9 +20,7 @@ import { tap } from 'rxjs/operators';
})
export class TrashScreenComponent extends BaseListingComponent<Dossier> implements OnInit {
readonly itemSize = 85;
private readonly _deleteRetentionHours = this._appConfigService.getConfig(
AppConfigKey.DELETE_RETENTION_HOURS
);
private readonly _deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS);
constructor(
private readonly _appStateService: AppStateService,

View File

@ -10,10 +10,7 @@
<div class="content-container">
<div class="header-item">
<span class="all-caps-label">
{{
'dossier-listing.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length || 0 }
}}
{{ 'dossier-listing.table-header.title' | translate: { length: (displayedEntities$ | async)?.length || 0 } }}
</span>
<redaction-quick-filters></redaction-quick-filters>
@ -28,53 +25,33 @@
label="dossier-listing.table-col-names.name"
></redaction-table-col-name>
<redaction-table-col-name
label="dossier-listing.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name label="dossier-listing.table-col-names.needs-work"></redaction-table-col-name>
<redaction-table-col-name
class="user-column"
label="dossier-listing.table-col-names.owner"
></redaction-table-col-name>
<redaction-table-col-name class="user-column" label="dossier-listing.table-col-names.owner"></redaction-table-col-name>
<redaction-table-col-name
class="flex-end"
label="dossier-listing.table-col-names.status"
></redaction-table-col-name>
<redaction-table-col-name class="flex-end" label="dossier-listing.table-col-names.status"></redaction-table-col-name>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
(action)="openAddDossierDialog()"
*ngIf="(allEntities$ | async)?.length === 0"
*ngIf="noData"
[showButton]="permissionsService.isManager()"
icon="red:folder"
screen="dossier-listing"
></redaction-empty-state>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async)?.length === 0"
screen="dossier-listing"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch" screen="dossier-listing" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="
let dw of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column
"
*cdkVirtualFor="let dw of displayedEntities$ | async | sortBy: sortingOption.order:sortingOption.column"
[class.pointer]="!!dw"
[routerLink]="[!!dw ? '/main/dossiers/' + dw.dossier.dossierId : []]"
class="table-item"
>
<div class="filename">
<div
[matTooltip]="dw.dossierName"
class="table-item-title heading"
matTooltipPosition="above"
>
<div [matTooltip]="dw.dossierName" class="table-item-title heading" matTooltipPosition="above">
{{ dw.dossierName }}
</div>
<div class="small-label stats-subtitle">
@ -107,15 +84,10 @@
</div>
</div>
<div>
<redaction-needs-work-badge
[needsWorkInput]="dw"
></redaction-needs-work-badge>
<redaction-needs-work-badge [needsWorkInput]="dw"></redaction-needs-work-badge>
</div>
<div class="user-column">
<redaction-initials-avatar
[userId]="dw.dossier.ownerId"
[withName]="true"
></redaction-initials-avatar>
<redaction-initials-avatar [userId]="dw.dossier.ownerId" [withName]="true"></redaction-initials-avatar>
</div>
<div class="status-container">
<redaction-dossier-listing-actions
@ -127,10 +99,7 @@
</div>
</cdk-virtual-scroll-viewport>
<redaction-scroll-button
[itemSize]="itemSize"
[scrollViewport]="scrollViewport"
></redaction-scroll-button>
<redaction-scroll-button [itemSize]="itemSize" [scrollViewport]="scrollViewport"></redaction-scroll-button>
</div>
<div class="right-container" redactionHasScrollbar>

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Component, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Dossier, DossierTemplateModel } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { UserService } from '@services/user.service';
@ -76,6 +76,18 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
this._loadEntitiesFromState();
}
private get _user() {
return this._userService.user;
}
private get _activeDossiersCount(): number {
return this._screenStateService.entities.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
}
private get _inactiveDossiersCount(): number {
return this._screenStateService.entities.length - this._activeDossiersCount;
}
ngOnInit(): void {
this.calculateData();
@ -135,22 +147,6 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
});
}
private _loadEntitiesFromState() {
this._screenStateService.setEntities(this._appStateService.allDossiers);
}
private get _user() {
return this._userService.user;
}
private get _activeDossiersCount(): number {
return this._screenStateService.entities.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
}
private get _inactiveDossiersCount(): number {
return this._screenStateService.entities.length - this._activeDossiersCount;
}
calculateData() {
this._computeAllFilters();
@ -173,6 +169,10 @@ export class DossierListingScreenComponent extends BaseListingComponent<DossierW
this.documentsChartData = this._translateChartService.translateStatus(this.documentsChartData);
}
private _loadEntitiesFromState() {
this._screenStateService.setEntities(this._appStateService.allDossiers);
}
private _computeAllFilters() {
const allDistinctFileStatus = new Set<string>();
const allDistinctPeople = new Set<string>();

View File

@ -1,8 +1,8 @@
<section *ngIf="!!activeDossier">
<redaction-page-header
[actionConfigs]="actionConfigs"
[showCloseButton]="true"
[searchPlaceholder]="'dossier-overview.search' | translate"
[showCloseButton]="true"
>
<redaction-file-download-btn
[disabled]="areSomeEntitiesSelected$ | async"
@ -24,9 +24,9 @@
<redaction-circle-button
(action)="fileInput.click()"
[tooltip]="'dossier-overview.header-actions.upload-document' | translate"
class="ml-14"
icon="red:upload"
[tooltip]="'dossier-overview.header-actions.upload-document' | translate"
tooltipPosition="below"
type="primary"
></redaction-circle-button>
@ -41,17 +41,12 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{
'dossier-overview.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length || 0 }
}}
{{ 'dossier-overview.table-header.title' | translate: { length: (displayedEntities$ | async)?.length || 0 } }}
</span>
<redaction-dossier-overview-bulk-actions
@ -62,11 +57,7 @@
<redaction-quick-filters></redaction-quick-filters>
</div>
<div
[class.no-data]="(allEntities$ | async).length === 0"
class="table-header"
redactionSyncWidth="table-item"
>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<!-- Table column names-->
<div class="select-oval-placeholder"></div>
@ -86,9 +77,7 @@
label="dossier-overview.table-col-names.added-on"
></redaction-table-col-name>
<redaction-table-col-name
label="dossier-overview.table-col-names.needs-work"
></redaction-table-col-name>
<redaction-table-col-name label="dossier-overview.table-col-names.needs-work"></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
@ -120,24 +109,18 @@
<redaction-empty-state
(action)="fileInput.click()"
*ngIf="(allEntities$ | async)?.length === 0"
*ngIf="noData"
buttonIcon="red:upload"
icon="red:document"
screen="dossier-overview"
></redaction-empty-state>
<redaction-empty-state
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async).length === 0"
screen="dossier-overview"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch" screen="dossier-overview" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="
let fileStatus of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column;
let fileStatus of displayedEntities$ | async | sortBy: sortingOption.order:sortingOption.column;
trackBy: trackByFileId
"
[class.disabled]="fileStatus.isExcluded"
@ -146,13 +129,8 @@
[routerLink]="fileLink(fileStatus)"
class="table-item"
>
<div
(click)="toggleEntitySelected($event, fileStatus)"
class="selection-column"
>
<redaction-round-checkbox
[active]="isSelected(fileStatus)"
></redaction-round-checkbox>
<div (click)="toggleEntitySelected($event, fileStatus)" class="selection-column">
<redaction-round-checkbox [active]="isSelected(fileStatus)"></redaction-round-checkbox>
</div>
<div>
@ -169,19 +147,13 @@
</div>
<div *ngIf="fileStatus.primaryAttribute" class="small-label stats-subtitle">
<div class="primary-attribute text-overflow">
<span
[matTooltip]="fileStatus.primaryAttribute"
matTooltipPosition="above"
>
<span [matTooltip]="fileStatus.primaryAttribute" matTooltipPosition="above">
{{ fileStatus.primaryAttribute }}
</span>
</div>
</div>
<div *ngIf="fileStatus.ocrTime" class="small-label stats-subtitle">
<div
[matTooltipPosition]="'above'"
[matTooltip]="'dossier-overview.ocr-performed' | translate"
>
<div [matTooltipPosition]="'above'" [matTooltip]="'dossier-overview.ocr-performed' | translate">
<mat-icon svgIcon="red:ocr"></mat-icon>
{{ fileStatus.ocrTime | date: 'mediumDate' }}
</div>
@ -196,23 +168,14 @@
<!-- always show A for error-->
<div *ngIf="fileStatus.isError">
<redaction-annotation-icon
color="#dd4d50"
label="A"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon color="#dd4d50" label="A" type="square"></redaction-annotation-icon>
</div>
<div *ngIf="!fileStatus.isError">
<redaction-needs-work-badge
[needsWorkInput]="fileStatus"
></redaction-needs-work-badge>
<redaction-needs-work-badge [needsWorkInput]="fileStatus"></redaction-needs-work-badge>
</div>
<div *ngIf="!fileStatus.isError" class="user-column">
<redaction-initials-avatar
[userId]="fileStatus.currentReviewer"
[withName]="true"
></redaction-initials-avatar>
<redaction-initials-avatar [userId]="fileStatus.currentReviewer" [withName]="true"></redaction-initials-avatar>
</div>
<div *ngIf="!fileStatus.isError">
@ -259,10 +222,7 @@
</div>
</cdk-virtual-scroll-viewport>
<redaction-scroll-button
[itemSize]="itemSize"
[scrollViewport]="scrollViewport"
></redaction-scroll-button>
<redaction-scroll-button [itemSize]="itemSize" [scrollViewport]="scrollViewport"></redaction-scroll-button>
</div>
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
@ -279,10 +239,4 @@
<redaction-type-filter [filter]="filter"></redaction-type-filter>
</ng-template>
<input
#fileInput
(change)="uploadFiles($event.target['files'])"
class="file-upload-input"
multiple="true"
type="file"
/>
<input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" />

View File

@ -36,6 +36,10 @@ export abstract class BaseListingComponent<T> {
return this._screenStateService.entities$;
}
get displayedEntities(): T[] {
return this._screenStateService.displayedEntities;
}
get allEntities(): T[] {
return this._screenStateService.entities;
}
@ -52,14 +56,22 @@ export abstract class BaseListingComponent<T> {
return this._sortingService.getSortingOption();
}
getFilter$(slug: string): Observable<FilterModel[]> {
return this.filterService.getFilter$(slug);
}
get searchForm() {
return this._searchService.searchForm;
}
get noMatch(): boolean {
return this.allEntities.length && this.displayedEntities?.length === 0;
}
get noData(): boolean {
return this.allEntities.length === 0;
}
getFilter$(slug: string): Observable<FilterModel[]> {
return this.filterService.getFilter$(slug);
}
resetFilters() {
this.filterService.reset();
}

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Component, EventEmitter, Output } from '@angular/core';
import { FilterModel } from '../popup-filter/model/filter.model';
import { FilterService } from '@shared/services/filter.service';