Merge branch 'master' into VM/RED-2853
# Conflicts: # apps/red-ui/src/app/modules/admin/admin.module.ts # apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts
This commit is contained in:
commit
83c698b2da
37
README.md
37
README.md
@ -1,20 +1,5 @@
|
|||||||
# Redaction
|
# Redaction
|
||||||
|
|
||||||
## Swagger Generated Code
|
|
||||||
|
|
||||||
To re-generate http rune swagger
|
|
||||||
|
|
||||||
YOu need swagger-codegen installed `brew install swagger-codegen`
|
|
||||||
|
|
||||||
```
|
|
||||||
BASE=https://dev-06.iqser.cloud/
|
|
||||||
URL="$BASE"redaction-gateway-v1/v2/api-docs?group=redaction-gateway-v1
|
|
||||||
rm -Rf /tmp/swagger
|
|
||||||
mkdir -p /tmp/swagger
|
|
||||||
swagger-codegen generate -i "$URL" -l typescript-angular -o /tmp/swagger
|
|
||||||
cd /tmp/swagger
|
|
||||||
```
|
|
||||||
|
|
||||||
## To Create a new Stack in rancher
|
## To Create a new Stack in rancher
|
||||||
|
|
||||||
Goto rancher.iqser.com: Select Cluster `Development`, go to apps, click launch and select `Redaction` from the `dev`
|
Goto rancher.iqser.com: Select Cluster `Development`, go to apps, click launch and select `Redaction` from the `dev`
|
||||||
@ -25,17 +10,25 @@ Add cloudflare certificate and specify a hostname to use `timo-redaction-dev.iqs
|
|||||||
|
|
||||||
## Keycloak Staging Config
|
## Keycloak Staging Config
|
||||||
|
|
||||||
keycloak:
|
- keycloak:
|
||||||
authServerUrl: 'https://redkc-staging.iqser.cloud/auth'
|
- authServerUrl: 'https://redkc-staging.iqser.cloud/auth'
|
||||||
client:
|
- client:
|
||||||
secret: 'a4e8aa56-03b0-4e6b-b822-8ac1f41280c4'
|
- secret: 'a4e8aa56-03b0-4e6b-b822-8ac1f41280c4'
|
||||||
|
|
||||||
## Default Testing URL
|
## Default Testing URL
|
||||||
|
|
||||||
`https://timo-redaction-dev.iqser.cloud/`
|
`https://dev-04.iqser.cloud/`
|
||||||
Hostname:
|
|
||||||
|
|
||||||
timo-redaction-dev.iqser.cloud
|
## Known errors
|
||||||
|
|
||||||
|
- In case of CORS or redirect_uri errors follow these steps:
|
||||||
|
- Go to `<HOST>.iqser.cloud/auth/admin/master/console`
|
||||||
|
- Login with `admin` and `admin1234`
|
||||||
|
- In the left menu go to `Clients`
|
||||||
|
- In the table click `redaction`
|
||||||
|
- Find `Valid Redirect URIs` input
|
||||||
|
- Under `/ui/*` add new value `http://localhost:4200/*`
|
||||||
|
- **Save**
|
||||||
|
|
||||||
## Test Users
|
## Test Users
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"cli": {
|
"cli": {
|
||||||
"analytics": "4b8eed12-a1e6-4b7a-9ea2-925b27941271"
|
"analytics": false
|
||||||
},
|
},
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
<iqser-full-page-loading-indicator></iqser-full-page-loading-indicator>
|
<iqser-full-page-loading-indicator></iqser-full-page-loading-indicator>
|
||||||
|
<iqser-connection-status></iqser-connection-status>
|
||||||
<iqser-full-page-error></iqser-full-page-error>
|
<iqser-full-page-error></iqser-full-page-error>
|
||||||
|
|||||||
@ -36,9 +36,10 @@ import { KeycloakService } from 'keycloak-angular';
|
|||||||
import { GeneralSettingsService } from '@services/general-settings.service';
|
import { GeneralSettingsService } from '@services/general-settings.service';
|
||||||
import { BreadcrumbsComponent } from '@components/breadcrumbs/breadcrumbs.component';
|
import { BreadcrumbsComponent } from '@components/breadcrumbs/breadcrumbs.component';
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
|
import { UserService } from '@services/user.service';
|
||||||
|
|
||||||
export function httpLoaderFactory(httpClient: HttpClient): PruningTranslationLoader {
|
export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader {
|
||||||
return new PruningTranslationLoader(httpClient, '/assets/i18n/', '.json');
|
return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupBaseUrl(baseUrl: string) {
|
function cleanupBaseUrl(baseUrl: string) {
|
||||||
@ -76,7 +77,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
|||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useFactory: httpLoaderFactory,
|
useFactory: httpLoaderFactory,
|
||||||
deps: [HttpClient],
|
deps: [HttpClient, ConfigService],
|
||||||
},
|
},
|
||||||
compiler: {
|
compiler: {
|
||||||
provide: TranslateCompiler,
|
provide: TranslateCompiler,
|
||||||
@ -114,7 +115,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
|||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
multi: true,
|
multi: true,
|
||||||
useFactory: configurationInitializer,
|
useFactory: configurationInitializer,
|
||||||
deps: [KeycloakService, Title, ConfigService, GeneralSettingsService, LanguageService, UserPreferenceService],
|
deps: [KeycloakService, Title, ConfigService, GeneralSettingsService, LanguageService, UserService, UserPreferenceService],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: MissingTranslationHandler,
|
provide: MissingTranslationHandler,
|
||||||
@ -136,6 +137,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
|||||||
export class AppModule {
|
export class AppModule {
|
||||||
constructor(private readonly _router: Router, private readonly _route: ActivatedRoute) {
|
constructor(private readonly _router: Router, private readonly _route: ActivatedRoute) {
|
||||||
this._configureKeyCloakRouteHandling();
|
this._configureKeyCloakRouteHandling();
|
||||||
|
// this._test();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _configureKeyCloakRouteHandling() {
|
private _configureKeyCloakRouteHandling() {
|
||||||
@ -152,4 +154,60 @@ export class AppModule {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private _test(){
|
||||||
|
//
|
||||||
|
// const flatGerman = flatten(german);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// const flatEnglish = flatten(english);
|
||||||
|
//
|
||||||
|
// const tmfc = new TranslateMessageFormatCompiler();
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// for (const key of Object.keys(flatGerman)) {
|
||||||
|
// try {
|
||||||
|
// const result = tmfc.compile(flatGerman[key], 'de');
|
||||||
|
// //console.log(result);
|
||||||
|
// } catch (e) {
|
||||||
|
// console.error('ERROR AT: ', flatGerman[key]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (const key of Object.keys(flatEnglish)) {
|
||||||
|
// try {
|
||||||
|
// const result = tmfc.compile(flatEnglish[key], 'de');
|
||||||
|
// //console.log(result);
|
||||||
|
// } catch (e) {
|
||||||
|
// console.error('ERROR AT: ', flatEnglish[key]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// console.log('done');
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// function flatten(data: any) {
|
||||||
|
// const result: any = {};
|
||||||
|
//
|
||||||
|
// function recurse(cur: any, prop: any) {
|
||||||
|
// if (Object(cur) !== cur) {
|
||||||
|
// result[prop] = cur;
|
||||||
|
// } else if (Array.isArray(cur)) {
|
||||||
|
// let l = 0;
|
||||||
|
// for (let i = 0, l = cur.length; i < l; i++) recurse(cur[i], prop + '[' + i + ']');
|
||||||
|
// if (l === 0) result[prop] = [];
|
||||||
|
// } else {
|
||||||
|
// let isEmpty = true;
|
||||||
|
// for (const p in cur) {
|
||||||
|
// isEmpty = false;
|
||||||
|
// recurse(cur[p], prop ? prop + '.' + p : p);
|
||||||
|
// }
|
||||||
|
// if (isEmpty && prop) result[prop] = {};
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// recurse(data, '');
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|||||||
@ -27,7 +27,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<redaction-user-button
|
<redaction-user-button
|
||||||
[matMenuTriggerFor]="userMenu"
|
[matMenuTriggerFor]="userMenu"
|
||||||
[showDot]="fileDownloadService.hasPendingDownloads"
|
|
||||||
[userId]="currentUser.id"
|
[userId]="currentUser.id"
|
||||||
iqserHelpMode="open-usermenu"
|
iqserHelpMode="open-usermenu"
|
||||||
></redaction-user-button>
|
></redaction-user-button>
|
||||||
@ -36,7 +35,6 @@
|
|||||||
<ng-container *ngFor="let item of userMenuItems; trackBy: trackByName">
|
<ng-container *ngFor="let item of userMenuItems; trackBy: trackByName">
|
||||||
<button (click)="(item.action)" *ngIf="item.show" [routerLink]="item.routerLink" mat-menu-item translate>
|
<button (click)="(item.action)" *ngIf="item.show" [routerLink]="item.routerLink" mat-menu-item translate>
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
<span *ngIf="item.showDot()" class="dot"></span>
|
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
|
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { filter, map, startWith } from 'rxjs/operators';
|
import { filter, map, startWith } from 'rxjs/operators';
|
||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
|
||||||
import { shareDistinctLast } from '@iqser/common-ui';
|
import { shareDistinctLast } from '@iqser/common-ui';
|
||||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||||
|
|
||||||
@ -36,26 +35,22 @@ export class BaseScreenComponent {
|
|||||||
routerLink: '/main/account',
|
routerLink: '/main/account',
|
||||||
show: true,
|
show: true,
|
||||||
action: this.appStateService.reset,
|
action: this.appStateService.reset,
|
||||||
showDot: () => false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: _('top-bar.navigation-items.my-account.children.admin'),
|
name: _('top-bar.navigation-items.my-account.children.admin'),
|
||||||
routerLink: '/main/admin',
|
routerLink: '/main/admin',
|
||||||
show: this.currentUser.isManager || this.currentUser.isUserAdmin,
|
show: this.currentUser.isManager || this.currentUser.isUserAdmin,
|
||||||
action: this.appStateService.reset,
|
action: this.appStateService.reset,
|
||||||
showDot: () => false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: _('top-bar.navigation-items.my-account.children.downloads'),
|
name: _('top-bar.navigation-items.my-account.children.downloads'),
|
||||||
routerLink: '/main/downloads',
|
routerLink: '/main/downloads',
|
||||||
show: this.currentUser.isUser,
|
show: this.currentUser.isUser,
|
||||||
showDot: () => this.fileDownloadService.hasPendingDownloads,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: _('top-bar.navigation-items.my-account.children.trash'),
|
name: _('top-bar.navigation-items.my-account.children.trash'),
|
||||||
routerLink: '/main/admin/trash',
|
routerLink: '/main/admin/trash',
|
||||||
show: this.currentUser.isManager,
|
show: this.currentUser.isManager,
|
||||||
showDot: () => false,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
readonly searchActions: readonly SpotlightSearchAction[] = [
|
readonly searchActions: readonly SpotlightSearchAction[] = [
|
||||||
@ -81,11 +76,9 @@ export class BaseScreenComponent {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly appStateService: AppStateService,
|
readonly appStateService: AppStateService,
|
||||||
readonly dossiersService: DossiersService,
|
|
||||||
readonly userService: UserService,
|
readonly userService: UserService,
|
||||||
readonly userPreferenceService: UserPreferenceService,
|
readonly userPreferenceService: UserPreferenceService,
|
||||||
readonly titleService: Title,
|
readonly titleService: Title,
|
||||||
readonly fileDownloadService: FileDownloadService,
|
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
readonly breadcrumbsService: BreadcrumbsService,
|
readonly breadcrumbsService: BreadcrumbsService,
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
<mat-icon *ngIf="!first" svgIcon="iqser:arrow-right"></mat-icon>
|
<mat-icon *ngIf="!first" svgIcon="iqser:arrow-right"></mat-icon>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
[routerLinkActiveOptions]="breadcrumb.routerLinkActiveOptions ?? { exact: false }"
|
[routerLinkActiveOptions]="breadcrumb.routerLinkActiveOptions || { exact: false }"
|
||||||
[routerLink]="breadcrumb.routerLink"
|
[routerLink]="breadcrumb.routerLink"
|
||||||
class="breadcrumb"
|
class="breadcrumb"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import {
|
|||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { RouterHistoryService } from '@services/router-history.service';
|
import { RouterHistoryService } from '@services/router-history.service';
|
||||||
|
import { firstValueFrom, interval } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-downloads-list-screen',
|
selector: 'redaction-downloads-list-screen',
|
||||||
@ -43,6 +45,9 @@ export class DownloadsListScreenComponent extends ListingComponent<DownloadStatu
|
|||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
this._loadingService.loadWhile(this._loadData());
|
this._loadingService.loadWhile(this._loadData());
|
||||||
|
this.addSubscription = interval(5000)
|
||||||
|
.pipe(switchMap(() => this._loadData()))
|
||||||
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadItem(download: DownloadStatus) {
|
downloadItem(download: DownloadStatus) {
|
||||||
@ -55,11 +60,11 @@ export class DownloadsListScreenComponent extends ListingComponent<DownloadStatu
|
|||||||
|
|
||||||
private async _deleteItems(downloads?: DownloadStatus[]) {
|
private async _deleteItems(downloads?: DownloadStatus[]) {
|
||||||
const storageIds = (downloads || this.listingService.selected).map(d => d.storageId);
|
const storageIds = (downloads || this.listingService.selected).map(d => d.storageId);
|
||||||
await this.fileDownloadService.delete({ storageIds }).toPromise();
|
await firstValueFrom(this.fileDownloadService.delete({ storageIds }));
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
await this.fileDownloadService.loadAll().toPromise();
|
await firstValueFrom(this.fileDownloadService.loadAll());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
@use 'variables';
|
@use 'variables';
|
||||||
|
|
||||||
.mt-2 {
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .notifications-backdrop + .cdk-overlay-connected-position-bounding-box {
|
::ng-deep .notifications-backdrop + .cdk-overlay-connected-position-bounding-box {
|
||||||
right: 0 !important;
|
right: 0 !important;
|
||||||
|
|
||||||
@ -20,6 +16,7 @@
|
|||||||
|
|
||||||
.view-all {
|
.view-all {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--iqser-primary);
|
color: var(--iqser-primary);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { DossiersService } from '@services/entity-services/dossiers.service';
|
|||||||
import { NotificationsService } from '@services/notifications.service';
|
import { NotificationsService } from '@services/notifications.service';
|
||||||
import { Notification } from '@red/domain';
|
import { Notification } from '@red/domain';
|
||||||
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
|
||||||
import { BehaviorSubject, Observable, timer } from 'rxjs';
|
import { BehaviorSubject, firstValueFrom, Observable, timer } from 'rxjs';
|
||||||
import { AutoUnsubscribe, List, shareLast } from '@iqser/common-ui';
|
import { AutoUnsubscribe, List, shareLast } from '@iqser/common-ui';
|
||||||
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
|
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
|
||||||
|
|
||||||
@ -63,12 +63,12 @@ export class NotificationsComponent extends AutoUnsubscribe implements OnInit {
|
|||||||
|
|
||||||
async markRead($event, notifications: List<string> = this._notifications$.getValue().map(n => n.id), isRead = true): Promise<void> {
|
async markRead($event, notifications: List<string> = this._notifications$.getValue().map(n => n.id), isRead = true): Promise<void> {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
await this._notificationsService.toggleNotificationRead(notifications, isRead).toPromise();
|
await firstValueFrom(this._notificationsService.toggleNotificationRead(notifications, isRead));
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadData(): Promise<void> {
|
private async _loadData(): Promise<void> {
|
||||||
const notifications = await this._notificationsService.getNotifications(INCLUDE_SEEN).toPromise();
|
const notifications = await firstValueFrom(this._notificationsService.getNotifications(INCLUDE_SEEN));
|
||||||
this._notifications$.next(notifications);
|
this._notifications$.next(notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,30 +1,29 @@
|
|||||||
<form (submit)="executeCurrentAction()">
|
<iqser-input-with-action
|
||||||
<iqser-input-with-action
|
(action)="executeCurrentAction()"
|
||||||
(click)="openMenuIfValue()"
|
(click)="openMenuIfValue()"
|
||||||
(valueChange)="valueChanges$.next($event)"
|
(valueChange)="valueChanges$.next($event)"
|
||||||
[placeholder]="placeholder"
|
[placeholder]="placeholder"
|
||||||
></iqser-input-with-action>
|
></iqser-input-with-action>
|
||||||
|
|
||||||
<mat-menu #menu="matMenu" class="search-menu" xPosition="after">
|
<mat-menu #menu="matMenu" class="search-menu" xPosition="after">
|
||||||
<ng-template matMenuContent>
|
<ng-template matMenuContent>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<button
|
<button
|
||||||
(click)="item.action(valueChanges$.getValue())"
|
(click)="item.action(valueChanges$.getValue())"
|
||||||
*ngFor="let item of shownActions; let index = index"
|
*ngFor="let item of shownActions; let index = index"
|
||||||
[class.highlight]="(currentActionIdx$ | async) === index"
|
[class.highlight]="(currentActionIdx$ | async) === index"
|
||||||
class="spotlight-row pointer"
|
class="spotlight-row pointer"
|
||||||
>
|
>
|
||||||
<mat-icon [svgIcon]="item.icon"></mat-icon>
|
<mat-icon [svgIcon]="item.icon"></mat-icon>
|
||||||
<span>{{ item.text }}</span>
|
<span>{{ item.text }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
<!-- https://material.angular.io/components/menu/overview#toggling-the-menu-programmatically -->
|
<!-- https://material.angular.io/components/menu/overview#toggling-the-menu-programmatically -->
|
||||||
<!-- To toggle menu programmatically a matMenuTriggerFor directive is needed -->
|
<!-- To toggle menu programmatically a matMenuTriggerFor directive is needed -->
|
||||||
<div [matMenuTriggerFor]="menu"></div>
|
<div [matMenuTriggerFor]="menu"></div>
|
||||||
|
|
||||||
<!-- A hack to avoid subscribing in component -->
|
<!-- A hack to avoid subscribing in component -->
|
||||||
<ng-container *ngIf="showActions$ | async"></ng-container>
|
<ng-container *ngIf="showActions$ | async"></ng-container>
|
||||||
</form>
|
|
||||||
|
|||||||
@ -1,40 +1,34 @@
|
|||||||
import { CanDeactivate } from '@angular/router';
|
import { CanDeactivate } from '@angular/router';
|
||||||
import { Directive, HostListener, Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { map, Observable } from 'rxjs';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { ConfirmationDialogService, ConfirmOptions } from '@iqser/common-ui';
|
||||||
|
|
||||||
export interface ComponentCanDeactivate {
|
export interface ComponentCanDeactivate {
|
||||||
hasChanges: boolean;
|
changed: boolean;
|
||||||
}
|
valid?: boolean;
|
||||||
|
isLeavingPage?: boolean;
|
||||||
@Directive()
|
save: () => Promise<void>;
|
||||||
export abstract class ComponentHasChanges implements ComponentCanDeactivate {
|
|
||||||
abstract hasChanges: boolean;
|
|
||||||
|
|
||||||
protected constructor(protected _translateService: TranslateService) {}
|
|
||||||
|
|
||||||
@HostListener('window:beforeunload', ['$event'])
|
|
||||||
unloadNotification($event: any) {
|
|
||||||
if (this.hasChanges) {
|
|
||||||
// This message will be displayed in IE/Edge
|
|
||||||
$event.returnValue = this._translateService.instant('pending-changes-guard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
|
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
|
||||||
constructor(private readonly _translateService: TranslateService) {}
|
constructor(private _dialogService: ConfirmationDialogService) {}
|
||||||
|
|
||||||
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
|
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
|
||||||
// if there are no pending changes, just allow deactivation; else confirm first
|
if (component.changed) {
|
||||||
return !component.hasChanges
|
component.isLeavingPage = true;
|
||||||
? true
|
|
||||||
: // NOTE: this warning message will only be shown when navigating elsewhere
|
const dialogRef = this._dialogService.openDialog({ disableConfirm: component.valid === false });
|
||||||
// within your angular app;
|
return dialogRef.afterClosed().pipe(
|
||||||
// when navigating away from your angular app,
|
map(result => {
|
||||||
// the browser will show a generic warning message
|
if (result === ConfirmOptions.CONFIRM) {
|
||||||
// see http://stackoverflow.com/a/42207299/7307355
|
component.save();
|
||||||
confirm(this._translateService.instant('pending-changes-guard'));
|
}
|
||||||
|
component.isLeavingPage = false;
|
||||||
|
return !!result;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
|||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||||
import { FilesService } from '@services/entity-services/files.service';
|
import { FilesService } from '@services/entity-services/files.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DossierFilesGuard implements CanActivate {
|
export class DossierFilesGuard implements CanActivate {
|
||||||
@ -22,7 +23,7 @@ export class DossierFilesGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._filesMapService.has(dossierId)) {
|
if (!this._filesMapService.has(dossierId)) {
|
||||||
await this._filesService.loadAll(dossierId).toPromise();
|
await firstValueFrom(this._filesService.loadAll(dossierId));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { CanActivate, Router } from '@angular/router';
|
import { CanActivate, Router } from '@angular/router';
|
||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DossiersGuard implements CanActivate {
|
export class DossiersGuard implements CanActivate {
|
||||||
@ -12,7 +13,7 @@ export class DossiersGuard implements CanActivate {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async canActivate(): Promise<boolean> {
|
async canActivate(): Promise<boolean> {
|
||||||
await this._dossiersService.loadAll().toPromise();
|
await firstValueFrom(this._dossiersService.loadAll());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -8,6 +9,7 @@ import { UserPreferenceService } from '@services/user-preference.service';
|
|||||||
export class LanguageService {
|
export class LanguageService {
|
||||||
constructor(private readonly _translateService: TranslateService, private readonly _userPreferenceService: UserPreferenceService) {
|
constructor(private readonly _translateService: TranslateService, private readonly _userPreferenceService: UserPreferenceService) {
|
||||||
_translateService.addLangs(['en', 'de']);
|
_translateService.addLangs(['en', 'de']);
|
||||||
|
_translateService.setDefaultLang('en');
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentLanguage() {
|
get currentLanguage() {
|
||||||
@ -26,12 +28,12 @@ export class LanguageService {
|
|||||||
}
|
}
|
||||||
document.documentElement.lang = defaultLang;
|
document.documentElement.lang = defaultLang;
|
||||||
this._translateService.setDefaultLang(defaultLang);
|
this._translateService.setDefaultLang(defaultLang);
|
||||||
this._translateService.use(defaultLang).toPromise().then();
|
firstValueFrom(this._translateService.use(defaultLang)).then();
|
||||||
}
|
}
|
||||||
|
|
||||||
async changeLanguage(language: string) {
|
async changeLanguage(language: string) {
|
||||||
await this._userPreferenceService.saveLanguage(language);
|
await this._userPreferenceService.saveLanguage(language);
|
||||||
document.documentElement.lang = language;
|
document.documentElement.lang = language;
|
||||||
await this._translateService.use(language).toPromise();
|
await firstValueFrom(this._translateService.use(language));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
import { isArray } from 'rxjs/internal-compatibility';
|
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
|
import { isArray } from 'lodash';
|
||||||
|
|
||||||
export class AnnotationPermissions {
|
export class AnnotationPermissions {
|
||||||
canUndo = true;
|
canUndo = true;
|
||||||
@ -14,6 +14,7 @@ export class AnnotationPermissions {
|
|||||||
canChangeLegalBasis = true;
|
canChangeLegalBasis = true;
|
||||||
canResizeAnnotation = true;
|
canResizeAnnotation = true;
|
||||||
canRecategorizeImage = true;
|
canRecategorizeImage = true;
|
||||||
|
canForceHint = true;
|
||||||
|
|
||||||
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
||||||
if (!isArray(annotations)) {
|
if (!isArray(annotations)) {
|
||||||
@ -29,12 +30,13 @@ export class AnnotationPermissions {
|
|||||||
permissions.canAcceptSuggestion = isApprover && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
permissions.canAcceptSuggestion = isApprover && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
||||||
permissions.canRejectSuggestion = isApprover && annotation.isSuggestion;
|
permissions.canRejectSuggestion = isApprover && annotation.isSuggestion;
|
||||||
|
|
||||||
|
permissions.canForceHint = annotation.isIgnoredHint;
|
||||||
permissions.canForceRedaction = annotation.isSkipped && !annotation.isFalsePositive;
|
permissions.canForceRedaction = annotation.isSkipped && !annotation.isFalsePositive;
|
||||||
permissions.canAcceptRecommendation = annotation.isRecommendation;
|
permissions.canAcceptRecommendation = annotation.isRecommendation;
|
||||||
|
|
||||||
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive;
|
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive;
|
||||||
|
|
||||||
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted;
|
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted || annotation.isHint;
|
||||||
permissions.canRemoveOrSuggestToRemoveFromDictionary =
|
permissions.canRemoveOrSuggestToRemoveFromDictionary =
|
||||||
annotation.isModifyDictionary && (annotation.isRedacted || annotation.isSkipped || annotation.isHint);
|
annotation.isModifyDictionary && (annotation.isRedacted || annotation.isSkipped || annotation.isHint);
|
||||||
|
|
||||||
@ -42,7 +44,9 @@ export class AnnotationPermissions {
|
|||||||
|
|
||||||
permissions.canRecategorizeImage = (annotation.isImage && !annotation.isSuggestion) || annotation.isSuggestionRecategorizeImage;
|
permissions.canRecategorizeImage = (annotation.isImage && !annotation.isSuggestion) || annotation.isSuggestionRecategorizeImage;
|
||||||
permissions.canResizeAnnotation =
|
permissions.canResizeAnnotation =
|
||||||
((annotation.isRedacted || annotation.isImage) && !annotation.isSuggestion) || annotation.isSuggestionResize;
|
((annotation.isRedacted || annotation.isImage) && !annotation.isSuggestion) ||
|
||||||
|
annotation.isSuggestionResize ||
|
||||||
|
annotation.isRecommendation;
|
||||||
|
|
||||||
summedPermissions._merge(permissions);
|
summedPermissions._merge(permissions);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export type AnnotationSuperType =
|
|||||||
| 'suggestion-remove-dictionary'
|
| 'suggestion-remove-dictionary'
|
||||||
| 'suggestion-add'
|
| 'suggestion-add'
|
||||||
| 'suggestion-remove'
|
| 'suggestion-remove'
|
||||||
|
| 'ignored-hint'
|
||||||
| 'skipped'
|
| 'skipped'
|
||||||
| 'redaction'
|
| 'redaction'
|
||||||
| 'manual-redaction'
|
| 'manual-redaction'
|
||||||
@ -43,7 +44,9 @@ export class AnnotationWrapper {
|
|||||||
legalBasisChangeValue?: string;
|
legalBasisChangeValue?: string;
|
||||||
resizing?: boolean;
|
resizing?: boolean;
|
||||||
rectangle?: boolean;
|
rectangle?: boolean;
|
||||||
|
hintDictionary?: boolean;
|
||||||
section?: string;
|
section?: string;
|
||||||
|
reference: Array<string>;
|
||||||
|
|
||||||
manual?: boolean;
|
manual?: boolean;
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isSuperTypeBasedColor() {
|
get isSuperTypeBasedColor() {
|
||||||
return this.isSkipped || this.isSuggestion || this.isDeclinedSuggestion;
|
return this.isSkipped || this.isSuggestion || this.isDeclinedSuggestion || this.isIgnoredHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSkipped() {
|
get isSkipped() {
|
||||||
@ -106,6 +109,20 @@ export class AnnotationWrapper {
|
|||||||
return this.recategorizationType || this.typeValue;
|
return this.recategorizationType || this.typeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get topLevelFilter() {
|
||||||
|
return (
|
||||||
|
this.superType !== 'hint' &&
|
||||||
|
this.superType !== 'redaction' &&
|
||||||
|
this.superType !== 'recommendation' &&
|
||||||
|
this.superType !== 'ignored-hint' &&
|
||||||
|
this.superType !== 'skipped'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get filterKey() {
|
||||||
|
return this.topLevelFilter ? this.superType : this.superType + this.type;
|
||||||
|
}
|
||||||
|
|
||||||
get isManuallySkipped() {
|
get isManuallySkipped() {
|
||||||
return this.isSkipped && this.manual;
|
return this.isSkipped && this.manual;
|
||||||
}
|
}
|
||||||
@ -113,7 +130,10 @@ export class AnnotationWrapper {
|
|||||||
get isFalsePositive() {
|
get isFalsePositive() {
|
||||||
return (
|
return (
|
||||||
this.type?.toLowerCase() === 'false_positive' &&
|
this.type?.toLowerCase() === 'false_positive' &&
|
||||||
(this.superType === 'skipped' || this.superType === 'hint' || this.superType === 'redaction')
|
(this.superType === 'skipped' ||
|
||||||
|
this.superType === 'hint' ||
|
||||||
|
this.superType === 'ignored-hint' ||
|
||||||
|
this.superType === 'redaction')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +153,10 @@ export class AnnotationWrapper {
|
|||||||
return this.superType === 'hint';
|
return this.superType === 'hint';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isIgnoredHint() {
|
||||||
|
return this.superType === 'ignored-hint';
|
||||||
|
}
|
||||||
|
|
||||||
get isRedacted() {
|
get isRedacted() {
|
||||||
return this.superType === 'redaction' || this.superType === 'manual-redaction';
|
return this.superType === 'redaction' || this.superType === 'manual-redaction';
|
||||||
}
|
}
|
||||||
@ -236,12 +260,14 @@ export class AnnotationWrapper {
|
|||||||
annotationWrapper.manual = redactionLogEntry.manual;
|
annotationWrapper.manual = redactionLogEntry.manual;
|
||||||
annotationWrapper.engines = redactionLogEntry.engines;
|
annotationWrapper.engines = redactionLogEntry.engines;
|
||||||
annotationWrapper.section = redactionLogEntry.section;
|
annotationWrapper.section = redactionLogEntry.section;
|
||||||
|
annotationWrapper.reference = redactionLogEntry.reference || [];
|
||||||
annotationWrapper.rectangle = redactionLogEntry.rectangle;
|
annotationWrapper.rectangle = redactionLogEntry.rectangle;
|
||||||
annotationWrapper.hasBeenResized = redactionLogEntry.hasBeenResized;
|
annotationWrapper.hasBeenResized = redactionLogEntry.hasBeenResized;
|
||||||
annotationWrapper.hasBeenRecategorized = redactionLogEntry.hasBeenRecategorized;
|
annotationWrapper.hasBeenRecategorized = redactionLogEntry.hasBeenRecategorized;
|
||||||
annotationWrapper.hasLegalBasisChanged = redactionLogEntry.hasLegalBasisChanged;
|
annotationWrapper.hasLegalBasisChanged = redactionLogEntry.hasLegalBasisChanged;
|
||||||
annotationWrapper.hasBeenForced = redactionLogEntry.hasBeenForced;
|
annotationWrapper.hasBeenForced = redactionLogEntry.hasBeenForced;
|
||||||
annotationWrapper.hasBeenRemovedByManualOverride = redactionLogEntry.hasBeenRemovedByManualOverride;
|
annotationWrapper.hasBeenRemovedByManualOverride = redactionLogEntry.hasBeenRemovedByManualOverride;
|
||||||
|
annotationWrapper.hintDictionary = redactionLogEntry.hintDictionary;
|
||||||
|
|
||||||
this._createContent(annotationWrapper, redactionLogEntry);
|
this._createContent(annotationWrapper, redactionLogEntry);
|
||||||
this._setSuperType(annotationWrapper, redactionLogEntry);
|
this._setSuperType(annotationWrapper, redactionLogEntry);
|
||||||
@ -259,11 +285,7 @@ export class AnnotationWrapper {
|
|||||||
|
|
||||||
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
||||||
if (redactionLogEntryWrapper.recommendation) {
|
if (redactionLogEntryWrapper.recommendation) {
|
||||||
if (redactionLogEntryWrapper.redacted) {
|
annotationWrapper.superType = 'recommendation';
|
||||||
annotationWrapper.superType = 'recommendation';
|
|
||||||
} else {
|
|
||||||
annotationWrapper.superType = 'skipped';
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +302,7 @@ export class AnnotationWrapper {
|
|||||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||||
annotationWrapper.superType = 'suggestion-force-redaction';
|
annotationWrapper.superType = 'suggestion-force-redaction';
|
||||||
} else if (redactionLogEntryWrapper.status === 'APPROVED') {
|
} else if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||||
annotationWrapper.superType = 'redaction';
|
annotationWrapper.superType = redactionLogEntryWrapper.hint ? 'hint' : 'redaction';
|
||||||
} else {
|
} else {
|
||||||
annotationWrapper.superType = 'skipped';
|
annotationWrapper.superType = 'skipped';
|
||||||
}
|
}
|
||||||
@ -408,6 +430,11 @@ export class AnnotationWrapper {
|
|||||||
if (!annotationWrapper.superType) {
|
if (!annotationWrapper.superType) {
|
||||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
||||||
}
|
}
|
||||||
|
if (annotationWrapper.superType === 'skipped') {
|
||||||
|
if (redactionLogEntryWrapper.hintDictionary) {
|
||||||
|
annotationWrapper.superType = 'ignored-hint';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
||||||
|
|||||||
@ -1,36 +1,48 @@
|
|||||||
import { Dictionary, File, IRedactionLog, IRedactionLogEntry, IViewedPage, User, ViewMode } from '@red/domain';
|
import { Dictionary, File, IRedactionLog, IRedactionLogEntry, IViewedPage, ViewMode } from '@red/domain';
|
||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
export class AnnotationData {
|
|
||||||
visibleAnnotations: AnnotationWrapper[];
|
|
||||||
allAnnotations: AnnotationWrapper[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FileDataModel {
|
export class FileDataModel {
|
||||||
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||||
|
allAnnotations: AnnotationWrapper[];
|
||||||
|
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||||
|
readonly blob$ = new BehaviorSubject<Blob>(undefined);
|
||||||
|
readonly file$ = new BehaviorSubject<File>(undefined);
|
||||||
|
|
||||||
hasChangeLog: boolean;
|
constructor(
|
||||||
|
private readonly _file: File,
|
||||||
|
private readonly _blob: Blob,
|
||||||
|
private _redactionLog: IRedactionLog,
|
||||||
|
public viewedPages?: IViewedPage[],
|
||||||
|
private _dictionaryData?: { [p: string]: Dictionary },
|
||||||
|
private _areDevFeaturesEnabled?: boolean,
|
||||||
|
) {
|
||||||
|
this.file$.next(_file);
|
||||||
|
this.blob$.next(_blob);
|
||||||
|
this._buildAllAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
constructor(public file: File, public fileData: Blob, public redactionLog: IRedactionLog, public viewedPages?: IViewedPage[]) {}
|
get file(): File {
|
||||||
|
return this.file$.value;
|
||||||
|
}
|
||||||
|
|
||||||
getAnnotations(
|
set file(file: File) {
|
||||||
dictionaryData: { [p: string]: Dictionary },
|
this.file$.next(file);
|
||||||
currentUser: User,
|
}
|
||||||
viewMode: ViewMode,
|
|
||||||
areDevFeaturesEnabled: boolean,
|
|
||||||
): AnnotationData {
|
|
||||||
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
|
||||||
let allAnnotations = entries
|
|
||||||
.map(entry => AnnotationWrapper.fromData(entry))
|
|
||||||
.filter(ann => ann.manual || !this.file.excludedPages.includes(ann.pageNumber));
|
|
||||||
|
|
||||||
if (!areDevFeaturesEnabled) {
|
get redactionLog(): IRedactionLog {
|
||||||
allAnnotations = allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
return this._redactionLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibleAnnotations = allAnnotations.filter(annotation => {
|
set redactionLog(redactionLog: IRedactionLog) {
|
||||||
|
this._redactionLog = redactionLog;
|
||||||
|
this._buildAllAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibleAnnotations(viewMode: ViewMode) {
|
||||||
|
return this.allAnnotations.filter(annotation => {
|
||||||
if (viewMode === 'STANDARD') {
|
if (viewMode === 'STANDARD') {
|
||||||
return !annotation.isChangeLogRemoved;
|
return !annotation.isChangeLogRemoved;
|
||||||
} else if (viewMode === 'DELTA') {
|
} else if (viewMode === 'DELTA') {
|
||||||
@ -39,11 +51,30 @@ export class FileDataModel {
|
|||||||
return annotation.isRedacted;
|
return annotation.isRedacted;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
private _buildAllAnnotations() {
|
||||||
visibleAnnotations: visibleAnnotations,
|
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
||||||
allAnnotations: allAnnotations,
|
|
||||||
};
|
const previousAnnotations = this.allAnnotations || [];
|
||||||
|
this.allAnnotations = entries
|
||||||
|
.map(entry => AnnotationWrapper.fromData(entry))
|
||||||
|
.filter(ann => ann.manual || !this._file.excludedPages.includes(ann.pageNumber));
|
||||||
|
|
||||||
|
if (!this._areDevFeaturesEnabled) {
|
||||||
|
this.allAnnotations = this.allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setHiddenPropertyToNewAnnotations(this.allAnnotations, previousAnnotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
||||||
|
newAnnotations.forEach(newAnnotation => {
|
||||||
|
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
|
||||||
|
if (oldAnnotation) {
|
||||||
|
newAnnotation.hidden = oldAnnotation.hidden;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _convertData(): RedactionLogEntryWrapper[] {
|
private _convertData(): RedactionLogEntryWrapper[] {
|
||||||
@ -55,6 +86,7 @@ export class FileDataModel {
|
|||||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
|
||||||
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
||||||
redactionLogEntryWrapper.type = redactionLogEntry.type;
|
redactionLogEntryWrapper.type = redactionLogEntry.type;
|
||||||
|
redactionLogEntryWrapper.hintDictionary = this._dictionaryData[redactionLogEntry.type].hint;
|
||||||
|
|
||||||
this._isChangeLogEntry(redactionLogEntry, redactionLogEntryWrapper);
|
this._isChangeLogEntry(redactionLogEntry, redactionLogEntryWrapper);
|
||||||
|
|
||||||
@ -101,11 +133,11 @@ export class FileDataModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _isChangeLogEntry(redactionLogEntry: IRedactionLogEntry, wrapper: RedactionLogEntryWrapper) {
|
private _isChangeLogEntry(redactionLogEntry: IRedactionLogEntry, wrapper: RedactionLogEntryWrapper) {
|
||||||
if (this.file.numberOfAnalyses > 1) {
|
if (this._file.numberOfAnalyses > 1) {
|
||||||
redactionLogEntry.changes.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf());
|
const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1);
|
||||||
|
viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf());
|
||||||
|
|
||||||
const lastChange =
|
const lastChange = viableChanges.length >= 1 ? viableChanges[viableChanges.length - 1] : undefined;
|
||||||
redactionLogEntry.changes.length >= 1 ? redactionLogEntry.changes[redactionLogEntry.changes.length - 1] : undefined;
|
|
||||||
const page = redactionLogEntry.positions?.[0].page;
|
const page = redactionLogEntry.positions?.[0].page;
|
||||||
|
|
||||||
const viewedPage = this.viewedPages.filter(p => p.page === page).pop();
|
const viewedPage = this.viewedPages.filter(p => p.page === page).pop();
|
||||||
@ -114,14 +146,14 @@ export class FileDataModel {
|
|||||||
if (viewedPage) {
|
if (viewedPage) {
|
||||||
const viewTime = moment(viewedPage.viewedTime).valueOf() - FileDataModel.DELTA_VIEW_TIME;
|
const viewTime = moment(viewedPage.viewedTime).valueOf() - FileDataModel.DELTA_VIEW_TIME;
|
||||||
// these are all unseen changes
|
// these are all unseen changes
|
||||||
const relevantChanges = redactionLogEntry.changes.filter(change => moment(change.dateTime).valueOf() > viewTime);
|
const relevantChanges = viableChanges.filter(change => moment(change.dateTime).valueOf() > viewTime);
|
||||||
// at least one unseen change
|
// at least one unseen change
|
||||||
if (relevantChanges.length > 0) {
|
if (relevantChanges.length > 0) {
|
||||||
// at least 1 relevant change
|
// at least 1 relevant change
|
||||||
wrapper.changeLogType = relevantChanges[relevantChanges.length - 1].type;
|
wrapper.changeLogType = relevantChanges[relevantChanges.length - 1].type;
|
||||||
wrapper.isChangeLogEntry = true;
|
wrapper.isChangeLogEntry = true;
|
||||||
viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf();
|
viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf();
|
||||||
this.hasChangeLog = true;
|
this.hasChangeLog$.next(true);
|
||||||
} else {
|
} else {
|
||||||
// no relevant changes - hide removed anyway
|
// no relevant changes - hide removed anyway
|
||||||
wrapper.isChangeLogEntry = false;
|
wrapper.isChangeLogEntry = false;
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export interface RedactionLogEntryWrapper {
|
|||||||
startOffset?: number;
|
startOffset?: number;
|
||||||
type?: string;
|
type?: string;
|
||||||
rectangle?: boolean;
|
rectangle?: boolean;
|
||||||
|
hintDictionary?: boolean;
|
||||||
|
|
||||||
color?: Array<number>;
|
color?: Array<number>;
|
||||||
dictionaryEntry?: boolean;
|
dictionaryEntry?: boolean;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { CompositeRouteGuard } from '@iqser/common-ui';
|
import { CompositeRouteGuard } from '@iqser/common-ui';
|
||||||
import { AuthGuard } from '../auth/auth.guard';
|
import { AuthGuard } from '../auth/auth.guard';
|
||||||
import { RedRoleGuard } from '../auth/red-role.guard';
|
import { RedRoleGuard } from '../auth/red-role.guard';
|
||||||
import { AppStateGuard } from '../../state/app-state.guard';
|
import { AppStateGuard } from '@state/app-state.guard';
|
||||||
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
export const NotificationCategories = {
|
|
||||||
inAppNotifications: 'inAppNotifications',
|
|
||||||
emailNotifications: 'emailNotifications',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const NotificationCategoriesValues = Object.values(NotificationCategories);
|
|
||||||
|
|
||||||
export const DossierNotificationsTypes = {
|
|
||||||
dossierOwnerSet: 'DOSSIER_OWNER_SET',
|
|
||||||
dossierOwnerRemoved: 'DOSSIER_OWNER_REMOVED',
|
|
||||||
userBecomseDossierMember: 'USER_BECOMES_DOSSIER_MEMBER',
|
|
||||||
userRemovedAsDossierMember: 'USER_REMOVED_AS_DOSSIER_MEMBER',
|
|
||||||
userPromotedToApprover: 'USER_PROMOTED_TO_APPROVER',
|
|
||||||
userDegradedToReviewer: 'USER_DEGRADED_TO_REVIEWER',
|
|
||||||
dossierOwnerDeleted: 'DOSSIER_OWNER_DELETED',
|
|
||||||
dossierDeleted: 'DOSSIER_DELETED',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const DossierNotificationsTypesValues = Object.values(DossierNotificationsTypes);
|
|
||||||
|
|
||||||
export const DocumentNotificationsTypes = {
|
|
||||||
assignReviewer: 'ASSIGN_REVIEWER',
|
|
||||||
assignApprover: 'ASSIGN_APPROVER',
|
|
||||||
unassignedFromFile: 'UNASSIGNED_FROM_FILE',
|
|
||||||
// documentUnderReview: 'DOCUMENT_UNDER_REVIEW',
|
|
||||||
// documentUnderApproval: 'DOCUMENT_UNDER_APPROVAL',
|
|
||||||
documentApproved: 'DOCUMENT_APPROVED',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const DocumentNotificationsTypesValues = Object.values(DocumentNotificationsTypes);
|
|
||||||
|
|
||||||
export const OtherNotificationsTypes = {
|
|
||||||
downloadReady: 'DOWNLOAD_READY',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const OtherNotificationsTypesValues = Object.values(OtherNotificationsTypes);
|
|
||||||
|
|
||||||
export const NotificationGroupsKeys = ['dossier', 'document', 'other'] as const;
|
|
||||||
export const NotificationGroupsValues = [
|
|
||||||
DossierNotificationsTypesValues,
|
|
||||||
DocumentNotificationsTypesValues,
|
|
||||||
OtherNotificationsTypesValues,
|
|
||||||
] as const;
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<form (submit)="save()" [formGroup]="formGroup">
|
<form (submit)="save()" [formGroup]="form">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div *ngFor="let category of notificationCategories">
|
<div *ngFor="let category of notificationCategories">
|
||||||
<div class="iqser-input-group header w-full">
|
<div class="iqser-input-group header w-full">
|
||||||
@ -7,25 +7,25 @@
|
|||||||
}}</mat-slide-toggle>
|
}}</mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="options-content" *ngIf="isCategoryActive(category)">
|
<div *ngIf="isCategoryActive(category)" class="options-content">
|
||||||
<div class="radio-container" *ngIf="category === 'emailNotifications'">
|
<!-- <div class="radio-container" *ngIf="category === 'emailNotifications'">-->
|
||||||
<div class="radio-button" *ngFor="let type of emailNotificationScheduleTypes">
|
<!-- <div class="radio-button" *ngFor="let type of emailNotificationScheduleTypes">-->
|
||||||
<iqser-round-checkbox [active]="getEmailNotificationType() === type" (click)="setEmailNotificationType(type)">
|
<!-- <iqser-round-checkbox [active]="getEmailNotificationType() === type" (click)="setEmailNotificationType(type)">-->
|
||||||
</iqser-round-checkbox>
|
<!-- </iqser-round-checkbox>-->
|
||||||
<span> {{ translations[type.toLocaleLowerCase()] | translate }} </span>
|
<!-- <span> {{ translations[type.toLocaleLowerCase()] | translate }} </span>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
|
|
||||||
<div class="statement" translate="notifications-screen.options-title"></div>
|
<div class="statement" translate="notifications-screen.options-title"></div>
|
||||||
|
|
||||||
<div class="group" *ngFor="let key of notificationGroupsKeys; let i = index">
|
<div *ngFor="let key of notificationGroupsKeys; let i = index" class="group">
|
||||||
<div class="group-title" [translate]="translations[key]"></div>
|
<div [translate]="translations[key]" class="group-title"></div>
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
*ngFor="let preference of notificationGroupsValues[i]"
|
|
||||||
color="primary"
|
|
||||||
[checked]="isPreferenceChecked(category, preference)"
|
|
||||||
(change)="addRemovePreference($event.checked, category, preference)"
|
(change)="addRemovePreference($event.checked, category, preference)"
|
||||||
|
*ngFor="let preference of notificationGroupsValues[i]"
|
||||||
|
[checked]="isPreferenceChecked(category, preference)"
|
||||||
|
color="primary"
|
||||||
>
|
>
|
||||||
{{ translations[preference] | translate }}
|
{{ translations[preference] | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="formGroup.invalid" color="primary" mat-flat-button type="submit">
|
<button [disabled]="form.invalid" color="primary" mat-flat-button type="submit">
|
||||||
{{ 'user-profile-screen.actions.save' | translate }}
|
{{ 'user-profile-screen.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,10 +2,15 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
|||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { notificationsTranslations } from '../../../translations/notifications-translations';
|
import { notificationsTranslations } from '../../../translations/notifications-translations';
|
||||||
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
||||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { NotificationCategoriesValues, NotificationGroupsKeys, NotificationGroupsValues } from '../constants';
|
import {
|
||||||
import { EmailNotificationScheduleTypesValues } from '@red/domain';
|
EmailNotificationScheduleTypesValues,
|
||||||
|
NotificationCategoriesValues,
|
||||||
|
NotificationGroupsKeys,
|
||||||
|
NotificationGroupsValues,
|
||||||
|
} from '@red/domain';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-notifications-screen',
|
selector: 'redaction-notifications-screen',
|
||||||
@ -13,26 +18,63 @@ import { EmailNotificationScheduleTypesValues } from '@red/domain';
|
|||||||
styleUrls: ['./notifications-screen.component.scss'],
|
styleUrls: ['./notifications-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class NotificationsScreenComponent implements OnInit {
|
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly emailNotificationScheduleTypes = EmailNotificationScheduleTypesValues;
|
readonly emailNotificationScheduleTypes = EmailNotificationScheduleTypesValues;
|
||||||
readonly notificationCategories = NotificationCategoriesValues;
|
readonly notificationCategories = NotificationCategoriesValues;
|
||||||
readonly notificationGroupsKeys = NotificationGroupsKeys;
|
readonly notificationGroupsKeys = NotificationGroupsKeys;
|
||||||
readonly notificationGroupsValues = NotificationGroupsValues;
|
readonly notificationGroupsValues = NotificationGroupsValues;
|
||||||
readonly translations = notificationsTranslations;
|
readonly translations = notificationsTranslations;
|
||||||
|
|
||||||
readonly formGroup: FormGroup = this._getForm();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _notificationPreferencesService: NotificationPreferencesService,
|
private readonly _notificationPreferencesService: NotificationPreferencesService,
|
||||||
) {}
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
await this._initializeForm();
|
await this._initializeForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCategoryActive(category: string) {
|
||||||
|
return this.form.get(`${category}Enabled`).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEmailNotificationType(type: string) {
|
||||||
|
this.form.get('emailNotificationType').setValue(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmailNotificationType() {
|
||||||
|
return this.form.get('emailNotificationType').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreferenceChecked(category: string, preference: string) {
|
||||||
|
return this.form.get(category).value.includes(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRemovePreference(checked: boolean, category: string, preference: string) {
|
||||||
|
const preferences = this.form.get(category).value;
|
||||||
|
if (checked) {
|
||||||
|
preferences.push(preference);
|
||||||
|
} else {
|
||||||
|
const indexOfPreference = preferences.indexOf(preference);
|
||||||
|
preferences.splice(indexOfPreference, 1);
|
||||||
|
}
|
||||||
|
this.form.get(category).setValue(preferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
this._loadingService.start();
|
||||||
|
try {
|
||||||
|
await firstValueFrom(this._notificationPreferencesService.update(this.form.value));
|
||||||
|
} catch (e) {
|
||||||
|
this._toaster.error(_('notifications-screen.error.generic'));
|
||||||
|
}
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
private _getForm(): FormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
inAppNotificationsEnabled: [undefined],
|
inAppNotificationsEnabled: [undefined],
|
||||||
@ -43,48 +85,13 @@ export class NotificationsScreenComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isCategoryActive(category: string) {
|
|
||||||
return this.formGroup.get(`${category}Enabled`).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEmailNotificationType(type: string) {
|
|
||||||
this.formGroup.get('emailNotificationType').setValue(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
getEmailNotificationType() {
|
|
||||||
return this.formGroup.get('emailNotificationType').value;
|
|
||||||
}
|
|
||||||
|
|
||||||
isPreferenceChecked(category: string, preference: string) {
|
|
||||||
return this.formGroup.get(category).value.includes(preference);
|
|
||||||
}
|
|
||||||
|
|
||||||
addRemovePreference(checked: boolean, category: string, preference: string) {
|
|
||||||
const preferences = this.formGroup.get(category).value;
|
|
||||||
if (checked) {
|
|
||||||
preferences.push(preference);
|
|
||||||
} else {
|
|
||||||
const indexOfPreference = preferences.indexOf(preference);
|
|
||||||
preferences.splice(indexOfPreference, 1);
|
|
||||||
}
|
|
||||||
this.formGroup.get(category).setValue(preferences);
|
|
||||||
}
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
this._loadingService.start();
|
|
||||||
try {
|
|
||||||
await this._notificationPreferencesService.update(this.formGroup.value).toPromise();
|
|
||||||
} catch (e) {
|
|
||||||
this._toaster.error(_('notifications-screen.error.generic'));
|
|
||||||
}
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _initializeForm() {
|
private async _initializeForm() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
|
|
||||||
const notificationPreferences = await this._notificationPreferencesService.get().toPromise();
|
this.form = this._getForm();
|
||||||
this.formGroup.patchValue(notificationPreferences);
|
const notificationPreferences = await firstValueFrom(this._notificationPreferencesService.get());
|
||||||
|
this.form.patchValue(notificationPreferences);
|
||||||
|
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
||||||
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,9 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SharedModule } from '@shared/shared.module';
|
import { SharedModule } from '@shared/shared.module';
|
||||||
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
||||||
|
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
|
||||||
|
|
||||||
const routes = [{ path: '', component: NotificationsScreenComponent }];
|
const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [NotificationsScreenComponent],
|
declarations: [NotificationsScreenComponent],
|
||||||
|
|||||||
@ -2,13 +2,14 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
|||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { LoadingService } from '@iqser/common-ui';
|
import { BaseFormComponent, LoadingService } from '@iqser/common-ui';
|
||||||
import { IProfile } from '@red/domain';
|
import { IProfile } from '@red/domain';
|
||||||
import { languagesTranslations } from '../../../translations/languages-translations';
|
import { languagesTranslations } from '../../../translations/languages-translations';
|
||||||
import { PermissionsService } from '../../../../../services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { UserService } from '../../../../../services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { ConfigService } from '../../../../../services/config.service';
|
import { ConfigService } from '../../../../../services/config.service';
|
||||||
import { LanguageService } from '../../../../../i18n/language.service';
|
import { LanguageService } from '../../../../../i18n/language.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-user-profile-screen',
|
selector: 'redaction-user-profile-screen',
|
||||||
@ -16,8 +17,7 @@ import { LanguageService } from '../../../../../i18n/language.service';
|
|||||||
styleUrls: ['./user-profile-screen.component.scss'],
|
styleUrls: ['./user-profile-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class UserProfileScreenComponent implements OnInit {
|
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
changePasswordUrl: SafeResourceUrl;
|
changePasswordUrl: SafeResourceUrl;
|
||||||
translations = languagesTranslations;
|
translations = languagesTranslations;
|
||||||
|
|
||||||
@ -30,25 +30,16 @@ export class UserProfileScreenComponent implements OnInit {
|
|||||||
private readonly _configService: ConfigService,
|
private readonly _configService: ConfigService,
|
||||||
private readonly _languageService: LanguageService,
|
private readonly _languageService: LanguageService,
|
||||||
private readonly _domSanitizer: DomSanitizer,
|
private readonly _domSanitizer: DomSanitizer,
|
||||||
private readonly _translateService: TranslateService,
|
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
|
protected readonly _translateService: TranslateService,
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
|
|
||||||
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
|
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
|
||||||
`${this._configService.values.OAUTH_URL}/account/password`,
|
`${this._configService.values.OAUTH_URL}/account/password`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
email: [undefined, [Validators.required, Validators.email]],
|
|
||||||
firstName: [undefined],
|
|
||||||
lastName: [undefined],
|
|
||||||
language: [undefined],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get languageChanged(): boolean {
|
get languageChanged(): boolean {
|
||||||
return this._profileModel['language'] !== this.form.get('language').value;
|
return this._profileModel['language'] !== this.form.get('language').value;
|
||||||
}
|
}
|
||||||
@ -82,24 +73,34 @@ export class UserProfileScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.profileChanged) {
|
if (this.profileChanged) {
|
||||||
const value = this.form.value as IProfile;
|
const value = this.form.getRawValue() as IProfile;
|
||||||
delete value.language;
|
delete value.language;
|
||||||
|
|
||||||
await this._userService
|
await firstValueFrom(
|
||||||
.updateMyProfile({
|
this._userService.updateMyProfile({
|
||||||
...value,
|
...value,
|
||||||
})
|
}),
|
||||||
.toPromise();
|
);
|
||||||
|
|
||||||
await this._userService.loadCurrentUser();
|
await this._userService.loadCurrentUser();
|
||||||
await this._userService.loadAll().toPromise();
|
await firstValueFrom(this._userService.loadAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
this._initializeForm();
|
this._initializeForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getForm(): FormGroup {
|
||||||
|
return this._formBuilder.group({
|
||||||
|
email: [undefined, [Validators.required, Validators.email]],
|
||||||
|
firstName: [undefined],
|
||||||
|
lastName: [undefined],
|
||||||
|
language: [undefined],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _initializeForm(): void {
|
private _initializeForm(): void {
|
||||||
try {
|
try {
|
||||||
|
this.form = this._getForm();
|
||||||
this._profileModel = {
|
this._profileModel = {
|
||||||
email: this._userService.currentUser.email,
|
email: this._userService.currentUser.email,
|
||||||
firstName: this._userService.currentUser.firstName,
|
firstName: this._userService.currentUser.firstName,
|
||||||
@ -111,6 +112,7 @@ export class UserProfileScreenComponent implements OnInit {
|
|||||||
this.form.get('email').disable();
|
this.form.get('email').disable();
|
||||||
}
|
}
|
||||||
this.form.patchValue(this._profileModel, { emitEvent: false });
|
this.form.patchValue(this._profileModel, { emitEvent: false });
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
} finally {
|
} finally {
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
|
|||||||
@ -3,8 +3,9 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SharedModule } from '@shared/shared.module';
|
import { SharedModule } from '@shared/shared.module';
|
||||||
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
||||||
|
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
|
||||||
|
|
||||||
const routes = [{ path: '', component: UserProfileScreenComponent }];
|
const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [UserProfileScreenComponent],
|
declarations: [UserProfileScreenComponent],
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import { GenericService } from '@iqser/common-ui';
|
import { GenericService } from '@iqser/common-ui';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { EmailNotificationScheduleTypes, INotificationPreferences } from '@red/domain';
|
import { EmailNotificationScheduleTypes, INotificationPreferences } from '@red/domain';
|
||||||
import { catchError } from 'rxjs/operators';
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -11,14 +11,6 @@ export class NotificationPreferencesService extends GenericService<INotification
|
|||||||
super(_injector, 'notification-preferences');
|
super(_injector, 'notification-preferences');
|
||||||
}
|
}
|
||||||
|
|
||||||
get(): Observable<INotificationPreferences> {
|
|
||||||
return super.get<INotificationPreferences>().pipe(catchError(() => of(this._defaultPreferences)));
|
|
||||||
}
|
|
||||||
|
|
||||||
update(notificationPreferences: INotificationPreferences): Observable<void> {
|
|
||||||
return super._post(notificationPreferences);
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _defaultPreferences(): INotificationPreferences {
|
private get _defaultPreferences(): INotificationPreferences {
|
||||||
return {
|
return {
|
||||||
emailNotificationType: EmailNotificationScheduleTypes.INSTANT,
|
emailNotificationType: EmailNotificationScheduleTypes.INSTANT,
|
||||||
@ -28,4 +20,12 @@ export class NotificationPreferencesService extends GenericService<INotification
|
|||||||
inAppNotificationsEnabled: true,
|
inAppNotificationsEnabled: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get(): Observable<INotificationPreferences> {
|
||||||
|
return super.get<INotificationPreferences>().pipe(catchError(() => of(this._defaultPreferences)));
|
||||||
|
}
|
||||||
|
|
||||||
|
update(notificationPreferences: INotificationPreferences): Observable<void> {
|
||||||
|
return super._post(notificationPreferences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { AuthGuard } from '../auth/auth.guard';
|
|||||||
import { CompositeRouteGuard } from '@iqser/common-ui';
|
import { CompositeRouteGuard } from '@iqser/common-ui';
|
||||||
import { RedRoleGuard } from '../auth/red-role.guard';
|
import { RedRoleGuard } from '../auth/red-role.guard';
|
||||||
import { AppStateGuard } from '@state/app-state.guard';
|
import { AppStateGuard } from '@state/app-state.guard';
|
||||||
import { DossierTemplatesListingScreenComponent } from './screens/dossier-template-listing/dossier-templates-listing-screen.component';
|
|
||||||
import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component';
|
import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component';
|
||||||
import { DictionaryOverviewScreenComponent } from './screens/dictionary-overview/dictionary-overview-screen.component';
|
import { DictionaryOverviewScreenComponent } from './screens/dictionary-overview/dictionary-overview-screen.component';
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
@ -21,6 +20,7 @@ import { DossierAttributesListingScreenComponent } from './screens/dossier-attri
|
|||||||
import { TrashScreenComponent } from './screens/trash/trash-screen.component';
|
import { TrashScreenComponent } from './screens/trash/trash-screen.component';
|
||||||
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
||||||
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
||||||
|
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
|
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
|
||||||
@ -29,15 +29,25 @@ const routes: Routes = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: DossierTemplatesListingScreenComponent,
|
component: BaseAdminScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
||||||
},
|
},
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./screens/dossier-templates-listing/dossier-templates-listing.module').then(
|
||||||
|
m => m.DossierTemplatesListingModule,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':dossierTemplateId',
|
path: ':dossierTemplateId',
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'info',
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () => import('./screens/info/dossier-template-info.module').then(m => m.DossierTemplateInfoModule),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'dictionaries',
|
path: 'dictionaries',
|
||||||
children: [
|
children: [
|
||||||
@ -111,11 +121,11 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'justifications',
|
path: 'justifications',
|
||||||
component: BaseAdminScreenComponent,
|
component: BaseDossierTemplateScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
loadChildren: () => import('./screens/justifications/justifications.module').then(m => m.JustificationsModule),
|
loadChildren: () => import('./screens/justifications/justifications.module').then(m => m.JustificationsModule),
|
||||||
},
|
},
|
||||||
{ path: '', redirectTo: 'dictionaries', pathMatch: 'full' },
|
{ path: '', redirectTo: 'info', pathMatch: 'full' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -164,6 +174,7 @@ const routes: Routes = [
|
|||||||
path: 'general-config',
|
path: 'general-config',
|
||||||
component: GeneralConfigScreenComponent,
|
component: GeneralConfigScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
||||||
requiredRoles: ['RED_ADMIN'],
|
requiredRoles: ['RED_ADMIN'],
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export class AdminSideNavComponent implements OnInit {
|
|||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
screen: 'dossier-templates',
|
screen: 'dossier-templates',
|
||||||
label: _('dossier-templates'),
|
label: _('dossier-templates.label'),
|
||||||
hideIf: !this.currentUser.isManager && !this.currentUser.isAdmin,
|
hideIf: !this.currentUser.isManager && !this.currentUser.isAdmin,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -50,6 +50,7 @@ export class AdminSideNavComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
dossierTemplates: [
|
dossierTemplates: [
|
||||||
|
{ screen: 'info', label: _('dossier-template-info') },
|
||||||
{ screen: 'dictionaries', label: _('dictionaries') },
|
{ screen: 'dictionaries', label: _('dictionaries') },
|
||||||
{
|
{
|
||||||
screen: 'rules',
|
screen: 'rules',
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { AdminRoutingModule } from './admin-routing.module';
|
import { AdminRoutingModule } from './admin-routing.module';
|
||||||
import { RulesScreenComponent } from './screens/rules/rules-screen.component';
|
import { RulesScreenComponent } from './screens/rules/rules-screen.component';
|
||||||
import { SharedModule } from '@shared/shared.module';
|
import { SharedModule } from '@shared/shared.module';
|
||||||
import { DossierTemplatesListingScreenComponent } from './screens/dossier-template-listing/dossier-templates-listing-screen.component';
|
|
||||||
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
||||||
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
||||||
import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component';
|
import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component';
|
||||||
@ -13,13 +12,12 @@ import { FileAttributesListingScreenComponent } from './screens/file-attributes-
|
|||||||
import { LicenseInformationScreenComponent } from './screens/license-information/license-information-screen.component';
|
import { LicenseInformationScreenComponent } from './screens/license-information/license-information-screen.component';
|
||||||
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
||||||
import { WatermarkScreenComponent } from './screens/watermark/watermark-screen.component';
|
import { WatermarkScreenComponent } from './screens/watermark/watermark-screen.component';
|
||||||
import { AdminBreadcrumbsComponent } from './components/breadcrumbs/admin-breadcrumbs.component';
|
import { DossierTemplateBreadcrumbsComponent } from './components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
||||||
import { DossierTemplateActionsComponent } from './components/dossier-template-actions/dossier-template-actions.component';
|
|
||||||
import { ColorPickerModule } from 'ngx-color-picker';
|
import { ColorPickerModule } from 'ngx-color-picker';
|
||||||
import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
|
import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
|
||||||
import { AddEditDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
|
import { AddEditDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
|
||||||
import { AddEditDictionaryDialogComponent } from './dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
|
import { AddEditDictionaryDialogComponent } from './dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
|
||||||
import { ConfirmDeleteFileAttributeDialogComponent } from './dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component';
|
import { ConfirmDeleteAttributeDialogComponent } from './dialogs/confirm-delete-attribute-dialog/confirm-delete-attribute-dialog.component';
|
||||||
import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component';
|
import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||||
import { ComboChartComponent, ComboSeriesVerticalComponent } from './components/combo-chart';
|
import { ComboChartComponent, ComboSeriesVerticalComponent } from './components/combo-chart';
|
||||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||||
@ -49,12 +47,14 @@ import { UploadDictionaryDialogComponent } from './dialogs/upload-dictionary-dia
|
|||||||
import { GeneralConfigFormComponent } from './screens/general-config/general-config-form/general-config-form.component';
|
import { GeneralConfigFormComponent } from './screens/general-config/general-config-form/general-config-form.component';
|
||||||
import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.component';
|
import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.component';
|
||||||
import { FileAttributesConfigurationsDialogComponent } from './dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component';
|
import { FileAttributesConfigurationsDialogComponent } from './dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component';
|
||||||
|
import { SharedAdminModule } from './shared/shared-admin.module';
|
||||||
|
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
||||||
|
|
||||||
const dialogs = [
|
const dialogs = [
|
||||||
AddEditDossierTemplateDialogComponent,
|
AddEditDossierTemplateDialogComponent,
|
||||||
AddEditDictionaryDialogComponent,
|
AddEditDictionaryDialogComponent,
|
||||||
AddEditFileAttributeDialogComponent,
|
AddEditFileAttributeDialogComponent,
|
||||||
ConfirmDeleteFileAttributeDialogComponent,
|
ConfirmDeleteAttributeDialogComponent,
|
||||||
EditColorDialogComponent,
|
EditColorDialogComponent,
|
||||||
SmtpAuthDialogComponent,
|
SmtpAuthDialogComponent,
|
||||||
AddEditUserDialogComponent,
|
AddEditUserDialogComponent,
|
||||||
@ -66,7 +66,6 @@ const dialogs = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const screens = [
|
const screens = [
|
||||||
DossierTemplatesListingScreenComponent,
|
|
||||||
RulesScreenComponent,
|
RulesScreenComponent,
|
||||||
AuditScreenComponent,
|
AuditScreenComponent,
|
||||||
DefaultColorsScreenComponent,
|
DefaultColorsScreenComponent,
|
||||||
@ -84,8 +83,7 @@ const screens = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
AdminBreadcrumbsComponent,
|
DossierTemplateBreadcrumbsComponent,
|
||||||
DossierTemplateActionsComponent,
|
|
||||||
ComboChartComponent,
|
ComboChartComponent,
|
||||||
ComboSeriesVerticalComponent,
|
ComboSeriesVerticalComponent,
|
||||||
UsersStatsComponent,
|
UsersStatsComponent,
|
||||||
@ -94,14 +92,17 @@ const components = [
|
|||||||
ResetPasswordComponent,
|
ResetPasswordComponent,
|
||||||
UserDetailsComponent,
|
UserDetailsComponent,
|
||||||
BaseAdminScreenComponent,
|
BaseAdminScreenComponent,
|
||||||
|
BaseDossierTemplateScreenComponent,
|
||||||
|
GeneralConfigFormComponent,
|
||||||
|
SmtpFormComponent,
|
||||||
|
|
||||||
...dialogs,
|
...dialogs,
|
||||||
...screens,
|
...screens,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [...components, GeneralConfigFormComponent, SmtpFormComponent],
|
declarations: [...components],
|
||||||
providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService],
|
providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService],
|
||||||
imports: [CommonModule, SharedModule, AdminRoutingModule, NgxChartsModule, ColorPickerModule, MonacoEditorModule],
|
imports: [CommonModule, SharedModule, AdminRoutingModule, SharedAdminModule, NgxChartsModule, ColorPickerModule, MonacoEditorModule],
|
||||||
})
|
})
|
||||||
export class AdminModule {}
|
export class AdminModule {}
|
||||||
|
|||||||
@ -1,26 +1,5 @@
|
|||||||
<!--TODO: This is only used for justifications for now, should be used for all admin screens-->
|
<div class="overlay-shadow"></div>
|
||||||
|
|
||||||
<section>
|
<redaction-admin-side-nav type="settings"></redaction-admin-side-nav>
|
||||||
<div class="page-header">
|
|
||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
|
||||||
|
|
||||||
<div class="flex-1 actions">
|
<router-outlet></router-outlet>
|
||||||
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
[routerLink]="['../..']"
|
|
||||||
[tooltip]="'common.close' | translate"
|
|
||||||
icon="iqser:close"
|
|
||||||
tooltipPosition="below"
|
|
||||||
></iqser-circle-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-inner">
|
|
||||||
<div class="overlay-shadow"></div>
|
|
||||||
|
|
||||||
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-base-admin-screen',
|
|
||||||
templateUrl: './base-admin-screen.component.html',
|
templateUrl: './base-admin-screen.component.html',
|
||||||
styleUrls: ['./base-admin-screen.component.scss'],
|
styleUrls: ['./base-admin-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
<!--TODO: Use this for all dossier template screens -->
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="page-header">
|
||||||
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
|
<div class="flex-1 actions">
|
||||||
|
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
||||||
|
|
||||||
|
<iqser-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
[tooltip]="'common.close' | translate"
|
||||||
|
icon="iqser:close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></iqser-circle-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-inner">
|
||||||
|
<div class="overlay-shadow"></div>
|
||||||
|
|
||||||
|
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './base-dossier-template-screen.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class BaseDossierTemplateScreenComponent {}
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
import { AppStateService } from '@state/app-state.service';
|
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'redaction-admin-breadcrumbs',
|
|
||||||
templateUrl: './admin-breadcrumbs.component.html',
|
|
||||||
styleUrls: ['./admin-breadcrumbs.component.scss'],
|
|
||||||
})
|
|
||||||
export class AdminBreadcrumbsComponent {
|
|
||||||
@Input() root = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly userPreferenceService: UserPreferenceService,
|
|
||||||
readonly permissionService: PermissionsService,
|
|
||||||
readonly appStateService: AppStateService,
|
|
||||||
readonly dossierTemplatesService: DossierTemplatesService,
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
@ -12,7 +12,18 @@ import {
|
|||||||
|
|
||||||
import { curveLinear } from 'd3-shape';
|
import { curveLinear } from 'd3-shape';
|
||||||
import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
|
import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
|
||||||
import { BaseChartComponent, calculateViewDimensions, ColorHelper, LineSeriesComponent, ViewDimensions } from '@swimlane/ngx-charts';
|
import {
|
||||||
|
BaseChartComponent,
|
||||||
|
calculateViewDimensions,
|
||||||
|
Color,
|
||||||
|
ColorHelper,
|
||||||
|
LegendPosition,
|
||||||
|
LineSeriesComponent,
|
||||||
|
Orientation,
|
||||||
|
ScaleType,
|
||||||
|
ViewDimensions,
|
||||||
|
} from '@swimlane/ngx-charts';
|
||||||
|
import { ILineChartSeries } from './models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// eslint-disable-next-line @angular-eslint/component-selector
|
// eslint-disable-next-line @angular-eslint/component-selector
|
||||||
@ -25,7 +36,7 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
@Input() curve: any = curveLinear;
|
@Input() curve: any = curveLinear;
|
||||||
@Input() legend = false;
|
@Input() legend = false;
|
||||||
@Input() legendTitle = 'Legend';
|
@Input() legendTitle = 'Legend';
|
||||||
@Input() legendPosition = 'right';
|
@Input() legendPosition: LegendPosition = LegendPosition.Right;
|
||||||
@Input() xAxis;
|
@Input() xAxis;
|
||||||
@Input() yAxis;
|
@Input() yAxis;
|
||||||
@Input() showXAxisLabel;
|
@Input() showXAxisLabel;
|
||||||
@ -38,33 +49,33 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
@Input() gradient: boolean;
|
@Input() gradient: boolean;
|
||||||
@Input() showGridLines = true;
|
@Input() showGridLines = true;
|
||||||
@Input() activeEntries: any[] = [];
|
@Input() activeEntries: any[] = [];
|
||||||
@Input() schemeType: string;
|
@Input() schemeType: ScaleType;
|
||||||
@Input() xAxisTickFormatting: any;
|
@Input() xAxisTickFormatting: any;
|
||||||
@Input() yAxisTickFormatting: any;
|
@Input() yAxisTickFormatting: any;
|
||||||
@Input() yRightAxisTickFormatting: any;
|
@Input() yRightAxisTickFormatting: any;
|
||||||
@Input() roundDomains = false;
|
@Input() roundDomains = false;
|
||||||
@Input() colorSchemeLine: any;
|
@Input() colorSchemeLine: Color;
|
||||||
@Input() autoScale;
|
@Input() autoScale;
|
||||||
@Input() lineChart: any;
|
@Input() lineChart: ILineChartSeries[];
|
||||||
@Input() yLeftAxisScaleFactor: any;
|
@Input() yLeftAxisScaleFactor: any;
|
||||||
@Input() yRightAxisScaleFactor: any;
|
@Input() yRightAxisScaleFactor: any;
|
||||||
@Input() rangeFillOpacity: number;
|
@Input() rangeFillOpacity: number;
|
||||||
@Input() animations = true;
|
@Input() animations = true;
|
||||||
@Input() noBarWhenZero = true;
|
@Input() noBarWhenZero = true;
|
||||||
|
|
||||||
@Output() activate: EventEmitter<any> = new EventEmitter();
|
@Output() activate = new EventEmitter<{ value; entries: unknown[] }>();
|
||||||
@Output() deactivate: EventEmitter<any> = new EventEmitter();
|
@Output() deactivate = new EventEmitter<{ value; entries: unknown[] }>();
|
||||||
|
|
||||||
@ContentChild('tooltipTemplate') tooltipTemplate: TemplateRef<any>;
|
@ContentChild('tooltipTemplate') tooltipTemplate: TemplateRef<unknown>;
|
||||||
@ContentChild('seriesTooltipTemplate') seriesTooltipTemplate: TemplateRef<any>;
|
@ContentChild('seriesTooltipTemplate') seriesTooltipTemplate: TemplateRef<unknown>;
|
||||||
|
|
||||||
@ViewChild(LineSeriesComponent) lineSeriesComponent: LineSeriesComponent;
|
@ViewChild(LineSeriesComponent) lineSeriesComponent: LineSeriesComponent;
|
||||||
|
|
||||||
dims: ViewDimensions;
|
dims: ViewDimensions;
|
||||||
xScale: any;
|
xScale: any;
|
||||||
yScale: any;
|
yScale: any;
|
||||||
xDomain: any;
|
xDomain: string[] | number[];
|
||||||
yDomain: any;
|
yDomain: string[] | number[];
|
||||||
transform: string;
|
transform: string;
|
||||||
colors: ColorHelper;
|
colors: ColorHelper;
|
||||||
colorsLine: ColorHelper;
|
colorsLine: ColorHelper;
|
||||||
@ -72,19 +83,19 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
xAxisHeight = 0;
|
xAxisHeight = 0;
|
||||||
yAxisWidth = 0;
|
yAxisWidth = 0;
|
||||||
legendOptions: any;
|
legendOptions: any;
|
||||||
scaleType = 'linear';
|
scaleType: ScaleType = ScaleType.Linear;
|
||||||
xScaleLine;
|
xScaleLine;
|
||||||
yScaleLine;
|
yScaleLine;
|
||||||
xDomainLine;
|
xDomainLine;
|
||||||
yDomainLine;
|
yDomainLine;
|
||||||
seriesDomain;
|
seriesDomain;
|
||||||
scaledAxis;
|
scaledAxis;
|
||||||
combinedSeries;
|
combinedSeries: ILineChartSeries[];
|
||||||
xSet;
|
xSet;
|
||||||
filteredDomain;
|
filteredDomain;
|
||||||
hoveredVertical;
|
hoveredVertical;
|
||||||
yOrientLeft = 'left';
|
yOrientLeft: Orientation = Orientation.Left;
|
||||||
yOrientRight = 'right';
|
yOrientRight: Orientation = Orientation.Right;
|
||||||
legendSpacing = 0;
|
legendSpacing = 0;
|
||||||
bandwidth: number;
|
bandwidth: number;
|
||||||
barPadding = 8;
|
barPadding = 8;
|
||||||
@ -176,15 +187,11 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
return this.combinedSeries.map(d => d.name);
|
return this.combinedSeries.map(d => d.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
isDate(value): boolean {
|
isDate(value): value is Date {
|
||||||
if (value instanceof Date) {
|
return value instanceof Date;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getScaleType(values): string {
|
getScaleType(values): ScaleType {
|
||||||
let date = true;
|
let date = true;
|
||||||
let num = true;
|
let num = true;
|
||||||
|
|
||||||
@ -199,16 +206,16 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (date) {
|
if (date) {
|
||||||
return 'time';
|
return ScaleType.Time;
|
||||||
}
|
}
|
||||||
if (num) {
|
if (num) {
|
||||||
return 'linear';
|
return ScaleType.Linear;
|
||||||
}
|
}
|
||||||
return 'ordinal';
|
return ScaleType.Ordinal;
|
||||||
}
|
}
|
||||||
|
|
||||||
getXDomainLine(): any[] {
|
getXDomainLine(): any[] {
|
||||||
let values = [];
|
let values: number[] = [];
|
||||||
|
|
||||||
for (const results of this.lineChart) {
|
for (const results of this.lineChart) {
|
||||||
for (const d of results.series) {
|
for (const d of results.series) {
|
||||||
@ -239,7 +246,7 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getYDomainLine(): any[] {
|
getYDomainLine(): any[] {
|
||||||
const domain = [];
|
const domain: number[] = [];
|
||||||
|
|
||||||
for (const results of this.lineChart) {
|
for (const results of this.lineChart) {
|
||||||
for (const d of results.series) {
|
for (const d of results.series) {
|
||||||
@ -263,7 +270,7 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
const max = Math.max(...domain);
|
const max = Math.max(...domain);
|
||||||
if (this.yRightAxisScaleFactor) {
|
if (this.yRightAxisScaleFactor) {
|
||||||
const minMax = this.yRightAxisScaleFactor(min, max);
|
const minMax = this.yRightAxisScaleFactor(min, max);
|
||||||
return [Math.min(0, minMax.min), minMax.max];
|
return [Math.min(0, minMax.min as number), minMax.max];
|
||||||
} else {
|
} else {
|
||||||
min = Math.min(0, min);
|
min = Math.min(0, min);
|
||||||
return [min, max];
|
return [min, max];
|
||||||
@ -317,12 +324,12 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getYDomain() {
|
getYDomain() {
|
||||||
const values = this.results.map(d => d.value);
|
const values: number[] = this.results.map(d => d.value);
|
||||||
const min = Math.min(0, ...values);
|
const min = Math.min(0, ...values);
|
||||||
const max = Math.max(...values);
|
const max = Math.max(...values);
|
||||||
if (this.yLeftAxisScaleFactor) {
|
if (this.yLeftAxisScaleFactor) {
|
||||||
const minMax = this.yLeftAxisScaleFactor(min, max);
|
const minMax = this.yLeftAxisScaleFactor(min, max);
|
||||||
return [Math.min(0, minMax.min), minMax.max];
|
return [Math.min(0, minMax.min as number), minMax.max];
|
||||||
} else {
|
} else {
|
||||||
return [min, max];
|
return [min, max];
|
||||||
}
|
}
|
||||||
@ -333,7 +340,7 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setColors(): void {
|
setColors(): void {
|
||||||
let domain;
|
let domain: number[] | string[];
|
||||||
if (this.schemeType === 'ordinal') {
|
if (this.schemeType === 'ordinal') {
|
||||||
domain = this.xDomain;
|
domain = this.xDomain;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||||
import { animate, style, transition, trigger } from '@angular/animations';
|
import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
import { formatLabel } from '@swimlane/ngx-charts';
|
import { Bar, BarOrientation, formatLabel, PlacementTypes, StyleTypes } from '@swimlane/ngx-charts';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// eslint-disable-next-line @angular-eslint/component-selector
|
// eslint-disable-next-line @angular-eslint/component-selector
|
||||||
@ -17,7 +17,7 @@ import { formatLabel } from '@swimlane/ngx-charts';
|
|||||||
[fill]="bar.color"
|
[fill]="bar.color"
|
||||||
[stops]="bar.gradientStops"
|
[stops]="bar.gradientStops"
|
||||||
[data]="bar.data"
|
[data]="bar.data"
|
||||||
[orientation]="'vertical'"
|
[orientation]="orientations.Vertical"
|
||||||
[roundEdges]="bar.roundEdges"
|
[roundEdges]="bar.roundEdges"
|
||||||
[gradient]="gradient"
|
[gradient]="gradient"
|
||||||
[isActive]="isActive(bar.data)"
|
[isActive]="isActive(bar.data)"
|
||||||
@ -27,8 +27,8 @@ import { formatLabel } from '@swimlane/ngx-charts';
|
|||||||
(deactivate)="deactivate.emit($event)"
|
(deactivate)="deactivate.emit($event)"
|
||||||
ngx-tooltip
|
ngx-tooltip
|
||||||
[tooltipDisabled]="tooltipDisabled"
|
[tooltipDisabled]="tooltipDisabled"
|
||||||
[tooltipPlacement]="0"
|
[tooltipPlacement]="tooltipPlacements.Top"
|
||||||
[tooltipType]="1"
|
[tooltipType]="tooltipTypes.tooltip"
|
||||||
[tooltipTitle]="bar.tooltipText"
|
[tooltipTitle]="bar.tooltipText"
|
||||||
></svg:g>
|
></svg:g>
|
||||||
`,
|
`,
|
||||||
@ -67,6 +67,9 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
bars: any;
|
bars: any;
|
||||||
x: any;
|
x: any;
|
||||||
y: any;
|
y: any;
|
||||||
|
readonly tooltipTypes = StyleTypes;
|
||||||
|
readonly tooltipPlacements = PlacementTypes;
|
||||||
|
readonly orientations = BarOrientation;
|
||||||
|
|
||||||
ngOnChanges(): void {
|
ngOnChanges(): void {
|
||||||
this.update();
|
this.update();
|
||||||
@ -91,7 +94,7 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
const formattedLabel = formatLabel(label);
|
const formattedLabel = formatLabel(label);
|
||||||
const roundEdges = this.type === 'standard';
|
const roundEdges = this.type === 'standard';
|
||||||
|
|
||||||
const bar: any = {
|
const bar: Bar = {
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
roundEdges,
|
roundEdges,
|
||||||
@ -101,8 +104,15 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
height: 0,
|
height: 0,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: label,
|
||||||
|
color: undefined,
|
||||||
|
gradientStops: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let offset0 = d0;
|
||||||
|
let offset1 = offset0 + value;
|
||||||
|
|
||||||
if (this.type === 'standard') {
|
if (this.type === 'standard') {
|
||||||
bar.height = Math.abs(this.yScale(value) - this.yScale(0));
|
bar.height = Math.abs(this.yScale(value) - this.yScale(0));
|
||||||
bar.x = this.xScale(label);
|
bar.x = this.xScale(label);
|
||||||
@ -113,18 +123,14 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
bar.y = this.yScale(value);
|
bar.y = this.yScale(value);
|
||||||
}
|
}
|
||||||
} else if (this.type === 'stacked') {
|
} else if (this.type === 'stacked') {
|
||||||
const offset0 = d0;
|
|
||||||
const offset1 = offset0 + value;
|
|
||||||
d0 += value;
|
d0 += value;
|
||||||
|
|
||||||
bar.height = this.yScale(offset0) - this.yScale(offset1);
|
bar.height = this.yScale(offset0) - this.yScale(offset1);
|
||||||
bar.x = 0;
|
bar.x = 0;
|
||||||
bar.y = this.yScale(offset1);
|
bar.y = this.yScale(offset1);
|
||||||
bar.offset0 = offset0;
|
// bar.offset0 = offset0;
|
||||||
bar.offset1 = offset1;
|
// bar.offset1 = offset1;
|
||||||
} else if (this.type === 'normalized') {
|
} else if (this.type === 'normalized') {
|
||||||
let offset0 = d0;
|
|
||||||
let offset1 = offset0 + value;
|
|
||||||
d0 += value;
|
d0 += value;
|
||||||
|
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
@ -138,8 +144,8 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
bar.height = this.yScale(offset0) - this.yScale(offset1);
|
bar.height = this.yScale(offset0) - this.yScale(offset1);
|
||||||
bar.x = 0;
|
bar.x = 0;
|
||||||
bar.y = this.yScale(offset1);
|
bar.y = this.yScale(offset1);
|
||||||
bar.offset0 = offset0;
|
// bar.offset0 = offset0;
|
||||||
bar.offset1 = offset1;
|
// bar.offset1 = offset1;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
value = (offset1 - offset0).toFixed(2) + '%';
|
value = (offset1 - offset0).toFixed(2) + '%';
|
||||||
@ -152,8 +158,8 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
bar.color = this.colors.getColor(value);
|
bar.color = this.colors.getColor(value);
|
||||||
bar.gradientStops = this.colors.getLinearGradientStops(value);
|
bar.gradientStops = this.colors.getLinearGradientStops(value);
|
||||||
} else {
|
} else {
|
||||||
bar.color = this.colors.getColor(bar.offset1);
|
bar.color = this.colors.getColor(offset1);
|
||||||
bar.gradientStops = this.colors.getLinearGradientStops(bar.offset1, bar.offset0);
|
bar.gradientStops = this.colors.getLinearGradientStops(offset1, offset0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
export interface ISeries {
|
||||||
|
name: number;
|
||||||
|
value: number;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILineChartSeries {
|
||||||
|
name: string;
|
||||||
|
series: ISeries[];
|
||||||
|
}
|
||||||
@ -3,13 +3,14 @@
|
|||||||
*ngIf="root || !!dossierTemplatesService.activeDossierTemplate"
|
*ngIf="root || !!dossierTemplatesService.activeDossierTemplate"
|
||||||
[routerLink]="'/main/admin/dossier-templates'"
|
[routerLink]="'/main/admin/dossier-templates'"
|
||||||
class="breadcrumb"
|
class="breadcrumb"
|
||||||
translate="dossier-templates"
|
translate="dossier-templates.label"
|
||||||
></a>
|
></a>
|
||||||
|
|
||||||
<ng-container *ngIf="dossierTemplatesService.activeDossierTemplate$ | async as activeDossierTemplate">
|
<mat-icon svgIcon="iqser:arrow-right"></mat-icon>
|
||||||
<mat-icon svgIcon="iqser:arrow-right"></mat-icon>
|
|
||||||
<a [class.active]="!appStateService.activeDictionaryType" [routerLink]="activeDossierTemplate.routerLink" class="breadcrumb ml-0">
|
<ng-container *ngIf="dossierTemplate$ | async as dossierTemplate">
|
||||||
{{ activeDossierTemplate.name }}
|
<a [class.active]="!appStateService.activeDictionaryType" [routerLink]="dossierTemplate.routerLink" class="breadcrumb ml-0">
|
||||||
|
{{ dossierTemplate.name }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ng-container *ngIf="appStateService.activeDictionary">
|
<ng-container *ngIf="appStateService.activeDictionary">
|
||||||
@ -17,7 +18,7 @@
|
|||||||
<a
|
<a
|
||||||
[routerLink]="
|
[routerLink]="
|
||||||
'/main/admin/dossier-templates/' +
|
'/main/admin/dossier-templates/' +
|
||||||
activeDossierTemplate.dossierTemplateId +
|
dossierTemplate.dossierTemplateId +
|
||||||
'/dictionaries/' +
|
'/dictionaries/' +
|
||||||
appStateService.activeDictionaryType
|
appStateService.activeDictionaryType
|
||||||
"
|
"
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { AppStateService } from '@state/app-state.service';
|
||||||
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { DossierTemplate } from '@red/domain';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-dossier-template-breadcrumbs',
|
||||||
|
templateUrl: './dossier-template-breadcrumbs.component.html',
|
||||||
|
styleUrls: ['./dossier-template-breadcrumbs.component.scss'],
|
||||||
|
})
|
||||||
|
export class DossierTemplateBreadcrumbsComponent {
|
||||||
|
@Input() root = false;
|
||||||
|
readonly dossierTemplate$: Observable<DossierTemplate>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly userPreferenceService: UserPreferenceService,
|
||||||
|
readonly permissionService: PermissionsService,
|
||||||
|
readonly appStateService: AppStateService,
|
||||||
|
readonly dossierTemplatesService: DossierTemplatesService,
|
||||||
|
private readonly _route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
this.dossierTemplate$ = _route.paramMap.pipe(
|
||||||
|
map(params => params.get('dossierTemplateId')),
|
||||||
|
switchMap((dossierTemplateId: string) => this.dossierTemplatesService.getEntityChanged$(dossierTemplateId)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,3 @@
|
|||||||
left: 270px;
|
left: 270px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-44 {
|
|
||||||
margin-top: 44px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -95,11 +95,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button (click)="save()" [disabled]="!valid || !changed" color="primary" mat-flat-button>
|
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
|
||||||
{{ 'add-edit-dictionary.save' | translate }}
|
{{ 'add-edit-dictionary.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -10,10 +10,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-14 {
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.technical-name {
|
.technical-name {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
import { BaseDialogComponent, shareDistinctLast, Toaster } from '@iqser/common-ui';
|
import { BaseDialogComponent, shareDistinctLast, Toaster } from '@iqser/common-ui';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { toKebabCase } from '@utils/functions';
|
import { toSnakeCase } from '@utils/functions';
|
||||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||||
import { Dictionary, IDictionary } from '@red/domain';
|
import { Dictionary, IDictionary } from '@red/domain';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
@ -21,7 +21,6 @@ import { HttpStatusCode } from '@angular/common/http';
|
|||||||
})
|
})
|
||||||
export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
|
export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
|
||||||
readonly dictionary = this._data.dictionary;
|
readonly dictionary = this._data.dictionary;
|
||||||
readonly form: FormGroup = this._getForm(this.dictionary);
|
|
||||||
readonly canEditLabel$ = this._canEditLabel$;
|
readonly canEditLabel$ = this._canEditLabel$;
|
||||||
readonly technicalName$: Observable<string>;
|
readonly technicalName$: Observable<string>;
|
||||||
readonly dialogHeader = this._translateService.instant('add-edit-dictionary.title', {
|
readonly dialogHeader = this._translateService.instant('add-edit-dictionary.title', {
|
||||||
@ -29,7 +28,6 @@ export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
|
|||||||
name: this._data.dictionary?.label,
|
name: this._data.dictionary?.label,
|
||||||
});
|
});
|
||||||
readonly hasColor$: Observable<boolean>;
|
readonly hasColor$: Observable<boolean>;
|
||||||
readonly disabled = false;
|
|
||||||
private readonly _dossierTemplateId = this._data.dossierTemplateId;
|
private readonly _dossierTemplateId = this._data.dossierTemplateId;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -39,37 +37,18 @@ export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
|
|||||||
private readonly _appStateService: AppStateService,
|
private readonly _appStateService: AppStateService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
private readonly _dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA)
|
@Inject(MAT_DIALOG_DATA)
|
||||||
private readonly _data: { readonly dictionary: Dictionary; readonly dossierTemplateId: string },
|
private readonly _data: { readonly dictionary: Dictionary; readonly dossierTemplateId: string },
|
||||||
) {
|
) {
|
||||||
super();
|
super(_injector, _dialogRef);
|
||||||
|
this.form = this._getForm(this.dictionary);
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this.hasColor$ = this._colorEmpty$;
|
this.hasColor$ = this._colorEmpty$;
|
||||||
this.technicalName$ = this.form.get('label').valueChanges.pipe(map(value => this._toTechnicalName(value)));
|
this.technicalName$ = this.form.get('label').valueChanges.pipe(map(value => this._toTechnicalName(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
get valid(): boolean {
|
|
||||||
return this.form.valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get changed(): boolean {
|
|
||||||
if (!this.dictionary) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(this.form.getRawValue())) {
|
|
||||||
if (key === 'caseSensitive') {
|
|
||||||
if (this.getDictCaseSensitive(this.dictionary) !== this.form.get(key).value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (this.dictionary[key] !== this.form.get(key).value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _canEditLabel$() {
|
private get _canEditLabel$() {
|
||||||
return this.userService.currentUser$.pipe(
|
return this.userService.currentUser$.pipe(
|
||||||
map(user => user.isAdmin || !this._data.dictionary),
|
map(user => user.isAdmin || !this._data.dictionary),
|
||||||
@ -98,8 +77,7 @@ export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
|
|||||||
observable = this._dictionaryService.addDictionary({ ...dictionary, dossierTemplateId });
|
observable = this._dictionaryService.addDictionary({ ...dictionary, dossierTemplateId });
|
||||||
}
|
}
|
||||||
|
|
||||||
return observable
|
return firstValueFrom(observable)
|
||||||
.toPromise()
|
|
||||||
.then(() => this._dialogRef.close(true))
|
.then(() => this._dialogRef.close(true))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.status === HttpStatusCode.Conflict) {
|
if (error.status === HttpStatusCode.Conflict) {
|
||||||
@ -126,11 +104,11 @@ export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
|
|||||||
|
|
||||||
private _toTechnicalName(value: string) {
|
private _toTechnicalName(value: string) {
|
||||||
const existingTechnicalNames = Object.keys(this._appStateService.dictionaryData[this._dossierTemplateId]);
|
const existingTechnicalNames = Object.keys(this._appStateService.dictionaryData[this._dossierTemplateId]);
|
||||||
const baseTechnicalName = toKebabCase(value.trim());
|
const baseTechnicalName = toSnakeCase(value.trim());
|
||||||
let technicalName = baseTechnicalName;
|
let technicalName = baseTechnicalName;
|
||||||
let suffix = 1;
|
let suffix = 1;
|
||||||
while (existingTechnicalNames.includes(technicalName)) {
|
while (existingTechnicalNames.includes(technicalName)) {
|
||||||
technicalName = [baseTechnicalName, suffix++].join('-');
|
technicalName = [baseTechnicalName, suffix++].join('_');
|
||||||
}
|
}
|
||||||
return technicalName;
|
return technicalName;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,11 +35,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button (click)="save()" [disabled]="form.invalid || !changed" color="primary" mat-flat-button>
|
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
|
||||||
{{ 'add-edit-dossier-attribute.save' | translate }}
|
{{ 'add-edit-dossier-attribute.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Component, HostListener, Inject, OnDestroy } from '@angular/core';
|
import { Component, HostListener, Inject, Injector, OnDestroy } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { DossierAttributeConfigTypes, FileAttributeConfigTypes, IDossierAttributeConfig } from '@red/domain';
|
import { DossierAttributeConfigTypes, FileAttributeConfigTypes, IDossierAttributeConfig } from '@red/domain';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { AutoUnsubscribe, IqserEventTarget, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseDialogComponent, IqserEventTarget, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
|
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
|
||||||
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
|
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
|
||||||
@ -12,9 +12,8 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
templateUrl: './add-edit-dossier-attribute-dialog.component.html',
|
templateUrl: './add-edit-dossier-attribute-dialog.component.html',
|
||||||
styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss'],
|
styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss'],
|
||||||
})
|
})
|
||||||
export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe implements OnDestroy {
|
export class AddEditDossierAttributeDialogComponent extends BaseDialogComponent implements OnDestroy {
|
||||||
dossierAttribute: IDossierAttributeConfig = this.data.dossierAttribute;
|
dossierAttribute: IDossierAttributeConfig = this.data.dossierAttribute;
|
||||||
readonly form: FormGroup = this._getForm(this.dossierAttribute);
|
|
||||||
readonly translations = dossierAttributeTypesTranslations;
|
readonly translations = dossierAttributeTypesTranslations;
|
||||||
readonly typeOptions = Object.keys(DossierAttributeConfigTypes);
|
readonly typeOptions = Object.keys(DossierAttributeConfigTypes);
|
||||||
|
|
||||||
@ -23,11 +22,14 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
|
|||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _dossierAttributesService: DossierAttributesService,
|
private readonly _dossierAttributesService: DossierAttributesService,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
readonly dialogRef: MatDialogRef<AddEditDossierAttributeDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditDossierAttributeDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA)
|
@Inject(MAT_DIALOG_DATA)
|
||||||
readonly data: { readonly dossierAttribute: IDossierAttributeConfig },
|
readonly data: { readonly dossierAttribute: IDossierAttributeConfig },
|
||||||
) {
|
) {
|
||||||
super();
|
super(_injector, _dialogRef);
|
||||||
|
this.form = this._getForm(this.dossierAttribute);
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
get changed(): boolean {
|
get changed(): boolean {
|
||||||
@ -55,7 +57,7 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
|
|||||||
|
|
||||||
this._dossierAttributesService.createOrUpdate(attribute).subscribe(
|
this._dossierAttributesService.createOrUpdate(attribute).subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.dialogRef.close(true);
|
this._dialogRef.close(true);
|
||||||
},
|
},
|
||||||
(error: HttpErrorResponse) => {
|
(error: HttpErrorResponse) => {
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
|
|||||||
@ -33,16 +33,11 @@
|
|||||||
|
|
||||||
<div class="validity">
|
<div class="validity">
|
||||||
<div>
|
<div>
|
||||||
<mat-checkbox
|
<mat-checkbox (change)="toggleHasValid('from')" [checked]="hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||||
(change)="hasValidFrom = !hasValidFrom"
|
|
||||||
[checked]="hasValidFrom"
|
|
||||||
class="filter-menu-checkbox"
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{{ 'add-edit-dossier-template.form.valid-from' | translate }}
|
{{ 'add-edit-dossier-template.form.valid-from' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
|
||||||
<mat-checkbox (change)="hasValidTo = !hasValidTo" [checked]="hasValidTo" class="filter-menu-checkbox" color="primary">
|
<mat-checkbox (change)="toggleHasValid('to')" [checked]="hasValidTo" class="filter-menu-checkbox" color="primary">
|
||||||
{{ 'add-edit-dossier-template.form.valid-to' | translate }}
|
{{ 'add-edit-dossier-template.form.valid-to' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -87,11 +82,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button (click)="save()" [disabled]="!valid || !changed" color="primary" mat-flat-button>
|
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
|
||||||
{{ 'add-edit-dossier-template.save' | translate }}
|
{{ 'add-edit-dossier-template.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject, Injector } from '@angular/core';
|
||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Moment } from 'moment';
|
import { Moment } from 'moment';
|
||||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||||
import { downloadTypesTranslations } from '../../../../translations/download-types-translations';
|
import { downloadTypesTranslations } from '../../../../translations/download-types-translations';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { BaseDialogComponent, Toaster } from '@iqser/common-ui';
|
import { BaseDialogComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { DownloadFileType, IDossierTemplate } from '@red/domain';
|
import { DownloadFileType, IDossierTemplate } from '@red/domain';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './add-edit-dossier-template-dialog.component.html',
|
templateUrl: './add-edit-dossier-template-dialog.component.html',
|
||||||
styleUrls: ['./add-edit-dossier-template-dialog.component.scss'],
|
styleUrls: ['./add-edit-dossier-template-dialog.component.scss'],
|
||||||
})
|
})
|
||||||
export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
hasValidFrom: boolean;
|
hasValidFrom: boolean;
|
||||||
hasValidTo: boolean;
|
hasValidTo: boolean;
|
||||||
downloadTypesEnum: DownloadFileType[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
downloadTypesEnum: DownloadFileType[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||||
@ -25,79 +25,64 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
|||||||
key: type,
|
key: type,
|
||||||
label: downloadTypesTranslations[type],
|
label: downloadTypesTranslations[type],
|
||||||
}));
|
}));
|
||||||
readonly disabled = false;
|
|
||||||
private _previousValidFrom: Moment;
|
private _previousValidFrom: Moment;
|
||||||
private _previousValidTo: Moment;
|
private _previousValidTo: Moment;
|
||||||
|
private _lastValidFrom: Moment;
|
||||||
|
private _lastValidTo: Moment;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _appStateService: AppStateService,
|
private readonly _appStateService: AppStateService,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
public dialogRef: MatDialogRef<AddEditDossierTemplateDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditDossierTemplateDialogComponent>,
|
||||||
|
private readonly _loadingService: LoadingService,
|
||||||
@Inject(MAT_DIALOG_DATA) readonly dossierTemplate: IDossierTemplate,
|
@Inject(MAT_DIALOG_DATA) readonly dossierTemplate: IDossierTemplate,
|
||||||
) {
|
) {
|
||||||
super();
|
super(_injector, _dialogRef);
|
||||||
|
this.form = this._getForm();
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
|
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
|
||||||
this.hasValidTo = !!this.dossierTemplate?.validTo;
|
this.hasValidTo = !!this.dossierTemplate?.validTo;
|
||||||
|
|
||||||
this._previousValidFrom = this.form.get('validFrom').value;
|
this._previousValidFrom = this._lastValidFrom = this.form.get('validFrom').value;
|
||||||
this._previousValidTo = this.form.get('validTo').value;
|
this._previousValidTo = this._lastValidTo = this.form.get('validTo').value;
|
||||||
|
|
||||||
this.form.valueChanges.subscribe(value => {
|
this.addSubscription = this.form.valueChanges.subscribe(value => {
|
||||||
this._applyValidityIntervalConstraints(value);
|
this._applyValidityIntervalConstraints(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.addSubscription = this.form.controls['validFrom'].valueChanges.subscribe(value => {
|
||||||
|
this._lastValidFrom = value ? value : this._lastValidFrom;
|
||||||
|
});
|
||||||
|
this.addSubscription = this.form.controls['validTo'].valueChanges.subscribe(value => {
|
||||||
|
this._lastValidFrom = value ? value : this._lastValidFrom;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get valid(): boolean {
|
toggleHasValid(extremity: string) {
|
||||||
return this.form.valid;
|
if (extremity === 'from') {
|
||||||
}
|
this.hasValidFrom = !this.hasValidFrom;
|
||||||
|
this.form.controls['validFrom'].setValue(this.hasValidFrom ? this._lastValidFrom : null);
|
||||||
get changed(): boolean {
|
} else {
|
||||||
if (!this.dossierTemplate) {
|
this.hasValidTo = !this.hasValidTo;
|
||||||
return true;
|
this.form.controls['validTo'].setValue(this.hasValidTo ? this._lastValidTo : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of Object.keys(this.form.getRawValue())) {
|
|
||||||
const formValue = this.form.get(key).value;
|
|
||||||
const objectValue = this.dossierTemplate[key];
|
|
||||||
if (key === 'validFrom') {
|
|
||||||
if (this.hasValidFrom !== !!objectValue || (this.hasValidFrom && !moment(objectValue).isSame(moment(formValue)))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (key === 'validTo') {
|
|
||||||
if (this.hasValidTo !== !!objectValue || (this.hasValidTo && !moment(objectValue).isSame(moment(formValue)))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (formValue instanceof Array) {
|
|
||||||
if (objectValue.length !== formValue.length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (const item of objectValue) {
|
|
||||||
if (!formValue.includes(item)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (objectValue !== formValue) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
this._loadingService.start();
|
||||||
try {
|
try {
|
||||||
const dossierTemplate = {
|
const dossierTemplate = {
|
||||||
dossierTemplateId: this.dossierTemplate?.dossierTemplateId,
|
dossierTemplateId: this.dossierTemplate?.dossierTemplateId,
|
||||||
...this.form.getRawValue(),
|
...this.form.getRawValue(),
|
||||||
validFrom: this.hasValidFrom ? this.form.get('validFrom').value : null,
|
validFrom: this.hasValidFrom ? this.form.get('validFrom').value : null,
|
||||||
validTo: this.hasValidTo ? this.form.get('validTo').value : null,
|
validTo: this.hasValidTo ? this.form.get('validTo').value : null,
|
||||||
};
|
} as IDossierTemplate;
|
||||||
await this._dossierTemplatesService.createOrUpdate(dossierTemplate).toPromise();
|
await this._dossierTemplatesService.createOrUpdate(dossierTemplate).toPromise();
|
||||||
await this._dossierTemplatesService.loadAll().toPromise();
|
|
||||||
await this._appStateService.loadDictionaryData();
|
await this._appStateService.loadDictionaryData();
|
||||||
this.dialogRef.close(true);
|
this._dialogRef.close(true);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const message =
|
const message =
|
||||||
error.status === HttpStatusCode.Conflict
|
error.status === HttpStatusCode.Conflict
|
||||||
@ -105,6 +90,7 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
|||||||
: _('add-edit-dossier-template.error.generic');
|
: _('add-edit-dossier-template.error.generic');
|
||||||
this._toaster.error(message, { error });
|
this._toaster.error(message, { error });
|
||||||
}
|
}
|
||||||
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
private _getForm(): FormGroup {
|
||||||
@ -134,7 +120,7 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _requiredIfValidator(predicate) {
|
private _requiredIfValidator(predicate) {
|
||||||
return formControl => {
|
return (formControl: AbstractControl) => {
|
||||||
if (!formControl.parent) {
|
if (!formControl.parent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,11 +84,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button (click)="save()" [disabled]="!valid || !changed" color="primary" mat-flat-button>
|
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
|
||||||
{{ 'add-edit-file-attribute.save' | translate }}
|
{{ 'add-edit-file-attribute.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain';
|
import { FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
|
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
import { BaseDialogComponent } from '../../../../../../../../libs/common-ui/src';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-add-edit-file-attribute-dialog',
|
selector: 'redaction-add-edit-file-attribute-dialog',
|
||||||
@ -13,12 +13,10 @@ import { BaseDialogComponent } from '@iqser/common-ui';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
|
export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
|
||||||
readonly disabled = false;
|
|
||||||
DISPLAYED_FILTERABLE_LIMIT = 3;
|
DISPLAYED_FILTERABLE_LIMIT = 3;
|
||||||
translations = fileAttributeTypesTranslations;
|
translations = fileAttributeTypesTranslations;
|
||||||
fileAttribute: IFileAttributeConfig = this.data.fileAttribute;
|
fileAttribute: IFileAttributeConfig = this.data.fileAttribute;
|
||||||
dossierTemplateId: string = this.data.dossierTemplateId;
|
dossierTemplateId: string = this.data.dossierTemplateId;
|
||||||
readonly form!: FormGroup;
|
|
||||||
readonly typeOptions = Object.keys(FileAttributeConfigTypes);
|
readonly typeOptions = Object.keys(FileAttributeConfigTypes);
|
||||||
readonly canSetDisplayed!: boolean;
|
readonly canSetDisplayed!: boolean;
|
||||||
readonly canSetFilterable!: boolean;
|
readonly canSetFilterable!: boolean;
|
||||||
@ -26,7 +24,8 @@ export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _fileAttributesService: FileAttributesService,
|
private readonly _fileAttributesService: FileAttributesService,
|
||||||
public dialogRef: MatDialogRef<AddEditFileAttributeDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditFileAttributeDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA)
|
@Inject(MAT_DIALOG_DATA)
|
||||||
public data: {
|
public data: {
|
||||||
fileAttribute: IFileAttributeConfig;
|
fileAttribute: IFileAttributeConfig;
|
||||||
@ -35,32 +34,11 @@ export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
|
|||||||
numberOfFilterableAttrs: number;
|
numberOfFilterableAttrs: number;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
super();
|
super(_injector, _dialogRef);
|
||||||
this.canSetDisplayed = data.numberOfDisplayedAttrs < this.DISPLAYED_FILTERABLE_LIMIT || data.fileAttribute?.displayedInFileList;
|
this.canSetDisplayed = data.numberOfDisplayedAttrs < this.DISPLAYED_FILTERABLE_LIMIT || data.fileAttribute?.displayedInFileList;
|
||||||
this.canSetFilterable = data.numberOfFilterableAttrs < this.DISPLAYED_FILTERABLE_LIMIT || data.fileAttribute?.filterable;
|
this.canSetFilterable = data.numberOfFilterableAttrs < this.DISPLAYED_FILTERABLE_LIMIT || data.fileAttribute?.filterable;
|
||||||
this.form = this._getForm(this.fileAttribute);
|
this.form = this._getForm(this.fileAttribute);
|
||||||
}
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
|
||||||
get valid(): boolean {
|
|
||||||
return this.form.valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get changed(): boolean {
|
|
||||||
if (!this.fileAttribute) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(this.form.getRawValue())) {
|
|
||||||
if (key === 'readonly') {
|
|
||||||
if (this.fileAttribute.editable === this.form.get(key).value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (this.fileAttribute[key] !== this.form.get(key).value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
@ -69,7 +47,7 @@ export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
|
|||||||
editable: !this.form.get('readonly').value,
|
editable: !this.form.get('readonly').value,
|
||||||
...this.form.getRawValue(),
|
...this.form.getRawValue(),
|
||||||
};
|
};
|
||||||
this.dialogRef.close(fileAttribute);
|
this._dialogRef.close(fileAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(fileAttribute: IFileAttributeConfig): FormGroup {
|
private _getForm(fileAttribute: IFileAttributeConfig): FormGroup {
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<redaction-user-details
|
<redaction-user-details
|
||||||
(closeDialog)="dialogRef.close($event)"
|
(closeDialog)="closeDialog($event)"
|
||||||
|
(cancel)="close()"
|
||||||
(toggleResetPassword)="toggleResetPassword()"
|
(toggleResetPassword)="toggleResetPassword()"
|
||||||
*ngIf="!resettingPassword"
|
[hidden]="resettingPassword"
|
||||||
[user]="user"
|
[user]="user"
|
||||||
></redaction-user-details>
|
></redaction-user-details>
|
||||||
|
|
||||||
<redaction-reset-password
|
<redaction-reset-password
|
||||||
(close)="dialogRef.close($event)"
|
(close)="closeDialog($event)"
|
||||||
(toggleResetPassword)="toggleResetPassword()"
|
(toggleResetPassword)="toggleResetPassword()"
|
||||||
*ngIf="resettingPassword"
|
[hidden]="!resettingPassword"
|
||||||
[user]="user"
|
[user]="user"
|
||||||
></redaction-reset-password>
|
></redaction-reset-password>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,18 +1,44 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject, Injector, ViewChild } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
|
import { UserDetailsComponent } from './user-details/user-details.component';
|
||||||
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-add-edit-user-dialog',
|
selector: 'redaction-add-edit-user-dialog',
|
||||||
templateUrl: './add-edit-user-dialog.component.html',
|
templateUrl: './add-edit-user-dialog.component.html',
|
||||||
styleUrls: ['./add-edit-user-dialog.component.scss'],
|
styleUrls: ['./add-edit-user-dialog.component.scss'],
|
||||||
})
|
})
|
||||||
export class AddEditUserDialogComponent {
|
export class AddEditUserDialogComponent extends BaseDialogComponent {
|
||||||
|
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
|
||||||
|
|
||||||
resettingPassword = false;
|
resettingPassword = false;
|
||||||
|
|
||||||
constructor(readonly dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: User) {}
|
constructor(
|
||||||
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditUserDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) readonly user: User,
|
||||||
|
) {
|
||||||
|
super(_injector, _dialogRef);
|
||||||
|
}
|
||||||
|
|
||||||
toggleResetPassword() {
|
toggleResetPassword() {
|
||||||
this.resettingPassword = !this.resettingPassword;
|
this.resettingPassword = !this.resettingPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async save(): Promise<void> {
|
||||||
|
await this._userDetailsComponent.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
get changed(): boolean {
|
||||||
|
return this._userDetailsComponent.changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
get valid(): boolean {
|
||||||
|
return this._userDetailsComponent.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDialog(event) {
|
||||||
|
this._dialogRef.close(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { UserService } from '@services/user.service';
|
|||||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-reset-password',
|
selector: 'redaction-reset-password',
|
||||||
@ -25,15 +26,15 @@ export class ResetPasswordComponent {
|
|||||||
async save() {
|
async save() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
try {
|
try {
|
||||||
await this._userService
|
await firstValueFrom(
|
||||||
.resetPassword(
|
this._userService.resetPassword(
|
||||||
{
|
{
|
||||||
password: this.form.get('temporaryPassword').value,
|
password: this.form.get('temporaryPassword').value,
|
||||||
temporary: true,
|
temporary: true,
|
||||||
},
|
},
|
||||||
this.user.id,
|
this.user.id,
|
||||||
)
|
),
|
||||||
.toPromise();
|
);
|
||||||
this.toggleResetPassword.emit();
|
this.toggleResetPassword.emit();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._toaster.error(_('reset-password-dialog.error.password-policy'));
|
this._toaster.error(_('reset-password-dialog.error.password-policy'));
|
||||||
|
|||||||
@ -53,6 +53,6 @@
|
|||||||
icon="iqser:trash"
|
icon="iqser:trash"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
<div class="all-caps-label cancel" mat-dialog-close translate="add-edit-user.actions.cancel"></div>
|
<div class="all-caps-label cancel" translate="add-edit-user.actions.cancel" (click)="cancel.emit()"></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,28 +1,29 @@
|
|||||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { rolesTranslations } from '../../../../../translations/roles-translations';
|
import { rolesTranslations } from '../../../../../translations/roles-translations';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-user-details',
|
selector: 'redaction-user-details',
|
||||||
templateUrl: './user-details.component.html',
|
templateUrl: './user-details.component.html',
|
||||||
styleUrls: ['./user-details.component.scss'],
|
styleUrls: ['./user-details.component.scss'],
|
||||||
})
|
})
|
||||||
export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges, OnDestroy {
|
export class UserDetailsComponent extends BaseFormComponent implements OnChanges, OnDestroy {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|
||||||
@Input() user: User;
|
@Input() user: User;
|
||||||
@Output() readonly toggleResetPassword = new EventEmitter();
|
@Output() readonly toggleResetPassword = new EventEmitter();
|
||||||
@Output() readonly closeDialog = new EventEmitter();
|
@Output() readonly closeDialog = new EventEmitter();
|
||||||
|
@Output() readonly cancel = new EventEmitter();
|
||||||
|
|
||||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||||
readonly translations = rolesTranslations;
|
readonly translations = rolesTranslations;
|
||||||
form: FormGroup;
|
|
||||||
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -35,29 +36,6 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
get changed(): boolean {
|
|
||||||
if (!this.user) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.user.roles.length !== this.activeRoles.length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(this.form.getRawValue())) {
|
|
||||||
const keyValue = this.form.get(key).value;
|
|
||||||
if (key.startsWith('RED_')) {
|
|
||||||
if (this.user.roles.includes(key) !== keyValue) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (this.user[key] !== keyValue) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get activeRoles(): string[] {
|
get activeRoles(): string[] {
|
||||||
return this.ROLES.reduce((acc, role) => {
|
return this.ROLES.reduce((acc, role) => {
|
||||||
if (this.form.get(role).value) {
|
if (this.form.get(role).value) {
|
||||||
@ -85,6 +63,7 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
|||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.form = this._getForm();
|
this.form = this._getForm();
|
||||||
this._setRolesRequirements();
|
this._setRolesRequirements();
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeDisabled(role: string): boolean {
|
shouldBeDisabled(role: string): boolean {
|
||||||
@ -107,9 +86,7 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
|||||||
const userData = { ...this.form.getRawValue(), roles: this.activeRoles };
|
const userData = { ...this.form.getRawValue(), roles: this.activeRoles };
|
||||||
|
|
||||||
if (!this.user) {
|
if (!this.user) {
|
||||||
await this.userService
|
await firstValueFrom(this.userService.create(userData))
|
||||||
.create(userData)
|
|
||||||
.toPromise()
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.closeDialog.emit(true);
|
this.closeDialog.emit(true);
|
||||||
})
|
})
|
||||||
@ -122,7 +99,7 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
|||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.userService.updateProfile(userData, this.user.id).toPromise();
|
await firstValueFrom(this.userService.updateProfile(userData, this.user.id));
|
||||||
this.closeDialog.emit(true);
|
this.closeDialog.emit(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +117,7 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
|||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
value: this.user?.email,
|
value: this.user?.email,
|
||||||
disabled: !!this.user,
|
disabled: !!this.user?.email,
|
||||||
},
|
},
|
||||||
[Validators.required, Validators.email],
|
[Validators.required, Validators.email],
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<div class="dialog-header heading-l">
|
<div class="dialog-header heading-l">
|
||||||
{{
|
{{ 'confirm-delete-file-attribute.title' | translate: translateArgs }}
|
||||||
'confirm-delete-file-attribute.title'
|
|
||||||
| translate
|
|
||||||
: {
|
|
||||||
type: type,
|
|
||||||
name: fileAttribute?.label
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
|
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
|
||||||
@ -20,14 +13,11 @@
|
|||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="heading" translate="confirm-delete-file-attribute.warning"></div>
|
<div class="heading" translate="confirm-delete-file-attribute.warning"></div>
|
||||||
|
|
||||||
<mat-checkbox
|
<ng-container *ngFor="let checkbox of checkboxes; let idx = index">
|
||||||
*ngFor="let checkbox of checkboxes; let idx = index"
|
<mat-checkbox [(ngModel)]="checkbox.value" [class.error]="!checkbox.value && showToast" color="primary">
|
||||||
[(ngModel)]="checkbox.value"
|
{{ checkbox.label }}
|
||||||
[class.error]="!checkbox.value && showToast"
|
</mat-checkbox>
|
||||||
color="primary"
|
</ng-container>
|
||||||
>
|
|
||||||
{{ checkbox.label | translate: { type: type } }}
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
@ -35,7 +25,7 @@
|
|||||||
{{ 'confirm-delete-file-attribute.delete' | translate: { type: type } }}
|
{{ 'confirm-delete-file-attribute.delete' | translate: { type: type } }}
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
(click)="cancel()"
|
(click)="this.dialogRef.close()"
|
||||||
[translateParams]="{ type: type }"
|
[translateParams]="{ type: type }"
|
||||||
[translate]="'confirm-delete-file-attribute.cancel'"
|
[translate]="'confirm-delete-file-attribute.cancel'"
|
||||||
class="all-caps-label cancel"
|
class="all-caps-label cancel"
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { DossierAttributeConfig, FileAttributeConfig } from '@red/domain';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
const isFileAttributeConfig = (value: DossierAttributeConfig | FileAttributeConfig): value is FileAttributeConfig =>
|
||||||
|
value instanceof FileAttributeConfig;
|
||||||
|
|
||||||
|
interface CheckBox {
|
||||||
|
value: boolean;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogData {
|
||||||
|
attribute: FileAttributeConfig | DossierAttributeConfig;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-confirm-delete-attribute-dialog',
|
||||||
|
templateUrl: './confirm-delete-attribute-dialog.component.html',
|
||||||
|
styleUrls: ['./confirm-delete-attribute-dialog.component.scss'],
|
||||||
|
})
|
||||||
|
export class ConfirmDeleteAttributeDialogComponent {
|
||||||
|
checkboxes: CheckBox[];
|
||||||
|
showToast = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _translateService: TranslateService,
|
||||||
|
readonly dialogRef: MatDialogRef<ConfirmDeleteAttributeDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: DialogData,
|
||||||
|
) {
|
||||||
|
this.checkboxes = this.checkBoxConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
get checkBoxConfig(): CheckBox[] {
|
||||||
|
const checkBoxes = isFileAttributeConfig(this.data.attribute) ? this._fileAttributeCheckboxes : this._dossierAttributeCheckboxes;
|
||||||
|
if (this.data.count !== 0) {
|
||||||
|
checkBoxes.push({
|
||||||
|
value: false,
|
||||||
|
label: this._translateService.instant('confirm-delete-file-attribute.impacted-report', { count: this.data.count }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkBoxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
get valid() {
|
||||||
|
return this.checkboxes.reduce((acc, currentValue) => acc && currentValue.value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): 'bulk' | 'single' {
|
||||||
|
return this.data.attribute ? 'single' : 'bulk';
|
||||||
|
}
|
||||||
|
|
||||||
|
get translateArgs() {
|
||||||
|
return {
|
||||||
|
type: this.type,
|
||||||
|
name: this.data.attribute?.label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _fileAttributeCheckboxes(): CheckBox[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
value: false,
|
||||||
|
label: this._translateService.instant('confirm-delete-file-attribute.file-impacted-documents', { type: this.type }),
|
||||||
|
},
|
||||||
|
{ value: false, label: this._translateService.instant('confirm-delete-file-attribute.file-lost-details') },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _dossierAttributeCheckboxes(): CheckBox[] {
|
||||||
|
return [
|
||||||
|
{ value: false, label: this._translateService.instant('confirm-delete-file-attribute.dossier-impacted-documents') },
|
||||||
|
{ value: false, label: this._translateService.instant('confirm-delete-file-attribute.dossier-lost-details') },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteFileAttribute() {
|
||||||
|
if (this.valid) {
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
} else {
|
||||||
|
this.showToast = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,45 +0,0 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
|
||||||
import { IFileAttributeConfig } from '@red/domain';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'redaction-confirm-delete-file-attribute-dialog',
|
|
||||||
templateUrl: './confirm-delete-file-attribute-dialog.component.html',
|
|
||||||
styleUrls: ['./confirm-delete-file-attribute-dialog.component.scss'],
|
|
||||||
})
|
|
||||||
export class ConfirmDeleteFileAttributeDialogComponent {
|
|
||||||
fileAttribute: IFileAttributeConfig;
|
|
||||||
checkboxes = [
|
|
||||||
{ value: false, label: _('confirm-delete-file-attribute.impacted-documents') },
|
|
||||||
{ value: false, label: _('confirm-delete-file-attribute.lost-details') },
|
|
||||||
];
|
|
||||||
showToast = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public dialogRef: MatDialogRef<ConfirmDeleteFileAttributeDialogComponent>,
|
|
||||||
@Inject(MAT_DIALOG_DATA) public data: IFileAttributeConfig,
|
|
||||||
) {
|
|
||||||
this.fileAttribute = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
get valid() {
|
|
||||||
return this.checkboxes[0].value && this.checkboxes[1].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get type(): 'bulk' | 'single' {
|
|
||||||
return this.fileAttribute ? 'single' : 'bulk';
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteFileAttribute() {
|
|
||||||
if (this.valid) {
|
|
||||||
this.dialogRef.close(true);
|
|
||||||
} else {
|
|
||||||
this.showToast = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.dialogRef.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,6 +4,7 @@ import { List, LoadingService } from '@iqser/common-ui';
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-confirm-delete-users-dialog',
|
selector: 'redaction-confirm-delete-users-dialog',
|
||||||
@ -40,7 +41,7 @@ export class ConfirmDeleteUsersDialogComponent {
|
|||||||
async deleteUser() {
|
async deleteUser() {
|
||||||
if (this.valid) {
|
if (this.valid) {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._userService.delete(this.userIds).toPromise();
|
await firstValueFrom(this._userService.delete(this.userIds));
|
||||||
this.dialogRef.close(true);
|
this.dialogRef.close(true);
|
||||||
} else {
|
} else {
|
||||||
this.showToast = true;
|
this.showToast = true;
|
||||||
|
|||||||
@ -28,11 +28,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button (click)="save()" [disabled]="!valid || !changed" color="primary" mat-flat-button>
|
<button (click)="save()" [disabled]="disabled" color="primary" mat-flat-button type="button">
|
||||||
{{ 'edit-color-dialog.save' | translate }}
|
{{ 'edit-color-dialog.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject, Injector } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { DefaultColorType, IColors } from '@red/domain';
|
import { DefaultColorType, IColors } from '@red/domain';
|
||||||
import { BaseDialogComponent, Toaster } from '@iqser/common-ui';
|
import { BaseDialogComponent, Toaster } from '@iqser/common-ui';
|
||||||
@ -7,6 +7,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
|
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
interface IEditColorData {
|
interface IEditColorData {
|
||||||
colors: IColors;
|
colors: IColors;
|
||||||
@ -19,10 +20,7 @@ interface IEditColorData {
|
|||||||
styleUrls: ['./edit-color-dialog.component.scss'],
|
styleUrls: ['./edit-color-dialog.component.scss'],
|
||||||
})
|
})
|
||||||
export class EditColorDialogComponent extends BaseDialogComponent {
|
export class EditColorDialogComponent extends BaseDialogComponent {
|
||||||
readonly form: FormGroup;
|
|
||||||
translations = defaultColorsTranslations;
|
translations = defaultColorsTranslations;
|
||||||
readonly disabled = false;
|
|
||||||
private readonly _initialColor: string;
|
|
||||||
private readonly _dossierTemplateId: string;
|
private readonly _dossierTemplateId: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -30,23 +28,16 @@ export class EditColorDialogComponent extends BaseDialogComponent {
|
|||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _dialogRef: MatDialogRef<EditColorDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<EditColorDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA)
|
@Inject(MAT_DIALOG_DATA)
|
||||||
readonly data: IEditColorData,
|
readonly data: IEditColorData,
|
||||||
) {
|
) {
|
||||||
super();
|
super(_injector, _dialogRef);
|
||||||
this._dossierTemplateId = data.dossierTemplateId;
|
this._dossierTemplateId = data.dossierTemplateId;
|
||||||
this._initialColor = data.colors[data.colorKey];
|
|
||||||
|
|
||||||
this.form = this._getForm();
|
this.form = this._getForm();
|
||||||
}
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
|
||||||
get changed(): boolean {
|
|
||||||
return this.form.get('color').value !== this._initialColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
get valid(): boolean {
|
|
||||||
return this.form.valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
@ -56,7 +47,7 @@ export class EditColorDialogComponent extends BaseDialogComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._dictionaryService.setColors(colors, this._dossierTemplateId).toPromise();
|
await firstValueFrom(this._dictionaryService.setColors(colors, this._dossierTemplateId));
|
||||||
this._dialogRef.close(true);
|
this._dialogRef.close(true);
|
||||||
const color = this._translateService.instant(defaultColorsTranslations[this.data.colorKey]);
|
const color = this._translateService.instant(defaultColorsTranslations[this.data.colorKey]);
|
||||||
this._toaster.info(_('edit-color-dialog.success'), { params: { color: color } });
|
this._toaster.info(_('edit-color-dialog.success'), { params: { color: color } });
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/c
|
|||||||
import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import * as Papa from 'papaparse';
|
import * as Papa from 'papaparse';
|
||||||
import { Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
import { map, startWith } from 'rxjs/operators';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
import { DefaultListingServices, ListingComponent, TableColumnConfig, Toaster, trackBy } from '@iqser/common-ui';
|
import { DefaultListingServices, ListingComponent, TableColumnConfig, Toaster, trackByFactory } from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { FileAttributeConfig, FileAttributeConfigTypes, IField, IFileAttributesConfig } from '@red/domain';
|
import { FileAttributeConfig, FileAttributeConfigTypes, IField, IFileAttributesConfig } from '@red/domain';
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
@ -34,7 +34,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
|
|||||||
columnSample = [];
|
columnSample = [];
|
||||||
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
||||||
readonly tableHeaderLabel = '';
|
readonly tableHeaderLabel = '';
|
||||||
readonly trackBy = trackBy();
|
readonly trackBy = trackByFactory();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
@ -56,14 +56,6 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
|
|
||||||
delimiter: [undefined, Validators.required],
|
|
||||||
encoding: ['UTF-8', Validators.required],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
readFile() {
|
readFile() {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.addEventListener('load', event => {
|
reader.addEventListener('load', event => {
|
||||||
@ -183,7 +175,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._fileAttributesService.setFileAttributeConfig(fileAttributes, this.data.dossierTemplateId).toPromise();
|
await firstValueFrom(this._fileAttributesService.setFileAttributeConfig(fileAttributes, this.data.dossierTemplateId));
|
||||||
this._toaster.success(_('file-attributes-csv-import.save.success'), { params: { count: this.activeFields.length } });
|
this._toaster.success(_('file-attributes-csv-import.save.success'), { params: { count: this.activeFields.length } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._toaster.error(_('file-attributes-csv-import.save.error'));
|
this._toaster.error(_('file-attributes-csv-import.save.error'));
|
||||||
@ -207,6 +199,14 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getForm(): FormGroup {
|
||||||
|
return this._formBuilder.group({
|
||||||
|
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
|
||||||
|
delimiter: [undefined, Validators.required],
|
||||||
|
encoding: ['UTF-8', Validators.required],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _autocompleteStringValidator(): ValidatorFn {
|
private _autocompleteStringValidator(): ValidatorFn {
|
||||||
return (control: AbstractControl): { [key: string]: any } | null => {
|
return (control: AbstractControl): { [key: string]: any } | null => {
|
||||||
if ((this.parseResult?.meta?.fields || []).indexOf(control.value) !== -1) {
|
if ((this.parseResult?.meta?.fields || []).indexOf(control.value) !== -1) {
|
||||||
|
|||||||
@ -27,5 +27,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,23 +1,27 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject, Injector } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { ISmtpConfiguration } from '@red/domain';
|
import { ISmtpConfiguration } from '@red/domain';
|
||||||
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-smtp-auth-dialog',
|
selector: 'redaction-smtp-auth-dialog',
|
||||||
templateUrl: './smtp-auth-dialog.component.html',
|
templateUrl: './smtp-auth-dialog.component.html',
|
||||||
styleUrls: ['./smtp-auth-dialog.component.scss'],
|
styleUrls: ['./smtp-auth-dialog.component.scss'],
|
||||||
})
|
})
|
||||||
export class SmtpAuthDialogComponent {
|
export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
public dialogRef: MatDialogRef<SmtpAuthDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<SmtpAuthDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: ISmtpConfiguration,
|
@Inject(MAT_DIALOG_DATA) public data: ISmtpConfiguration,
|
||||||
) {}
|
) {
|
||||||
|
super(_injector, _dialogRef);
|
||||||
|
this.form = this._getForm();
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
private _getForm(): FormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
@ -27,6 +31,6 @@ export class SmtpAuthDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.dialogRef.close(this.form.getRawValue());
|
this._dialogRef.close(this.form.getRawValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,7 +60,6 @@
|
|||||||
*ngIf="form.get('userId').value !== ALL_USERS"
|
*ngIf="form.get('userId').value !== ALL_USERS"
|
||||||
[user]="form.get('userId').value"
|
[user]="form.get('userId').value"
|
||||||
[withName]="true"
|
[withName]="true"
|
||||||
size="small"
|
|
||||||
></redaction-initials-avatar>
|
></redaction-initials-avatar>
|
||||||
<div *ngIf="form.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
|
<div *ngIf="form.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||||
</mat-select-trigger>
|
</mat-select-trigger>
|
||||||
@ -69,7 +68,6 @@
|
|||||||
*ngIf="userId !== ALL_USERS"
|
*ngIf="userId !== ALL_USERS"
|
||||||
[user]="userId"
|
[user]="userId"
|
||||||
[withName]="true"
|
[withName]="true"
|
||||||
size="small"
|
|
||||||
></redaction-initials-avatar>
|
></redaction-initials-avatar>
|
||||||
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
@ -109,7 +107,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user-column cell">
|
<div class="user-column cell">
|
||||||
<redaction-initials-avatar [user]="log.userId" [withName]="true" size="small"></redaction-initials-avatar>
|
<redaction-initials-avatar [user]="log.userId" [withName]="true"></redaction-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [translate]="translations[log.category]" class="cell"></div>
|
<div [translate]="translations[log.category]" class="cell"></div>
|
||||||
|
|||||||
@ -17,11 +17,3 @@ form {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-0 {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mr-20 {
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest } from '@red/domain';
|
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest } from '@red/domain';
|
||||||
import { AuditService } from '../../services/audit.service';
|
import { AuditService } from '../../services/audit.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
|
|
||||||
@ -52,15 +53,6 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnD
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
category: [this.ALL_CATEGORIES],
|
|
||||||
userId: [this.ALL_USERS],
|
|
||||||
from: [],
|
|
||||||
to: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get totalPages(): number {
|
get totalPages(): number {
|
||||||
if (!this.logs) {
|
if (!this.logs) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -76,6 +68,15 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnD
|
|||||||
await this._fetchData();
|
await this._fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getForm(): FormGroup {
|
||||||
|
return this._formBuilder.group({
|
||||||
|
category: [this.ALL_CATEGORIES],
|
||||||
|
userId: [this.ALL_USERS],
|
||||||
|
from: [],
|
||||||
|
to: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _updateDateFilters(value): boolean {
|
private _updateDateFilters(value): boolean {
|
||||||
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
|
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
|
||||||
return true;
|
return true;
|
||||||
@ -105,8 +106,8 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnD
|
|||||||
to,
|
to,
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(this._auditService.getCategories().toPromise());
|
promises.push(firstValueFrom(this._auditService.getCategories()));
|
||||||
promises.push(this._auditService.searchAuditLog(logsRequestBody).toPromise());
|
promises.push(firstValueFrom(this._auditService.searchAuditLog(logsRequestBody)));
|
||||||
|
|
||||||
const data = await Promise.all(promises);
|
const data = await Promise.all(promises);
|
||||||
this.categories = data[0].map(c => c.category);
|
this.categories = data[0].map(c => c.category);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
<div class="flex-1 actions">
|
<div class="flex-1 actions">
|
||||||
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
interface ListItem extends IListable {
|
interface ListItem extends IListable {
|
||||||
readonly key: string;
|
readonly key: string;
|
||||||
@ -72,7 +73,7 @@ export class DefaultColorsScreenComponent extends ListingComponent<ListItem> imp
|
|||||||
|
|
||||||
private async _loadColors() {
|
private async _loadColors() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const data = await this._appStateService.loadColors(this._dossierTemplatesService.activeDossierTemplateId).toPromise();
|
const data = await firstValueFrom(this._appStateService.loadColors(this._dossierTemplatesService.activeDossierTemplateId));
|
||||||
this._colorsObj = data;
|
this._colorsObj = data;
|
||||||
const entities = Object.keys(data)
|
const entities = Object.keys(data)
|
||||||
.map(key => ({
|
.map(key => ({
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
<div class="flex-1 actions">
|
<div class="flex-1 actions">
|
||||||
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
||||||
@ -78,51 +78,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #tableItemTemplate let-entity="entity">
|
<ng-container *ngIf="templateStats$ | async as templateStats">
|
||||||
<div *ngIf="cast(entity) as dict">
|
<ng-template #tableItemTemplate let-entity="entity">
|
||||||
<div class="cell">
|
<div *ngIf="cast(entity) as dict">
|
||||||
<div [ngStyle]="{ 'background-color': dict.hexColor }" class="color-square"></div>
|
<div class="cell">
|
||||||
<div class="dict-name">
|
<div [ngStyle]="{ 'background-color': dict.hexColor }" class="color-square"></div>
|
||||||
<div class="table-item-title heading">
|
<div class="dict-name">
|
||||||
{{ dict.label }}
|
<div class="table-item-title heading">
|
||||||
</div>
|
{{ dict.label }}
|
||||||
<div class="small-label stats-subtitle">
|
|
||||||
<div>
|
|
||||||
<mat-icon svgIcon="red:entries"></mat-icon>
|
|
||||||
{{ dict.entries?.length }}
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!dict.caseInsensitive">
|
<div class="small-label stats-subtitle">
|
||||||
<mat-icon svgIcon="red:case-sensitive"></mat-icon>
|
<div>
|
||||||
{{ 'dictionary-listing.case-sensitive' | translate }}
|
<mat-icon svgIcon="red:entries"></mat-icon>
|
||||||
|
{{ templateStats.dictionarySummary(dict.type)?.entriesCount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!dict.caseInsensitive">
|
||||||
|
<mat-icon svgIcon="red:case-sensitive"></mat-icon>
|
||||||
|
{{ 'dictionary-listing.case-sensitive' | translate }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell center small-label">
|
<div class="cell center small-label">
|
||||||
{{ dict.rank }}
|
{{ dict.rank }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell center">
|
<div class="cell center">
|
||||||
<redaction-annotation-icon [dictionary]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
|
<redaction-annotation-icon [dictionary]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<div *ngIf="currentUser.isAdmin" class="action-buttons">
|
<div *ngIf="currentUser.isAdmin" class="action-buttons">
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="openDeleteDictionariesDialog($event, [dict])"
|
(action)="openDeleteDictionariesDialog($event, [dict])"
|
||||||
[tooltip]="'dictionary-listing.action.delete' | translate"
|
[tooltip]="'dictionary-listing.action.delete' | translate"
|
||||||
[type]="circleButtonTypes.dark"
|
[type]="circleButtonTypes.dark"
|
||||||
icon="iqser:trash"
|
icon="iqser:trash"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="openAddEditDictionaryDialog($event, dict)"
|
(action)="openAddEditDictionaryDialog($event, dict)"
|
||||||
[tooltip]="'dictionary-listing.action.edit' | translate"
|
[tooltip]="'dictionary-listing.action.edit' | translate"
|
||||||
[type]="circleButtonTypes.dark"
|
[type]="circleButtonTypes.dark"
|
||||||
icon="iqser:edit"
|
icon="iqser:edit"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-container>
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { Component, forwardRef, Injector, OnInit } from '@angular/core';
|
import { Component, forwardRef, Injector, OnInit } from '@angular/core';
|
||||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { catchError, defaultIfEmpty, tap } from 'rxjs/operators';
|
|
||||||
import { forkJoin, of } from 'rxjs';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
CircleButtonTypes,
|
CircleButtonTypes,
|
||||||
@ -17,14 +15,11 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { Dictionary } from '@red/domain';
|
import { Dictionary, DossierTemplateStats } from '@red/domain';
|
||||||
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
const toChartConfig = (dict: Dictionary): DoughnutChartConfig => ({
|
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
|
||||||
value: dict.entries?.length ?? 0,
|
import { ActivatedRoute } from '@angular/router';
|
||||||
color: dict.hexColor,
|
import { tap } from 'rxjs/operators';
|
||||||
label: dict.label,
|
|
||||||
key: dict.type,
|
|
||||||
});
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './dictionary-listing-screen.component.html',
|
templateUrl: './dictionary-listing-screen.component.html',
|
||||||
@ -42,6 +37,8 @@ export class DictionaryListingScreenComponent extends ListingComponent<Dictionar
|
|||||||
{ label: _('dictionary-listing.table-col-names.hint-redaction'), class: 'flex-center' },
|
{ label: _('dictionary-listing.table-col-names.hint-redaction'), class: 'flex-center' },
|
||||||
];
|
];
|
||||||
chartData: DoughnutChartConfig[] = [];
|
chartData: DoughnutChartConfig[] = [];
|
||||||
|
readonly templateStats$: Observable<DossierTemplateStats>;
|
||||||
|
templateStats: DossierTemplateStats;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _injector: Injector,
|
protected readonly _injector: Injector,
|
||||||
@ -52,27 +49,30 @@ export class DictionaryListingScreenComponent extends ListingComponent<Dictionar
|
|||||||
private readonly _dialogService: AdminDialogService,
|
private readonly _dialogService: AdminDialogService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
|
private readonly _dossierTemplateStatsService: DossierTemplateStatsService,
|
||||||
|
private readonly _route: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
_loadingService.start();
|
_loadingService.start();
|
||||||
|
const dossierTemplateId = _route.snapshot.paramMap.get('dossierTemplateId');
|
||||||
|
this.templateStats = this._dossierTemplateStatsService.get(dossierTemplateId);
|
||||||
|
this.templateStats$ = this._dossierTemplateStatsService.watch$(dossierTemplateId).pipe(tap(stats => (this.templateStats = stats)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
async ngOnInit(): Promise<void> {
|
||||||
this._loadDictionaryData();
|
await this._loadDictionaryData(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
openDeleteDictionariesDialog($event?: MouseEvent, types = this.listingService.selected) {
|
openDeleteDictionariesDialog($event?: MouseEvent, types = this.listingService.selected) {
|
||||||
this._dialogService.openDialog('confirm', $event, null, async () => {
|
this._dialogService.openDialog('confirm', $event, null, async () => {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._dictionaryService
|
await firstValueFrom(
|
||||||
.deleteDictionaries(
|
this._dictionaryService.deleteDictionaries(
|
||||||
types.map(t => t.type),
|
types.map(t => t.type),
|
||||||
this._dossierTemplatesService.activeDossierTemplateId,
|
this._dossierTemplatesService.activeDossierTemplateId,
|
||||||
)
|
),
|
||||||
.toPromise();
|
);
|
||||||
await this._appStateService.loadDictionaryData();
|
await this._loadDictionaryData();
|
||||||
this._loadDictionaryData(false);
|
|
||||||
this._calculateData();
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -87,50 +87,31 @@ export class DictionaryListingScreenComponent extends ListingComponent<Dictionar
|
|||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._appStateService.loadDictionaryData();
|
await this._loadDictionaryData();
|
||||||
this._loadDictionaryData(false);
|
|
||||||
this._calculateData();
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadDictionaryData(loadEntries = true): void {
|
private async _loadDictionaryData(refresh = true): Promise<void> {
|
||||||
|
if (refresh) {
|
||||||
|
await this._appStateService.loadDictionaryData();
|
||||||
|
}
|
||||||
|
|
||||||
const appStateDictionaryData = this._appStateService.dictionaryData[this._dossierTemplatesService.activeDossierTemplateId];
|
const appStateDictionaryData = this._appStateService.dictionaryData[this._dossierTemplatesService.activeDossierTemplateId];
|
||||||
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);
|
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);
|
||||||
|
this.entitiesService.setEntities(entities);
|
||||||
|
|
||||||
if (!loadEntries) {
|
this._calculateData();
|
||||||
this.entitiesService.setEntities(
|
|
||||||
entities.map(dict => {
|
|
||||||
dict.entries = this.allEntities.find(d => d.type === dict.type)?.entries || [];
|
|
||||||
return dict;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.entitiesService.setEntities(entities);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loadEntries) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataObs = this.allEntities.map(dict =>
|
|
||||||
this._dictionaryService.getForType(this._dossierTemplatesService.activeDossierTemplateId, dict.type).pipe(
|
|
||||||
tap(values => (dict.entries = [...values.entries] ?? [])),
|
|
||||||
catchError(() => {
|
|
||||||
dict.entries = [];
|
|
||||||
return of({});
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
forkJoin(dataObs)
|
|
||||||
.pipe(defaultIfEmpty(null))
|
|
||||||
.subscribe(() => this._calculateData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _calculateData(): void {
|
private _calculateData(): void {
|
||||||
this.chartData = this.allEntities.map(dict => toChartConfig(dict));
|
this.chartData = this.allEntities.map(dict => ({
|
||||||
|
value: this.templateStats.dictionarySummary(dict.type).entriesCount ?? 0,
|
||||||
|
color: dict.hexColor,
|
||||||
|
label: dict.label,
|
||||||
|
key: dict.type,
|
||||||
|
}));
|
||||||
this.chartData.sort((a, b) => (a.label < b.label ? -1 : 1));
|
this.chartData.sort((a, b) => (a.label < b.label ? -1 : 1));
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-admin-breadcrumbs></redaction-admin-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
@ -55,8 +55,9 @@
|
|||||||
|
|
||||||
<redaction-dictionary-manager
|
<redaction-dictionary-manager
|
||||||
#dictionaryManager
|
#dictionaryManager
|
||||||
(saveDictionary)="saveEntries($event)"
|
(saveDictionary)="save()"
|
||||||
[canEdit]="currentUser.isAdmin"
|
[canEdit]="currentUser.isAdmin"
|
||||||
|
[isLeavingPage]="isLeavingPage"
|
||||||
[filterByDossierTemplate]="true"
|
[filterByDossierTemplate]="true"
|
||||||
[initialEntries]="initialEntries"
|
[initialEntries]="initialEntries"
|
||||||
></redaction-dictionary-manager>
|
></redaction-dictionary-manager>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { AppStateService } from '@state/app-state.service';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||||
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
||||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||||
@ -11,17 +10,19 @@ import { CircleButtonTypes, LoadingService } from '@iqser/common-ui';
|
|||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { Dictionary } from '@red/domain';
|
import { Dictionary } from '@red/domain';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './dictionary-overview-screen.component.html',
|
templateUrl: './dictionary-overview-screen.component.html',
|
||||||
styleUrls: ['./dictionary-overview-screen.component.scss'],
|
styleUrls: ['./dictionary-overview-screen.component.scss'],
|
||||||
})
|
})
|
||||||
export class DictionaryOverviewScreenComponent extends ComponentHasChanges implements OnInit, OnDestroy {
|
export class DictionaryOverviewScreenComponent implements OnInit, OnDestroy {
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
|
|
||||||
initialEntries: string[] = [];
|
initialEntries: string[] = [];
|
||||||
dictionary: Dictionary;
|
dictionary: Dictionary;
|
||||||
|
isLeavingPage = false;
|
||||||
|
|
||||||
@ViewChild('dictionaryManager', { static: false })
|
@ViewChild('dictionaryManager', { static: false })
|
||||||
private readonly _dictionaryManager: DictionaryManagerComponent;
|
private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||||
@ -37,11 +38,9 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
private readonly _dialogService: AdminDialogService,
|
private readonly _dialogService: AdminDialogService,
|
||||||
protected readonly _translateService: TranslateService,
|
protected readonly _translateService: TranslateService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
) {
|
) {}
|
||||||
super(_translateService);
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasChanges() {
|
get changed() {
|
||||||
return this._dictionaryManager.editor.hasChanges;
|
return this._dictionaryManager.editor.hasChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
$event?.stopPropagation();
|
$event?.stopPropagation();
|
||||||
|
|
||||||
this._dialogService.openDialog('confirm', $event, null, async () => {
|
this._dialogService.openDialog('confirm', $event, null, async () => {
|
||||||
await this._dictionaryService.deleteDictionaries([this.dictionary.type], this.dictionary.dossierTemplateId).toPromise();
|
await firstValueFrom(this._dictionaryService.deleteDictionaries([this.dictionary.type], this.dictionary.dossierTemplateId));
|
||||||
await this._appStateService.loadDictionaryData();
|
await this._appStateService.loadDictionaryData();
|
||||||
await this._router.navigate([
|
await this._router.navigate([
|
||||||
'/main',
|
'/main',
|
||||||
@ -116,7 +115,9 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveEntries(entries: string[]) {
|
save() {
|
||||||
|
const entries = this._dictionaryManager.editor?.currentEntries;
|
||||||
|
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
this._dictionaryService
|
this._dictionaryService
|
||||||
.saveEntries(entries, this.initialEntries, this.dictionary.dossierTemplateId, this.dictionary.type, null)
|
.saveEntries(entries, this.initialEntries, this.dictionary.dossierTemplateId, this.dictionary.type, null)
|
||||||
@ -149,9 +150,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
|
|
||||||
private async _loadEntries() {
|
private async _loadEntries() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._dictionaryService
|
await firstValueFrom(this._dictionaryService.getForType(this.dictionary.dossierTemplateId, this.dictionary.type))
|
||||||
.getForType(this.dictionary.dossierTemplateId, this.dictionary.type)
|
|
||||||
.toPromise()
|
|
||||||
.then(
|
.then(
|
||||||
data => {
|
data => {
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { RouterHistoryService } from '@services/router-history.service';
|
import { RouterHistoryService } from '@services/router-history.service';
|
||||||
import { DigitalSignatureService } from '../../services/digital-signature.service';
|
import { DigitalSignatureService } from '../../services/digital-signature.service';
|
||||||
import { IDigitalSignature } from '@red/domain';
|
import { IDigitalSignature, IDigitalSignatureRequest } from '@red/domain';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -40,29 +40,30 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveDigitalSignature() {
|
saveDigitalSignature() {
|
||||||
const digitalSignature = {
|
const formValue = this.form.getRawValue();
|
||||||
...this.form.getRawValue(),
|
const digitalSignature: IDigitalSignature = {
|
||||||
|
...formValue,
|
||||||
};
|
};
|
||||||
//adjusted for chrome auto-complete / password manager
|
//adjusted for chrome auto-complete / password manager
|
||||||
digitalSignature.password = digitalSignature.keySecret;
|
digitalSignature.password = formValue.keySecret;
|
||||||
|
|
||||||
const observable = this.digitalSignatureExists
|
const observable = this.digitalSignatureExists
|
||||||
? this._digitalSignatureService.update(digitalSignature)
|
? this._digitalSignatureService.update(digitalSignature)
|
||||||
: this._digitalSignatureService.save(digitalSignature);
|
: this._digitalSignatureService.save(digitalSignature);
|
||||||
|
|
||||||
this.addSubscription = observable.subscribe(
|
this.addSubscription = observable.subscribe({
|
||||||
() => {
|
next: () => {
|
||||||
this.loadDigitalSignatureAndInitializeForm();
|
this.loadDigitalSignatureAndInitializeForm();
|
||||||
this._toaster.success(_('digital-signature-screen.action.save-success'));
|
this._toaster.success(_('digital-signature-screen.action.save-success'));
|
||||||
},
|
},
|
||||||
error => {
|
error: error => {
|
||||||
if (error.status === HttpStatusCode.BadRequest) {
|
if (error.status === HttpStatusCode.BadRequest) {
|
||||||
this._toaster.error(_('digital-signature-screen.action.certificate-not-valid-error'));
|
this._toaster.error(_('digital-signature-screen.action.certificate-not-valid-error'));
|
||||||
} else {
|
} else {
|
||||||
this._toaster.error(_('digital-signature-screen.action.save-error'));
|
this._toaster.error(_('digital-signature-screen.action.save-error'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDigitalSignature() {
|
removeDigitalSignature() {
|
||||||
@ -85,23 +86,23 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
|
|||||||
this.form.get('certificateName').setValue(file.name);
|
this.form.get('certificateName').setValue(file.name);
|
||||||
input.value = null;
|
input.value = null;
|
||||||
};
|
};
|
||||||
fileReader.readAsDataURL(file);
|
fileReader.readAsDataURL(file as Blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDigitalSignatureAndInitializeForm() {
|
loadDigitalSignatureAndInitializeForm() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
this.addSubscription = this._digitalSignatureService
|
this._digitalSignatureService
|
||||||
.getSignature()
|
.getSignature()
|
||||||
.subscribe(
|
.subscribe({
|
||||||
digitalSignature => {
|
next: digitalSignature => {
|
||||||
this.digitalSignatureExists = true;
|
this.digitalSignatureExists = true;
|
||||||
this.digitalSignature = digitalSignature;
|
this.digitalSignature = digitalSignature;
|
||||||
},
|
},
|
||||||
() => {
|
error: () => {
|
||||||
this.digitalSignatureExists = false;
|
this.digitalSignatureExists = false;
|
||||||
this.digitalSignature = {};
|
this.digitalSignature = {};
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
.add(() => {
|
.add(() => {
|
||||||
this.form = this._getForm();
|
this.form = this._getForm();
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
<div class="actions flex-1">
|
<div class="actions flex-1">
|
||||||
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { DossierAttributeConfig, IDossierAttributeConfig } from '@red/domain';
|
import { DossierAttributeConfig, IDossierAttributeConfig } from '@red/domain';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { ReportTemplateService } from '../../../../services/report-template.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './dossier-attributes-listing-screen.component.html',
|
templateUrl: './dossier-attributes-listing-screen.component.html',
|
||||||
@ -44,6 +46,7 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
|||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _dossierAttributesService: DossierAttributesService,
|
private readonly _dossierAttributesService: DossierAttributesService,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
|
private readonly _reportTemplateService: ReportTemplateService,
|
||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
}
|
}
|
||||||
@ -52,11 +55,16 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
|||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: IDossierAttributeConfig) {
|
async openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: DossierAttributeConfig) {
|
||||||
this._dialogService.openDialog('confirm', $event, null, async () => {
|
const dossierTemplateId = this._dossierTemplatesService.activeDossierTemplateId;
|
||||||
|
const resp = await firstValueFrom(
|
||||||
|
this._reportTemplateService.getTemplatesByPlaceholder(dossierTemplateId, dossierAttribute.placeholder),
|
||||||
|
);
|
||||||
|
|
||||||
|
this._dialogService.openDialog('deleteAttribute', $event, { attribute: dossierAttribute, count: resp.length }, async () => {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const ids = dossierAttribute ? [dossierAttribute.id] : this.listingService.selected.map(item => item.id);
|
const ids = dossierAttribute ? [dossierAttribute.id] : this.listingService.selected.map(item => item.id);
|
||||||
await this._dossierAttributesService.delete(ids).toPromise();
|
await firstValueFrom(this._dossierAttributesService.delete(ids));
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -71,7 +79,7 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
|||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._dossierAttributesService.loadAll().toPromise();
|
await firstValueFrom(this._dossierAttributesService.loadAll());
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,100 +0,0 @@
|
|||||||
<section class="settings">
|
|
||||||
<div class="overlay-shadow"></div>
|
|
||||||
|
|
||||||
<redaction-admin-side-nav type="settings"></redaction-admin-side-nav>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<iqser-page-header
|
|
||||||
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
|
|
||||||
[pageLabel]="'dossier-templates' | translate"
|
|
||||||
[showCloseButton]="currentUser.isUser"
|
|
||||||
></iqser-page-header>
|
|
||||||
|
|
||||||
<div class="content-inner">
|
|
||||||
<div class="content-container">
|
|
||||||
<iqser-table
|
|
||||||
[bulkActions]="bulkActions"
|
|
||||||
[headerTemplate]="headerTemplate"
|
|
||||||
[itemSize]="80"
|
|
||||||
[noDataText]="'dossier-templates-listing.no-data.title' | translate"
|
|
||||||
[noMatchText]="'dossier-templates-listing.no-match.title' | translate"
|
|
||||||
[selectionEnabled]="true"
|
|
||||||
[tableColumnConfigs]="tableColumnConfigs"
|
|
||||||
noDataIcon="red:template"
|
|
||||||
></iqser-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<ng-template #bulkActions>
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="openBulkDeleteTemplatesDialog($event)"
|
|
||||||
*ngIf="currentUser.isAdmin && (listingService.areSomeSelected$ | async)"
|
|
||||||
[tooltip]="'dossier-templates-listing.bulk.delete' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="iqser:trash"
|
|
||||||
></iqser-circle-button>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template #actionsTemplate let-dossierTemplate="entity">
|
|
||||||
<redaction-dossier-template-actions
|
|
||||||
(loadDossierTemplatesData)="loadDossierTemplateStats()"
|
|
||||||
[dossierTemplateId]="dossierTemplate.dossierTemplateId"
|
|
||||||
class="actions-container"
|
|
||||||
></redaction-dossier-template-actions>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template #headerTemplate>
|
|
||||||
<div class="table-header-actions">
|
|
||||||
<iqser-input-with-action
|
|
||||||
[(value)]="searchService.searchValue"
|
|
||||||
[placeholder]="'dossier-templates-listing.search' | translate"
|
|
||||||
></iqser-input-with-action>
|
|
||||||
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="openAddDossierTemplateDialog()"
|
|
||||||
*ngIf="currentUser.isAdmin && userPreferenceService.areDevFeaturesEnabled"
|
|
||||||
[label]="'dossier-templates-listing.add-new' | translate"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
icon="iqser:plus"
|
|
||||||
></iqser-icon-button>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template #tableItemTemplate let-entity="entity">
|
|
||||||
<div *ngIf="cast(entity) as dossierTemplate">
|
|
||||||
<div class="cell">
|
|
||||||
<div class="table-item-title heading">
|
|
||||||
{{ dossierTemplate.name }}
|
|
||||||
</div>
|
|
||||||
<div class="small-label stats-subtitle">
|
|
||||||
<div>
|
|
||||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
|
||||||
{{ 'dossier-templates-listing.dictionaries' | translate: { length: dossierTemplate.dictionariesCount } }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell user-column">
|
|
||||||
<redaction-initials-avatar
|
|
||||||
[defaultValue]="'unknown' | translate"
|
|
||||||
[user]="dossierTemplate.createdBy || 'system'"
|
|
||||||
[withName]="true"
|
|
||||||
></redaction-initials-avatar>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="small-label">
|
|
||||||
{{ dossierTemplate.dateAdded | date: 'd MMM. yyyy' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="small-label">
|
|
||||||
{{ dossierTemplate.dateModified | date: 'd MMM. yyyy' }}
|
|
||||||
</div>
|
|
||||||
<ng-container *ngTemplateOutlet="actionsTemplate; context: { entity: dossierTemplate }"></ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<iqser-page-header
|
||||||
|
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
|
||||||
|
[pageLabel]="'dossier-templates' | translate"
|
||||||
|
[showCloseButton]="currentUser.isUser"
|
||||||
|
></iqser-page-header>
|
||||||
|
|
||||||
|
<iqser-table
|
||||||
|
[bulkActions]="bulkActions"
|
||||||
|
[headerTemplate]="headerTemplate"
|
||||||
|
[itemSize]="80"
|
||||||
|
[noDataText]="'dossier-templates-listing.no-data.title' | translate"
|
||||||
|
[noMatchText]="'dossier-templates-listing.no-match.title' | translate"
|
||||||
|
[selectionEnabled]="true"
|
||||||
|
[tableColumnConfigs]="tableColumnConfigs"
|
||||||
|
noDataIcon="red:template"
|
||||||
|
></iqser-table>
|
||||||
|
|
||||||
|
<ng-template #bulkActions>
|
||||||
|
<iqser-circle-button
|
||||||
|
(action)="openBulkDeleteTemplatesDialog($event)"
|
||||||
|
*ngIf="currentUser.isAdmin && (listingService.areSomeSelected$ | async)"
|
||||||
|
[tooltip]="'dossier-templates-listing.bulk.delete' | translate"
|
||||||
|
[type]="circleButtonTypes.dark"
|
||||||
|
icon="iqser:trash"
|
||||||
|
></iqser-circle-button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #headerTemplate>
|
||||||
|
<div class="table-header-actions">
|
||||||
|
<iqser-input-with-action
|
||||||
|
[(value)]="searchService.searchValue"
|
||||||
|
[placeholder]="'dossier-templates-listing.search' | translate"
|
||||||
|
></iqser-input-with-action>
|
||||||
|
|
||||||
|
<iqser-icon-button
|
||||||
|
(action)="openAddDossierTemplateDialog()"
|
||||||
|
*ngIf="currentUser.isAdmin && userPreferenceService.areDevFeaturesEnabled"
|
||||||
|
[label]="'dossier-templates-listing.add-new' | translate"
|
||||||
|
[type]="iconButtonTypes.primary"
|
||||||
|
icon="iqser:plus"
|
||||||
|
></iqser-icon-button>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #tableItemTemplate let-entity="entity">
|
||||||
|
<redaction-table-item [dossierTemplate]="entity"></redaction-table-item>
|
||||||
|
</ng-template>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, forwardRef, Injector } from '@angular/core';
|
||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
import { DossierTemplate } from '@red/domain';
|
import { DossierTemplate } from '@red/domain';
|
||||||
import {
|
import {
|
||||||
CircleButtonTypes,
|
CircleButtonTypes,
|
||||||
@ -18,6 +18,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { RouterHistoryService } from '@services/router-history.service';
|
import { RouterHistoryService } from '@services/router-history.service';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './dossier-templates-listing-screen.component.html',
|
templateUrl: './dossier-templates-listing-screen.component.html',
|
||||||
@ -29,7 +30,7 @@ import { HttpStatusCode } from '@angular/common/http';
|
|||||||
{ provide: ListingComponent, useExisting: forwardRef(() => DossierTemplatesListingScreenComponent) },
|
{ provide: ListingComponent, useExisting: forwardRef(() => DossierTemplatesListingScreenComponent) },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class DossierTemplatesListingScreenComponent extends ListingComponent<DossierTemplate> implements OnInit {
|
export class DossierTemplatesListingScreenComponent extends ListingComponent<DossierTemplate> {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
@ -39,6 +40,7 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
|
|||||||
{ label: _('dossier-templates-listing.table-col-names.created-by'), class: 'user-column' },
|
{ label: _('dossier-templates-listing.table-col-names.created-by'), class: 'user-column' },
|
||||||
{ label: _('dossier-templates-listing.table-col-names.created-on'), sortByKey: 'dateAdded' },
|
{ label: _('dossier-templates-listing.table-col-names.created-on'), sortByKey: 'dateAdded' },
|
||||||
{ label: _('dossier-templates-listing.table-col-names.modified-on'), sortByKey: 'dateModified' },
|
{ label: _('dossier-templates-listing.table-col-names.modified-on'), sortByKey: 'dateModified' },
|
||||||
|
{ label: _('dossier-templates-listing.table-col-names.status'), sortByKey: 'dossierTemplateStatus' },
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -55,10 +57,6 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
|
|||||||
super(_injector);
|
super(_injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.loadDossierTemplateStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
openBulkDeleteTemplatesDialog($event?: MouseEvent) {
|
openBulkDeleteTemplatesDialog($event?: MouseEvent) {
|
||||||
return this._dialogService.openDialog('confirm', $event, null, () => {
|
return this._dialogService.openDialog('confirm', $event, null, () => {
|
||||||
this._loadingService.loadWhile(this._deleteTemplates());
|
this._loadingService.loadWhile(this._deleteTemplates());
|
||||||
@ -66,38 +64,17 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
|
|||||||
}
|
}
|
||||||
|
|
||||||
openAddDossierTemplateDialog() {
|
openAddDossierTemplateDialog() {
|
||||||
this._dialogService.openDialog('addEditDossierTemplate', null, null, () => {
|
this._dialogService.openDialog('addEditDossierTemplate', null, null);
|
||||||
this.loadDossierTemplateStats();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadDossierTemplateStats() {
|
|
||||||
this.entitiesService.all.forEach(rs => {
|
|
||||||
const dictionaries = this._appStateService.dictionaryData[rs.dossierTemplateId];
|
|
||||||
if (dictionaries) {
|
|
||||||
rs.dictionariesCount = Object.keys(dictionaries)
|
|
||||||
.map(key => dictionaries[key])
|
|
||||||
.filter(d => !d.virtual || d.type === 'false_positive').length;
|
|
||||||
} else {
|
|
||||||
rs.dictionariesCount = 0;
|
|
||||||
rs.totalDictionaryEntries = 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _deleteTemplates(templateIds = this.listingService.selected.map(d => d.dossierTemplateId)) {
|
private async _deleteTemplates(templateIds = this.listingService.selected.map(d => d.dossierTemplateId)) {
|
||||||
await this._dossierTemplatesService
|
await firstValueFrom(this._dossierTemplatesService.delete(templateIds)).catch(error => {
|
||||||
.delete(templateIds)
|
if (error.status === HttpStatusCode.Conflict) {
|
||||||
.toPromise()
|
this._toaster.error(_('dossier-templates-listing.error.conflict'));
|
||||||
.catch(error => {
|
} else {
|
||||||
if (error.status === HttpStatusCode.Conflict) {
|
this._toaster.error(_('dossier-templates-listing.error.generic'));
|
||||||
this._toaster.error(_('dossier-templates-listing.error.conflict'));
|
}
|
||||||
} else {
|
});
|
||||||
this._toaster.error(_('dossier-templates-listing.error.generic'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await this._dossierTemplatesService.loadAll().toPromise();
|
|
||||||
await this._appStateService.loadDictionaryData();
|
await this._appStateService.loadDictionaryData();
|
||||||
this.loadDossierTemplateStats();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { TableItemComponent } from './table-item/table-item.component';
|
||||||
|
import { DossierTemplatesListingScreenComponent } from './dossier-templates-listing-screen/dossier-templates-listing-screen.component';
|
||||||
|
import { SharedAdminModule } from '../../shared/shared-admin.module';
|
||||||
|
|
||||||
|
const routes = [{ path: '', component: DossierTemplatesListingScreenComponent }];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [TableItemComponent, DossierTemplatesListingScreenComponent],
|
||||||
|
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedAdminModule],
|
||||||
|
})
|
||||||
|
export class DossierTemplatesListingModule {}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
<div class="cell">
|
||||||
|
<div class="table-item-title heading">
|
||||||
|
{{ dossierTemplate.name }}
|
||||||
|
</div>
|
||||||
|
<div class="small-label stats-subtitle">
|
||||||
|
<div *ngIf="stats$ | async as stats">
|
||||||
|
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||||
|
{{ 'dossier-templates-listing.dictionaries' | translate: { length: stats.numberOfDictionaries } }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell user-column">
|
||||||
|
<redaction-initials-avatar
|
||||||
|
[defaultValue]="'unknown' | translate"
|
||||||
|
[user]="dossierTemplate.createdBy || 'system'"
|
||||||
|
[withName]="true"
|
||||||
|
></redaction-initials-avatar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<div class="small-label">
|
||||||
|
{{ dossierTemplate.dateAdded | date: 'd MMM. yyyy' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<div class="small-label">
|
||||||
|
{{ dossierTemplate.dateModified | date: 'd MMM. yyyy' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<div class="small-label">
|
||||||
|
{{ translations[dossierTemplate.dossierTemplateStatus] | translate }}
|
||||||
|
</div>
|
||||||
|
<redaction-dossier-template-actions
|
||||||
|
[dossierTemplateId]="dossierTemplate.dossierTemplateId"
|
||||||
|
class="actions-container"
|
||||||
|
></redaction-dossier-template-actions>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||||
|
import { DossierTemplate, DossierTemplateStats } from '@red/domain';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import { dossierTemplateStatusTranslations } from '../../../translations/dossier-template-status-translations';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-table-item [dossierTemplate]',
|
||||||
|
templateUrl: './table-item.component.html',
|
||||||
|
styleUrls: ['./table-item.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class TableItemComponent implements OnChanges {
|
||||||
|
@Input() dossierTemplate!: DossierTemplate;
|
||||||
|
|
||||||
|
readonly translations = dossierTemplateStatusTranslations;
|
||||||
|
readonly stats$: Observable<DossierTemplateStats>;
|
||||||
|
private readonly _ngOnChanges$ = new BehaviorSubject<string>(undefined);
|
||||||
|
|
||||||
|
constructor(readonly dossierTemplateStatsService: DossierTemplateStatsService) {
|
||||||
|
this.stats$ = this._ngOnChanges$.pipe(switchMap(id => this.dossierTemplateStatsService.watch$(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
if (this.dossierTemplate) {
|
||||||
|
this._ngOnChanges$.next(this.dossierTemplate.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
<div class="actions flex-1">
|
<div class="actions flex-1">
|
||||||
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import { FileAttributeConfig, IFileAttributeConfig, IFileAttributesConfig } from
|
|||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { ReportTemplateService } from '../../../../services/report-template.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './file-attributes-listing-screen.component.html',
|
templateUrl: './file-attributes-listing-screen.component.html',
|
||||||
@ -59,6 +61,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
|||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
private readonly _dialogService: AdminDialogService,
|
private readonly _dialogService: AdminDialogService,
|
||||||
private readonly _fileAttributesService: FileAttributesService,
|
private readonly _fileAttributesService: FileAttributesService,
|
||||||
|
private readonly _reportTemplateService: ReportTemplateService,
|
||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
}
|
}
|
||||||
@ -87,23 +90,23 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openConfigurationsDialog($event: MouseEvent) {
|
async openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||||
this._dialogService.openDialog('fileAttributesConfigurations', $event, this._existingConfiguration);
|
const dossierTemplateId = this._dossierTemplatesService.activeDossierTemplateId;
|
||||||
}
|
const resp = await firstValueFrom(
|
||||||
|
this._reportTemplateService.getTemplatesByPlaceholder(dossierTemplateId, fileAttribute.placeholder),
|
||||||
|
);
|
||||||
|
|
||||||
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: IFileAttributeConfig) {
|
this._dialogService.openDialog('deleteAttribute', $event, { attribute: fileAttribute, count: resp.length }, async () => {
|
||||||
this._dialogService.openDialog('deleteFileAttribute', $event, fileAttribute, async () => {
|
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const dossierTemplateId = this._dossierTemplatesService.activeDossierTemplateId;
|
|
||||||
if (fileAttribute) {
|
if (fileAttribute) {
|
||||||
await this._fileAttributesService.deleteFileAttributes([fileAttribute.id], dossierTemplateId).toPromise();
|
await firstValueFrom(this._fileAttributesService.deleteFileAttributes([fileAttribute.id], dossierTemplateId));
|
||||||
} else {
|
} else {
|
||||||
await this._fileAttributesService
|
await firstValueFrom(
|
||||||
.deleteFileAttributes(
|
this._fileAttributesService.deleteFileAttributes(
|
||||||
this.listingService.selected.map(f => f.id),
|
this.listingService.selected.map(f => f.id),
|
||||||
dossierTemplateId,
|
dossierTemplateId,
|
||||||
)
|
),
|
||||||
.toPromise();
|
);
|
||||||
}
|
}
|
||||||
await this._appStateService.refreshDossierTemplate(dossierTemplateId);
|
await this._appStateService.refreshDossierTemplate(dossierTemplateId);
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
@ -126,18 +129,21 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openConfigurationsDialog($event: MouseEvent) {
|
||||||
|
this._dialogService.openDialog('fileAttributesConfigurations', $event, this._existingConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
private async _createNewFileAttributeAndRefreshView(newValue: IFileAttributeConfig): Promise<void> {
|
private async _createNewFileAttributeAndRefreshView(newValue: IFileAttributeConfig): Promise<void> {
|
||||||
await this._fileAttributesService
|
await firstValueFrom(
|
||||||
.setFileAttributesConfig(newValue, this._dossierTemplatesService.activeDossierTemplateId)
|
this._fileAttributesService.setFileAttributesConfig(newValue, this._dossierTemplatesService.activeDossierTemplateId),
|
||||||
.toPromise()
|
).catch(error => {
|
||||||
.catch(error => {
|
if (error.status === HttpStatusCode.Conflict) {
|
||||||
if (error.status === HttpStatusCode.Conflict) {
|
this._toaster.error(_('file-attributes-listing.error.conflict'));
|
||||||
this._toaster.error(_('file-attributes-listing.error.conflict'));
|
} else {
|
||||||
} else {
|
this._toaster.error(_('file-attributes-listing.error.generic'));
|
||||||
this._toaster.error(_('file-attributes-listing.error.generic'));
|
}
|
||||||
}
|
this._loadingService.stop();
|
||||||
this._loadingService.stop();
|
});
|
||||||
});
|
|
||||||
await this._appStateService.refreshDossierTemplate(this._dossierTemplatesService.activeDossierTemplateId);
|
await this._appStateService.refreshDossierTemplate(this._dossierTemplatesService.activeDossierTemplateId);
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
@ -146,9 +152,9 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
|||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this._fileAttributesService
|
const response = await firstValueFrom(
|
||||||
.getFileAttributesConfig(this._dossierTemplatesService.activeDossierTemplateId)
|
this._fileAttributesService.getFileAttributesConfig(this._dossierTemplatesService.activeDossierTemplateId),
|
||||||
.toPromise();
|
);
|
||||||
this._existingConfiguration = response;
|
this._existingConfiguration = response;
|
||||||
const fileAttributeConfig = response?.fileAttributeConfigs.map(item => new FileAttributeConfig(item)) || [];
|
const fileAttributeConfig = response?.fileAttributeConfigs.map(item => new FileAttributeConfig(item)) || [];
|
||||||
this.entitiesService.setEntities(fileAttributeConfig);
|
this.entitiesService.setEntities(fileAttributeConfig);
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="heading-l" translate="general-config-screen.general.title"></div>
|
<div class="heading-l" translate="general-config-screen.general.title"></div>
|
||||||
<div translate="general-config-screen.general.subtitle"></div>
|
<div translate="general-config-screen.general.subtitle"></div>
|
||||||
</div>
|
</div>
|
||||||
<form (submit)="saveGeneralConfig()" [formGroup]="form">
|
<form (submit)="save()" [formGroup]="form" *ngIf="form">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="dialog-content-left">
|
<div class="dialog-content-left">
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="form.invalid || !generalConfigurationChanged" color="primary" mat-flat-button type="submit">
|
<button [disabled]="form?.invalid || !changed" color="primary" mat-flat-button type="submit">
|
||||||
{{ 'general-config-screen.actions.save' | translate }}
|
{{ 'general-config-screen.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { AutoUnsubscribe, LoadingService } from '@iqser/common-ui';
|
import { BaseFormComponent, LoadingService } from '@iqser/common-ui';
|
||||||
import { GeneralSettingsService } from '@services/general-settings.service';
|
import { GeneralSettingsService } from '@services/general-settings.service';
|
||||||
import { IGeneralConfiguration } from '@red/domain';
|
import { IGeneralConfiguration } from '@red/domain';
|
||||||
import { ConfigService } from '@services/config.service';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-general-config-form',
|
selector: 'redaction-general-config-form',
|
||||||
templateUrl: './general-config-form.component.html',
|
templateUrl: './general-config-form.component.html',
|
||||||
styleUrls: ['./general-config-form.component.scss'],
|
styleUrls: ['./general-config-form.component.scss'],
|
||||||
})
|
})
|
||||||
export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
|
export class GeneralConfigFormComponent extends BaseFormComponent implements OnInit, OnDestroy {
|
||||||
private _initialConfiguration: IGeneralConfiguration;
|
private _initialConfiguration: IGeneralConfiguration;
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
@ -21,28 +21,7 @@ export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnIni
|
|||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
this.form = this._getForm();
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
forgotPasswordFunctionEnabled: [false],
|
|
||||||
auxiliaryName: [undefined],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
|
||||||
await this._loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveGeneralConfig() {
|
|
||||||
this._loadingService.start();
|
|
||||||
|
|
||||||
const configFormValues = this.form.getRawValue();
|
|
||||||
|
|
||||||
await this._generalSettingsService.updateGeneralConfigurations(configFormValues).toPromise();
|
|
||||||
this._initialConfiguration = await this._generalSettingsService.getGeneralConfigurations().toPromise();
|
|
||||||
this._configService.updateDisplayName(this._initialConfiguration.displayName);
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get generalConfigurationChanged(): boolean {
|
get generalConfigurationChanged(): boolean {
|
||||||
@ -59,11 +38,35 @@ export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnIni
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
await this._loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
this._loadingService.start();
|
||||||
|
|
||||||
|
const configFormValues = this.form.getRawValue();
|
||||||
|
|
||||||
|
await firstValueFrom(this._generalSettingsService.updateGeneralConfigurations(configFormValues));
|
||||||
|
this._initialConfiguration = await firstValueFrom(this._generalSettingsService.getGeneralConfigurations());
|
||||||
|
this._configService.updateDisplayName(this._initialConfiguration.displayName);
|
||||||
|
this._loadingService.stop();
|
||||||
|
await this._loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getForm(): FormGroup {
|
||||||
|
return this._formBuilder.group({
|
||||||
|
forgotPasswordFunctionEnabled: [false],
|
||||||
|
auxiliaryName: [undefined],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
try {
|
try {
|
||||||
this._initialConfiguration = await this._generalSettingsService.getGeneralConfigurations().toPromise();
|
this._initialConfiguration = await firstValueFrom(this._generalSettingsService.getGeneralConfigurations());
|
||||||
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
|
|||||||
@ -1,13 +1,52 @@
|
|||||||
import { Component } from '@angular/core';
|
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
|
import { GeneralConfigFormComponent } from './general-config-form/general-config-form.component';
|
||||||
|
import { SmtpFormComponent } from './smtp-form/smtp-form.component';
|
||||||
|
import { BaseFormComponent } from '@iqser/common-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-general-config-screen',
|
selector: 'redaction-general-config-screen',
|
||||||
templateUrl: './general-config-screen.component.html',
|
templateUrl: './general-config-screen.component.html',
|
||||||
styleUrls: ['./general-config-screen.component.scss'],
|
styleUrls: ['./general-config-screen.component.scss'],
|
||||||
})
|
})
|
||||||
export class GeneralConfigScreenComponent {
|
export class GeneralConfigScreenComponent extends BaseFormComponent implements AfterViewInit {
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
|
|
||||||
constructor(private readonly _userService: UserService) {}
|
@ViewChild(GeneralConfigFormComponent) generalConfigFormComponent: GeneralConfigFormComponent;
|
||||||
|
@ViewChild(SmtpFormComponent) smtpFormComponent: SmtpFormComponent;
|
||||||
|
children: BaseFormComponent[];
|
||||||
|
|
||||||
|
constructor(private readonly _userService: UserService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.children = [this.generalConfigFormComponent, this.smtpFormComponent];
|
||||||
|
}
|
||||||
|
|
||||||
|
get changed(): boolean {
|
||||||
|
for (const child of this.children) {
|
||||||
|
if (child.changed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get valid() {
|
||||||
|
for (const child of this.children) {
|
||||||
|
if (!child.valid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(): Promise<void> {
|
||||||
|
for (const child of this.children) {
|
||||||
|
if (child.changed) {
|
||||||
|
await child.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,7 +94,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="form.invalid || !smtpConfigurationChanged" color="primary" mat-flat-button type="submit">
|
<button [disabled]="form.invalid || !changed" color="primary" mat-flat-button type="submit">
|
||||||
{{ 'general-config-screen.actions.save' | translate }}
|
{{ 'general-config-screen.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { ISmtpConfiguration } from '@red/domain';
|
import { ISmtpConfiguration } from '@red/domain';
|
||||||
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
import { SmtpConfigService } from '../../../services/smtp-config.service';
|
import { SmtpConfigService } from '../../../services/smtp-config.service';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-smtp-form',
|
selector: 'redaction-smtp-form',
|
||||||
templateUrl: './smtp-form.component.html',
|
templateUrl: './smtp-form.component.html',
|
||||||
styleUrls: ['./smtp-form.component.scss'],
|
styleUrls: ['./smtp-form.component.scss'],
|
||||||
})
|
})
|
||||||
export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
|
export class SmtpFormComponent extends BaseFormComponent implements OnInit, OnDestroy {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
private _initialConfiguration: ISmtpConfiguration;
|
private _initialConfiguration: ISmtpConfiguration;
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
@ -24,6 +24,7 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
|||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
this.form = this._getForm();
|
||||||
this.addSubscription = this.form.controls.auth.valueChanges.subscribe(auth => {
|
this.addSubscription = this.form.controls.auth.valueChanges.subscribe(auth => {
|
||||||
if (auth) {
|
if (auth) {
|
||||||
this.openAuthConfigDialog();
|
this.openAuthConfigDialog();
|
||||||
@ -35,6 +36,36 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
|||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
||||||
|
this._dialogService.openDialog('smtpAuthConfig', null, this.form.getRawValue(), null, authConfig => {
|
||||||
|
if (authConfig) {
|
||||||
|
this.form.patchValue(authConfig);
|
||||||
|
} else if (!skipDisableOnCancel) {
|
||||||
|
this.form.patchValue({ auth: false }, { emitEvent: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
this._loadingService.start();
|
||||||
|
await firstValueFrom(this._smtpConfigService.updateSMTPConfiguration(this.form.getRawValue()));
|
||||||
|
this._initialConfiguration = this.form.getRawValue();
|
||||||
|
this._loadingService.stop();
|
||||||
|
this._loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async testConnection() {
|
||||||
|
this._loadingService.start();
|
||||||
|
try {
|
||||||
|
await firstValueFrom(this._smtpConfigService.testSMTPConfiguration(this.form.getRawValue()));
|
||||||
|
this._toaster.success(_('general-config-screen.test.success'));
|
||||||
|
} catch (e) {
|
||||||
|
this._toaster.error(_('general-config-screen.test.error'));
|
||||||
|
} finally {
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
private _getForm(): FormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
host: [undefined, Validators.required],
|
host: [undefined, Validators.required],
|
||||||
@ -52,57 +83,15 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
|
||||||
this._dialogService.openDialog('smtpAuthConfig', null, this.form.getRawValue(), null, authConfig => {
|
|
||||||
if (authConfig) {
|
|
||||||
this.form.patchValue(authConfig);
|
|
||||||
} else if (!skipDisableOnCancel) {
|
|
||||||
this.form.patchValue({ auth: false }, { emitEvent: false });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._smtpConfigService.updateSMTPConfiguration(this.form.getRawValue()).toPromise();
|
|
||||||
this._initialConfiguration = this.form.getRawValue();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
get smtpConfigurationChanged(): boolean {
|
|
||||||
if (!this._initialConfiguration) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(this.form.getRawValue())) {
|
|
||||||
if (this._initialConfiguration[key] !== this.form.get(key).value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async testConnection() {
|
|
||||||
this._loadingService.start();
|
|
||||||
try {
|
|
||||||
await this._smtpConfigService.testSMTPConfiguration(this.form.getRawValue()).toPromise();
|
|
||||||
this._toaster.success(_('general-config-screen.test.success'));
|
|
||||||
} catch (e) {
|
|
||||||
this._toaster.error(_('general-config-screen.test.error'));
|
|
||||||
} finally {
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._initialConfiguration = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
|
this._initialConfiguration = await firstValueFrom(this._smtpConfigService.getCurrentSMTPConfiguration());
|
||||||
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { DossierTemplateInfoScreenComponent } from './info-screen/dossier-template-info-screen.component';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
|
||||||
|
const routes = [{ path: '', component: DossierTemplateInfoScreenComponent }];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [DossierTemplateInfoScreenComponent],
|
||||||
|
imports: [RouterModule.forChild(routes), CommonModule, SharedModule],
|
||||||
|
})
|
||||||
|
export class DossierTemplateInfoModule {}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
<div *ngIf="dossierTemplate$ | async as dossierTemplate" class="content-container" iqserHasScrollbar>
|
||||||
|
<ng-container *ngIf="dossierTemplateStats$ | async as stats">
|
||||||
|
<div class="heading-xl">{{ dossierTemplate.name }}</div>
|
||||||
|
|
||||||
|
<div class="all-caps-label mt-24 mb-8" translate="dossier-template-info-screen.created-by"></div>
|
||||||
|
|
||||||
|
<redaction-initials-avatar
|
||||||
|
[user]="dossierTemplate.createdBy || 'system'"
|
||||||
|
[withName]="true"
|
||||||
|
size="large"
|
||||||
|
></redaction-initials-avatar>
|
||||||
|
|
||||||
|
<div class="small-label stats-subtitle">
|
||||||
|
<div>
|
||||||
|
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||||
|
{{ 'dossier-template-info-screen.dictionaries' | translate: { count: stats.numberOfDictionaries } }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="dossierTemplate.validTo && dossierTemplate.validFrom">
|
||||||
|
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||||
|
{{ 'dossier-template-info-screen.valid-from' | translate: { date: dossierTemplate.validFrom | date: 'd MMM. yyyy' } }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||||
|
{{ 'dossier-template-info-screen.created-on' | translate: { date: dossierTemplate.dateAdded | date: 'd MMM. yyyy' } }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<mat-icon svgIcon="red:entries"></mat-icon>
|
||||||
|
{{ 'dossier-template-info-screen.entries' | translate: { count: stats.numberOfEntries } }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="dossierTemplate.validTo && dossierTemplate.validFrom">
|
||||||
|
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||||
|
{{ 'dossier-template-info-screen.valid-to' | translate: { date: dossierTemplate.validTo | date: 'd MMM. yyyy' } }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="dossierTemplate.dateModified">
|
||||||
|
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||||
|
{{ 'dossier-template-info-screen.modified-on' | translate: { date: dossierTemplate.dateModified | date: 'd MMM. yyyy' } }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="heading mt-40" translate="dossier-template-info-screen.description">
|
||||||
|
<iqser-circle-button
|
||||||
|
(action)="openEditDossierTemplateDialog($event, dossierTemplate)"
|
||||||
|
*ngIf="permissionsService.isAdmin()"
|
||||||
|
class="ml-8"
|
||||||
|
icon="iqser:edit"
|
||||||
|
></iqser-circle-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>{{ dossierTemplate.description }}</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
@use 'variables';
|
||||||
|
@use 'common-mixins';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
flex: 1;
|
||||||
|
padding: 30px;
|
||||||
|
overflow: auto;
|
||||||
|
@include common-mixins.scroll-bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-subtitle {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, max-content);
|
||||||
|
grid-row-gap: 8px;
|
||||||
|
grid-column-gap: 40px;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { DossierTemplate, DossierTemplateStats } from '@red/domain';
|
||||||
|
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
|
||||||
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './dossier-template-info-screen.component.html',
|
||||||
|
styleUrls: ['./dossier-template-info-screen.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class DossierTemplateInfoScreenComponent {
|
||||||
|
readonly dossierTemplate$: Observable<DossierTemplate>;
|
||||||
|
readonly dossierTemplateStats$: Observable<DossierTemplateStats>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
|
private readonly _dossierTemplateStatsService: DossierTemplateStatsService,
|
||||||
|
private readonly _dialogService: AdminDialogService,
|
||||||
|
private readonly _route: ActivatedRoute,
|
||||||
|
readonly permissionsService: PermissionsService,
|
||||||
|
) {
|
||||||
|
const dossierTemplateId = _route.snapshot.paramMap.get('dossierTemplateId');
|
||||||
|
this.dossierTemplate$ = this._dossierTemplatesService.getEntityChanged$(dossierTemplateId);
|
||||||
|
this.dossierTemplateStats$ = this._dossierTemplateStatsService.watch$(dossierTemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
openEditDossierTemplateDialog($event: MouseEvent, dossierTemplate: DossierTemplate) {
|
||||||
|
this._dialogService.openDialog('addEditDossierTemplate', $event, dossierTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,13 +43,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="form.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button [disabled]="disabled" color="primary" mat-flat-button type="submit">
|
||||||
{{ 'add-edit-justification.actions.save' | translate }}
|
{{ 'add-edit-justification.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="all-caps-label cancel" mat-dialog-close translate="add-edit-justification.actions.cancel"></div>
|
<div class="all-caps-label cancel" translate="add-edit-justification.actions.cancel" (click)="close()"></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Justification } from '@red/domain';
|
import { Justification } from '@red/domain';
|
||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { LoadingService } from '@iqser/common-ui';
|
import { BaseDialogComponent, LoadingService } from '@iqser/common-ui';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-add-edit-justification-dialog',
|
selector: 'redaction-add-edit-justification-dialog',
|
||||||
@ -12,33 +13,31 @@ import { LoadingService } from '@iqser/common-ui';
|
|||||||
styleUrls: ['./add-edit-justification-dialog.component.scss'],
|
styleUrls: ['./add-edit-justification-dialog.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class AddEditJustificationDialogComponent {
|
export class AddEditJustificationDialogComponent extends BaseDialogComponent {
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _justificationService: JustificationsService,
|
private readonly _justificationService: JustificationsService,
|
||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
public dialogRef: MatDialogRef<AddEditJustificationDialogComponent>,
|
protected readonly _injector: Injector,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditJustificationDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public justification: Justification,
|
@Inject(MAT_DIALOG_DATA) public justification: Justification,
|
||||||
) {}
|
) {
|
||||||
|
super(_injector, _dialogRef);
|
||||||
|
|
||||||
get changed(): boolean {
|
this.form = this._getForm();
|
||||||
return (
|
this.initialFormValue = this.form.getRawValue();
|
||||||
!this.justification ||
|
|
||||||
Object.keys(this.form.getRawValue()).reduce((prev, key) => prev || this.justification[key] !== this.form.get(key).value, false)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
const dossierTemplateId = this._dossierTemplatesService.activeDossierTemplateId;
|
const dossierTemplateId = this._dossierTemplatesService.activeDossierTemplateId;
|
||||||
|
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._justificationService.createOrUpdate(this.form.getRawValue(), dossierTemplateId).toPromise();
|
await firstValueFrom(this._justificationService.createOrUpdate(this.form.getRawValue(), dossierTemplateId));
|
||||||
await this._justificationService.loadAll(dossierTemplateId).toPromise();
|
await firstValueFrom(this._justificationService.loadAll(dossierTemplateId));
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
this.dialogRef.close(true);
|
this._dialogRef.close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): FormGroup {
|
private _getForm(): FormGroup {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user