Pull request #261: Common lib

Merge in RED/ui from common-lib to master

* commit '9df7530e5db2390971de3beaea78365cdc976c37':
  update scroll button
  move entities service to common lib
  remove duplicated methods
  update common lib
  move table column name to common lib
  move search service, update user wrapper and service
  move sort and humanize pipe
  mode sorting to common-lib
This commit is contained in:
Dan Percic 2021-08-09 15:40:23 +02:00
commit 1028f97e7a
106 changed files with 704 additions and 1314 deletions

View File

@ -1,7 +1,7 @@
<div class="red-top-bar">
<div class="top-bar-row">
<div *ngIf="!permissionsService.isUser()" class="menu-placeholder"></div>
<div *ngIf="permissionsService.isUser()" class="menu visible-lt-lg">
<div *ngIf="!currentUser.isUser" class="menu-placeholder"></div>
<div *ngIf="currentUser.isUser" class="menu visible-lt-lg">
<button [matMenuTriggerFor]="menuNav" mat-flat-button>
<mat-icon svgIcon="red:menu"></mat-icon>
</button>
@ -23,20 +23,19 @@
</button>
</mat-menu>
</div>
<div *ngIf="permissionsService.isUser()" class="menu flex-2 visible-lg breadcrumbs-container">
<a
*ngIf="dossiersView"
[routerLinkActiveOptions]="{ exact: true }"
class="breadcrumb"
routerLink="/main/dossiers"
routerLinkActive="active"
translate="top-bar.navigation-items.dossiers"
></a>
<a *ngIf="!dossiersView" class="breadcrumb back" redactionNavigateLastDossiersScreen>
<div *ngIf="currentUser.isUser" class="menu flex-2 visible-lg breadcrumbs-container">
<a *ngIf="(isDossiersView$ | async) === false" class="breadcrumb back" redactionNavigateLastDossiersScreen>
<mat-icon svgIcon="red:expand"></mat-icon>
{{ 'top-bar.navigation-items.back' | translate }}
</a>
<ng-container *ngIf="dossiersView">
<ng-container *ngIf="isDossiersView$ | async">
<a
[routerLinkActiveOptions]="{ exact: true }"
class="breadcrumb"
routerLink="/main/dossiers"
routerLinkActive="active"
translate="top-bar.navigation-items.dossiers"
></a>
<mat-icon *ngIf="appStateService.activeDossier" svgIcon="red:arrow-right"></mat-icon>
<a
*ngIf="appStateService.activeDossier"
@ -68,7 +67,7 @@
<div class="buttons">
<iqser-circle-button
(action)="openSpotlightSearch()"
*ngIf="!isSearchScreen"
*ngIf="(isSearchScreen$ | async) === false"
[icon]="'red:search'"
[tooltip]="'search.header-label' | translate"
tooltipPosition="below"
@ -76,7 +75,11 @@
<redaction-notifications *ngIf="userPreferenceService.areDevFeaturesEnabled"></redaction-notifications>
</div>
<redaction-user-button [matMenuTriggerFor]="userMenu" [showDot]="showPendingDownloadsDot" [user]="user"></redaction-user-button>
<redaction-user-button
[matMenuTriggerFor]="userMenu"
[showDot]="fileDownloadService.hasPendingDownloads"
[userId]="currentUser.id"
></redaction-user-button>
<mat-menu #userMenu="matMenu" xPosition="before">
<ng-container *ngFor="let item of userMenuItems; trackBy: trackByName">
@ -85,7 +88,7 @@
</button>
</ng-container>
<button (click)="logout()" mat-menu-item>
<button (click)="userService.logout()" mat-menu-item>
<mat-icon svgIcon="red:logout"></mat-icon>
<span translate="top-bar.navigation-items.my-account.children.logout"> </span>
</button>

View File

