RED-6829: update file data service

This commit is contained in:
Dan Percic 2023-06-09 23:37:01 +03:00
parent e083fa5eb4
commit bf0685339a
75 changed files with 407 additions and 442 deletions

View File

@ -25,7 +25,7 @@ import { TrashGuard } from '@guards/trash.guard';
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
import { DossierFilesGuard } from '@guards/dossier-files-guard';
import { WebViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { mainResolver } from '@utils/main.resolver';
const dossierTemplateIdRoutes: IqserRoutes = [
@ -36,7 +36,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
routeGuards: [DossiersGuard],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
allow: [ROLES.files.readStatus],
allow: [Roles.files.readStatus],
redirectTo: '/auth-error',
},
},
@ -49,7 +49,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
allow: [ROLES.dossierAttributes.read, ROLES.dossierAttributes.readConfig],
allow: [Roles.dossierAttributes.read, Roles.dossierAttributes.readConfig],
redirectTo: '/auth-error',
},
skeleton: 'dossier',
@ -64,7 +64,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
allow: [ROLES.readRedactionLog, ROLES.files.downloadOriginal, ROLES.highlights.read],
allow: [Roles.readRedactionLog, Roles.files.downloadOriginal, Roles.highlights.read],
redirectTo: '/auth-error',
},
},
@ -120,19 +120,19 @@ const mainRoutes: IqserRoutes = [
routeGuards: [IqserAuthGuard, RedRoleGuard, IqserPermissionsGuard, DossierTemplatesGuard, DashboardGuard],
permissions: {
allow: [
ROLES.any,
ROLES.templates.read,
ROLES.fileAttributes.readConfig,
ROLES.watermarks.read,
ROLES.dictionaryTypes.read,
ROLES.colors.read,
ROLES.states.read,
ROLES.notifications.read,
Roles.any,
Roles.templates.read,
Roles.fileAttributes.readConfig,
Roles.watermarks.read,
Roles.dictionaryTypes.read,
Roles.colors.read,
Roles.states.read,
Roles.notifications.read,
'RED_USER',
],
redirectTo: {
RED_USER: '/main/admin',
[ROLES.templates.read]: '/main/admin',
[Roles.templates.read]: '/main/admin',
[DEFAULT_REDIRECT_KEY]: '/auth-error',
},
},
@ -147,7 +147,7 @@ const mainRoutes: IqserRoutes = [
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: ROLES.readDownloadStatus,
allow: Roles.readDownloadStatus,
redirectTo: '/auth-error',
},
},
@ -159,7 +159,7 @@ const mainRoutes: IqserRoutes = [
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard],
permissions: {
allow: [ROLES.search],
allow: [Roles.search],
redirectTo: '/auth-error',
},
},
@ -172,7 +172,7 @@ const mainRoutes: IqserRoutes = [
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard, TrashGuard],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
allow: [ROLES.dossiers.read, ROLES.files.readStatus],
allow: [Roles.dossiers.read, Roles.files.readStatus],
redirectTo: '/auth-error',
},
},
@ -185,19 +185,19 @@ const mainRoutes: IqserRoutes = [
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard],
permissions: {
allow: [
ROLES.any,
ROLES.templates.read,
ROLES.fileAttributes.readConfig,
ROLES.watermarks.read,
ROLES.dictionaryTypes.read,
ROLES.colors.read,
ROLES.states.read,
ROLES.notifications.read,
ROLES.dossiers.read,
Roles.any,
Roles.templates.read,
Roles.fileAttributes.readConfig,
Roles.watermarks.read,
Roles.dictionaryTypes.read,
Roles.colors.read,
Roles.states.read,
Roles.notifications.read,
Roles.dossiers.read,
'RED_USER',
],
redirectTo: {
[ROLES.any]: '/auth-error',
[Roles.any]: '/auth-error',
RED_USER: '/main/admin',
[DEFAULT_REDIRECT_KEY]: '/',
},

View File

@ -10,7 +10,7 @@ import { IqserPermissionsService, List, shareDistinctLast, TenantsService } from
import { BreadcrumbsService } from '@services/breadcrumbs.service';
import { FeaturesService } from '@services/features.service';
import { ARCHIVE_ROUTE, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE } from '@red/domain';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { REDDocumentViewer } from '../../modules/pdf-viewer/services/document-viewer.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@ -22,7 +22,7 @@ const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
styleUrls: ['./base-screen.component.scss'],
})
export class BaseScreenComponent {
readonly roles = ROLES;
readonly roles = Roles;
readonly documentViewer = inject(REDDocumentViewer);
readonly currentUser = this.userService.currentUser;
readonly searchActions: List<SpotlightSearchAction> = [

View File

@ -1,9 +1,10 @@
import { Component, inject } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { getCurrentUser, IqserPermissionsService, List, TenantsService } from '@iqser/common-ui';
import { User } from '@red/domain';
import { UserService } from '@users/user.service';
interface MenuItem {
readonly id: string;
readonly name: string;
@ -34,19 +35,19 @@ export class UserMenuComponent {
id: 'admin',
name: _('top-bar.navigation-items.my-account.children.admin'),
routerLink: '/main/admin',
show: (this.currentUser.isManager || this.currentUser.isUserAdmin) && this.#permissionsService.has([ROLES.templates.read]),
show: (this.currentUser.isManager || this.currentUser.isUserAdmin) && this.#permissionsService.has([Roles.templates.read]),
},
{
id: 'downloads',
name: _('top-bar.navigation-items.my-account.children.downloads'),
routerLink: '/main/downloads',
show: this.currentUser.isUser && this.#permissionsService.has(ROLES.readDownloadStatus),
show: this.currentUser.isUser && this.#permissionsService.has(Roles.readDownloadStatus),
},
{
id: 'trash',
name: _('top-bar.navigation-items.my-account.children.trash'),
routerLink: '/main/trash',
show: this.currentUser.isUser && this.#permissionsService.has([ROLES.dossiers.read, ROLES.files.readStatus]),
show: this.currentUser.isUser && this.#permissionsService.has([Roles.dossiers.read, Roles.files.readStatus]),
},
];

View File

@ -2,7 +2,7 @@ import { AnnotationWrapper } from './annotation.wrapper';
import { Dictionary } from '@red/domain';
import { isArray } from 'lodash-es';
import { IqserPermissionsService } from '@iqser/common-ui';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
export class AnnotationPermissions {
canUndo = true;
@ -29,9 +29,9 @@ export class AnnotationPermissions {
}
const summedPermissions: AnnotationPermissions = new AnnotationPermissions();
const canAddRedaction = permissionsService.has(ROLES.redactions.write);
const canRequestRedaction = permissionsService.has(ROLES.redactions.request);
const canProcessManualRedaction = permissionsService.has(ROLES.redactions.processManualRequest);
const canAddRedaction = permissionsService.has(Roles.redactions.write);
const canRequestRedaction = permissionsService.has(Roles.redactions.request);
const canProcessManualRedaction = permissionsService.has(Roles.redactions.processManualRequest);
const canAddOrRequestRedaction = canAddRedaction || canRequestRedaction;
for (const annotation of annotations) {

View File

@ -4,7 +4,7 @@ import { CompositeRouteGuard, IqserAuthGuard, IqserPermissionsGuard, IqserRoutes
import { RedRoleGuard } from '@users/red-role.guard';
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
import { PreferencesComponent } from './screens/preferences/preferences.component';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
const routes: IqserRoutes = [
{ path: '', redirectTo: 'user-profile', pathMatch: 'full' },
@ -24,7 +24,7 @@ const routes: IqserRoutes = [
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: [ROLES.notifications.write, 'RED_USER'],
allow: [Roles.notifications.write, 'RED_USER'],
redirectTo: '/',
},
},

View File

@ -1,7 +1,7 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { getCurrentUser, IqserPermissionsService } from '@iqser/common-ui';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { User } from '@red/domain';
interface NavItem {
@ -26,7 +26,7 @@ export class AccountSideNavComponent {
},
{
screen: 'notifications',
show: this.currentUser.isUser && this._permissionsService.has(ROLES.notifications.write),
show: this.currentUser.isUser && this._permissionsService.has(Roles.notifications.write),
label: _('notifications.label'),
},
{

View File

@ -3,7 +3,7 @@ import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.s
import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { AsControl, BaseFormComponent, IqserPermissionsService } from '@iqser/common-ui';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface PreferencesForm {
// preferences
@ -52,7 +52,7 @@ export class PreferencesComponent extends BaseFormComponent {
loadAllAnnotationsWarning: [this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
});
if (!this._permissionsService.has(ROLES.managePreferences)) {
if (!this._permissionsService.has(Roles.managePreferences)) {
this.form.disable();
}

View File

@ -16,7 +16,7 @@ import { languagesTranslations } from '@translations/languages-translations';
import { UserService } from '@users/user.service';
import { firstValueFrom } from 'rxjs';
import { UserPreferenceService } from '@users/user-preference.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -136,7 +136,7 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
private _initializeForm(): void {
try {
this.form = this._getForm();
if (!this._permissionsService.has(ROLES.updateMyProfile)) {
if (!this._permissionsService.has(Roles.updateMyProfile)) {
this.form.disable();
}
this.#profileModel = {

View File

@ -17,7 +17,7 @@ import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
import { PermissionsGuard } from '@guards/permissions-guard';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
const dossierTemplateIdRoutes: IqserRoutes = [
{
@ -54,7 +54,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: [ROLES.rules.read],
allow: [Roles.rules.read],
redirectTo: 'info',
},
},
@ -82,7 +82,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
canActivate: [IqserAuthGuard, IqserPermissionsGuard],
data: {
permissions: {
allow: [ROLES.reportTemplates.read],
allow: [Roles.reportTemplates.read],
redirectTo: '/auth-error',
},
},
@ -122,7 +122,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
canActivate: [IqserPermissionsGuard],
data: {
permissions: {
allow: [ROLES.legalBasis.read],
allow: [Roles.legalBasis.read],
redirectTo: '/auth-error',
},
},
@ -155,7 +155,7 @@ const routes: IqserRoutes = [
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard],
requiredRoles: ['RED_MANAGER', 'RED_ADMIN'],
permissions: {
allow: [ROLES.templates.read],
allow: [Roles.templates.read],
redirectTo: '/',
},
},
@ -173,7 +173,7 @@ const routes: IqserRoutes = [
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: [ROLES.users.read, 'RED_USER_ADMIN'],
allow: [Roles.users.read, 'RED_USER_ADMIN'],
redirectTo: '/',
},
},
@ -186,7 +186,7 @@ const routes: IqserRoutes = [
routeGuards: [IqserAuthGuard, RedRoleGuard, PermissionsGuard],
permissionsObject: 'Dossier',
permissions: {
allow: [ROLES.manageAclPermissions, 'RED_ADMIN'],
allow: [Roles.manageAclPermissions, 'RED_ADMIN'],
redirectTo: '/',
},
},
@ -198,7 +198,7 @@ const routes: IqserRoutes = [
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
data: {
permissions: {
allow: [ROLES.license.readReport, 'RED_ADMIN'],
allow: [Roles.license.readReport, 'RED_ADMIN'],
redirectTo: '/',
},
},
@ -216,7 +216,7 @@ const routes: IqserRoutes = [
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
data: {
permissions: {
allow: [ROLES.digitalSignature.read, 'RED_ADMIN'],
allow: [Roles.digitalSignature.read, 'RED_ADMIN'],
redirectTo: '/',
},
},
@ -233,7 +233,7 @@ const routes: IqserRoutes = [
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
data: {
permissions: {
allow: [ROLES.searchAudit, 'RED_ADMIN'],
allow: [Roles.searchAudit, 'RED_ADMIN'],
redirectTo: '/',
},
},
@ -252,7 +252,7 @@ const routes: IqserRoutes = [
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: [ROLES.generalConfiguration.read, ROLES.smtp.read, 'RED_ADMIN'],
allow: [Roles.generalConfiguration.read, Roles.smtp.read, 'RED_ADMIN'],
redirectTo: '/',
},
},

View File

@ -9,7 +9,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossierTemplate, IDossierTemplate } from '@red/domain';
import { HttpStatusCode } from '@angular/common/http';
import dayjs, { Dayjs } from 'dayjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface EditCloneTemplateData {
dossierTemplateId: string;
@ -26,7 +26,7 @@ const downloadTypes = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(t
styleUrls: ['./add-edit-clone-dossier-template-dialog.component.scss'],
})
export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogComponent {
readonly roles = ROLES;
readonly roles = Roles;
hasValidFrom: boolean;
hasValidTo: boolean;
readonly downloadTypes = downloadTypes;

View File

@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, inject, ViewChild } from '@angular/
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
import { BaseDialogComponent, IconButtonTypes } from '@iqser/common-ui';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface DialogData {
readonly dossierTemplateId: string;
@ -15,7 +15,7 @@ interface DialogData {
})
export class AddEntityDialogComponent extends BaseDialogComponent {
readonly iconButtonTypes = IconButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
readonly data = inject<DialogData>(MAT_DIALOG_DATA);
@ViewChild(AddEditEntityComponent, { static: true }) private readonly _addEditEntityComponent: AddEditEntityComponent;

View File

@ -17,7 +17,7 @@ import { AuditService } from '../../services/audit.service';
import { firstValueFrom } from 'rxjs';
import { Dayjs } from 'dayjs';
import { RouterHistoryService } from '@services/router-history.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { AdminDialogService } from '../../services/admin-dialog.service';
const PAGE_SIZE = 50;
@ -35,7 +35,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
readonly form: UntypedFormGroup = this._getForm();
readonly routerHistoryService = inject(RouterHistoryService);
readonly permissionsService = inject(IqserPermissionsService);
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
categories: string[] = [];

View File

@ -15,7 +15,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { combineLatest } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface ListItem extends IListable {
readonly key: string;
@ -31,7 +31,7 @@ interface ListItem extends IListable {
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> {
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = getCurrentUser<User>();
readonly roles = ROLES;
readonly roles = Roles;
readonly translations = defaultColorsTranslations;
readonly tableHeaderLabel = _('default-colors-screen.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<ListItem>[] = [

View File

@ -8,7 +8,7 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
import { PkcsSignatureConfigurationComponent } from '../../dialogs/configure-digital-signature-dialog/form/pkcs-signature-configuration/pkcs-signature-configuration.component';
import { KmsSignatureConfigurationComponent } from '../../dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component';
import { DigitalSignatureOptions, IKmsDigitalSignatureRequest, IPkcsDigitalSignatureRequest, User } from '@red/domain';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-digital-signature-screen',
@ -21,7 +21,7 @@ export class DigitalSignatureScreenComponent implements OnInit {
readonly certificateType = DigitalSignatureOptions;
readonly iconButtonTypes = IconButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
digitalSignature: IPkcsDigitalSignatureRequest | IKmsDigitalSignatureRequest;

View File

@ -16,7 +16,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { RouterHistoryService } from '@services/router-history.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { firstValueFrom } from 'rxjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
templateUrl: './dossier-templates-listing-screen.component.html',
@ -30,7 +30,7 @@ import { ROLES } from '@users/roles';
export class DossierTemplatesListingScreenComponent extends ListingComponent<DossierTemplate> {
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
readonly tableHeaderLabel = _('dossier-templates-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<DossierTemplate>[] = [

View File

@ -6,7 +6,7 @@ import { getCurrentUser, getParam, IqserPermissionsService, List, LoadingService
import { BehaviorSubject } from 'rxjs';
import { DICTIONARY_TO_ENTRY_TYPE_MAP, DICTIONARY_TYPE_KEY_MAP, DictionaryType, DOSSIER_TEMPLATE_ID, ENTITY_TYPE, User } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { NGXLogger } from 'ngx-logger';
@Component({
@ -16,7 +16,7 @@ import { NGXLogger } from 'ngx-logger';
})
export class DictionaryScreenComponent implements OnInit {
readonly currentUser = getCurrentUser<User>();
readonly roles = ROLES;
readonly roles = Roles;
readonly initialEntries$ = new BehaviorSubject<string[]>([]);
isLeavingPage = false;
readonly type: DictionaryType;

View File

@ -7,7 +7,7 @@ import { PermissionsService } from '@services/permissions.service';
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
import { IconButtonTypes, IqserEventTarget } from '@iqser/common-ui';
import { Observable } from 'rxjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-entity-info',
@ -19,7 +19,7 @@ export class EntityInfoComponent {
readonly currentUser = getCurrentUser();
readonly entity$: Observable<Dictionary>;
readonly dossierTemplateId: string;
readonly roles = ROLES;
readonly roles = Roles;
readonly iconButtonTypes = IconButtonTypes;
@ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent;

View File

@ -10,7 +10,7 @@ import { MatSelectModule } from '@angular/material/select';
import { NgForOf } from '@angular/common';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
export interface AddEditFileAttributeDialogData {
@ -40,7 +40,7 @@ export interface AddEditFileAttributeDialogData {
],
})
export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
readonly roles = ROLES;
readonly roles = Roles;
readonly DISPLAYED_FILTERABLE_LIMIT = 3;
readonly translations = fileAttributeTypesTranslations;
readonly fileAttribute = this.data.fileAttribute;

View File

@ -21,7 +21,7 @@ import { HttpStatusCode } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { PermissionsService } from '@services/permissions.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { MatDialog } from '@angular/material/dialog';
import {
AddEditFileAttributeDialogComponent,
@ -58,7 +58,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
rightIconTooltip: _('file-attributes-listing.table-col-names.primary-info-tooltip'),
},
];
readonly roles = ROLES;
readonly roles = Roles;
@ViewChild('impactedTemplates') private readonly _impactedTemplatesRef: TemplateRef<unknown>;
#existingConfiguration: IFileAttributesConfig;
@ViewChild('fileInput') private _fileInput: ElementRef;

View File

@ -5,7 +5,7 @@ import { IGeneralConfiguration } from '@red/domain';
import { ConfigService } from '@services/config.service';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { firstValueFrom } from 'rxjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-general-config-form',
@ -23,7 +23,7 @@ export class GeneralConfigFormComponent extends BaseFormComponent implements OnI
) {
super();
this.form = this._getForm();
if (!_permissionsService.has(ROLES.generalConfiguration.write)) {
if (!_permissionsService.has(Roles.generalConfiguration.write)) {
this.form.disable();
}
}

View File

@ -6,7 +6,7 @@ import { AdminDialogService } from '../../../services/admin-dialog.service';
import { SmtpConfigService } from '../../../services/smtp-config.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { firstValueFrom } from 'rxjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-smtp-form',
@ -26,7 +26,7 @@ export class SmtpFormComponent extends BaseFormComponent implements OnInit {
) {
super();
this.form = this._getForm();
if (!_permissionsService.has(ROLES.smtp.write)) {
if (!_permissionsService.has(Roles.smtp.write)) {
this.form.disable();
}
}

View File

@ -4,7 +4,7 @@ import { SystemPreferences } from '@red/domain';
import { BaseFormComponent, IqserPermissionsService, KeysOf, LoadingService } from '@iqser/common-ui';
import { SystemPreferencesService } from '@services/system-preferences.service';
import { systemPreferencesTranslations } from '@translations/system-preferences-translations';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
export type ValueType = 'number' | 'string' | 'boolean';
@ -30,7 +30,7 @@ export class SystemPreferencesFormComponent extends BaseFormComponent {
super();
this.form = this._getForm();
this._loadData();
if (!_permissionsService.has(ROLES.appConfiguration.write)) {
if (!_permissionsService.has(Roles.appConfiguration.write)) {
this.form.disable();
}
}

View File

@ -6,7 +6,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { RouterHistoryService } from '@services/router-history.service';
import { LicenseService } from '@services/license.service';
import { map } from 'rxjs/operators';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import type { User } from '@red/domain';
@Component({
@ -14,7 +14,7 @@ import type { User } from '@red/domain';
styleUrls: ['./license-screen.component.scss'],
})
export class LicenseScreenComponent {
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
readonly currentYear = new Date().getFullYear();
readonly buttonConfigs: readonly ButtonConfig[] = [
@ -23,7 +23,7 @@ export class LicenseScreenComponent {
action: (): void => this.sendMail(),
type: IconButtonTypes.primary,
helpModeKey: 'license_information',
hide: !this.permissionsService.has(ROLES.license.readReport),
hide: !this.permissionsService.has(Roles.license.readReport),
},
];

View File

@ -19,7 +19,7 @@ import { interval, map, Subscription } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { permissionsTranslations } from '@translations/permissions-translations';
import { RouterHistoryService } from '@services/router-history.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
templateUrl: './permissions-screen.component.html',
@ -27,7 +27,7 @@ import { ROLES } from '@users/roles';
providers: listingProvidersFactory(PermissionsScreenComponent),
})
export class PermissionsScreenComponent extends ListingComponent<PermissionsMapping> implements OnDestroy {
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
readonly translations = permissionsTranslations;
readonly tableColumnConfigs: TableColumnConfig<PermissionsMapping>[];

View File

@ -12,7 +12,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { ReportTemplateService } from '@services/report-template.service';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface Placeholder {
placeholder: string;
@ -33,7 +33,7 @@ export class ReportsScreenComponent implements OnInit {
readonly placeholders$ = new BehaviorSubject<Placeholder[]>([]);
readonly availableTemplates$ = new BehaviorSubject<IReportTemplate[]>([]);
readonly currentUser = getCurrentUser<User>();
readonly roles = ROLES;
readonly roles = Roles;
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);

View File

@ -12,7 +12,6 @@ import {
listingProvidersFactory,
LoadingService,
NestedFilter,
OverlappingElements,
SearchPositions,
TableColumnConfig,
} from '@iqser/common-ui';
@ -22,7 +21,7 @@ import { rolesTranslations } from '@translations/roles-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { userTypeChecker, userTypeFilters } from '../../../../utils';
import { RouterHistoryService } from '@services/router-history.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
function configToFilter({ key, label }: DonutChartConfig) {
return new NestedFilter({
@ -45,7 +44,7 @@ export class UserListingScreenComponent extends ListingComponent<User> implement
readonly translations = rolesTranslations;
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = this._userService.currentUser;
readonly canDeleteSelected$ = this.#canDeleteSelected$;
readonly tableHeaderLabel = _('user-listing.table-header.title');

View File

@ -36,7 +36,7 @@ import { LicenseService } from '@services/license.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { Router } from '@angular/router';
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { environment } from '@environments/environment';
import { tap } from 'rxjs/operators';
import { watermarkTranslations } from '@translations/watermark-translations';
@ -143,7 +143,7 @@ export class WatermarkScreenComponent implements OnInit {
verticalTextAlignment: [null],
});
if (!this.currentUser.isAdmin || !this.permissionsService.has(ROLES.watermarks.write)) {
if (!this.currentUser.isAdmin || !this.permissionsService.has(Roles.watermarks.write)) {
form.disable();
}

View File

@ -17,7 +17,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { WatermarkService } from '@services/entity-services/watermark.service';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
templateUrl: './watermarks-listing-screen.component.html',
@ -28,7 +28,7 @@ export class WatermarksListingScreenComponent extends ListingComponent<Watermark
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = getCurrentUser<User>();
readonly roles = ROLES;
readonly roles = Roles;
readonly tableColumnConfigs: TableColumnConfig<Watermark>[] = [
{ label: _('watermarks-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' },
{ label: _('watermarks-listing.table-col-names.status'), sortByKey: 'enabled', class: 'flex-center' },

View File

@ -4,7 +4,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { adminSideNavTranslations } from '@translations/admin-side-nav-translations';
import { ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router';
import { AdminSideNavType, AdminSideNavTypes, DOSSIER_TEMPLATE_ID, ENTITY_TYPE, User, WATERMARK_ID } from '@red/domain';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { getCurrentUser, IqserHelpModeModule, IqserPermissionsService, SideNavComponent, TenantPipe } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
import { NgForOf, NgIf } from '@angular/common';
@ -28,7 +28,7 @@ export class AdminSideNavComponent implements OnInit {
@Input() disabledItems: string[] = [];
readonly translations = adminSideNavTranslations;
readonly currentUser = getCurrentUser<User>();
readonly roles = ROLES;
readonly roles = Roles;
prefix: string;
readonly items: { readonly [key in AdminSideNavType]: NavItem[] } = {
@ -36,43 +36,43 @@ export class AdminSideNavComponent implements OnInit {
{
screen: 'dossier-templates',
label: _('admin-side-nav.dossier-templates'),
show: (this.currentUser.isManager || this.currentUser.isAdmin) && this._permissionsService.has(ROLES.templates.read),
show: (this.currentUser.isManager || this.currentUser.isAdmin) && this._permissionsService.has(Roles.templates.read),
helpModeKey: 'dossier_templates',
},
{
screen: 'digital-signature',
label: _('admin-side-nav.digital-signature'),
show: this.currentUser.isAdmin && this._permissionsService.has(ROLES.digitalSignature.read),
show: this.currentUser.isAdmin && this._permissionsService.has(Roles.digitalSignature.read),
helpModeKey: 'digital_signature',
},
{
screen: 'license-info',
label: _('admin-side-nav.license-information'),
show: this.currentUser.isAdmin && this._permissionsService.has(ROLES.license.readReport),
show: this.currentUser.isAdmin && this._permissionsService.has(Roles.license.readReport),
helpModeKey: 'license_information',
},
{
screen: 'audit',
label: _('admin-side-nav.audit'),
show: this.currentUser.isAdmin && this._permissionsService.has(ROLES.searchAudit),
show: this.currentUser.isAdmin && this._permissionsService.has(Roles.searchAudit),
helpModeKey: 'audit',
},
{
screen: 'users',
label: _('admin-side-nav.user-management'),
show: this.currentUser.isUserAdmin && this._permissionsService.has(ROLES.users.read),
show: this.currentUser.isUserAdmin && this._permissionsService.has(Roles.users.read),
helpModeKey: 'user_management',
},
{
screen: 'dossier-permissions',
label: _('dossier-permissions'),
show: this.currentUser.isAdmin && this._permissionsService.has(ROLES.manageAclPermissions),
show: this.currentUser.isAdmin && this._permissionsService.has(Roles.manageAclPermissions),
helpModeKey: 'dossier_permissions',
},
{
screen: 'general-config',
label: _('admin-side-nav.configurations'),
show: this.currentUser.isAdmin && this._permissionsService.has([ROLES.generalConfiguration.read, ROLES.smtp.read]),
show: this.currentUser.isAdmin && this._permissionsService.has([Roles.generalConfiguration.read, Roles.smtp.read]),
helpModeKey: 'configurations',
},
],
@ -92,7 +92,7 @@ export class AdminSideNavComponent implements OnInit {
{
screen: 'rules',
label: _('admin-side-nav.rule-editor'),
show: this.userPreferenceService.areDevFeaturesEnabled && this._permissionsService.has(ROLES.rules.read),
show: this.userPreferenceService.areDevFeaturesEnabled && this._permissionsService.has(Roles.rules.read),
},
{
screen: 'default-colors',
@ -127,13 +127,13 @@ export class AdminSideNavComponent implements OnInit {
{
screen: 'reports',
label: _('admin-side-nav.reports'),
show: this._permissionsService.has([ROLES.reportTemplates.read]),
show: this._permissionsService.has([Roles.reportTemplates.read]),
helpModeKey: 'reports',
},
{
screen: 'justifications',
label: _('admin-side-nav.justifications'),
show: this._permissionsService.has([ROLES.legalBasis.read]),
show: this._permissionsService.has([Roles.legalBasis.read]),
helpModeKey: 'justifications',
},
],

View File

@ -3,7 +3,7 @@ import { DashboardStats } from '@red/domain';
import { IconButtonTypes } from '@iqser/common-ui';
import { TranslateChartService } from '@services/translate-chart.service';
import { SharedDialogService } from '@shared/services/dialog.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { PermissionsService } from '@services/permissions.service';
@Component({
@ -13,7 +13,7 @@ import { PermissionsService } from '@services/permissions.service';
})
export class TemplateStatsComponent {
readonly iconButtonTypes = IconButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
@Input() stats: DashboardStats;

View File

@ -30,7 +30,7 @@ import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { map, tap } from 'rxjs/operators';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { FilesMapService } from '@services/files/files-map.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface DossierDetailsContext {
needsWorkFilters: INestedFilter[] | undefined;
@ -49,7 +49,7 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
@Output() readonly toggleCollapse = new EventEmitter();
editingOwner = false;
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
readonly collapseTooltip = _('dossier-details.collapse');
readonly expandTooltip = _('dossier-details.expand');

View File

@ -21,7 +21,7 @@ import { UserService } from '@users/user.service';
import { ConfigService } from '../../config.service';
import { PrimaryFileAttributeService } from '@services/primary-file-attribute.service';
import { Router } from '@angular/router';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-dossier-overview-screen-header [dossier] [upload]',
@ -31,7 +31,7 @@ export class DossierOverviewScreenHeaderComponent implements OnInit {
@Input() dossier: Dossier;
@Output() readonly upload = new EventEmitter<void>();
readonly circleButtonTypes = CircleButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
actionConfigs: List<ActionConfig>;
readonly downloadBtnDisabled$: Observable<boolean>;

View File

@ -1,4 +1,4 @@
import { Component, HostListener, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, HostListener, Input, OnDestroy } from '@angular/core';
import { Dossier, File, FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain';
import { BaseFormComponent, HelpModeService, ListingService, Toaster } from '@iqser/common-ui';
import { PermissionsService } from '@services/permissions.service';
@ -76,7 +76,7 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
await firstValueFrom(
this.fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId),
);
await firstValueFrom(this._filesService.reload(this.file.dossierId, this.file));
await this._filesService.reload(this.file.dossierId, this.file);
this.initialFormValue = rawFormValue;
this._toaster.success(_('file-attribute.update.success'));
} catch (e) {

View File

@ -12,7 +12,6 @@ import {
ListingMode,
ListingModes,
NestedFilter,
OverlappingElements,
TableColumnConfig,
WorkflowColumn,
WorkflowConfig,
@ -46,7 +45,7 @@ import { map } from 'rxjs/operators';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Injectable()
export class ConfigService {
@ -160,7 +159,7 @@ export class ConfigService {
label: this._translateService.instant('dossier-overview.header-actions.edit'),
action: () => this._openEditDossierDialog(dossierId),
icon: 'iqser:edit',
hide: !this.#currentUser.isManager && !this._iqserPermissionsService.has(ROLES.dossiers.edit),
hide: !this.#currentUser.isManager && !this._iqserPermissionsService.has(Roles.dossiers.edit),
helpModeKey: 'edit_dossier_in_dossier',
disabled$,
},

View File

@ -9,7 +9,7 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
import { HelpModeService, IqserPermissionsService, shareLast } from '@iqser/common-ui';
import { ViewModeService } from '../../services/view-mode.service';
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { map } from 'rxjs';
export const AnnotationButtonTypes = {
@ -30,7 +30,7 @@ export class AnnotationActionsComponent implements OnChanges {
@Input() canPerformAnnotationActions: boolean;
@Input() alwaysVisible: boolean;
annotationPermissions: AnnotationPermissions;
readonly roles = ROLES;
readonly roles = Roles;
isImage = true;

View File

@ -2,7 +2,7 @@ import { Component, Input } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { MultiSelectService } from '../../services/multi-select.service';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-annotation-card',
@ -10,7 +10,7 @@ import { ROLES } from '@users/roles';
styleUrls: ['./annotation-card.component.scss'],
})
export class AnnotationCardComponent {
readonly roles = ROLES;
readonly roles = Roles;
readonly annotationTypesTranslations = annotationTypesTranslations;
@Input() annotation: AnnotationWrapper;
@Input() isSelected = false;

View File

@ -1,4 +1,4 @@
<div *ngIf="annotationReferencesService.references$ | async as references" class="content-container">
<div *ngIf="annotationReferencesService.references() as references" class="content-container">
<div class="dialog references-dialog">
<div class="references-header flex">
<div class="small-label uppercase">
@ -7,7 +7,7 @@
<iqser-circle-button (action)="annotationReferencesService.hide()" icon="iqser:close"></iqser-circle-button>
</div>
<div *ngIf="annotationReferencesService.annotation$ | async as annotation" class="annotations-container flex">
<div *ngIf="annotationReferencesService.annotation() as annotation" class="annotations-container flex">
<div [class.active]="isSelected$ | async" class="annotation-container">
<div class="annotation-card-container flex">
<redaction-annotation-card [annotation]="annotation"></redaction-annotation-card>

View File

@ -4,6 +4,7 @@ import { AnnotationReferencesService } from '../../services/annotation-reference
import { Observable, switchMap } from 'rxjs';
import { AnnotationsListingService } from '../../services/annotations-listing.service';
import { filter } from 'rxjs/operators';
import { toObservable } from '@angular/core/rxjs-interop';
@Component({
selector: 'redaction-annotation-references-list',
@ -13,12 +14,13 @@ import { filter } from 'rxjs/operators';
export class AnnotationReferencesListComponent {
@Output() readonly referenceClicked = new EventEmitter<AnnotationWrapper>();
readonly isSelected$: Observable<boolean>;
readonly annotation$ = toObservable(this.annotationReferencesService.annotation);
constructor(
private readonly _listingService: AnnotationsListingService,
readonly annotationReferencesService: AnnotationReferencesService,
) {
this.isSelected$ = this.annotationReferencesService.annotation$.pipe(
this.isSelected$ = this.annotation$.pipe(
filter(annotation => !!annotation),
switchMap(annotation => this._listingService.isSelected$(annotation)),
);

View File

@ -11,5 +11,5 @@
<redaction-annotation-references-list
(referenceClicked)="referenceClicked($event)"
*ngIf="annotationReferencesService.annotation$ | async"
*ngIf="annotationReferencesService.annotation()"
></redaction-annotation-references-list>

View File

@ -9,7 +9,7 @@
<div class="comment-actions">
<iqser-circle-button
(action)="deleteComment(comment)"
*ngIf="permissionsService.canDeleteComment(comment, ctx.file, ctx.dossier)"
*ngIf="permissionsService.canDeleteComment(comment, _state.file(), ctx.dossier)"
[iconSize]="10"
[size]="20"
class="pointer"
@ -23,7 +23,7 @@
<iqser-input-with-action
(action)="addComment($event)"
*ngIf="permissionsService.canAddComment(ctx.file, ctx.dossier)"
*ngIf="permissionsService.canAddComment(_state.file(), ctx.dossier)"
[placeholder]="'comments.add-comment' | translate"
autocomplete="off"
icon="iqser:collapse"

View File

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, HostBinding, Input, OnInit, ViewChild } from '@angular/core';
import type { Dossier, File, IComment, User } from '@red/domain';
import type { Dossier, IComment, User } from '@red/domain';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { PermissionsService } from '@services/permissions.service';
import { ContextComponent, getCurrentUser, InputWithActionComponent, LoadingService, trackByFactory } from '@iqser/common-ui';
@ -11,7 +11,6 @@ import { ManualRedactionService } from '../../services/manual-redaction.service'
interface CommentsContext {
dossier: Dossier;
file: File;
hiddenComments: boolean;
}
@ -34,7 +33,7 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
private readonly _commentingService: CommentingService,
private readonly _loadingService: LoadingService,
private readonly _changeRef: ChangeDetectorRef,
private readonly _stateService: FilePreviewStateService,
protected readonly _state: FilePreviewStateService,
) {
super();
}
@ -47,8 +46,7 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
);
super._initContext({
file: this._stateService.file$,
dossier: this._stateService.dossier$,
dossier: this._state.dossier$,
hiddenComments: this.hiddenComments$,
});
}
@ -58,7 +56,7 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
return;
}
this._loadingService.start();
const { dossierId, fileId } = this._stateService;
const { dossierId, fileId } = this._state;
const commentId = await this._manualRedactionService.addComment(value, this.annotation.id, dossierId, fileId);
this.annotation.comments.push({
text: value,
@ -77,7 +75,7 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
async deleteComment(comment: IComment): Promise<void> {
this._loadingService.start();
const { dossierId, fileId } = this._stateService;
const { dossierId, fileId } = this._state;
await this._manualRedactionService.deleteComment(comment.id, this.annotation.id, dossierId, fileId);
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
this._changeRef.markForCheck();

View File

@ -2,18 +2,18 @@
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
<div>
<iqser-circle-button
buttonId="edit-document-info-btn"
(action)="edit(ctx.file)"
*ngIf="permissionsService.canEditFileAttributes(ctx.file, ctx.dossier)"
(action)="edit()"
*ngIf="permissionsService.canEditFileAttributes(_state.file(), ctx.dossier)"
[tooltip]="'file-preview.tabs.document-info.edit' | translate"
buttonId="edit-document-info-btn"
icon="iqser:edit"
tooltipPosition="before"
></iqser-circle-button>
<iqser-circle-button
buttonId="close-document-info-btn"
(action)="documentInfoService.hide()"
[tooltip]="'file-preview.tabs.document-info.close' | translate"
buttonId="close-document-info-btn"
icon="iqser:close"
tooltipPosition="before"
></iqser-circle-button>
@ -38,10 +38,10 @@
<div>
<mat-icon svgIcon="iqser:document"></mat-icon>
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate : { pages: ctx.file.numberOfPages } }}</span>
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate : { pages: _state.file().numberOfPages } }}</span>
</div>
<div *ngIf="ctx.file.added | date : 'mediumDate' as added">
<div *ngIf="_state.file().added | date : 'mediumDate' as added">
<mat-icon svgIcon="red:calendar"></mat-icon>
<span [innerHTML]="'file-preview.tabs.document-info.details.created-on' | translate : { date: added }"></span>
</div>

View File

@ -1,11 +1,11 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DocumentInfoService } from '../../services/document-info.service';
import { combineLatest, switchMap } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { map } from 'rxjs/operators';
import { type Dossier, type File, type FileAttributeConfigType, FileAttributeConfigTypes } from '@red/domain';
import { type Dossier, type FileAttributeConfigType, FileAttributeConfigTypes } from '@red/domain';
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { ContextComponent } from '@iqser/common-ui';
@ -17,7 +17,6 @@ interface FileAttribute {
}
interface Context {
readonly file: File;
readonly dossier: Dossier;
readonly dossierTemplateName: string;
readonly fileAttributes: FileAttribute[];
@ -29,8 +28,9 @@ interface Context {
styleUrls: ['./document-info.component.scss'],
})
export class DocumentInfoComponent extends ContextComponent<Context> {
protected readonly _state = inject(FilePreviewStateService);
constructor(
state: FilePreviewStateService,
fileAttributesService: FileAttributesService,
readonly permissionsService: PermissionsService,
readonly documentInfoService: DocumentInfoService,
@ -38,25 +38,24 @@ export class DocumentInfoComponent extends ContextComponent<Context> {
private readonly _dossierTemplatesService: DossierTemplatesService,
) {
super();
const fileAttributes$ = combineLatest([state.file$, state.dossier$, fileAttributesService.fileAttributesConfig$]).pipe(
const fileAttributes$ = combineLatest([this._state.file$, this._state.dossier$, fileAttributesService.fileAttributesConfig$]).pipe(
switchMap(([file, dossier]) => this.documentInfoService.fileAttributes$(file.id, dossier.id, dossier.dossierTemplateId)),
);
const dossierTemplateName$ = state.dossier$.pipe(
const dossierTemplateName$ = this._state.dossier$.pipe(
switchMap(dossier => this._dossierTemplatesService.getEntityChanged$(dossier.dossierTemplateId)),
map(dossierTemplate => dossierTemplate.name),
);
super._initContext({
file: state.file$,
dossier: state.dossier$,
dossier: this._state.dossier$,
dossierTemplateName: dossierTemplateName$,
fileAttributes: fileAttributes$,
});
}
edit(file: File) {
this._dialogService.openDialog('documentInfo', file);
edit() {
this._dialogService.openDialog('documentInfo', this._state.file());
}
isDate(attribute: FileAttribute) {

View File

@ -275,7 +275,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
scrollQuickNavLast() {
this.pdf.navigateTo(this.state.file.numberOfPages);
this.pdf.navigateTo(this.state.file().numberOfPages);
}
preventKeyDefault($event: KeyboardEvent): void {

View File

@ -6,7 +6,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
import { FileDataService } from '../../services/file-data.service';
import { MultiSelectService } from '../../services/multi-select.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-highlights-separator [highlightGroup] [annotation]',
@ -20,7 +20,7 @@ export class HighlightsSeparatorComponent {
readonly circleButtonTypes = CircleButtonTypes;
readonly isWritable$ = this._state.isWritable$;
readonly multiSelectInactive$ = this._multiSelectService.inactive$;
readonly roles = ROLES;
readonly roles = Roles;
constructor(
private readonly _dialogService: FilePreviewDialogService,

View File

@ -5,7 +5,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IPageRange } from '@red/domain';
import { ReanalysisService } from '@services/reanalysis.service';
import { ExcludedPagesService } from '../../services/excluded-pages.service';
import { combineLatest, firstValueFrom, Observable } from 'rxjs';
import { combineLatest, Observable } from 'rxjs';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { map } from 'rxjs/operators';
import { extractPageRanges } from '@utils/page-ranges-mapper';
@ -53,11 +53,10 @@ export class PageExclusionComponent {
async excludePagesRange(inputValue: string): Promise<void> {
this._loadingService.start();
const file = this._state.file;
const file = this._state.file();
try {
const pageRanges = extractPageRanges(inputValue, file);
const excludePages$ = this._reanalysisService.excludePages({ pageRanges }, file.dossierId, file);
await firstValueFrom(excludePages$);
await this._reanalysisService.excludePages({ pageRanges }, file);
this._inputComponent.reset();
} catch (e) {
this._toaster.error(_('file-preview.tabs.exclude-pages.error'));
@ -67,15 +66,7 @@ export class PageExclusionComponent {
async includePagesRange(range: IPageRange): Promise<void> {
this._loadingService.start();
const file = this._state.file;
const includePages$ = this._reanalysisService.includePages(
{
pageRanges: [range],
},
file.dossierId,
file,
);
await firstValueFrom(includePages$);
await this._reanalysisService.includePages({ pageRanges: [range] }, this._state.file());
this._inputComponent.reset();
this._loadingService.stop();
}

View File

@ -62,7 +62,7 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
}
async toggleReadState() {
if (this._permissionService.canMarkPagesAsViewed(this._state.file)) {
if (this._permissionService.canMarkPagesAsViewed(this._state.file())) {
if (this.read) {
await this.#markPageUnread();
} else {
@ -72,7 +72,7 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
}
handlePageRead(): void {
if (!this._permissionService.canMarkPagesAsViewed(this._state.file)) {
if (!this._permissionService.canMarkPagesAsViewed(this._state.file())) {
return;
}

View File

@ -35,7 +35,7 @@ export class PagesComponent {
hasOnlyManualRedactionsAndIsExcluded(pageNumber: number): boolean {
const hasOnlyManualRedactions = this.displayedAnnotations.get(pageNumber)?.every(annotation => annotation.manual);
return hasOnlyManualRedactions && this.#state.file.excludedPages.includes(pageNumber);
return hasOnlyManualRedactions && this.#state.file().excludedPages.includes(pageNumber);
}
getViewedPage(viewedPages: ViewedPage[], pageNumber: number) {

View File

@ -1,51 +1,49 @@
<ng-container *ngIf="viewModeService.viewMode$ | async as viewMode">
<button
(click)="switchView(viewModes.STANDARD)"
[attr.help-mode-key]="'views'"
[class.active]="viewModeService.isStandard()"
[matTooltipPosition]="'above'"
[matTooltip]="'file-preview.standard-tooltip' | translate"
class="red-tab"
>
{{ 'file-preview.standard' | translate }}
</button>
<div [matTooltipPosition]="'above'" [matTooltip]="'file-preview.delta-tooltip' | translate">
<button
(click)="switchView(viewModes.STANDARD)"
[class.active]="viewMode === viewModes.STANDARD"
(click)="switchView(viewModes.DELTA)"
*deny="roles.getRss"
[attr.help-mode-key]="'views'"
[matTooltip]="'file-preview.standard-tooltip' | translate"
[matTooltipPosition]="'above'"
[class.active]="viewModeService.isDelta()"
[disabled]="!canSwitchToDeltaView()"
class="red-tab"
>
{{ 'file-preview.standard' | translate }}
{{ 'file-preview.delta' | translate }}
</button>
</div>
<div [matTooltip]="'file-preview.delta-tooltip' | translate" [matTooltipPosition]="'above'">
<button
(click)="switchView(viewModes.DELTA)"
*deny="roles.getRss"
[class.active]="viewMode === viewModes.DELTA"
[disabled]="(canSwitchToDeltaView$ | async) === false"
[attr.help-mode-key]="'views'"
class="red-tab"
>
{{ 'file-preview.delta' | translate }}
</button>
</div>
<div [matTooltipPosition]="'above'" [matTooltip]="'file-preview.redacted-tooltip' | translate">
<button
(click)="switchView(viewModes.REDACTED)"
*deny="roles.getRss"
[attr.help-mode-key]="'views'"
[class.active]="viewModeService.isRedacted()"
[disabled]="!canSwitchToRedactedView()"
class="red-tab"
>
{{ 'file-preview.redacted' | translate }}
</button>
</div>
<div [matTooltip]="'file-preview.redacted-tooltip' | translate" [matTooltipPosition]="'above'">
<button
(click)="switchView(viewModes.REDACTED)"
*deny="roles.getRss"
[class.active]="viewMode === viewModes.REDACTED"
[disabled]="(canSwitchToRedactedView$ | async) === false"
[attr.help-mode-key]="'views'"
class="red-tab"
>
{{ 'file-preview.redacted' | translate }}
</button>
</div>
<div [matTooltip]="'file-preview.text-highlights-tooltip' | translate" [matTooltipPosition]="'above'">
<button
(click)="switchView(viewModes.TEXT_HIGHLIGHTS)"
*deny="roles.getRss"
[class.active]="viewMode === viewModes.TEXT_HIGHLIGHTS"
[disabled]="(canSwitchToEarmarksView$ | async) === false"
[attr.help-mode-key]="'views'"
class="red-tab"
>
{{ 'file-preview.text-highlights' | translate }}
</button>
</div>
</ng-container>
<div [matTooltipPosition]="'above'" [matTooltip]="'file-preview.text-highlights-tooltip' | translate">
<button
(click)="switchView(viewModes.TEXT_HIGHLIGHTS)"
*deny="roles.getRss"
[attr.help-mode-key]="'views'"
[class.active]="viewModeService.isEarmarks()"
[disabled]="!canSwitchToEarmarksView()"
class="red-tab"
>
{{ 'file-preview.text-highlights' | translate }}
</button>
</div>

View File

@ -1,15 +1,13 @@
import { Component, Inject } from '@angular/core';
import { Component, computed, Inject } from '@angular/core';
import { ViewMode, ViewModes } from '@red/domain';
import { ViewModeService } from '../../services/view-mode.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { FileDataService } from '../../services/file-data.service';
import { BASE_HREF, ConfirmOptions, IConfirmationDialogData, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserPreferenceService } from '@users/user-preference.service';
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-view-switch',
@ -17,32 +15,32 @@ import { ROLES } from '@users/roles';
styleUrls: ['./view-switch.component.scss'],
})
export class ViewSwitchComponent {
readonly roles = Roles;
readonly viewModes = ViewModes;
readonly canSwitchToDeltaView$: Observable<boolean>;
readonly canSwitchToRedactedView$: Observable<boolean>;
readonly canSwitchToEarmarksView$: Observable<boolean>;
readonly roles = ROLES;
readonly canSwitchToDeltaView = computed(() => {
const annotations = this._fileDataService.annotations();
const file = this._state.file();
const hasChangeLog = annotations.some(a => a.isChangeLogEntry);
return hasChangeLog && !file.isApproved;
});
readonly canSwitchToRedactedView = computed(() => {
const file = this._state.file();
return !file.analysisRequired && !file.excluded;
});
readonly canSwitchToEarmarksView = computed(() => {
const file = this._state.file();
return file.hasHighlights && !file.analysisRequired && !file.excluded;
});
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
readonly viewModeService: ViewModeService,
private readonly _stateService: FilePreviewStateService,
private readonly _state: FilePreviewStateService,
private readonly _fileDataService: FileDataService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _dialogService: FilePreviewDialogService,
private readonly _toaster: Toaster,
) {
const hasChangeLog$ = this._fileDataService.annotations$.pipe(map(annotations => annotations.some(a => a.isChangeLogEntry)));
this.canSwitchToDeltaView$ = combineLatest([hasChangeLog$, _stateService.file$]).pipe(
map(([hasChangeLog, file]) => hasChangeLog && !file.isApproved),
);
this.canSwitchToRedactedView$ = _stateService.file$.pipe(map(file => !file.analysisRequired && !file.excluded));
this.canSwitchToEarmarksView$ = _stateService.file$.pipe(
map(file => file.hasHighlights && !file.analysisRequired && !file.excluded),
);
}
) {}
switchView(viewMode: ViewMode) {
if (viewMode === ViewModes.REDACTED) {
@ -57,7 +55,7 @@ export class ViewSwitchComponent {
return this.viewModeService.switchToRedacted();
}
const suggestions = (await this._fileDataService.annotations).filter(a => a.isSuggestion);
const suggestions = this._fileDataService.annotations().filter(a => a.isSuggestion);
if (!suggestions.length) {
return this.viewModeService.switchToRedacted();
}

View File

@ -43,7 +43,7 @@ export class DocumentInfoDialogComponent extends BaseDialogComponent implements
...this._mapFormValues(this.form.getRawValue()),
};
await firstValueFrom(this._fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId));
await firstValueFrom(this._filesService.reload(this.file.dossierId, this.file));
await this._filesService.reload(this.file.dossierId, this.file);
this._dialogRef.close(true);
}

View File

@ -7,7 +7,7 @@ import { EarmarksService } from '@services/files/earmarks.service';
import { firstValueFrom } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { highlightsTranslations } from '@translations/highlights-translations';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
export interface HighlightActionData {
readonly operation: EarmarkOperation;
@ -24,7 +24,7 @@ export interface HighlightActionData {
})
export class HighlightActionDialogComponent extends BaseDialogComponent {
readonly translations = highlightsTranslations;
readonly roles = ROLES;
readonly roles = Roles;
readonly #operation = this.data.operation;
readonly options: DetailsRadioOption<EarmarkOperationPages>[] = [
{

View File

@ -10,7 +10,7 @@ import { BaseDialogComponent, CircleButtonTypes, IqserPermissionsService } from
import { firstValueFrom } from 'rxjs';
import { ManualRedactionService } from '../../services/manual-redaction.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
export interface LegalBasisOption {
label?: string;
@ -23,7 +23,7 @@ export interface LegalBasisOption {
styleUrls: ['./manual-annotation-dialog.component.scss'],
})
export class ManualAnnotationDialogComponent extends BaseDialogComponent implements OnInit {
readonly roles = ROLES;
readonly roles = Roles;
readonly circleButtonTypes = CircleButtonTypes;
isDictionaryRequest: boolean;
isFalsePositiveRequest: boolean;
@ -172,7 +172,7 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
addRedactionRequest.legalBasis = legalOption.legalBasis;
}
if (this.iqserPermissionsService.has(ROLES.getRss)) {
if (this.iqserPermissionsService.has(Roles.getRss)) {
const selectedType = this.possibleDictionaries.find(d => d.type === addRedactionRequest.type);
addRedactionRequest.addToDictionary = selectedType.hasDictionary;
} else {

View File

@ -41,12 +41,12 @@ import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.se
import { AnnotationProcessingService } from './services/annotation-processing.service';
import { Dictionary, File, ViewModes } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
import { combineLatest, firstValueFrom, Observable, of, pairwise } from 'rxjs';
import { combineLatest, firstValueFrom, of, pairwise } from 'rxjs';
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
import { byId, byPage, download, handleFilterDelta, hasChanges } from '../../utils';
import { FilesService } from '@services/files/files.service';
import { FileManagementService } from '@services/files/file-management.service';
import { catchError, filter, map, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/files/files-map.service';
import { ViewModeService } from './services/view-mode.service';
import { ReanalysisService } from '@services/reanalysis.service';
@ -72,7 +72,7 @@ import { AnnotationsListingService } from './services/annotations-listing.servic
import { PdfProxyService } from './services/pdf-proxy.service';
import { ConfigService } from '@services/config.service';
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { SuggestionsService } from './services/suggestions.service';
const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
@ -87,7 +87,7 @@ export class FilePreviewScreenComponent
implements AfterViewInit, OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate
{
readonly circleButtonTypes = CircleButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
fullScreen = false;
readonly fileId = this.state.fileId;
readonly dossierId = this.state.dossierId;
@ -176,7 +176,6 @@ export class FilePreviewScreenComponent
const earmarks$ = isEarmarksViewMode$.pipe(
tap(() => this._loadingService.start()),
switchMap(() => this._fileDataService.loadEarmarks()),
switchMap(() => this._fileDataService.earmarks$),
tap(() => this.updateViewMode().then(() => this._loadingService.stop())),
);
@ -202,8 +201,8 @@ export class FilePreviewScreenComponent
);
return isChangingFromEarmarksViewMode$.pipe(
withLatestFrom(this._fileDataService.earmarks$),
map(([, earmarks]) => this.deleteAnnotations(earmarks.get(this.pdf.currentPage) ?? [], [])),
map(() => this._fileDataService.earmarks().get(this.pdf.currentPage) ?? []),
map(earmarks => this.deleteAnnotations(earmarks, [])),
);
}
@ -220,7 +219,7 @@ export class FilePreviewScreenComponent
switch (this._viewModeService.viewMode()) {
case ViewModes.STANDARD: {
const wrappers = await this._fileDataService.annotations;
const wrappers = this._fileDataService.annotations();
const ocrAnnotationIds = wrappers.filter(a => a.isOCR).map(a => a.id);
const standardEntries = annotations
.filter(a => !bool(a.getCustomData('changeLogRemoved')) && !this._annotationManager.isHidden(a.Id))
@ -288,7 +287,7 @@ export class FilePreviewScreenComponent
}
async ngOnAttach(previousRoute: ActivatedRouteSnapshot) {
if (!this.state.file.canBeOpened) {
if (!this.state.file().canBeOpened) {
return this._navigateToDossier();
}
@ -302,7 +301,7 @@ export class FilePreviewScreenComponent
}
async ngOnInit(): Promise<void> {
const file = this.state.file;
const file = this.state.file();
if (!file) {
return this._handleDeletedFile();
@ -329,7 +328,7 @@ export class FilePreviewScreenComponent
openManualAnnotationDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
return this._ngZone.run(() => {
const file = this.state.file;
const file = this.state.file();
this._dialogService.openDialog(
'manualAnnotation',
@ -403,7 +402,7 @@ export class FilePreviewScreenComponent
async viewerReady(pageNumber: string = this._activatedRoute.snapshot.queryParams.page) {
if (pageNumber) {
const file = this.state.file;
const file = this.state.file();
let page = parseInt(pageNumber, 10);
if (page < 1 || Number.isNaN(page)) {
@ -463,18 +462,22 @@ export class FilePreviewScreenComponent
);
const currentPageAnnotations$ = combineLatest([currentPageIfNotHighlightsView$, annotations$]).pipe(
tap(() => console.time('annotation-filter')),
map(
([page, [oldAnnotations, newAnnotations]]) =>
[oldAnnotations.filter(byPage(page)), newAnnotations.filter(byPage(page))] as const,
),
tap(() => console.timeEnd('annotation-filter')),
);
return combineLatest([currentPageAnnotations$, documentLoaded$]).pipe(
filter(([, loaded]) => loaded),
tap(() => console.time('loadAnnotations')),
map(([annotations]) => annotations),
switchMap(annotations => this.drawChangedAnnotations(...annotations)),
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)),
tap(() => this.updateViewMode()),
tap(() => console.timeEnd('loadAnnotations')),
);
}
@ -622,7 +625,7 @@ export class FilePreviewScreenComponent
this.addActiveScreenSubscription = this.state.blob$
.pipe(
tap(() => this._errorService.clear()),
switchMap(blob => this.pdf.loadDocument(blob, this.state.file, () => this.state.reloadBlob())),
switchMap(blob => this.pdf.loadDocument(blob, this.state.file(), () => this.state.reloadBlob())),
)
.subscribe();
@ -641,9 +644,9 @@ export class FilePreviewScreenComponent
this.addActiveScreenSubscription = this._viewerHeaderService.events$
.pipe(
filter(event => event.type === ViewerEvents.LOAD_ALL_ANNOTATIONS),
switchMap(() => this._fileDataService.annotations),
switchMap<AnnotationWrapper[], Observable<readonly [boolean, AnnotationWrapper[]]>>(annotations => {
switchMap(() => {
// TODO: this switchMap is ugly, to be refactored
const annotations = this._fileDataService.annotations();
const showWarning = !this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning);
const annotationsExceedThreshold = annotations.length >= this.configService.values.ANNOTATIONS_THRESHOLD;
@ -749,7 +752,7 @@ export class FilePreviewScreenComponent
const secondaryFilters = this._filterService.getGroup('secondaryFilters').filters;
const hasAnyFilterSet = [...primaryFilters, ...secondaryFilters].some(f => f.checked || f.indeterminate);
const autoExpandFilters = this.userPreferenceService.getAutoExpandFiltersOnActions();
const isReviewer = this.permissionsService.isFileAssignee(this.state.file);
const isReviewer = this.permissionsService.isFileAssignee(this.state.file());
const shouldExpandFilters = hasAnyFilterSet && autoExpandFilters && isReviewer;
if (!shouldExpandFilters) {

View File

@ -1,34 +1,28 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, switchMap } from 'rxjs';
import { filterEach, shareDistinctLast } from '@iqser/common-ui';
import { computed, Injectable, Signal, signal } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { filter, map } from 'rxjs/operators';
import { FileDataService } from './file-data.service';
@Injectable()
export class AnnotationReferencesService {
readonly annotation$: Observable<AnnotationWrapper>;
readonly references$: Observable<AnnotationWrapper[]>;
readonly references: Signal<AnnotationWrapper[]>;
readonly annotation: Signal<AnnotationWrapper | undefined>;
private readonly _annotation$ = new BehaviorSubject<AnnotationWrapper | undefined>(undefined);
readonly #annotation = signal<AnnotationWrapper | undefined>(undefined);
constructor(private readonly _fileDataService: FileDataService) {
this.annotation$ = this._annotation$.asObservable().pipe(shareDistinctLast());
this.references$ = this.#references$;
}
get #references$(): Observable<AnnotationWrapper[]> {
const annotations$ = this._fileDataService.annotations$.pipe(map(dict => Object.values(dict)));
return this.annotation$.pipe(
filter(annotation => !!annotation),
switchMap(({ reference }) => annotations$.pipe(filterEach(a => reference.includes(a.id)))),
);
this.annotation = this.#annotation.asReadonly();
this.references = computed(() => {
const annotation = this.annotation();
return annotation ? this._fileDataService.annotations().filter(a => annotation.reference.includes(a.id)) : [];
});
}
show(annotation: AnnotationWrapper) {
this._annotation$.next(annotation);
this.#annotation.set(annotation);
}
hide() {
this._annotation$.next(undefined);
this.#annotation.set(undefined);
}
}

View File

@ -10,15 +10,14 @@ import {
ViewModes,
} from '@red/domain';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { firstValueFrom, Observable, Subject } from 'rxjs';
import { Injectable, signal } from '@angular/core';
import { firstValueFrom, Observable } from 'rxjs';
import { effect, Injectable, Signal, signal } from '@angular/core';
import { FilePreviewStateService } from './file-preview-state.service';
import { ViewedPagesService } from '@services/files/viewed-pages.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { PermissionsService } from '@services/permissions.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { EntitiesService, shareLast, Toaster } from '@iqser/common-ui';
import { EntitiesService, Toaster } from '@iqser/common-ui';
import { RedactionLogService } from '@services/files/redaction-log.service';
import { EarmarksService } from '@services/files/earmarks.service';
import { ViewModeService } from './view-mode.service';
@ -30,7 +29,7 @@ import { DefaultColorsService } from '@services/entity-services/default-colors.s
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { SuggestionsService } from './suggestions.service';
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { toObservable } from '@angular/core/rxjs-interop';
const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
@ -45,10 +44,11 @@ export function chronologicallyBy<T>(property: (x: T) => string) {
@Injectable()
export class FileDataService extends EntitiesService<AnnotationWrapper, AnnotationWrapper> {
missingTypes = new Set<string>();
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
readonly annotations: Signal<AnnotationWrapper[]>;
readonly annotations$: Observable<AnnotationWrapper[]>;
readonly earmarks$: Observable<Map<number, AnnotationWrapper[]>>;
protected readonly _entityClass = AnnotationWrapper;
readonly #redactionLog$ = new Subject<IRedactionLog>();
readonly #annotations = signal<AnnotationWrapper[]>([]);
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
#originalViewedPages: ViewedPage[] = [];
@ -70,38 +70,15 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
private readonly _defaultColorsService: DefaultColorsService,
) {
super();
this.annotations$ = this.#annotations$;
this.earmarks$ = toObservable(this.#earmarks);
this._viewModeService.viewMode$
.pipe(
switchMap(viewMode =>
viewMode === ViewModes.TEXT_HIGHLIGHTS
? this.earmarks$.pipe(map(textHighlights => ([] as AnnotationWrapper[]).concat(...textHighlights.values())))
: this.annotations$.pipe(map(annotations => this.#getVisibleAnnotations(annotations, viewMode))),
),
tap(annotations => this.setEntities(annotations)),
takeUntilDestroyed(),
)
.subscribe();
}
get annotations() {
return firstValueFrom(this.annotations$);
}
get #annotations$() {
return this.#redactionLog$.pipe(
tap(() => console.time('buildAnnotations')),
withLatestFrom(this._state.file$),
tap(([redactionLog, file]) => this.#buildRemovedRedactions(redactionLog, file)),
switchMap(([redactionLog, file]) => this.#convertData(redactionLog, file)),
tap(() => this.#checkMissingTypes()),
map(annotations =>
this._userPreferenceService.areDevFeaturesEnabled ? annotations : annotations.filter(a => !a.isFalsePositive),
),
tap(() => console.timeEnd('buildAnnotations')),
shareLast(),
);
this.annotations$ = toObservable(this.#annotations);
this.annotations = this.#annotations.asReadonly();
this.earmarks = this.#earmarks.asReadonly();
effect(() => {
const viewMode = this._viewModeService.viewMode();
const earmarks = ([] as AnnotationWrapper[]).concat(...this.#earmarks().values());
const annotations = this.#getVisibleAnnotations(this.#annotations(), viewMode);
this.setEntities(viewMode === ViewModes.TEXT_HIGHLIGHTS ? earmarks : annotations);
});
}
setEntities(entities: AnnotationWrapper[]): void {
@ -137,14 +114,14 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
async annotationsChanged() {
this._multiSelectService.deactivate();
const file = this._state.file;
const fileReloaded = await firstValueFrom(this._filesService.reload(file.dossierId, file));
const file = this._state.file();
const fileReloaded = await this._filesService.reload(file.dossierId, file);
if (!fileReloaded) {
await this.loadAnnotations(file);
}
}
async loadEarmarks() {
async loadEarmarks(): Promise<Map<number, AnnotationWrapper[]>> {
const rawHighlights = await this._earmarksService.getEarmarks(this._state.dossierId, this._state.fileId);
const earmarks = rawHighlights.groupBy(h => h.pageNumber);
this.#earmarks.set(earmarks);
@ -153,14 +130,14 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
}
async loadRedactionLog() {
this._logger.info('[REDACTION-LOG] Loading redaction log...');
console.time('redaction-log');
const redactionLog$ = this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId);
const redactionLog = await firstValueFrom(redactionLog$);
this.#redactionLog$.next(redactionLog);
console.timeEnd('redaction-log');
this._logger.info('[REDACTION-LOG] Redaction log loaded', redactionLog);
return redactionLog;
this._logger.info('[REDACTION_LOG] Loading redaction log...');
const redactionLog = await this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId);
this._logger.info('[REDACTION_LOG] Redaction log loaded', redactionLog);
let [annotations] = await Promise.all([this.#convertData(redactionLog), this.#buildRemovedRedactions(redactionLog)]);
this.#checkMissingTypes();
annotations = this._userPreferenceService.areDevFeaturesEnabled ? annotations : annotations.filter(a => !a.isFalsePositive);
this.#annotations.set(annotations);
}
#checkMissingTypes() {
@ -199,7 +176,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
});
}
async #buildRemovedRedactions(redactionLog: IRedactionLog, file: File): Promise<void> {
async #buildRemovedRedactions(redactionLog: IRedactionLog): Promise<void> {
const redactionLogCopy: IRedactionLog = JSON.parse(JSON.stringify(redactionLog));
const redactionLogEntries: IRedactionLogEntry[] = [];
@ -217,15 +194,16 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
}
redactionLogCopy.redactionLogEntry = redactionLogEntries;
const annotations = await this.#convertData(redactionLogCopy, file);
const annotations = await this.#convertData(redactionLogCopy);
this._suggestionsService.removedRedactions = annotations.filter(a => !a.isSkipped);
}
async #convertData(redactionLog: IRedactionLog, file: File) {
async #convertData(redactionLog: IRedactionLog) {
if (!redactionLog.redactionLogEntry) {
return [];
}
const file = this._state.file();
const annotations: AnnotationWrapper[] = [];
const sourceIds: { [key: string]: AnnotationWrapper[] } = {};
const dictionaries = this._state.dictionaries;

View File

@ -1,4 +1,4 @@
import { Injectable, Injector } from '@angular/core';
import { Injectable, Injector, Signal } from '@angular/core';
import { combineLatest, firstValueFrom, from, merge, Observable, of, pairwise, Subject, switchMap } from 'rxjs';
import { Dictionary, Dossier, DOSSIER_ID, DOSSIER_TEMPLATE_ID, File, FILE_ID } from '@red/domain';
import { Router } from '@angular/router';
@ -14,6 +14,7 @@ import { HttpEvent, HttpEventType, HttpProgressEvent, HttpResponse } from '@angu
import { TranslateService } from '@ngx-translate/core';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
import { toSignal } from '@angular/core/rxjs-interop';
const ONE_MEGABYTE = 1024 * 1024;
@ -41,7 +42,7 @@ export class FilePreviewStateService {
readonly dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
readonly fileId = getParam(FILE_ID);
dossier: Dossier;
file: File;
readonly file: Signal<File>;
readonly dossierDictionary$: Observable<Dictionary>;
#dossierDictionary: Dictionary;
readonly #reloadBlob$ = new Subject();
@ -66,7 +67,8 @@ export class FilePreviewStateService {
) {
const dossiersService = dossiersServiceResolver(_injector, router);
this.dossier$ = dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.dossier = dossier)));
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId).pipe(tap(file => (this.file = file)));
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
this.file = toSignal(this.file$);
// this.file$ = combineLatest([this.isAttached, file$]).pipe(
// filter(([isAttached]) => isAttached),
// map(([, file]) => file),
@ -157,7 +159,7 @@ export class FilePreviewStateService {
const progress = Math.round((event.loaded / event.total) * 100);
const loading: string = this._translateService.instant('loading');
this._loadingService.update({
title: loading + ' ' + this.file.filename,
title: loading + ' ' + this.file().filename,
type: 'progress-bar',
value: progress,
remainingTime: this.#getRemainingTimeVerbose(event, startTime),

View File

@ -20,7 +20,7 @@ import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
import { NGXLogger } from 'ngx-logger';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { firstValueFrom, of } from 'rxjs';
function getResponseType(error: boolean, isConflict: boolean) {
@ -95,12 +95,12 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
addAnnotation(requests: List<IAddRedactionRequest>, dossierId: string, fileId: string, dictionaryLabel?: string) {
const toast = requests[0].addToDictionary ? this.#showAddToDictionaryToast(requests, dictionaryLabel) : this.#showToast('add');
const canAddRedaction = this._iqaerPermissionsService.has(ROLES.redactions.write);
const canAddRedaction = this._iqaerPermissionsService.has(Roles.redactions.write);
if (canAddRedaction && this._permissionsService.isApprover(this.#dossier(dossierId))) {
return this.add(requests, dossierId, fileId).pipe(toast);
}
const canRequestRedaction = this._iqaerPermissionsService.has(ROLES.redactions.request);
const canRequestRedaction = this._iqaerPermissionsService.has(Roles.redactions.request);
if (canRequestRedaction) {
return this.requestAdd(requests, dossierId, fileId).pipe(toast);
}

View File

@ -8,7 +8,7 @@ import { AnnotationActionsService } from './annotation-actions.service';
import { BASE_HREF_FN, IqserPermissionsService } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IHeaderElement } from '@red/domain';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
@Injectable()
@ -159,7 +159,7 @@ export class PdfAnnotationActionsService {
canAcceptRecommendation: permissions.reduce((acc, next) => acc && next.canAcceptRecommendation, true),
canAcceptSuggestion: permissions.reduce((acc, next) => acc && next.canAcceptSuggestion, true),
canUndo:
this.#iqserPermissionsService.has(ROLES.redactions.deleteManual) &&
this.#iqserPermissionsService.has(Roles.redactions.deleteManual) &&
permissions.reduce((acc, next) => acc && next.canUndo, true),
canMarkAsFalsePositive: permissions.reduce((acc, next) => acc && next.canMarkAsFalsePositive, true),
canForceRedaction: permissions.reduce((acc, next) => acc && next.canForceRedaction, true),

View File

@ -33,7 +33,7 @@ import { PermissionsService } from '@services/permissions.service';
import { AnnotationsListingService } from './annotations-listing.service';
import { PdfAnnotationActionsService } from './pdf-annotation-actions.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { AnnotationActionsService } from './annotation-actions.service';
import Annotation = Core.Annotations.Annotation;
import Quad = Core.Math.Quad;
@ -53,7 +53,7 @@ export class PdfProxyService {
readonly #visibilityOffIcon = this._convertPath('/assets/icons/general/visibility-off.svg');
readonly #visibilityIcon = this._convertPath('/assets/icons/general/visibility.svg');
readonly #falsePositiveIcon = this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg');
readonly #addRedactionIcon = this._iqserPermissionsService.has(ROLES.getRss)
readonly #addRedactionIcon = this._iqserPermissionsService.has(Roles.getRss)
? this._convertPath('/assets/icons/general/pdftron-action-add-component.svg')
: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg');
readonly #addDictIcon = this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg');
@ -148,7 +148,7 @@ export class PdfProxyService {
private _configureTextPopup() {
const popups: IHeaderElement[] = [];
if (!this._state.file.isApproved) {
if (!this._state.file().isApproved) {
// Adding directly to the false-positive dict is only available in dev-mode
if (this._userPreferenceService.areDevFeaturesEnabled) {
popups.push({
@ -160,7 +160,7 @@ export class PdfProxyService {
});
}
if (this._iqserPermissionsService.has(ROLES.redactions.write) || this._iqserPermissionsService.has(ROLES.redactions.request)) {
if (this._iqserPermissionsService.has(Roles.redactions.write) || this._iqserPermissionsService.has(Roles.redactions.request)) {
popups.push({
type: 'actionButton',
dataElement: TextPopups.ADD_REDACTION,
@ -169,7 +169,7 @@ export class PdfProxyService {
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION),
});
if (!this._iqserPermissionsService.has(ROLES.getRss)) {
if (!this._iqserPermissionsService.has(Roles.getRss)) {
popups.push({
type: 'actionButton',
dataElement: TextPopups.ADD_DICTIONARY,
@ -194,7 +194,7 @@ export class PdfProxyService {
}
private _handleCustomActions() {
const isCurrentPageExcluded = this._state.file.isPageExcluded(this._pdf.currentPage);
const isCurrentPageExcluded = this._state.file().isPageExcluded(this._pdf.currentPage);
if (this._viewModeService.isRedacted()) {
this._viewerHeaderService.enable([HeaderElements.TOGGLE_READABLE_REDACTIONS]);

View File

@ -29,7 +29,7 @@ export class StampService {
return;
}
const file = this._state.file;
const file = this._state.file();
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
try {

View File

@ -2,7 +2,7 @@ import { Injectable, Injector } from '@angular/core';
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
import { RotationType } from '@red/domain';
import { FileManagementService } from '@services/files/file-management.service';
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import {
ConfirmationDialogComponent,
ConfirmOption,
@ -21,8 +21,8 @@ import { REDDocumentViewer } from './document-viewer.service';
@Injectable()
export class PageRotationService {
readonly #rotations$ = new BehaviorSubject<Record<number, number>>({});
readonly rotations$: Observable<Record<number, number>>;
readonly #rotations$ = new BehaviorSubject<Record<number, number>>({});
constructor(
private readonly _pdf: PdfViewer,
@ -48,6 +48,10 @@ export class PageRotationService {
return Math.abs(rotationsSum < 360 ? rotationsSum : 360 - rotationsSum);
}
get #rotations() {
return this.#rotations$.value;
}
isRotated$(page: number) {
return this.#rotations$.pipe(
map(rotations => !!rotations[page]),
@ -55,7 +59,7 @@ export class PageRotationService {
);
}
applyRotation() {
async applyRotation() {
this._loadingService.start();
const pages = this.#rotations$.value;
const { dossierId, fileId } = this._pdf;
@ -73,9 +77,10 @@ export class PageRotationService {
const request$ = this._fileManagementService.rotatePage({ pages }, dossierId, fileId);
this.clearRotations();
const reloaded$ = request$.pipe(switchMap(() => this._filesService.reload(dossierId, file)));
await firstValueFrom(request$);
await this._filesService.reload(dossierId, file);
return firstValueFrom(reloaded$.pipe(tap(() => this._loadingService.stop())));
this._loadingService.stop();
}
discardRotation() {
@ -101,10 +106,6 @@ export class PageRotationService {
return this.hasRotations ? this.#showConfirmationDialog() : of(false);
}
get #rotations() {
return this.#rotations$.value;
}
#showConfirmationDialog() {
const ref = this._injector.get(MatDialog).open(ConfirmationDialogComponent, {
...defaultDialogConfig,

View File

@ -7,7 +7,7 @@ import { LongPressEvent } from '@shared/directives/long-press.directive';
import { UserPreferenceService } from '@users/user-preference.service';
import { FilesMapService } from '@services/files/files-map.service';
import { ReanalysisService } from '@services/reanalysis.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
@Component({
selector: 'redaction-dossiers-listing-actions [dossier]',
@ -15,7 +15,7 @@ import { ROLES } from '@users/roles';
})
export class DossiersListingActionsComponent implements OnChanges {
readonly circleButtonTypes = CircleButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
readonly scrollableParentViews = ScrollableParentViews;
readonly currentUser = getCurrentUser<User>();

View File

@ -29,7 +29,7 @@ import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.
import { FileAssignService } from '../../services/file-assign.service';
import { ViewerHeaderService } from '../../../pdf-viewer/services/viewer-header.service';
import { ROTATION_ACTION_BUTTONS } from '../../../pdf-viewer/utils/constants';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
@Component({
@ -145,7 +145,7 @@ export class FileActionsComponent implements OnChanges {
action: () => this.#openImportRedactionsDialog(),
tooltip: _('dossier-overview.import-redactions'),
icon: 'red:import_redactions',
show: this.showImportRedactions && !this._iqserPermissionsService.has(ROLES.getRss),
show: this.showImportRedactions && !this._iqserPermissionsService.has(Roles.getRss),
},
{
id: 'download-file-btn',
@ -176,7 +176,7 @@ export class FileActionsComponent implements OnChanges {
show:
!!this._excludedPagesService &&
this._permissionsService.canExcludePages(this.file, this.dossier) &&
!this._iqserPermissionsService.has(ROLES.getRss),
!this._iqserPermissionsService.has(Roles.getRss),
},
{
id: 'set-file-to-new-btn',

View File

@ -22,7 +22,7 @@ import { EditDossierTeamComponent } from './edit-dossier-team/edit-dossier-team.
import { PermissionsService } from '@services/permissions.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes';
@ -40,7 +40,7 @@ interface NavItem {
providers: [dossiersServiceProvider],
})
export class EditDossierDialogComponent extends BaseDialogComponent implements AfterViewInit {
readonly roles = ROLES;
readonly roles = Roles;
navItems: NavItem[] = [];
readonly iconButtonTypes = IconButtonTypes;
activeNav: Section;
@ -100,7 +100,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
return (
(['members'].includes(this.activeNav) &&
this.#currentUser.isManager &&
this.iqserPermissionsService.has(ROLES.dossiers.edit)) ||
this.iqserPermissionsService.has(Roles.dossiers.edit)) ||
this._permissionsService.canEditDossier(this._dossier)
);
}

View File

@ -1,7 +1,7 @@
import { Component, ElementRef, EventEmitter, inject, Input, OnChanges, Output, ViewChild } from '@angular/core';
import { CircleButtonTypes, getCurrentUser, List } from '@iqser/common-ui';
import { DossiersDialogService } from '../../../shared-dossiers/services/dossiers-dialog.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { User } from '@red/domain';
import { UserService } from '@users/user.service';
@ -12,7 +12,7 @@ import { UserService } from '@users/user.service';
})
export class TeamMembersComponent implements OnChanges {
readonly circleButtonTypes = CircleButtonTypes;
readonly roles = ROLES;
readonly roles = Roles;
readonly currentUser = getCurrentUser<User>();
readonly userService = inject(UserService);
@ -31,11 +31,6 @@ export class TeamMembersComponent implements OnChanges {
constructor(private readonly _dialogService: DossiersDialogService) {}
ngOnChanges() {
this.memberIds ??= [];
this.memberIds = [...this.memberIds].sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b)));
}
get maxTeamMembersBeforeExpand(): number {
return this.perLine - (this.canAdd ? 1 : 0);
}
@ -48,6 +43,11 @@ export class TeamMembersComponent implements OnChanges {
return this.memberIds.length > this.maxTeamMembersBeforeExpand ? this.memberIds.length - (this.maxTeamMembersBeforeExpand - 1) : 0;
}
ngOnChanges() {
this.memberIds ??= [];
this.memberIds = [...this.memberIds].sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b)));
}
toggleExpandedTeam() {
this.expandedTeam = !this.expandedTeam;
}

View File

@ -11,7 +11,7 @@ import { firstValueFrom } from 'rxjs';
import dayjs from 'dayjs';
import { Router } from '@angular/router';
import { DossiersDialogService } from '../../../shared-dossiers/services/dossiers-dialog.service';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
interface DialogData {
readonly dossierTemplateId?: string;
@ -22,7 +22,7 @@ interface DialogData {
styleUrls: ['./add-dossier-dialog.component.scss'],
})
export class AddDossierDialogComponent extends BaseDialogComponent implements OnInit {
readonly roles = ROLES;
readonly roles = Roles;
readonly iconButtonTypes = IconButtonTypes;
hasDueDate = false;
@ -33,8 +33,8 @@ export class AddDossierDialogComponent extends BaseDialogComponent implements On
}))
.filter(
element =>
!this.permissionsService.has(ROLES.getRss) ||
(this.permissionsService.has(ROLES.getRss) && !['DELTA_PREVIEW', 'REDACTED'].includes(element.key)),
!this.permissionsService.has(Roles.getRss) ||
(this.permissionsService.has(Roles.getRss) && !['DELTA_PREVIEW', 'REDACTED'].includes(element.key)),
);
dossierTemplates: IDossierTemplate[];
availableReportTypes: IReportTemplate[] = [];

View File

@ -18,7 +18,7 @@ export class EarmarksService extends GenericService<EarmarkResponse> {
map(response => response.highlights),
map(highlights => highlights.map(highlight => AnnotationWrapper.fromEarmark(highlight))),
map(highlights => highlights.sort((h1, h2) => h1.color.localeCompare(h2.color))),
catchError(() => of([])),
catchError(() => of([] as AnnotationWrapper[])),
);
return firstValueFrom(request);

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { EntitiesService, isArray, List, mapEach, QueryParam } from '@iqser/common-ui';
import { File, IFile } from '@red/domain';
import { firstValueFrom, Observable } from 'rxjs';
import { firstValueFrom } from 'rxjs';
import { UserService } from '@users/user.service';
import { FilesMapService } from './files-map.service';
import { map, switchMap, tap } from 'rxjs/operators';
@ -36,13 +36,12 @@ export class FilesService extends EntitiesService<IFile, File> {
return loadStats$.pipe(tap(files => this._filesMapService.set(dossierId, files)));
}
reload(dossierId: string, file: File): Observable<boolean> {
return super._getOne([dossierId, file.id]).pipe(
map(_file => new File(_file, this._userService.getName(_file.assignee))),
switchMap(_file => this._dossierStatsService.getFor([dossierId]).pipe(map(() => _file))),
map(_file => this._filesMapService.replace(dossierId, [_file])),
tap(() => this._logger.info('[FILE] Reloaded')),
);
async reload(dossierId: string, file: File) {
const _file = await firstValueFrom(super._getOne([dossierId, file.id]));
const reloadedFile = new File(_file, this._userService.getName(_file.assignee));
await firstValueFrom(this._dossierStatsService.getFor([dossierId]));
this._logger.info('[FILE] Reloaded');
return this._filesMapService.replace(dossierId, [reloadedFile]);
}
async setAssignee(files: File | List<File>, assigneeId: string) {

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { GenericService, QueryParam } from '@iqser/common-ui';
import { IRedactionLog, ISectionGrid } from '@red/domain';
import { catchError, tap } from 'rxjs/operators';
import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { firstValueFrom, of } from 'rxjs';
@Injectable({
providedIn: 'root',
@ -10,17 +10,16 @@ import { of } from 'rxjs';
export class RedactionLogService extends GenericService<unknown> {
protected readonly _defaultModelPath = '';
getRedactionLog(dossierId: string, fileId: string, withManualRedactions?: boolean) {
async getRedactionLog(dossierId: string, fileId: string, withManualRedactions?: boolean) {
const queryParams: QueryParam[] = [];
if (withManualRedactions) {
queryParams.push({ key: 'withManualRedactions', value: withManualRedactions });
}
const redactionLog$ = this._getOne<IRedactionLog>([dossierId, fileId], 'redactionLog', queryParams);
return redactionLog$.pipe(
tap(redactionLog => redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page)),
catchError(() => of({} as IRedactionLog)),
);
const redactionLog = await firstValueFrom(redactionLog$.pipe(catchError(() => of({} as IRedactionLog))));
redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page);
return redactionLog;
}
getSectionGrid(dossierId: string, fileId: string) {

View File

@ -4,7 +4,7 @@ import { DashboardStats, Dictionary, Dossier, DOSSIERS_ARCHIVE, DossierTemplate,
import { FilesMapService } from '@services/files/files-map.service';
import { FeaturesService } from '@services/features.service';
import { IqserPermissionsService } from '@iqser/common-ui';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { UserPreferenceService } from '@users/user-preference.service';
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
@ -25,16 +25,16 @@ export class PermissionsService {
canAddEditJustifications() {
return (
this._userPreferenceService.areDevFeaturesEnabled && this.isAdmin() && this._iqserPermissionsService.has(ROLES.legalBasis.write)
this._userPreferenceService.areDevFeaturesEnabled && this.isAdmin() && this._iqserPermissionsService.has(Roles.legalBasis.write)
);
}
canEditGlobalFileAttributes() {
return this._iqserPermissionsService.has(ROLES.fileAttributes.write) && this.isAdmin();
return this._iqserPermissionsService.has(Roles.fileAttributes.write) && this.isAdmin();
}
canEditGlobalDossierAttributes() {
return this._iqserPermissionsService.has(ROLES.dossierAttributes.writeConfig) && this.isAdmin();
return this._iqserPermissionsService.has(Roles.dossierAttributes.writeConfig) && this.isAdmin();
}
canDownloadEntityDictionary(): boolean {
@ -42,11 +42,11 @@ export class PermissionsService {
}
canPerformDossierStatesActions() {
return this.isAdmin() && this._iqserPermissionsService.has(ROLES.states.write);
return this.isAdmin() && this._iqserPermissionsService.has(Roles.states.write);
}
canEditEntities(): boolean {
return this._iqserPermissionsService.has(ROLES.dictionaryTypes.write) && this.isAdmin();
return this._iqserPermissionsService.has(Roles.dictionaryTypes.write) && this.isAdmin();
}
canDeleteEntities(entity: Dictionary | Dictionary[]): boolean {
@ -54,7 +54,7 @@ export class PermissionsService {
return (
entities.length &&
this.isAdmin() &&
this._iqserPermissionsService.has(ROLES.dictionaryTypes.delete) &&
this._iqserPermissionsService.has(Roles.dictionaryTypes.delete) &&
entities.reduce((acc, _entity) => this._canDeleteEntity(_entity) && acc, true)
);
}
@ -66,14 +66,14 @@ export class PermissionsService {
displayReanalyseBtn(dossier: Dossier): boolean {
return (
dossier.isActive &&
this._iqserPermissionsService.has(ROLES.dossiers.reanalyze) &&
this._iqserPermissionsService.has(Roles.dossiers.reanalyze) &&
this.isApprover(dossier) &&
!!this._filesMapService.get(dossier.id).find(f => f.analysisRequired)
);
}
canUploadFiles(dossier: Dossier): boolean {
return this._iqserPermissionsService.has(ROLES.files.upload) && dossier.isActive;
return this._iqserPermissionsService.has(Roles.files.upload) && dossier.isActive;
}
canDownloadCsvReport(dossier: Dossier): boolean {
@ -88,7 +88,7 @@ export class PermissionsService {
}
canToggleAnalysis(file: File | File[], dossier: Dossier): boolean {
if (!this._iqserPermissionsService.has(ROLES.files.reanalyze)) {
if (!this._iqserPermissionsService.has(Roles.files.reanalyze)) {
return false;
}
@ -98,27 +98,27 @@ export class PermissionsService {
}
showToggleAnalysis(dossier: Dossier): boolean {
return this._iqserPermissionsService.has(ROLES.excludeIncludeFile) && dossier.isActive;
return this._iqserPermissionsService.has(Roles.excludeIncludeFile) && dossier.isActive;
}
canReanalyseFile(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.reanalyze) &&
this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canReanalyseFile(_file, dossier) && acc, true)
);
}
canEnableAutoAnalysis(files: File[], dossier: Dossier): boolean {
return (
this._iqserPermissionsService.has(ROLES.files.reanalyze) &&
this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canEnableAutoAnalysis(_file, dossier) && acc, true)
);
}
canDisableAutoAnalysis(files: File[], dossier: Dossier): boolean {
return (
this._iqserPermissionsService.has(ROLES.files.reanalyze) &&
this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canDisableAutoAnalysis(_file, dossier) && acc, true)
);
}
@ -130,7 +130,7 @@ export class PermissionsService {
canSoftDeleteFile(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.delete) &&
this._iqserPermissionsService.has(Roles.files.delete) &&
files.reduce((acc, _file) => this._canSoftDeleteFile(_file, dossier) && acc, true)
);
}
@ -138,7 +138,7 @@ export class PermissionsService {
canRestoreFile(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.delete) &&
this._iqserPermissionsService.has(Roles.files.delete) &&
files.reduce((acc, _file) => this._canRestoreFile(_file, dossier) && acc, true)
);
}
@ -146,7 +146,7 @@ export class PermissionsService {
canHardDeleteFile(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.delete) &&
this._iqserPermissionsService.has(Roles.files.delete) &&
files.reduce((acc, _file) => this._canHardDeleteFile(_file, dossier) && acc, true)
);
}
@ -154,7 +154,7 @@ export class PermissionsService {
canOcrFile(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.reanalyze) &&
this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canOcrFile(_file, dossier) && acc, true)
);
}
@ -162,7 +162,7 @@ export class PermissionsService {
canAssignToSelf(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.setReviewer) &&
this._iqserPermissionsService.has(Roles.setReviewer) &&
files.reduce((acc, _file) => this._canAssignToSelf(_file, dossier) && acc, true)
);
}
@ -170,7 +170,7 @@ export class PermissionsService {
canAssignUser(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.setReviewer) &&
this._iqserPermissionsService.has(Roles.setReviewer) &&
files.reduce((acc, _file) => this._canAssignUser(_file, dossier) && acc, true)
);
}
@ -178,7 +178,7 @@ export class PermissionsService {
canUnassignUser(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.setReviewer) &&
this._iqserPermissionsService.has(Roles.setReviewer) &&
files.reduce((acc, _file) => this._canUnassignUser(_file, dossier) && acc, true)
);
}
@ -196,7 +196,7 @@ export class PermissionsService {
canBeApproved(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.setApproved) &&
this._iqserPermissionsService.has(Roles.files.setApproved) &&
files.reduce((acc, _file) => this._canBeApproved(_file, dossier) && acc, true)
);
}
@ -204,7 +204,7 @@ export class PermissionsService {
isReadyForApproval(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.setApproved) &&
this._iqserPermissionsService.has(Roles.files.setApproved) &&
files.reduce((acc, _file) => this._isReadyForApproval(_file, dossier) && acc, true)
);
}
@ -212,7 +212,7 @@ export class PermissionsService {
canSetUnderApproval(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.setUnderApproval) &&
this._iqserPermissionsService.has(Roles.files.setUnderApproval) &&
files.reduce((acc, _file) => this._canSetUnderApproval(_file, dossier) && acc, true)
);
}
@ -246,14 +246,14 @@ export class PermissionsService {
canUndoApproval(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file;
return (
this._iqserPermissionsService.has(ROLES.files.setApproved) &&
this._iqserPermissionsService.has(Roles.files.setApproved) &&
files.reduce((acc, _file) => this._canUndoApproval(_file, dossier) && acc, true)
);
}
canMarkPagesAsViewed(file: File): boolean {
return (
this._iqserPermissionsService.has(ROLES.manageViewedPages) &&
this._iqserPermissionsService.has(Roles.manageViewedPages) &&
(file.isUnderReview || file.isUnderApproval) &&
this.isFileAssignee(file)
);
@ -267,20 +267,20 @@ export class PermissionsService {
}
canDeleteDossier(dossier: IDossier): boolean {
return this._iqserPermissionsService.has(ROLES.dossiers.delete) && this.isOwner(dossier);
return this._iqserPermissionsService.has(Roles.dossiers.delete) && this.isOwner(dossier);
}
canRestoreDossier(dossier: IDossier): boolean {
return this._iqserPermissionsService.has(ROLES.dossiers.delete) && this.isOwner(dossier);
return this._iqserPermissionsService.has(Roles.dossiers.delete) && this.isOwner(dossier);
}
canCreateDossier(dossierTemplate: DossierTemplate | DashboardStats): boolean {
return this._iqserPermissionsService.has(ROLES.dossiers.write) && this.isManager() && dossierTemplate.isActive;
return this._iqserPermissionsService.has(Roles.dossiers.write) && this.isManager() && dossierTemplate.isActive;
}
canArchiveDossier(dossier: Dossier): boolean {
return (
this._iqserPermissionsService.has(ROLES.dossiers.archived) &&
this._iqserPermissionsService.has(Roles.dossiers.archived) &&
this._featuresService.isEnabled(DOSSIERS_ARCHIVE) &&
dossier.isActive &&
this.isOwner(dossier)
@ -288,7 +288,7 @@ export class PermissionsService {
}
canEditDossier(dossier: Dossier): boolean {
return this._iqserPermissionsService.has(ROLES.dossiers.edit) && this.isManager() && !!dossier?.ownerId;
return this._iqserPermissionsService.has(Roles.dossiers.edit) && this.isManager() && !!dossier?.ownerId;
}
canEditDossierDictionary(dossier: Dossier): boolean {
@ -296,16 +296,16 @@ export class PermissionsService {
dossier.isActive &&
this._dossierDictionariesMapService.get(dossier.id).length > 0 &&
this.isOwner(dossier) &&
this._iqserPermissionsService.has(ROLES.dossiers.dictionaryEntries.write)
this._iqserPermissionsService.has(Roles.dossiers.dictionaryEntries.write)
);
}
canEditDossierAttributes(dossier: Dossier): boolean {
return dossier.isActive && this.isOwner(dossier) && this._iqserPermissionsService.has(ROLES.dossierAttributes.write);
return dossier.isActive && this.isOwner(dossier) && this._iqserPermissionsService.has(Roles.dossierAttributes.write);
}
canEditTeamMembers(): boolean {
return this._iqserPermissionsService.has(ROLES.dossiers.edit) && this.isManager();
return this._iqserPermissionsService.has(Roles.dossiers.edit) && this.isManager();
}
isAdmin(): boolean {
@ -317,16 +317,16 @@ export class PermissionsService {
}
canAddComment(file: File, dossier: Dossier): boolean {
return this._iqserPermissionsService.has(ROLES.comments.write) && this.isAssigneeOrApprover(file, dossier) && !file.isApproved;
return this._iqserPermissionsService.has(Roles.comments.write) && this.isAssigneeOrApprover(file, dossier) && !file.isApproved;
}
canExcludePages(file: File, dossier: Dossier): boolean {
return this._iqserPermissionsService.has(ROLES.excludeIncludePages) && this.canPerformAnnotationActions(file, dossier);
return this._iqserPermissionsService.has(Roles.excludeIncludePages) && this.canPerformAnnotationActions(file, dossier);
}
canDeleteComment(comment: IComment, file: File, dossier: Dossier) {
return (
this._iqserPermissionsService.has(ROLES.comments.delete) &&
this._iqserPermissionsService.has(Roles.comments.delete) &&
(comment.user === this.#userId || this.isApprover(dossier)) &&
!file.isApproved
);
@ -334,7 +334,7 @@ export class PermissionsService {
canImportRedactions(file: File, dossier: Dossier) {
return (
this._iqserPermissionsService.has(ROLES.redactions.write) &&
this._iqserPermissionsService.has(Roles.redactions.write) &&
dossier.isActive &&
this.isFileAssignee(file) &&
!file.isApproved &&
@ -343,15 +343,15 @@ export class PermissionsService {
}
canRotatePage(file: File) {
return this.isFileAssignee(file) && this._iqserPermissionsService.has(ROLES.files.rotatePage);
return this.isFileAssignee(file) && this._iqserPermissionsService.has(Roles.files.rotatePage);
}
canDownloadRedactedFile() {
return this._iqserPermissionsService.has(ROLES.files.processDownload);
return this._iqserPermissionsService.has(Roles.files.processDownload);
}
canEditRules() {
return this._iqserPermissionsService.has(ROLES.rules.write) && this.isAdmin();
return this._iqserPermissionsService.has(Roles.rules.write) && this.isAdmin();
}
private _canDeleteEntity(entity: Dictionary): boolean {

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { GenericService, List, QueryParam, Toaster } from '@iqser/common-ui';
import { Dossier, File, IPageExclusionRequest } from '@red/domain';
import { catchError, switchMap, tap } from 'rxjs/operators';
import { catchError, tap } from 'rxjs/operators';
import { FilesService } from './files/files.service';
import { FilesMapService } from './files/files-map.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -26,12 +26,16 @@ export class ReanalysisService extends GenericService<unknown> {
super();
}
excludePages(body: IPageExclusionRequest, dossierId: string, file: File) {
return this._post(body, `exclude-pages/${dossierId}/${file.id}`).pipe(switchMap(() => this._filesService.reload(dossierId, file)));
async excludePages(body: IPageExclusionRequest, file: File) {
const dossierId = file.dossierId;
await firstValueFrom(this._post(body, `exclude-pages/${dossierId}/${file.id}`));
return await this._filesService.reload(dossierId, file);
}
includePages(body: IPageExclusionRequest, dossierId: string, file: File) {
return this._post(body, `include-pages/${dossierId}/${file.id}`).pipe(switchMap(() => this._filesService.reload(dossierId, file)));
async includePages(body: IPageExclusionRequest, file: File) {
const dossierId = file.dossierId;
await firstValueFrom(this._post(body, `include-pages/${dossierId}/${file.id}`));
return await this._filesService.reload(dossierId, file);
}
async reanalyzeFilesForDossier(files: List<File>, dossierId: string, params?: ReanalyzeQueryParams) {

View File

@ -1,4 +1,4 @@
export const ROLES = {
export const Roles = {
RED_CREATE_TENANT: 'red-create-tenant',
RED_DEPLOYMENT_INFO: 'red-deployment-info',
RED_GET_TENANTS: 'red-get-tenants',

View File

@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core';
import { User } from '@red/domain';
import { IIqserUser, IqserUserService, List, QueryParam } from '@iqser/common-ui';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { of } from 'rxjs';
@Injectable({
@ -15,19 +15,19 @@ export class UserService extends IqserUserService<IIqserUser, User> {
const currentUser = await super.loadCurrentUser();
this._permissionsService.add({
[ROLES.any]: (permission, all) => Object.keys(all).some(key => this._permissionsFilter(key)),
[Roles.any]: (permission, all) => Object.keys(all).some(key => this._permissionsFilter(key)),
});
return currentUser;
}
loadAll() {
const canReadUsers = this._permissionsService.has(ROLES.users.read);
const canReadUsers = this._permissionsService.has(Roles.users.read);
return canReadUsers ? super.loadAll() : of([]);
}
getAll() {
const canReadAllUsers = this._permissionsService.has(ROLES.users.readAll);
const canReadAllUsers = this._permissionsService.has(Roles.users.readAll);
const url = canReadAllUsers ? this._defaultModelPath : `${this._defaultModelPath}/red`;
return super.getAll(url);
}

View File

@ -10,7 +10,7 @@ import { FeaturesService } from '@services/features.service';
import { GeneralSettingsService } from '@services/general-settings.service';
import { tap } from 'rxjs/operators';
import { firstValueFrom } from 'rxjs';
import { ROLES } from '@users/roles';
import { Roles } from '@users/roles';
import { DossiersChangesService } from '@services/dossiers/dossier-changes.service';
import { ResolveFn } from '@angular/router';
@ -26,7 +26,7 @@ export const mainResolver: ResolveFn<void> = async () => {
logger.info('[ROUTES] Main resolver started...');
inject(FeaturesService).loadConfig();
if (inject(IqserPermissionsService).has(ROLES.dossiers.read)) {
if (inject(IqserPermissionsService).has(Roles.dossiers.read)) {
inject(DossiersChangesService).initialize();
}