RED-7155, add redact recommandation modal.
This commit is contained in:
parent
599507e262
commit
62e60b253a
@ -0,0 +1,58 @@
|
||||
<section class="dialog">
|
||||
<form (submit)="save()" [formGroup]="form">
|
||||
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content redaction">
|
||||
<div *ngIf="form.get('selectedText').value as selectedText" class="iqser-input-group w-450">
|
||||
<label [translate]="'redact-text.dialog.content.selected-text'" class="selected-text"></label>
|
||||
{{ selectedText }}
|
||||
</div>
|
||||
|
||||
<iqser-details-radio
|
||||
(extraOptionChanged)="extraOptionChanged($event)"
|
||||
[options]="options"
|
||||
formControlName="option"
|
||||
></iqser-details-radio>
|
||||
|
||||
<div *ngIf="dictionaryRequest" class="iqser-input-group required w-450">
|
||||
<label [translate]="'redact-text.dialog.content.type'"></label>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select [placeholder]="'redact-text.dialog.content.type-placeholder' | translate" formControlName="dictionary">
|
||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
||||
<mat-option
|
||||
(click)="typeChanged()"
|
||||
*ngFor="let dictionary of dictionaries"
|
||||
[matTooltip]="dictionary.description"
|
||||
[value]="dictionary.type"
|
||||
matTooltipPosition="after"
|
||||
>
|
||||
<span> {{ dictionary.label }} </span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!dictionaryRequest" class="iqser-input-group w-450">
|
||||
<label [translate]="'redact-text.dialog.content.comment'"></label>
|
||||
<textarea
|
||||
[placeholder]="'redact-text.dialog.content.comment-placeholder' | translate"
|
||||
formControlName="comment"
|
||||
iqserHasScrollbar
|
||||
name="comment"
|
||||
rows="4"
|
||||
type="text"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<iqser-icon-button [label]="'redact-text.dialog.actions.save' | translate" [submit]="true" [type]="iconButtonTypes.primary">
|
||||
</iqser-icon-button>
|
||||
|
||||
<div [translate]="'redact-text.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||
</section>
|
||||
@ -0,0 +1,170 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { Dictionary, Dossier, IAddRedactionRequest, SuperTypes } from '@red/domain';
|
||||
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { Roles } from '@users/roles';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { LegalBasisOption } from '../manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
import { getRedactOrHintOptions, RedactOrHintOption, RedactOrHintOptions } from '../../utils/dialog-options';
|
||||
import { RedactRecommendationData, RedactRecommendationResult } from '../../utils/dialog-types';
|
||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
|
||||
@Component({
|
||||
templateUrl: './redact-recommendation-dialog.component.html',
|
||||
})
|
||||
export class RedactRecommendationDialogComponent
|
||||
extends IqserDialogComponent<RedactRecommendationDialogComponent, RedactRecommendationData, RedactRecommendationResult>
|
||||
implements OnInit
|
||||
{
|
||||
readonly roles = Roles;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly options: DetailsRadioOption<RedactOrHintOption>[];
|
||||
readonly firstEntry: AnnotationWrapper;
|
||||
readonly isMulti: boolean;
|
||||
dictionaryRequest = false;
|
||||
legalOptions: LegalBasisOption[] = [];
|
||||
dictionaries: Dictionary[] = [];
|
||||
form!: UntypedFormGroup;
|
||||
|
||||
#manualRedactionTypeExists = true;
|
||||
#applyToAllDossiers: boolean;
|
||||
|
||||
readonly #dossier: Dossier;
|
||||
|
||||
constructor(
|
||||
private readonly _justificationsService: JustificationsService,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
) {
|
||||
super();
|
||||
this.#dossier = this._activeDossiersService.find(this.data.dossierId);
|
||||
this.#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
||||
this.options = getRedactOrHintOptions(this.#dossier, false, false, this.#applyToAllDossiers, this.data.isApprover);
|
||||
this.#manualRedactionTypeExists = this._dictionaryService.hasManualType(this.#dossier.dossierTemplateId);
|
||||
this.firstEntry = this.data.annotations[0];
|
||||
this.isMulti = this.data.annotations.length > 1;
|
||||
|
||||
this.form = this.#getForm();
|
||||
|
||||
this.form
|
||||
.get('option')
|
||||
.valueChanges.pipe(
|
||||
tap((option: DetailsRadioOption<RedactOrHintOption>) => {
|
||||
this.dictionaryRequest = option.value === RedactOrHintOptions.IN_DOSSIER;
|
||||
this.#setDictionaries();
|
||||
this.#resetValues();
|
||||
}),
|
||||
takeUntilDestroyed(),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.form.get('option').setValue(this.options[1]);
|
||||
}
|
||||
|
||||
get displayedDictionaryLabel() {
|
||||
const dictType = this.form.get('dictionary').value;
|
||||
if (dictType) {
|
||||
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.dictionaryRequest && !this.form.get('dictionary').value;
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.#setDictionaries();
|
||||
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this.#dossier.dossierTemplateId));
|
||||
this.legalOptions = data.map(lbm => ({
|
||||
legalBasis: lbm.reason,
|
||||
description: lbm.description,
|
||||
label: lbm.name,
|
||||
}));
|
||||
this.legalOptions.sort((a, b) => a.label.localeCompare(b.label));
|
||||
this.#selectReason();
|
||||
this.#resetValues();
|
||||
}
|
||||
|
||||
extraOptionChanged(option: DetailsRadioOption<RedactOrHintOption>): void {
|
||||
this.#applyToAllDossiers = option.extraOption.checked;
|
||||
|
||||
this.#setDictionaries();
|
||||
if (this.#applyToAllDossiers && this.form.get('dictionary').value) {
|
||||
const selectedDictionaryLabel = this.form.get('dictionary').value;
|
||||
const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryLabel);
|
||||
if (!selectedDictionary) {
|
||||
this.form.get('dictionary').setValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeChanged() {
|
||||
if (!this.#applyToAllDossiers) {
|
||||
const selectedDictionaryType = this.form.get('dictionary').value;
|
||||
const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryType);
|
||||
this.options[1].extraOption.disabled = selectedDictionary.dossierDictionaryOnly;
|
||||
}
|
||||
}
|
||||
|
||||
save(): void {
|
||||
const redaction = this.#convertRecommendationToRedaction(this.firstEntry);
|
||||
this.dialogRef.close({
|
||||
redaction,
|
||||
isMulti: this.isMulti,
|
||||
});
|
||||
}
|
||||
|
||||
#setDictionaries() {
|
||||
this.dictionaries = this._dictionaryService.getRedactTextDictionaries(this.#dossier.dossierTemplateId, !this.#applyToAllDossiers);
|
||||
}
|
||||
|
||||
#getForm(): UntypedFormGroup {
|
||||
return this._formBuilder.group({
|
||||
selectedText: this.isMulti ? null : this.firstEntry.value,
|
||||
comment: [null],
|
||||
dictionary: [null],
|
||||
option: [null],
|
||||
});
|
||||
}
|
||||
|
||||
#selectReason() {
|
||||
if (this.legalOptions.length === 1) {
|
||||
this.form.get('reason').setValue(this.legalOptions[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#convertRecommendationToRedaction(recommendation: AnnotationWrapper): IAddRedactionRequest {
|
||||
const addRedactionRequest: IAddRedactionRequest = { ...recommendation };
|
||||
addRedactionRequest.type = this.form.get('dictionary').value;
|
||||
addRedactionRequest.value = this.form.get('selectedText').value;
|
||||
|
||||
const selectedType = this.dictionaries.find(d => d.type === addRedactionRequest.type);
|
||||
|
||||
if (selectedType) {
|
||||
addRedactionRequest.addToDictionary = selectedType.hasDictionary;
|
||||
} else {
|
||||
addRedactionRequest.addToDictionary = this.dictionaryRequest && addRedactionRequest.type !== 'dossier_redaction';
|
||||
}
|
||||
|
||||
const commentValue = this.form.get('comment').value;
|
||||
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
|
||||
addRedactionRequest.addToAllDossiers = this.data.isApprover && this.dictionaryRequest && this.#applyToAllDossiers;
|
||||
return addRedactionRequest;
|
||||
}
|
||||
|
||||
#resetValues() {
|
||||
this.#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
||||
if (this.dictionaryRequest) {
|
||||
this.form.get('dictionary').setValue(this.firstEntry.type);
|
||||
return;
|
||||
}
|
||||
this.form.get('dictionary').setValue(this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DetailsRadioOption, IconButtonTypes } from '@iqser/common-ui';
|
||||
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
@ -18,6 +17,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
> {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly options: DetailsRadioOption<RemoveRedactionOption>[];
|
||||
readonly isRecommendation = this.data.redaction.isRecommendation;
|
||||
|
||||
form!: UntypedFormGroup;
|
||||
hint: boolean;
|
||||
|
||||
@ -73,6 +73,7 @@ import { ResizeAnnotationDialogComponent } from './dialogs/docu-mine/resize-anno
|
||||
import { EditAnnotationDialogComponent } from './dialogs/docu-mine/edit-annotation-dialog/edit-annotation-dialog.component';
|
||||
import { EditRedactionDialogComponent } from './dialogs/edit-redaction-dialog/edit-redaction-dialog.component';
|
||||
import { TablesService } from './services/tables.service';
|
||||
import { RedactRecommendationDialogComponent } from './dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
@ -102,6 +103,7 @@ const dialogs = [
|
||||
AddAnnotationDialogComponent,
|
||||
RemoveAnnotationDialogComponent,
|
||||
ResizeAnnotationDialogComponent,
|
||||
RedactRecommendationDialogComponent,
|
||||
];
|
||||
|
||||
const components = [
|
||||
|
||||
@ -14,13 +14,7 @@ import {
|
||||
} from '@red/domain';
|
||||
import { toPosition } from '../utils/pdf-calculation.utils';
|
||||
import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service';
|
||||
import {
|
||||
AcceptRecommendationData,
|
||||
AcceptRecommendationDialogComponent,
|
||||
AcceptRecommendationReturnType,
|
||||
} from '../dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component';
|
||||
import { defaultDialogConfig, getConfig } from '@iqser/common-ui';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { getConfig } from '@iqser/common-ui';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { FilePreviewStateService } from './file-preview-state.service';
|
||||
import { FilePreviewDialogService } from './file-preview-dialog.service';
|
||||
@ -32,7 +26,7 @@ import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.ser
|
||||
import { RemoveRedactionDialogComponent } from '../dialogs/remove-redaction-dialog/remove-redaction-dialog.component';
|
||||
import { IqserDialog } from '@common-ui/dialog/iqser-dialog.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { isJustOne, List, log } from '@iqser/common-ui/lib/utils';
|
||||
import { List, log } from '@iqser/common-ui/lib/utils';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import {
|
||||
EditRedactionData,
|
||||
@ -48,6 +42,7 @@ import { ResizeRedactionDialogComponent } from '../dialogs/resize-redaction-dial
|
||||
import { ResizeAnnotationDialogComponent } from '../dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component';
|
||||
import { EditRedactionDialogComponent } from '../dialogs/edit-redaction-dialog/edit-redaction-dialog.component';
|
||||
import { EditAnnotationDialogComponent } from '../dialogs/docu-mine/edit-annotation-dialog/edit-annotation-dialog.component';
|
||||
import { RedactRecommendationDialogComponent } from '../dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component';
|
||||
|
||||
@Injectable()
|
||||
export class AnnotationActionsService {
|
||||
@ -207,18 +202,19 @@ export class AnnotationActionsService {
|
||||
|
||||
async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) {
|
||||
const { dossierId, fileId } = this._state;
|
||||
const dialogRef = this._dialog.open<AcceptRecommendationDialogComponent, AcceptRecommendationData, AcceptRecommendationReturnType>(
|
||||
AcceptRecommendationDialogComponent,
|
||||
{ ...defaultDialogConfig, autoFocus: true, data: { annotations: recommendations, dossierId } },
|
||||
);
|
||||
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations));
|
||||
await firstValueFrom(dialogClosed).then(({ annotations, comment: commentText }) => {
|
||||
if (isJustOne(annotations) && this._annotationManager.resizingAnnotationId === annotations[0].id) {
|
||||
this.cancelResize(annotations[0]).then();
|
||||
}
|
||||
const comment = commentText ? { text: commentText } : undefined;
|
||||
this.#processObsAndEmit(this._manualRedactionService.addRecommendation(annotations, dossierId, fileId, comment));
|
||||
});
|
||||
const data = this.#getRedactRecommendationDialogData(recommendations);
|
||||
const dialog = this._iqserDialog.openDefault(RedactRecommendationDialogComponent, { data });
|
||||
const result = await dialog.result();
|
||||
console.log(result);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.isMulti && this._annotationManager.resizingAnnotationId === recommendations[0].id) {
|
||||
this.cancelResize(recommendations[0]).then();
|
||||
}
|
||||
|
||||
this.#processObsAndEmit(this._manualRedactionService.addRecommendation(recommendations, result.redaction, dossierId, fileId));
|
||||
}
|
||||
|
||||
async resize(annotationWrapper: AnnotationWrapper) {
|
||||
@ -472,4 +468,17 @@ export class AnnotationActionsService {
|
||||
}
|
||||
return this._iqserDialog.openDefault(EditRedactionDialogComponent, { data });
|
||||
}
|
||||
|
||||
#getRedactRecommendationDialogData(annotations: AnnotationWrapper[]) {
|
||||
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
|
||||
const isApprover = this._permissionsService.isApprover(this._state.dossier());
|
||||
const applyDictionaryUpdatesToAllDossiersByDefault = dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault;
|
||||
|
||||
return {
|
||||
annotations,
|
||||
dossierId: this._state.dossierId,
|
||||
applyToAllDossiers: isApprover ? applyDictionaryUpdatesToAllDossiersByDefault : false,
|
||||
isApprover,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,15 +65,16 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
return firstValueFrom(super.delete({}, url));
|
||||
}
|
||||
|
||||
addRecommendation(annotations: AnnotationWrapper[], dossierId: string, fileId: string, comment = { text: 'Accepted Recommendation' }) {
|
||||
addRecommendation(annotations: AnnotationWrapper[], redaction: IAddRedactionRequest, dossierId: string, fileId: string) {
|
||||
const recommendations: List<IAddRedactionRequest> = annotations.map(annotation => ({
|
||||
addToDictionary: true,
|
||||
addToDictionary: redaction.addToDictionary,
|
||||
addToAllDossiers: redaction.addToAllDossiers,
|
||||
sourceId: annotation.id,
|
||||
value: annotation.value,
|
||||
reason: annotation.legalBasis ?? 'Dictionary Request',
|
||||
positions: annotation.positions,
|
||||
type: annotation.recommendationType,
|
||||
comment,
|
||||
type: redaction.type,
|
||||
comment: redaction.comment,
|
||||
}));
|
||||
return this.addAnnotation(recommendations, dossierId, fileId);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { Dictionary, Dossier, File, IManualRedactionEntry } from '@red/domain';
|
||||
import { Dictionary, Dossier, File, IAddRedactionRequest, IManualRedactionEntry } from '@red/domain';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { DetailsRadioOption } from '@iqser/common-ui';
|
||||
import { RemoveRedactionOption } from './dialog-options';
|
||||
@ -16,6 +16,7 @@ export interface EditRedactionData {
|
||||
annotations: AnnotationWrapper[];
|
||||
dossierId: string;
|
||||
applyToAllDossiers: boolean;
|
||||
isApprover?: boolean;
|
||||
}
|
||||
|
||||
export type AddAnnotationData = RedactTextData;
|
||||
@ -26,6 +27,13 @@ export interface RedactTextResult {
|
||||
dictionary: Dictionary;
|
||||
}
|
||||
|
||||
export type RedactRecommendationData = EditRedactionData;
|
||||
|
||||
export interface RedactRecommendationResult {
|
||||
redaction: IAddRedactionRequest;
|
||||
isMulti: boolean;
|
||||
}
|
||||
|
||||
export interface EditRedactResult {
|
||||
typeChanged: boolean;
|
||||
legalBasis: string;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"ADMIN_CONTACT_NAME": null,
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dom1.iqser.cloud",
|
||||
"API_URL": "https://dan.iqser.cloud",
|
||||
"APP_NAME": "RedactManager",
|
||||
"IS_DOCUMINE": false,
|
||||
"AUTO_READ_TIME": 3,
|
||||
@ -12,7 +12,7 @@
|
||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"OAUTH_IDP_HINT": null,
|
||||
"OAUTH_URL": "https://dom1.iqser.cloud/auth",
|
||||
"OAUTH_URL": "https://dan.iqser.cloud/auth",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural",
|
||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user