@ -1,9 +1,8 @@
import { Component } from '@angular/core';
import { UserService } from '@services/user.service';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { Router } from '@angular/router';
import { NavigationStart, Router } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { TranslateService } from '@ngx-translate/core';
@ -12,19 +11,33 @@ import { SpotlightSearchComponent } from '@components/spotlight-search/spotlight
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
import { SpotlightSearchDialogData } from '@components/spotlight-search/spotlight-search-dialog-data';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { distinctUntilChanged, filter, map, startWith } from 'rxjs/operators';
interface MenuItem {
name: string;
routerLink?: string;
show: boolean;
action?: () => void;
readonly name: string;
readonly routerLink?: string;
readonly show: boolean;
readonly action?: () => void;
}
const isNavigationStart = event => event instanceof NavigationStart;
const isDossiersView = url => url.includes('/main/dossiers') && !url.includes('/search');
const isSearchScreen = url => url.includes('/main/dossiers') && url.includes('/search');
@Component({
templateUrl: './base-screen.component.html',
styleUrls: ['./base-screen.component.scss']
})
export class BaseScreenComponent {
private readonly _navigationStart$ = this._router.events.pipe(
filter(isNavigationStart),
map((event: NavigationStart) => event.url),
startWith(this._router.url),
distinctUntilChanged()
);
readonly currentUser = this.userService.currentUser;
readonly isDossiersView$ = this._navigationStart$.pipe(map(isDossiersView));
readonly isSearchScreen$ = this._navigationStart$.pipe(map(isSearchScreen));
readonly userMenuItems: MenuItem[] = [
{
name: _('top-bar.navigation-items.my-account.children.my-profile'),
@ -34,60 +47,31 @@ export class BaseScreenComponent {
{
name: _('top-bar.navigation-items.my-account.children.admin'),
routerLink: '/main/admin',
show: this.permissionsService.isManager() || this.permissionsService.isUserAdmin(),
show: this.currentUser.isManager || this.currentUser.isUserAdmin,
action: this.appStateService.reset
},
{
name: _('top-bar.navigation-items.my-account.children.downloads'),
routerLink: '/main/downloads',
show: this.permissionsService.isUser()
show: this.currentUser.isUser
},
{
name: _('top-bar.navigation-items.my-account.children.trash'),
routerLink: '/main/admin/trash',
show: this.permissionsService.isManager() || this.permissionsService.isUserAdmin()
show: this.currentUser.isManager || this.currentUser.isUserAdmin
}
];
showSearch = false;
constructor(
readonly appStateService: AppStateService,
readonly permissionsService: PermissionsService,
readonly userService: UserService,
readonly userPreferenceService: UserPreferenceService,
readonly titleService: Title,
readonly fileDownloadService: FileDownloadService,
private readonly _router: Router,
private readonly _userService: UserService,
private readonly _translateService: TranslateService,
private readonly _dialog: MatDialog
) {
_router.events.subscribe(() => {
this._dossiersView = _router.url.includes('/main/dossiers') && !this.isSearchScreen;
});
}
private _dossiersView: boolean;
get dossiersView() {
return this._dossiersView;
}
get isSearchScreen() {
return this._router.url.includes('/search');
}
get user() {
return this._userService.user;
}
get showPendingDownloadsDot() {
return this.fileDownloadService.hasPendingDownloads;
}
get languages(): string[] {
return this._translateService.langs;
}
) {}
openSpotlightSearch() {
const spotlightSearchActions: SpotlightSearchAction[] = [
@ -95,12 +79,12 @@ export class BaseScreenComponent {
text: this._translateService.instant('search.this-dossier'),
icon: 'red:enter',
hide: !this.appStateService.activeDossier,
action: query => this._searchThisDossier(query)
action: query => this._search(query, this.appStateService.activeDossier.dossierId)
},
{
text: this._translateService.instant('search.entire-platform'),
icon: 'red:enter',
action: query => this._searchEntirePlatform(query)
action: query => this._search(query)
}
];
@ -112,23 +96,9 @@ export class BaseScreenComponent {
});
}
private _searchThisDossier(query: string) {
this._router
.navigate(['main/dossiers/search'], {
queryParams: {
query: query,
dossierId: this.appStateService.activeDossier.dossierId
}
})
.then();
}
private _searchEntirePlatform(query: string) {
this._router.navigate(['main/dossiers/search'], { queryParams: { query: query } }).then();
}
logout() {
this._userService.logout();
private _search(query: string, dossierId?: string) {
const queryParams = { query, dossierId };
this._router.navigate(['main/dossiers/search'], { queryParams }).then();
}
trackByName(index: number, item: MenuItem) {

View File

@ -7,12 +7,12 @@
[bulkActions]="bulkActions"
[hasEmptyColumn]="true"
[selectionEnabled]="true"
[tableColConfigs]="tableColConfigs"
[tableColumnConfigs]="tableColConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[text]="'downloads-list.no-data.title' | translate"
icon="red:download"
></redaction-empty-state>
@ -74,7 +74,7 @@
<ng-template #bulkActions>
<iqser-circle-button
(action)="deleteItems()"
*ngIf="screenStateService.areSomeEntitiesSelected$ | async"
*ngIf="entitiesService.areSomeSelected$ | async"
[tooltip]="'downloads-list.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"

View File

@ -2,32 +2,27 @@ import { Component, Injector, OnInit } from '@angular/core';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { DownloadStatusWrapper } from '@upload-download/model/download-status.wrapper';
import { DownloadControllerService } from '@redaction/red-ui-http';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, TableColumnConfig } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TableColConfig } from '@shared/components/table-col-name/table-col-name.component';
import { CircleButtonTypes } from '@iqser/common-ui';
@Component({
selector: 'redaction-downloads-list-screen',
templateUrl: './downloads-list-screen.component.html',
styleUrls: ['./downloads-list-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DownloadsListScreenComponent extends BaseListingComponent<DownloadStatusWrapper> implements OnInit {
readonly circleButtonTypes = CircleButtonTypes;
readonly itemSize = 80;
readonly tableColConfigs: TableColConfig[] = [
protected readonly _primaryKey = 'storageId';
readonly tableHeaderLabel = _('downloads-list.table-header.title');
readonly tableColConfigs: TableColumnConfig<DownloadStatusWrapper>[] = [
{ label: _('downloads-list.table-col-names.name') },
{ label: _('downloads-list.table-col-names.size') },
{ label: _('downloads-list.table-col-names.date') },
{ label: _('downloads-list.table-col-names.status') }
];
protected readonly _primaryKey = 'storageId';
protected readonly _tableHeaderLabel = _('downloads-list.table-header.title');
constructor(
readonly fileDownloadService: FileDownloadService,
@ -46,13 +41,13 @@ export class DownloadsListScreenComponent extends BaseListingComponent<DownloadS
}
async deleteItems(downloads?: DownloadStatusWrapper[]) {
const storageIds = (downloads || this.screenStateService.selectedEntities).map(d => d.storageId);
const storageIds = (downloads || this.entitiesService.selected).map(d => d.storageId);
await this._downloadControllerService.deleteDownload({ storageIds }).toPromise();
await this._loadData();
}
private async _loadData() {
await this.fileDownloadService.getDownloadStatus().toPromise();
this.screenStateService.setEntities(this.fileDownloadService.downloads);
this.entitiesService.setEntities(this.fileDownloadService.downloads);
}
}

View File

@ -98,12 +98,12 @@ export class UserProfileScreenComponent implements OnInit {
private _initializeForm(): void {
try {
this._profileModel = {
email: this._userService.user.email,
firstName: this._userService.user.firstName,
lastName: this._userService.user.lastName,
email: this._userService.currentUser.email,
firstName: this._userService.currentUser.firstName,
lastName: this._userService.currentUser.lastName,
language: this._languageService.currentLanguage
};
if (this._userService.user.email) {
if (this._userService.currentUser.email) {
// disable email if it's already set
this.formGroup.get('email').disable();
}

View File

@ -1,12 +1,7 @@
<redaction-side-nav [title]="translations[type] | translate">
<ng-container *ngFor="let item of items[type]">
<div
*ngIf="
(!item.onlyAdmin || permissionsService.isAdmin()) &&
(!item.onlyDevMode || userPreferenceService.areDevFeaturesEnabled) &&
(!item.userManagerOnly || permissionsService.canManageUsers()) &&
(!item.onlyManager || permissionsService.isManager())
"
*ngIf="!item.hideIf"
[routerLinkActiveOptions]="{ exact: false }"
[routerLink]="prefix + item.screen"
class="item"

View File

@ -1,12 +1,18 @@
import { Component, Input } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { AppStateService } from '@state/app-state.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { adminSideNavTranslations } from '../translations/admin-side-nav-translations';
import { UserService } from '@services/user.service';
type Type = 'settings' | 'dossierTemplates';
interface NavItem {
readonly label: string;
readonly screen: string;
readonly hideIf?: boolean;
}
@Component({
selector: 'redaction-admin-side-nav',
templateUrl: './admin-side-nav.component.html',
@ -14,29 +20,42 @@ type Type = 'settings' | 'dossierTemplates';
})
export class AdminSideNavComponent {
@Input() type: Type;
translations = adminSideNavTranslations;
readonly translations = adminSideNavTranslations;
readonly currentUser = this._userService.currentUser;
readonly prefix = this._appStateService.activeDictionaryType ? '../../' : '../';
items: {
[key in Type]: {
screen: string;
onlyDevMode?: boolean;
onlyAdmin?: boolean;
onlyManager?: boolean;
userManagerOnly?: boolean;
label: string;
}[];
} = {
readonly items: { readonly [key in Type]: NavItem[] } = {
settings: [
{ screen: 'dossier-templates', label: _('dossier-templates'), onlyManager: true },
{ screen: 'digital-signature', label: _('digital-signature'), onlyAdmin: true },
{ screen: 'license-info', label: _('license-information'), onlyAdmin: true },
{ screen: 'audit', label: _('audit'), onlyAdmin: true },
{ screen: 'users', label: _('user-management'), userManagerOnly: true },
{ screen: 'general-config', label: _('configurations'), onlyAdmin: true }
{
screen: 'dossier-templates',
label: _('dossier-templates'),
hideIf: !this.currentUser.isManager
},
{
screen: 'digital-signature',
label: _('digital-signature'),
hideIf: !this.currentUser.isAdmin
},
{
screen: 'license-info',
label: _('license-information'),
hideIf: !this.currentUser.isAdmin
},
{ screen: 'audit', label: _('audit'), hideIf: !this.currentUser.isAdmin },
{ screen: 'users', label: _('user-management'), hideIf: !this.currentUser.isUserAdmin },
{
screen: 'general-config',
label: _('configurations'),
hideIf: !this.currentUser.isAdmin
}
],
dossierTemplates: [
{ screen: 'dictionaries', label: _('dictionaries') },
{ screen: 'rules', onlyDevMode: true, label: _('rule-editor') },
{
screen: 'rules',
label: _('rule-editor'),
hideIf: !this.userPreferenceService.areDevFeaturesEnabled
},
{ screen: 'default-colors', label: _('default-colors') },
{ screen: 'watermark', label: _('watermark') },
{ screen: 'file-attributes', label: _('file-attributes') },
@ -46,16 +65,8 @@ export class AdminSideNavComponent {
};
constructor(
private readonly _userService: UserService,
private readonly _appStateService: AppStateService,
readonly userPreferenceService: UserPreferenceService,
readonly permissionsService: PermissionsService
readonly userPreferenceService: UserPreferenceService
) {}
get prefix() {
if (this._appStateService.activeDictionaryType) {
return '../../';
}
return '../';
}
}

View File

@ -6,8 +6,8 @@ import { Observable } from 'rxjs';
import { Toaster } from '@services/toaster.service';
import { TranslateService } from '@ngx-translate/core';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { humanize } from '../../../../utils/functions';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { humanize } from '@iqser/common-ui';
@Component({
selector: 'redaction-add-edit-dictionary-dialog',

View File

@ -1,6 +1,6 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { User } from '@redaction/red-ui-http';
import { UserWrapper } from '@services/user.service';
@Component({
selector: 'redaction-add-edit-user-dialog',
@ -10,7 +10,7 @@ import { User } from '@redaction/red-ui-http';
export class AddEditUserDialogComponent {
resettingPassword = false;
constructor(public dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) public user: User) {}
constructor(readonly dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: UserWrapper) {}
toggleResetPassword() {
this.resettingPassword = !this.resettingPassword;

View File

@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { UserService } from '@services/user.service';
import { FormBuilder, Validators } from '@angular/forms';
import { UserControllerService } from '@redaction/red-ui-http';
import { UserService, UserWrapper } from '@services/user.service';
import { LoadingService } from '@services/loading.service';
@Component({
@ -10,8 +10,10 @@ import { LoadingService } from '@services/loading.service';
styleUrls: ['./reset-password.component.scss']
})
export class ResetPasswordComponent {
passwordForm: FormGroup;
@Input() user: User;
readonly passwordForm = this._formBuilder.group({
temporaryPassword: [null, Validators.required]
});
@Input() user: UserWrapper;
@Output() toggleResetPassword = new EventEmitter();
constructor(
@ -19,14 +21,10 @@ export class ResetPasswordComponent {
private readonly _userControllerService: UserControllerService,
private readonly _userService: UserService,
private readonly _loadingService: LoadingService
) {
this.passwordForm = this._formBuilder.group({
temporaryPassword: [null, Validators.required]
});
}
) {}
get userName() {
return this._userService.getNameForId(this.user.userId);
return this._userService.getNameForId(this.user.id);
}
async save() {
@ -37,7 +35,7 @@ export class ResetPasswordComponent {
password: this.passwordForm.get('temporaryPassword').value,
temporary: true
},
this.user.userId
this.user.id
)
.toPromise();
this._loadingService.stop();

View File

@ -1,10 +1,11 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { UserControllerService } from '@redaction/red-ui-http';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { rolesTranslations } from '../../../../../translations/roles-translations';
import { IconButtonTypes } from '@iqser/common-ui';
import { UserWrapper } from '@services/user.service';
@Component({
selector: 'redaction-user-details',
@ -14,7 +15,7 @@ import { IconButtonTypes } from '@iqser/common-ui';
export class UserDetailsComponent implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
@Input() user: User;
@Input() user: UserWrapper;
@Output() toggleResetPassword = new EventEmitter();
@Output() closeDialog = new EventEmitter<any>();
userForm: FormGroup;
@ -99,7 +100,7 @@ export class UserDetailsComponent implements OnInit {
if (!this.user) {
await this._userControllerService.createUser(userData).toPromise();
} else {
await this._userControllerService.updateProfile(userData, this.user.userId).toPromise();
await this._userControllerService.updateProfile(userData, this.user.id).toPromise();
}
this.closeDialog.emit(true);

View File

@ -1,9 +1,10 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { UserControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { LoadingService } from '@services/loading.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserWrapper } from '@services/user.service';
@Component({
selector: 'redaction-confirm-delete-users-dialog',
@ -11,7 +12,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
styleUrls: ['./confirm-delete-users-dialog.component.scss']
})
export class ConfirmDeleteUsersDialogComponent {
checkboxes = [
readonly checkboxes = [
{ value: false, label: _('confirm-delete-users.impacted-dossiers') },
{ value: false, label: _('confirm-delete-users.impacted-documents') }
];
@ -22,12 +23,12 @@ export class ConfirmDeleteUsersDialogComponent {
private readonly _appStateService: AppStateService,
private readonly _loadingService: LoadingService,
private readonly _userControllerService: UserControllerService,
public dialogRef: MatDialogRef<ConfirmDeleteUsersDialogComponent>,
@Inject(MAT_DIALOG_DATA) public users: User[]
readonly dialogRef: MatDialogRef<ConfirmDeleteUsersDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly users: UserWrapper[]
) {
this.dossiersCount = this._appStateService.allDossiers.filter(dw => {
for (const user of this.users) {
if (dw.memberIds.indexOf(user.userId) !== -1) {
if (dw.memberIds.indexOf(user.id) !== -1) {
return true;
}
}
@ -42,7 +43,7 @@ export class ConfirmDeleteUsersDialogComponent {
async deleteUser() {
if (this.valid) {
this._loadingService.start();
await this._userControllerService.deleteUsers(this.users.map(u => u.userId)).toPromise();
await this._userControllerService.deleteUsers(this.users.map(u => u.id)).toPromise();
this.dialogRef.close(true);
} else {
this.showToast = true;

View File

@ -1,14 +1,14 @@
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: (screenStateService.allEntitiesLength$ | async) } }}
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: (entitiesService.allLength$ | async) } }}
</span>
<ng-container *ngIf="screenStateService.areSomeEntitiesSelected$ | async">
<ng-container *ngIf="entitiesService.areSomeSelected$ | async">
<iqser-circle-button
[matMenuTriggerFor]="readOnlyMenu"
[tooltip]="'file-attributes-csv-import.table-header.actions.read-only' | translate"
@ -54,32 +54,29 @@
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
[label]="'file-attributes-csv-import.table-col-names.name' | translate"
class="name"
></redaction-table-col-name>
<iqser-table-column-name [label]="'file-attributes-csv-import.table-col-names.name' | translate" class="name"></iqser-table-column-name>
<redaction-table-col-name [label]="'file-attributes-csv-import.table-col-names.type' | translate"></redaction-table-col-name>
<iqser-table-column-name [label]="'file-attributes-csv-import.table-col-names.type' | translate"></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-csv-import.table-col-names.read-only' | translate"
class="flex-center"
leftIcon="red:read-only"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-csv-import.table-col-names.primary' | translate"
[rightIconTooltip]="'file-attributes-csv-import.table-col-names.primary-info-tooltip' | translate"
class="flex-center"
rightIcon="red:status-info"
></redaction-table-col-name>
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[text]="'file-attributes-csv-import.no-data.title' | translate"
icon="red:attribute"
></redaction-empty-state>

View File

@ -1,6 +1,6 @@
@import '../../../../../../assets/styles/variables';
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding: 0 13px 0 10px !important;

View File

@ -1,52 +1,51 @@
import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Field } from '../file-attributes-csv-import-dialog.component';
import { FileAttributeConfig } from '@redaction/red-ui-http';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { fileAttributeTypesTranslations } from '../../../translations/file-attribute-types-translations';
import { CircleButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { fileAttributeTypesTranslations } from '../../../translations/file-attribute-types-translations';
@Component({
selector: 'redaction-active-fields-listing',
templateUrl: './active-fields-listing.component.html',
styleUrls: ['./active-fields-listing.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class ActiveFieldsListingComponent extends BaseListingComponent<Field> implements OnChanges {
protected readonly _primaryKey = 'csvColumn';
readonly circleButtonTypes = CircleButtonTypes;
readonly translations = fileAttributeTypesTranslations;
readonly typeOptions = [
FileAttributeConfig.TypeEnum.TEXT,
FileAttributeConfig.TypeEnum.NUMBER,
FileAttributeConfig.TypeEnum.DATE
] as const;
@Input() entities: Field[];
@Output() entitiesChange = new EventEmitter<Field[]>();
@Output() setHoveredColumn = new EventEmitter<string>();
@Output() toggleFieldActive = new EventEmitter<Field>();
readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
protected readonly _primaryKey = 'csvColumn';
constructor(protected readonly _injector: Injector) {
super(_injector);
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.entities) {
this.screenStateService.setEntities(this.entities);
this.screenStateService.updateSelection();
this.entitiesService.setEntities(this.entities);
this.entitiesService.updateSelection();
}
}
deactivateSelection() {
this.allEntities.filter(field => this.isSelected(field)).forEach(field => (field.primaryAttribute = false));
this.screenStateService.setEntities(this.allEntities.filter(field => !this.isSelected(field)));
this.entitiesService.setEntities(this.allEntities.filter(field => !this.isSelected(field)));
this.entitiesChange.emit(this.allEntities);
this.screenStateService.setSelectedEntities([]);
this.entitiesService.setSelected([]);
}
setAttributeForSelection(attribute: string, value: any) {
for (const item of this.screenStateService.selectedEntities) {
for (const item of this.entitiesService.selected) {
this.allEntities.find(f => f.csvColumn === item.csvColumn)[attribute] = value;
}
}

View File

@ -8,11 +8,7 @@ import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { Toaster } from '@services/toaster.service';
import { TranslateService } from '@ngx-translate/core';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
export interface Field {
@ -29,7 +25,7 @@ export interface Field {
@Component({
templateUrl: './file-attributes-csv-import-dialog.component.html',
styleUrls: ['./file-attributes-csv-import-dialog.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class FileAttributesCsvImportDialogComponent extends BaseListingComponent<Field> {
protected readonly _primaryKey = 'csvColumn';
@ -97,7 +93,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
this.parseResult.meta.fields = Object.keys(this.parseResult.data[0]);
}
this.screenStateService.setEntities(this.parseResult.meta.fields.map(field => this._buildAttribute(field)));
this.entitiesService.setEntities(this.parseResult.meta.fields.map(field => this._buildAttribute(field)));
this.activeFields = [];
for (const entity of this.allEntities) {

View File

@ -19,7 +19,7 @@ export class SmtpAuthDialogComponent {
@Inject(MAT_DIALOG_DATA) public data: SMTPConfigurationModel
) {
this.authForm = this._formBuilder.group({
user: [data?.user || this._userService.user.email, [Validators.required]],
user: [data?.user || this._userService.currentUser.email, [Validators.required]],
password: [data?.password, Validators.required]
});
}

View File

@ -9,7 +9,7 @@
<div class="actions">
<iqser-circle-button
*ngIf="permissionsService.isUser()"
*ngIf="currentUser.isUser"
[tooltip]="'common.close' | translate"
class="ml-6"
icon="red:close"
@ -87,23 +87,23 @@
</div>
<div class="table-header" redactionSyncWidth="table-item">
<redaction-table-col-name
<iqser-table-column-name
[label]="'audit-screen.table-col-names.message' | translate"
column="message"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'audit-screen.table-col-names.date' | translate"
column="date"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'audit-screen.table-col-names.user' | translate"
class="user-column"
column="user"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'audit-screen.table-col-names.category' | translate"
column="category"
></redaction-table-col-name>
></iqser-table-column-name>
<div class="scrollbar-placeholder"></div>
</div>

View File

@ -1,5 +1,4 @@
import { Component, OnDestroy } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { AuditControllerService, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http';
import { Moment } from 'moment';
@ -8,6 +7,7 @@ import { LoadingService } from '@services/loading.service';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { auditCategoriesTranslations } from '../../translations/audit-categories-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
const PAGE_SIZE = 50;
@ -19,7 +19,8 @@ const PAGE_SIZE = 50;
export class AuditScreenComponent extends AutoUnsubscribeComponent implements OnDestroy {
readonly ALL_CATEGORIES = 'allCategories';
readonly ALL_USERS = _('audit-screen.all-users');
translations = auditCategoriesTranslations;
readonly translations = auditCategoriesTranslations;
readonly currentUser = this._userService.currentUser;
filterForm: FormGroup;
categories: string[] = [];
@ -30,10 +31,10 @@ export class AuditScreenComponent extends AutoUnsubscribeComponent implements On
private _previousTo: Moment;
constructor(
readonly permissionsService: PermissionsService,
private readonly _userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _auditControllerService: AuditControllerService,
private readonly _loadingService: LoadingService
private readonly _loadingService: LoadingService,
private readonly _auditControllerService: AuditControllerService
) {
super();
this.filterForm = this._formBuilder.group({

View File

@ -22,21 +22,21 @@
<div class="content-container">
<div class="header-item">
<span class="all-caps-label">
{{ 'default-colors-screen.table-header.title' | translate: { length: screenStateService.allEntitiesLength$ | async } }}
{{ 'default-colors-screen.table-header.title' | translate: { length: entitiesService.allLength$ | async } }}
</span>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<redaction-table-col-name
<iqser-table-column-name
[label]="'default-colors-screen.table-col-names.key' | translate"
[withSort]="true"
column="key"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'default-colors-screen.table-col-names.color' | translate"
class="flex-center"
></redaction-table-col-name>
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>

View File

@ -5,20 +5,16 @@ import { ActivatedRoute } from '@angular/router';
import { PermissionsService } from '@services/permissions.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { DefaultColorType } from '@models/default-color-key.model';
import { SortingService } from '@services/sorting.service';
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
import { CircleButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { DefaultColorType } from '@models/default-color-key.model';
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
@Component({
templateUrl: './default-colors-screen.component.html',
styleUrls: ['./default-colors-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DefaultColorsScreenComponent
extends BaseListingComponent<{
@ -70,7 +66,7 @@ export class DefaultColorsScreenComponent
key,
value: data[key]
}));
this.screenStateService.setEntities(entities);
this.entitiesService.setEntities(entities);
this._loadingService.stop();
}
}

View File

@ -23,12 +23,12 @@
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'dictionary-listing.table-header.title' | translate: { length: (screenStateService.displayedLength$ | async) } }}
{{ 'dictionary-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
@ -57,33 +57,33 @@
</div>
</div>
<div [class.no-data]="screenStateService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="entitiesService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dictionary-listing.table-col-names.type' | translate"
[withSort]="true"
column="label"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dictionary-listing.table-col-names.order-of-importance' | translate"
[withSort]="true"
class="flex-center"
column="rank"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dictionary-listing.table-col-names.hint-redaction' | translate"
class="flex-center"
></redaction-table-col-name>
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
(action)="openAddEditDictionaryDialog()"
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dictionary-listing.no-data.action' | translate"
[showButton]="permissionsService.isAdmin()"
[text]="'dictionary-listing.no-data.title' | translate"
@ -156,7 +156,7 @@
<div class="right-container" redactionHasScrollbar>
<redaction-simple-doughnut-chart
*ngIf="(screenStateService.noData$ | async) === false"
*ngIf="(entitiesService.noData$ | async) === false"
[config]="chartData"
[counterText]="'dictionary-listing.stats.charts.entries' | translate"
[radius]="82"

View File

@ -15,7 +15,7 @@
}
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
}

View File

@ -9,13 +9,9 @@ import { ActivatedRoute } from '@angular/router';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '@services/loading.service';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { AdminDialogService } from '../../services/admin-dialog.service';
const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
value: dict.entries?.length ?? 0,
@ -27,7 +23,7 @@ const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
@Component({
templateUrl: './dictionary-listing-screen.component.html',
styleUrls: ['./dictionary-listing-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValueWrapper> implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
@ -56,7 +52,7 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
this._loadDictionaryData();
}
openDeleteDictionariesDialog($event?: MouseEvent, types = this.screenStateService.selectedEntities) {
openDeleteDictionariesDialog($event?: MouseEvent, types = this.entitiesService.selected) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dictionaryControllerService
@ -65,7 +61,7 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
this._appStateService.activeDossierTemplateId
)
.toPromise();
this.screenStateService.setSelectedEntities([]);
this.entitiesService.setSelected([]);
await this._appStateService.loadDictionaryData();
this._loadDictionaryData(false);
this._calculateData();
@ -96,13 +92,13 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);
if (!loadEntries)
this.screenStateService.setEntities(
this.entitiesService.setEntities(
entities.map(dict => {
dict.entries = this.allEntities.find(d => d.type === dict.type)?.entries || [];
return dict;
})
);
else this.screenStateService.setEntities(entities);
else this.entitiesService.setEntities(entities);
if (!loadEntries) return;

View File

@ -4,10 +4,7 @@
<redaction-admin-side-nav type="settings"></redaction-admin-side-nav>
<div>
<redaction-page-header
[pageLabel]="'digital-signature' | translate"
[showCloseButton]="permissionsService.isUser()"
></redaction-page-header>
<redaction-page-header [pageLabel]="'digital-signature' | translate" [showCloseButton]="currentUser.isUser"></redaction-page-header>
<div class="red-content-inner">
<div class="content-container">

View File

@ -2,12 +2,11 @@ import { Component, OnDestroy } from '@angular/core';
import { DigitalSignature, DigitalSignatureControllerService } from '@redaction/red-ui-http';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Toaster } from '@services/toaster.service';
import { PermissionsService } from '@services/permissions.service';
import { lastIndexOfEnd } from '@utils/functions';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { AutoUnsubscribeComponent, IconButtonTypes } from '@iqser/common-ui';
import { LoadingService } from '@services/loading.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IconButtonTypes } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-digital-signature-screen',
@ -16,6 +15,7 @@ import { IconButtonTypes } from '@iqser/common-ui';
})
export class DigitalSignatureScreenComponent extends AutoUnsubscribeComponent implements OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly currentUser = this._userService.currentUser;
digitalSignature: DigitalSignature;
digitalSignatureForm: FormGroup;
@ -23,11 +23,11 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribeComponent im
digitalSignatureExists = false;
constructor(
private readonly _digitalSignatureControllerService: DigitalSignatureControllerService,
private readonly _toaster: Toaster,
private readonly _formBuilder: FormBuilder,
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
readonly permissionsService: PermissionsService
private readonly _digitalSignatureControllerService: DigitalSignatureControllerService
) {
super();
this.loadDigitalSignatureAndInitializeForm();

View File

@ -20,23 +20,22 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<div *ngIf="(screenStateService.noData$ | async) === false" class="header-item">
<div *ngIf="(entitiesService.noData$ | async) === false" class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{
'dossier-attributes-listing.table-header.title'
| translate: { length: (screenStateService.displayedLength$ | async) }
'dossier-attributes-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) }
}}
</span>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event)"
*ngIf="permissionsService.isAdmin() && screenStateService.areSomeEntitiesSelected$ | async"
*ngIf="permissionsService.isAdmin() && entitiesService.areSomeSelected$ | async"
[tooltip]="'dossier-attributes-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
@ -59,24 +58,24 @@
</div>
</div>
<div *ngIf="(screenStateService.noData$ | async) === false" class="table-header" redactionSyncWidth="table-item">
<div *ngIf="(entitiesService.noData$ | async) === false" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dossier-attributes-listing.table-col-names.label' | translate"
[withSort]="true"
column="label"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dossier-attributes-listing.table-col-names.placeholder' | translate"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dossier-attributes-listing.table-col-names.type' | translate"
[withSort]="true"
column="type"
></redaction-table-col-name>
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
@ -84,7 +83,7 @@
<redaction-empty-state
(action)="openAddEditAttributeDialog($event)"
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dossier-attributes-listing.no-data.action' | translate"
[showButton]="permissionsService.isAdmin()"
[text]="'dossier-attributes-listing.no-data.title' | translate"

View File

@ -9,7 +9,7 @@
padding: 0 24px 0 10px;
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
}

View File

@ -1,23 +1,19 @@
import { Component, Injector, OnInit } from '@angular/core';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { DossierAttributeConfig } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { SortingService } from '@services/sorting.service';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { PermissionsService } from '@services/permissions.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
@Component({
templateUrl: './dossier-attributes-listing-screen.component.html',
styleUrls: ['./dossier-attributes-listing-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DossierAttributesListingScreenComponent extends BaseListingComponent<DossierAttributeConfig> implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
@ -45,7 +41,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: DossierAttributeConfig) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
const ids = dossierAttribute ? [dossierAttribute.id] : this.screenStateService.selectedEntities.map(item => item.id);
const ids = dossierAttribute ? [dossierAttribute.id] : this.entitiesService.selected.map(item => item.id);
await this._dossierAttributesService.deleteConfigs(ids);
await this._loadData();
});
@ -65,7 +61,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
private async _loadData() {
this._loadingService.start();
const attributes = await this._dossierAttributesService.getConfig();
this.screenStateService.setEntities(attributes);
this.entitiesService.setEntities(attributes);
this._loadingService.stop();
}
}

View File

@ -4,30 +4,27 @@
<redaction-admin-side-nav type="settings"></redaction-admin-side-nav>
<div>
<redaction-page-header
[pageLabel]="'dossier-templates' | translate"
[showCloseButton]="permissionsService.isUser()"
></redaction-page-header>
<redaction-page-header [pageLabel]="'dossier-templates' | translate" [showCloseButton]="currentUser.isUser"></redaction-page-header>
<div class="red-content-inner">
<div class="content-container">
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{
'dossier-templates-listing.table-header.title'
| translate: { length: (screenStateService.displayedLength$ | async) }
| translate: { length: (entitiesService.displayedLength$ | async) }
}}
</span>
<iqser-circle-button
(action)="openDeleteTemplatesDialog($event)"
*ngIf="canBulkDelete$(permissionsService.isAdmin()) | async"
*ngIf="canBulkDelete$(currentUser.isAdmin) | async"
[tooltip]="'dossier-templates-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
@ -42,7 +39,7 @@
<iqser-icon-button
(action)="openAddDossierTemplateDialog()"
*ngIf="permissionsService.isAdmin() && userPreferenceService.areDevFeaturesEnabled"
*ngIf="currentUser.isAdmin && userPreferenceService.areDevFeaturesEnabled"
[label]="'dossier-templates-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
@ -50,34 +47,34 @@
</div>
</div>
<div [class.no-data]="screenStateService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="entitiesService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
<iqser-table-column-name
[label]="'dossier-templates-listing.table-col-names.name' | translate"
[withSort]="true"
column="name"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dossier-templates-listing.table-col-names.created-by' | translate"
class="user-column"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dossier-templates-listing.table-col-names.created-on' | translate"
[withSort]="true"
column="dateAdded"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dossier-templates-listing.table-col-names.modified-on' | translate"
[withSort]="true"
column="dateModified"
></redaction-table-col-name>
></iqser-table-column-name>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[text]="'dossier-templates-listing.no-data.title' | translate"
icon="red:template"
></redaction-empty-state>

View File

@ -5,7 +5,7 @@
padding: 0 24px 0 10px;
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
}

View File

@ -1,37 +1,34 @@
import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
import { LoadingService } from '@services/loading.service';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { SortingService } from '@services/sorting.service';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { UserService } from '@services/user.service';
@Component({
templateUrl: './dossier-templates-listing-screen.component.html',
styleUrls: ['./dossier-templates-listing-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DossierTemplatesListingScreenComponent extends BaseListingComponent<DossierTemplateModelWrapper> implements OnInit {
protected readonly _primaryKey = 'name';
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
protected readonly _primaryKey = 'name';
readonly currentUser = this._userService.currentUser;
constructor(
private readonly _dialogService: AdminDialogService,
private readonly _appStateService: AppStateService,
private readonly _loadingService: LoadingService,
private readonly _dossierTemplateControllerService: DossierTemplateControllerService,
protected readonly _injector: Injector,
readonly permissionsService: PermissionsService,
readonly userPreferenceService: UserPreferenceService
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
private readonly _appStateService: AppStateService,
private readonly _dialogService: AdminDialogService,
readonly userPreferenceService: UserPreferenceService,
private readonly _dossierTemplateControllerService: DossierTemplateControllerService
) {
super(_injector);
}
@ -44,9 +41,9 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
return this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dossierTemplateControllerService
.deleteDossierTemplates(this.screenStateService.selectedEntities.map(d => d.dossierTemplateId))
.deleteDossierTemplates(this.entitiesService.selected.map(d => d.dossierTemplateId))
.toPromise();
this.screenStateService.setSelectedEntities([]);
this.entitiesService.setSelected([]);
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
this.loadDossierTemplatesData();
@ -56,7 +53,7 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
loadDossierTemplatesData() {
this._loadingService.start();
this._appStateService.reset();
this.screenStateService.setEntities(this._appStateService.dossierTemplates);
this.entitiesService.setEntities(this._appStateService.dossierTemplates);
this._loadDossierTemplateStats();
this._loadingService.stop();
}
@ -70,7 +67,7 @@ export class DossierTemplatesListingScreenComponent extends BaseListingComponent
}
private _loadDossierTemplateStats() {
this.screenStateService.allEntities.forEach(rs => {
this.entitiesService.all.forEach(rs => {
const dictionaries = this._appStateService.dictionaryData[rs.dossierTemplateId];
if (dictionaries) {
rs.dictionariesCount = Object.keys(dictionaries)

View File

@ -23,14 +23,12 @@
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{
'file-attributes-listing.table-header.title' | translate: { length: (screenStateService.displayedLength$ | async) }
}}
{{ 'file-attributes-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
@ -68,38 +66,38 @@
</div>
</div>
<div [class.no-data]="screenStateService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="entitiesService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.name' | translate"
[withSort]="true"
column="label"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.type' | translate"
[withSort]="true"
column="type"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.read-only' | translate"
[withSort]="true"
class="flex-center"
column="editable"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.csv-column' | translate"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.primary' | translate"
[rightIconTooltip]="'file-attributes-listing.table-col-names.primary-info-tooltip' | translate"
class="flex-center"
rightIcon="red:status-info"
></redaction-table-col-name>
></iqser-table-column-name>
<div></div>
@ -107,7 +105,7 @@
</div>
<redaction-empty-state
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[text]="'file-attributes-listing.no-data.title' | translate"
icon="red:attribute"
></redaction-empty-state>

View File

@ -9,7 +9,7 @@
padding: 0 24px 0 10px;
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
}

View File

@ -5,19 +5,15 @@ import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
@Component({
templateUrl: './file-attributes-listing-screen.component.html',
styleUrls: ['./file-attributes-listing-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class FileAttributesListingScreenComponent extends BaseListingComponent<FileAttributeConfig> implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
@ -69,7 +65,7 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
} else {
await this._fileAttributesService
.deleteFileAttributes(
this.screenStateService.selectedEntities.map(f => f.id),
this.entitiesService.selected.map(f => f.id),
this._appStateService.activeDossierTemplateId
)
.toPromise();
@ -102,7 +98,7 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
.getFileAttributesConfiguration(this._appStateService.activeDossierTemplateId)
.toPromise();
this._existingConfiguration = response;
this.screenStateService.setEntities(response?.fileAttributeConfigs || []);
this.entitiesService.setEntities(response?.fileAttributeConfigs || []);
} catch (e) {}
this._loadingService.stop();

View File

@ -9,7 +9,7 @@
<div class="actions">
<iqser-circle-button
*ngIf="permissionsService.isUser()"
*ngIf="currentUser.isUser"
class="ml-6"
icon="red:close"
redactionNavigateLastDossiersScreen

View File

@ -1,5 +1,4 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AdminDialogService } from '../../services/admin-dialog.service';
import {
@ -9,11 +8,11 @@ import {
SMTPConfigurationModel
} from '@redaction/red-ui-http';
import { AppConfigService } from '@app-config/app-config.service';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { AutoUnsubscribeComponent, IconButtonTypes } from '@iqser/common-ui';
import { Toaster } from '@services/toaster.service';
import { LoadingService } from '@services/loading.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IconButtonTypes } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-general-config-screen',
@ -22,6 +21,8 @@ import { IconButtonTypes } from '@iqser/common-ui';
})
export class GeneralConfigScreenComponent extends AutoUnsubscribeComponent implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly configForm: FormGroup;
readonly smtpForm: FormGroup;
@ -29,14 +30,14 @@ export class GeneralConfigScreenComponent extends AutoUnsubscribeComponent imple
private _initialSMTPConfiguration: SMTPConfigurationModel;
constructor(
readonly permissionsService: PermissionsService,
private readonly _smtpConfigService: SmtpConfigurationControllerService,
private readonly _appConfigService: AppConfigService,
private readonly _formBuilder: FormBuilder,
private readonly _toaster: Toaster,
private readonly _userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _dialogService: AdminDialogService,
private readonly _generalSettingsControllerService: GeneralSettingsControllerService,
private readonly _loadingService: LoadingService
private readonly _appConfigService: AppConfigService,
private readonly _smtpConfigService: SmtpConfigurationControllerService,
private readonly _generalSettingsControllerService: GeneralSettingsControllerService
) {
super();

View File

@ -7,7 +7,7 @@
<redaction-page-header
[buttonConfigs]="buttonConfigs"
[pageLabel]="'license-information' | translate"
[showCloseButton]="permissionsService.isUser()"
[showCloseButton]="currentUser.isUser"
></redaction-page-header>
<div class="red-content-inner">

View File

@ -1,13 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { LicenseReport, LicenseReportControllerService } from '@redaction/red-ui-http';
import { AppConfigService } from '@app-config/app-config.service';
import * as moment from 'moment';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '../../../../services/loading.service';
import { ButtonConfig } from '../../../shared/components/page-header/models/button-config.model';
import { LoadingService } from '@services/loading.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { IconButtonTypes } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-license-information-screen',
@ -15,6 +15,15 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
styleUrls: ['./license-information-screen.component.scss']
})
export class LicenseInformationScreenComponent implements OnInit {
readonly currentUser = this._userService.currentUser;
readonly buttonConfigs: ButtonConfig[] = [
{
label: _('license-info-screen.email-report'),
action: () => this.sendMail(),
type: IconButtonTypes.primary
}
];
currentInfo: LicenseReport = {};
totalInfo: LicenseReport = {};
unlicensedInfo: LicenseReport = {};
@ -34,20 +43,13 @@ export class LicenseInformationScreenComponent implements OnInit {
group: 'Ordinal',
domain: ['#0389ec']
};
buttonConfigs: ButtonConfig[] = [
{
label: _('license-info-screen.email-report'),
action: () => this.sendMail(),
type: IconButtonTypes.primary
}
];
constructor(
readonly permissionsService: PermissionsService,
private readonly _userService: UserService,
readonly appConfigService: AppConfigService,
private readonly _licenseReportController: LicenseReportControllerService,
private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService,
private readonly _loadingService: LoadingService
private readonly _licenseReportController: LicenseReportControllerService
) {
_loadingService.start();
}

View File

@ -8,17 +8,17 @@
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'trash.table-header.title' | translate: { length: (screenStateService.displayedLength$ | async) } }}
{{ 'trash.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
(action)="bulkRestore()"
*ngIf="screenStateService.areSomeEntitiesSelected$ | async"
*ngIf="entitiesService.areSomeSelected$ | async"
[tooltip]="'trash.bulk.restore' | translate"
icon="red:put-back"
[type]="circleButtonTypes.dark"
@ -26,40 +26,37 @@
<iqser-circle-button
(action)="bulkDelete()"
*ngIf="screenStateService.areSomeEntitiesSelected$ | async"
*ngIf="entitiesService.areSomeSelected$ | async"
[tooltip]="'trash.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</div>
<div [class.no-data]="screenStateService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="entitiesService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
<iqser-table-column-name
[label]="'trash.table-col-names.name' | translate"
[withSort]="true"
column="dossierName"
></redaction-table-col-name>
<redaction-table-col-name
[label]="'trash.table-col-names.owner' | translate"
class="user-column"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name [label]="'trash.table-col-names.owner' | translate" class="user-column"></iqser-table-column-name>
<iqser-table-column-name
[label]="'trash.table-col-names.deleted-on' | translate"
[withSort]="true"
column="softDeletedTime"
></redaction-table-col-name>
<redaction-table-col-name
></iqser-table-column-name>
<iqser-table-column-name
[label]="'trash.table-col-names.time-to-restore' | translate"
[withSort]="true"
column="softDeletedTime"
></redaction-table-col-name>
></iqser-table-column-name>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[text]="'trash.no-data.title' | translate"
icon="red:template"
></redaction-empty-state>

View File

@ -9,7 +9,7 @@
}
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
}

View File

@ -4,15 +4,11 @@ import { Dossier } from '@redaction/red-ui-http';
import { LoadingService } from '@services/loading.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import * as moment from 'moment';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { CircleButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { ConfirmationDialogInput, TitleColors } from '../../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
const HOURS_IN_A_DAY = 24;
@ -22,12 +18,12 @@ const MINUTES_IN_AN_HOUR = 60;
templateUrl: './trash-screen.component.html',
styleUrls: ['./trash-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService, DossiersService]
providers: [...DefaultListingServices, DossiersService]
})
export class TrashScreenComponent extends BaseListingComponent<Dossier> implements OnInit {
readonly circleButtonTypes = CircleButtonTypes;
readonly itemSize = 80;
protected readonly _primaryKey = 'dossierName';
readonly circleButtonTypes = CircleButtonTypes;
private readonly _deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS);
constructor(
@ -50,7 +46,7 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
}
async loadDossierTemplatesData(): Promise<void> {
this.screenStateService.setEntities(await this._dossiersService.getDeleted());
this.entitiesService.setEntities(await this._dossiersService.getDeleted());
}
canRestore(softDeletedTime: string): boolean {
@ -70,11 +66,11 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
return moment(softDeletedTime).add(this._deleteRetentionHours, 'hours').toISOString();
}
bulkDelete(dossiers = this.screenStateService.selectedEntities) {
bulkDelete(dossiers = this.entitiesService.selected) {
this._loadingService.loadWhile(this._hardDelete(dossiers));
}
bulkRestore(dossierIds = this.screenStateService.selectedEntities.map(d => d.dossierId)) {
bulkRestore(dossierIds = this.entitiesService.selected.map(d => d.dossierId)) {
this._loadingService.loadWhile(this._restore(dossierIds));
}
@ -107,8 +103,8 @@ export class TrashScreenComponent extends BaseListingComponent<Dossier> implemen
}
private _removeFromList(ids: string[]): void {
const entities = this.screenStateService.allEntities.filter(e => !ids.includes(e.dossierId));
this.screenStateService.setEntities(entities);
this.screenStateService.setSelectedEntities([]);
const entities = this.entitiesService.all.filter(e => !ids.includes(e.dossierId));
this.entitiesService.setEntities(entities);
this.entitiesService.setSelected([]);
}
}

View File

@ -15,13 +15,13 @@
></redaction-input-with-action>
<iqser-icon-button
(action)="openAddEditUserDialog($event)"
*ngIf="permissionsService.isUserAdmin()"
*ngIf="currentUser.isUserAdmin"
[label]="'user-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
<iqser-circle-button
*ngIf="permissionsService.isUser()"
*ngIf="currentUser.isUser"
[tooltip]="'common.close' | translate"
class="ml-6"
icon="red:close"
@ -36,17 +36,17 @@
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'user-listing.table-header.title' | translate: { length: (screenStateService.displayedLength$ | async) } }}
{{ 'user-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
(action)="bulkDelete()"
*ngIf="screenStateService.areSomeEntitiesSelected$ | async"
*ngIf="entitiesService.areSomeSelected$ | async"
[disabled]="(canDeleteSelected$ | async) === false"
[tooltip]="
(canDeleteSelected$ | async)
@ -62,16 +62,16 @@
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name [label]="'user-listing.table-col-names.name' | translate"></redaction-table-col-name>
<iqser-table-column-name [label]="'user-listing.table-col-names.name' | translate"></iqser-table-column-name>
<redaction-table-col-name [label]="'user-listing.table-col-names.email' | translate"></redaction-table-col-name>
<iqser-table-column-name [label]="'user-listing.table-col-names.email' | translate"></iqser-table-column-name>
<redaction-table-col-name
<iqser-table-column-name
[label]="'user-listing.table-col-names.active' | translate"
class="flex-center"
></redaction-table-col-name>
></iqser-table-column-name>
<redaction-table-col-name [label]="'user-listing.table-col-names.roles' | translate"></redaction-table-col-name>
<iqser-table-column-name [label]="'user-listing.table-col-names.roles' | translate"></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
@ -87,17 +87,13 @@
</div>
<div>
<redaction-initials-avatar
[showYou]="true"
[userId]="user.userId"
[withName]="true"
></redaction-initials-avatar>
<redaction-initials-avatar [showYou]="true" [userId]="user.id" [withName]="true"></redaction-initials-avatar>
</div>
<div class="small-label">{{ user.email || '-' }}</div>
<div class="center">
<mat-slide-toggle
(toggleChange)="toggleActive(user)"
[checked]="userService.isActive(user)"
[checked]="user.isActive"
color="primary"
></mat-slide-toggle>
</div>
@ -112,7 +108,7 @@
></iqser-circle-button>
<iqser-circle-button
(action)="openDeleteUsersDialog([user], $event)"
[disabled]="user.userId === userService.userId"
[disabled]="user.id === userService.currentUser.id"
[tooltip]="'user-listing.action.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"

View File

@ -3,7 +3,7 @@
padding: 0 24px 0 10px;
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding: 0 13px 0 10px !important;
}

View File

@ -1,55 +1,51 @@
import { Component, Injector, OnInit, QueryList, ViewChildren } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { UserService } from '@services/user.service';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { UserService, UserWrapper } from '@services/user.service';
import { UserControllerService } from '@redaction/red-ui-http';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateChartService } from '@services/translate-chart.service';
import { LoadingService } from '@services/loading.service';
import { InitialsAvatarComponent } from '@shared/components/initials-avatar/initials-avatar.component';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { rolesTranslations } from '../../../../translations/roles-translations';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
@Component({
templateUrl: './user-listing-screen.component.html',
styleUrls: ['./user-listing-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class UserListingScreenComponent extends BaseListingComponent<User> implements OnInit {
export class UserListingScreenComponent extends BaseListingComponent<UserWrapper> implements OnInit {
protected readonly _primaryKey = 'id';
readonly translations = rolesTranslations;
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this.userService.currentUser;
readonly canDeleteSelected$ = this._canDeleteSelected$;
collapsedDetails = false;
chartData: DoughnutChartConfig[] = [];
readonly translations = rolesTranslations;
protected readonly _primaryKey = 'userId';
@ViewChildren(InitialsAvatarComponent)
private readonly _avatars: QueryList<InitialsAvatarComponent>;
constructor(
readonly permissionsService: PermissionsService,
readonly userService: UserService,
private readonly _translateService: TranslateService,
private readonly _dialogService: AdminDialogService,
private readonly _userControllerService: UserControllerService,
private readonly _translateChartService: TranslateChartService,
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
protected readonly _injector: Injector
private readonly _dialogService: AdminDialogService,
private readonly _translateService: TranslateService,
private readonly _userControllerService: UserControllerService,
private readonly _translateChartService: TranslateChartService
) {
super(_injector);
}
get _canDeleteSelected$(): Observable<boolean> {
const entities$ = this.screenStateService.selectedEntities$;
return entities$.pipe(map(all => all.indexOf(this.userService.user) === -1));
const entities$ = this.entitiesService.selected$;
return entities$.pipe(map(all => all.indexOf(this.currentUser) === -1));
}
async ngOnInit() {
@ -57,19 +53,19 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
this.searchService.setSearchKey('searchKey');
}
openAddEditUserDialog($event: MouseEvent, user?: User) {
openAddEditUserDialog($event: MouseEvent, user?: UserWrapper) {
this._dialogService.openDialog('addEditUser', $event, user, async () => {
await this._loadData();
});
}
openDeleteUsersDialog(users: User[], $event?: MouseEvent) {
openDeleteUsersDialog(users: UserWrapper[], $event?: MouseEvent) {
this._dialogService.openDialog('deleteUsers', $event, users, async () => {
await this._loadData();
});
}
getDisplayRoles(user: User) {
getDisplayRoles(user: UserWrapper) {
const separator = ', ';
return (
user.roles.map(role => this._translateService.instant(this.translations[role])).join(separator) ||
@ -77,20 +73,20 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
);
}
async toggleActive(user: User) {
async toggleActive(user: UserWrapper) {
this._loadingService.start();
user.roles = this.userService.isActive(user) ? [] : ['RED_USER'];
await this._userControllerService.updateProfile(user, user.userId).toPromise();
user.roles = user.isActive ? [] : ['RED_USER'];
await this._userControllerService.updateProfile(user, user.id).toPromise();
await this._loadData();
this._avatars.find(item => item.userId === user.userId).detectChanges();
this._avatars.find(item => item.userId === user.id).detectChanges();
}
bulkDelete() {
this.openDeleteUsersDialog(this.screenStateService.allEntities.filter(u => this.isSelected(u)));
this.openDeleteUsersDialog(this.entitiesService.all.filter(u => this.isSelected(u)));
}
private async _loadData() {
this.screenStateService.setEntities(await this.userService.loadAllUsers());
this.entitiesService.setEntities(await this.userService.loadAllUsers());
await this.userService.loadAllUsers();
this._computeStats();
this._loadingService.stop();
@ -100,7 +96,7 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
this.chartData = this._translateChartService.translateRoles(
[
{
value: this.allEntities.filter(user => !this.userService.isActive(user)).length,
value: this.allEntities.filter(user => !user.isActive).length,
color: 'INACTIVE',
label: 'INACTIVE'
},
@ -110,22 +106,22 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
label: 'REGULAR'
},
{
value: this.allEntities.filter(user => this.userService.isManager(user) && !this.userService.isAdmin(user)).length,
value: this.allEntities.filter(user => user.isManager && !user.isAdmin).length,
color: 'MANAGER',
label: 'RED_MANAGER'
},
{
value: this.allEntities.filter(user => this.userService.isManager(user) && this.userService.isAdmin(user)).length,
value: this.allEntities.filter(user => user.isManager && user.isAdmin).length,
color: 'MANAGER_ADMIN',
label: 'MANAGER_ADMIN'
},
{
value: this.allEntities.filter(user => this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)).length,
value: this.allEntities.filter(user => user.isUserAdmin && !user.isAdmin).length,
color: 'USER_ADMIN',
label: 'RED_USER_ADMIN'
},
{
value: this.allEntities.filter(user => this.userService.isAdmin(user) && !this.userService.isManager(user)).length,
value: this.allEntities.filter(user => user.isAdmin && !user.isManager).length,
color: 'ADMIN',
label: 'RED_ADMIN'
}

View File

@ -16,7 +16,7 @@ export class RedRoleGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return new Observable(obs => {
if (!this._userService.user.hasAnyREDRoles) {
if (!this._userService.currentUser.hasAnyREDRoles) {
this._router.navigate(['/auth-error']);
this._loadingService.stop();
obs.next(false);
@ -26,8 +26,8 @@ export class RedRoleGuard implements CanActivate {
// we have at least 1 RED Role -> if it's not user he must be admin
if (
this._userService.user.isUserAdmin &&
!this._userService.user.isAdmin &&
this._userService.currentUser.isUserAdmin &&
!this._userService.currentUser.isAdmin &&
!(state.url.startsWith('/main/admin/users') || state.url.startsWith('/main/my-profile'))
) {
this._router.navigate(['/main/admin/users']);
@ -36,7 +36,7 @@ export class RedRoleGuard implements CanActivate {
return;
}
if (!this._userService.isUser() && state.url.startsWith('/main/dossiers')) {
if (!this._userService.currentUser.isUser && state.url.startsWith('/main/dossiers')) {
this._router.navigate(['/main/admin']);
obs.next(false);
obs.complete();

View File

@ -5,6 +5,7 @@ import { PermissionsService } from '@services/permissions.service';
import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { AnnotationActionsService } from '../../services/annotation-actions.service';
import { WebViewerInstance } from '@pdftron/webviewer';
import { UserService } from '@services/user.service';
export const AnnotationButtonTypes = {
dark: 'dark',
@ -28,9 +29,10 @@ export class AnnotationActionsComponent implements OnInit {
annotationPermissions: AnnotationPermissions;
constructor(
public appStateService: AppStateService,
public annotationActionsService: AnnotationActionsService,
private _permissionsService: PermissionsService
readonly appStateService: AppStateService,
readonly annotationActionsService: AnnotationActionsService,
private readonly _permissionsService: PermissionsService,
private readonly _userService: UserService
) {}
private _annotations: AnnotationWrapper[];
@ -89,7 +91,7 @@ export class AnnotationActionsComponent implements OnInit {
private _setPermissions() {
this.annotationPermissions = AnnotationPermissions.forUser(
this._permissionsService.isApprover(),
this._permissionsService.currentUser,
this._userService.currentUser,
this.annotations
);
}

View File

@ -8,10 +8,9 @@ import { from, Observable } from 'rxjs';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { LoadingService } from '@services/loading.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { CircleButtonTypes, EntitiesService } from '@iqser/common-ui';
import { TranslateService } from '@ngx-translate/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { CircleButtonTypes } from '@iqser/common-ui';
@Component({
selector: 'redaction-dossier-overview-bulk-actions',
@ -33,7 +32,7 @@ export class DossierOverviewBulkActionsComponent {
private readonly _fileActionService: FileActionService,
private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService,
private readonly _screenStateService: ScreenStateService<FileStatusWrapper>
private readonly _entitiesService: EntitiesService<FileStatusWrapper>
) {}
get dossier() {
@ -41,7 +40,7 @@ export class DossierOverviewBulkActionsComponent {
}
get selectedFiles(): FileStatusWrapper[] {
return this._screenStateService.selectedEntities;
return this._entitiesService.selected;
}
get areAllFilesSelected() {
@ -146,7 +145,7 @@ export class DossierOverviewBulkActionsComponent {
.toPromise();
await this._appStateService.reloadActiveDossierFiles();
this.reload.emit();
this._screenStateService.setSelectedEntities([]);
this._entitiesService.setSelected([]);
this._loadingService.stop();
}
);

View File

@ -39,7 +39,7 @@ export class CommentsComponent {
this.annotation.comments.push({
text: value,
id: commentResponse.commentId,
user: this._userService.userId
user: this._userService.currentUser.id
});
});
this.commentForm.reset();

View File

@ -15,11 +15,11 @@
<div class="all-caps-label" translate="dossier-details.owner"></div>
<div class="mt-12 d-flex">
<ng-container *ngIf="!editingOwner; else editOwner">
<redaction-initials-avatar [userId]="owner?.userId" [withName]="true" color="gray" size="large"></redaction-initials-avatar>
<redaction-initials-avatar [userId]="owner?.id" [withName]="true" color="gray" size="large"></redaction-initials-avatar>
<iqser-circle-button
(action)="editingOwner = true"
*ngIf="permissionsService.isManager()"
*ngIf="currentUser.isManager"
[tooltip]="'dossier-details.edit-owner' | translate"
class="ml-14"
icon="red:edit"

View File

@ -2,11 +2,9 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro
import { AppStateService } from '@state/app-state.service';
import { groupBy } from '@utils/functions';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { PermissionsService } from '@services/permissions.service';
import { TranslateChartService } from '@services/translate-chart.service';
import { StatusSorter } from '@utils/sorters/status-sorter';
import { UserService } from '@services/user.service';
import { User } from '@redaction/red-ui-http';
import { UserService, UserWrapper } from '@services/user.service';
import { Toaster } from '@services/toaster.service';
import { FilterService } from '@iqser/common-ui';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
@ -20,7 +18,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
})
export class DossierDetailsComponent implements OnInit {
documentsChartData: DoughnutChartConfig[] = [];
owner: User;
owner: UserWrapper;
editingOwner = false;
@Input() dossierAttributes: DossierAttributeWithValue[];
@Output() openAssignDossierMembersDialog = new EventEmitter();
@ -30,11 +28,11 @@ export class DossierDetailsComponent implements OnInit {
expandTooltip = _('dossier-details.expand');
readonly needsWorkFilters$ = this.filterService.getFilterModels$('needsWorkFilters');
readonly currentUser = this._userService.currentUser;
constructor(
readonly appStateService: AppStateService,
readonly translateChartService: TranslateChartService,
readonly permissionsService: PermissionsService,
readonly filterService: FilterService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _userService: UserService,
@ -81,13 +79,13 @@ export class DossierDetailsComponent implements OnInit {
this._changeDetectorRef.detectChanges();
}
async assignOwner(user: User | string) {
async assignOwner(user: UserWrapper | string) {
this.owner = typeof user === 'string' ? this._userService.getRedUserById(user) : user;
const dw = Object.assign({}, this.appStateService.activeDossier);
dw.dossier.ownerId = this.owner.userId;
dw.dossier.ownerId = this.owner.id;
await this.appStateService.createOrUpdateDossier(dw.dossier);
const ownerName = this._userService.getNameForId(this.owner.userId);
const ownerName = this._userService.getNameForId(this.owner.id);
const dossierName = this.appStateService.activeDossier.name;
const msg = 'Successfully assigned ' + ownerName + ' to dossier: ' + dossierName;
this._toaster.info(msg);

View File

@ -1,8 +1,8 @@
<redaction-status-bar [config]="getDossierStatusConfig(dossier)"></redaction-status-bar>
<div [class.active]="actionMenuOpen" class="action-buttons">
<div class="action-buttons">
<iqser-circle-button
(action)="openEditDossierDialog($event, dossier)"
*ngIf="permissionsService.isManager()"
*ngIf="currentUser.isManager"
[tooltip]="'dossier-listing.edit.action' | translate"
icon="red:edit"
[type]="circleButtonTypes.dark"

View File

@ -5,6 +5,7 @@ import { StatusSorter } from '@utils/sorters/status-sorter';
import { AppStateService } from '@state/app-state.service';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { CircleButtonTypes } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-dossier-listing-actions',
@ -13,15 +14,16 @@ import { CircleButtonTypes } from '@iqser/common-ui';
})
export class DossierListingActionsComponent {
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
@Input() dossier: DossierWrapper;
@Output() actionPerformed = new EventEmitter<DossierWrapper | undefined>();
actionMenuOpen = false;
constructor(
readonly permissionsService: PermissionsService,
readonly appStateService: AppStateService,
private readonly _dialogService: DossiersDialogService
private readonly _dialogService: DossiersDialogService,
private readonly _userService: UserService
) {}
openEditDossierDialog($event: MouseEvent, dossierWrapper: DossierWrapper) {

View File

@ -1,4 +1,4 @@
<div *ngIf="screen === 'dossier-overview'" [class.active]="actionMenuOpen" class="action-buttons">
<div *ngIf="screen === 'dossier-overview'" class="action-buttons">
<ng-container *ngTemplateOutlet="actions"></ng-container>
<redaction-status-bar *ngIf="fileStatus?.isWorkable" [config]="statusBarConfig"></redaction-status-bar>
</div>

View File

@ -10,6 +10,7 @@ import { FileManagementControllerService } from '@redaction/red-ui-http';
import { TranslateService } from '@ngx-translate/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { CircleButtonTypes } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-file-actions',
@ -18,12 +19,12 @@ import { CircleButtonTypes } from '@iqser/common-ui';
})
export class FileActionsComponent implements OnInit {
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
@Input() fileStatus: FileStatusWrapper;
@Input() activeDocumentInfo: boolean;
@Input() activeExcludePages: boolean;
@Output() actionPerformed = new EventEmitter<string>();
actionMenuOpen: boolean;
screen: 'file-preview' | 'dossier-overview';
@ -34,7 +35,8 @@ export class FileActionsComponent implements OnInit {
private readonly _fileActionService: FileActionService,
private readonly _loadingService: LoadingService,
private readonly _fileManagementControllerService: FileManagementControllerService,
private readonly _translateService: TranslateService
private readonly _translateService: TranslateService,
private readonly _userService: UserService
) {}
get statusBarConfig() {
@ -50,7 +52,7 @@ export class FileActionsComponent implements OnInit {
}
get toggleTooltip(): string {
if (!this.permissionsService.isManager()) {
if (!this.currentUser.isManager) {
return _('file-preview.toggle-analysis.only-managers');
}

View File

@ -1,7 +1,7 @@
<button (click)="scroll(buttonType.top)" [hidden]="!showScroll(buttonType.top)" class="scroll-button top pointer">
<button (click)="scroll(buttonType.top)" [hidden]="(showScrollUp$ | async) === false" class="scroll-button top pointer">
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
</button>
<button (click)="scroll(buttonType.bottom)" [hidden]="!showScroll(buttonType.bottom)" class="scroll-button bottom pointer">
<button (click)="scroll(buttonType.bottom)" [hidden]="(showScrollDown$ | async) === false" class="scroll-button bottom pointer">
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
</button>

View File

@ -1,5 +1,7 @@
import { Component, HostListener, Input } from '@angular/core';
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { concatMap, delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
const ButtonTypes = {
top: 'top',
@ -13,28 +15,40 @@ type ButtonType = keyof typeof ButtonTypes;
templateUrl: './scroll-button.component.html',
styleUrls: ['./scroll-button.component.scss']
})
export class ScrollButtonComponent {
buttonType = ButtonTypes;
export class ScrollButtonComponent implements OnInit {
readonly buttonType = ButtonTypes;
@Input()
scrollViewport: CdkVirtualScrollViewport;
@Input()
itemSize: number;
showScrollUp$: Observable<boolean>;
showScrollDown$: Observable<boolean>;
ngOnInit() {
const scrollSize = () => this.scrollViewport.getDataLength() * this.itemSize;
const scrollIsNeeded = () => this.scrollViewport.getViewportSize() < scrollSize();
const reachedEnd = (type: ButtonType) => this.scrollViewport.measureScrollOffset(type) === 0;
const showScrollUp = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.top);
const showScrollDown = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.bottom);
const scroll$ = this.scrollViewport.elementScrolled().pipe(
startWith(''),
/** Delay first value so that we can wait for items to be rendered in viewport and get correct values */
concatMap((value, index) => (index === 0 ? of(value).pipe(delay(0)) : of(value)))
);
this.showScrollUp$ = scroll$.pipe(map(showScrollUp), distinctUntilChanged());
this.showScrollDown$ = scroll$.pipe(map(showScrollDown), distinctUntilChanged());
}
scroll(type: ButtonType): void {
const viewportSize = (this.scrollViewport?.getViewportSize() - this.itemSize) * (type === ButtonTypes.top ? -1 : 1);
const scrollOffset = this.scrollViewport?.measureScrollOffset('top');
this.scrollViewport?.scrollToOffset(scrollOffset + viewportSize, 'smooth');
}
showScroll(type: ButtonType): boolean {
const reachedEnd = this.scrollViewport?.measureScrollOffset(type) === 0;
const scrollSize = this.scrollViewport?.getDataLength() * this.itemSize;
const scrollIsNeeded = this.scrollViewport?.getViewportSize() < scrollSize;
return scrollIsNeeded && !reachedEnd;
}
@HostListener('document:keyup', ['$event'])
spaceAndPageDownScroll(event: KeyboardEvent): void {
if (['Space', 'PageDown'].includes(event.code) && (event.target as any).tagName === 'BODY') {

View File

@ -43,15 +43,15 @@ export class TeamMembersManagerComponent implements OnInit {
}
get ownersSelectOptions() {
return this.userService.managerUsers.map(m => m.userId);
return this.userService.managerUsers.map(m => m.id);
}
get membersSelectOptions() {
const searchQuery = this.searchForm.get('query').value;
return this.userService.eligibleUsers
.filter(user => this.userService.getNameForId(user.userId).toLowerCase().includes(searchQuery.toLowerCase()))
.filter(user => this.selectedOwnerId !== user.userId)
.map(user => user.userId);
.filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(searchQuery.toLowerCase()))
.filter(user => this.selectedOwnerId !== user.id)
.map(user => user.id);
}
get valid(): boolean {

View File

@ -16,7 +16,7 @@
</div>
<iqser-circle-button
(action)="openAssignDossierMembersDialog.emit()"
*ngIf="permissionsService.isManager() && canAdd"
*ngIf="currentUser.isManager && canAdd"
[class.large-spacing]="largeSpacing"
[size]="32"
[tooltip]="'dossier-details.assign-members' | translate"

View File

@ -1,6 +1,6 @@
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { CircleButtonTypes } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-team-members',
@ -9,6 +9,7 @@ import { CircleButtonTypes } from '@iqser/common-ui';
})
export class TeamMembersComponent {
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
@Input() memberIds: string[];
@Input() perLine: number;
@ -23,7 +24,7 @@ export class TeamMembersComponent {
expandedTeam = false;
constructor(public permissionsService: PermissionsService) {}
constructor(private readonly _userService: UserService) {}
get maxTeamMembersBeforeExpand(): number {
return this.perLine - (this.canAdd ? 1 : 0);

View File

@ -103,7 +103,7 @@ export class AssignReviewerApproverDialogComponent {
uniqueReviewers.add(file.currentReviewer);
}
}
let singleUser = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.userId;
let singleUser = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.currentUser.id;
singleUser = this.singleUsersSelectOptions.indexOf(singleUser) >= 0 ? singleUser : this.singleUsersSelectOptions[0];

View File

@ -1,9 +1,9 @@
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { AppStateService } from '../../../../../state/app-state.service';
import { DossierWrapper } from '../../../../../state/model/dossier.wrapper';
import { AppStateService } from '@state/app-state.service';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { PermissionsService } from '@services/permissions.service';
import { TeamMembersManagerComponent } from '../../../components/team-members-manager/team-members-manager.component';
import { UserService } from '@services/user.service';
@Component({
selector: 'redaction-edit-dossier-team-members',
@ -11,19 +11,21 @@ import { TeamMembersManagerComponent } from '../../../components/team-members-ma
styleUrls: ['./edit-dossier-team-members.component.scss']
})
export class EditDossierTeamMembersComponent implements EditDossierSectionInterface {
readonly currentUser = this._userService.currentUser;
@Input() dossierWrapper: DossierWrapper;
@Output() updateDossier = new EventEmitter<any>();
@ViewChild(TeamMembersManagerComponent) managerComponent: TeamMembersManagerComponent;
constructor(private readonly _appStateService: AppStateService, private readonly _permissionsService: PermissionsService) {}
constructor(private readonly _appStateService: AppStateService, private readonly _userService: UserService) {}
get changed() {
return this.managerComponent.changed;
}
get disabled() {
return !this._permissionsService.isManager();
return !this.currentUser.isManager;
}
async save() {

View File

@ -2,9 +2,9 @@ import { Component, Inject } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { humanize } from '../../../../utils/functions';
import { PermissionsService } from '../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { humanize } from '@iqser/common-ui';
export interface RemoveAnnotationsDialogInput {
annotationsToRemove: AnnotationWrapper[];

View File

@ -8,20 +8,23 @@
<div class="red-content-inner">
<div class="content-container">
<redaction-table-header [tableColConfigs]="tableColConfigs" [tableHeaderLabel]="tableHeaderLabel"></redaction-table-header>
<redaction-table-header
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
(action)="openAddDossierDialog()"
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dossier-listing.no-data.action' | translate"
[showButton]="permissionsService.isManager()"
[showButton]="currentUser.isManager"
[text]="'dossier-listing.no-data.title' | translate"
icon="red:folder"
></redaction-empty-state>
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-listing.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="let dw of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.pointer]="!!dw"
@ -82,7 +85,7 @@
<div class="right-container" redactionHasScrollbar>
<redaction-dossier-listing-details
*ngIf="(screenStateService.noData$ | async) === false"
*ngIf="(entitiesService.noData$ | async) === false"
[documentsChartData]="documentsChartData"
[dossiersChartData]="dossiersChartData"
></redaction-dossier-listing-details>

View File

@ -16,12 +16,8 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { UserPreferenceService } from '@services/user-preference.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { FilterService, NestedFilter } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { SortingService } from '@services/sorting.service';
import { TableColConfig } from '@shared/components/table-col-name/table-col-name.component';
import { NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { workloadTranslations } from '../../translations/workload-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations';
@ -31,29 +27,34 @@ import {
dossierStatusChecker,
dossierTemplateChecker
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { PermissionsService } from '@services/permissions.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
const isLeavingScreen = event => event instanceof NavigationStart && event.url !== '/main/dossiers';
@Component({
templateUrl: './dossier-listing-screen.component.html',
styleUrls: ['./dossier-listing-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DossierListingScreenComponent
extends BaseListingComponent<DossierWrapper>
implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach
{
readonly itemSize = 95;
buttonConfigs: ButtonConfig[] = [
protected readonly _primaryKey = 'dossierName';
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
readonly buttonConfigs: ButtonConfig[] = [
{
label: _('dossier-listing.add-new'),
action: () => this.openAddDossierDialog(),
hide: !this.permissionsService.isManager(),
hide: !this.currentUser.isManager,
icon: 'red:plus',
type: 'primary'
}
];
tableColConfigs: TableColConfig[] = [
readonly tableColumnConfigs: TableColumnConfig<DossierWrapper>[] = [
{
label: _('dossier-listing.table-col-names.name'),
withSort: true,
@ -73,22 +74,23 @@ export class DossierListingScreenComponent
];
dossiersChartData: DoughnutChartConfig[] = [];
documentsChartData: DoughnutChartConfig[] = [];
protected readonly _primaryKey = 'dossierName';
protected _tableHeaderLabel = _('dossier-listing.table-header.title');
private _lastScrollPosition: number;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ViewChild(CdkVirtualScrollViewport)
private readonly _scrollViewport: CdkVirtualScrollViewport;
constructor(
private readonly _translateChartService: TranslateChartService,
private readonly _userService: UserService,
private readonly _dialogService: DossiersDialogService,
private readonly _translateService: TranslateService,
private readonly _router: Router,
private readonly _appStateService: AppStateService,
private readonly _userPreferenceService: UserPreferenceService,
protected readonly _injector: Injector,
private readonly _userService: UserService,
readonly changeDetectorRef: ChangeDetectorRef,
protected readonly _injector: Injector
readonly permissionsService: PermissionsService,
private readonly _appStateService: AppStateService,
private readonly _translateService: TranslateService,
private readonly _dialogService: DossiersDialogService,
private readonly _translateChartService: TranslateChartService,
private readonly _userPreferenceService: UserPreferenceService
) {
super(_injector);
this._appStateService.reset();
@ -96,15 +98,15 @@ export class DossierListingScreenComponent
}
private get _userId() {
return this._userService.userId;
return this._userService.currentUser.id;
}
private get _activeDossiersCount(): number {
return this.screenStateService.allEntities.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
return this.entitiesService.all.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
}
private get _inactiveDossiersCount(): number {
return this.screenStateService.allEntities.length - this._activeDossiersCount;
return this.entitiesService.all.length - this._activeDossiersCount;
}
ngOnInit(): void {
@ -121,7 +123,7 @@ export class DossierListingScreenComponent
});
this.addSubscription = this._router.events.pipe(filter(isLeavingScreen)).subscribe(() => {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
this._lastScrollPosition = this._scrollViewport.measureScrollOffset('top');
});
}
@ -130,7 +132,7 @@ export class DossierListingScreenComponent
}
ngOnAttach() {
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._appStateService.reset();
this._loadEntitiesFromState();
this.ngOnInit();
@ -179,7 +181,7 @@ export class DossierListingScreenComponent
}
private _loadEntitiesFromState() {
this.screenStateService.setEntities(this._appStateService.allDossiers);
this.entitiesService.setEntities(this._appStateService.allDossiers);
}
private _computeAllFilters() {
@ -188,7 +190,7 @@ export class DossierListingScreenComponent
const allDistinctNeedsWork = new Set<string>();
const allDistinctDossierTemplates = new Set<string>();
this.screenStateService?.allEntities?.forEach(entry => {
this.entitiesService?.all?.forEach(entry => {
// all people
entry.dossier.memberIds.forEach(f => allDistinctPeople.add(f));
// Needs work
@ -255,7 +257,7 @@ export class DossierListingScreenComponent
slug: 'dossierTemplateFilters',
label: this._translateService.instant('filters.dossier-templates'),
icon: 'red:template',
hide: this.filterService.getFilterGroup('dossierTemplateFilters')?.filters?.length <= 1,
hide: this.filterService.getGroup('dossierTemplateFilters')?.filters?.length <= 1,
filters: dossierTemplateFilters,
checker: dossierTemplateChecker
});

View File

@ -5,17 +5,17 @@
[showCloseButton]="true"
>
<redaction-file-download-btn
[disabled]="screenStateService.areSomeEntitiesSelected$ | async"
[disabled]="entitiesService.areSomeSelected$ | async"
[dossier]="activeDossier"
[file]="screenStateService.allEntities$ | async"
[file]="entitiesService.all$ | async"
tooltipPosition="below"
></redaction-file-download-btn>
<iqser-circle-button
(action)="reanalyseDossier()"
*ngIf="permissionsService.displayReanalyseBtn()"
[disabled]="screenStateService.areSomeEntitiesSelected$ | async"
[tooltipClass]="'small ' + ((screenStateService.areSomeEntitiesSelected$ | async) ? '' : 'warn')"
[disabled]="entitiesService.areSomeSelected$ | async"
[tooltipClass]="'small ' + ((entitiesService.areSomeSelected$ | async) ? '' : 'warn')"
[tooltip]="'dossier-overview.new-rule.toast.actions.reanalyse-all' | translate"
icon="red:refresh"
tooltipPosition="below"
@ -39,13 +39,13 @@
<redaction-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[tableColConfigs]="tableColConfigs"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
(action)="fileInput.click()"
*ngIf="screenStateService.noData$ | async"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dossier-overview.no-data.action' | translate"
[text]="'dossier-overview.no-data.title' | translate"
buttonIcon="red:upload"
@ -54,11 +54,11 @@
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-overview.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="let fileStatus of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.disabled]="fileStatus.isExcluded"
[class.last-opened]="isLastOpenedFile(fileStatus)"
[class.last-opened]="isLastOpenedFile(fileStatus.fileId)"
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
[routerLink]="fileLink(fileStatus)"
class="table-item"

View File

@ -9,7 +9,7 @@
padding: 0 24px 0 10px;
}
redaction-table-col-name::ng-deep {
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
}

View File

@ -21,11 +21,8 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import { NestedFilter, FilterService, keyChecker } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { CircleButtonTypes, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { LoadingService } from '@services/loading.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
@ -33,36 +30,35 @@ import { UserPreferenceService } from '@services/user-preference.service';
import { workloadTranslations } from '../../translations/workload-translations';
import { fileStatusTranslations } from '../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { CircleButtonTypes } from '@iqser/common-ui';
import { TableColConfig } from '@shared/components/table-col-name/table-col-name.component';
import { annotationFilterChecker } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { PermissionsService } from '@services/permissions.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
@Component({
templateUrl: './dossier-overview-screen.component.html',
styleUrls: ['./dossier-overview-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class DossierOverviewScreenComponent
extends BaseListingComponent<FileStatusWrapper>
implements OnInit, OnDestroy, OnDetach, OnAttach
{
private readonly _lastOpenedFileKey = 'Dossier-Recent-' + this.activeDossier.dossierId;
readonly itemSize = 80;
protected readonly _primaryKey = 'filename';
readonly circleButtonTypes = CircleButtonTypes;
protected readonly _tableHeaderLabel = _('dossier-overview.table-header.title');
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
private readonly _lastOpenedFileKey = 'Dossier-Recent-' + this.activeDossier.dossierId;
readonly itemSize = 80;
collapsedDetails = false;
readonly actionConfigs: ActionConfig[] = [
{
label: this._translateService.instant('dossier-overview.header-actions.edit'),
action: $event => this.openEditDossierDialog($event),
icon: 'red:edit',
hide: !this.permissionsService.isManager()
hide: !this.currentUser.isManager
}
];
dossierAttributes: DossierAttributeWithValue[] = [];
tableColConfigs: TableColConfig[] = [
readonly tableColumnConfigs: TableColumnConfig<FileStatusWrapper>[] = [
{
label: _('dossier-overview.table-col-names.name'),
withSort: true,
@ -94,29 +90,35 @@ export class DossierOverviewScreenComponent
column: 'statusSort'
}
];
collapsedDetails = false;
dossierAttributes: DossierAttributeWithValue[] = [];
@ViewChild(DossierDetailsComponent, { static: false })
private readonly _dossierDetailsComponent: DossierDetailsComponent;
private _lastScrollPosition: number;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ViewChild('fileInput') private _fileInput: ElementRef;
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
@ViewChild(CdkVirtualScrollViewport)
private readonly _scrollViewport: CdkVirtualScrollViewport;
constructor(
private readonly _userService: UserService,
private readonly _router: Router,
private readonly _toaster: Toaster,
protected readonly _injector: Injector,
private readonly _userService: UserService,
readonly permissionsService: PermissionsService,
private readonly _loadingService: LoadingService,
private readonly _appStateService: AppStateService,
private readonly _appConfigService: AppConfigService,
private readonly _translateService: TranslateService,
private readonly _dialogService: DossiersDialogService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _fileUploadService: FileUploadService,
private readonly _statusOverlayService: StatusOverlayService,
private readonly _router: Router,
private readonly _translateService: TranslateService,
private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _appStateService: AppStateService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _appConfigService: AppConfigService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _loadingService: LoadingService,
private readonly _dossierAttributesService: DossierAttributesService,
protected readonly _injector: Injector
private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _dossierAttributesService: DossierAttributesService
) {
super(_injector);
this._loadEntitiesFromState();
@ -126,19 +128,15 @@ export class DossierOverviewScreenComponent
return this._appStateService.activeDossier;
}
get userId() {
return this._userService.userId;
}
get checkedRequiredFilters() {
return this.filterService.getFilterGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
}
get checkedNotRequiredFilters() {
return this.filterService.getFilterGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
}
isLastOpenedFile({ fileId }: FileStatusWrapper): boolean {
isLastOpenedFile(fileId: string): boolean {
return this._userPreferenceService.getLastOpenedFileId(this._lastOpenedFileKey) === fileId;
}
@ -162,7 +160,7 @@ export class DossierOverviewScreenComponent
.pipe(filter(event => event instanceof NavigationStart))
.subscribe((event: NavigationStart) => {
if (!event.url.endsWith(this._appStateService.activeDossierId)) {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
this._lastScrollPosition = this._scrollViewport.measureScrollOffset('top');
}
});
@ -183,7 +181,7 @@ export class DossierOverviewScreenComponent
await this._appStateService.reloadActiveDossierFiles();
this._loadEntitiesFromState();
await this.ngOnInit();
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._scrollViewport.scrollTo({ top: this._lastScrollPosition });
}
ngOnDetach() {
@ -239,7 +237,7 @@ export class DossierOverviewScreenComponent
}
bulkActionPerformed() {
this.screenStateService.setSelectedEntities([]);
this.entitiesService.setSelected([]);
this.reloadDossiers();
}
@ -275,7 +273,7 @@ export class DossierOverviewScreenComponent
moment(file.lastUpdated).add(this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS), 'hours').isAfter(moment());
private _loadEntitiesFromState() {
if (this.activeDossier) this.screenStateService.setEntities(this.activeDossier.files);
if (this.activeDossier) this.entitiesService.setEntities(this.activeDossier.files);
}
private async _uploadFiles(files: FileUploadModel[]) {
@ -291,7 +289,7 @@ export class DossierOverviewScreenComponent
const allDistinctAddedDates = new Set<string>();
const allDistinctNeedsWork = new Set<string>();
this.screenStateService.allEntities.forEach(file => {
this.entitiesService.all.forEach(file => {
allDistinctPeople.add(file.currentReviewer);
allDistinctFileStatusWrapper.add(file.status);
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'));
@ -369,7 +367,7 @@ export class DossierOverviewScreenComponent
private _createQuickFilters() {
let quickFilters = [];
if (this.screenStateService.allEntities.filter(this.recentlyModifiedChecker).length > 0) {
if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) {
const recentPeriod = this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS);
quickFilters = [
{
@ -388,7 +386,7 @@ export class DossierOverviewScreenComponent
{
key: 'assigned-to-me',
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'),
checker: (file: FileStatusWrapper) => file.currentReviewer === this.userId
checker: (file: FileStatusWrapper) => file.currentReviewer === this.currentUser.id
},
{
key: 'unassigned',
@ -398,7 +396,7 @@ export class DossierOverviewScreenComponent
{
key: 'assigned-to-others',
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'),
checker: (file: FileStatusWrapper) => !!file.currentReviewer && file.currentReviewer !== this.userId
checker: (file: FileStatusWrapper) => !!file.currentReviewer && file.currentReviewer !== this.currentUser.id
}
];
}

View File

@ -17,12 +17,11 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { PermissionsService } from '@services/permissions.service';
import { timer } from 'rxjs';
import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service';
import { UserService, UserWrapper } from '@services/user.service';
import {
FileManagementControllerService,
FileStatus,
StatusControllerService,
User,
UserPreferenceControllerService
} from '@redaction/red-ui-http';
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
@ -34,10 +33,8 @@ import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { LoadingService } from '@services/loading.service';
import { stampPDFPage } from '@utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';
import { AutoUnsubscribeComponent, CircleButtonTypes } from '@iqser/common-ui';
import { AutoUnsubscribeComponent, CircleButtonTypes, NestedFilter, processFilters } from '@iqser/common-ui';
import { fileStatusTranslations } from '../../translations/file-status-translations';
import { NestedFilter } from '@iqser/common-ui';
import { processFilters } from '@iqser/common-ui';
import { handleFilterDelta } from '@shared/components/filters/popup-filter/utils/filter-utils';
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
@ -276,7 +273,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribeComponent impleme
const processStartTime = new Date().getTime();
this.annotationData = this.fileData.getAnnotations(
this.appStateService.dictionaryData[this.appStateService.activeDossier.dossierTemplateId],
this.permissionsService.currentUser,
this.userService.currentUser,
this.viewMode,
this.userPreferenceService.areDevFeaturesEnabled
);
@ -467,8 +464,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribeComponent impleme
});
}
async assignReviewer(user: User | string) {
const reviewerId = typeof user === 'string' ? user : user.userId;
async assignReviewer(user: UserWrapper | string) {
const reviewerId = typeof user === 'string' ? user : user.id;
const reviewerName = this.userService.getNameForId(reviewerId);
const { dossierId, fileId, filename } = this.fileData.fileStatus;

View File

@ -10,7 +10,10 @@
<div class="red-content-inner">
<div class="content-container">
<redaction-table-header [tableColConfigs]="tableColConfigs" [tableHeaderLabel]="tableHeaderLabel"></redaction-table-header>
<redaction-table-header
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
*ngIf="searchResult.length === 0"
@ -18,7 +21,7 @@
[text]="'search-screen.no-data' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="let item of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.pointer]="true"

View File

@ -1,21 +1,16 @@
import { Component, Injector, OnDestroy } from '@angular/core';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { MatchedDocument, SearchControllerService, SearchResult } from '@redaction/red-ui-http';
import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, map, skip, switchMap, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { TableColConfig } from '@shared/components/table-col-name/table-col-name.component';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '@services/sorting.service';
import { keyChecker, TableColumnConfig } from '@iqser/common-ui';
import { AppStateService } from '@state/app-state.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { LoadingService } from '@services/loading.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations';
import { SearchPositions } from '@shared/components/page-header/models/search-positions.type';
import { keyChecker } from '@iqser/common-ui';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { TranslateService } from '@ngx-translate/core';
@ -38,37 +33,29 @@ interface SearchInput {
@Component({
templateUrl: './search-screen.component.html',
styleUrls: ['./search-screen.component.scss'],
providers: [FilterService, SearchService, ScreenStateService, SortingService]
providers: [...DefaultListingServices]
})
export class SearchScreenComponent extends BaseListingComponent<ListItem> implements OnDestroy {
readonly fileStatusTranslations = fileStatusTranslations;
readonly searchPositions = SearchPositions;
readonly itemSize = 85;
protected readonly _primaryKey = 'filename';
readonly tableHeaderLabel = _('search-screen.table-header');
readonly tableColumnConfigs: TableColumnConfig<ListItem>[] = [
{ label: _('search-screen.cols.document') },
{ label: _('search-screen.cols.status') },
{ label: _('search-screen.cols.dossier') },
{ label: _('search-screen.cols.pages') }
];
readonly search$ = new BehaviorSubject<SearchInput>(null);
readonly searchResults$: Observable<ListItem[]> = this.search$.asObservable().pipe(
switchMap(query => this._search(query)),
map(searchResult => this._toMatchedDocuments(searchResult)),
map(docs => this._toListItems(docs)),
tap(result => this.screenStateService.setEntities(result)),
tap(result => this.entitiesService.setEntities(result)),
tap(() => this._loadingService.stop())
);
readonly tableColConfigs: TableColConfig[] = [
{
label: _('search-screen.cols.document')
},
{
label: _('search-screen.cols.status')
},
{
label: _('search-screen.cols.dossier')
},
{
label: _('search-screen.cols.pages')
}
];
protected readonly _primaryKey = 'fileName';
protected readonly _tableHeaderLabel = _('search-screen.table-header');
constructor(
protected readonly _injector: Injector,

View File

@ -9,13 +9,15 @@ import { getFirstRelevantTextPart } from '@utils/functions';
import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { DossiersDialogService } from './dossiers-dialog.service';
import { BASE_HREF } from '../../../tokens';
import { UserService } from '@services/user.service';
@Injectable()
export class AnnotationActionsService {
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
private readonly _permissionsService: PermissionsService,
private readonly _ngZone: NgZone,
private readonly _userService: UserService,
private readonly _permissionsService: PermissionsService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _translateService: TranslateService,
private readonly _dialogService: DossiersDialogService
@ -89,7 +91,7 @@ export class AnnotationActionsService {
annotations.forEach(annotation => {
const permissions = AnnotationPermissions.forUser(
this._permissionsService.isApprover(),
this._permissionsService.currentUser,
this._userService.currentUser,
annotation
);
const value = permissions.canMarkTextOnlyAsFalsePositive ? annotation.value : this._getFalsePositiveText(annotation);
@ -132,9 +134,9 @@ export class AnnotationActionsService {
): Record<string, unknown>[] {
const availableActions = [];
const annotationPermissions = annotations.map(a => ({
annotation: a,
permissions: AnnotationPermissions.forUser(this._permissionsService.isApprover(), this._permissionsService.currentUser, a)
const annotationPermissions = annotations.map(annotation => ({
annotation,
permissions: AnnotationPermissions.forUser(this._permissionsService.isApprover(), this._userService.currentUser, annotation)
}));
const canRecategorizeImage = annotations.length === 1 && annotationPermissions[0].permissions.canRecategorizeImage;

View File

@ -154,7 +154,7 @@ export class FileActionService {
.setFileReviewerForList(
fileStatus.map(f => f.fileId),
this._appStateService.activeDossierId,
this._userService.userId
this._userService.currentUser.id
)
.toPromise();
await this._appStateService.reloadActiveDossierFiles();

View File

@ -70,8 +70,6 @@ export class IconsModule {
'report',
'search',
'secret',
'sort-asc',
'sort-desc',
'status',
'status-collapse',
'status-expand',

View File

@ -1,35 +1,36 @@
import { Component, Injector, OnDestroy, ViewChild } from '@angular/core';
import { SortingOrders, SortingService } from '@services/sorting.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { SearchService } from '../services/search.service';
import { ScreenStateService } from '../services/screen-state.service';
import { Component, Injector, OnDestroy } from '@angular/core';
import {
AutoUnsubscribeComponent,
EntitiesService,
FilterService,
KeysOf,
SearchService,
SortingOrders,
SortingService,
TableColumnConfig
} from '@iqser/common-ui';
import { combineLatest, Observable } from 'rxjs';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { PermissionsService } from '@services/permissions.service';
import { FilterService } from '@iqser/common-ui';
export const DefaultListingServices = [FilterService, SearchService, EntitiesService, SortingService] as const;
@Component({ template: '' })
export abstract class BaseListingComponent<T> extends AutoUnsubscribeComponent implements OnDestroy {
@ViewChild(CdkVirtualScrollViewport)
readonly scrollViewport: CdkVirtualScrollViewport;
readonly permissionsService = this._injector.get(PermissionsService);
export abstract class BaseListingComponent<T extends object> extends AutoUnsubscribeComponent implements OnDestroy {
readonly filterService = this._injector.get(FilterService);
readonly sortingService = this._injector.get(SortingService);
readonly searchService = this._injector.get(SearchService);
readonly screenStateService = this._injector.get<ScreenStateService<T>>(ScreenStateService);
readonly searchService = this._injector.get<SearchService<T>>(SearchService);
readonly sortingService = this._injector.get<SortingService<T>>(SortingService);
readonly entitiesService = this._injector.get<EntitiesService<T>>(EntitiesService);
readonly sortedDisplayedEntities$ = this._sortedDisplayedEntities$;
readonly noMatch$ = this._noMatch$;
readonly sortedDisplayedEntities$ = this._sortedDisplayedEntities$;
protected readonly _tableHeaderLabel: string;
readonly tableColumnConfigs: TableColumnConfig<T>[];
/**
* Key used in the *trackBy* function with **ngFor* or **cdkVirtualFor*
* and in the default sorting and as the search field
* @protected
*/
protected abstract _primaryKey: string;
protected readonly _primaryKey: KeysOf<T>;
protected constructor(protected readonly _injector: Injector) {
super();
@ -37,26 +38,18 @@ export abstract class BaseListingComponent<T> extends AutoUnsubscribeComponent i
setTimeout(() => this.setInitialConfig());
}
get tableHeaderLabel(): string {
if (!this._tableHeaderLabel) {
throw new Error('Not implemented');
}
return this._tableHeaderLabel;
}
get allEntities(): T[] {
return this.screenStateService.allEntities;
return this.entitiesService.all;
}
private get _sortedDisplayedEntities$(): Observable<T[]> {
return this.sortingService.sortingOption$.pipe(
switchMap(() => this.screenStateService.displayedEntities$.pipe(map(entities => this.sortingService.defaultSort(entities))))
);
const sort = entities => this.sortingService.defaultSort(entities);
const sortedEntities = () => this.entitiesService.displayed$.pipe(map(sort));
return this.sortingService.sortingOption$.pipe(switchMap(sortedEntities));
}
private get _noMatch$(): Observable<boolean> {
return combineLatest([this.screenStateService.allEntitiesLength$, this.screenStateService.displayedLength$]).pipe(
return combineLatest([this.entitiesService.allLength$, this.entitiesService.displayedLength$]).pipe(
map(([hasEntities, hasDisplayedEntities]) => hasEntities && !hasDisplayedEntities),
distinctUntilChanged()
);
@ -70,28 +63,24 @@ export abstract class BaseListingComponent<T> extends AutoUnsubscribeComponent i
this.searchService.setSearchKey(this._primaryKey);
}
ngOnDestroy(): void {
super.ngOnDestroy();
}
canBulkDelete$(hasPermission = true): Observable<boolean> {
return this.screenStateService.areSomeEntitiesSelected$.pipe(
return this.entitiesService.areSomeSelected$.pipe(
map(areSomeEntitiesSelected => areSomeEntitiesSelected && hasPermission),
distinctUntilChanged()
);
}
toggleSelectAll() {
return this.screenStateService.selectEntities();
return this.entitiesService.selectAll();
}
toggleEntitySelected(event: MouseEvent, entity: T) {
event.stopPropagation();
return this.screenStateService.selectEntities([entity]);
return this.entitiesService.select(entity);
}
isSelected(entity: T): boolean {
return this.screenStateService.isSelected(entity);
return this.entitiesService.isSelected(entity);
}
trackByPrimaryKey(index: number, item: T) {

View File

@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { User } from '@redaction/red-ui-http';
import { UserService } from '@services/user.service';
import { UserService, UserWrapper } from '@services/user.service';
@Component({
selector: 'redaction-assign-user-dropdown',
@ -9,25 +8,25 @@ import { UserService } from '@services/user.service';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AssignUserDropdownComponent {
oldUser: User | string;
@Input() options: (User | string)[];
@Output() save = new EventEmitter<User | string>();
oldUser: UserWrapper | string;
@Input() options: (UserWrapper | string)[];
@Output() save = new EventEmitter<UserWrapper | string>();
@Output() cancel = new EventEmitter<never>();
private _currentUser: User | string;
private _currentUser: UserWrapper | string;
constructor(private readonly _userService: UserService) {}
get value(): User | string {
get value(): UserWrapper | string {
return this._currentUser;
}
@Input()
set value(value: User | string) {
set value(value: UserWrapper | string) {
if (this.oldUser === undefined) this.oldUser = value;
this._currentUser = value;
}
getContext(user: User | string) {
return { userId: typeof user === 'string' ? user : user?.userId };
getContext(user: UserWrapper | string) {
return { userId: typeof user === 'string' ? user : user?.id };
}
}

View File

@ -1,5 +1,5 @@
<button [class.overlay]="showDot" mat-button>
<redaction-initials-avatar [userId]="user.userId" [withName]="true" size="small"></redaction-initials-avatar>
<redaction-initials-avatar [userId]="userId" [withName]="true" size="small"></redaction-initials-avatar>
<mat-icon svgIcon="iqser:arrow-down"></mat-icon>
</button>
<div *ngIf="showDot" class="dot"></div>

View File

@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { UserWrapper } from '@services/user.service';
@Component({
selector: 'redaction-user-button',
@ -8,6 +7,6 @@ import { UserWrapper } from '@services/user.service';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserButtonComponent {
@Input() user: UserWrapper;
@Input() userId: string;
@Input() showDot = false;
}

View File

@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy } from '@angular/core';
import { UserService } from '@services/user.service';
import { User } from '@redaction/red-ui-http';
import { UserService, UserWrapper } from '@services/user.service';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
@Component({
selector: 'redaction-initials-avatar',
@ -10,7 +9,7 @@ import { Subscription } from 'rxjs';
styleUrls: ['./initials-avatar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InitialsAvatarComponent implements OnChanges, OnDestroy {
export class InitialsAvatarComponent extends AutoUnsubscribeComponent implements OnChanges, OnDestroy {
@Input() userId: string;
@Input() color = 'lightgray';
@Input() size: 'small' | 'large' = 'small';
@ -21,26 +20,25 @@ export class InitialsAvatarComponent implements OnChanges, OnDestroy {
displayName: string;
initials: string;
colorClass: string;
user: User;
private _subscription: Subscription;
user: UserWrapper;
constructor(
private readonly _userService: UserService,
private readonly _translateService: TranslateService,
private readonly _changeDetectorRef: ChangeDetectorRef
) {
this._subscription = _userService.usersReloaded$.subscribe(() => {
super();
this.addSubscription = _userService.usersReloaded$.subscribe(() => {
this.detectChanges();
});
}
get hasBorder(): boolean {
return !!this.user && !this._isCurrentUser && this._userService.isManager(this.user);
return !!this.user && !this._isCurrentUser && this.user.isManager;
}
get disabled(): boolean {
return !this._userService.isActive(this.user);
return !this.user?.isActive;
}
private get _colorClass() {
@ -52,11 +50,7 @@ export class InitialsAvatarComponent implements OnChanges, OnDestroy {
}
private get _isCurrentUser(): boolean {
return this._userService.userId === this.user?.userId;
}
ngOnDestroy() {
this._subscription?.unsubscribe();
return this._userService.currentUser.id === this.user?.id;
}
ngOnChanges(): void {

View File

@ -1,8 +1,7 @@
import { Component, Input, Optional } from '@angular/core';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { FilterService, SearchService } from '@iqser/common-ui';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { combineLatest, Observable, of } from 'rxjs';
import { SearchPosition, SearchPositions } from '@shared/components/page-header/models/search-positions.type';
@ -12,7 +11,7 @@ import { SearchPosition, SearchPositions } from '@shared/components/page-header/
templateUrl: './page-header.component.html',
styleUrls: ['./page-header.component.scss']
})
export class PageHeaderComponent {
export class PageHeaderComponent<T extends object> {
readonly searchPositions = SearchPositions;
@Input() pageLabel: string;
@ -26,7 +25,7 @@ export class PageHeaderComponent {
readonly filters$ = this.filterService?.filterGroups$.pipe(map(all => all.filter(f => f.icon)));
readonly showResetFilters$ = this._showResetFilters$;
constructor(@Optional() readonly filterService: FilterService, @Optional() readonly searchService: SearchService) {}
constructor(@Optional() readonly filterService: FilterService, @Optional() readonly searchService: SearchService<T>) {}
get _showResetFilters$(): Observable<boolean> {
if (!this.filterService) return of(false);

View File

@ -1,12 +0,0 @@
<div (click)="withSort && sortingService?.toggleSort(column)" [class.pointer]="withSort" [ngClass]="class">
<mat-icon *ngIf="!!leftIcon" [svgIcon]="leftIcon"></mat-icon>
<span class="all-caps-label">{{ label }}</span>
<mat-icon *ngIf="!!rightIcon" [matTooltip]="rightIconTooltip" [svgIcon]="rightIcon" matTooltipPosition="above"></mat-icon>
<ng-container *ngIf="sortingService?.sortingOption$ | async as sortingOption">
<div *ngIf="withSort" [class.force-display]="sortingOption.column === column" class="sort-arrows-container">
<mat-icon *ngIf="sortingOption.order === 'asc'" svgIcon="red:sort-asc"></mat-icon>
<mat-icon *ngIf="sortingOption.order === 'desc'" svgIcon="red:sort-desc"></mat-icon>
</div>
</ng-container>
</div>

View File

@ -1,51 +0,0 @@
@import '../../../../../assets/styles/variables';
:host {
display: flex;
height: 30px;
flex: 1;
> div {
align-items: center;
display: flex;
width: 100%;
line-height: 11px;
padding: 0 24px;
> mat-icon {
width: 10px;
height: 10px;
margin-right: 6px;
opacity: 0.7;
&:not(:first-child) {
margin-left: 6px;
}
}
}
.flex-end {
min-width: 58px;
}
.sort-arrows-container {
display: none;
color: $primary;
margin-left: 8px;
mat-icon {
height: 14px;
width: 6px;
}
}
&:hover {
.sort-arrows-container {
display: initial;
}
}
.sort-arrows-container.force-display {
display: initial;
}
}

View File

@ -1,29 +0,0 @@
import { Component, Input, Optional } from '@angular/core';
import { SortingService } from '@services/sorting.service';
export interface TableColConfig {
readonly column?: string;
readonly label: string;
readonly withSort?: boolean;
readonly class?: string;
readonly leftIcon?: string;
readonly rightIcon?: string;
readonly rightIconTooltip?: string;
}
@Component({
selector: 'redaction-table-col-name',
templateUrl: './table-col-name.component.html',
styleUrls: ['./table-col-name.component.scss']
})
export class TableColNameComponent {
@Input() column: string;
@Input() label: string;
@Input() withSort = false;
@Input() class: string;
@Input() leftIcon: string;
@Input() rightIcon: string;
@Input() rightIconTooltip: string;
constructor(@Optional() readonly sortingService: SortingService) {}
}

View File

@ -1,13 +1,13 @@
<div [class.selection-enabled]="selectionEnabled" class="header-item">
<iqser-round-checkbox
(click)="screenStateService.selectEntities()"
(click)="entitiesService.selectAll()"
*ngIf="selectionEnabled"
[active]="screenStateService.areAllEntitiesSelected$ | async"
[indeterminate]="screenStateService.notAllEntitiesSelected$ | async"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ tableHeaderLabel | translate: { length: (screenStateService.displayedLength$ | async) } }}
{{ tableHeaderLabel | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<ng-container [ngTemplateOutlet]="bulkActions"></ng-container>
@ -18,8 +18,8 @@
<div [class.selection-enabled]="selectionEnabled" class="table-header" redactionSyncWidth="table-item">
<div *ngIf="selectionEnabled" class="select-oval-placeholder"></div>
<redaction-table-col-name
*ngFor="let config of tableColConfigs"
<iqser-table-column-name
*ngFor="let config of tableColumnConfigs"
[class]="config.class"
[column]="config.column"
[label]="config.label | translate"
@ -27,7 +27,7 @@
[rightIconTooltip]="config.rightIconTooltip"
[rightIcon]="config.rightIcon"
[withSort]="config.withSort"
></redaction-table-col-name>
></iqser-table-column-name>
<div *ngIf="hasEmptyColumn"></div>

View File

@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, Component, Input, TemplateRef } from '@angular/core';
import { TableColConfig } from '@shared/components/table-col-name/table-col-name.component';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { EntitiesService, Required, TableColumnConfig } from '@iqser/common-ui';
@Component({
selector: 'redaction-table-header',
@ -8,12 +7,12 @@ import { ScreenStateService } from '@shared/services/screen-state.service';
styleUrls: ['./table-header.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TableHeaderComponent<T> {
@Input() tableHeaderLabel: string;
@Input() tableColConfigs: TableColConfig[];
export class TableHeaderComponent<T extends object> {
@Input() @Required() tableHeaderLabel: string;
@Input() @Required() tableColumnConfigs: TableColumnConfig<T>[];
@Input() hasEmptyColumn = false;
@Input() selectionEnabled = false;
@Input() bulkActions: TemplateRef<any>;
constructor(readonly screenStateService: ScreenStateService<T>) {}
constructor(readonly entitiesService: EntitiesService<T>) {}
}

View File

@ -1,11 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
import { humanize } from '@utils/functions';
@Pipe({
name: 'humanize'
})
export class HumanizePipe implements PipeTransform {
transform(item: string, lowercase: boolean = false): any {
return humanize(item, lowercase);
}
}

View File

@ -1,11 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
import { SortingService } from '@services/sorting.service';
@Pipe({ name: 'sortBy' })
export class SortByPipe implements PipeTransform {
constructor(private readonly _sortingService: SortingService) {}
transform<T>(value: T[], order = '', column: string = ''): T[] {
return this._sortingService.sort(value, order, column);
}
}

View File

@ -1,135 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable, pipe } from 'rxjs';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { FilterService } from '@iqser/common-ui';
import { SearchService } from '@shared/services/search.service';
import { getFilteredEntities } from '@iqser/common-ui';
const toLengthValue = (entities: unknown[]) => entities?.length ?? 0;
const getLength = pipe(map(toLengthValue), distinctUntilChanged());
@Injectable()
export class ScreenStateService<T> {
private readonly _allEntities$ = new BehaviorSubject<T[]>([]);
readonly allEntities$ = this._allEntities$.asObservable();
readonly allEntitiesLength$ = this._allEntities$.pipe(getLength);
private readonly _displayedEntities$ = new BehaviorSubject<T[]>([]);
readonly displayedEntities$ = this._getDisplayedEntities$;
readonly displayedLength$ = this._displayedEntities$.pipe(getLength);
private readonly _selectedEntities$ = new BehaviorSubject<T[]>([]);
readonly selectedEntities$ = this._selectedEntities$.asObservable();
readonly selectedLength$ = this._selectedEntities$.pipe(getLength);
readonly noData$ = this._noData$;
readonly areAllEntitiesSelected$ = this._areAllEntitiesSelected$;
readonly areSomeEntitiesSelected$ = this._areSomeEntitiesSelected$;
readonly notAllEntitiesSelected$ = this._notAllEntitiesSelected$;
constructor(private readonly _filterService: FilterService, private readonly _searchService: SearchService) {
// setInterval(() => {
// console.log('All entities subs: ', this._allEntities$.observers);
// console.log('Displayed entities subs: ', this._displayedEntities$.observers);
// console.log('Selected entities subs: ', this._selectedEntities$.observers);
// }, 10000);
}
get allEntities(): T[] {
return Object.values(this._allEntities$.getValue());
}
get selectedEntities(): T[] {
return Object.values(this._selectedEntities$.getValue());
}
get displayedEntities(): T[] {
return Object.values(this._displayedEntities$.getValue());
}
get _getDisplayedEntities$(): Observable<T[]> {
const filterGroups$ = this._filterService.filterGroups$;
const searchValue$ = this._searchService.valueChanges$;
return combineLatest([this.allEntities$, filterGroups$, searchValue$]).pipe(
map(([entities, filterGroups]) => getFilteredEntities(entities, filterGroups)),
map(entities => this._searchService.searchIn(entities)),
tap(entities => this._displayedEntities$.next(entities))
);
}
private get _areAllEntitiesSelected$(): Observable<boolean> {
return combineLatest([this.displayedLength$, this.selectedLength$]).pipe(
map(([displayedLength, selectedLength]) => displayedLength && displayedLength === selectedLength),
distinctUntilChanged()
);
}
private get _areSomeEntitiesSelected$(): Observable<boolean> {
return this.selectedLength$.pipe(
map(value => !!value),
distinctUntilChanged()
);
}
private get _notAllEntitiesSelected$(): Observable<boolean> {
return combineLatest([this.areAllEntitiesSelected$, this.areSomeEntitiesSelected$]).pipe(
map(([allEntitiesAreSelected, someEntitiesAreSelected]) => !allEntitiesAreSelected && someEntitiesAreSelected),
distinctUntilChanged()
);
}
private get _noData$(): Observable<boolean> {
return this.allEntitiesLength$.pipe(
map(length => length === 0),
distinctUntilChanged()
);
}
private get _allEntitiesSelected() {
return this.displayedEntities.length !== 0 && this.displayedEntities.length === this.selectedEntities.length;
}
setEntities(newEntities: T[]): void {
this._allEntities$.next(newEntities);
}
setSelectedEntities(newEntities: T[]): void {
this._selectedEntities$.next(newEntities);
}
isSelected(entity: T): boolean {
return this.selectedEntities.indexOf(entity) !== -1;
}
selectEntities(entities?: T[]): void {
if (entities?.length > 0) {
return entities.forEach(entity => this._selectOne(entity));
}
return this._selectAll();
}
updateSelection(): void {
const items = this.displayedEntities.filter(item => this.selectedEntities.includes(item));
this.setSelectedEntities(items);
}
logCurrentState(): void {
console.log('Entities', this.allEntities);
console.log('Displayed', this.displayedEntities);
console.log('Selected', this.selectedEntities);
}
private _selectOne(entity: T): void {
const currentEntityIdx = this.selectedEntities.indexOf(entity);
if (currentEntityIdx === -1) {
return this.setSelectedEntities([...this.selectedEntities, entity]);
}
this.setSelectedEntities(this.selectedEntities.filter((el, idx) => idx !== currentEntityIdx));
}
private _selectAll(): void {
if (this._allEntitiesSelected) return this.setSelectedEntities([]);
this.setSelectedEntities(this.displayedEntities);
}
}

View File

@ -1,41 +0,0 @@
import { Injectable } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { startWith } from 'rxjs/operators';
@Injectable()
export class SearchService {
readonly searchForm = this._formBuilder.group({
query: ['']
});
readonly valueChanges$ = this.searchForm.get('query').valueChanges.pipe(startWith(''));
private _searchKey: string;
constructor(private readonly _formBuilder: FormBuilder) {}
get searchValue(): string {
return this.searchForm.get('query').value;
}
set searchValue(value: string) {
this.searchForm.patchValue({ query: value });
}
searchIn<T>(entities: T[]) {
if (!this._searchKey) return entities;
const searchValue = this.searchValue.toLowerCase();
return entities.filter(entity => this._searchField(entity).includes(searchValue));
}
setSearchKey(value: string): void {
this._searchKey = value;
}
reset(): void {
this.searchForm.reset({ query: '' }, { emitEvent: true });
}
private _searchField<T>(entity: T): string {
return entity[this._searchKey].toString().toLowerCase();
}
}

View File

@ -4,7 +4,6 @@ import { FullPageLoadingIndicatorComponent } from './components/full-page-loadin
import { TranslateModule } from '@ngx-translate/core';
import { InitialsAvatarComponent } from './components/initials-avatar/initials-avatar.component';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { TableColNameComponent } from './components/table-col-name/table-col-name.component';
import { PaginationComponent } from './components/pagination/pagination.component';
import { FileDownloadBtnComponent } from './components/buttons/file-download-btn/file-download-btn.component';
import { UserButtonComponent } from './components/buttons/user-button/user-button.component';
@ -14,14 +13,13 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
import { StatusBarComponent } from './components/status-bar/status-bar.component';
import { HumanizePipe } from './pipes/humanize.pipe';
import { SyncWidthDirective } from './directives/sync-width.directive';
import { HasScrollbarDirective } from './directives/has-scrollbar.directive';
import { DictionaryAnnotationIconComponent } from './components/dictionary-annotation-icon/dictionary-annotation-icon.component';
import { HiddenActionComponent } from './components/hidden-action/hidden-action.component';
import { ConfirmationDialogComponent } from './dialogs/confirmation-dialog/confirmation-dialog.component';
import { EmptyStateComponent } from './components/empty-state/empty-state.component';
import { SortByPipe } from './pipes/sort-by.pipe';
import { CommonUiModule } from '@iqser/common-ui';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { SelectComponent } from './components/select/select.component';
@ -36,14 +34,13 @@ import { InputWithActionComponent } from '@shared/components/input-with-action/i
import { PageHeaderComponent } from './components/page-header/page-header.component';
import { DatePipe } from '@shared/pipes/date.pipe';
import { TableHeaderComponent } from './components/table-header/table-header.component';
import { CommonUiModule } from '@iqser/common-ui';
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
const components = [
FullPageLoadingIndicatorComponent,
InitialsAvatarComponent,
TableColNameComponent,
TableHeaderComponent,
PaginationComponent,
InputWithActionComponent,
AnnotationIconComponent,
@ -54,7 +51,6 @@ const components = [
PopupFilterComponent,
ConfirmationDialogComponent,
EmptyStateComponent,
SortByPipe,
SelectComponent,
SideNavComponent,
DictionaryManagerComponent,
@ -65,7 +61,7 @@ const components = [
...buttons
];
const utils = [HumanizePipe, DatePipe, SyncWidthDirective, HasScrollbarDirective, NavigateLastDossiersScreenDirective];
const utils = [DatePipe, SyncWidthDirective, HasScrollbarDirective, NavigateLastDossiersScreenDirective];
const modules = [MatConfigModule, TranslateModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule, CommonUiModule];

View File

@ -8,8 +8,8 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { mergeMap, tap } from 'rxjs/operators';
import { DownloadStatusWrapper } from '../model/download-status.wrapper';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { KeycloakService } from 'keycloak-angular';
import { UserService } from '@services/user.service';
@Injectable()
export class FileDownloadService {
@ -18,17 +18,17 @@ export class FileDownloadService {
hasPendingDownloads;
constructor(
private readonly _userService: UserService,
private readonly _applicationRef: ApplicationRef,
private readonly _keycloakService: KeycloakService,
private readonly _appStateService: AppStateService,
private readonly _permissionsService: PermissionsService,
private readonly _downloadControllerService: DownloadControllerService,
private readonly _translateService: TranslateService,
private readonly _appConfigService: AppConfigService,
private readonly _keycloakService: KeycloakService,
private readonly _downloadControllerService: DownloadControllerService,
private readonly _fileManagementControllerService: FileManagementControllerService
) {
interval(5000).subscribe(() => {
if (_permissionsService.isUser()) {
if (_userService.currentUser.isUser) {
this.getDownloadStatus().subscribe(() => {});
}
});

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { UserService, UserWrapper } from './user.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { User, Comment } from '@redaction/red-ui-http';
import { Comment } from '@redaction/red-ui-http';
import { DossierWrapper } from '@state/model/dossier.wrapper';
@Injectable({
@ -11,10 +11,6 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
export class PermissionsService {
constructor(private readonly _appStateService: AppStateService, private readonly _userService: UserService) {}
get currentUser(): UserWrapper {
return this._userService.user;
}
private get _activeFile(): FileStatusWrapper | undefined {
return this._appStateService.activeFile;
}
@ -23,10 +19,6 @@ export class PermissionsService {
return this._appStateService.activeDossier;
}
isManager(user?: User) {
return this._userService.isManager(user);
}
isReviewerOrApprover(fileStatus?: FileStatusWrapper): boolean {
return this.isFileReviewer(fileStatus) || this.isApprover();
}
@ -44,7 +36,7 @@ export class PermissionsService {
}
canToggleAnalysis(fileStatus: FileStatusWrapper): boolean {
return this.isManager() && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(fileStatus.status);
return this._userService.currentUser.isManager && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(fileStatus.status);
}
canReanalyseFile(fileStatus = this._activeFile): boolean {
@ -55,7 +47,7 @@ export class PermissionsService {
}
isFileReviewer(fileStatus = this._activeFile): boolean {
return fileStatus.currentReviewer === this._userService.userId;
return fileStatus.currentReviewer === this._userService.currentUser.id;
}
canDeleteFile(fileStatus = this._activeFile, dossier?: DossierWrapper): boolean {
@ -112,19 +104,19 @@ export class PermissionsService {
return fileStatus?.isUnderReview && this.isReviewerOrApprover(fileStatus);
}
isOwner(dossier = this._activeDossier, user = this.currentUser): boolean {
isOwner(dossier = this._activeDossier, user = this._userService.currentUser): boolean {
return dossier?.ownerId === user.id;
}
isApprover(dossier = this._activeDossier, user = this.currentUser): boolean {
isApprover(dossier = this._activeDossier, user = this._userService.currentUser): boolean {
return dossier?.approverIds.indexOf(user.id) >= 0;
}
isDossierReviewer(dossier = this._activeDossier, user = this.currentUser): boolean {
isDossierReviewer(dossier = this._activeDossier, user = this._userService.currentUser): boolean {
return this.isDossierMember(dossier, user) && !this.isApprover(dossier, user);
}
isDossierMember(dossier = this._activeDossier, user = this.currentUser): boolean {
isDossierMember(dossier = this._activeDossier, user = this._userService.currentUser): boolean {
return dossier?.memberIds.includes(user.id);
}
@ -156,29 +148,21 @@ export class PermissionsService {
}
canDeleteDossier(dossier = this._activeDossier): boolean {
return dossier.ownerId === this.currentUser.userId;
return dossier.ownerId === this._userService.currentUser.id;
}
isAdmin(user = this.currentUser): boolean {
isAdmin(user = this._userService.currentUser): boolean {
return user.isAdmin;
}
isUserAdmin(user = this.currentUser): boolean {
return user.isUserAdmin;
}
isUser(user = this.currentUser): boolean {
return user.isUser;
}
canOcrFile(fileStatus = this._activeFile): boolean {
return (
!fileStatus.isExcluded && !fileStatus.ocrTime && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(fileStatus.status)
);
}
canManageUsers(user?: UserWrapper): boolean {
return this.isUserAdmin(user);
canManageUsers(user: UserWrapper = this._userService.currentUser): boolean {
return user.isUserAdmin;
}
canAddComment(fileStatus = this._activeFile): boolean {
@ -190,6 +174,6 @@ export class PermissionsService {
}
canDeleteComment(comment: Comment, fileStatus = this._activeFile) {
return (comment.user === this._userService.userId || this.isApprover()) && !fileStatus.isApproved;
return (comment.user === this._userService.currentUser.id || this.isApprover()) && !fileStatus.isApproved;
}
}

View File

@ -1,63 +0,0 @@
import { Injectable } from '@angular/core';
import { orderBy } from 'lodash';
import { BehaviorSubject } from 'rxjs';
export const SortingOrders = {
asc: 'asc',
desc: 'desc'
} as const;
export type SortingOrder = keyof typeof SortingOrders;
export interface SortingOption {
readonly order: SortingOrder;
readonly column: string;
}
@Injectable({
providedIn: 'root'
})
export class SortingService {
private readonly _sortingOption$ = new BehaviorSubject<SortingOption>(null);
readonly sortingOption$ = this._sortingOption$.asObservable();
get sortingOption(): SortingOption {
return this._sortingOption$.getValue();
}
setSortingOption(value: SortingOption): void {
this._sortingOption$.next(value);
}
sort<T>(values: T[], order?: string, column?: string): T[] {
if (!values || !order) {
return values;
} // no array
if (!column) {
if (order === SortingOrders.asc) {
return values.sort();
} else {
return values.sort().reverse();
}
} // sort 1d array
if (values.length <= 1) {
return values;
} // array with only one item
return orderBy(values, [column], [order]);
}
defaultSort<T>(values: T[]) {
return this.sort(values, this.sortingOption?.order, this.sortingOption?.column);
}
toggleSort(column: string) {
if (this.sortingOption.column === column) {
this._sortingOption$.next({
column,
order: this.sortingOption.order === SortingOrders.asc ? SortingOrders.desc : SortingOrders.asc
});
} else {
this._sortingOption$.next({ column, order: SortingOrders.asc });
}
}
}

View File

@ -1,10 +1,11 @@
import { EventEmitter, Inject, Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { KeycloakService } from 'keycloak-angular';
import { KeycloakProfile } from 'keycloak-js';
import jwt_decode from 'jwt-decode';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { wipeCaches } from '@redaction/red-cache';
import { BASE_HREF } from '../tokens';
import { Subject } from 'rxjs';
export interface ProfileModel {
username?: string;
@ -15,62 +16,28 @@ export interface ProfileModel {
}
export class UserWrapper {
name: string;
constructor(private readonly _user: KeycloakProfile | User, public roles: string[], public id: string) {}
constructor(private readonly _currentUser: KeycloakProfile | User, public roles: string[], public id: string) {
this.name = this.firstName && this.lastName ? `${this.firstName} ${this.lastName}` : this.username;
}
email = this._user.email;
username = this._user.username || this.email;
firstName = this._user.firstName;
lastName = this._user.lastName;
name = this.firstName && this.lastName ? `${this.firstName} ${this.lastName}` : this.username;
searchKey = this.name + this.username + this.email;
get username() {
return this._currentUser.username || this.email;
}
get userId() {
return this.id;
}
get email() {
return this._currentUser.email;
}
get firstName() {
return this._currentUser.firstName;
}
get lastName() {
return this._currentUser.lastName;
}
get searchKey() {
return this.name + this.username + this.email;
}
get isManager() {
return this.roles.indexOf('RED_MANAGER') >= 0;
}
get isUserAdmin() {
return this.roles.indexOf('RED_USER_ADMIN') >= 0;
}
get isUser() {
return this.roles.indexOf('RED_USER') >= 0;
}
get isAdmin() {
return this.roles.indexOf('RED_ADMIN') >= 0;
}
get hasAnyREDRoles() {
return this.isUser || this.isManager || this.isAdmin || this.isUserAdmin;
}
isActive = this.roles.length > 0;
isManager = this.roles.indexOf('RED_MANAGER') >= 0;
isUserAdmin = this.roles.indexOf('RED_USER_ADMIN') >= 0;
isUser = this.roles.indexOf('RED_USER') >= 0;
isAdmin = this.roles.indexOf('RED_ADMIN') >= 0;
hasAnyREDRoles = this.isUser || this.isManager || this.isAdmin || this.isUserAdmin;
}
@Injectable({
providedIn: 'root'
})
export class UserService {
usersReloaded$: EventEmitter<any> = new EventEmitter<any>();
usersReloaded$ = new Subject();
private _currentUser: UserWrapper;
private _allUsers: UserWrapper[];
@ -82,35 +49,18 @@ export class UserService {
private _allRedUsers: UserWrapper[];
get allRedUsers(): UserWrapper[] {
return this._allRedUsers;
}
get userId(): string {
return this._currentUser.id;
}
get managerUsers(): UserWrapper[] {
return this._allRedUsers.filter(u => u.roles.indexOf('RED_MANAGER') >= 0);
return this._allRedUsers.filter(user => user.isManager);
}
get eligibleUsers(): UserWrapper[] {
return this._allRedUsers.filter(u => u.roles.indexOf('RED_USER') >= 0 || u.roles.indexOf('RED_MANAGER') >= 0);
return this._allRedUsers.filter(user => user.isUser || user.isManager);
}
get user() {
get currentUser(): UserWrapper {
return this._currentUser;
}
private static _hasAnyRedRole(user: UserWrapper) {
return (
user.roles.indexOf('RED_USER') >= 0 ||
user.roles.indexOf('RED_MANAGER') >= 0 ||
user.roles.indexOf('RED_ADMIN') >= 0 ||
user.roles.indexOf('RED_USER_ADMIN') >= 0
);
}
logout() {
wipeCaches().then();
this._keycloakService.logout(window.location.origin + this._baseHref).then();
@ -123,14 +73,14 @@ export class UserService {
}
async loadAllUsers() {
let allUsers = [];
let allUsers: User[];
if (this._currentUser.isUserAdmin) {
allUsers = await this._userControllerService.getAllUsers().toPromise();
} else {
allUsers = await this._userControllerService.getUsers().toPromise();
}
this._allUsers = allUsers.map(user => new UserWrapper(user, user.roles, user.userId));
this._allRedUsers = this._allUsers.filter(u => UserService._hasAnyRedRole(u));
this._allRedUsers = this._allUsers.filter(user => user.hasAnyREDRoles);
this.usersReloaded$.next();
return this._allUsers;
}
@ -147,42 +97,27 @@ export class UserService {
}
getRedUserById(id: string) {
return this._allRedUsers.find(u => u.userId === id);
return this._allRedUsers.find(u => u.id === id);
}
getUserById(id: string) {
return this._allUsers ? this._allUsers.find(u => u.userId === id) : this.getRedUserById(id);
return this._allUsers ? this._allUsers.find(u => u.id === id) : this.getRedUserById(id);
}
getNameForId(userId: string) {
return this.getName(this.getUserById(userId));
getNameForId(userId: string): string | undefined {
const user = this.getUserById(userId);
return user ? this.getName(user) : undefined;
}
getName({ firstName, lastName, username }: User = {}) {
getName({ firstName, lastName, username }: UserWrapper) {
return firstName && lastName ? `${firstName} ${lastName}` : username;
}
isManager(user: User | UserWrapper = this.user): boolean {
isManager(user: UserWrapper = this._currentUser): boolean {
return user.roles.indexOf('RED_MANAGER') >= 0;
}
isUser(user: User | UserWrapper = this.user): boolean {
return user.roles?.indexOf('RED_USER') >= 0;
}
isUserAdmin(user: User | UserWrapper = this.user): boolean {
return user.roles?.indexOf('RED_USER_ADMIN') >= 0;
}
isAdmin(user: User | UserWrapper = this.user): boolean {
return user.roles?.indexOf('RED_ADMIN') >= 0;
}
isActive(user: User | UserWrapper = this.user): boolean {
return user.roles?.length > 0;
}
hasAnyRole(requiredRoles: string[], user: User | UserWrapper = this.user) {
hasAnyRole(requiredRoles: string[], user: UserWrapper = this._currentUser) {
if (requiredRoles?.length > 0) {
for (const role of requiredRoles) if (user.roles.indexOf(role) >= 0) return true;

View File

@ -14,17 +14,17 @@ export class AppStateGuard implements CanActivate {
) {}
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
if (this._userService.user.isUserAdmin) {
if (this._userService.currentUser.isUserAdmin) {
await this._userService.loadUsersIfNecessary();
}
if (this._userService.user.isUser || this._userService.user.isAdmin) {
if (this._userService.currentUser.isUser || this._userService.currentUser.isAdmin) {
await this._userService.loadUsersIfNecessary();
await this._appStateService.loadDossierTemplatesIfNecessary();
await this._appStateService.loadDictionaryDataIfNecessary();
}
if (this._userService.isUser()) {
if (this._userService.currentUser.isUser) {
await this._appStateService.loadAllDossiersIfNecessary();
}

View File

@ -15,13 +15,14 @@ import { Event, NavigationEnd, ResolveStart, Router } from '@angular/router';
import { UserService } from '@services/user.service';
import { forkJoin, Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { FALLBACK_COLOR, hexToRgb, humanize } from '@utils/functions';
import { FALLBACK_COLOR, hexToRgb } from '@utils/functions';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { DossierWrapper } from './model/dossier.wrapper';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
import { DossiersService } from '../modules/dossier/services/dossiers.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { humanize } from '@iqser/common-ui';
export interface AppState {
dossiers: DossierWrapper[];

View File

@ -7,25 +7,6 @@ export function groupBy(xs: any[], key: string) {
}, {});
}
export function computerize(str: string) {
if (!str) {
return undefined;
}
const frags = str.toLowerCase().split(/[ \-_]+/);
return frags.join('_');
}
export function humanize(str: string, lowercase: boolean = true) {
if (!str) {
return undefined;
}
const frags = (lowercase ? str.toLowerCase() : str).split(/[ \-_]+/);
for (let i = 0; i < frags.length; i++) {
frags[i] = frags[i].charAt(0).toUpperCase() + frags[i].slice(1);
}
return frags.join(' ');
}
export function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
@ -37,14 +18,6 @@ export function hexToRgb(hex) {
: null;
}
export function keypress(key: string) {
document.dispatchEvent(new KeyboardEvent('keypress', { key: key }));
}
export function reference(x: any) {
return x;
}
export function getFirstRelevantTextPart(text, direction: 'FORWARD' | 'BACKWARD') {
let spaceCount = 0;
let accumulator = '';
@ -58,11 +31,7 @@ export function getFirstRelevantTextPart(text, direction: 'FORWARD' | 'BACKWARD'
accumulator += char;
if (spaceCount >= 2) {
return true;
} else {
return false;
}
return spaceCount >= 2;
};
if (direction === 'FORWARD') {

View File

@ -1016,7 +1016,7 @@
"form": {
"forgot-password": ""
},
"subtitle": "",
"subtitle": " ",
"title": ""
},
"subtitle": "Mit SMTP (Simple Mail Transfer Protocol) können Sie Ihre E-Mails über die angegebenen Servereinstellungen senden.",

View File

@ -1028,7 +1028,7 @@
"form": {
"forgot-password": "Show Forgot password link on Login screen"
},
"subtitle": "",
"subtitle": " ",
"title": "General Configurations"
},
"subtitle": "SMTP (Simple Mail Transfer Protocol) enables you to send your emails through the specified server settings.",

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="14px" version="1.1" viewBox="0 0 6 14" width="6px" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Icons" stroke="none" stroke-width="1">
<g id="Artboard" transform="translate(-1212.000000, -325.000000)">
<polygon fill="currentColor" id="Fill-1" points="1215 338 1218 334 1212 334"></polygon>
<polygon fill="#28324180" id="Fill-1"
points="1215 330 1218 326 1212 326"
transform="translate(1215.000000, 328.000000) rotate(-180.000000) translate(-1215.000000, -328.000000) "></polygon>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 669 B

Some files were not shown because too many files have changed in this diff Show More