Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
953c0c65cc | ||
|
|
7ef5fc7bc9 | ||
|
|
a2870531c9 | ||
|
|
b7ff80ecac | ||
|
|
4bf2e79c57 | ||
|
|
4945b6a9f2 | ||
|
|
03804df3b9 | ||
|
|
57d3c008cc | ||
|
|
f00a92a5ea |
@ -1,7 +1,7 @@
|
|||||||
<iqser-side-nav [title]="'account-settings' | translate">
|
<iqser-side-nav [title]="'account-settings' | translate">
|
||||||
<ng-container *ngFor="let item of items">
|
@for (item of items; track item.helpModeKey) {
|
||||||
|
@if (item.show) {
|
||||||
<div
|
<div
|
||||||
*ngIf="item.show"
|
|
||||||
[routerLinkActiveOptions]="{ exact: false }"
|
[routerLinkActiveOptions]="{ exact: false }"
|
||||||
[routerLink]="'../' + item.screen"
|
[routerLink]="'../' + item.screen"
|
||||||
[attr.help-mode-key]="item.helpModeKey"
|
[attr.help-mode-key]="item.helpModeKey"
|
||||||
@ -10,5 +10,6 @@
|
|||||||
>
|
>
|
||||||
{{ item.label | translate }}
|
{{ item.label | translate }}
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
}
|
||||||
</iqser-side-nav>
|
</iqser-side-nav>
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { User } from '@red/domain';
|
|||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { SideNavComponent } from '@common-ui/shared';
|
import { SideNavComponent } from '@common-ui/shared';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
@ -22,7 +21,7 @@ interface NavItem {
|
|||||||
styleUrls: ['./account-side-nav.component.scss'],
|
styleUrls: ['./account-side-nav.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SideNavComponent, TranslateModule, NgForOf, NgIf, RouterLinkActive, RouterLink],
|
imports: [SideNavComponent, TranslateModule, RouterLinkActive, RouterLink],
|
||||||
})
|
})
|
||||||
export class AccountSideNavComponent {
|
export class AccountSideNavComponent {
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
<div class="content-container full-height">
|
<div class="content-container full-height">
|
||||||
<div class="overlay-shadow"></div>
|
<div class="overlay-shadow"></div>
|
||||||
<div [ngClass]="!isWarningsScreen && 'dialog'">
|
<div [ngClass]="!isWarningsScreen && 'dialog'">
|
||||||
<div *ngIf="!isWarningsScreen" class="dialog-header">
|
@if (!isWarningsScreen) {
|
||||||
|
<div class="dialog-header">
|
||||||
<div class="heading-l" [translate]="translations[path]"></div>
|
<div class="heading-l" [translate]="translations[path]"></div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||||
import { Router, RouterOutlet } from '@angular/router';
|
import { Router, RouterOutlet } from '@angular/router';
|
||||||
import { accountTranslations } from '@translations/account-translations';
|
import { accountTranslations } from '@translations/account-translations';
|
||||||
import { NgClass, NgIf } from '@angular/common';
|
import { NgClass } from '@angular/common';
|
||||||
import { AccountSideNavComponent } from '../account-side-nav/account-side-nav.component';
|
import { AccountSideNavComponent } from '../account-side-nav/account-side-nav.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
styleUrls: ['./base-account-screen-component.scss'],
|
styleUrls: ['./base-account-screen-component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgClass, NgIf, RouterOutlet, AccountSideNavComponent, TranslateModule],
|
imports: [NgClass, RouterOutlet, AccountSideNavComponent, TranslateModule],
|
||||||
})
|
})
|
||||||
export class BaseAccountScreenComponent implements OnInit {
|
export class BaseAccountScreenComponent implements OnInit {
|
||||||
readonly translations = accountTranslations;
|
readonly translations = accountTranslations;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<form (submit)="save()" [formGroup]="form">
|
<form (submit)="save()" [formGroup]="form">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div *ngFor="let category of notificationCategories">
|
@for (category of notificationCategories; track category) {
|
||||||
<div class="iqser-input-group header w-full">
|
<div class="iqser-input-group header w-full">
|
||||||
<mat-slide-toggle color="primary" formControlName="{{ category }}Enabled"
|
<mat-slide-toggle color="primary" formControlName="{{ category }}Enabled"
|
||||||
>{{ translations[category] | translate }}
|
>{{ translations[category] | translate }}
|
||||||
@ -8,26 +8,31 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO: This lots of getters-->
|
<!-- TODO: This lots of getters-->
|
||||||
<div *ngIf="isCategoryActive(category)" class="options-content">
|
@if (isCategoryActive(category)) {
|
||||||
|
<div class="options-content">
|
||||||
<div [translate]="'notifications-screen.options-title'" class="statement"></div>
|
<div [translate]="'notifications-screen.options-title'" class="statement"></div>
|
||||||
|
|
||||||
<div *ngFor="let key of notificationGroupsKeys; let i = index" class="group">
|
@for (key of notificationGroupsKeys; track key) {
|
||||||
|
<div class="group">
|
||||||
<div [translate]="translations[key]" class="group-title"></div>
|
<div [translate]="translations[key]" class="group-title"></div>
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<ng-container *ngFor="let preference of getRssFilteredSettings(notificationGroupsValues[i])">
|
@for (preference of getRssFilteredSettings(notificationGroupsValues[$index]); track preference) {
|
||||||
|
@if (!skipPreference(preference)) {
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
(change)="addRemovePreference($event.checked, category, preference)"
|
(change)="addRemovePreference($event.checked, category, preference)"
|
||||||
*ngIf="!skipPreference(preference)"
|
|
||||||
[checked]="isPreferenceChecked(category, preference)"
|
[checked]="isPreferenceChecked(category, preference)"
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{{ translations[preference] | translate }}
|
{{ translations[preference] | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</ng-container>
|
}
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import {
|
|||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
@ -26,7 +25,7 @@ const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_RE
|
|||||||
styleUrls: ['./notifications-screen.component.scss'],
|
styleUrls: ['./notifications-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [ReactiveFormsModule, NgForOf, MatSlideToggle, TranslateModule, NgIf, MatCheckbox, IconButtonComponent],
|
imports: [ReactiveFormsModule, MatSlideToggle, TranslateModule, MatCheckbox, IconButtonComponent],
|
||||||
})
|
})
|
||||||
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly #toaster = inject(Toaster);
|
readonly #toaster = inject(Toaster);
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
}}</mat-option>
|
}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox *ngIf="displayExtraOptionAddRedaction" formControlName="addRedactionApplyToAll">{{
|
<mat-checkbox *ngIf="displayExtraOptionAddRedaction()" formControlName="addRedactionApplyToAll">{{
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
}}</mat-checkbox>
|
}}</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
}}</mat-option>
|
}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox *ngIf="displayExtraOptionRemoveRedaction" formControlName="removeRedactionApplyToAll">{{
|
<mat-checkbox *ngIf="displayExtraOptionRemoveRedaction()" formControlName="removeRedactionApplyToAll">{{
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
}}</mat-checkbox>
|
}}</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
}}</mat-option>
|
}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox *ngIf="displayExtraOptionRemoveRecommendation" formControlName="removeRecommendationApplyToAll">{{
|
<mat-checkbox *ngIf="displayExtraOptionRemoveRecommendation()" formControlName="removeRecommendationApplyToAll">{{
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
}}</mat-checkbox>
|
}}</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<mat-option *ngFor="let option of hintAddOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
<mat-option *ngFor="let option of hintAddOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox *ngIf="displayExtraOptionAddHint" formControlName="addHintApplyToAll">{{
|
<mat-checkbox *ngIf="displayExtraOptionAddHint()" formControlName="addHintApplyToAll">{{
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
}}</mat-checkbox>
|
}}</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<mat-option *ngFor="let option of removeOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
<mat-option *ngFor="let option of removeOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox *ngIf="displayExtraOptionRemoveHint" formControlName="removeHintApplyToAll">{{
|
<mat-checkbox *ngIf="displayExtraOptionRemoveHint()" formControlName="removeHintApplyToAll">{{
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
}}</mat-checkbox>
|
}}</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
import { ChangeDetectorRef, Component, inject } from '@angular/core';
|
import { ChangeDetectorRef, Component, computed, inject } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatOption, MatSelect } from '@angular/material/select';
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
import { BaseFormComponent } from '@common-ui/form';
|
import { BaseFormComponent } from '@common-ui/form';
|
||||||
import { AsControl } from '@common-ui/utils';
|
import { AsControl, formValueToSignal } from '@common-ui/utils';
|
||||||
import { IconButtonComponent } from '@iqser/common-ui';
|
import { IconButtonComponent } from '@iqser/common-ui';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||||
@ -48,7 +48,39 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
readonly #formBuilder = inject(FormBuilder);
|
readonly #formBuilder = inject(FormBuilder);
|
||||||
readonly #userPreferences = inject(UserPreferenceService);
|
readonly #userPreferences = inject(UserPreferenceService);
|
||||||
readonly #changeDetectorRef = inject(ChangeDetectorRef);
|
readonly #changeDetectorRef = inject(ChangeDetectorRef);
|
||||||
form: FormGroup<AsControl<DefaultOptionsForm>> = this.#formBuilder.group({
|
readonly redactionAddOptions = redactionAddOptions;
|
||||||
|
readonly hintAddOptions = hintAddOptions;
|
||||||
|
readonly removeOptions = removeOptions;
|
||||||
|
readonly redactionRemoveOptions = redactionRemoveOptions;
|
||||||
|
readonly recommendationRemoveOptions = recommendationRemoveOptions;
|
||||||
|
|
||||||
|
form: FormGroup<AsControl<DefaultOptionsForm>> = this.#formBuilder.group(this.#initialFormValue);
|
||||||
|
initialFormValue = this.form.getRawValue();
|
||||||
|
|
||||||
|
readonly formToSignalMap = {
|
||||||
|
addRedaction: formValueToSignal<DefaultOptionsForm['addRedaction']>(this.form.controls.addRedaction),
|
||||||
|
addHint: formValueToSignal<DefaultOptionsForm['addHint']>(this.form.controls.addHint),
|
||||||
|
removeRedaction: formValueToSignal<DefaultOptionsForm['removeRedaction']>(this.form.controls.removeRedaction),
|
||||||
|
removeHint: formValueToSignal<DefaultOptionsForm['removeHint']>(this.form.controls.removeHint),
|
||||||
|
removeRecommendation: formValueToSignal<DefaultOptionsForm['removeRecommendation']>(this.form.controls.removeRecommendation),
|
||||||
|
};
|
||||||
|
|
||||||
|
readonly displayExtraOptionAddRedaction = computed(() => RedactOrHintOptions.IN_DOSSIER === this.formToSignalMap['addRedaction']());
|
||||||
|
readonly displayExtraOptionAddHint = computed(() => RedactOrHintOptions.IN_DOSSIER === this.formToSignalMap['addHint']());
|
||||||
|
readonly displayExtraOptionRemoveRedaction = computed(() =>
|
||||||
|
(
|
||||||
|
[RemoveRedactionOptions.IN_DOSSIER, RemoveRedactionOptions.FALSE_POSITIVE] as Partial<
|
||||||
|
RemoveRedactionOption | SystemDefaultType
|
||||||
|
>[]
|
||||||
|
).includes(this.formToSignalMap['removeRedaction']()),
|
||||||
|
);
|
||||||
|
readonly displayExtraOptionRemoveHint = computed(() => RemoveRedactionOptions.IN_DOSSIER === this.formToSignalMap['removeHint']());
|
||||||
|
readonly displayExtraOptionRemoveRecommendation = computed(
|
||||||
|
() => RemoveRedactionOptions.DO_NOT_RECOMMEND === this.formToSignalMap['removeRecommendation'](),
|
||||||
|
);
|
||||||
|
|
||||||
|
get #initialFormValue() {
|
||||||
|
return {
|
||||||
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
||||||
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
||||||
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
||||||
@ -59,46 +91,10 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
||||||
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
||||||
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
||||||
});
|
} as DefaultOptionsForm;
|
||||||
initialFormValue = this.form.getRawValue();
|
|
||||||
|
|
||||||
readonly redactionAddOptions = redactionAddOptions;
|
|
||||||
readonly hintAddOptions = hintAddOptions;
|
|
||||||
readonly removeOptions = removeOptions;
|
|
||||||
readonly redactionRemoveOptions = redactionRemoveOptions;
|
|
||||||
readonly recommendationRemoveOptions = recommendationRemoveOptions;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionAddRedaction() {
|
|
||||||
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addRedaction.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionAddHint() {
|
|
||||||
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addHint.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionRemoveRedaction() {
|
|
||||||
return (
|
|
||||||
[RemoveRedactionOptions.IN_DOSSIER, RemoveRedactionOptions.FALSE_POSITIVE] as Partial<
|
|
||||||
RemoveRedactionOption | SystemDefaultType
|
|
||||||
>[]
|
|
||||||
).includes(this.form.controls.removeRedaction.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionRemoveHint() {
|
|
||||||
return RemoveRedactionOptions.IN_DOSSIER === this.form.controls.removeHint.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionRemoveRecommendation() {
|
|
||||||
return RemoveRedactionOptions.DO_NOT_RECOMMEND === this.form.controls.removeRecommendation.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<any> {
|
async save(): Promise<any> {
|
||||||
const formValue = this.form.value;
|
|
||||||
|
|
||||||
if (this.initialFormValue.addRedaction !== this.form.controls.addRedaction.value) {
|
if (this.initialFormValue.addRedaction !== this.form.controls.addRedaction.value) {
|
||||||
await this.#userPreferences.saveAddRedactionDefaultOption(this.form.controls.addRedaction.value);
|
await this.#userPreferences.saveAddRedactionDefaultOption(this.form.controls.addRedaction.value);
|
||||||
}
|
}
|
||||||
@ -115,7 +111,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
await this.#userPreferences.saveRemoveHintDefaultOption(this.form.controls.removeHint.value);
|
await this.#userPreferences.saveRemoveHintDefaultOption(this.form.controls.removeHint.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayExtraOptionAddRedaction) {
|
if (this.displayExtraOptionAddRedaction()) {
|
||||||
if (this.initialFormValue.addRedactionApplyToAll !== this.form.controls.addRedactionApplyToAll.value) {
|
if (this.initialFormValue.addRedactionApplyToAll !== this.form.controls.addRedactionApplyToAll.value) {
|
||||||
await this.#userPreferences.saveAddRedactionDefaultExtraOption(this.form.controls.addRedactionApplyToAll.value);
|
await this.#userPreferences.saveAddRedactionDefaultExtraOption(this.form.controls.addRedactionApplyToAll.value);
|
||||||
}
|
}
|
||||||
@ -123,7 +119,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
await this.#userPreferences.saveAddRedactionDefaultExtraOption('undefined');
|
await this.#userPreferences.saveAddRedactionDefaultExtraOption('undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayExtraOptionAddHint) {
|
if (this.displayExtraOptionAddHint()) {
|
||||||
if (this.initialFormValue.addHintApplyToAll !== this.form.controls.addHintApplyToAll.value) {
|
if (this.initialFormValue.addHintApplyToAll !== this.form.controls.addHintApplyToAll.value) {
|
||||||
await this.#userPreferences.saveAddHintDefaultExtraOption(this.form.controls.addHintApplyToAll.value);
|
await this.#userPreferences.saveAddHintDefaultExtraOption(this.form.controls.addHintApplyToAll.value);
|
||||||
}
|
}
|
||||||
@ -131,7 +127,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
await this.#userPreferences.saveAddHintDefaultExtraOption('undefined');
|
await this.#userPreferences.saveAddHintDefaultExtraOption('undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayExtraOptionRemoveRedaction) {
|
if (this.displayExtraOptionRemoveRedaction()) {
|
||||||
if (this.initialFormValue.removeRedactionApplyToAll !== this.form.controls.removeRedactionApplyToAll.value) {
|
if (this.initialFormValue.removeRedactionApplyToAll !== this.form.controls.removeRedactionApplyToAll.value) {
|
||||||
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption(this.form.controls.removeRedactionApplyToAll.value);
|
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption(this.form.controls.removeRedactionApplyToAll.value);
|
||||||
}
|
}
|
||||||
@ -139,7 +135,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption('undefined');
|
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption('undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayExtraOptionRemoveHint) {
|
if (this.displayExtraOptionRemoveHint()) {
|
||||||
if (this.initialFormValue.removeHintApplyToAll !== this.form.controls.removeHintApplyToAll.value) {
|
if (this.initialFormValue.removeHintApplyToAll !== this.form.controls.removeHintApplyToAll.value) {
|
||||||
await this.#userPreferences.saveRemoveHintDefaultExtraOption(this.form.controls.removeHintApplyToAll.value);
|
await this.#userPreferences.saveRemoveHintDefaultExtraOption(this.form.controls.removeHintApplyToAll.value);
|
||||||
}
|
}
|
||||||
@ -147,7 +143,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
await this.#userPreferences.saveRemoveHintDefaultExtraOption('undefined');
|
await this.#userPreferences.saveRemoveHintDefaultExtraOption('undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.displayExtraOptionRemoveRecommendation) {
|
if (this.displayExtraOptionRemoveRecommendation()) {
|
||||||
if (this.initialFormValue.removeRecommendationApplyToAll !== this.form.controls.removeRecommendationApplyToAll.value) {
|
if (this.initialFormValue.removeRecommendationApplyToAll !== this.form.controls.removeRecommendationApplyToAll.value) {
|
||||||
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption(
|
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption(
|
||||||
this.form.controls.removeRecommendationApplyToAll.value,
|
this.form.controls.removeRecommendationApplyToAll.value,
|
||||||
@ -164,17 +160,6 @@ export class DialogDefaultsComponent extends BaseFormComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#patchValues() {
|
#patchValues() {
|
||||||
this.form.patchValue({
|
this.form.patchValue(this.#initialFormValue);
|
||||||
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
|
||||||
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
|
||||||
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
|
||||||
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
|
|
||||||
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
|
|
||||||
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
|
|
||||||
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
|
|
||||||
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
|
||||||
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
|
||||||
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,7 @@ import { MatFormField } from '@angular/material/form-field';
|
|||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
|
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
|
||||||
import { formControlToSignal } from '@utils/functions';
|
import { AsControl, formValueToSignal } from '@common-ui/utils';
|
||||||
import { AsControl } from '@common-ui/utils';
|
|
||||||
|
|
||||||
interface UserProfileForm {
|
interface UserProfileForm {
|
||||||
email: string;
|
email: string;
|
||||||
@ -59,7 +58,7 @@ export class UserProfileScreenComponent extends BaseFormComponent {
|
|||||||
|
|
||||||
readonly profileKeys = ['email', 'firstName', 'lastName'];
|
readonly profileKeys = ['email', 'firstName', 'lastName'];
|
||||||
readonly languages = this._translateService.langs;
|
readonly languages = this._translateService.langs;
|
||||||
readonly language = formControlToSignal<UserProfileForm['language']>(this.form.controls.language);
|
readonly language = formValueToSignal<UserProfileForm['language']>(this.form.controls.language);
|
||||||
readonly languageSelectLabel = computed(() => this.translations[this.language()]);
|
readonly languageSelectLabel = computed(() => this.translations[this.language()]);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -82,28 +81,28 @@ export class UserProfileScreenComponent extends BaseFormComponent {
|
|||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
get languageChanged(): boolean {
|
get #languageChanged(): boolean {
|
||||||
return this.initialFormValue['language'] !== this.form.controls.language.value;
|
return this.initialFormValue['language'] !== this.form.controls.language.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get themeChanged(): boolean {
|
get #themeChanged(): boolean {
|
||||||
return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value;
|
return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get emailChanged(): boolean {
|
get #emailChanged(): boolean {
|
||||||
return this.initialFormValue['email'] !== this.form.controls.email.value;
|
return this.initialFormValue['email'] !== this.form.controls.email.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get profileChanged(): boolean {
|
get #profileChanged(): boolean {
|
||||||
return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value);
|
return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (this.profileChanged) {
|
if (this.#profileChanged) {
|
||||||
const value = this.form.getRawValue() as IProfile;
|
const value = this.form.getRawValue() as IProfile;
|
||||||
|
|
||||||
if (this.emailChanged) {
|
if (this.#emailChanged) {
|
||||||
const dialogRef = this._dialogService.openDialog('confirmPassword');
|
const dialogRef = this._dialogService.openDialog('confirmPassword');
|
||||||
const password = await firstValueFrom(dialogRef.afterClosed());
|
const password = await firstValueFrom(dialogRef.afterClosed());
|
||||||
if (!password) {
|
if (!password) {
|
||||||
@ -119,12 +118,12 @@ export class UserProfileScreenComponent extends BaseFormComponent {
|
|||||||
await firstValueFrom(this._userService.loadAll());
|
await firstValueFrom(this._userService.loadAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.languageChanged) {
|
if (this.#languageChanged) {
|
||||||
await this._languageService.change(this.form.controls.language.value);
|
await this._languageService.change(this.form.controls.language.value);
|
||||||
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
|
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.themeChanged) {
|
if (this.#themeChanged) {
|
||||||
await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light');
|
await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,8 @@ import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators }
|
|||||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
import { BaseFormComponent, IconButtonComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, IconButtonComponent, 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 { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -40,7 +38,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
acc.push(role);
|
acc.push(role);
|
||||||
@ -49,7 +47,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
get sendSetPasswordMail() {
|
get #sendSetPasswordMail() {
|
||||||
return !this.form.controls.sendSetPasswordMail.value;
|
return !this.form.controls.sendSetPasswordMail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +89,8 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const userData: IProfileUpdateRequest = {
|
const userData: IProfileUpdateRequest = {
|
||||||
...this.form.getRawValue(),
|
...this.form.getRawValue(),
|
||||||
roles: this.activeRoles,
|
roles: this.#activeRoles,
|
||||||
sendSetPasswordMail: this.sendSetPasswordMail,
|
sendSetPasswordMail: this.#sendSetPasswordMail,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.user()) {
|
if (!this.user()) {
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import { JustificationsService } from '@services/entity-services/justifications.
|
|||||||
import { BaseDialogComponent, CircleButtonComponent, HasScrollbarDirective, IconButtonComponent } from '@iqser/common-ui';
|
import { BaseDialogComponent, CircleButtonComponent, HasScrollbarDirective, IconButtonComponent } from '@iqser/common-ui';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { formControlToSignal } from '@utils/functions';
|
|
||||||
import { toSignal } from '@angular/core/rxjs-interop';
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
interface DialogData {
|
interface DialogData {
|
||||||
justification?: Justification;
|
justification?: Justification;
|
||||||
@ -22,7 +22,7 @@ interface DialogData {
|
|||||||
})
|
})
|
||||||
export class AddEditJustificationDialogComponent extends BaseDialogComponent {
|
export class AddEditJustificationDialogComponent extends BaseDialogComponent {
|
||||||
readonly form = this.#getForm();
|
readonly form = this.#getForm();
|
||||||
readonly name = formControlToSignal(this.form.controls['name']);
|
readonly name = formValueToSignal(this.form.controls['name']);
|
||||||
readonly allJustifications = toSignal(this._justificationService.all$);
|
readonly allJustifications = toSignal(this._justificationService.all$);
|
||||||
readonly technicalName = computed(() => {
|
readonly technicalName = computed(() => {
|
||||||
if (this.data.justification) {
|
if (this.data.justification) {
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div #viewer class="viewer"></div>
|
<div #viewer class="viewer"></div>
|
||||||
|
|
||||||
<redaction-paginator (changePage)="navigateTo($event)" *ngIf="loaded$ | async"></redaction-paginator>
|
@if (loaded$ | async) {
|
||||||
|
<redaction-paginator (changePage)="navigateTo($event)"></redaction-paginator>
|
||||||
|
}
|
||||||
|
|
||||||
<div *ngIf="!!instance && changed && currentUser.isAdmin" class="changes-box">
|
@if (!!instance && changed() && currentUser.isAdmin) {
|
||||||
|
<div class="changes-box">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[disabled]="!valid"
|
[disabled]="!valid()"
|
||||||
[label]="'watermark-screen.action.save' | translate"
|
[label]="'watermark-screen.action.save' | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
icon="iqser:check"
|
icon="iqser:check"
|
||||||
@ -14,9 +17,11 @@
|
|||||||
|
|
||||||
<div (click)="revert()" [translate]="'watermark-screen.action.revert'" class="all-caps-label cancel"></div>
|
<div (click)="revert()" [translate]="'watermark-screen.action.revert'" class="all-caps-label cancel"></div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="watermark$ | async" class="right-container" [attr.help-mode-key]="'watermarks'" iqserHasScrollbar>
|
@if (watermark$ | async) {
|
||||||
|
<div class="right-container" [attr.help-mode-key]="'watermarks'" iqserHasScrollbar>
|
||||||
<form (keyup)="configChanged()" [formGroup]="form">
|
<form (keyup)="configChanged()" [formGroup]="form">
|
||||||
<div class="iqser-input-group required w-300">
|
<div class="iqser-input-group required w-300">
|
||||||
<label [translate]="'watermark-screen.form.name-label'"></label>
|
<label [translate]="'watermark-screen.form.name-label'"></label>
|
||||||
@ -39,48 +44,51 @@
|
|||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<label [translate]="'watermark-screen.form.orientation'" class="all-caps-label mb-8"></label>
|
<label [translate]="'watermark-screen.form.orientation'" class="all-caps-label mb-8"></label>
|
||||||
<div class="square-options">
|
<div class="square-options">
|
||||||
|
@for (option of orientationOptions; track option) {
|
||||||
<div
|
<div
|
||||||
(click)="setValue('orientation', option)"
|
(click)="setValue('orientation', option)"
|
||||||
*ngFor="let option of orientationOptions"
|
|
||||||
[class.active]="form.controls.orientation.value === option"
|
[class.active]="form.controls.orientation.value === option"
|
||||||
[class.disabled]="form.controls.orientation.disabled"
|
[class.disabled]="orientationStatus() === 'DISABLED'"
|
||||||
[ngClass]="option"
|
[ngClass]="option"
|
||||||
>
|
>
|
||||||
<span>ABC</span>
|
<span>ABC</span>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
|
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div [class.disabled]="form.controls.horizontalTextAlignment.disabled" class="alignment-buttons">
|
<div [class.disabled]="horizontalTextAlignmentStatus() === 'DISABLED'" class="alignment-buttons">
|
||||||
|
@for (alignment of watermarkHorizontalAlignments; track alignment) {
|
||||||
<div
|
<div
|
||||||
(click)="alignHorizontally(alignment)"
|
(click)="alignHorizontally(alignment)"
|
||||||
*ngFor="let alignment of watermarkHorizontalAlignments"
|
|
||||||
[class.active]="currentAlignment.horizontal === alignment"
|
[class.active]="currentAlignment.horizontal === alignment"
|
||||||
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
|
[class.disabled]="horizontalTextAlignmentStatus() === 'DISABLED'"
|
||||||
[matTooltipPosition]="'above'"
|
[matTooltipPosition]="'above'"
|
||||||
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
|
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
|
||||||
[ngClass]="'horizontal-' + alignment.toLowerCase()"
|
[ngClass]="'horizontal-' + loweredWatermarkHorizontalAlignments[alignment]"
|
||||||
class="alignment"
|
class="alignment"
|
||||||
>
|
>
|
||||||
<mat-icon [svgIcon]="'red:align-horizontal-' + alignment.toLowerCase()"></mat-icon>
|
<mat-icon [svgIcon]="'red:align-horizontal-' + loweredWatermarkHorizontalAlignments[alignment]"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div [class.disabled]="form.controls.verticalTextAlignment.disabled" class="alignment-buttons">
|
<div [class.disabled]="verticalTextAlignmentStatus() === 'DISABLED'" class="alignment-buttons">
|
||||||
|
@for (alignment of watermarkVerticalAlignments; track alignment) {
|
||||||
<div
|
<div
|
||||||
(click)="alignVertically(alignment)"
|
(click)="alignVertically(alignment)"
|
||||||
*ngFor="let alignment of watermarkVerticalAlignments"
|
|
||||||
[class.active]="currentAlignment.vertical === alignment"
|
[class.active]="currentAlignment.vertical === alignment"
|
||||||
[class.disabled]="form.controls.verticalTextAlignment.disabled"
|
[class.disabled]="verticalTextAlignmentStatus() === 'DISABLED'"
|
||||||
[matTooltipPosition]="'above'"
|
[matTooltipPosition]="'above'"
|
||||||
[matTooltip]="translations.VERTICAL[alignment] | translate"
|
[matTooltip]="translations.VERTICAL[alignment] | translate"
|
||||||
[ngClass]="'vertical-' + alignment.toLowerCase()"
|
[ngClass]="'vertical-' + loweredWatermarkVerticalAlignments[alignment]"
|
||||||
class="alignment"
|
class="alignment"
|
||||||
>
|
>
|
||||||
<mat-icon [svgIcon]="'red:align-vertical-' + alignment.toLowerCase()"></mat-icon>
|
<mat-icon [svgIcon]="'red:align-vertical-' + loweredWatermarkVerticalAlignments[alignment]"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -110,35 +118,36 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
(colorPickerChange)="setValue('hexColor', $event)"
|
(colorPickerChange)="setValue('hexColor', $event)"
|
||||||
[class.disabled]="form.controls.hexColor.disabled"
|
[class.disabled]="hexColorStatus() === 'DISABLED'"
|
||||||
[colorPicker]="form.controls.hexColor.value"
|
[colorPicker]="form.controls.hexColor.value"
|
||||||
[cpDisabled]="form.controls.hexColor.disabled"
|
[cpDisabled]="hexColorStatus() === 'DISABLED'"
|
||||||
[cpOutputFormat]="'hex'"
|
[cpOutputFormat]="'hex'"
|
||||||
[cpPosition]="'top-right'"
|
[cpPosition]="'top-right'"
|
||||||
[cpUseRootViewContainer]="true"
|
[cpUseRootViewContainer]="true"
|
||||||
[style.background]="form.controls.hexColor.value"
|
[style.background]="form.controls.hexColor.value"
|
||||||
class="input-icon"
|
class="input-icon"
|
||||||
>
|
>
|
||||||
<mat-icon
|
@if (!form.controls.hexColor?.value || form.controls.hexColor.value?.length === 0) {
|
||||||
*ngIf="!form.controls.hexColor?.value || form.controls.hexColor.value?.length === 0"
|
<mat-icon svgIcon="iqser:color-picker"></mat-icon>
|
||||||
svgIcon="iqser:color-picker"
|
}
|
||||||
></mat-icon>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<label [translate]="'watermark-screen.form.font-type'" class="all-caps-label mb-8"></label>
|
<label [translate]="'watermark-screen.form.font-type'" class="all-caps-label mb-8"></label>
|
||||||
<div class="square-options">
|
<div class="square-options">
|
||||||
|
@for (option of fontOptions; track option.value) {
|
||||||
<div
|
<div
|
||||||
(click)="setValue('fontType', option.value)"
|
(click)="setValue('fontType', option.value)"
|
||||||
*ngFor="let option of fontOptions"
|
|
||||||
[class.active]="form.controls.fontType.value === option.value"
|
[class.active]="form.controls.fontType.value === option.value"
|
||||||
[class.disabled]="form.controls.fontType.disabled"
|
[class.disabled]="fontTypeStatus() === 'DISABLED'"
|
||||||
[ngClass]="option.value"
|
[ngClass]="option.value"
|
||||||
>
|
>
|
||||||
{{ option.display }}
|
{{ option.display }}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { AsyncPipe, NgClass, NgForOf, NgIf } from '@angular/common';
|
import { AsyncPipe, NgClass } from '@angular/common';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ChangeDetectorRef, Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, computed, ElementRef, inject, OnInit, viewChild, ViewChild } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { MatSlider, MatSliderThumb } from '@angular/material/slider';
|
import { MatSlider, MatSliderThumb } from '@angular/material/slider';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
@ -18,7 +18,15 @@ import {
|
|||||||
Toaster,
|
Toaster,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { AsControl, Debounce, getParam, trackByFactory, UI_ROOT_PATH_FN } from '@iqser/common-ui/lib/utils';
|
import {
|
||||||
|
AsControl,
|
||||||
|
Debounce,
|
||||||
|
formStatusToSignal,
|
||||||
|
formValueToSignal,
|
||||||
|
getParam,
|
||||||
|
trackByFactory,
|
||||||
|
UI_ROOT_PATH_FN,
|
||||||
|
} from '@iqser/common-ui/lib/utils';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||||
import {
|
import {
|
||||||
@ -77,14 +85,12 @@ interface WatermarkForm {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
PaginatorComponent,
|
PaginatorComponent,
|
||||||
NgIf,
|
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
HasScrollbarDirective,
|
HasScrollbarDirective,
|
||||||
MatTooltip,
|
MatTooltip,
|
||||||
NgForOf,
|
|
||||||
NgClass,
|
NgClass,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
MatSlider,
|
MatSlider,
|
||||||
@ -93,7 +99,7 @@ interface WatermarkForm {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class WatermarkScreenComponent implements OnInit {
|
export class WatermarkScreenComponent implements OnInit {
|
||||||
@ViewChild('viewer', { static: true }) private readonly _viewer: ElementRef<HTMLDivElement>;
|
private readonly _viewer = viewChild.required<ElementRef<HTMLDivElement>>('viewer');
|
||||||
readonly #loaded$ = new BehaviorSubject(false);
|
readonly #loaded$ = new BehaviorSubject(false);
|
||||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||||
readonly #watermarkId = Number(getParam(WATERMARK_ID));
|
readonly #watermarkId = Number(getParam(WATERMARK_ID));
|
||||||
@ -104,7 +110,7 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
readonly translations = watermarkTranslations;
|
readonly translations = watermarkTranslations;
|
||||||
readonly trackBy = trackByFactory();
|
readonly trackBy = trackByFactory();
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
readonly form = this.#form;
|
readonly form: FormGroup<AsControl<WatermarkForm>> = this.#form;
|
||||||
readonly watermark$: Observable<Partial<IWatermark>>;
|
readonly watermark$: Observable<Partial<IWatermark>>;
|
||||||
readonly fontOptions = [
|
readonly fontOptions = [
|
||||||
{ value: 'times-new-roman', display: 'Times' },
|
{ value: 'times-new-roman', display: 'Times' },
|
||||||
@ -117,6 +123,42 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
readonly watermarkHorizontalAlignments = Object.values(WATERMARK_HORIZONTAL_ALIGNMENTS);
|
readonly watermarkHorizontalAlignments = Object.values(WATERMARK_HORIZONTAL_ALIGNMENTS);
|
||||||
readonly watermarkVerticalAlignments = Object.values(WATERMARK_VERTICAL_ALIGNMENTS);
|
readonly watermarkVerticalAlignments = Object.values(WATERMARK_VERTICAL_ALIGNMENTS);
|
||||||
currentAlignment: WatermarkAlignment;
|
currentAlignment: WatermarkAlignment;
|
||||||
|
readonly loweredWatermarkHorizontalAlignments = this.watermarkHorizontalAlignments.reduce(
|
||||||
|
(acc, curr: string) => ({
|
||||||
|
...acc,
|
||||||
|
[curr]: curr.toLowerCase(),
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
readonly loweredWatermarkVerticalAlignments = this.watermarkVerticalAlignments.reduce(
|
||||||
|
(acc, curr: string) => ({
|
||||||
|
...acc,
|
||||||
|
[curr]: curr.toLowerCase(),
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
readonly hexColorStatus = formStatusToSignal(this.form.controls.hexColor);
|
||||||
|
readonly fontTypeStatus = formStatusToSignal(this.form.controls.fontType);
|
||||||
|
readonly verticalTextAlignmentStatus = formStatusToSignal(this.form.controls.verticalTextAlignment);
|
||||||
|
readonly horizontalTextAlignmentStatus = formStatusToSignal(this.form.controls.horizontalTextAlignment);
|
||||||
|
readonly orientationStatus = formStatusToSignal(this.form.controls.orientation);
|
||||||
|
readonly formValue = formValueToSignal(this.form);
|
||||||
|
readonly formStatus = formStatusToSignal(this.form);
|
||||||
|
readonly changed = computed(() => {
|
||||||
|
for (const key of Object.keys(this.form.getRawValue())) {
|
||||||
|
if (this.#watermark[key] !== this.formValue()[key]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
readonly valid = computed(() => {
|
||||||
|
if (!this.formValue()['name'] || !this.formValue()['text']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.formStatus() === 'VALID';
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _http: HttpClient,
|
private readonly _http: HttpClient,
|
||||||
@ -137,26 +179,10 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
this.watermark$ = obs$.pipe(tap(watermark => this.#initForm(watermark)));
|
this.watermark$ = obs$.pipe(tap(watermark => this.#initForm(watermark)));
|
||||||
}
|
}
|
||||||
|
|
||||||
get changed(): boolean {
|
get #form(): FormGroup<AsControl<WatermarkForm>> {
|
||||||
for (const key of Object.keys(this.form.getRawValue())) {
|
const form = this._formBuilder.group({
|
||||||
if (this.#watermark[key] !== this.form.get(key)?.value) {
|
name: ['', Validators.required],
|
||||||
return true;
|
text: ['', Validators.required],
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get valid(): boolean {
|
|
||||||
if (!this.form.controls.name?.value || !this.form.controls.text?.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.form.valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get #form() {
|
|
||||||
const form: FormGroup<AsControl<WatermarkForm>> = this._formBuilder.group({
|
|
||||||
name: [null],
|
|
||||||
text: [null],
|
|
||||||
hexColor: [null],
|
hexColor: [null],
|
||||||
opacity: [null],
|
opacity: [null],
|
||||||
fontSize: [null],
|
fontSize: [null],
|
||||||
@ -175,7 +201,6 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
|
|
||||||
@Debounce()
|
@Debounce()
|
||||||
async configChanged() {
|
async configChanged() {
|
||||||
console.log('configChanged');
|
|
||||||
await this.#drawWatermark();
|
await this.#drawWatermark();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +283,7 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
// use nativeElement instead of document.getElementById('viwer')
|
// use nativeElement instead of document.getElementById('viwer')
|
||||||
// because WebViewer works better with this approach
|
// because WebViewer works better with this approach
|
||||||
this._viewer.nativeElement,
|
this._viewer().nativeElement,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.instance.UI.setTheme(this._userPreferenceService.getTheme());
|
this.instance.UI.setTheme(this._userPreferenceService.getTheme());
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
|
@if (stats(); as dossierTemplate) {
|
||||||
<a
|
<a
|
||||||
*ngIf="stats as dossierTemplate"
|
[attr.help-mode-key]="!isTemplateEmpty() ? 'open_dossier_template' : null"
|
||||||
[attr.help-mode-key]="!dossierTemplate.isEmpty ? 'open_dossier_template' : null"
|
[class.empty]="isTemplateEmpty()"
|
||||||
[class.empty]="dossierTemplate.isEmpty"
|
[routerLink]="isTemplateEmpty() ? null : ['..', dossierTemplate.dossierTemplateId, 'dossiers']"
|
||||||
[routerLink]="dossierTemplate.isEmpty ? null : ['..', dossierTemplate.dossierTemplateId, 'dossiers']"
|
|
||||||
class="dialog"
|
class="dialog"
|
||||||
>
|
>
|
||||||
<ng-container *ngIf="!dossierTemplate.isEmpty; else empty">
|
@if (!isTemplateEmpty()) {
|
||||||
<div class="flex-2">
|
<div class="flex-2">
|
||||||
<div class="heading mb-6">{{ dossierTemplate.name }}</div>
|
<div class="heading mb-6">{{ dossierTemplate.name }}</div>
|
||||||
<div class="stats-subtitle">
|
<div class="stats-subtitle">
|
||||||
@ -27,7 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:user"></mat-icon>
|
<mat-icon svgIcon="red:user"></mat-icon>
|
||||||
<span [innerHTML]="'dossier-template-stats.total-people' | translate: { count: dossierTemplate.numberOfPeople }"></span>
|
<span
|
||||||
|
[innerHTML]="'dossier-template-stats.total-people' | translate: { count: dossierTemplate.numberOfPeople }"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="iqser:pages"></mat-icon>
|
<mat-icon svgIcon="iqser:pages"></mat-icon>
|
||||||
@ -39,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-3">
|
<div class="flex-3">
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
[config]="translateChartService.translateAndSortDossierStates(dossierTemplate.dossiersChartConfig, dossierTemplate.id)"
|
[config]="dossierStates()"
|
||||||
[radius]="63"
|
[radius]="63"
|
||||||
[strokeWidth]="15"
|
[strokeWidth]="15"
|
||||||
[subtitles]="['dossier-template-stats.active-dossiers' | translate: { count: dossierTemplate.numberOfActiveDossiers }]"
|
[subtitles]="['dossier-template-stats.active-dossiers' | translate: { count: dossierTemplate.numberOfActiveDossiers }]"
|
||||||
@ -49,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-3">
|
<div class="flex-3">
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
[config]="translateChartService.translateWorkflowStatus(dossierTemplate.documentsChartConfig)"
|
[config]="workflowStatuses()"
|
||||||
[radius]="63"
|
[radius]="63"
|
||||||
[strokeWidth]="15"
|
[strokeWidth]="15"
|
||||||
[subtitles]="['dossier-template-stats.total-documents' | translate]"
|
[subtitles]="['dossier-template-stats.total-documents' | translate]"
|
||||||
@ -57,9 +59,7 @@
|
|||||||
totalType="sum"
|
totalType="sum"
|
||||||
></redaction-donut-chart>
|
></redaction-donut-chart>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
} @else {
|
||||||
|
|
||||||
<ng-template #empty>
|
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
<div class="heading mb-8">
|
<div class="heading mb-8">
|
||||||
{{ dossierTemplate.name }}
|
{{ dossierTemplate.name }}
|
||||||
@ -69,14 +69,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (canCreateDossier()) {
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="newDossier()"
|
(action)="newDossier()"
|
||||||
*ngIf="permissionsService.canCreateDossier(dossierTemplate)"
|
|
||||||
[attr.help-mode-key]="'new_dossier'"
|
[attr.help-mode-key]="'new_dossier'"
|
||||||
[label]="'dashboard.empty-template.new-dossier' | translate"
|
[label]="'dashboard.empty-template.new-dossier' | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
[buttonId]="(dossierTemplate.name | snakeCase) + '-icon-button'"
|
[buttonId]="(dossierTemplate.name | snakeCase) + '-icon-button'"
|
||||||
icon="iqser:plus"
|
icon="iqser:plus"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
</ng-template>
|
}
|
||||||
|
}
|
||||||
</a>
|
</a>
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, computed, input } from '@angular/core';
|
||||||
import { DashboardStats } from '@red/domain';
|
import { DashboardStats } from '@red/domain';
|
||||||
import { IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
import { IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
||||||
import { TranslateChartService } from '@services/translate-chart.service';
|
import { TranslateChartService } from '@services/translate-chart.service';
|
||||||
import { SharedDialogService } from '@shared/services/dialog.service';
|
import { SharedDialogService } from '@shared/services/dialog.service';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -17,13 +16,20 @@ import { SnakeCasePipe } from '@common-ui/pipes/snake-case.pipe';
|
|||||||
templateUrl: './template-stats.component.html',
|
templateUrl: './template-stats.component.html',
|
||||||
styleUrls: ['./template-stats.component.scss'],
|
styleUrls: ['./template-stats.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgIf, RouterLink, MatIcon, TranslateModule, DonutChartComponent, IconButtonComponent, SnakeCasePipe],
|
imports: [RouterLink, MatIcon, TranslateModule, DonutChartComponent, IconButtonComponent, SnakeCasePipe],
|
||||||
})
|
})
|
||||||
export class TemplateStatsComponent {
|
export class TemplateStatsComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
|
|
||||||
@Input() stats: DashboardStats;
|
readonly stats = input<DashboardStats>();
|
||||||
|
|
||||||
|
readonly dossierStates = computed(() =>
|
||||||
|
this.translateChartService.translateAndSortDossierStates(this.stats().dossiersChartConfig, this.stats().id),
|
||||||
|
);
|
||||||
|
readonly workflowStatuses = computed(() => this.translateChartService.translateWorkflowStatus(this.stats().documentsChartConfig));
|
||||||
|
readonly isTemplateEmpty = computed(() => this.stats().isEmpty);
|
||||||
|
readonly canCreateDossier = computed(() => this.permissionsService.canCreateDossier(this.stats()));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _dialogService: SharedDialogService,
|
private readonly _dialogService: SharedDialogService,
|
||||||
@ -32,6 +38,6 @@ export class TemplateStatsComponent {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
newDossier(): void {
|
newDossier(): void {
|
||||||
this._dialogService.openDialog('addDossier', { dossierTemplateId: this.stats.dossierTemplateId });
|
this._dialogService.openDialog('addDossier', { dossierTemplateId: this.stats().dossierTemplateId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
<ng-container (longPress)="forceReanalysisAction($event)" *ngIf="selectedFiles.length" redactionLongPress>
|
@if (selectedFiles().length) {
|
||||||
|
<ng-container (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
||||||
<redaction-expandable-file-actions
|
<redaction-expandable-file-actions
|
||||||
[actions]="buttons"
|
[actions]="buttons"
|
||||||
[buttonType]="buttonType"
|
[buttonType]="buttonType()"
|
||||||
[maxWidth]="maxWidth"
|
[maxWidth]="maxWidth()"
|
||||||
[tooltipPosition]="IqserTooltipPositions.above"
|
[tooltipPosition]="IqserTooltipPositions.above"
|
||||||
>
|
>
|
||||||
</redaction-expandable-file-actions>
|
</redaction-expandable-file-actions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, OnChanges } from '@angular/core';
|
import { Component, input, OnChanges } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { CircleButtonType, CircleButtonTypes } from '@iqser/common-ui';
|
import { CircleButtonType, CircleButtonTypes } from '@iqser/common-ui';
|
||||||
import { Action, ActionTypes, Dossier, File, ProcessingFileStatuses } from '@red/domain';
|
import { Action, ActionTypes, Dossier, File, ProcessingFileStatuses } from '@red/domain';
|
||||||
@ -8,14 +8,13 @@ import { UserPreferenceService } from '@users/user-preference.service';
|
|||||||
import { BulkActionsService } from '../../services/bulk-actions.service';
|
import { BulkActionsService } from '../../services/bulk-actions.service';
|
||||||
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
||||||
import { IqserTooltipPositions } from '@common-ui/utils';
|
import { IqserTooltipPositions } from '@common-ui/utils';
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-dossier-overview-bulk-actions [dossier] [selectedFiles]',
|
selector: 'redaction-dossier-overview-bulk-actions [dossier] [selectedFiles]',
|
||||||
templateUrl: './dossier-overview-bulk-actions.component.html',
|
templateUrl: './dossier-overview-bulk-actions.component.html',
|
||||||
styleUrls: ['./dossier-overview-bulk-actions.component.scss'],
|
styleUrls: ['./dossier-overview-bulk-actions.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [LongPressDirective, ExpandableFileActionsComponent, NgIf],
|
imports: [LongPressDirective, ExpandableFileActionsComponent],
|
||||||
})
|
})
|
||||||
export class DossierOverviewBulkActionsComponent implements OnChanges {
|
export class DossierOverviewBulkActionsComponent implements OnChanges {
|
||||||
#analysisForced: boolean;
|
#analysisForced: boolean;
|
||||||
@ -37,10 +36,10 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
#toggleAnalysisTooltip: string;
|
#toggleAnalysisTooltip: string;
|
||||||
#allFilesAreExcluded: boolean;
|
#allFilesAreExcluded: boolean;
|
||||||
#canMoveToSameState: boolean;
|
#canMoveToSameState: boolean;
|
||||||
@Input() dossier: Dossier;
|
readonly dossier = input<Dossier>();
|
||||||
@Input() selectedFiles: File[];
|
readonly selectedFiles = input<File[]>();
|
||||||
@Input() buttonType: CircleButtonType = CircleButtonTypes.default;
|
readonly buttonType = input<CircleButtonType>(CircleButtonTypes.default);
|
||||||
@Input() maxWidth: number;
|
readonly maxWidth = input<number>();
|
||||||
buttons: Action[];
|
buttons: Action[];
|
||||||
readonly IqserTooltipPositions = IqserTooltipPositions;
|
readonly IqserTooltipPositions = IqserTooltipPositions;
|
||||||
|
|
||||||
@ -50,12 +49,12 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
private readonly _bulkActionsService: BulkActionsService,
|
private readonly _bulkActionsService: BulkActionsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private get _buttons(): Action[] {
|
get #buttons(): Action[] {
|
||||||
const actions: Action[] = [
|
const actions: Action[] = [
|
||||||
{
|
{
|
||||||
id: 'delete-files-btn',
|
id: 'delete-files-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.delete(this.selectedFiles),
|
action: () => this._bulkActionsService.delete(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.bulk.delete'),
|
tooltip: _('dossier-overview.bulk.delete'),
|
||||||
icon: 'iqser:trash',
|
icon: 'iqser:trash',
|
||||||
show: this.#canDelete,
|
show: this.#canDelete,
|
||||||
@ -63,7 +62,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'assign-files-btn',
|
id: 'assign-files-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.assign(this.selectedFiles),
|
action: () => this._bulkActionsService.assign(this.selectedFiles()),
|
||||||
tooltip: this.#assignTooltip,
|
tooltip: this.#assignTooltip,
|
||||||
icon: 'red:assign',
|
icon: 'red:assign',
|
||||||
show: this.#canAssign,
|
show: this.#canAssign,
|
||||||
@ -71,7 +70,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'assign-files-to-me-btn',
|
id: 'assign-files-to-me-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.assignToMe(this.selectedFiles),
|
action: () => this._bulkActionsService.assignToMe(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.assign-me'),
|
tooltip: _('dossier-overview.assign-me'),
|
||||||
icon: 'red:assign-me',
|
icon: 'red:assign-me',
|
||||||
show: this.#canAssignToSelf,
|
show: this.#canAssignToSelf,
|
||||||
@ -79,7 +78,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'to-new-btn',
|
id: 'to-new-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.setToNew(this.selectedFiles),
|
action: () => this._bulkActionsService.setToNew(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.back-to-new'),
|
tooltip: _('dossier-overview.back-to-new'),
|
||||||
icon: 'red:undo',
|
icon: 'red:undo',
|
||||||
show: this.#canSetToNew,
|
show: this.#canSetToNew,
|
||||||
@ -87,7 +86,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'to-under-approval-btn',
|
id: 'to-under-approval-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles),
|
action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.under-approval'),
|
tooltip: _('dossier-overview.under-approval'),
|
||||||
icon: 'red:ready-for-approval',
|
icon: 'red:ready-for-approval',
|
||||||
show: this.#canSetToUnderApproval,
|
show: this.#canSetToUnderApproval,
|
||||||
@ -95,7 +94,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'to-under-review-btn',
|
id: 'to-under-review-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.backToUnderReview(this.selectedFiles),
|
action: () => this._bulkActionsService.backToUnderReview(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.under-review'),
|
tooltip: _('dossier-overview.under-review'),
|
||||||
icon: 'red:undo',
|
icon: 'red:undo',
|
||||||
show: this.#canSetToUnderReview,
|
show: this.#canSetToUnderReview,
|
||||||
@ -103,14 +102,14 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'download-files-btn',
|
id: 'download-files-btn',
|
||||||
type: ActionTypes.downloadBtn,
|
type: ActionTypes.downloadBtn,
|
||||||
show: !this.selectedFiles.some(file => file.processingStatus === ProcessingFileStatuses.ERROR || !file.lastProcessed),
|
show: !this.selectedFiles().some(file => file.processingStatus === ProcessingFileStatuses.ERROR || !file.lastProcessed),
|
||||||
files: this.selectedFiles,
|
files: this.selectedFiles(),
|
||||||
dossier: this.dossier,
|
dossier: this.dossier(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'approve-files-btn',
|
id: 'approve-files-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.approve(this.selectedFiles),
|
action: () => this._bulkActionsService.approve(this.selectedFiles()),
|
||||||
disabled: !this.#canApprove,
|
disabled: !this.#canApprove,
|
||||||
tooltip: this.#canApprove ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'),
|
tooltip: this.#canApprove ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'),
|
||||||
icon: 'red:approved',
|
icon: 'red:approved',
|
||||||
@ -119,7 +118,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'set-under-approval-btn',
|
id: 'set-under-approval-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles),
|
action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.under-approval'),
|
tooltip: _('dossier-overview.under-approval'),
|
||||||
icon: 'red:undo',
|
icon: 'red:undo',
|
||||||
show: this.#canUndoApproval,
|
show: this.#canUndoApproval,
|
||||||
@ -127,7 +126,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'ocr-files-btn',
|
id: 'ocr-files-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.ocr(this.selectedFiles),
|
action: () => this._bulkActionsService.ocr(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.ocr-file'),
|
tooltip: _('dossier-overview.ocr-file'),
|
||||||
icon: 'iqser:ocr',
|
icon: 'iqser:ocr',
|
||||||
show: this.#canOcr,
|
show: this.#canOcr,
|
||||||
@ -135,17 +134,17 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'reanalyse-files-btn',
|
id: 'reanalyse-files-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.reanalyse(this.selectedFiles),
|
action: () => this._bulkActionsService.reanalyse(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.bulk.reanalyse'),
|
tooltip: _('dossier-overview.bulk.reanalyse'),
|
||||||
icon: 'iqser:refresh',
|
icon: 'iqser:refresh',
|
||||||
show:
|
show:
|
||||||
this.#canReanalyse &&
|
this.#canReanalyse &&
|
||||||
(this.#analysisForced || this.#canEnableAutoAnalysis || this.selectedFiles.every(file => file.isError)),
|
(this.#analysisForced || this.#canEnableAutoAnalysis || this.selectedFiles().every(file => file.isError)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'stop-automatic-analysis-btn',
|
id: 'stop-automatic-analysis-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles),
|
action: () => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.stop-auto-analysis'),
|
tooltip: _('dossier-overview.stop-auto-analysis'),
|
||||||
icon: 'red:disable-analysis',
|
icon: 'red:disable-analysis',
|
||||||
show: this.#canDisableAutoAnalysis,
|
show: this.#canDisableAutoAnalysis,
|
||||||
@ -153,7 +152,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'start-automatic-analysis-btn',
|
id: 'start-automatic-analysis-btn',
|
||||||
type: ActionTypes.circleBtn,
|
type: ActionTypes.circleBtn,
|
||||||
action: () => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles),
|
action: () => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles()),
|
||||||
tooltip: _('dossier-overview.start-auto-analysis'),
|
tooltip: _('dossier-overview.start-auto-analysis'),
|
||||||
icon: 'red:enable-analysis',
|
icon: 'red:enable-analysis',
|
||||||
show: this.#canEnableAutoAnalysis,
|
show: this.#canEnableAutoAnalysis,
|
||||||
@ -161,7 +160,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
id: 'toggle-analysis-btn',
|
id: 'toggle-analysis-btn',
|
||||||
type: ActionTypes.toggle,
|
type: ActionTypes.toggle,
|
||||||
action: () => this._bulkActionsService.toggleAnalysis(this.selectedFiles, !this.#allFilesAreExcluded),
|
action: () => this._bulkActionsService.toggleAnalysis(this.selectedFiles(), !this.#allFilesAreExcluded),
|
||||||
tooltip: this.#toggleAnalysisTooltip,
|
tooltip: this.#toggleAnalysisTooltip,
|
||||||
checked: !this.#allFilesAreExcluded,
|
checked: !this.#allFilesAreExcluded,
|
||||||
show: this.#canToggleAnalysis,
|
show: this.#canToggleAnalysis,
|
||||||
@ -172,56 +171,56 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this._setup();
|
this.#setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
forceReanalysisAction($event: LongPressEvent) {
|
forceReanalysisAction($event: LongPressEvent) {
|
||||||
this.#analysisForced = !$event.touchEnd && this._userPreferenceService.isIqserDevMode;
|
this.#analysisForced = !$event.touchEnd && this._userPreferenceService.isIqserDevMode;
|
||||||
this._setup();
|
this.#setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setup() {
|
#setup() {
|
||||||
if (!this.selectedFiles.length) {
|
if (!this.selectedFiles().length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
|
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles().reduce(
|
||||||
(acc, file) => acc && (file.isUnderReview || file.isNew),
|
(acc, file) => acc && (file.isUnderReview || file.isNew),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
const allFilesAreUnderApproval = this.selectedFiles().reduce((acc, file) => acc && file.isUnderApproval, true);
|
||||||
const allFilesAreApproved = this.selectedFiles.reduce((acc, file) => acc && file.isApproved, true);
|
const allFilesAreApproved = this.selectedFiles().reduce((acc, file) => acc && file.isApproved, true);
|
||||||
this.#allFilesAreExcluded = this.selectedFiles.reduce((acc, file) => acc && file.excluded, true);
|
this.#allFilesAreExcluded = this.selectedFiles().reduce((acc, file) => acc && file.excluded, true);
|
||||||
this.#canMoveToSameState = allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval || allFilesAreApproved;
|
this.#canMoveToSameState = allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval || allFilesAreApproved;
|
||||||
|
|
||||||
this.#canAssign =
|
this.#canAssign =
|
||||||
this.#canMoveToSameState &&
|
this.#canMoveToSameState &&
|
||||||
(this._permissionsService.canAssignUser(this.selectedFiles, this.dossier) ||
|
(this._permissionsService.canAssignUser(this.selectedFiles(), this.dossier()) ||
|
||||||
this._permissionsService.canUnassignUser(this.selectedFiles, this.dossier));
|
this._permissionsService.canUnassignUser(this.selectedFiles(), this.dossier()));
|
||||||
this.#canAssignToSelf = this.#canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles, this.dossier);
|
this.#canAssignToSelf = this.#canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canDelete = this._permissionsService.canSoftDeleteFile(this.selectedFiles, this.dossier);
|
this.#canDelete = this._permissionsService.canSoftDeleteFile(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canReanalyse = this._permissionsService.canReanalyseFile(this.selectedFiles, this.dossier);
|
this.#canReanalyse = this._permissionsService.canReanalyseFile(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canDisableAutoAnalysis = this._permissionsService.canDisableAutoAnalysis(this.selectedFiles, this.dossier);
|
this.#canDisableAutoAnalysis = this._permissionsService.canDisableAutoAnalysis(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canEnableAutoAnalysis = this._permissionsService.canEnableAutoAnalysis(this.selectedFiles, this.dossier);
|
this.#canEnableAutoAnalysis = this._permissionsService.canEnableAutoAnalysis(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canToggleAnalysis = this._permissionsService.canToggleAnalysis(this.selectedFiles, this.dossier);
|
this.#canToggleAnalysis = this._permissionsService.canToggleAnalysis(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canOcr = this._permissionsService.canOcrFile(this.selectedFiles, this.dossier);
|
this.#canOcr = this._permissionsService.canOcrFile(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canSetToNew = this._permissionsService.canSetToNew(this.selectedFiles, this.dossier);
|
this.#canSetToNew = this._permissionsService.canSetToNew(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canSetToUnderReview = this._permissionsService.canSetUnderReview(this.selectedFiles, this.dossier);
|
this.#canSetToUnderReview = this._permissionsService.canSetUnderReview(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canSetToUnderApproval = this._permissionsService.canSetUnderApproval(this.selectedFiles, this.dossier);
|
this.#canSetToUnderApproval = this._permissionsService.canSetUnderApproval(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#isReadyForApproval = this._permissionsService.isReadyForApproval(this.selectedFiles, this.dossier);
|
this.#isReadyForApproval = this._permissionsService.isReadyForApproval(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canApprove = this._permissionsService.canBeApproved(this.selectedFiles, this.dossier);
|
this.#canApprove = this._permissionsService.canBeApproved(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#canUndoApproval = this._permissionsService.canUndoApproval(this.selectedFiles, this.dossier);
|
this.#canUndoApproval = this._permissionsService.canUndoApproval(this.selectedFiles(), this.dossier());
|
||||||
|
|
||||||
this.#assignTooltip = allFilesAreUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer');
|
this.#assignTooltip = allFilesAreUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer');
|
||||||
|
|
||||||
@ -229,6 +228,6 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
|||||||
? _('file-preview.toggle-analysis.enable')
|
? _('file-preview.toggle-analysis.enable')
|
||||||
: _('file-preview.toggle-analysis.disable');
|
: _('file-preview.toggle-analysis.disable');
|
||||||
|
|
||||||
this.buttons = this._buttons;
|
this.buttons = this.#buttons;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,21 @@
|
|||||||
<ng-container *ngIf="dossierStats$ | async as stats">
|
@if (dossierStats$ | async; as stats) {
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="iqser:document"></mat-icon>
|
<mat-icon svgIcon="iqser:document"></mat-icon>
|
||||||
<span>{{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: stats.numberOfFiles } }}</span>
|
<span>{{ 'dossier-overview.dossier-details.stats.documents' | translate: { count: stats.numberOfFiles } }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="stats.numberOfProcessingFiles">
|
@if (stats.numberOfProcessingFiles) {
|
||||||
|
<div>
|
||||||
<mat-icon svgIcon="red:reanalyse"></mat-icon>
|
<mat-icon svgIcon="red:reanalyse"></mat-icon>
|
||||||
<span>{{
|
<span>{{
|
||||||
'dossier-overview.dossier-details.stats.processing-documents' | translate: { count: stats.numberOfProcessingFiles }
|
'dossier-overview.dossier-details.stats.processing-documents' | translate: { count: stats.numberOfProcessingFiles }
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:user"></mat-icon>
|
<mat-icon svgIcon="red:user"></mat-icon>
|
||||||
<span>{{ 'dossier-overview.dossier-details.stats.people' | translate : { count: dossier.memberIds.length } }}</span>
|
<span>{{ 'dossier-overview.dossier-details.stats.people' | translate: { count: dossier().memberIds.length } }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -21,61 +23,75 @@
|
|||||||
<span>{{ 'dossier-overview.dossier-details.stats.analysed-pages' | translate: { count: stats.numberOfPages | number } }}</span>
|
<span>{{ 'dossier-overview.dossier-details.stats.analysed-pages' | translate: { count: stats.numberOfPages | number } }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="dossier.date | date : 'd MMM yyyy' as date">
|
@if (dossier().date | date: 'd MMM yyyy'; as date) {
|
||||||
|
<div>
|
||||||
<mat-icon svgIcon="iqser:calendar"></mat-icon>
|
<mat-icon svgIcon="iqser:calendar"></mat-icon>
|
||||||
<span [innerHTML]="'dossier-overview.dossier-details.stats.created-on' | translate: { date }"></span>
|
<span [innerHTML]="'dossier-overview.dossier-details.stats.created-on' | translate: { date }"></span>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div *ngIf="dossier.dueDate | date : 'd MMM yyyy' as dueDate">
|
@if (dossier().dueDate | date: 'd MMM yyyy'; as dueDate) {
|
||||||
|
<div>
|
||||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||||
<span [innerHTML]="'dossier-overview.dossier-details.stats.due-date' | translate: { date: dueDate }"></span>
|
<span [innerHTML]="'dossier-overview.dossier-details.stats.due-date' | translate: { date: dueDate }"></span>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:template"></mat-icon>
|
<mat-icon svgIcon="red:template"></mat-icon>
|
||||||
<span>{{ dossierTemplateName }} </span>
|
<span>{{ dossierTemplateName }} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (!isDocumine) {
|
||||||
<div
|
<div
|
||||||
(click)="openEditDossierDialog('dossierDictionary')"
|
(click)="openEditDossierDialog('dossierDictionary')"
|
||||||
*ngIf="!isDocumine"
|
|
||||||
[attr.help-mode-key]="'edit_dossier_dossier_dictionary'"
|
[attr.help-mode-key]="'edit_dossier_dossier_dictionary'"
|
||||||
class="link-property"
|
class="link-property"
|
||||||
>
|
>
|
||||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||||
<span>{{ 'dossier-overview.dossier-details.dictionary' | translate }} </span>
|
<span>{{ 'dossier-overview.dossier-details.dictionary' | translate }} </span>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!--TODO: Navigate to trash with filter on click?-->
|
<!--TODO: Navigate to trash with filter on click?-->
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="iqser:trash"></mat-icon>
|
<mat-icon svgIcon="iqser:trash"></mat-icon>
|
||||||
<span>{{ 'dossier-overview.dossier-details.stats.deleted' | translate: { count: stats.numberOfSoftDeletedFiles } }}</span>
|
<span>{{ 'dossier-overview.dossier-details.stats.deleted' | translate: { count: stats.numberOfSoftDeletedFiles } }}</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
|
||||||
<ng-container *ngIf="dossierAttributes?.length">
|
@if (dossierAttributes()?.length) {
|
||||||
|
@if (!attributesExpanded) {
|
||||||
<div
|
<div
|
||||||
(click)="attributesExpanded = true"
|
(click)="attributesExpanded = true"
|
||||||
*ngIf="!attributesExpanded"
|
|
||||||
[attr.help-mode-key]="'edit_dossier_dossier_attributes'"
|
[attr.help-mode-key]="'edit_dossier_dossier_attributes'"
|
||||||
class="all-caps-label show-attributes"
|
class="all-caps-label show-attributes"
|
||||||
>
|
>
|
||||||
{{ 'dossier-overview.dossier-details.attributes.expand' | translate : { count: dossierAttributes.length } }}
|
{{ 'dossier-overview.dossier-details.attributes.expand' | translate: { count: dossierAttributes().length } }}
|
||||||
</div>
|
</div>
|
||||||
|
} @else {
|
||||||
<div *ngIf="attributesExpanded" [attr.help-mode-key]="'edit_dossier_dossier_attributes'" class="attributes">
|
<div [attr.help-mode-key]="'edit_dossier_dossier_attributes'" class="attributes">
|
||||||
<div (click)="openEditDossierDialog('dossierAttributes')" *ngFor="let attr of dossierAttributes" class="link-property">
|
@for (attr of dossierAttributes(); track attr.id) {
|
||||||
|
<div (click)="openEditDossierDialog('dossierAttributes')" class="link-property">
|
||||||
<mat-icon svgIcon="red:attribute"></mat-icon>
|
<mat-icon svgIcon="red:attribute"></mat-icon>
|
||||||
<span *ngIf="!attr.value"> {{ attr.label + ': -' }}</span>
|
@if (!attr.value) {
|
||||||
<span *ngIf="attr.value && attr.type === 'DATE'"> {{ attr.label + ': ' + (attr.value | date : 'd MMM yyyy') }}</span>
|
<span> {{ attr.label + ': -' }}</span>
|
||||||
<span *ngIf="attr.value && attr.type === 'IMAGE'">
|
}
|
||||||
{{ attr.label + ': ' + ('dossier-overview.dossier-details.attributes.image-uploaded' | translate) }}</span
|
@if (attr.value && attr.type === 'DATE') {
|
||||||
>
|
<span> {{ attr.label + ': ' + (attr.value | date: 'd MMM yyyy') }}</span>
|
||||||
<span *ngIf="attr.value && (attr.type === 'TEXT' || attr.type === 'NUMBER')"> {{ attr.label + ': ' + attr.value }}</span>
|
}
|
||||||
|
@if (attr.value && attr.type === 'IMAGE') {
|
||||||
|
<span> {{ attr.label + ': ' + ('dossier-overview.dossier-details.attributes.image-uploaded' | translate) }}</span>
|
||||||
|
}
|
||||||
|
@if (attr.value && (attr.type === 'TEXT' || attr.type === 'NUMBER')) {
|
||||||
|
<span> {{ attr.label + ': ' + attr.value }}</span>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div (click)="attributesExpanded = false" class="all-caps-label hide-attributes">
|
<div (click)="attributesExpanded = false" class="all-caps-label hide-attributes">
|
||||||
{{ 'dossier-overview.dossier-details.attributes.show-less' | translate }}
|
{{ 'dossier-overview.dossier-details.attributes.show-less' | translate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { AsyncPipe, DecimalPipe, NgForOf, NgIf } from '@angular/common';
|
import { AsyncPipe, DecimalPipe } from '@angular/common';
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, input, OnInit, untracked } from '@angular/core';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { getConfig, largeDialogConfig } from '@iqser/common-ui';
|
import { getConfig, largeDialogConfig } from '@iqser/common-ui';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -17,11 +17,11 @@ import { DossiersDialogService } from '../../../shared-dossiers/services/dossier
|
|||||||
templateUrl: './dossier-details-stats.component.html',
|
templateUrl: './dossier-details-stats.component.html',
|
||||||
styleUrls: ['./dossier-details-stats.component.scss'],
|
styleUrls: ['./dossier-details-stats.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [MatIcon, NgIf, AsyncPipe, TranslateModule, DatePipe, DecimalPipe, NgForOf],
|
imports: [MatIcon, AsyncPipe, TranslateModule, DatePipe, DecimalPipe],
|
||||||
})
|
})
|
||||||
export class DossierDetailsStatsComponent implements OnInit {
|
export class DossierDetailsStatsComponent implements OnInit {
|
||||||
@Input() dossierAttributes: DossierAttributeWithValue[];
|
readonly dossierAttributes = input<DossierAttributeWithValue[]>();
|
||||||
@Input() dossier: Dossier;
|
readonly dossier = input<Dossier>();
|
||||||
|
|
||||||
attributesExpanded = false;
|
attributesExpanded = false;
|
||||||
dossierTemplateName: string;
|
dossierTemplateName: string;
|
||||||
@ -36,14 +36,16 @@ export class DossierDetailsStatsComponent implements OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dossierStats$ = this._dossierStatsService.watch$(this.dossier.id);
|
const dossier = untracked(this.dossier);
|
||||||
this.dossierTemplateName = this._dossierTemplatesService.find(this.dossier.dossierTemplateId)?.name || '-';
|
this.dossierStats$ = this._dossierStatsService.watch$(dossier.id);
|
||||||
|
this.dossierTemplateName = this._dossierTemplatesService.find(dossier.dossierTemplateId)?.name || '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
openEditDossierDialog(section: string): void {
|
openEditDossierDialog(section: string): void {
|
||||||
const data = { dossierId: this.dossier.id, section };
|
const dossier = untracked(this.dossier);
|
||||||
|
const data = { dossierId: dossier.id, section };
|
||||||
this._dialogService.open(EditDossierDialogComponent, data, { ...largeDialogConfig, width: '98vw', maxWidth: '98vw' }, async () => {
|
this._dialogService.open(EditDossierDialogComponent, data, { ...largeDialogConfig, width: '98vw', maxWidth: '98vw' }, async () => {
|
||||||
await firstValueFrom(this._filesService.loadAll(this.dossier.id));
|
await firstValueFrom(this._filesService.loadAll(dossier.id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<ng-container *ngIf="componentContext$ | async as ctx">
|
@if (componentContext$ | async; as ctx) {
|
||||||
<div class="collapsed-wrapper">
|
<div class="collapsed-wrapper">
|
||||||
<ng-container *ngTemplateOutlet="collapsible; context: { action: 'expand', tooltip: (expandTooltip | translate) }"></ng-container>
|
<ng-container *ngTemplateOutlet="collapsible; context: { action: 'expand', tooltip: (expandTooltip | translate) }"></ng-container>
|
||||||
<div class="all-caps-label" translate="dossier-details.title"></div>
|
<div class="all-caps-label" translate="dossier-details.title"></div>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="mt-24">
|
<div class="mt-24">
|
||||||
<div class="all-caps-label" translate="dossier-details.owner"></div>
|
<div class="all-caps-label" translate="dossier-details.owner"></div>
|
||||||
<div class="mt-12 flex">
|
<div class="mt-12 flex">
|
||||||
<ng-container *ngIf="!editingOwner; else editOwner">
|
@if (!editingOwner) {
|
||||||
<iqser-initials-avatar [user]="ctx.dossier?.ownerId" [withName]="true" color="gray" size="large"></iqser-initials-avatar>
|
<iqser-initials-avatar [user]="ctx.dossier?.ownerId" [withName]="true" color="gray" size="large"></iqser-initials-avatar>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
@ -25,7 +25,14 @@
|
|||||||
[tooltip]="'dossier-details.edit-owner' | translate"
|
[tooltip]="'dossier-details.edit-owner' | translate"
|
||||||
class="ml-14"
|
class="ml-14"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
</ng-container>
|
} @else {
|
||||||
|
<redaction-assign-user-dropdown
|
||||||
|
(cancel)="editingOwner = false"
|
||||||
|
(save)="editingOwner = false; assignOwner($event, ctx.dossier)"
|
||||||
|
[options]="managers()"
|
||||||
|
[value]="ctx.dossier?.ownerId"
|
||||||
|
></redaction-assign-user-dropdown>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -38,8 +45,9 @@
|
|||||||
></redaction-team-members>
|
></redaction-team-members>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="ctx.dossierStats as stats">
|
@if (ctx.dossierStats; as stats) {
|
||||||
<div *ngIf="stats.hasFiles" class="mt-24">
|
@if (stats.hasFiles) {
|
||||||
|
<div class="mt-24">
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
(subtitleChanged)="onSubtitleChanged($event)"
|
(subtitleChanged)="onSubtitleChanged($event)"
|
||||||
[config]="chartConfig"
|
[config]="chartConfig"
|
||||||
@ -54,53 +62,46 @@
|
|||||||
direction="row"
|
direction="row"
|
||||||
></redaction-donut-chart>
|
></redaction-donut-chart>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
<div *ngIf="ctx.statusConfig as statusConfig" class="mt-24">
|
@if (ctx.statusConfig; as statusConfig) {
|
||||||
|
<div class="mt-24">
|
||||||
<div class="all-caps-label mb-8" translate="dossier-details.document-status"></div>
|
<div class="all-caps-label mb-8" translate="dossier-details.document-status"></div>
|
||||||
|
@for (config of statusConfig; track config.id) {
|
||||||
<iqser-progress-bar
|
<iqser-progress-bar
|
||||||
*ngFor="let config of statusConfig"
|
|
||||||
[attr.help-mode-key]="'dashboard_in_dossier'"
|
[attr.help-mode-key]="'dashboard_in_dossier'"
|
||||||
[config]="config"
|
[config]="config"
|
||||||
filterKey="processingTypeFilters"
|
filterKey="processingTypeFilters"
|
||||||
></iqser-progress-bar>
|
></iqser-progress-bar>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div
|
@if (stats.hasFiles && !isDocumine && ctx.needsWorkFilters; as filters) {
|
||||||
*ngIf="stats.hasFiles && !isDocumine && ctx.needsWorkFilters as filters"
|
<div [attr.help-mode-key]="'dashboard_in_dossier'" class="mt-32 legend pb-32">
|
||||||
[attr.help-mode-key]="'dashboard_in_dossier'"
|
@for (filter of filters; track filter.id) {
|
||||||
class="mt-32 legend pb-32"
|
<div (click)="filterService.toggleFilter('needsWorkFilters', filter.id)" [class.active]="filter.checked">
|
||||||
>
|
<redaction-type-filter
|
||||||
<div
|
[dossierTemplateId]="ctx.dossier?.dossierTemplateId"
|
||||||
(click)="filterService.toggleFilter('needsWorkFilters', filter.id)"
|
[filter]="filter"
|
||||||
*ngFor="let filter of filters"
|
></redaction-type-filter>
|
||||||
[class.active]="filter.checked"
|
|
||||||
>
|
|
||||||
<redaction-type-filter [dossierTemplateId]="ctx.dossier?.dossierTemplateId" [filter]="filter"></redaction-type-filter>
|
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
<div [class.mt-24]="!stats.hasFiles" class="pb-32">
|
<div [class.mt-24]="!stats.hasFiles" class="pb-32">
|
||||||
<redaction-dossier-details-stats
|
<redaction-dossier-details-stats
|
||||||
[dossierAttributes]="dossierAttributes"
|
[dossierAttributes]="dossierAttributes()"
|
||||||
[dossier]="ctx.dossier"
|
[dossier]="ctx.dossier"
|
||||||
></redaction-dossier-details-stats>
|
></redaction-dossier-details-stats>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
@if (ctx.dossier?.description; as description) {
|
||||||
<div *ngIf="ctx.dossier?.description as description" class="pb-32">
|
<div class="pb-32">
|
||||||
<div class="heading" translate="dossier-overview.dossier-details.description"></div>
|
<div class="heading" translate="dossier-overview.dossier-details.description"></div>
|
||||||
<div class="mt-8">{{ description }}</div>
|
<div class="mt-8">{{ description }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
<ng-template #editOwner>
|
}
|
||||||
<redaction-assign-user-dropdown
|
|
||||||
(cancel)="editingOwner = false"
|
|
||||||
(save)="editingOwner = false; assignOwner($event, ctx.dossier)"
|
|
||||||
[options]="managers"
|
|
||||||
[value]="ctx.dossier?.ownerId"
|
|
||||||
></redaction-assign-user-dropdown>
|
|
||||||
</ng-template>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-template #collapsible let-action="action" let-tooltip="tooltip">
|
<ng-template #collapsible let-action="action" let-tooltip="tooltip">
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { AsyncPipe, NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
|
import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, computed, input, output } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import {
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
@ -55,14 +55,12 @@ interface DossierDetailsContext {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
NgTemplateOutlet,
|
NgTemplateOutlet,
|
||||||
NgIf,
|
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
IqserAllowDirective,
|
IqserAllowDirective,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
DonutChartComponent,
|
DonutChartComponent,
|
||||||
IqserLoadingModule,
|
IqserLoadingModule,
|
||||||
NgForOf,
|
|
||||||
TypeFilterComponent,
|
TypeFilterComponent,
|
||||||
DossierDetailsStatsComponent,
|
DossierDetailsStatsComponent,
|
||||||
AssignUserDropdownComponent,
|
AssignUserDropdownComponent,
|
||||||
@ -74,8 +72,8 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
|
|||||||
#currentChartSubtitleIndex = 0;
|
#currentChartSubtitleIndex = 0;
|
||||||
readonly #dossierId = getParam(DOSSIER_ID);
|
readonly #dossierId = getParam(DOSSIER_ID);
|
||||||
protected readonly circleButtonTypes = CircleButtonTypes;
|
protected readonly circleButtonTypes = CircleButtonTypes;
|
||||||
@Input() dossierAttributes: DossierAttributeWithValue[];
|
readonly dossierAttributes = input<DossierAttributeWithValue[]>();
|
||||||
@Output() readonly toggleCollapse = new EventEmitter();
|
readonly toggleCollapse = output();
|
||||||
editingOwner = false;
|
editingOwner = false;
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
@ -84,6 +82,12 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
|
|||||||
chartConfig: DonutChartConfig[] = [];
|
chartConfig: DonutChartConfig[] = [];
|
||||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
readonly IqserTooltipPositions = IqserTooltipPositions;
|
readonly IqserTooltipPositions = IqserTooltipPositions;
|
||||||
|
readonly managers = computed(() =>
|
||||||
|
this._userService
|
||||||
|
.allUsers()
|
||||||
|
.filter(u => u.isManager)
|
||||||
|
.map(u => u.id),
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
@ -113,10 +117,6 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get managers() {
|
|
||||||
return this._userService.all.filter(u => u.isManager).map(u => u.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async assignOwner(user: User | string, dossier: Dossier) {
|
async assignOwner(user: User | string, dossier: Dossier) {
|
||||||
const owner = typeof user === 'string' ? this._userService.find(user) : user;
|
const owner = typeof user === 'string' ? this._userService.find(user) : user;
|
||||||
const dossierRequest: IDossierRequest = { ...dossier, ownerId: owner.id };
|
const dossierRequest: IDossierRequest = { ...dossier, ownerId: owner.id };
|
||||||
|
|||||||
@ -1,58 +1,61 @@
|
|||||||
<ng-container *ngIf="configService.listingMode$ | async as mode">
|
@if (configService.listingMode$ | async; as mode) {
|
||||||
<div
|
<div
|
||||||
(click)="handleFieldClick($event)"
|
(click)="handleFieldClick($event)"
|
||||||
(mousedown)="handleClick($event)"
|
(mousedown)="handleClick($event)"
|
||||||
[ngClass]="{ 'workflow-attribute': mode === 'workflow', 'file-name-column': fileNameColumn }"
|
[ngClass]="{ 'workflow-attribute': mode === 'workflow', 'file-name-column': fileNameColumn() }"
|
||||||
class="file-attribute"
|
class="file-attribute"
|
||||||
>
|
>
|
||||||
<div [ngClass]="{ 'workflow-value': mode === 'workflow' }" class="value" *ngIf="!isInEditMode || mode === 'workflow'">
|
@if (!isInEditMode || mode === 'workflow') {
|
||||||
<mat-icon *ngIf="!fileAttribute.editable" [matTooltip]="'readonly' | translate" svgIcon="red:read-only"></mat-icon>
|
<div [ngClass]="{ 'workflow-value': mode === 'workflow' }" class="value">
|
||||||
|
@if (!fileAttribute().editable) {
|
||||||
|
<mat-icon [matTooltip]="'readonly' | translate" svgIcon="red:read-only"></mat-icon>
|
||||||
|
}
|
||||||
|
@if (!isDate()) {
|
||||||
<span
|
<span
|
||||||
*ngIf="!isDate; else date"
|
|
||||||
[style.max-width]="attributeValueWidth()"
|
[style.max-width]="attributeValueWidth()"
|
||||||
[matTooltip]="fileAttributeValue"
|
[matTooltip]="value()"
|
||||||
[ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }"
|
[ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }"
|
||||||
>
|
>
|
||||||
<b *ngIf="mode === 'workflow' && !isInEditMode"> {{ fileAttribute.label }}: </b>
|
@if (mode === 'workflow' && !isInEditMode) {
|
||||||
{{ fileAttributeValue || '-' }}
|
<b> {{ fileAttribute().label }}: </b>
|
||||||
|
}
|
||||||
|
{{ value() || '-' }}
|
||||||
</span>
|
</span>
|
||||||
<ng-template #date>
|
} @else {
|
||||||
<span [ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }">
|
<span [ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }">
|
||||||
<b *ngIf="mode === 'workflow' && !isInEditMode"> {{ fileAttribute.label }}: </b>
|
@if (mode === 'workflow' && !isInEditMode) {
|
||||||
{{ fileAttributeValue ? (fileAttributeValue | date: 'd MMM yyyy') : '-' }}
|
<b> {{ fileAttribute().label }}: </b>
|
||||||
|
}
|
||||||
|
{{ value() ? (value() | date: 'd MMM yyyy') : '-' }}
|
||||||
</span>
|
</span>
|
||||||
</ng-template>
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<ng-container
|
@if (
|
||||||
*ngIf="
|
|
||||||
(fileAttributesService.isEditingFileAttribute() === false || isInEditMode) &&
|
(fileAttributesService.isEditingFileAttribute() === false || isInEditMode) &&
|
||||||
!file.isInitialProcessing &&
|
!file().isInitialProcessing &&
|
||||||
permissionsService.canEditFileAttributes(file, dossier) &&
|
permissionsService.canEditFileAttributes(file(), dossier()) &&
|
||||||
fileAttribute.editable
|
fileAttribute().editable
|
||||||
"
|
) {
|
||||||
>
|
@if (!isInEditMode) {
|
||||||
<div
|
<div
|
||||||
(click)="editFileAttribute($event)"
|
(click)="editFileAttribute($event)"
|
||||||
*ngIf="!isInEditMode; else input"
|
|
||||||
[attr.help-mode-key]="'edit_file_attributes'"
|
[attr.help-mode-key]="'edit_file_attributes'"
|
||||||
[class.help-mode-button]="helpModeService.isHelpModeActive$ | async"
|
[class.help-mode-button]="helpModeService.isHelpModeActive$ | async"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'workflow-edit-button': mode === 'workflow',
|
'workflow-edit-button': mode === 'workflow',
|
||||||
'action-buttons edit-button': !fileNameColumn,
|
'action-buttons edit-button': !fileNameColumn(),
|
||||||
'filename-edit-button': fileNameColumn,
|
'filename-edit-button': fileNameColumn(),
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div [ngClass]="{ 'workflow-edit-icon': mode === 'workflow', 'edit-icon': !fileNameColumn }">
|
<div [ngClass]="{ 'workflow-edit-icon': mode === 'workflow', 'edit-icon': !fileNameColumn() }">
|
||||||
<mat-icon [svgIcon]="'iqser:edit'"></mat-icon>
|
<mat-icon [svgIcon]="'iqser:edit'"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
} @else {
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-template #input>
|
|
||||||
<div
|
<div
|
||||||
[ngClass]="{ 'workflow-edit-input': mode === 'workflow', 'file-name-column-input': fileNameColumn }"
|
[ngClass]="{ 'workflow-edit-input': mode === 'workflow', 'file-name-column-input': fileNameColumn() }"
|
||||||
class="edit-input"
|
class="edit-input"
|
||||||
iqserStopPropagation
|
iqserStopPropagation
|
||||||
>
|
>
|
||||||
@ -62,28 +65,30 @@
|
|||||||
(keyup.enter)="form.valid && save()"
|
(keyup.enter)="form.valid && save()"
|
||||||
(keydown.escape)="close()"
|
(keydown.escape)="close()"
|
||||||
[style.width]="inputFieldWidth()"
|
[style.width]="inputFieldWidth()"
|
||||||
[formControlName]="fileAttribute.id"
|
[formControlName]="fileAttribute().id"
|
||||||
[id]="fileAttribute.id"
|
[id]="fileAttribute().id"
|
||||||
[ngClass]="{ 'workflow-input': mode === 'workflow' || fileNameColumn, 'file-name-input': fileNameColumn }"
|
[ngClass]="{ 'workflow-input': mode === 'workflow' || fileNameColumn(), 'file-name-input': fileNameColumn() }"
|
||||||
[type]="fileAttribute.type"
|
[type]="fileAttribute().type"
|
||||||
></iqser-dynamic-input>
|
></iqser-dynamic-input>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
[icon]="'iqser:check'"
|
[icon]="'iqser:check'"
|
||||||
[size]="mode === 'workflow' || fileNameColumn ? 15 : 34"
|
[size]="mode === 'workflow' || fileNameColumn() ? 15 : 34"
|
||||||
[ngClass]="{ 'file-name-btn': fileNameColumn }"
|
[ngClass]="{ 'file-name-btn': fileNameColumn() }"
|
||||||
class="save"
|
class="save"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="close()"
|
(action)="close()"
|
||||||
[ngClass]="{ 'file-name-btn': fileNameColumn }"
|
[ngClass]="{ 'file-name-btn': fileNameColumn() }"
|
||||||
[icon]="'iqser:close'"
|
[icon]="'iqser:close'"
|
||||||
[size]="mode === 'workflow' || fileNameColumn ? 15 : 34"
|
[size]="mode === 'workflow' || fileNameColumn() ? 15 : 34"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
}
|
||||||
</ng-container>
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { AsyncPipe, NgClass, NgIf, NgTemplateOutlet } from '@angular/common';
|
import { AsyncPipe, NgClass, NgTemplateOutlet } from '@angular/common';
|
||||||
import { Component, computed, effect, HostListener, input, Input, OnDestroy } from '@angular/core';
|
import { Component, computed, effect, HostListener, input, untracked } from '@angular/core';
|
||||||
import { AbstractControl, FormBuilder, FormsModule, ReactiveFormsModule, UntypedFormGroup, ValidatorFn } from '@angular/forms';
|
import { AbstractControl, FormBuilder, FormsModule, ReactiveFormsModule, UntypedFormGroup, ValidatorFn } from '@angular/forms';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { NavigationEnd, Router } from '@angular/router';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { DynamicInputComponent } from '@common-ui/inputs/dynamic-input/dynamic-input.component';
|
import { DynamicInputComponent } from '@common-ui/inputs/dynamic-input/dynamic-input.component';
|
||||||
import {
|
import {
|
||||||
@ -22,9 +21,9 @@ import { FilesService } from '@services/files/files.service';
|
|||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { DatePipe } from '@shared/pipes/date.pipe';
|
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { firstValueFrom, Subscription } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { filter, map, tap } from 'rxjs/operators';
|
|
||||||
import { ConfigService } from '../../config.service';
|
import { ConfigService } from '../../config.service';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-file-attribute',
|
selector: 'redaction-file-attribute',
|
||||||
@ -32,7 +31,6 @@ import { ConfigService } from '../../config.service';
|
|||||||
styleUrls: ['./file-attribute.component.scss'],
|
styleUrls: ['./file-attribute.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
NgIf,
|
|
||||||
NgClass,
|
NgClass,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
@ -47,31 +45,33 @@ import { ConfigService } from '../../config.service';
|
|||||||
StopPropagationDirective,
|
StopPropagationDirective,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class FileAttributeComponent extends BaseFormComponent implements OnDestroy {
|
export class FileAttributeComponent extends BaseFormComponent {
|
||||||
readonly #subscriptions = new Subscription();
|
|
||||||
isInEditMode = false;
|
isInEditMode = false;
|
||||||
closedDatepicker = true;
|
closedDatepicker = true;
|
||||||
@Input({ required: true }) fileAttribute!: IFileAttributeConfig;
|
readonly fileAttribute = input.required<IFileAttributeConfig>();
|
||||||
@Input({ required: true }) file!: File;
|
readonly file = input.required<File>();
|
||||||
|
readonly dossier = input<Dossier>();
|
||||||
|
readonly fileNameColumn = input(false);
|
||||||
|
readonly width = input<number>();
|
||||||
|
|
||||||
|
readonly isText = computed(() => this.fileAttribute().type === FileAttributeConfigTypes.TEXT);
|
||||||
|
readonly value = computed(() => this.file().fileAttributes.attributeIdToValue[this.fileAttribute().id]);
|
||||||
|
readonly isDate = computed(() => this.fileAttribute().type === FileAttributeConfigTypes.DATE);
|
||||||
|
|
||||||
|
readonly #selectedLength = toSignal(this._listingService.selectedLength$);
|
||||||
readonly #shouldClose = computed(
|
readonly #shouldClose = computed(
|
||||||
() =>
|
() =>
|
||||||
(this.fileAttributesService.isEditingFileAttribute() &&
|
(this.fileAttributesService.isEditingFileAttribute() &&
|
||||||
this.file &&
|
(this.fileAttribute().id !== this.fileAttributesService.openAttributeEdit() ||
|
||||||
this.fileAttribute &&
|
this.file().fileId !== this.fileAttributesService.fileEdit())) ||
|
||||||
(this.fileAttribute.id !== this.fileAttributesService.openAttributeEdit() ||
|
|
||||||
this.file.fileId !== this.fileAttributesService.fileEdit())) ||
|
|
||||||
!this.fileAttributesService.openAttributeEdits().length,
|
!this.fileAttributesService.openAttributeEdits().length,
|
||||||
);
|
);
|
||||||
@Input({ required: true }) dossier!: Dossier;
|
|
||||||
@Input() fileNameColumn = false;
|
|
||||||
readonlyAttrs: string[] = [];
|
readonlyAttrs: string[] = [];
|
||||||
readonly width = input<number>();
|
|
||||||
readonly inputFormWidth = computed(() => (this.width() ? this.width() + 'px' : 'unset'));
|
readonly inputFormWidth = computed(() => (this.width() ? this.width() + 'px' : 'unset'));
|
||||||
readonly inputFieldWidth = computed(() => (this.width() ? this.width() - 50 + 'px' : 'unset'));
|
readonly inputFieldWidth = computed(() => (this.width() ? this.width() - 50 + 'px' : 'unset'));
|
||||||
readonly attributeValueWidth = computed(() => (this.width() ? `${this.width() * 0.9}px` : 'unset'));
|
readonly attributeValueWidth = computed(() => (this.width() ? `${this.width() * 0.9}px` : 'unset'));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
router: Router,
|
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _filesService: FilesService,
|
private readonly _filesService: FilesService,
|
||||||
@ -82,14 +82,11 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
readonly configService: ConfigService,
|
readonly configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
const sub = router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => this.close());
|
effect(() => {
|
||||||
this.#subscriptions.add(sub);
|
if (this.#selectedLength()) {
|
||||||
|
this.close();
|
||||||
const sub2 = this._listingService.selectedLength$.pipe(
|
}
|
||||||
map(selectedLength => !!selectedLength),
|
});
|
||||||
tap(() => this.close()),
|
|
||||||
);
|
|
||||||
this.#subscriptions.add(sub2.subscribe());
|
|
||||||
|
|
||||||
effect(
|
effect(
|
||||||
() => {
|
() => {
|
||||||
@ -101,27 +98,11 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDate(): boolean {
|
|
||||||
return this.fileAttribute.type === FileAttributeConfigTypes.DATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isNumber(): boolean {
|
|
||||||
return this.fileAttribute.type === FileAttributeConfigTypes.NUMBER;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isText(): boolean {
|
|
||||||
return this.fileAttribute.type === FileAttributeConfigTypes.TEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get fileAttributeValue(): string {
|
|
||||||
return this.file.fileAttributes.attributeIdToValue[this.fileAttribute.id];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Debounce(60)
|
@Debounce(60)
|
||||||
@HostListener('document:click', ['$event'])
|
@HostListener('document:click', ['$event'])
|
||||||
clickOutside($event: MouseEvent) {
|
clickOutside($event: MouseEvent) {
|
||||||
const clickCalendarCell = ($event.target as HTMLElement).classList?.contains('mat-calendar-body-cell-content');
|
const clickCalendarCell = ($event.target as HTMLElement).classList?.contains('mat-calendar-body-cell-content');
|
||||||
if (this.isDate) {
|
if (this.isDate()) {
|
||||||
this.#focusOnEditInput();
|
this.#focusOnEditInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,13 +116,9 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.#subscriptions.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFieldClick($event: MouseEvent) {
|
handleFieldClick($event: MouseEvent) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
if (!this.fileNameColumn) {
|
if (!this.fileNameColumn()) {
|
||||||
this.editFileAttribute($event);
|
this.editFileAttribute($event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,34 +126,34 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
editFileAttribute($event: MouseEvent) {
|
editFileAttribute($event: MouseEvent) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
if (
|
if (
|
||||||
!this.file.isInitialProcessing &&
|
!this.file().isInitialProcessing &&
|
||||||
this.permissionsService.canEditFileAttributes(this.file, this.dossier) &&
|
this.permissionsService.canEditFileAttributes(this.file(), this.dossier()) &&
|
||||||
this.fileAttribute.editable &&
|
this.fileAttribute().editable &&
|
||||||
!this.isInEditMode
|
!this.isInEditMode
|
||||||
) {
|
) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this.fileAttributesService.openAttributeEdits.update(value => [
|
this.fileAttributesService.openAttributeEdits.update(value => [
|
||||||
...value,
|
...value,
|
||||||
{ attribute: this.fileAttribute.id, file: this.file.id },
|
{ attribute: this.fileAttribute().id, file: this.file().id },
|
||||||
]);
|
]);
|
||||||
this.fileAttributesService.setFileEdit(this.file.fileId);
|
this.fileAttributesService.setFileEdit(this.file().fileId);
|
||||||
this.fileAttributesService.setOpenAttributeEdit(this.fileAttribute.id);
|
this.fileAttributesService.setOpenAttributeEdit(this.fileAttribute().id);
|
||||||
this.#toggleEdit();
|
this.#toggleEdit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
const rawFormValue = this.form.getRawValue();
|
const rawFormValue = this.form.getRawValue();
|
||||||
const fileAttrValue = rawFormValue[this.fileAttribute.id];
|
const fileAttrValue = rawFormValue[this.fileAttribute().id];
|
||||||
const attributeIdToValue = {
|
const attributeIdToValue = {
|
||||||
...this.#getForm().getRawValue(),
|
...this.#getForm().getRawValue(),
|
||||||
[this.fileAttribute.id]: this.#formatAttributeValue(fileAttrValue),
|
[this.fileAttribute().id]: this.#formatAttributeValue(fileAttrValue),
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await firstValueFrom(
|
await firstValueFrom(
|
||||||
this.fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId),
|
this.fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file().dossierId, this.file().fileId),
|
||||||
);
|
);
|
||||||
await this._filesService.reload(this.file.dossierId, this.file);
|
await this._filesService.reload(this.file().dossierId, this.file());
|
||||||
this.initialFormValue = rawFormValue;
|
this.initialFormValue = rawFormValue;
|
||||||
this._toaster.success(_('file-attribute.update.success'));
|
this._toaster.success(_('file-attribute.update.success'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -188,6 +165,8 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
if (this.isInEditMode) {
|
if (this.isInEditMode) {
|
||||||
|
const fileAttribute = untracked(this.fileAttribute);
|
||||||
|
const file = untracked(this.file);
|
||||||
this.form = this.#getForm();
|
this.form = this.#getForm();
|
||||||
this.#toggleEdit();
|
this.#toggleEdit();
|
||||||
this.fileAttributesService.openAttributeEdits.update(value =>
|
this.fileAttributesService.openAttributeEdits.update(value =>
|
||||||
@ -195,8 +174,8 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
val =>
|
val =>
|
||||||
JSON.stringify(val) !==
|
JSON.stringify(val) !==
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
attribute: this.fileAttribute.id,
|
attribute: fileAttribute.id,
|
||||||
file: this.file.id,
|
file: file.id,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -204,18 +183,20 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
}
|
}
|
||||||
|
|
||||||
#initFileAttributes() {
|
#initFileAttributes() {
|
||||||
const configs = this.fileAttributesService.getFileAttributeConfig(this.file.dossierTemplateId).fileAttributeConfigs;
|
const file = untracked(this.file);
|
||||||
|
const configs = this.fileAttributesService.getFileAttributeConfig(file.dossierTemplateId).fileAttributeConfigs;
|
||||||
this.readonlyAttrs = configs.filter(config => config.editable === false).map(config => config.id);
|
this.readonlyAttrs = configs.filter(config => config.editable === false).map(config => config.id);
|
||||||
configs.forEach(config => {
|
configs.forEach(config => {
|
||||||
if (!this.file.fileAttributes.attributeIdToValue[config.id]) {
|
if (!file.fileAttributes.attributeIdToValue[config.id]) {
|
||||||
this.file.fileAttributes.attributeIdToValue[config.id] = null;
|
file.fileAttributes.attributeIdToValue[config.id] = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#getForm(): UntypedFormGroup {
|
#getForm(): UntypedFormGroup {
|
||||||
const config = {};
|
const config = {};
|
||||||
const fileAttributes = this.file.fileAttributes.attributeIdToValue;
|
const file = untracked(this.file);
|
||||||
|
const fileAttributes = file.fileAttributes.attributeIdToValue;
|
||||||
Object.keys(fileAttributes).forEach(key => {
|
Object.keys(fileAttributes).forEach(key => {
|
||||||
const attrValue = fileAttributes[key];
|
const attrValue = fileAttributes[key];
|
||||||
config[key] = [
|
config[key] = [
|
||||||
@ -231,19 +212,25 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
}
|
}
|
||||||
|
|
||||||
#checkEmptyInput(): ValidatorFn {
|
#checkEmptyInput(): ValidatorFn {
|
||||||
|
const isText = untracked(this.isText);
|
||||||
|
const fileAttribute = untracked(this.fileAttribute);
|
||||||
|
const value = untracked(this.value);
|
||||||
return (control: AbstractControl) =>
|
return (control: AbstractControl) =>
|
||||||
(this.isText && !control.get(this.fileAttribute.id)?.value?.trim().length && !this.fileAttributeValue) ||
|
(isText && !control.get(fileAttribute.id)?.value?.trim().length && !this.value()) ||
|
||||||
control.get(this.fileAttribute.id)?.value === this.fileAttributeValue
|
control.get(fileAttribute.id)?.value === value
|
||||||
? { emptyString: true }
|
? { emptyString: true }
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#checkDate(): ValidatorFn {
|
#checkDate(): ValidatorFn {
|
||||||
|
const isDate = untracked(this.isDate);
|
||||||
|
const fileAttribute = untracked(this.fileAttribute);
|
||||||
|
const value = untracked(this.value);
|
||||||
return (control: AbstractControl) => {
|
return (control: AbstractControl) => {
|
||||||
const expr = new RegExp('(0?[1-9]|[12][0-9]|3[01])(/|.)(0?[1-9]|1[12])(/|.)\\d{2}');
|
const expr = new RegExp('(0?[1-9]|[12][0-9]|3[01])(/|.)(0?[1-9]|1[12])(/|.)\\d{2}');
|
||||||
return this.isDate
|
return isDate
|
||||||
? (!expr.test(control.get(this.fileAttribute.id)?.value) && control.get(this.fileAttribute.id)?.value?.length) ||
|
? (!expr.test(control.get(fileAttribute.id)?.value) && control.get(fileAttribute.id)?.value?.length) ||
|
||||||
this.#formatAttributeValue(control.get(this.fileAttribute.id)?.value) === this.fileAttributeValue
|
this.#formatAttributeValue(control.get(fileAttribute.id)?.value) === value
|
||||||
? { invalidDate: true }
|
? { invalidDate: true }
|
||||||
: null
|
: null
|
||||||
: null;
|
: null;
|
||||||
@ -251,11 +238,12 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
}
|
}
|
||||||
|
|
||||||
#formatAttributeValue(attrValue) {
|
#formatAttributeValue(attrValue) {
|
||||||
if (this.isDate) {
|
const isDate = untracked(this.isDate);
|
||||||
|
if (isDate) {
|
||||||
return attrValue && dayjs(attrValue).format('YYYY-MM-DD');
|
return attrValue && dayjs(attrValue).format('YYYY-MM-DD');
|
||||||
}
|
}
|
||||||
|
const isText = untracked(this.isText);
|
||||||
if (this.isText) {
|
if (isText) {
|
||||||
return attrValue.trim().replaceAll(/\s\s+/g, ' ');
|
return attrValue.trim().replaceAll(/\s\s+/g, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,9 +265,12 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
}
|
}
|
||||||
|
|
||||||
#focusOnEditInput(): void {
|
#focusOnEditInput(): void {
|
||||||
if (this.isDate || this.isText) {
|
const isDate = untracked(this.isDate);
|
||||||
|
const isText = untracked(this.isText);
|
||||||
|
const fileAttribute = untracked(this.fileAttribute);
|
||||||
|
if (isDate || isText) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const input = document.getElementById(this.fileAttribute.id) as HTMLInputElement;
|
const input = document.getElementById(fileAttribute.id) as HTMLInputElement;
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<iqser-page-header
|
<iqser-page-header
|
||||||
(closeAction)="router.navigate([dossier.dossiersListRouterLink])"
|
(closeAction)="router.navigate([dossier().dossiersListRouterLink])"
|
||||||
[actionConfigs]="actionConfigs"
|
[actionConfigs]="actionConfigs"
|
||||||
[helpModeKey]="'document'"
|
[helpModeKey]="'document'"
|
||||||
[showCloseButton]="true"
|
[showCloseButton]="true"
|
||||||
@ -10,33 +10,35 @@
|
|||||||
[attr.help-mode-key]="isDocumine ? 'dossier_download_dossier' : 'download_dossier_in_dossier'"
|
[attr.help-mode-key]="isDocumine ? 'dossier_download_dossier' : 'download_dossier_in_dossier'"
|
||||||
[buttonId]="'download-files-btn'"
|
[buttonId]="'download-files-btn'"
|
||||||
[disabled]="downloadFilesDisabled$ | async"
|
[disabled]="downloadFilesDisabled$ | async"
|
||||||
[dossier]="dossier"
|
[dossier]="dossier()"
|
||||||
[files]="entitiesService.all$ | async"
|
[files]="entitiesService.all$ | async"
|
||||||
dossierDownload
|
dossierDownload
|
||||||
></redaction-file-download-btn>
|
></redaction-file-download-btn>
|
||||||
|
|
||||||
|
@if (permissionsService.canDownloadCsvReport(dossier())) {
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="downloadDossierAsCSV()"
|
(action)="downloadDossierAsCSV()"
|
||||||
*ngIf="permissionsService.canDownloadCsvReport(dossier)"
|
|
||||||
[attr.help-mode-key]="'download_csv'"
|
[attr.help-mode-key]="'download_csv'"
|
||||||
[disabled]="listingService.areSomeSelected$ | async"
|
[disabled]="listingService.areSomeSelected$ | async"
|
||||||
[icon]="'iqser:csv'"
|
[icon]="'iqser:csv'"
|
||||||
[tooltip]="'dossier-overview.header-actions.download-csv' | translate"
|
[tooltip]="'dossier-overview.header-actions.download-csv' | translate"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (permissionsService.displayReanalyseBtn(dossier())) {
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="reanalyseDossier()"
|
(action)="reanalyseDossier()"
|
||||||
*ngIf="permissionsService.displayReanalyseBtn(dossier)"
|
|
||||||
[disabled]="listingService.areSomeSelected$ | async"
|
[disabled]="listingService.areSomeSelected$ | async"
|
||||||
[icon]="'iqser:refresh'"
|
[icon]="'iqser:refresh'"
|
||||||
[tooltipClass]="'small warn'"
|
[tooltipClass]="'small warn'"
|
||||||
[tooltip]="'dossier-overview.new-rule.toast.actions.reanalyse-all' | translate"
|
[tooltip]="'dossier-overview.new-rule.toast.actions.reanalyse-all' | translate"
|
||||||
[type]="circleButtonTypes.warn"
|
[type]="circleButtonTypes.warn"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (permissionsService.canUploadFiles(dossier())) {
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="upload.emit()"
|
(action)="upload.emit()"
|
||||||
*ngIf="permissionsService.canUploadFiles(dossier)"
|
|
||||||
[attr.help-mode-key]="'upload_document'"
|
[attr.help-mode-key]="'upload_document'"
|
||||||
[buttonId]="'upload-document-btn'"
|
[buttonId]="'upload-document-btn'"
|
||||||
[icon]="'iqser:upload'"
|
[icon]="'iqser:upload'"
|
||||||
@ -44,6 +46,7 @@
|
|||||||
[type]="circleButtonTypes.primary"
|
[type]="circleButtonTypes.primary"
|
||||||
class="ml-14"
|
class="ml-14"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</iqser-page-header>
|
</iqser-page-header>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, input, OnInit, output, untracked } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActionConfig,
|
ActionConfig,
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
@ -25,9 +25,8 @@ import { Router } from '@angular/router';
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
||||||
import { List, some } from '@iqser/common-ui/lib/utils';
|
import { List, some } from '@iqser/common-ui/lib/utils';
|
||||||
import { ComponentLogService } from '@services/files/component-log.service';
|
|
||||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { FileDownloadBtnComponent } from '@shared/components/buttons/file-download-btn/file-download-btn.component';
|
import { FileDownloadBtnComponent } from '@shared/components/buttons/file-download-btn/file-download-btn.component';
|
||||||
import { ViewModeSelectionComponent } from '../view-mode-selection/view-mode-selection.component';
|
import { ViewModeSelectionComponent } from '../view-mode-selection/view-mode-selection.component';
|
||||||
@ -44,7 +43,6 @@ import { ViewModeSelectionComponent } from '../view-mode-selection/view-mode-sel
|
|||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
FileDownloadBtnComponent,
|
FileDownloadBtnComponent,
|
||||||
NgIf,
|
|
||||||
ViewModeSelectionComponent,
|
ViewModeSelectionComponent,
|
||||||
DisableStopPropagationDirective,
|
DisableStopPropagationDirective,
|
||||||
MatMenu,
|
MatMenu,
|
||||||
@ -52,8 +50,8 @@ import { ViewModeSelectionComponent } from '../view-mode-selection/view-mode-sel
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class DossierOverviewScreenHeaderComponent implements OnInit {
|
export class DossierOverviewScreenHeaderComponent implements OnInit {
|
||||||
@Input() dossier: Dossier;
|
readonly dossier = input<Dossier>();
|
||||||
@Output() readonly upload = new EventEmitter<void>();
|
readonly upload = output();
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
actionConfigs: List<ActionConfig>;
|
actionConfigs: List<ActionConfig>;
|
||||||
@ -84,13 +82,14 @@ export class DossierOverviewScreenHeaderComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.actionConfigs = this.configService.actionConfig(this.dossier.id, this.listingService.areSomeSelected$);
|
this.actionConfigs = this.configService.actionConfig(this.dossier().id, this.listingService.areSomeSelected$);
|
||||||
}
|
}
|
||||||
|
|
||||||
async reanalyseDossier() {
|
async reanalyseDossier() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
try {
|
try {
|
||||||
await this._reanalysisService.reanalyzeDossier(this.dossier, true);
|
const dossier = untracked(this.dossier);
|
||||||
|
await this._reanalysisService.reanalyzeDossier(dossier, true);
|
||||||
this._toaster.success(_('dossier-overview.reanalyse-dossier.success'));
|
this._toaster.success(_('dossier-overview.reanalyse-dossier.success'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._toaster.error(_('dossier-overview.reanalyse-dossier.error'));
|
this._toaster.error(_('dossier-overview.reanalyse-dossier.error'));
|
||||||
@ -101,12 +100,13 @@ export class DossierOverviewScreenHeaderComponent implements OnInit {
|
|||||||
async downloadDossierAsCSV() {
|
async downloadDossierAsCSV() {
|
||||||
const displayedEntities = await firstValueFrom(this.listingService.displayed$);
|
const displayedEntities = await firstValueFrom(this.listingService.displayed$);
|
||||||
const entities = this.sortingService.defaultSort(displayedEntities);
|
const entities = this.sortingService.defaultSort(displayedEntities);
|
||||||
const fileName = this.dossier.dossierName + '.export.csv';
|
const dossier = untracked(this.dossier);
|
||||||
|
const fileName = dossier.dossierName + '.export.csv';
|
||||||
const mapper = (file?: File) => ({
|
const mapper = (file?: File) => ({
|
||||||
...file,
|
...file,
|
||||||
hasAnnotations: file.hasRedactions,
|
hasAnnotations: file.hasRedactions,
|
||||||
assignee: this._userService.getName(file.assignee) || '-',
|
assignee: this._userService.getName(file.assignee) || '-',
|
||||||
primaryAttribute: this._primaryFileAttributeService.getPrimaryFileAttributeValue(file, this.dossier.dossierTemplateId),
|
primaryAttribute: this._primaryFileAttributeService.getPrimaryFileAttributeValue(file, this.dossier().dossierTemplateId),
|
||||||
});
|
});
|
||||||
const documineOnlyFields = ['hasAnnotations'];
|
const documineOnlyFields = ['hasAnnotations'];
|
||||||
const redactionOnlyFields = ['hasHints', 'hasImages', 'hasUpdates', 'hasRedactions'];
|
const redactionOnlyFields = ['hasHints', 'hasImages', 'hasUpdates', 'hasRedactions'];
|
||||||
|
|||||||
@ -1,34 +1,43 @@
|
|||||||
<div class="needs-work">
|
<div class="needs-work">
|
||||||
|
@if (file().analysisRequired) {
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
*ngIf="file.analysisRequired"
|
|
||||||
[color]="analysisColor$ | async"
|
[color]="analysisColor$ | async"
|
||||||
[label]="(workloadTranslations['analysis'] | translate)[0]"
|
[label]="(workloadTranslations['analysis'] | translate)[0]"
|
||||||
type="square"
|
type="square"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
}
|
||||||
|
@if (updated()) {
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
*ngIf="updated"
|
|
||||||
[color]="updatedColor$ | async"
|
[color]="updatedColor$ | async"
|
||||||
[label]="(workloadTranslations['updated'] | translate)[0]"
|
[label]="(workloadTranslations['updated'] | translate)[0]"
|
||||||
type="square"
|
type="square"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
}
|
||||||
|
@if (file().hasRedactions) {
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
*ngIf="file.hasRedactions"
|
|
||||||
[color]="redactionColor$ | async"
|
[color]="redactionColor$ | async"
|
||||||
[label]="'redaction-abbreviation' | translate"
|
[label]="'redaction-abbreviation' | translate"
|
||||||
type="square"
|
type="square"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
}
|
||||||
|
@if (file().hasImages) {
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
*ngIf="file.hasImages"
|
|
||||||
[color]="imageColor$ | async"
|
[color]="imageColor$ | async"
|
||||||
[label]="(workloadTranslations['image'] | translate)[0]"
|
[label]="(workloadTranslations['image'] | translate)[0]"
|
||||||
type="square"
|
type="square"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
}
|
||||||
|
@if (file().hintsOnly) {
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
*ngIf="file.hintsOnly"
|
|
||||||
[color]="hintColor$ | async"
|
[color]="hintColor$ | async"
|
||||||
[label]="(workloadTranslations['hint'] | translate)[0]"
|
[label]="(workloadTranslations['hint'] | translate)[0]"
|
||||||
type="circle"
|
type="circle"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
<mat-icon *ngIf="file.hasAnnotationComments" svgIcon="red:comment"></mat-icon>
|
}
|
||||||
<ng-container *ngIf="noWorkloadItems"> -</ng-container>
|
@if (file().hasAnnotationComments) {
|
||||||
|
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||||
|
}
|
||||||
|
@if (noWorkloadItems()) {
|
||||||
|
-
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, computed, input, OnInit } from '@angular/core';
|
||||||
import { annotationDefaultColorConfig, DefaultBasedColorType, File } from '@red/domain';
|
import { annotationDefaultColorConfig, DefaultBasedColorType, File } from '@red/domain';
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
@ -7,7 +7,7 @@ import { UserService } from '@users/user.service';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { AnnotationIconComponent } from '@shared/components/annotation-icon/annotation-icon.component';
|
import { AnnotationIconComponent } from '@shared/components/annotation-icon/annotation-icon.component';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { workloadTranslations } from '@translations/workload-translations';
|
import { workloadTranslations } from '@translations/workload-translations';
|
||||||
@ -18,11 +18,11 @@ import { workloadTranslations } from '@translations/workload-translations';
|
|||||||
styleUrls: ['./file-workload.component.scss'],
|
styleUrls: ['./file-workload.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AnnotationIconComponent, AsyncPipe, TranslateModule, MatIcon, NgIf],
|
imports: [AnnotationIconComponent, AsyncPipe, TranslateModule, MatIcon],
|
||||||
})
|
})
|
||||||
export class FileWorkloadComponent implements OnInit {
|
export class FileWorkloadComponent implements OnInit {
|
||||||
#dossierTemplateId: string;
|
#dossierTemplateId: string;
|
||||||
@Input() file: File;
|
file = input<File>();
|
||||||
imageColor$: Observable<string>;
|
imageColor$: Observable<string>;
|
||||||
updatedColor$: Observable<string>;
|
updatedColor$: Observable<string>;
|
||||||
analysisColor$: Observable<string>;
|
analysisColor$: Observable<string>;
|
||||||
@ -30,6 +30,20 @@ export class FileWorkloadComponent implements OnInit {
|
|||||||
redactionColor$: Observable<string>;
|
redactionColor$: Observable<string>;
|
||||||
readonly workloadTranslations = workloadTranslations;
|
readonly workloadTranslations = workloadTranslations;
|
||||||
|
|
||||||
|
readonly updated = computed(
|
||||||
|
() => this.file().hasUpdates && this.file().assignee === this._userService.currentUser.id && !this.file().isApproved,
|
||||||
|
);
|
||||||
|
|
||||||
|
readonly noWorkloadItems = computed(
|
||||||
|
() =>
|
||||||
|
!this.updated() &&
|
||||||
|
!this.file().analysisRequired &&
|
||||||
|
!this.file().hasRedactions &&
|
||||||
|
!this.file().hasImages &&
|
||||||
|
!this.file().hintsOnly &&
|
||||||
|
!this.file().hasAnnotationComments,
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _defaultColorsService: DefaultColorsService,
|
private readonly _defaultColorsService: DefaultColorsService,
|
||||||
@ -37,23 +51,8 @@ export class FileWorkloadComponent implements OnInit {
|
|||||||
private readonly _dictionariesMapService: DictionariesMapService,
|
private readonly _dictionariesMapService: DictionariesMapService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get updated(): boolean {
|
|
||||||
return this.file.hasUpdates && this.file.assignee === this._userService.currentUser.id && !this.file.isApproved;
|
|
||||||
}
|
|
||||||
|
|
||||||
get noWorkloadItems(): boolean {
|
|
||||||
return (
|
|
||||||
!this.updated &&
|
|
||||||
!this.file.analysisRequired &&
|
|
||||||
!this.file.hasRedactions &&
|
|
||||||
!this.file.hasImages &&
|
|
||||||
!this.file.hintsOnly &&
|
|
||||||
!this.file.hasAnnotationComments
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.#dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId;
|
this.#dossierTemplateId = this._dossiersService.find(this.file().dossierId).dossierTemplateId;
|
||||||
this.updatedColor$ = this.#getDefaultColor$('updated');
|
this.updatedColor$ = this.#getDefaultColor$('updated');
|
||||||
this.analysisColor$ = this.#getDefaultColor$('analysis');
|
this.analysisColor$ = this.#getDefaultColor$('analysis');
|
||||||
this.hintColor$ = this.#getDefaultColor$('hint');
|
this.hintColor$ = this.#getDefaultColor$('hint');
|
||||||
|
|||||||
@ -1,78 +1,94 @@
|
|||||||
<div class="cell" [class.file-name-cell]="!fileAttributesService.isEditingFileAttribute()">
|
<div class="cell" [class.file-name-cell]="!fileAttributesService.isEditingFileAttribute()">
|
||||||
<redaction-file-name-column [dossierTemplateId]="dossierTemplateId" [file]="file" [dossier]="dossier"></redaction-file-name-column>
|
<redaction-file-name-column
|
||||||
|
[dossierTemplateId]="dossierTemplateId()"
|
||||||
|
[file]="file()"
|
||||||
|
[dossier]="dossier()"
|
||||||
|
></redaction-file-name-column>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<redaction-date-column [date]="file.added" [isError]="file.isError"></redaction-date-column>
|
<redaction-date-column [date]="file().added" [isError]="file().isError"></redaction-date-column>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<redaction-date-column [date]="file.redactionModificationDate" [isError]="file.isError"></redaction-date-column>
|
<redaction-date-column [date]="file().redactionModificationDate" [isError]="file().isError"></redaction-date-column>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let config of displayedAttributes" class="cell">
|
@for (config of displayedAttributes(); track config.id) {
|
||||||
<redaction-file-attribute [file]="file" [dossier]="dossier" [fileAttribute]="config"></redaction-file-attribute>
|
<div class="cell">
|
||||||
|
<redaction-file-attribute [file]="file()" [dossier]="dossier()" [fileAttribute]="config"></redaction-file-attribute>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- always show A for error-->
|
<!-- always show A for error-->
|
||||||
<div *ngIf="file.isError && !isDocumine" class="cell">
|
@if (file().isError && !isDocumine) {
|
||||||
|
<div class="cell">
|
||||||
<redaction-annotation-icon color="#dd4d50" label="A" type="square"></redaction-annotation-icon>
|
<redaction-annotation-icon color="#dd4d50" label="A" type="square"></redaction-annotation-icon>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<ng-container *ngIf="!file.isError">
|
@if (!file().isError) {
|
||||||
<div class="cell" *ngIf="!isDocumine">
|
@if (!isDocumine) {
|
||||||
<redaction-file-workload *ngIf="!file.excluded" [file]="file"></redaction-file-workload>
|
<div class="cell">
|
||||||
|
@if (!file().excluded) {
|
||||||
|
<redaction-file-workload [file]="file()"></redaction-file-workload>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="user-column cell">
|
<div class="user-column cell">
|
||||||
<iqser-initials-avatar [user]="file.assignee" [withName]="true"></iqser-initials-avatar>
|
<iqser-initials-avatar [user]="file().assignee" [withName]="true"></iqser-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<div class="small-label stats-subtitle">
|
<div class="small-label stats-subtitle">
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="iqser:pages"></mat-icon>
|
<mat-icon svgIcon="iqser:pages"></mat-icon>
|
||||||
{{ file.numberOfPages }}
|
{{ file().numberOfPages }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
|
||||||
<div [class.extend-cols]="file.isError" class="status-container cell">
|
<div [class.extend-cols]="file().isError" class="status-container cell">
|
||||||
<div *ngIf="file.isError" class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div>
|
@if (file().isError) {
|
||||||
|
<div class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div>
|
||||||
|
}
|
||||||
|
|
||||||
<div *ngIf="file.isUnprocessed" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
|
@if (file().isUnprocessed) {
|
||||||
|
<div class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="status-wrapper">
|
<div class="status-wrapper">
|
||||||
<ng-container *ngIf="file.isOcrProcessing; else defaultProcessing">
|
@if (file().isOcrProcessing) {
|
||||||
<redaction-ocr-progress-bar
|
<redaction-ocr-progress-bar
|
||||||
[numberOfOCRedPages]="file.numberOfOCRedPages"
|
[numberOfOCRedPages]="file().numberOfOCRedPages"
|
||||||
[numberOfPagesToOCR]="file.numberOfPagesToOCR"
|
[numberOfPagesToOCR]="file().numberOfPagesToOCR"
|
||||||
[progressColor]="'primary'"
|
[progressColor]="'primary'"
|
||||||
></redaction-ocr-progress-bar>
|
></redaction-ocr-progress-bar>
|
||||||
</ng-container>
|
} @else {
|
||||||
</div>
|
<redaction-processing-indicator [file]="file()"></redaction-processing-indicator>
|
||||||
|
|
||||||
<ng-template #defaultProcessing>
|
|
||||||
<redaction-processing-indicator [file]="file"></redaction-processing-indicator>
|
|
||||||
|
|
||||||
|
@if (!file().isError && !file().isUnprocessed && !file().isInitialProcessing) {
|
||||||
<iqser-status-bar
|
<iqser-status-bar
|
||||||
*ngIf="!file.isError && !file.isUnprocessed && !file.isInitialProcessing"
|
|
||||||
[configs]="[
|
[configs]="[
|
||||||
{
|
{
|
||||||
color: file.workflowStatus,
|
color: file().workflowStatus,
|
||||||
length: 1,
|
length: 1,
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
></iqser-status-bar>
|
></iqser-status-bar>
|
||||||
</ng-template>
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (!file().isProcessing) {
|
||||||
<redaction-file-actions
|
<redaction-file-actions
|
||||||
*ngIf="!file.isProcessing"
|
[dossier]="dossier()"
|
||||||
[dossier]="dossier"
|
[file]="file()"
|
||||||
[file]="file"
|
|
||||||
[singleEntityAction]="true"
|
[singleEntityAction]="true"
|
||||||
class="mr-4"
|
class="mr-4"
|
||||||
type="dossier-overview-list"
|
type="dossier-overview-list"
|
||||||
></redaction-file-actions>
|
></redaction-file-actions>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, input } from '@angular/core';
|
||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
import { FileNameColumnComponent } from '@shared/components/file-name-column/file-name-column.component';
|
import { FileNameColumnComponent } from '@shared/components/file-name-column/file-name-column.component';
|
||||||
import { DateColumnComponent } from '../../../shared-dossiers/components/date-column/date-column.component';
|
import { DateColumnComponent } from '../../../shared-dossiers/components/date-column/date-column.component';
|
||||||
import { FileAttributeComponent } from '../file-attribute/file-attribute.component';
|
import { FileAttributeComponent } from '../file-attribute/file-attribute.component';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { AnnotationIconComponent } from '@shared/components/annotation-icon/annotation-icon.component';
|
import { AnnotationIconComponent } from '@shared/components/annotation-icon/annotation-icon.component';
|
||||||
import { FileWorkloadComponent } from './file-workload/file-workload.component';
|
import { FileWorkloadComponent } from './file-workload/file-workload.component';
|
||||||
import { InitialsAvatarComponent, IqserUsersModule } from '@common-ui/users';
|
import { InitialsAvatarComponent, IqserUsersModule } from '@common-ui/users';
|
||||||
@ -25,7 +24,6 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
FileNameColumnComponent,
|
FileNameColumnComponent,
|
||||||
DateColumnComponent,
|
DateColumnComponent,
|
||||||
FileAttributeComponent,
|
FileAttributeComponent,
|
||||||
NgIf,
|
|
||||||
AnnotationIconComponent,
|
AnnotationIconComponent,
|
||||||
FileWorkloadComponent,
|
FileWorkloadComponent,
|
||||||
IqserUsersModule,
|
IqserUsersModule,
|
||||||
@ -35,15 +33,14 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
StatusBarComponent,
|
StatusBarComponent,
|
||||||
FileActionsComponent,
|
FileActionsComponent,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
NgForOf,
|
|
||||||
InitialsAvatarComponent,
|
InitialsAvatarComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class TableItemComponent {
|
export class TableItemComponent {
|
||||||
@Input({ required: true }) file: File;
|
readonly file = input.required<File>();
|
||||||
@Input({ required: true }) dossier: Dossier;
|
readonly dossier = input.required<Dossier>();
|
||||||
@Input({ required: true }) displayedAttributes: IFileAttributeConfig[];
|
readonly displayedAttributes = input.required<IFileAttributeConfig[]>();
|
||||||
@Input({ required: true }) dossierTemplateId: string;
|
readonly dossierTemplateId = input.required<string>();
|
||||||
|
|
||||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<div *ngIf="configService.listingMode$ | async as mode" class="view-mode-selection">
|
@if (configService.listingMode$ | async; as mode) {
|
||||||
|
<div class="view-mode-selection">
|
||||||
<div class="all-caps-label" translate="view-mode.view-as"></div>
|
<div class="all-caps-label" translate="view-mode.view-as"></div>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
@ -19,3 +20,4 @@
|
|||||||
icon="iqser:lanes"
|
icon="iqser:lanes"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { CircleButtonComponent, ListingMode, ListingModes, ListingService } from
|
|||||||
import { File } from '@red/domain';
|
import { File } from '@red/domain';
|
||||||
import { ConfigService } from '../../config.service';
|
import { ConfigService } from '../../config.service';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-view-mode-selection',
|
selector: 'redaction-view-mode-selection',
|
||||||
@ -11,7 +11,7 @@ import { AsyncPipe, NgIf } from '@angular/common';
|
|||||||
styleUrls: ['./view-mode-selection.component.scss'],
|
styleUrls: ['./view-mode-selection.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CircleButtonComponent, TranslateModule, AsyncPipe, NgIf],
|
imports: [CircleButtonComponent, TranslateModule, AsyncPipe],
|
||||||
})
|
})
|
||||||
export class ViewModeSelectionComponent {
|
export class ViewModeSelectionComponent {
|
||||||
readonly listingModes = ListingModes;
|
readonly listingModes = ListingModes;
|
||||||
|
|||||||
@ -7,41 +7,44 @@
|
|||||||
<div class="details">
|
<div class="details">
|
||||||
<div
|
<div
|
||||||
[attr.help-mode-key]="'workflow_view'"
|
[attr.help-mode-key]="'workflow_view'"
|
||||||
[matTooltip]="file.filename"
|
[matTooltip]="file().filename"
|
||||||
[routerLink]="file.routerLink"
|
[routerLink]="fileRouterLink()"
|
||||||
class="filename pointer"
|
class="filename pointer"
|
||||||
matTooltipPosition="above"
|
matTooltipPosition="above"
|
||||||
>
|
>
|
||||||
{{ file.filename }}
|
{{ file().filename }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-file-stats [file]="file"></redaction-file-stats>
|
<redaction-file-stats [file]="file()"></redaction-file-stats>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user">
|
<div class="user">
|
||||||
<iqser-initials-avatar [user]="file.assignee"></iqser-initials-avatar>
|
<iqser-initials-avatar [user]="file().assignee"></iqser-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let config of displayedAttributes; trackBy: trackBy" class="small-label mt-8 attribute">
|
@for (config of displayedAttributes(); track config.id) {
|
||||||
<redaction-file-attribute [dossier]="dossier" [fileAttribute]="config" [file]="file"></redaction-file-attribute>
|
<div class="small-label mt-8 attribute">
|
||||||
|
<redaction-file-attribute [dossier]="dossier()" [fileAttribute]="config" [file]="file()"></redaction-file-attribute>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<redaction-file-workload [file]="file"></redaction-file-workload>
|
<redaction-file-workload [file]="file()"></redaction-file-workload>
|
||||||
|
|
||||||
<div class="file-actions overflow-visible">
|
<div class="file-actions overflow-visible">
|
||||||
<redaction-processing-indicator [file]="file" class="mr-8"></redaction-processing-indicator>
|
<redaction-processing-indicator [file]="file()" class="mr-8"></redaction-processing-indicator>
|
||||||
|
|
||||||
<div #actionsWrapper class="actions-wrapper">
|
<div #actionsWrapper class="actions-wrapper">
|
||||||
|
@if (!file().isProcessing) {
|
||||||
<redaction-file-actions
|
<redaction-file-actions
|
||||||
*ngIf="!file.isProcessing"
|
[dossier]="dossier()"
|
||||||
[dossier]="dossier"
|
[file]="file()"
|
||||||
[file]="file"
|
|
||||||
[maxWidth]="width"
|
[maxWidth]="width"
|
||||||
[singleEntityAction]="true"
|
[singleEntityAction]="true"
|
||||||
iqserDisableStopPropagation
|
iqserDisableStopPropagation
|
||||||
type="dossier-overview-workflow"
|
type="dossier-overview-workflow"
|
||||||
></redaction-file-actions>
|
></redaction-file-actions>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectorRef, Component, computed, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, computed, ElementRef, input, OnInit, Optional, ViewChild } from '@angular/core';
|
||||||
import { DisableStopPropagationDirective, HelpModeService } from '@iqser/common-ui';
|
import { DisableStopPropagationDirective, HelpModeService } from '@iqser/common-ui';
|
||||||
import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils';
|
import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||||
@ -11,7 +11,7 @@ import { FileAttributeComponent } from '../file-attribute/file-attribute.compone
|
|||||||
import { FileWorkloadComponent } from '../table-item/file-workload/file-workload.component';
|
import { FileWorkloadComponent } from '../table-item/file-workload/file-workload.component';
|
||||||
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
|
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
|
||||||
import { FileActionsComponent } from '../../../shared-dossiers/components/file-actions/file-actions.component';
|
import { FileActionsComponent } from '../../../shared-dossiers/components/file-actions/file-actions.component';
|
||||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-workflow-item',
|
selector: 'redaction-workflow-item',
|
||||||
@ -27,10 +27,8 @@ import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
|||||||
FileWorkloadComponent,
|
FileWorkloadComponent,
|
||||||
ProcessingIndicatorComponent,
|
ProcessingIndicatorComponent,
|
||||||
FileActionsComponent,
|
FileActionsComponent,
|
||||||
NgIf,
|
|
||||||
DisableStopPropagationDirective,
|
DisableStopPropagationDirective,
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
NgForOf,
|
|
||||||
InitialsAvatarComponent,
|
InitialsAvatarComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -38,9 +36,10 @@ export class WorkflowItemComponent implements OnInit {
|
|||||||
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
||||||
width: number;
|
width: number;
|
||||||
readonly trackBy = trackByFactory();
|
readonly trackBy = trackByFactory();
|
||||||
@Input({ required: true }) file: File;
|
readonly file = input.required<File>();
|
||||||
@Input({ required: true }) dossier: Dossier;
|
readonly dossier = input.required<Dossier>();
|
||||||
@Input({ required: true }) displayedAttributes: IFileAttributeConfig[];
|
readonly displayedAttributes = input.required<IFileAttributeConfig[]>();
|
||||||
|
readonly fileRouterLink = computed(() => this.file().routerLink);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly fileAttributesService: FileAttributesService,
|
readonly fileAttributesService: FileAttributesService,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<ng-container *ngIf="(files$ | async) && dossier$ | async as dossier">
|
@if ((files$ | async) && dossier$ | async; as dossier) {
|
||||||
<section>
|
<section>
|
||||||
<redaction-dossier-overview-screen-header
|
<redaction-dossier-overview-screen-header
|
||||||
(upload)="fileInput.click()"
|
(upload)="fileInput.click()"
|
||||||
@ -8,10 +8,11 @@
|
|||||||
<div class="overlay-shadow"></div>
|
<div class="overlay-shadow"></div>
|
||||||
|
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<div *ngIf="listingMode$ | async as mode" [class.extended]="collapsedDetails" class="content-container">
|
@if (listingMode$ | async; as mode) {
|
||||||
|
<div [class.extended]="collapsedDetails" class="content-container">
|
||||||
|
@if (mode === listingModes.table) {
|
||||||
<iqser-table
|
<iqser-table
|
||||||
(noDataAction)="fileInput.click()"
|
(noDataAction)="fileInput.click()"
|
||||||
*ngIf="mode === listingModes.table"
|
|
||||||
[bulkActions]="bulkActions"
|
[bulkActions]="bulkActions"
|
||||||
[hasScrollButton]="true"
|
[hasScrollButton]="true"
|
||||||
[headerHelpModeKey]="'document_list'"
|
[headerHelpModeKey]="'document_list'"
|
||||||
@ -29,11 +30,10 @@
|
|||||||
[rowIdPrefix]="'file'"
|
[rowIdPrefix]="'file'"
|
||||||
[namePropertyKey]="'filename'"
|
[namePropertyKey]="'filename'"
|
||||||
></iqser-table>
|
></iqser-table>
|
||||||
|
} @else if (mode === listingModes.workflow) {
|
||||||
<iqser-workflow
|
<iqser-workflow
|
||||||
(addElement)="fileInput.click()"
|
(addElement)="fileInput.click()"
|
||||||
(noDataAction)="fileInput.click()"
|
(noDataAction)="fileInput.click()"
|
||||||
*ngIf="mode === listingModes.workflow"
|
|
||||||
[addElementIcon]="'iqser:upload'"
|
[addElementIcon]="'iqser:upload'"
|
||||||
[bulkActions]="bulkActions"
|
[bulkActions]="bulkActions"
|
||||||
[config]="workflowConfig"
|
[config]="workflowConfig"
|
||||||
@ -54,14 +54,18 @@
|
|||||||
></redaction-workflow-item>
|
></redaction-workflow-item>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</iqser-workflow>
|
</iqser-workflow>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div *ngIf="dossierAttributes$ | async" [class.collapsed]="collapsedDetails" class="right-container">
|
@if (dossierAttributes$ | async) {
|
||||||
|
<div [class.collapsed]="collapsedDetails" class="right-container">
|
||||||
<redaction-dossier-details
|
<redaction-dossier-details
|
||||||
(toggleCollapse)="collapsedDetails = !collapsedDetails"
|
(toggleCollapse)="collapsedDetails = !collapsedDetails"
|
||||||
[dossierAttributes]="dossierAttributes"
|
[dossierAttributes]="dossierAttributes"
|
||||||
></redaction-dossier-details>
|
></redaction-dossier-details>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -82,7 +86,7 @@
|
|||||||
[file]="file"
|
[file]="file"
|
||||||
></redaction-table-item>
|
></redaction-table-item>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
}
|
||||||
|
|
||||||
<ng-template #needsWorkFilterTemplate let-filter="filter">
|
<ng-template #needsWorkFilterTemplate let-filter="filter">
|
||||||
<redaction-type-filter [dossierTemplateId]="dossierTemplateId" [filter]="filter"></redaction-type-filter>
|
<redaction-type-filter [dossierTemplateId]="dossierTemplateId" [filter]="filter"></redaction-type-filter>
|
||||||
|
|||||||
@ -45,7 +45,7 @@ import { filter, skip, switchMap, tap } from 'rxjs/operators';
|
|||||||
import { ConfigService } from '../config.service';
|
import { ConfigService } from '../config.service';
|
||||||
import { BulkActionsService } from '../services/bulk-actions.service';
|
import { BulkActionsService } from '../services/bulk-actions.service';
|
||||||
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
import { DossierOverviewScreenHeaderComponent } from '../components/screen-header/dossier-overview-screen-header.component';
|
import { DossierOverviewScreenHeaderComponent } from '../components/screen-header/dossier-overview-screen-header.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { WorkflowItemComponent } from '../components/workflow-item/workflow-item.component';
|
import { WorkflowItemComponent } from '../components/workflow-item/workflow-item.component';
|
||||||
@ -64,7 +64,6 @@ import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.
|
|||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
IqserListingModule,
|
IqserListingModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
NgIf,
|
|
||||||
WorkflowItemComponent,
|
WorkflowItemComponent,
|
||||||
DossierDetailsComponent,
|
DossierDetailsComponent,
|
||||||
DossierOverviewBulkActionsComponent,
|
DossierOverviewBulkActionsComponent,
|
||||||
@ -128,11 +127,11 @@ export default class DossierOverviewScreenComponent extends ListingComponent<Fil
|
|||||||
this.#updateFileAttributes();
|
this.#updateFileAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
get checkedRequiredFilters(): NestedFilter[] {
|
get #checkedRequiredFilters(): NestedFilter[] {
|
||||||
return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
|
return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
get checkedNotRequiredFilters(): NestedFilter[] {
|
get #checkedNotRequiredFilters(): NestedFilter[] {
|
||||||
return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
|
return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,8 +269,8 @@ export default class DossierOverviewScreenComponent extends ListingComponent<Fil
|
|||||||
this.#fileAttributeConfigs,
|
this.#fileAttributeConfigs,
|
||||||
this.#dossier.dossierTemplateId,
|
this.#dossier.dossierTemplateId,
|
||||||
this._needsWorkFilterTemplate,
|
this._needsWorkFilterTemplate,
|
||||||
() => this.checkedRequiredFilters,
|
() => this.#checkedRequiredFilters,
|
||||||
() => this.checkedNotRequiredFilters,
|
() => this.#checkedNotRequiredFilters,
|
||||||
);
|
);
|
||||||
this.filterService.addFilterGroups(filterGroups, true);
|
this.filterService.addFilterGroups(filterGroups, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
<iqser-status-bar *ngIf="stats" [configs]="statusBarConfig"></iqser-status-bar>
|
@if (stats()) {
|
||||||
|
<iqser-status-bar [configs]="statusBarConfig()"></iqser-status-bar>
|
||||||
|
}
|
||||||
|
|||||||
@ -1,27 +1,22 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
||||||
import { DossierStats, StatusSorter } from '@red/domain';
|
import { DossierStats, StatusSorter } from '@red/domain';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
import { StatusBarComponent, StatusBarConfig } from '@iqser/common-ui/lib/shared';
|
import { StatusBarComponent, StatusBarConfig } from '@iqser/common-ui/lib/shared';
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-dossier-documents-status',
|
selector: 'redaction-dossier-documents-status',
|
||||||
templateUrl: './dossier-documents-status.component.html',
|
templateUrl: './dossier-documents-status.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [StatusBarComponent, NgIf],
|
imports: [StatusBarComponent],
|
||||||
})
|
})
|
||||||
export class DossierDocumentsStatusComponent implements OnChanges {
|
export class DossierDocumentsStatusComponent {
|
||||||
@Input() stats: DossierStats;
|
readonly stats = input<DossierStats>();
|
||||||
statusBarConfig: List<StatusBarConfig<string>>;
|
readonly statusBarConfig = computed(() => this.#statusConfig);
|
||||||
|
|
||||||
private get _statusConfig(): List<StatusBarConfig<string>> {
|
get #statusConfig(): List<StatusBarConfig<string>> {
|
||||||
const { fileCountPerWorkflowStatus } = this.stats;
|
const { fileCountPerWorkflowStatus } = this.stats();
|
||||||
const statuses = Object.keys(fileCountPerWorkflowStatus).sort(StatusSorter.byStatus);
|
const statuses = Object.keys(fileCountPerWorkflowStatus).sort(StatusSorter.byStatus);
|
||||||
return statuses.map(status => ({ length: fileCountPerWorkflowStatus[status], color: status }));
|
return statuses.map(status => ({ length: fileCountPerWorkflowStatus[status], color: status }));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(): void {
|
|
||||||
this.statusBarConfig = this._statusConfig;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
<div class="needs-work">
|
<div class="needs-work">
|
||||||
|
@if (dossierStats().hasRedactionsFilePresent) {
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
*ngIf="dossierStats.hasRedactionsFilePresent"
|
|
||||||
[color]="redactionColor$ | async"
|
[color]="redactionColor$ | async"
|
||||||
[label]="'redaction-abbreviation' | translate"
|
[label]="'redaction-abbreviation' | translate"
|
||||||
type="square"
|
type="square"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
}
|
||||||
|
|
||||||
<redaction-annotation-icon
|
@if (dossierStats().hasHintsNoRedactionsFilePresent) {
|
||||||
*ngIf="dossierStats.hasHintsNoRedactionsFilePresent"
|
<redaction-annotation-icon [color]="hintColor$ | async" label="H" type="circle"></redaction-annotation-icon>
|
||||||
[color]="hintColor$ | async"
|
}
|
||||||
label="H"
|
|
||||||
type="circle"
|
|
||||||
></redaction-annotation-icon>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, input, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
import { DefaultColorType, Dossier, DossierStats } from '@red/domain';
|
import { DefaultColorType, Dossier, DossierStats } from '@red/domain';
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { BehaviorSubject, Observable, switchMap } from 'rxjs';
|
import { BehaviorSubject, Observable, switchMap } from 'rxjs';
|
||||||
import { AnnotationIconComponent } from '@shared/components/annotation-icon/annotation-icon.component';
|
import { AnnotationIconComponent } from '@shared/components/annotation-icon/annotation-icon.component';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -12,12 +12,12 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
styleUrls: ['./dossier-workload-column.component.scss'],
|
styleUrls: ['./dossier-workload-column.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AnnotationIconComponent, AsyncPipe, TranslateModule, NgIf],
|
imports: [AnnotationIconComponent, AsyncPipe, TranslateModule],
|
||||||
})
|
})
|
||||||
export class DossierWorkloadColumnComponent implements OnChanges {
|
export class DossierWorkloadColumnComponent implements OnChanges {
|
||||||
readonly #dossierTemplateId$ = new BehaviorSubject<string>(null);
|
readonly #dossierTemplateId$ = new BehaviorSubject<string>(null);
|
||||||
@Input() dossier: Dossier;
|
readonly dossier = input<Dossier>();
|
||||||
@Input() dossierStats: DossierStats;
|
readonly dossierStats = input<DossierStats>();
|
||||||
readonly hintColor$: Observable<string>;
|
readonly hintColor$: Observable<string>;
|
||||||
readonly redactionColor$: Observable<string>;
|
readonly redactionColor$: Observable<string>;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export class DossierWorkloadColumnComponent implements OnChanges {
|
|||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes.dossier) {
|
if (changes.dossier) {
|
||||||
this.#dossierTemplateId$.next(this.dossier.dossierTemplateId);
|
this.#dossierTemplateId$.next(this.dossier().dossierTemplateId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<div *ngIf="stats$ | async as stats">
|
@if (stats$ | async; as stats) {
|
||||||
|
<div>
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
[config]="dossiersChartConfig$ | async"
|
[config]="dossiersChartConfig$ | async"
|
||||||
[radius]="80"
|
[radius]="80"
|
||||||
@ -25,6 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="right-chart">
|
<div class="right-chart">
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { map } from 'rxjs/operators';
|
|||||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||||
import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
|
import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
|
||||||
import { AsyncPipe, DecimalPipe, NgIf } from '@angular/common';
|
import { AsyncPipe, DecimalPipe } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ import { MatIcon } from '@angular/material/icon';
|
|||||||
styleUrls: ['./dossiers-listing-details.component.scss'],
|
styleUrls: ['./dossiers-listing-details.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [DonutChartComponent, AsyncPipe, NgIf, TranslateModule, MatIcon, DecimalPipe],
|
imports: [DonutChartComponent, AsyncPipe, TranslateModule, MatIcon, DecimalPipe],
|
||||||
})
|
})
|
||||||
export class DossiersListingDetailsComponent {
|
export class DossiersListingDetailsComponent {
|
||||||
readonly stats$: Observable<DashboardStats>;
|
readonly stats$: Observable<DashboardStats>;
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
<ng-container *ngIf="stats$ | async as stats">
|
@if (stats$ | async; as stats) {
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<redaction-dossier-name-column [dossierStats]="stats" [dossier]="dossier"></redaction-dossier-name-column>
|
<redaction-dossier-name-column [dossierStats]="stats" [dossier]="dossier()"></redaction-dossier-name-column>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<redaction-date-column [date]="stats.fileManipulationDate"></redaction-date-column>
|
<redaction-date-column [date]="stats.fileManipulationDate"></redaction-date-column>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell" *ngIf="!isDocumine">
|
@if (!isDocumine) {
|
||||||
<redaction-dossier-workload-column [dossierStats]="stats" [dossier]="dossier"></redaction-dossier-workload-column>
|
<div class="cell">
|
||||||
|
<redaction-dossier-workload-column [dossierStats]="stats" [dossier]="dossier()"></redaction-dossier-workload-column>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="cell user-column">
|
<div class="cell user-column">
|
||||||
<iqser-initials-avatar [user]="dossier.ownerId" [withName]="true"></iqser-initials-avatar>
|
<iqser-initials-avatar [user]="dossier().ownerId" [withName]="true"></iqser-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell status-container">
|
<div class="cell status-container">
|
||||||
@ -20,8 +22,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<redaction-dossier-state [dossier]="dossier"></redaction-dossier-state>
|
<redaction-dossier-state [dossier]="dossier()"></redaction-dossier-state>
|
||||||
|
|
||||||
<redaction-dossiers-listing-actions [dossier]="dossier"></redaction-dossiers-listing-actions>
|
<redaction-dossiers-listing-actions [dossier]="dossier()"></redaction-dossiers-listing-actions>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, input, OnChanges } from '@angular/core';
|
||||||
import { Dossier, DossierStats } from '@red/domain';
|
import { Dossier, DossierStats } from '@red/domain';
|
||||||
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
@ -6,7 +6,7 @@ import { switchMap, tap } from 'rxjs/operators';
|
|||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { DossierNameColumnComponent } from '@shared/components/dossier-name-column/dossier-name-column.component';
|
import { DossierNameColumnComponent } from '@shared/components/dossier-name-column/dossier-name-column.component';
|
||||||
import { DateColumnComponent } from '../../../shared-dossiers/components/date-column/date-column.component';
|
import { DateColumnComponent } from '../../../shared-dossiers/components/date-column/date-column.component';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
import { DossierWorkloadColumnComponent } from '../dossier-workload-column/dossier-workload-column.component';
|
import { DossierWorkloadColumnComponent } from '../dossier-workload-column/dossier-workload-column.component';
|
||||||
import { InitialsAvatarComponent } from '@common-ui/users';
|
import { InitialsAvatarComponent } from '@common-ui/users';
|
||||||
import { DossierDocumentsStatusComponent } from '../dossier-documents-status/dossier-documents-status.component';
|
import { DossierDocumentsStatusComponent } from '../dossier-documents-status/dossier-documents-status.component';
|
||||||
@ -23,7 +23,6 @@ import { DossiersListingActionsComponent } from '../../../shared-dossiers/compon
|
|||||||
DossierNameColumnComponent,
|
DossierNameColumnComponent,
|
||||||
DateColumnComponent,
|
DateColumnComponent,
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
NgIf,
|
|
||||||
DossierWorkloadColumnComponent,
|
DossierWorkloadColumnComponent,
|
||||||
InitialsAvatarComponent,
|
InitialsAvatarComponent,
|
||||||
DossierDocumentsStatusComponent,
|
DossierDocumentsStatusComponent,
|
||||||
@ -32,7 +31,7 @@ import { DossiersListingActionsComponent } from '../../../shared-dossiers/compon
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class TableItemComponent implements OnChanges {
|
export class TableItemComponent implements OnChanges {
|
||||||
@Input() dossier!: Dossier;
|
readonly dossier = input.required<Dossier>();
|
||||||
|
|
||||||
readonly stats$: Observable<DossierStats>;
|
readonly stats$: Observable<DossierStats>;
|
||||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
@ -43,14 +42,14 @@ export class TableItemComponent implements OnChanges {
|
|||||||
switchMap(dossierId => this.dossierStatsService.watch$(dossierId)),
|
switchMap(dossierId => this.dossierStatsService.watch$(dossierId)),
|
||||||
// TODO required for sorting the dossier table - fix me Baby one more time!
|
// TODO required for sorting the dossier table - fix me Baby one more time!
|
||||||
tap(stats => {
|
tap(stats => {
|
||||||
this.dossier.changedDate = stats.fileManipulationDate;
|
this.dossier().changedDate = stats.fileManipulationDate;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
if (this.dossier) {
|
if (this.dossier()) {
|
||||||
this.#ngOnChanges$.next(this.dossier.id);
|
this.#ngOnChanges$.next(this.dossier().id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
[noDataButtonLabel]="'dossier-listing.no-data.action' | translate"
|
[noDataButtonLabel]="'dossier-listing.no-data.action' | translate"
|
||||||
[noDataText]="'dossier-listing.no-data.title' | translate"
|
[noDataText]="'dossier-listing.no-data.title' | translate"
|
||||||
[noMatchText]="'dossier-listing.no-match.title' | translate"
|
[noMatchText]="'dossier-listing.no-match.title' | translate"
|
||||||
[showNoDataButton]="permissionsService.canCreateDossier(dossierTemplate())"
|
[showNoDataButton]="canCreateDossier()"
|
||||||
[tableColumnConfigs]="tableColumnConfigs"
|
[tableColumnConfigs]="tableColumnConfigs"
|
||||||
[rowIdPrefix]="'dossier'"
|
[rowIdPrefix]="'dossier'"
|
||||||
[namePropertyKey]="'dossierName'"
|
[namePropertyKey]="'dossierName'"
|
||||||
@ -27,7 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-container" iqserHasScrollbar>
|
<div class="right-container" iqserHasScrollbar>
|
||||||
<redaction-dossiers-listing-details *ngIf="(entitiesService.noData$ | async) === false"></redaction-dossiers-listing-details>
|
@if ((entitiesService.noData$ | async) === false) {
|
||||||
|
<redaction-dossiers-listing-details></redaction-dossiers-listing-details>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -51,7 +51,8 @@ export class DossiersListingScreenComponent extends ListingComponent<Dossier> im
|
|||||||
readonly dossierTemplates = toSignal(this.dossierTemplatesService.all$);
|
readonly dossierTemplates = toSignal(this.dossierTemplatesService.all$);
|
||||||
readonly dossierTemplate = computed(() => this.dossierTemplates().find(template => this.dossierTemplateId === template.id));
|
readonly dossierTemplate = computed(() => this.dossierTemplates().find(template => this.dossierTemplateId === template.id));
|
||||||
readonly buttonConfigs = computed(() => this._configService.buttonsConfig(this.dossierTemplate()));
|
readonly buttonConfigs = computed(() => this._configService.buttonsConfig(this.dossierTemplate()));
|
||||||
readonly computeFilters$ = this._activeDossiersService.all$.pipe(tap(() => this._computeAllFilters()));
|
readonly canCreateDossier = computed(() => this.permissionsService.canCreateDossier(this.dossierTemplate()));
|
||||||
|
readonly computeFilters$ = this._activeDossiersService.all$.pipe(tap(() => this.#computeAllFilters()));
|
||||||
@ViewChild('needsWorkFilterTemplate', {
|
@ViewChild('needsWorkFilterTemplate', {
|
||||||
read: TemplateRef,
|
read: TemplateRef,
|
||||||
static: true,
|
static: true,
|
||||||
@ -92,7 +93,7 @@ export class DossiersListingScreenComponent extends ListingComponent<Dossier> im
|
|||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeAllFilters() {
|
#computeAllFilters() {
|
||||||
const filterGroups = this._configService.filterGroups(
|
const filterGroups = this._configService.filterGroups(
|
||||||
this.entitiesService.all,
|
this.entitiesService.all,
|
||||||
this._needsWorkFilterTemplate,
|
this._needsWorkFilterTemplate,
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export class AnnotationActionsComponent {
|
|||||||
AnnotationPermissions.forUser(
|
AnnotationPermissions.forUser(
|
||||||
this._permissionsService.isApprover(this._state.dossier()),
|
this._permissionsService.isApprover(this._state.dossier()),
|
||||||
this.annotations(),
|
this.annotations(),
|
||||||
this._state.dictionaries,
|
untracked(this._state.dictionaries),
|
||||||
this._iqserPermissionsService,
|
this._iqserPermissionsService,
|
||||||
this._state.file().excludedFromAutomaticAnalysis,
|
this._state.file().excludedFromAutomaticAnalysis,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,44 +1,42 @@
|
|||||||
<div class="details">
|
<div class="details">
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
[color]="annotation.color"
|
[color]="annotation().color"
|
||||||
[label]="annotation.isEarmark ? '' : ((annotationTypesTranslations[annotation.superType] | translate)?.[0] | uppercase)"
|
[label]="isEarmark() ? '' : ((annotationTypesTranslations[annotation().superType] | translate)?.[0] | uppercase)"
|
||||||
[type]="annotation.iconShape"
|
[type]="iconShape()"
|
||||||
class="mt-6 mr-10"
|
class="mt-6 mr-10"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ annotation.superTypeLabel | translate }}</strong>
|
<strong>{{ annotation().superTypeLabel | translate }}</strong>
|
||||||
|
|
||||||
<strong *ngIf="annotation.pending" class="pending-analysis">
|
<strong *ngIf="annotation().pending" class="pending-analysis">
|
||||||
{{ 'annotation.pending' | translate }}
|
{{ 'annotation().pending' | translate }}
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="annotation.typeLabel">
|
<div *ngIf="annotation().typeLabel">
|
||||||
<strong>
|
<strong>
|
||||||
<span>{{ annotation.descriptor | translate }}</span
|
<span>{{ descriptor() | translate }}</span
|
||||||
>:
|
>:
|
||||||
</strong>
|
</strong>
|
||||||
{{ annotation.typeLabel }}
|
{{ annotation().typeLabel }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div *deny="roles.getRss; if: !!annotation().shortContent && !isHint() && !isSkipped() && !isIgnoredHint()">
|
||||||
*deny="roles.getRss; if: !!annotation.shortContent && !annotation.isHint && !annotation.isSkipped && !annotation.isIgnoredHint"
|
<strong><span translate="content"></span>: </strong>{{ annotation().shortContent }}
|
||||||
>
|
|
||||||
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="annotation.isEarmark">
|
<div *ngIf="isEarmark()">
|
||||||
<strong><span translate="color"></span>: </strong>{{ annotation.color }}
|
<strong><span translate="color"></span>: </strong>{{ annotation().color }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="annotation.isEarmark">
|
<div *ngIf="isEarmark()">
|
||||||
<strong><span translate="size"></span>: </strong>{{ annotation.width }}x{{ annotation.height }} px
|
<strong><span translate="size"></span>: </strong>{{ width() }}x{{ height() }} px
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="active-icon-marker-container">
|
<div class="active-icon-marker-container">
|
||||||
<iqser-round-checkbox *ngIf="multiSelectService.active() && isSelected" active></iqser-round-checkbox>
|
<iqser-round-checkbox *ngIf="multiSelectService.active() && isSelected()" active></iqser-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { NgIf, UpperCasePipe } from '@angular/common';
|
import { NgIf, UpperCasePipe } from '@angular/common';
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, computed, input } from '@angular/core';
|
||||||
import { RoundCheckboxComponent } from '@common-ui/inputs/round-checkbox/round-checkbox.component';
|
import { RoundCheckboxComponent } from '@common-ui/inputs/round-checkbox/round-checkbox.component';
|
||||||
import { getConfig, IqserDenyDirective } from '@iqser/common-ui';
|
import { getConfig, IqserDenyDirective } from '@iqser/common-ui';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
@ -20,8 +20,17 @@ export class AnnotationCardComponent {
|
|||||||
protected readonly roles = Roles;
|
protected readonly roles = Roles;
|
||||||
protected readonly annotationTypesTranslations = annotationTypesTranslations;
|
protected readonly annotationTypesTranslations = annotationTypesTranslations;
|
||||||
protected readonly isDocumine = getConfig().IS_DOCUMINE;
|
protected readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
@Input() annotation: AnnotationWrapper;
|
annotation = input<AnnotationWrapper>();
|
||||||
@Input() isSelected = false;
|
isSelected = input(false);
|
||||||
|
|
||||||
|
readonly isEarmark = computed(() => this.annotation().isEarmark);
|
||||||
|
readonly iconShape = computed(() => this.annotation().iconShape);
|
||||||
|
readonly width = computed(() => this.annotation().width);
|
||||||
|
readonly height = computed(() => this.annotation().height);
|
||||||
|
readonly isHint = computed(() => this.annotation().isHint);
|
||||||
|
readonly isSkipped = computed(() => this.annotation().isSkipped);
|
||||||
|
readonly isIgnoredHint = computed(() => this.annotation().isIgnoredHint);
|
||||||
|
readonly descriptor = computed(() => this.annotation().descriptor);
|
||||||
|
|
||||||
constructor(readonly multiSelectService: MultiSelectService) {}
|
constructor(readonly multiSelectService: MultiSelectService) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
matTooltipPosition="above"
|
matTooltipPosition="above"
|
||||||
></redaction-annotation-card>
|
></redaction-annotation-card>
|
||||||
|
|
||||||
@if (!annotation().item.isEarmark) {
|
@if (!isEarmark()) {
|
||||||
<div class="actions-wrapper">
|
<div class="actions-wrapper">
|
||||||
@if (!annotation().item.pending) {
|
@if (!annotation().item.pending) {
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -42,6 +42,8 @@ export class AnnotationWrapperComponent {
|
|||||||
protected readonly multiSelectService = inject(MultiSelectService);
|
protected readonly multiSelectService = inject(MultiSelectService);
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
|
||||||
|
readonly isEarmark = computed(() => this.annotation().item.isEarmark);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
this.active = this.annotation().isSelected;
|
this.active = this.annotation().isSelected;
|
||||||
|
|||||||
@ -23,7 +23,9 @@
|
|||||||
<div class="section">
|
<div class="section">
|
||||||
<div *ngFor="let attr of _fileAttributes()" class="attribute">
|
<div *ngFor="let attr of _fileAttributes()" class="attribute">
|
||||||
<div class="small-label">{{ attr.label }}:</div>
|
<div class="small-label">{{ attr.label }}:</div>
|
||||||
<div>{{ attr.value ? (isDate(attr) ? (attr.value | date : 'd MMM yyyy') : attr.value) : '-' }}</div>
|
<div>
|
||||||
|
{{ attr.value ? (attr.type === FileAttributeConfigTypes.DATE ? (attr.value | date: 'd MMM yyyy') : attr.value) : '-' }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,7 @@ export class DocumentInfoComponent extends ContextComponent<Context> {
|
|||||||
this._fileAttributesConfig();
|
this._fileAttributesConfig();
|
||||||
return this._attributes();
|
return this._attributes();
|
||||||
});
|
});
|
||||||
|
protected readonly FileAttributeConfigTypes = FileAttributeConfigTypes;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly permissionsService: PermissionsService,
|
readonly permissionsService: PermissionsService,
|
||||||
@ -56,10 +57,6 @@ export class DocumentInfoComponent extends ContextComponent<Context> {
|
|||||||
this._dialogService.openDialog('documentInfo', this._state.file());
|
this._dialogService.openDialog('documentInfo', this._state.file());
|
||||||
}
|
}
|
||||||
|
|
||||||
isDate(attribute: FileAttribute) {
|
|
||||||
return attribute.type === FileAttributeConfigTypes.DATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
closeView() {
|
closeView() {
|
||||||
this._documentInfoService.hide();
|
this._documentInfoService.hide();
|
||||||
setLocalStorageDataByFileId(this._state.fileId, 'show-document-info', false);
|
setLocalStorageDataByFileId(this._state.fileId, 'show-document-info', false);
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
<div class="editing-actions">
|
<div class="editing-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[disabled]="disabled"
|
[disabled]="disabled()"
|
||||||
[label]="'component-management.actions.save' | translate"
|
[label]="'component-management.actions.save' | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|||||||
@ -48,7 +48,16 @@ export class EditableStructuredComponentValueComponent implements OnInit {
|
|||||||
protected entryLabel: string;
|
protected entryLabel: string;
|
||||||
protected editing = false;
|
protected editing = false;
|
||||||
protected initialEntry: IComponentLogEntry;
|
protected initialEntry: IComponentLogEntry;
|
||||||
|
readonly disabled = computed(() => {
|
||||||
|
for (let i = 0; i < this.currentEntry().componentValues.length; i++) {
|
||||||
|
if (this.currentEntry().componentValues[i].value !== this.initialEntry.componentValues[i]?.value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.currentEntry().componentValues.length === this.initialEntry.componentValues.length;
|
||||||
|
});
|
||||||
protected readonly iconButtonTypes = IconButtonTypes;
|
protected readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
readonly #initialEntry = computed(() => JSON.parse(JSON.stringify(this.entry())));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly helpModeService: HelpModeService,
|
readonly helpModeService: HelpModeService,
|
||||||
@ -56,26 +65,13 @@ export class EditableStructuredComponentValueComponent implements OnInit {
|
|||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get disabled() {
|
|
||||||
for (let i = 0; i < this.currentEntry().componentValues.length; i++) {
|
|
||||||
if (this.currentEntry().componentValues[i].value !== this.initialEntry.componentValues[i]?.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.currentEntry().componentValues.length === this.initialEntry.componentValues.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
get #initialEntry() {
|
|
||||||
return JSON.parse(JSON.stringify(this.entry()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.currentEntry = signal(this.entry());
|
this.currentEntry = signal(this.entry());
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.initialEntry = this.#initialEntry;
|
this.initialEntry = this.#initialEntry();
|
||||||
this.entryLabel = this.parseName(this.currentEntry().name);
|
this.entryLabel = this.parseName(this.currentEntry().name);
|
||||||
this.deselect();
|
this.deselect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -172,7 +172,7 @@
|
|||||||
translate="file-preview.tabs.annotations.reset"
|
translate="file-preview.tabs.annotations.reset"
|
||||||
></a>
|
></a>
|
||||||
{{ 'file-preview.tabs.annotations.the-filters' | translate }}
|
{{ 'file-preview.tabs.annotations.the-filters' | translate }}
|
||||||
} @else if (state.componentReferenceIds?.length === 0) {
|
} @else if (state.componentReferenceIdsSignal()?.length === 0) {
|
||||||
{{ 'file-preview.tabs.annotations.no-annotations' | translate }}
|
{{ 'file-preview.tabs.annotations.no-annotations' | translate }}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
<div
|
<div
|
||||||
(click)="pageSelected.emit(number)"
|
(click)="pageSelected.emit(pageNumber())"
|
||||||
(dblclick)="toggleReadState()"
|
(dblclick)="toggleReadState()"
|
||||||
*ngIf="assigneeChanged$ | async"
|
*ngIf="assigneeChanged$ | async"
|
||||||
[class.active]="isActive"
|
[class.active]="isActive()"
|
||||||
[class.read]="read"
|
[class.read]="read()"
|
||||||
[id]="'quick-nav-page-' + number"
|
[id]="'quick-nav-page-' + pageNumber()"
|
||||||
class="page-wrapper"
|
class="page-wrapper"
|
||||||
>
|
>
|
||||||
<mat-icon [svgIcon]="showDottedIcon ? 'red:excluded-page' : 'red:page'"></mat-icon>
|
<mat-icon [svgIcon]="showDottedIcon() ? 'red:excluded-page' : 'red:page'"></mat-icon>
|
||||||
|
|
||||||
<div class="text">{{ number }}</div>
|
<div class="text">{{ pageNumber() }}</div>
|
||||||
|
|
||||||
<div *ngIf="activeSelection" class="dot"></div>
|
<div *ngIf="activeSelection()" class="dot"></div>
|
||||||
|
|
||||||
<div *ngIf="pageRotationService.rotations()[number]" class="rotated">
|
<div *ngIf="pageRotationService.rotations()[pageNumber()]" class="rotated">
|
||||||
<mat-icon svgIcon="red:rotation"></mat-icon>
|
<mat-icon svgIcon="red:rotation"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, effect, EventEmitter, inject, Input, OnChanges, Output } from '@angular/core';
|
import { Component, computed, effect, inject, input, output, untracked } from '@angular/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { ViewedPagesService } from '@services/files/viewed-pages.service';
|
import { ViewedPagesService } from '@services/files/viewed-pages.service';
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
@ -12,6 +12,9 @@ import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
|||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe, NgIf } from '@angular/common';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
|
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-page-indicator',
|
selector: 'redaction-page-indicator',
|
||||||
@ -20,16 +23,26 @@ import { MatIcon } from '@angular/material/icon';
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgIf, AsyncPipe, MatIcon],
|
imports: [NgIf, AsyncPipe, MatIcon],
|
||||||
})
|
})
|
||||||
export class PageIndicatorComponent implements OnChanges {
|
export class PageIndicatorComponent {
|
||||||
|
readonly #multiSelectService = inject(MultiSelectService);
|
||||||
|
readonly #listingService = inject(AnnotationsListingService);
|
||||||
readonly #config = getConfig<AppConfig>();
|
readonly #config = getConfig<AppConfig>();
|
||||||
readonly #logger = inject(NGXLogger);
|
readonly #logger = inject(NGXLogger);
|
||||||
@Input({ required: true }) number: number;
|
readonly pageNumber = input.required<number>();
|
||||||
@Input() showDottedIcon = false;
|
readonly viewedPages = input.required<ViewedPage[]>();
|
||||||
@Input() activeSelection = false;
|
readonly showDottedIcon = computed(() => this._state.file().excludedPages.includes(this.pageNumber()));
|
||||||
@Input() read = false;
|
readonly selectedLength = toSignal(this.#listingService.selectedLength$);
|
||||||
@Output() readonly pageSelected = new EventEmitter<number>();
|
readonly activeSelection = computed(
|
||||||
|
() =>
|
||||||
|
this.#multiSelectService.active() &&
|
||||||
|
this.selectedLength() &&
|
||||||
|
this.#listingService.selected.find(p => p.pageNumber === this.pageNumber()),
|
||||||
|
);
|
||||||
|
readonly read = computed(() => this.viewedPages().find(p => p.page === this.pageNumber()));
|
||||||
|
readonly pageSelected = output<number>();
|
||||||
pageReadTimeout: number = null;
|
pageReadTimeout: number = null;
|
||||||
readonly assigneeChanged$: Observable<boolean>;
|
readonly assigneeChanged$: Observable<boolean>;
|
||||||
|
isActive = computed(() => untracked(this.pageNumber) === this._pdf.currentPage());
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _viewedPagesService: ViewedPagesService,
|
private readonly _viewedPagesService: ViewedPagesService,
|
||||||
@ -48,27 +61,19 @@ export class PageIndicatorComponent implements OnChanges {
|
|||||||
);
|
);
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
if (this.isActive) {
|
if (this.isActive()) {
|
||||||
this.handlePageRead();
|
this.handlePageRead();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get isActive() {
|
|
||||||
return this.number === this._pdf.currentPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
this.handlePageRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleReadState() {
|
async toggleReadState() {
|
||||||
if (!this._permissionService.canMarkPagesAsViewed(this._state.file())) {
|
if (!this._permissionService.canMarkPagesAsViewed(this._state.file())) {
|
||||||
this.#logger.info('[PAGES] Cannot toggle read state');
|
this.#logger.info('[PAGES] Cannot toggle read state');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const read = untracked(this.read);
|
||||||
if (this.read) {
|
if (read) {
|
||||||
await this.#markPageUnread();
|
await this.#markPageUnread();
|
||||||
} else {
|
} else {
|
||||||
await this.#markPageRead();
|
await this.#markPageRead();
|
||||||
@ -85,9 +90,10 @@ export class PageIndicatorComponent implements OnChanges {
|
|||||||
clearTimeout(this.pageReadTimeout);
|
clearTimeout(this.pageReadTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isActive && !this.read) {
|
const read = untracked(this.read);
|
||||||
|
if (this.isActive() && !read) {
|
||||||
this.pageReadTimeout = window.setTimeout(async () => {
|
this.pageReadTimeout = window.setTimeout(async () => {
|
||||||
if (this.isActive && !this.read) {
|
if (this.isActive() && !read) {
|
||||||
await this.#markPageRead();
|
await this.#markPageRead();
|
||||||
}
|
}
|
||||||
}, this.#config.AUTO_READ_TIME * 1000);
|
}, this.#config.AUTO_READ_TIME * 1000);
|
||||||
@ -95,19 +101,21 @@ export class PageIndicatorComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #markPageRead() {
|
async #markPageRead() {
|
||||||
this.#logger.info('[PAGES] Mark page read', this.number);
|
const number = untracked(this.pageNumber);
|
||||||
|
this.#logger.info('[PAGES] Mark page read', number);
|
||||||
|
|
||||||
const fileId = this._state.fileId;
|
const fileId = this._state.fileId;
|
||||||
await this._viewedPagesService.add({ page: this.number }, this._state.dossierId, fileId);
|
await this._viewedPagesService.add({ page: number }, this._state.dossierId, fileId);
|
||||||
const viewedPage = new ViewedPage({ page: this.number, fileId });
|
const viewedPage = new ViewedPage({ page: number, fileId });
|
||||||
this._viewedPagesMapService.add(fileId, viewedPage);
|
this._viewedPagesMapService.add(fileId, viewedPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #markPageUnread() {
|
async #markPageUnread() {
|
||||||
this.#logger.info('[PAGES] Mark page unread', this.number);
|
const number = untracked(this.pageNumber);
|
||||||
|
this.#logger.info('[PAGES] Mark page unread', number);
|
||||||
|
|
||||||
const fileId = this._state.fileId;
|
const fileId = this._state.fileId;
|
||||||
await this._viewedPagesService.remove(this._state.dossierId, fileId, this.number);
|
await this._viewedPagesService.remove(this._state.dossierId, fileId, number);
|
||||||
this._viewedPagesMapService.delete(fileId, this.number);
|
this._viewedPagesMapService.delete(fileId, number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<div *ngIf="viewedPages$ | async as viewedPages" class="pages" id="pages" [attr.help-mode-key]="'workload_page_list'">
|
<div class="pages" id="pages" [attr.help-mode-key]="'workload_page_list'">
|
||||||
|
@for (pageNumber of pages(); track pageNumber) {
|
||||||
<redaction-page-indicator
|
<redaction-page-indicator
|
||||||
(pageSelected)="pageSelectedByClick($event)"
|
(pageSelected)="pageSelectedByClick($event)"
|
||||||
*ngFor="let pageNumber of pages; trackBy: trackBy"
|
[pageNumber]="pageNumber"
|
||||||
[activeSelection]="pageHasSelection(pageNumber)"
|
[viewedPages]="viewedPages()"
|
||||||
[number]="pageNumber"
|
|
||||||
[read]="!!getViewedPage(viewedPages, pageNumber)"
|
|
||||||
[showDottedIcon]="isPageExcluded(pageNumber)"
|
|
||||||
></redaction-page-indicator>
|
></redaction-page-indicator>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
||||||
import { AfterViewInit, Component, inject, Input } from '@angular/core';
|
import { AfterViewInit, Component, inject, input } from '@angular/core';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
import { ViewedPage } from '@red/domain';
|
|
||||||
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||||
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { MultiSelectService } from '../../services/multi-select.service';
|
|
||||||
import { PageIndicatorComponent } from '../page-indicator/page-indicator.component';
|
import { PageIndicatorComponent } from '../page-indicator/page-indicator.component';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-pages',
|
selector: 'redaction-pages',
|
||||||
@ -19,11 +17,10 @@ import { PageIndicatorComponent } from '../page-indicator/page-indicator.compone
|
|||||||
})
|
})
|
||||||
export class PagesComponent implements AfterViewInit {
|
export class PagesComponent implements AfterViewInit {
|
||||||
readonly #state = inject(FilePreviewStateService);
|
readonly #state = inject(FilePreviewStateService);
|
||||||
readonly #multiSelectService = inject(MultiSelectService);
|
|
||||||
readonly #listingService = inject(AnnotationsListingService);
|
|
||||||
protected readonly _pdf = inject(PdfViewer);
|
protected readonly _pdf = inject(PdfViewer);
|
||||||
@Input({ required: true }) pages: List<number>;
|
readonly pages = input.required<List<number>>();
|
||||||
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
|
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
|
||||||
|
readonly viewedPages = toSignal(this.viewedPages$);
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -45,18 +42,4 @@ export class PagesComponent implements AfterViewInit {
|
|||||||
pageSelectedByClick($event: number): void {
|
pageSelectedByClick($event: number): void {
|
||||||
this._pdf.navigateTo($event);
|
this._pdf.navigateTo($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly trackBy = (_index: number, item: number) => item;
|
|
||||||
|
|
||||||
pageHasSelection(page: number) {
|
|
||||||
return this.#multiSelectService.active() && !!this.#listingService.selected.find(a => a.pageNumber === page);
|
|
||||||
}
|
|
||||||
|
|
||||||
isPageExcluded(pageNumber: number): boolean {
|
|
||||||
return this.#state.file().excludedPages.includes(pageNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
getViewedPage(viewedPages: ViewedPage[], pageNumber: number) {
|
|
||||||
return viewedPages.find(p => p.page === pageNumber);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<cdk-virtual-scroll-viewport [itemSize]="LIST_ITEM_SIZE" [ngStyle]="{ height: redactedTextsAreaHeight + 'px' }">
|
<cdk-virtual-scroll-viewport [itemSize]="LIST_ITEM_SIZE" [ngStyle]="{ height: redactedTextsAreaHeight() + 'px' }">
|
||||||
<ul>
|
<ul>
|
||||||
<li *cdkVirtualFor="let value of values">{{ value }}</li>
|
<li *cdkVirtualFor="let value of values()">{{ value }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, computed, input } from '@angular/core';
|
||||||
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
import { NgStyle } from '@angular/common';
|
import { NgStyle } from '@angular/common';
|
||||||
|
|
||||||
@ -13,10 +13,9 @@ const MAX_ITEMS_DISPLAY = 5;
|
|||||||
styleUrl: './selected-annotations-list.component.scss',
|
styleUrl: './selected-annotations-list.component.scss',
|
||||||
})
|
})
|
||||||
export class SelectedAnnotationsListComponent {
|
export class SelectedAnnotationsListComponent {
|
||||||
@Input({ required: true }) values: string[];
|
values = input.required<string[]>();
|
||||||
|
readonly redactedTextsAreaHeight = computed(() =>
|
||||||
|
this.values.length <= MAX_ITEMS_DISPLAY ? LIST_ITEM_SIZE * this.values.length : LIST_ITEM_SIZE * MAX_ITEMS_DISPLAY,
|
||||||
|
);
|
||||||
protected readonly LIST_ITEM_SIZE = LIST_ITEM_SIZE;
|
protected readonly LIST_ITEM_SIZE = LIST_ITEM_SIZE;
|
||||||
|
|
||||||
get redactedTextsAreaHeight() {
|
|
||||||
return this.values.length <= MAX_ITEMS_DISPLAY ? LIST_ITEM_SIZE * this.values.length : LIST_ITEM_SIZE * MAX_ITEMS_DISPLAY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
<redaction-editable-structured-component-value
|
<redaction-editable-structured-component-value
|
||||||
#editableComponent
|
#editableComponent
|
||||||
[entry]="entry"
|
[entry]="entry"
|
||||||
[canEdit]="canEdit"
|
[canEdit]="canEdit()"
|
||||||
(deselectLast)="deselectLast()"
|
(deselectLast)="deselectLast()"
|
||||||
(revertOverride)="revertOverride($event)"
|
(revertOverride)="revertOverride($event)"
|
||||||
(overrideValue)="overrideValue($event)"
|
(overrideValue)="overrideValue($event)"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, effect, Input, OnInit, signal, ViewChildren } from '@angular/core';
|
import { Component, computed, effect, input, OnInit, signal, viewChild } from '@angular/core';
|
||||||
import { List } from '@common-ui/utils';
|
import { List } from '@common-ui/utils';
|
||||||
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||||
import { ComponentLogEntry, Dictionary, File, IComponentLogEntry, WorkflowFileStatuses } from '@red/domain';
|
import { ComponentLogEntry, Dictionary, File, IComponentLogEntry, WorkflowFileStatuses } from '@red/domain';
|
||||||
@ -21,13 +21,14 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
|
|||||||
imports: [PopupFilterComponent, NgIf, AsyncPipe, TranslateModule, NgForOf, EditableStructuredComponentValueComponent],
|
imports: [PopupFilterComponent, NgIf, AsyncPipe, TranslateModule, NgForOf, EditableStructuredComponentValueComponent],
|
||||||
})
|
})
|
||||||
export class StructuredComponentManagementComponent implements OnInit {
|
export class StructuredComponentManagementComponent implements OnInit {
|
||||||
|
file = input<File>();
|
||||||
|
dictionaries = input<Dictionary[]>();
|
||||||
|
editableComponents = viewChild<List<EditableStructuredComponentValueComponent>>('editableComponent');
|
||||||
|
canEdit = computed(() => this.file().workflowStatus !== WorkflowFileStatuses.APPROVED);
|
||||||
protected readonly componentLogData = signal<ComponentLogEntry[] | undefined>(undefined);
|
protected readonly componentLogData = signal<ComponentLogEntry[] | undefined>(undefined);
|
||||||
protected readonly componentLogData$ = toObservable(this.componentLogData);
|
protected readonly componentLogData$ = toObservable(this.componentLogData);
|
||||||
protected readonly iconButtonTypes = IconButtonTypes;
|
protected readonly iconButtonTypes = IconButtonTypes;
|
||||||
protected displayedComponents$: Observable<ComponentLogEntry[]>;
|
protected displayedComponents$: Observable<ComponentLogEntry[]>;
|
||||||
@Input() file: File;
|
|
||||||
@Input() dictionaries: Dictionary[];
|
|
||||||
@ViewChildren('editableComponent') editableComponents: List<EditableStructuredComponentValueComponent>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _componentLogService: ComponentLogService,
|
private readonly _componentLogService: ComponentLogService,
|
||||||
@ -42,17 +43,13 @@ export class StructuredComponentManagementComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get canEdit() {
|
|
||||||
return this.file.workflowStatus !== WorkflowFileStatuses.APPROVED;
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
await this.#loadData();
|
await this.#loadData();
|
||||||
this.displayedComponents$ = this.#displayedComponents$();
|
this.displayedComponents$ = this.#displayedComponents$();
|
||||||
}
|
}
|
||||||
|
|
||||||
deselectLast() {
|
deselectLast() {
|
||||||
const lastSelected = this.editableComponents.find(c => c.selected);
|
const lastSelected = this.editableComponents().find(c => c.selected);
|
||||||
if (lastSelected) {
|
if (lastSelected) {
|
||||||
lastSelected.deselect();
|
lastSelected.deselect();
|
||||||
}
|
}
|
||||||
@ -61,7 +58,9 @@ export class StructuredComponentManagementComponent implements OnInit {
|
|||||||
async revertOverride(originalKey: string) {
|
async revertOverride(originalKey: string) {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await firstValueFrom(
|
await firstValueFrom(
|
||||||
this._componentLogService.revertOverride(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId, [originalKey]),
|
this._componentLogService.revertOverride(this.file().dossierTemplateId, this.file().dossierId, this.file().fileId, [
|
||||||
|
originalKey,
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
await this.#loadData();
|
await this.#loadData();
|
||||||
}
|
}
|
||||||
@ -69,7 +68,7 @@ export class StructuredComponentManagementComponent implements OnInit {
|
|||||||
async overrideValue(componentLogEntry: IComponentLogEntry) {
|
async overrideValue(componentLogEntry: IComponentLogEntry) {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await firstValueFrom(
|
await firstValueFrom(
|
||||||
this._componentLogService.override(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId, componentLogEntry),
|
this._componentLogService.override(this.file().dossierTemplateId, this.file().dossierId, this.file().fileId, componentLogEntry),
|
||||||
);
|
);
|
||||||
await this.#loadData();
|
await this.#loadData();
|
||||||
}
|
}
|
||||||
@ -84,10 +83,10 @@ export class StructuredComponentManagementComponent implements OnInit {
|
|||||||
async #loadData(): Promise<void> {
|
async #loadData(): Promise<void> {
|
||||||
const componentLogData = await firstValueFrom(
|
const componentLogData = await firstValueFrom(
|
||||||
this._componentLogService.getComponentLogData(
|
this._componentLogService.getComponentLogData(
|
||||||
this.file.dossierTemplateId,
|
this.file().dossierTemplateId,
|
||||||
this.file.dossierId,
|
this.file().dossierId,
|
||||||
this.file.fileId,
|
this.file().fileId,
|
||||||
this.dictionaries,
|
this.dictionaries(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.#computeFilters(componentLogData);
|
this.#computeFilters(componentLogData);
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select formControlName="dictionary" [placeholder]="'add-hint.dialog.content.type-placeholder' | translate">
|
<mat-select formControlName="dictionary" [placeholder]="'add-hint.dialog.content.type-placeholder' | translate">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option
|
<mat-option
|
||||||
*ngFor="let dictionary of dictionaries"
|
*ngFor="let dictionary of dictionaries"
|
||||||
[matTooltip]="dictionary.description"
|
[matTooltip]="dictionary.description"
|
||||||
@ -100,7 +100,7 @@
|
|||||||
[label]="'add-hint.dialog.actions.save' | translate"
|
[label]="'add-hint.dialog.actions.save' | translate"
|
||||||
[submit]="true"
|
[submit]="true"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
[disabled]="disabled"
|
[disabled]="_disabled()"
|
||||||
>
|
>
|
||||||
</iqser-icon-button>
|
</iqser-icon-button>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { NgClass, NgForOf, NgIf, NgStyle } from '@angular/common';
|
import { NgClass, NgForOf, NgIf, NgStyle } from '@angular/common';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, computed, effect, OnInit } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
||||||
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
@ -24,10 +23,10 @@ import { DictionaryService } from '@services/entity-services/dictionary.service'
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { calcTextWidthInPixels, stringToBoolean } from '@utils/functions';
|
import { calcTextWidthInPixels, stringToBoolean } from '@utils/functions';
|
||||||
import { tap } from 'rxjs/operators';
|
|
||||||
import { SystemDefaultOption, SystemDefaults } from '../../../account/utils/dialog-defaults';
|
import { SystemDefaultOption, SystemDefaults } from '../../../account/utils/dialog-defaults';
|
||||||
import { getRedactOrHintOptions } from '../../utils/dialog-options';
|
import { getRedactOrHintOptions } from '../../utils/dialog-options';
|
||||||
import { AddHintData, AddHintResult, RedactOrHintOption, RedactOrHintOptions } from '../../utils/dialog-types';
|
import { AddHintData, AddHintResult, RedactOrHintOption, RedactOrHintOptions } from '../../utils/dialog-types';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
const MAXIMUM_TEXT_AREA_WIDTH = 421;
|
const MAXIMUM_TEXT_AREA_WIDTH = 421;
|
||||||
|
|
||||||
@ -67,11 +66,22 @@ export class AddHintDialogComponent extends IqserDialogComponent<AddHintDialogCo
|
|||||||
readonly maximumSelectedTextWidth = 567;
|
readonly maximumSelectedTextWidth = 567;
|
||||||
dictionaryRequest = false;
|
dictionaryRequest = false;
|
||||||
dictionaries: Dictionary[] = [];
|
dictionaries: Dictionary[] = [];
|
||||||
form!: UntypedFormGroup;
|
form = this.#getForm();
|
||||||
isEditingSelectedText = false;
|
isEditingSelectedText = false;
|
||||||
selectedTextRows = 1;
|
selectedTextRows = 1;
|
||||||
textWidth: number;
|
textWidth: number;
|
||||||
|
|
||||||
|
dictionary = formValueToSignal(this.form.get('dictionary'));
|
||||||
|
displayedDictionaryLabel = computed(() => {
|
||||||
|
if (this.dictionary()) {
|
||||||
|
return this.dictionaries.find(d => d.type === this.dictionary())?.label ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
_disabled = computed(() => this.#isRss || !this.dictionary());
|
||||||
|
readonly optionValue = formValueToSignal<DetailsRadioOption<RedactOrHintOption>>(this.form.get('option'));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
private readonly _activeDossiersService: ActiveDossiersService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
@ -80,7 +90,7 @@ export class AddHintDialogComponent extends IqserDialogComponent<AddHintDialogCo
|
|||||||
private readonly _userPreferences: UserPreferenceService,
|
private readonly _userPreferences: UserPreferenceService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.#dossier = _activeDossiersService.find(this.data.dossierId);
|
this.#dossier = this._activeDossiersService.find(this.data.dossierId);
|
||||||
this.#applyToAllDossiers = this.applyToAll;
|
this.#applyToAllDossiers = this.applyToAll;
|
||||||
this.options = getRedactOrHintOptions(
|
this.options = getRedactOrHintOptions(
|
||||||
this.#dossier,
|
this.#dossier,
|
||||||
@ -92,20 +102,13 @@ export class AddHintDialogComponent extends IqserDialogComponent<AddHintDialogCo
|
|||||||
this.#isRss,
|
this.#isRss,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.form = this.#getForm();
|
|
||||||
this.textWidth = calcTextWidthInPixels(this.form.controls.selectedText.value);
|
this.textWidth = calcTextWidthInPixels(this.form.controls.selectedText.value);
|
||||||
|
|
||||||
this.form
|
effect(() => {
|
||||||
.get('option')
|
this.dictionaryRequest = this.optionValue().value === RedactOrHintOptions.IN_DOSSIER;
|
||||||
.valueChanges.pipe(
|
|
||||||
tap((option: DetailsRadioOption<RedactOrHintOption>) => {
|
|
||||||
this.dictionaryRequest = option.value === RedactOrHintOptions.IN_DOSSIER;
|
|
||||||
this.#setDictionaries();
|
this.#setDictionaries();
|
||||||
this.#resetValues();
|
this.#resetValues();
|
||||||
}),
|
});
|
||||||
takeUntilDestroyed(),
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleEditingSelectedText() {
|
toggleEditingSelectedText() {
|
||||||
@ -120,14 +123,6 @@ export class AddHintDialogComponent extends IqserDialogComponent<AddHintDialogCo
|
|||||||
this.form.patchValue({ selectedText: this.initialText });
|
this.form.patchValue({ selectedText: this.initialText });
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const dictType = this.form.get('dictionary').value;
|
|
||||||
if (dictType) {
|
|
||||||
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get applyToAll() {
|
get applyToAll() {
|
||||||
return this.isSystemDefault || this._userPreferences.getAddHintDefaultExtraOption() === 'undefined'
|
return this.isSystemDefault || this._userPreferences.getAddHintDefaultExtraOption() === 'undefined'
|
||||||
? this.data.applyToAllDossiers ?? true
|
? this.data.applyToAllDossiers ?? true
|
||||||
@ -150,10 +145,6 @@ export class AddHintDialogComponent extends IqserDialogComponent<AddHintDialogCo
|
|||||||
return defaultOption ?? this.options[0];
|
return defaultOption ?? this.options[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
get disabled() {
|
|
||||||
return this.#isRss || !this.form.get('dictionary').value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
this.#setDictionaries();
|
this.#setDictionaries();
|
||||||
this.#resetValues();
|
this.#resetValues();
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<input formControlName="section" name="section" type="text" />
|
<input formControlName="section" name="section" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="this.allRectangles" class="iqser-input-group w-400">
|
<div *ngIf="allRectangles" class="iqser-input-group w-400">
|
||||||
<label [translate]="'change-legal-basis-dialog.content.classification'"></label>
|
<label [translate]="'change-legal-basis-dialog.content.classification'"></label>
|
||||||
<input formControlName="classification" name="classification" type="text" />
|
<input formControlName="classification" name="classification" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import { LegalBasisOption } from '../../utils/dialog-types';
|
|||||||
})
|
})
|
||||||
export class ChangeLegalBasisDialogComponent extends BaseDialogComponent implements OnInit {
|
export class ChangeLegalBasisDialogComponent extends BaseDialogComponent implements OnInit {
|
||||||
legalOptions: LegalBasisOption[] = [];
|
legalOptions: LegalBasisOption[] = [];
|
||||||
|
allRectangles = this._data.annotations.reduce((acc, a) => acc && a.AREA, true);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _justificationsService: JustificationsService,
|
private readonly _justificationsService: JustificationsService,
|
||||||
@ -44,10 +45,6 @@ export class ChangeLegalBasisDialogComponent extends BaseDialogComponent impleme
|
|||||||
this.form = this._getForm();
|
this.form = this._getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
get allRectangles(): boolean {
|
|
||||||
return this._data.annotations.reduce((acc, a) => acc && a.AREA, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this._data.dossier.dossierTemplateId));
|
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this._data.dossier.dossierTemplateId));
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [placeholder]="'add-annotation.dialog.content.type-placeholder' | translate" formControlName="dictionary">
|
<mat-select [placeholder]="'add-annotation.dialog.content.type-placeholder' | translate" formControlName="dictionary">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option
|
<mat-option
|
||||||
*ngFor="let dictionary of dictionaries"
|
*ngFor="let dictionary of dictionaries"
|
||||||
[matTooltip]="dictionary.description"
|
[matTooltip]="dictionary.description"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, computed, inject, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||||
import { Dictionary, IAddRedactionRequest } from '@red/domain';
|
import { Dictionary, IAddRedactionRequest } from '@red/domain';
|
||||||
@ -11,6 +11,7 @@ import { NgForOf } from '@angular/common';
|
|||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './add-annotation-dialog.component.html',
|
templateUrl: './add-annotation-dialog.component.html',
|
||||||
@ -38,6 +39,15 @@ export class AddAnnotationDialogComponent
|
|||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
dictionaries: Dictionary[] = [];
|
dictionaries: Dictionary[] = [];
|
||||||
readonly form = this.#getForm();
|
readonly form = this.#getForm();
|
||||||
|
readonly dictionaryType = formValueToSignal(this.form.controls.dictionary);
|
||||||
|
|
||||||
|
readonly displayedDictionaryLabel = computed(() => {
|
||||||
|
const dictType = this.dictionaryType();
|
||||||
|
if (dictType) {
|
||||||
|
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
@ -46,14 +56,6 @@ export class AddAnnotationDialogComponent
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const dictType = this.form.controls.dictionary.value;
|
|
||||||
if (dictType) {
|
|
||||||
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get disabled() {
|
get disabled() {
|
||||||
return !this.form.controls.dictionary.value;
|
return !this.form.controls.dictionary.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<label [translate]="'edit-redaction.dialog.content.type'"></label>
|
<label [translate]="'edit-redaction.dialog.content.type'"></label>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [placeholder]="'edit-redaction.dialog.content.unchanged' | translate" formControlName="type">
|
<mat-select [placeholder]="'edit-redaction.dialog.content.unchanged' | translate" formControlName="type">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option
|
<mat-option
|
||||||
*ngFor="let dictionary of dictionaries"
|
*ngFor="let dictionary of dictionaries"
|
||||||
[matTooltip]="dictionary.description"
|
[matTooltip]="dictionary.description"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, computed, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
@ -20,6 +20,7 @@ import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select
|
|||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'edit-annotation-dialog.component.html',
|
templateUrl: 'edit-annotation-dialog.component.html',
|
||||||
@ -50,9 +51,19 @@ export class EditAnnotationDialogComponent
|
|||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly redactedTexts: string[];
|
readonly redactedTexts: string[];
|
||||||
|
readonly #dossier: Dossier = this._activeDossiersService.find(this.data.dossierId);
|
||||||
dictionaries: Dictionary[] = [];
|
dictionaries: Dictionary[] = [];
|
||||||
form: UntypedFormGroup;
|
form: UntypedFormGroup = this.#getForm();
|
||||||
readonly #dossier: Dossier;
|
showList = this.data.annotations.every(annotation => annotation.isSkipped || annotation.isRedacted);
|
||||||
|
selectedDictionaryType = formValueToSignal(this.form.get('type'));
|
||||||
|
|
||||||
|
displayedDictionaryLabel = computed(() => {
|
||||||
|
const selectedDictionaryType = this.selectedDictionaryType();
|
||||||
|
if (selectedDictionaryType) {
|
||||||
|
return this.dictionaries.find(d => d.type === selectedDictionaryType)?.label ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
private readonly _activeDossiersService: ActiveDossiersService,
|
||||||
@ -60,25 +71,11 @@ export class EditAnnotationDialogComponent
|
|||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.#dossier = this._activeDossiersService.find(this.data.dossierId);
|
|
||||||
const annotations = this.data.annotations;
|
const annotations = this.data.annotations;
|
||||||
this.redactedTexts = annotations.map(annotation => annotation.value);
|
this.redactedTexts = annotations.map(annotation => annotation.value);
|
||||||
this.form = this.#getForm();
|
|
||||||
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const selectedDictionaryType = this.form.get('type').value;
|
|
||||||
if (selectedDictionaryType) {
|
|
||||||
return this.dictionaries.find(d => d.type === selectedDictionaryType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get showList() {
|
|
||||||
return this.data.annotations.every(annotation => annotation.isSkipped || annotation.isRedacted);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
this.#setTypes();
|
this.#setTypes();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<ul *cdkVirtualFor="let text of redactedTexts; let idx = index">
|
<ul *cdkVirtualFor="let text of redactedTexts; let idx = index">
|
||||||
<li>
|
<li>
|
||||||
{{
|
{{
|
||||||
(isFalsePositive
|
(isFalsePositive()
|
||||||
? 'remove-annotation.dialog.content.list-item-false-positive'
|
? 'remove-annotation.dialog.content.list-item-false-positive'
|
||||||
: 'remove-annotation.dialog.content.list-item'
|
: 'remove-annotation.dialog.content.list-item'
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf } from '@angular/cdk/scrolling';
|
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf } from '@angular/cdk/scrolling';
|
||||||
import { NgIf, NgStyle } from '@angular/common';
|
import { NgIf, NgStyle } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component, computed } from '@angular/core';
|
||||||
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
||||||
@ -17,6 +17,7 @@ import {
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { getRemoveRedactionOptions } from '../../../utils/dialog-options';
|
import { getRemoveRedactionOptions } from '../../../utils/dialog-options';
|
||||||
import { RemoveAnnotationData, RemoveAnnotationOption, RemoveAnnotationOptions, RemoveAnnotationResult } from '../../../utils/dialog-types';
|
import { RemoveAnnotationData, RemoveAnnotationOption, RemoveAnnotationOptions, RemoveAnnotationResult } from '../../../utils/dialog-types';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'remove-annotation-dialog.component.html',
|
templateUrl: 'remove-annotation-dialog.component.html',
|
||||||
@ -46,6 +47,8 @@ export class RemoveAnnotationDialogComponent extends IqserDialogComponent<
|
|||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly options: DetailsRadioOption<RemoveAnnotationOption>[];
|
readonly options: DetailsRadioOption<RemoveAnnotationOption>[];
|
||||||
readonly redactedTexts: string[];
|
readonly redactedTexts: string[];
|
||||||
|
readonly option = formValueToSignal<DetailsRadioOption<RemoveAnnotationOption>>(this.form.get('option'));
|
||||||
|
readonly isFalsePositive = computed(() => this.option().value === RemoveAnnotationOptions.FALSE_POSITIVE);
|
||||||
|
|
||||||
form!: UntypedFormGroup;
|
form!: UntypedFormGroup;
|
||||||
|
|
||||||
@ -56,10 +59,6 @@ export class RemoveAnnotationDialogComponent extends IqserDialogComponent<
|
|||||||
this.form = this.#getForm();
|
this.form = this.#getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
get isFalsePositive(): boolean {
|
|
||||||
return this.form.get('option').value.value === RemoveAnnotationOptions.FALSE_POSITIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
get #applyToAllDossiers(): boolean {
|
get #applyToAllDossiers(): boolean {
|
||||||
const selectedOption = this.form.get('option').value.value;
|
const selectedOption = this.form.get('option').value.value;
|
||||||
return selectedOption === RemoveAnnotationOptions.IN_DOSSIER || selectedOption === RemoveAnnotationOptions.FALSE_POSITIVE;
|
return selectedOption === RemoveAnnotationOptions.IN_DOSSIER || selectedOption === RemoveAnnotationOptions.FALSE_POSITIVE;
|
||||||
|
|||||||
@ -18,6 +18,7 @@ interface RevertValueResult {}
|
|||||||
})
|
})
|
||||||
export class RevertValueDialogComponent extends IqserDialogComponent<RevertValueDialogComponent, RevertValueData, RevertValueResult> {
|
export class RevertValueDialogComponent extends IqserDialogComponent<RevertValueDialogComponent, RevertValueData, RevertValueResult> {
|
||||||
protected readonly entry = this.data.entry;
|
protected readonly entry = this.data.entry;
|
||||||
|
readonly valueDescription = this.#valueDescription;
|
||||||
|
|
||||||
constructor(private readonly _translateService: TranslateService) {
|
constructor(private readonly _translateService: TranslateService) {
|
||||||
super();
|
super();
|
||||||
@ -26,7 +27,7 @@ export class RevertValueDialogComponent extends IqserDialogComponent<RevertValue
|
|||||||
this.close(ConfirmOptions.CONFIRM);
|
this.close(ConfirmOptions.CONFIRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
get valueDescription(): string {
|
get #valueDescription(): string {
|
||||||
const componentRuleString = this._translateService.instant('revert-value-dialog.component-rule');
|
const componentRuleString = this._translateService.instant('revert-value-dialog.component-rule');
|
||||||
const valueDescription = this.entry.componentValues[0]?.valueDescription || '';
|
const valueDescription = this.entry.componentValues[0]?.valueDescription || '';
|
||||||
return `<strong>${componentRuleString}</strong> ${valueDescription}`;
|
return `<strong>${componentRuleString}</strong> ${valueDescription}`;
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
[placeholder]="isBulkEdit ? ('edit-redaction.dialog.content.unchanged' | translate) : ''"
|
[placeholder]="isBulkEdit ? ('edit-redaction.dialog.content.unchanged' | translate) : ''"
|
||||||
formControlName="type"
|
formControlName="type"
|
||||||
>
|
>
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option
|
<mat-option
|
||||||
(click)="typeChanged()"
|
(click)="typeChanged()"
|
||||||
*ngFor="let dictionary of typeSelectOptions"
|
*ngFor="let dictionary of typeSelectOptions"
|
||||||
@ -44,8 +44,8 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="showExtras && !hiddenReason">
|
<ng-container *ngIf="showExtras && !hiddenReason()">
|
||||||
<div [class.required]="!form.controls.reason.disabled" class="iqser-input-group w-450">
|
<div [class.required]="!reasonDisabled()" class="iqser-input-group w-450">
|
||||||
<label [translate]="'edit-redaction.dialog.content.reason'"></label>
|
<label [translate]="'edit-redaction.dialog.content.reason'"></label>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select
|
<mat-select
|
||||||
@ -81,7 +81,7 @@
|
|||||||
<label [translate]="'edit-redaction.dialog.content.section'"></label>
|
<label [translate]="'edit-redaction.dialog.content.section'"></label>
|
||||||
<input
|
<input
|
||||||
[placeholder]="
|
[placeholder]="
|
||||||
isBulkEdit && !hideParagraphPlaceholder ? ('edit-redaction.dialog.content.unchanged' | translate) : ''
|
isBulkEdit && !hideParagraphPlaceholder() ? ('edit-redaction.dialog.content.unchanged' | translate) : ''
|
||||||
"
|
"
|
||||||
formControlName="section"
|
formControlName="section"
|
||||||
name="section"
|
name="section"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, computed, inject, OnInit } from '@angular/core';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
@ -38,6 +38,7 @@ import {
|
|||||||
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
||||||
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
||||||
import { validatePageRange } from '../../utils/form-validators';
|
import { validatePageRange } from '../../utils/form-validators';
|
||||||
|
import { formStatusToSignal, formValueToSignal, isJustOne } from '@common-ui/utils';
|
||||||
import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
|
import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
|
||||||
|
|
||||||
interface TypeSelectOptions {
|
interface TypeSelectOptions {
|
||||||
@ -95,10 +96,33 @@ export class EditRedactionDialogComponent
|
|||||||
typeSelectOptions: TypeSelectOptions[] = [];
|
typeSelectOptions: TypeSelectOptions[] = [];
|
||||||
readonly form = this.#getForm();
|
readonly form = this.#getForm();
|
||||||
hasTypeChanged = false;
|
hasTypeChanged = false;
|
||||||
initialReasonDisabled = this.someSkipped;
|
initialReasonDisabled = this.#someSkipped;
|
||||||
protected readonly roles = Roles;
|
protected readonly roles = Roles;
|
||||||
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
||||||
|
|
||||||
|
readonly formStatus = formStatusToSignal(this.form);
|
||||||
|
readonly formInvalid = computed(() => this.formStatus() === 'INVALID');
|
||||||
|
readonly dictionaryType = formValueToSignal(this.form.controls.type);
|
||||||
|
readonly reasonStatus = formStatusToSignal(this.form.controls.reason);
|
||||||
|
readonly reasonValue = formValueToSignal(this.form.controls.reason);
|
||||||
|
readonly sectionValue = formValueToSignal(this.form.controls.section);
|
||||||
|
readonly displayedDictionaryLabel = computed(() => {
|
||||||
|
const selectedDictionaryType = this.dictionaryType();
|
||||||
|
if (selectedDictionaryType) {
|
||||||
|
return this.typeSelectOptions.find(option => option.type === selectedDictionaryType)?.label ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
readonly reasonDisabled = computed(() => this.reasonStatus() === 'DISABLED');
|
||||||
|
readonly hiddenReason = computed(() => this.isImage && this.reasonDisabled());
|
||||||
|
readonly hideParagraphPlaceholder = computed(
|
||||||
|
() => this.sectionValue() !== this.initialFormValue['section'] || this.#isFieldEmpty('section'),
|
||||||
|
);
|
||||||
|
readonly hideReasonPlaceholder = this.#hideReasonPlaceholder;
|
||||||
|
|
||||||
|
readonly isBulkEdit = !isJustOne(this.annotations);
|
||||||
|
readonly showExtras = (this.isImage && this.isRedacted) || !(this.isImage || this.isHint);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _justificationsService: JustificationsService,
|
private readonly _justificationsService: JustificationsService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
@ -114,54 +138,27 @@ export class EditRedactionDialogComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const selectedDictionaryType = this.form.controls.type.value;
|
|
||||||
if (selectedDictionaryType) {
|
|
||||||
return this.typeSelectOptions.find(option => option.type === selectedDictionaryType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get showExtras() {
|
|
||||||
return (this.isImage && this.isRedacted) || !(this.isImage || this.isHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
get disabled() {
|
get disabled() {
|
||||||
return (
|
return this.formInvalid() || (!this.isImage && this.showExtras && !this.reasonDisabled() ? !this.reasonValue() : false);
|
||||||
this.form.invalid ||
|
|
||||||
(!this.isImage && this.showExtras && !this.form.controls.reason.disabled ? !this.form.controls.reason.value : false)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get someSkipped() {
|
get #someSkipped() {
|
||||||
return this.annotations.some(annotation => annotation.isSkipped);
|
return this.annotations.some(annotation => annotation.isSkipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
get redactBasedTypes() {
|
get #redactBasedTypes() {
|
||||||
return this._dictionaryService.getRedactionTypes(this.#dossier.dossierTemplateId).map(dictionary => dictionary.type);
|
return this._dictionaryService.getRedactionTypes(this.#dossier.dossierTemplateId).map(dictionary => dictionary.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isRedactBasedType() {
|
get #isRedactBasedType() {
|
||||||
return this.redactBasedTypes.includes(this.form.controls.type.value);
|
return this.#redactBasedTypes.includes(this.form.controls.type.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get hideReasonPlaceholder() {
|
get #hideReasonPlaceholder() {
|
||||||
return (this.hasTypeChanged && this.isRedactBasedType) || this.#isFieldEmpty('legalBasisValue');
|
return (this.hasTypeChanged && this.#isRedactBasedType) || this.#isFieldEmpty('legalBasisValue');
|
||||||
}
|
}
|
||||||
|
|
||||||
get hideParagraphPlaceholder() {
|
get #sameType() {
|
||||||
return this.form.controls.section.value !== this.initialFormValue['section'] || this.#isFieldEmpty('section');
|
|
||||||
}
|
|
||||||
|
|
||||||
get hiddenReason() {
|
|
||||||
return this.isImage && this.form.controls.reason.disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isBulkEdit() {
|
|
||||||
return this.annotations.length > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get sameType() {
|
|
||||||
return this.annotations.every(annotation => annotation.type === this.annotations[0].type);
|
return this.annotations.every(annotation => annotation.type === this.annotations[0].type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +193,7 @@ export class EditRedactionDialogComponent
|
|||||||
const selectedDictionaryType = this.form.controls.type.value;
|
const selectedDictionaryType = this.form.controls.type.value;
|
||||||
|
|
||||||
const initialReason = this.form.get('type').value === this.initialFormValue.type && !this.initialReasonDisabled;
|
const initialReason = this.form.get('type').value === this.initialFormValue.type && !this.initialReasonDisabled;
|
||||||
if (this.redactBasedTypes.includes(selectedDictionaryType) || initialReason) {
|
if (this.#redactBasedTypes.includes(selectedDictionaryType) || initialReason) {
|
||||||
this.form.controls.reason.enable();
|
this.form.controls.reason.enable();
|
||||||
this.hasTypeChanged = true;
|
this.hasTypeChanged = true;
|
||||||
if (initialReason) {
|
if (initialReason) {
|
||||||
@ -239,7 +236,7 @@ export class EditRedactionDialogComponent
|
|||||||
this.isImage,
|
this.isImage,
|
||||||
this.isHint,
|
this.isHint,
|
||||||
this.annotations.every(annotation => annotation.isOCR),
|
this.annotations.every(annotation => annotation.isOCR),
|
||||||
this.sameType ? this.annotations[0].type : null,
|
this.#sameType ? this.annotations[0].type : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.typeSelectOptions = this.dictionaries.map(dictionary => ({
|
this.typeSelectOptions = this.dictionaries.map(dictionary => ({
|
||||||
@ -257,10 +254,10 @@ export class EditRedactionDialogComponent
|
|||||||
#getForm() {
|
#getForm() {
|
||||||
const sameSection = this.annotations.every(annotation => annotation.section === this.annotations[0].section);
|
const sameSection = this.annotations.every(annotation => annotation.section === this.annotations[0].section);
|
||||||
return new FormGroup({
|
return new FormGroup({
|
||||||
reason: new FormControl<LegalBasisOption>({ value: null, disabled: this.someSkipped }),
|
reason: new FormControl<LegalBasisOption>({ value: null, disabled: this.#someSkipped }),
|
||||||
comment: new FormControl<string>(null),
|
comment: new FormControl<string>(null),
|
||||||
type: new FormControl<string>({
|
type: new FormControl<string>({
|
||||||
value: this.sameType ? this.annotations[0].type : null,
|
value: this.#sameType ? this.annotations[0].type : null,
|
||||||
disabled: this.isImported,
|
disabled: this.isImported,
|
||||||
}),
|
}),
|
||||||
section: new FormControl<string>({ value: sameSection ? this.annotations[0].section : null, disabled: this.isImported }),
|
section: new FormControl<string>({ value: sameSection ? this.annotations[0].section : null, disabled: this.isImported }),
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<div class="iqser-input-group w-400">
|
<div class="iqser-input-group w-400">
|
||||||
<label [translate]="'manual-annotation.dialog.content.legalBasis'"></label>
|
<label [translate]="'manual-annotation.dialog.content.legalBasis'"></label>
|
||||||
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
|
<input [value]="reasonValue().legalBasis" disabled type="text" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,9 +10,8 @@ import {
|
|||||||
IqserDialogComponent,
|
IqserDialogComponent,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
import { Dossier, ILegalBasisChangeRequest } from '@red/domain';
|
import { ILegalBasisChangeRequest } from '@red/domain';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import {
|
||||||
@ -29,6 +28,7 @@ import { MatFormField } from '@angular/material/form-field';
|
|||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
const DOCUMINE_LEGAL_BASIS = 'n-a.';
|
const DOCUMINE_LEGAL_BASIS = 'n-a.';
|
||||||
|
|
||||||
@ -70,6 +70,13 @@ export class ForceAnnotationDialogComponent
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
legalOptions: LegalBasisOption[] = [];
|
legalOptions: LegalBasisOption[] = [];
|
||||||
|
readonly isImageHint = this.data.annotations.every(annotation => annotation.IMAGE_HINT);
|
||||||
|
readonly isHintDialog = this.data.hint;
|
||||||
|
readonly isImageDialog = this.data.image;
|
||||||
|
readonly dialogTitle = this.#dialogTitle;
|
||||||
|
|
||||||
|
readonly reasonValue = formValueToSignal<ILegalBasisChangeRequest>(this.form.get('reason'));
|
||||||
|
|
||||||
protected readonly roles = Roles;
|
protected readonly roles = Roles;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -81,23 +88,11 @@ export class ForceAnnotationDialogComponent
|
|||||||
this.form = this.#getForm();
|
this.form = this.#getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
get isImageHint() {
|
|
||||||
return this.data.annotations.every(annotation => annotation.IMAGE_HINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isHintDialog() {
|
|
||||||
return this.data.hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isImageDialog() {
|
|
||||||
return this.data.image;
|
|
||||||
}
|
|
||||||
|
|
||||||
get disabled(): boolean {
|
get disabled(): boolean {
|
||||||
return !this.valid;
|
return !this.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
get dialogTitle(): string {
|
get #dialogTitle(): string {
|
||||||
return this.isImageHint
|
return this.isImageHint
|
||||||
? _('manual-annotation.dialog.header.force-redaction-image-hint')
|
? _('manual-annotation.dialog.header.force-redaction-image-hint')
|
||||||
: this.isHintDialog
|
: this.isHintDialog
|
||||||
|
|||||||
@ -9,12 +9,7 @@
|
|||||||
<div class="iqser-input-group required w-150">
|
<div class="iqser-input-group required w-150">
|
||||||
<label translate="highlight-action-dialog.form.color.label"></label>
|
<label translate="highlight-action-dialog.form.color.label"></label>
|
||||||
<input class="hex-color-input" formControlName="color" name="color" type="text" />
|
<input class="hex-color-input" formControlName="color" name="color" type="text" />
|
||||||
<div
|
<div [colorPicker]="colorValue()" [cpDisabled]="true" [style.background]="colorValue()" class="input-icon"></div>
|
||||||
[colorPicker]="form.get('color').value"
|
|
||||||
[cpDisabled]="true"
|
|
||||||
[style.background]="form.get('color').value"
|
|
||||||
class="input-icon"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<iqser-details-radio [displayInRow]="true" [options]="options" class="mt-25" formControlName="option"></iqser-details-radio>
|
<iqser-details-radio [displayInRow]="true" [options]="options" class="mt-25" formControlName="option"></iqser-details-radio>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { highlightsTranslations } from '@translations/highlights-translations';
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { ColorPickerModule } from 'ngx-color-picker';
|
import { ColorPickerModule } from 'ngx-color-picker';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
export interface HighlightActionData {
|
export interface HighlightActionData {
|
||||||
readonly operation: EarmarkOperation;
|
readonly operation: EarmarkOperation;
|
||||||
@ -59,6 +60,8 @@ export class HighlightActionDialogComponent extends BaseDialogComponent {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
readonly colorValue = formValueToSignal(this.form.get('color'));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _dialogRef: MatDialogRef<HighlightActionDialogComponent>,
|
protected readonly _dialogRef: MatDialogRef<HighlightActionDialogComponent>,
|
||||||
private readonly _textHighlightService: EarmarksService,
|
private readonly _textHighlightService: EarmarksService,
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<div *deny="roles.getRss" class="iqser-input-group w-450">
|
<div *deny="roles.getRss" class="iqser-input-group w-450">
|
||||||
<label [translate]="'manual-annotation.dialog.content.legalBasis'"></label>
|
<label [translate]="'manual-annotation.dialog.content.legalBasis'"></label>
|
||||||
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
|
<input [value]="reasonValue().legalBasis" disabled type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group w-450">
|
<div class="iqser-input-group w-450">
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
IqserDialogComponent,
|
IqserDialogComponent,
|
||||||
Toaster,
|
Toaster,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { Dossier, IAddRedactionRequest, SuperTypes } from '@red/domain';
|
import { Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, SuperTypes } from '@red/domain';
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
@ -33,6 +33,7 @@ import { getRectangleRedactOptions } from '../../utils/dialog-options';
|
|||||||
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
||||||
import { SystemDefaults } from '../../../account/utils/dialog-defaults';
|
import { SystemDefaults } from '../../../account/utils/dialog-defaults';
|
||||||
import { validatePageRange } from '../../utils/form-validators';
|
import { validatePageRange } from '../../utils/form-validators';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
export const NON_READABLE_CONTENT = 'non-readable content';
|
export const NON_READABLE_CONTENT = 'non-readable content';
|
||||||
|
|
||||||
@ -68,7 +69,8 @@ export class RectangleAnnotationDialog
|
|||||||
protected readonly options: DetailsRadioOption<RectangleRedactOption>[];
|
protected readonly options: DetailsRadioOption<RectangleRedactOption>[];
|
||||||
protected legalOptions: LegalBasisOption[] = [];
|
protected legalOptions: LegalBasisOption[] = [];
|
||||||
|
|
||||||
readonly form: UntypedFormGroup;
|
readonly form: UntypedFormGroup = this.#getForm();
|
||||||
|
readonly reasonValue = formValueToSignal<ILegalBasisChangeRequest>(this.form.get('reason'));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly activeDossiersService: ActiveDossiersService,
|
private readonly activeDossiersService: ActiveDossiersService,
|
||||||
@ -81,7 +83,6 @@ export class RectangleAnnotationDialog
|
|||||||
|
|
||||||
this.options = getRectangleRedactOptions();
|
this.options = getRectangleRedactOptions();
|
||||||
|
|
||||||
this.form = this.#getForm();
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [placeholder]="'redact-text.dialog.content.unchanged' | translate" formControlName="dictionary">
|
<mat-select [placeholder]="'redact-text.dialog.content.unchanged' | translate" formControlName="dictionary">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option
|
<mat-option
|
||||||
(click)="typeChanged()"
|
(click)="typeChanged()"
|
||||||
*ngFor="let dictionary of dictionaries"
|
*ngFor="let dictionary of dictionaries"
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, computed, effect, inject, OnInit } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
||||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
@ -16,7 +15,6 @@ import { ActiveDossiersService } from '@services/dossiers/active-dossiers.servic
|
|||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
|
||||||
import {
|
import {
|
||||||
SelectedAnnotationsTableComponent,
|
SelectedAnnotationsTableComponent,
|
||||||
ValueColumn,
|
ValueColumn,
|
||||||
@ -30,6 +28,7 @@ import {
|
|||||||
RedactRecommendationResult,
|
RedactRecommendationResult,
|
||||||
ResizeOptions,
|
ResizeOptions,
|
||||||
} from '../../utils/dialog-types';
|
} from '../../utils/dialog-types';
|
||||||
|
import { formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './redact-recommendation-dialog.component.html',
|
templateUrl: './redact-recommendation-dialog.component.html',
|
||||||
@ -81,6 +80,17 @@ export class RedactRecommendationDialogComponent
|
|||||||
{ label: redaction.typeLabel },
|
{ label: redaction.typeLabel },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
readonly dictionaryType = formValueToSignal(this.form.controls.dictionary);
|
||||||
|
readonly optionValue = formValueToSignal<DetailsRadioOption<RedactOrHintOption>>(this.form.controls.option);
|
||||||
|
|
||||||
|
readonly displayedDictionaryLabel = computed(() => {
|
||||||
|
const dictType = this.dictionaryType();
|
||||||
|
if (dictType) {
|
||||||
|
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _justificationsService: JustificationsService,
|
private readonly _justificationsService: JustificationsService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
@ -88,32 +98,19 @@ export class RedactRecommendationDialogComponent
|
|||||||
super();
|
super();
|
||||||
this.options = getRedactOrHintOptions(this.#dossier, this.#applyToAllDossiers, this.data.isApprover, false, true);
|
this.options = getRedactOrHintOptions(this.#dossier, this.#applyToAllDossiers, this.data.isApprover, false, true);
|
||||||
|
|
||||||
this.form.controls.option.valueChanges
|
effect(() => {
|
||||||
.pipe(
|
this.dictionaryRequest = this.optionValue().value === RedactOrHintOptions.IN_DOSSIER;
|
||||||
tap((option: DetailsRadioOption<RedactOrHintOption>) => {
|
|
||||||
this.dictionaryRequest = option.value === RedactOrHintOptions.IN_DOSSIER;
|
|
||||||
this.#setDictionaries();
|
this.#setDictionaries();
|
||||||
this.#resetValues();
|
this.#resetValues();
|
||||||
}),
|
});
|
||||||
takeUntilDestroyed(),
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
|
|
||||||
this.form.controls.option.setValue(this.options[0]);
|
this.form.controls.option.setValue(this.options[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isBulkLocal(): boolean {
|
get #isBulkLocal(): boolean {
|
||||||
return this.form.controls.option.value.value === ResizeOptions.IN_DOCUMENT;
|
return this.form.controls.option.value.value === ResizeOptions.IN_DOCUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const dictType = this.form.controls.dictionary.value;
|
|
||||||
if (dictType) {
|
|
||||||
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get disabled() {
|
get disabled() {
|
||||||
return !this.form.controls.dictionary.value;
|
return !this.form.controls.dictionary.value;
|
||||||
}
|
}
|
||||||
@ -157,7 +154,7 @@ export class RedactRecommendationDialogComponent
|
|||||||
this.close({
|
this.close({
|
||||||
redaction,
|
redaction,
|
||||||
isMulti: this.isMulti,
|
isMulti: this.isMulti,
|
||||||
bulkLocal: this.isBulkLocal,
|
bulkLocal: this.#isBulkLocal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +186,7 @@ export class RedactRecommendationDialogComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
const commentValue = this.form.controls.comment.value;
|
const commentValue = this.form.controls.comment.value;
|
||||||
addRedactionRequest.comment = commentValue ? (this.isBulkLocal ? commentValue : { text: commentValue }) : null;
|
addRedactionRequest.comment = commentValue ? (this.#isBulkLocal ? commentValue : { text: commentValue }) : null;
|
||||||
addRedactionRequest.addToAllDossiers = this.data.isApprover && this.dictionaryRequest && this.#applyToAllDossiers;
|
addRedactionRequest.addToAllDossiers = this.data.isApprover && this.dictionaryRequest && this.#applyToAllDossiers;
|
||||||
return addRedactionRequest;
|
return addRedactionRequest;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,7 +91,7 @@
|
|||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [placeholder]="'redact-text.dialog.content.type-placeholder' | translate" formControlName="dictionary">
|
<mat-select [placeholder]="'redact-text.dialog.content.type-placeholder' | translate" formControlName="dictionary">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel$ | async }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option
|
<mat-option
|
||||||
(click)="typeChanged()"
|
(click)="typeChanged()"
|
||||||
*ngFor="let dictionary of dictionaries"
|
*ngFor="let dictionary of dictionaries"
|
||||||
@ -120,7 +120,7 @@
|
|||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
[disabled]="!form.valid"
|
[disabled]="!isFormValid()"
|
||||||
[label]="'redact-text.dialog.actions.save' | translate"
|
[label]="'redact-text.dialog.actions.save' | translate"
|
||||||
[submit]="true"
|
[submit]="true"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { AsyncPipe, NgClass, NgForOf, NgIf, NgStyle } from '@angular/common';
|
import { AsyncPipe, NgClass, NgForOf, NgIf, NgStyle } from '@angular/common';
|
||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, computed, effect, inject, OnInit } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
@ -17,8 +16,7 @@ import { JustificationsService } from '@services/entity-services/justifications.
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { calcTextWidthInPixels, stringToBoolean } from '@utils/functions';
|
import { calcTextWidthInPixels, stringToBoolean } from '@utils/functions';
|
||||||
import { firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { map, tap } from 'rxjs/operators';
|
|
||||||
import { SystemDefaultOption, SystemDefaults } from '../../../account/utils/dialog-defaults';
|
import { SystemDefaultOption, SystemDefaults } from '../../../account/utils/dialog-defaults';
|
||||||
import { getRedactOrHintOptions } from '../../utils/dialog-options';
|
import { getRedactOrHintOptions } from '../../utils/dialog-options';
|
||||||
import {
|
import {
|
||||||
@ -30,6 +28,7 @@ import {
|
|||||||
ResizeOptions,
|
ResizeOptions,
|
||||||
} from '../../utils/dialog-types';
|
} from '../../utils/dialog-types';
|
||||||
import { enhanceManualRedactionRequest, EnhanceRequestData } from '../../utils/enhance-manual-redaction-request.utils';
|
import { enhanceManualRedactionRequest, EnhanceRequestData } from '../../utils/enhance-manual-redaction-request.utils';
|
||||||
|
import { formStatusToSignal, formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
const MAXIMUM_TEXT_AREA_WIDTH = 421;
|
const MAXIMUM_TEXT_AREA_WIDTH = 421;
|
||||||
|
|
||||||
@ -63,19 +62,33 @@ export class RedactTextDialogComponent
|
|||||||
{
|
{
|
||||||
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
||||||
readonly #manualRedactionTypeExists = inject(DictionaryService).hasManualType(this.#dossier.dossierTemplateId);
|
readonly #manualRedactionTypeExists = inject(DictionaryService).hasManualType(this.#dossier.dossierTemplateId);
|
||||||
#applyToAllDossiers = this.applyToAll;
|
#applyToAllDossiers = this.#applyToAll;
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly initialText = this.data?.manualRedactionEntryWrapper?.manualRedactionEntry?.value;
|
readonly initialText = this.data?.manualRedactionEntryWrapper?.manualRedactionEntry?.value;
|
||||||
readonly form: FormGroup;
|
readonly options: DetailsRadioOption<RedactOrHintOption>[] = getRedactOrHintOptions(
|
||||||
|
this.#dossier,
|
||||||
|
this.#applyToAllDossiers,
|
||||||
|
this.data.isApprover,
|
||||||
|
this.data.isPageExcluded,
|
||||||
|
);
|
||||||
|
readonly form: FormGroup = this.#getForm();
|
||||||
|
readonly formStatus = formStatusToSignal(this.form);
|
||||||
|
readonly isFormValid = computed(() => this.formStatus() === 'VALID');
|
||||||
|
|
||||||
|
readonly dictionaryType = formValueToSignal(this.form.controls.dictionary);
|
||||||
|
readonly displayedDictionaryLabel = computed(() => {
|
||||||
|
return this.dictionaries.find(d => d.type === this.dictionaryType())?.label ?? null;
|
||||||
|
});
|
||||||
|
|
||||||
|
readonly optionValue = formValueToSignal<DetailsRadioOption<RedactOrHintOption>>(this.form.controls.option);
|
||||||
|
|
||||||
dictionaryRequest = false;
|
dictionaryRequest = false;
|
||||||
legalOptions: LegalBasisOption[] = [];
|
legalOptions: LegalBasisOption[] = [];
|
||||||
dictionaries: Dictionary[] = [];
|
dictionaries: Dictionary[] = [];
|
||||||
isEditingSelectedText = false;
|
isEditingSelectedText = false;
|
||||||
modifiedText = this.initialText;
|
modifiedText = this.initialText;
|
||||||
selectedTextRows = 1;
|
selectedTextRows = 1;
|
||||||
readonly options: DetailsRadioOption<RedactOrHintOption>[];
|
|
||||||
readonly displayedDictionaryLabel$: Observable<string>;
|
|
||||||
readonly maximumTextAreaWidth = MAXIMUM_TEXT_AREA_WIDTH;
|
readonly maximumTextAreaWidth = MAXIMUM_TEXT_AREA_WIDTH;
|
||||||
readonly maximumSelectedTextWidth = 567;
|
readonly maximumSelectedTextWidth = 567;
|
||||||
|
|
||||||
@ -88,14 +101,11 @@ export class RedactTextDialogComponent
|
|||||||
private readonly _userPreferences: UserPreferenceService,
|
private readonly _userPreferences: UserPreferenceService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.options = getRedactOrHintOptions(this.#dossier, this.#applyToAllDossiers, this.data.isApprover, this.data.isPageExcluded);
|
|
||||||
this.form = this.#getForm();
|
|
||||||
this.#setupValidators(this.dictionaryRequest ? RedactOrHintOptions.IN_DOSSIER : RedactOrHintOptions.ONLY_HERE);
|
this.#setupValidators(this.dictionaryRequest ? RedactOrHintOptions.IN_DOSSIER : RedactOrHintOptions.ONLY_HERE);
|
||||||
this.textWidth = calcTextWidthInPixels(this.form.controls.selectedText.value);
|
this.textWidth = calcTextWidthInPixels(this.form.controls.selectedText.value);
|
||||||
this.form.controls.option.valueChanges
|
|
||||||
.pipe(
|
effect(() => {
|
||||||
tap((option: DetailsRadioOption<RedactOrHintOption>) => {
|
this.dictionaryRequest = this.optionValue().value === RedactOrHintOptions.IN_DOSSIER;
|
||||||
this.dictionaryRequest = option.value === RedactOrHintOptions.IN_DOSSIER;
|
|
||||||
if (this.dictionaryRequest) {
|
if (this.dictionaryRequest) {
|
||||||
this.#setDictionaries();
|
this.#setDictionaries();
|
||||||
this.form.patchValue({ selectedText: this.modifiedText });
|
this.form.patchValue({ selectedText: this.modifiedText });
|
||||||
@ -104,28 +114,21 @@ export class RedactTextDialogComponent
|
|||||||
this.modifiedText = this.form.controls.selectedText.value;
|
this.modifiedText = this.form.controls.selectedText.value;
|
||||||
this.form.patchValue({ selectedText: this.initialText }, { emitEvent: true });
|
this.form.patchValue({ selectedText: this.initialText }, { emitEvent: true });
|
||||||
}
|
}
|
||||||
this.#setupValidators(option.value);
|
this.#setupValidators(this.optionValue().value);
|
||||||
this.#resetValues();
|
this.#resetValues();
|
||||||
}),
|
});
|
||||||
takeUntilDestroyed(),
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
|
|
||||||
this.displayedDictionaryLabel$ = this.form.controls.dictionary.valueChanges.pipe(
|
|
||||||
map(dictionary => this.dictionaries.find(d => d.type === dictionary)?.label ?? null),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isBulkLocal() {
|
get #isBulkLocal() {
|
||||||
return this.form.controls.option.value.value === ResizeOptions.IN_DOCUMENT;
|
return this.form.controls.option.value.value === ResizeOptions.IN_DOCUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSystemDefault(): boolean {
|
get #isSystemDefault(): boolean {
|
||||||
return this._userPreferences.getAddRedactionDefaultOption() === SystemDefaultOption.SYSTEM_DEFAULT;
|
return this._userPreferences.getAddRedactionDefaultOption() === SystemDefaultOption.SYSTEM_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
get defaultOption() {
|
get #defaultOption() {
|
||||||
const defaultOption = this.isSystemDefault
|
const defaultOption = this.#isSystemDefault
|
||||||
? this.#getOption(SystemDefaults.ADD_REDACTION_DEFAULT)
|
? this.#getOption(SystemDefaults.ADD_REDACTION_DEFAULT)
|
||||||
: this.#getOption(this._userPreferences.getAddRedactionDefaultOption() as RedactOrHintOption);
|
: this.#getOption(this._userPreferences.getAddRedactionDefaultOption() as RedactOrHintOption);
|
||||||
this.dictionaryRequest = defaultOption.value === RedactOrHintOptions.IN_DOSSIER;
|
this.dictionaryRequest = defaultOption.value === RedactOrHintOptions.IN_DOSSIER;
|
||||||
@ -136,9 +139,9 @@ export class RedactTextDialogComponent
|
|||||||
return defaultOption ?? this.options[0];
|
return defaultOption ?? this.options[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
get applyToAll() {
|
get #applyToAll() {
|
||||||
return this.isSystemDefault || this._userPreferences.getAddRedactionDefaultExtraOption() === 'undefined'
|
return this.#isSystemDefault || this._userPreferences.getAddRedactionDefaultExtraOption() === 'undefined'
|
||||||
? this.data.applyToAllDossiers ?? true
|
? (this.data.applyToAllDossiers ?? true)
|
||||||
: stringToBoolean(this._userPreferences.getAddRedactionDefaultExtraOption());
|
: stringToBoolean(this._userPreferences.getAddRedactionDefaultExtraOption());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +189,7 @@ export class RedactTextDialogComponent
|
|||||||
this.close({
|
this.close({
|
||||||
redaction,
|
redaction,
|
||||||
dictionary: this.dictionaries.find(d => d.type === this.form.controls.dictionary.value),
|
dictionary: this.dictionaries.find(d => d.type === this.form.controls.dictionary.value),
|
||||||
bulkLocal: this.isBulkLocal,
|
bulkLocal: this.#isBulkLocal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +235,7 @@ export class RedactTextDialogComponent
|
|||||||
reason: [null as LegalBasisOption],
|
reason: [null as LegalBasisOption],
|
||||||
comment: [null],
|
comment: [null],
|
||||||
dictionary: [this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null],
|
dictionary: [this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null],
|
||||||
option: this.defaultOption,
|
option: this.#defaultOption,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +246,7 @@ export class RedactTextDialogComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
#resetValues() {
|
#resetValues() {
|
||||||
this.#applyToAllDossiers = this.applyToAll;
|
this.#applyToAllDossiers = this.#applyToAll;
|
||||||
this.options[2].additionalCheck.checked = this.#applyToAllDossiers;
|
this.options[2].additionalCheck.checked = this.#applyToAllDossiers;
|
||||||
if (this.dictionaryRequest) {
|
if (this.dictionaryRequest) {
|
||||||
this.form.controls.reason.setValue(null);
|
this.form.controls.reason.setValue(null);
|
||||||
@ -267,7 +270,7 @@ export class RedactTextDialogComponent
|
|||||||
comment: this.form.controls.comment.value,
|
comment: this.form.controls.comment.value,
|
||||||
isApprover: this.data.isApprover,
|
isApprover: this.data.isApprover,
|
||||||
applyToAllDossiers: this.#applyToAllDossiers,
|
applyToAllDossiers: this.#applyToAllDossiers,
|
||||||
bulkLocal: this.isBulkLocal,
|
bulkLocal: this.#isBulkLocal,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { NgStyle } from '@angular/common';
|
import { NgStyle } from '@angular/common';
|
||||||
import { Component, computed } from '@angular/core';
|
import { Component, computed } from '@angular/core';
|
||||||
import { toSignal } from '@angular/core/rxjs-interop';
|
|
||||||
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
||||||
@ -18,7 +17,6 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { stringToBoolean } from '@utils/functions';
|
import { stringToBoolean } from '@utils/functions';
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { SystemDefaultOption, SystemDefaults } from '../../../account/utils/dialog-defaults';
|
import { SystemDefaultOption, SystemDefaults } from '../../../account/utils/dialog-defaults';
|
||||||
import {
|
import {
|
||||||
SelectedAnnotationsTableComponent,
|
SelectedAnnotationsTableComponent,
|
||||||
@ -37,6 +35,8 @@ import {
|
|||||||
import { isJustOne } from '@common-ui/utils';
|
import { isJustOne } from '@common-ui/utils';
|
||||||
import { validatePageRange } from '../../utils/form-validators';
|
import { validatePageRange } from '../../utils/form-validators';
|
||||||
import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
|
import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './remove-redaction-dialog.component.html',
|
templateUrl: './remove-redaction-dialog.component.html',
|
||||||
@ -112,11 +112,12 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
|||||||
readonly redactedTexts = this.data.redactions.map(annotation => annotation.value);
|
readonly redactedTexts = this.data.redactions.map(annotation => annotation.value);
|
||||||
form: UntypedFormGroup = this._formBuilder.group({
|
form: UntypedFormGroup = this._formBuilder.group({
|
||||||
comment: [null],
|
comment: [null],
|
||||||
option: [this.defaultOption, validatePageRange(this.data.file.numberOfPages, true)],
|
option: [this.#defaultOption, validatePageRange(this.data.file.numberOfPages, true)],
|
||||||
});
|
});
|
||||||
|
|
||||||
readonly selectedOption = toSignal(this.form.get('option').valueChanges.pipe(map(value => value.value)));
|
readonly selectedOption = toSignal(this.form.get('option').valueChanges.pipe(map(value => value.value)));
|
||||||
readonly isFalsePositive = computed(() => this.selectedOption() === RemoveRedactionOptions.FALSE_POSITIVE);
|
readonly isFalsePositive = computed(() => this.selectedOption() === RemoveRedactionOptions.FALSE_POSITIVE);
|
||||||
|
readonly hasFalsePositiveOption = !!this.options.find(option => option.value === RemoveRedactionOptions.FALSE_POSITIVE);
|
||||||
readonly tableColumns = computed<ValueColumn[]>(() => [
|
readonly tableColumns = computed<ValueColumn[]>(() => [
|
||||||
{ label: 'Value', width: '25%' },
|
{ label: 'Value', width: '25%' },
|
||||||
{ label: 'Type', width: '25%' },
|
{ label: 'Type', width: '25%' },
|
||||||
@ -135,6 +136,11 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
readonly isBulk = !isJustOne(this.data.redactions);
|
||||||
|
readonly redactedTextsAreaHeight = this.redactedTexts.length <= 10 ? 18 * this.redactedTexts.length : 180;
|
||||||
|
readonly dialogContentHeight = this.options.length * 75 + 230;
|
||||||
|
readonly typeTranslationArg = { type: this.annotationsType };
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _userPreferences: UserPreferenceService,
|
private readonly _userPreferences: UserPreferenceService,
|
||||||
@ -150,32 +156,12 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasFalsePositiveOption() {
|
get #defaultOption() {
|
||||||
return !!this.options.find(option => option.value === RemoveRedactionOptions.FALSE_POSITIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
get defaultOption() {
|
|
||||||
const removeDefaultOption = this.#getOption(this.defaultOptionPreference as RemoveRedactionOption);
|
const removeDefaultOption = this.#getOption(this.defaultOptionPreference as RemoveRedactionOption);
|
||||||
if (!!removeDefaultOption && !removeDefaultOption.disabled) return removeDefaultOption;
|
if (!!removeDefaultOption && !removeDefaultOption.disabled) return removeDefaultOption;
|
||||||
return this.options[0];
|
return this.options[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
get typeTranslationArg() {
|
|
||||||
return { type: this.annotationsType };
|
|
||||||
}
|
|
||||||
|
|
||||||
get isBulk() {
|
|
||||||
return !isJustOne(this.data.redactions);
|
|
||||||
}
|
|
||||||
|
|
||||||
get redactedTextsAreaHeight() {
|
|
||||||
return this.redactedTexts.length <= 10 ? 18 * this.redactedTexts.length : 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
get dialogContentHeight() {
|
|
||||||
return this.options.length * 75 + 230;
|
|
||||||
}
|
|
||||||
|
|
||||||
extraOptionChanged(option: DetailsRadioOption<RemoveRedactionOption | RectangleRedactOption>): void {
|
extraOptionChanged(option: DetailsRadioOption<RemoveRedactionOption | RectangleRedactOption>): void {
|
||||||
if (option.value === RectangleRedactOptions.MULTIPLE_PAGES) {
|
if (option.value === RectangleRedactOptions.MULTIPLE_PAGES) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<div [innerHTML]="'resize-redaction.dialog.header' | translate: { type: dialogHeaderType }" class="dialog-header heading-l"></div>
|
<div [innerHTML]="'resize-redaction.dialog.header' | translate: { type: dialogHeaderType }" class="dialog-header heading-l"></div>
|
||||||
|
|
||||||
<div class="dialog-content redaction">
|
<div class="dialog-content redaction">
|
||||||
<ng-container *ngIf="!redaction.isImage && !redaction.AREA">
|
<ng-container *ngIf="!isImage && !redaction.AREA">
|
||||||
<div class="flex-start">
|
<div class="flex-start">
|
||||||
<label [translate]="'resize-redaction.dialog.content.original-text'"></label>
|
<label [translate]="'resize-redaction.dialog.content.original-text'"></label>
|
||||||
<span class="multi-line-ellipsis"
|
<span class="multi-line-ellipsis"
|
||||||
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select formControlName="dictionary">
|
<mat-select formControlName="dictionary">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel() }}</mat-select-trigger>
|
||||||
<mat-option [value]="entity?.type">
|
<mat-option [value]="entity?.type">
|
||||||
<span> {{ redaction.typeLabel }} </span>
|
<span> {{ redaction.typeLabel }} </span>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { NgIf } from '@angular/common';
|
import { NgIf } from '@angular/common';
|
||||||
import { Component, inject } from '@angular/core';
|
import { Component, computed, inject } from '@angular/core';
|
||||||
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
@ -12,6 +12,13 @@ import { ActiveDossiersService } from '@services/dossiers/active-dossiers.servic
|
|||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { getResizeRedactionOptions } from '../../utils/dialog-options';
|
import { getResizeRedactionOptions } from '../../utils/dialog-options';
|
||||||
import { ResizeOptions, ResizeRedactionData, ResizeRedactionOption, ResizeRedactionResult } from '../../utils/dialog-types';
|
import { ResizeOptions, ResizeRedactionData, ResizeRedactionOption, ResizeRedactionResult } from '../../utils/dialog-types';
|
||||||
|
import { AsControl, formValueToSignal } from '@common-ui/utils';
|
||||||
|
|
||||||
|
interface ResizeForm {
|
||||||
|
comment: string;
|
||||||
|
dictionary: string;
|
||||||
|
option: DetailsRadioOption<ResizeRedactionOption>;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './resize-redaction-dialog.component.html',
|
templateUrl: './resize-redaction-dialog.component.html',
|
||||||
@ -44,11 +51,17 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent<
|
|||||||
readonly dictionaries = inject(DictionariesMapService).get(this.#dossier.dossierTemplateId);
|
readonly dictionaries = inject(DictionariesMapService).get(this.#dossier.dossierTemplateId);
|
||||||
readonly entity = this.dictionaries.find(d => d.type === this.data.redaction.type);
|
readonly entity = this.dictionaries.find(d => d.type === this.data.redaction.type);
|
||||||
readonly redaction = this.data.redaction;
|
readonly redaction = this.data.redaction;
|
||||||
readonly form: FormGroup<{
|
readonly isImage = this.redaction.isImage;
|
||||||
comment: FormControl<string>;
|
readonly form: FormGroup<AsControl<ResizeForm>> = this.#getForm();
|
||||||
dictionary: FormControl<string>;
|
readonly dialogHeaderType = this.data.redaction.HINT ? 'hint' : this.data.redaction.isSkippedImageHint ? 'image' : 'redaction';
|
||||||
option: FormControl<DetailsRadioOption<ResizeRedactionOption>>;
|
readonly dictionaryType = formValueToSignal(this.form.get('dictionary'));
|
||||||
}>;
|
readonly displayedDictionaryLabel = computed(() => {
|
||||||
|
const dictType = this.dictionaryType();
|
||||||
|
if (dictType) {
|
||||||
|
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
constructor(private readonly _formBuilder: FormBuilder) {
|
constructor(private readonly _formBuilder: FormBuilder) {
|
||||||
super();
|
super();
|
||||||
@ -60,19 +73,6 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent<
|
|||||||
this.data.isApprover,
|
this.data.isApprover,
|
||||||
this.data.permissions.canResizeInDictionary,
|
this.data.permissions.canResizeInDictionary,
|
||||||
);
|
);
|
||||||
this.form = this.#getForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
get dialogHeaderType() {
|
|
||||||
return this.data.redaction.HINT ? 'hint' : this.data.redaction.isSkippedImageHint ? 'image' : 'redaction';
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const dictType = this.form.get('dictionary').value;
|
|
||||||
if (dictType) {
|
|
||||||
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
|
|||||||
@ -5,11 +5,12 @@
|
|||||||
|
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
|
@if (isDocumine) {
|
||||||
<redaction-structured-component-management
|
<redaction-structured-component-management
|
||||||
*ngIf="isDocumine"
|
|
||||||
[file]="file"
|
[file]="file"
|
||||||
[dictionaries]="state.dictionaries"
|
[dictionaries]="state.dictionaries()"
|
||||||
></redaction-structured-component-management>
|
></redaction-structured-component-management>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-container" [class.documine-container]="isDocumine">
|
<div class="right-container" [class.documine-container]="isDocumine">
|
||||||
|
|||||||
@ -7,11 +7,9 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|||||||
import { Core } from '@pdftron/webviewer';
|
import { Core } from '@pdftron/webviewer';
|
||||||
import {
|
import {
|
||||||
DictionaryEntryTypes,
|
DictionaryEntryTypes,
|
||||||
DownloadFileTypes,
|
|
||||||
EarmarkOperation,
|
EarmarkOperation,
|
||||||
type IBulkLocalRemoveRequest,
|
type IBulkLocalRemoveRequest,
|
||||||
IBulkRecategorizationRequest,
|
IBulkRecategorizationRequest,
|
||||||
ILegalBasisChangeRequest,
|
|
||||||
IRecategorizationRequest,
|
IRecategorizationRequest,
|
||||||
IRectangle,
|
IRectangle,
|
||||||
type IRemoveRedactionRequest,
|
type IRemoveRedactionRequest,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable, untracked } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||||
@ -78,7 +78,8 @@ export class AnnotationProcessingService {
|
|||||||
const filters: INestedFilter[] = [];
|
const filters: INestedFilter[] = [];
|
||||||
|
|
||||||
this._fileDataService.all?.forEach(a => {
|
this._fileDataService.all?.forEach(a => {
|
||||||
const dictionary = this._state.dictionaries.find(dictionary => dictionary.type === a.type);
|
const dictionaries = untracked(this._state.dictionaries);
|
||||||
|
const dictionary = dictionaries.find(dictionary => dictionary.type === a.type);
|
||||||
const doesTypeExist = !!dictionary;
|
const doesTypeExist = !!dictionary;
|
||||||
if (
|
if (
|
||||||
(this.#isDocumine && !this.#devMode && a.isOCR) ||
|
(this.#isDocumine && !this.#devMode && a.isOCR) ||
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { effect, inject, Injectable, Signal, signal } from '@angular/core';
|
import { effect, inject, Injectable, Signal, signal, untracked } from '@angular/core';
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
import { toObservable } from '@angular/core/rxjs-interop';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
||||||
@ -180,7 +180,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
const file = this._state.file();
|
const file = this._state.file();
|
||||||
let annotations: AnnotationWrapper[] = [];
|
let annotations: AnnotationWrapper[] = [];
|
||||||
const defaultColors = this._defaultColorsService.find(this._state.dossierTemplateId);
|
const defaultColors = this._defaultColorsService.find(this._state.dossierTemplateId);
|
||||||
let dictionaries = this._state.dictionaries;
|
let dictionaries = untracked(this._state.dictionaries);
|
||||||
let checkDictionary = true;
|
let checkDictionary = true;
|
||||||
|
|
||||||
for (const entry of entityLog.entityLogEntry) {
|
for (const entry of entityLog.entityLogEntry) {
|
||||||
|
|||||||
@ -43,12 +43,14 @@ export class FilePreviewStateService {
|
|||||||
readonly blob$: Observable<Blob>;
|
readonly blob$: Observable<Blob>;
|
||||||
readonly componentReferenceIds$: Observable<string[] | null>;
|
readonly componentReferenceIds$: Observable<string[] | null>;
|
||||||
readonly #componentReferenceIds$ = new BehaviorSubject<string[] | null>(null);
|
readonly #componentReferenceIds$ = new BehaviorSubject<string[] | null>(null);
|
||||||
|
readonly componentReferenceIdsSignal: Signal<string[] | null>;
|
||||||
readonly dossierId = getParam(DOSSIER_ID);
|
readonly dossierId = getParam(DOSSIER_ID);
|
||||||
readonly dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
readonly dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||||
readonly fileId = getParam(FILE_ID);
|
readonly fileId = getParam(FILE_ID);
|
||||||
readonly excludedPages: WritableSignal<number[]>;
|
readonly excludedPages: WritableSignal<number[]>;
|
||||||
readonly updateExcludedPagesStyle = computed(() => this.excludedPages());
|
readonly updateExcludedPagesStyle = computed(() => this.excludedPages());
|
||||||
readonly isEditingReviewer = signal(false);
|
readonly isEditingReviewer = signal(false);
|
||||||
|
readonly dictionaries: Signal<Dictionary[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
@ -76,6 +78,14 @@ export class FilePreviewStateService {
|
|||||||
this.blob$ = this.#blob$;
|
this.blob$ = this.#blob$;
|
||||||
|
|
||||||
this.dossierDictionary = toSignal(inject(DossierDictionariesMapService).watch$(this.dossierId, 'dossier_redaction'));
|
this.dossierDictionary = toSignal(inject(DossierDictionariesMapService).watch$(this.dossierId, 'dossier_redaction'));
|
||||||
|
this.dictionaries = computed(() => {
|
||||||
|
const dictionaries = this._dictionariesMapService.get(this.dossierTemplateId);
|
||||||
|
if (this.dossierDictionary()) {
|
||||||
|
dictionaries.push(this.dossierDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionaries;
|
||||||
|
});
|
||||||
this.#dossierFilesChange$
|
this.#dossierFilesChange$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => this._filesService.loadAll(this.dossierId)),
|
switchMap(() => this._filesService.loadAll(this.dossierId)),
|
||||||
@ -92,21 +102,14 @@ export class FilePreviewStateService {
|
|||||||
},
|
},
|
||||||
{ allowSignalWrites: true },
|
{ allowSignalWrites: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.componentReferenceIdsSignal = toSignal(this.componentReferenceIds$);
|
||||||
}
|
}
|
||||||
|
|
||||||
set componentReferenceIds(ids: string[]) {
|
set componentReferenceIds(ids: string[]) {
|
||||||
this.#componentReferenceIds$.next(ids);
|
this.#componentReferenceIds$.next(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
get dictionaries(): Dictionary[] {
|
|
||||||
const dictionaries = this._dictionariesMapService.get(this.dossierTemplateId);
|
|
||||||
if (this.dossierDictionary()) {
|
|
||||||
dictionaries.push(this.dossierDictionary());
|
|
||||||
}
|
|
||||||
|
|
||||||
return dictionaries;
|
|
||||||
}
|
|
||||||
|
|
||||||
get blob(): Promise<Blob> {
|
get blob(): Promise<Blob> {
|
||||||
return firstValueFrom(this.blob$);
|
return firstValueFrom(this.blob$);
|
||||||
}
|
}
|
||||||
@ -133,10 +136,6 @@ export class FilePreviewStateService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get componentReferenceIds() {
|
|
||||||
return this.#componentReferenceIds$.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadBlob(): void {
|
reloadBlob(): void {
|
||||||
this.#reloadBlob$.next(true);
|
this.#reloadBlob$.next(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { inject, Injectable, NgZone } from '@angular/core';
|
import { inject, Injectable, NgZone, untracked } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
||||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||||
@ -135,7 +135,7 @@ export class PdfAnnotationActionsService {
|
|||||||
#getAnnotationsPermissions(annotations: AnnotationWrapper[]): AnnotationPermissions {
|
#getAnnotationsPermissions(annotations: AnnotationWrapper[]): AnnotationPermissions {
|
||||||
const dossier = this.#state.dossier();
|
const dossier = this.#state.dossier();
|
||||||
const isApprover = this.#permissionsService.isApprover(dossier);
|
const isApprover = this.#permissionsService.isApprover(dossier);
|
||||||
const dictionaries = this.#state.dictionaries;
|
const dictionaries = untracked(this.#state.dictionaries);
|
||||||
const autoAnalysisDisabled = this.#state.file().excludedFromAutomaticAnalysis;
|
const autoAnalysisDisabled = this.#state.file().excludedFromAutomaticAnalysis;
|
||||||
const permissions = annotations.map(a =>
|
const permissions = annotations.map(a =>
|
||||||
AnnotationPermissions.forUser(isApprover, a, dictionaries, this.#iqserPermissionsService, autoAnalysisDisabled),
|
AnnotationPermissions.forUser(isApprover, a, dictionaries, this.#iqserPermissionsService, autoAnalysisDisabled),
|
||||||
|
|||||||
@ -1,33 +1,30 @@
|
|||||||
<div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress>
|
<div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress>
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="openEditDossierDialog(dossier.id)"
|
(action)="openEditDossierDialog(dossier().id)"
|
||||||
*allow="roles.dossiers.read; if: currentUser.isUser"
|
*allow="roles.dossiers.read; if: currentUser.isUser"
|
||||||
[attr.help-mode-key]="'edit_dossier'"
|
[attr.help-mode-key]="'edit_dossier'"
|
||||||
[icon]="
|
[icon]="(hasEditDossierRole() && currentUser.isManager) || canEditDossierDictionary() ? 'iqser:edit' : 'red:info'"
|
||||||
((iqserPermissionsService.has$(roles.dossiers.edit) | async) && currentUser.isManager) || canEditDossierDictionary
|
|
||||||
? 'iqser:edit'
|
|
||||||
: 'red:info'
|
|
||||||
"
|
|
||||||
[tooltip]="
|
[tooltip]="
|
||||||
(((iqserPermissionsService.has$(roles.dossiers.edit) | async) && currentUser.isManager) || canEditDossierDictionary
|
((hasEditDossierRole() && currentUser.isManager) || canEditDossierDictionary()
|
||||||
? 'dossier-listing.edit.action'
|
? 'dossier-listing.edit.action'
|
||||||
: 'dossier-listing.dossier-info.action'
|
: 'dossier-listing.dossier-info.action'
|
||||||
) | translate
|
) | translate
|
||||||
"
|
"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
|
@if (displayReanalyseBtn) {
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="reanalyseDossier(dossier)"
|
(action)="reanalyseDossier(dossier())"
|
||||||
*ngIf="displayReanalyseBtn"
|
|
||||||
[tooltip]="'dossier-listing.reanalyse.action' | translate"
|
[tooltip]="'dossier-listing.reanalyse.action' | translate"
|
||||||
icon="iqser:refresh"
|
icon="iqser:refresh"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
}
|
||||||
|
|
||||||
<redaction-file-download-btn
|
<redaction-file-download-btn
|
||||||
[attr.help-mode-key]="isDocumine ? 'template_download_dossier' : 'download_dossier'"
|
[attr.help-mode-key]="isDocumine ? 'template_download_dossier' : 'download_dossier'"
|
||||||
[buttonId]="'download-dossier-files-' + dossier.id"
|
[buttonId]="'download-dossier-files-' + dossier().id"
|
||||||
[disabled]="downloadBtnDisabled"
|
[disabled]="downloadBtnDisabled"
|
||||||
[dossier]="dossier"
|
[dossier]="dossier()"
|
||||||
[files]="files"
|
[files]="files"
|
||||||
dossierDownload
|
dossierDownload
|
||||||
></redaction-file-download-btn>
|
></redaction-file-download-btn>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
import { Component, Input, OnChanges } from '@angular/core';
|
import { Component, computed, input, OnChanges } from '@angular/core';
|
||||||
import { CircleButtonComponent, getConfig, IqserAllowDirective, IqserPermissionsService, largeDialogConfig } from '@iqser/common-ui';
|
import { CircleButtonComponent, getConfig, IqserAllowDirective, IqserPermissionsService, largeDialogConfig } from '@iqser/common-ui';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -13,12 +13,13 @@ import { Roles } from '@users/roles';
|
|||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { EditDossierDialogComponent } from '../../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
|
import { EditDossierDialogComponent } from '../../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
|
||||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-dossiers-listing-actions [dossier]',
|
selector: 'redaction-dossiers-listing-actions [dossier]',
|
||||||
templateUrl: './dossiers-listing-actions.component.html',
|
templateUrl: './dossiers-listing-actions.component.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [LongPressDirective, CircleButtonComponent, IqserAllowDirective, TranslateModule, NgIf, FileDownloadBtnComponent, AsyncPipe],
|
imports: [LongPressDirective, CircleButtonComponent, IqserAllowDirective, TranslateModule, FileDownloadBtnComponent, AsyncPipe],
|
||||||
})
|
})
|
||||||
export class DossiersListingActionsComponent implements OnChanges {
|
export class DossiersListingActionsComponent implements OnChanges {
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
@ -30,7 +31,9 @@ export class DossiersListingActionsComponent implements OnChanges {
|
|||||||
displayReanalyseBtn = false;
|
displayReanalyseBtn = false;
|
||||||
downloadBtnDisabled = false;
|
downloadBtnDisabled = false;
|
||||||
|
|
||||||
@Input() dossier: Dossier;
|
readonly dossier = input<Dossier>();
|
||||||
|
readonly canEditDossierDictionary = computed(() => this.permissionsService.canEditDossierDictionary(this.dossier()));
|
||||||
|
readonly hasEditDossierRole = toSignal(this.iqserPermissionsService.has$(this.roles.dossiers.edit));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _reanalysisService: ReanalysisService,
|
private readonly _reanalysisService: ReanalysisService,
|
||||||
@ -41,14 +44,10 @@ export class DossiersListingActionsComponent implements OnChanges {
|
|||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get canEditDossierDictionary() {
|
|
||||||
return this.permissionsService.canEditDossierDictionary(this.dossier);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.files = this.filesMapService.get(this.dossier.id);
|
this.files = this.filesMapService.get(this.dossier().id);
|
||||||
this.downloadBtnDisabled = this.files.some(file => !file.lastProcessed);
|
this.downloadBtnDisabled = this.files.some(file => !file.lastProcessed);
|
||||||
this.displayReanalyseBtn = this.permissionsService.displayReanalyseBtn(this.dossier) && this.analysisForced;
|
this.displayReanalyseBtn = this.permissionsService.displayReanalyseBtn(this.dossier()) && this.analysisForced;
|
||||||
}
|
}
|
||||||
|
|
||||||
forceReanalysisAction($event: LongPressEvent) {
|
forceReanalysisAction($event: LongPressEvent) {
|
||||||
|
|||||||
@ -30,13 +30,13 @@ import { DossiersService } from '@services/dossiers/dossiers.service';
|
|||||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||||
import { TrashService } from '@services/entity-services/trash.service';
|
import { TrashService } from '@services/entity-services/trash.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { dateWithoutTime, formControlToSignal } from '@utils/functions';
|
import { dateWithoutTime } from '@utils/functions';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||||
import { type EditDossierDialogComponent } from '../edit-dossier-dialog.component';
|
import { type EditDossierDialogComponent } from '../edit-dossier-dialog.component';
|
||||||
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||||
import { AsControl, isJustOne } from '@common-ui/utils';
|
import { AsControl, formValueToSignal, isJustOne } from '@common-ui/utils';
|
||||||
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
|
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
|
||||||
|
|
||||||
interface GeneralInfoForm {
|
interface GeneralInfoForm {
|
||||||
@ -85,8 +85,8 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
|||||||
dueDate: [null],
|
dueDate: [null],
|
||||||
});
|
});
|
||||||
initialFormValue: GeneralInfoForm;
|
initialFormValue: GeneralInfoForm;
|
||||||
readonly dossierStatusIdControl = formControlToSignal(this.form.controls.dossierStatusId);
|
readonly dossierStatusIdControl = formValueToSignal(this.form.controls.dossierStatusId);
|
||||||
readonly dossierTemplateIdControl = formControlToSignal<GeneralInfoForm['dossierTemplateId']>(this.form.controls.dossierTemplateId);
|
readonly dossierTemplateIdControl = formValueToSignal<GeneralInfoForm['dossierTemplateId']>(this.form.controls.dossierTemplateId);
|
||||||
readonly states = signal([null]);
|
readonly states = signal([null]);
|
||||||
readonly stateNameAndColor = computed(() => {
|
readonly stateNameAndColor = computed(() => {
|
||||||
const nameAndColor = {};
|
const nameAndColor = {};
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
<svg:circle
|
||||||
|
[attr.cx]="cx()"
|
||||||
|
[attr.cy]="cy()"
|
||||||
|
[attr.r]="radius()"
|
||||||
|
[attr.stroke-dasharray]="circumference()"
|
||||||
|
[attr.stroke-dashoffset]="strokeDashOffset()"
|
||||||
|
[attr.stroke-width]="strokeWidth()"
|
||||||
|
[attr.stroke]="stroke()"
|
||||||
|
[attr.transform]="circleTransformValue()"
|
||||||
|
[class]="config().color"
|
||||||
|
fill="transparent"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
/>
|
||||||
|
After Width: | Height: | Size: 387 B |
@ -0,0 +1,4 @@
|
|||||||
|
:host {
|
||||||
|
display: contents;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
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