Merge branch 'master' into VM/RED-7345

This commit is contained in:
Valentin Mihai 2024-09-23 17:24:03 +03:00
commit 8caa92d663
23 changed files with 215 additions and 271 deletions

View File

@ -21,9 +21,10 @@ localazy update:
paths:
- .yarn-cache/
script:
- git config user.email "${CI_EMAIL}"
- git config user.name "${CI_USERNAME}"
- git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
# - git config user.email "${CI_EMAIL}"
# - git config user.name "${CI_USERNAME}"
# - git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
- git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
- cd tools/localazy
- yarn install --cache-folder .yarn-cache
- yarn start
@ -36,6 +37,8 @@ localazy update:
git status
git commit -m "push back localazy update"
git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME}
# git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
# git push
fi
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"

View File

@ -28,7 +28,7 @@ import { MatIcon } from '@angular/material/icon';
import { SelectComponent } from '@shared/components/select/select.component';
import { MatSuffix } from '@angular/material/form-field';
const downloadTypes = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(type => ({
const downloadTypes = ['ORIGINAL', 'PREVIEW', 'OPTIMIZED_PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(type => ({
key: type,
label: downloadTypesTranslations[type],
}));

View File

@ -3,7 +3,7 @@ import { LicenseService } from '@services/license.service';
import { map } from 'rxjs/operators';
import { ChartDataset } from 'chart.js';
import { ChartBlue, ChartGreen, ChartRed } from '../../utils/constants';
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig, isCurrentMonthAndYear } from '../../utils/functions';
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig, isCurrentMonth } from '../../utils/functions';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { size } from '@iqser/common-ui/lib/utils';
@ -43,7 +43,7 @@ export class LicenseAnalysisCapacityUsageComponent {
#getCapacityDatasets(): ChartDataset[] {
const monthlyData = [...this.licenseService.selectedLicenseReport.monthlyData];
const dataUntilCurrentMonth = getDataUntilCurrentMonth(monthlyData);
if (monthlyData.length === 1 || isCurrentMonthAndYear(monthlyData[0].startDate)) {
if (monthlyData.length === 1 || isCurrentMonth(monthlyData[0].startDate)) {
const empty = { analysedFilesBytes: null } as ILicenseData;
dataUntilCurrentMonth.splice(0, 0, empty);
monthlyData.splice(0, 0, empty);

View File

@ -3,7 +3,7 @@ import { LicenseService } from '@services/license.service';
import { map } from 'rxjs/operators';
import { ChartDataset } from 'chart.js';
import { ChartBlue, ChartGreen, ChartRed } from '../../utils/constants';
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig, isCurrentMonthAndYear } from '../../utils/functions';
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig, isCurrentMonth } from '../../utils/functions';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ILicenseData } from '@red/domain';
@ -40,7 +40,7 @@ export class LicensePageUsageComponent {
#getPagesDatasets(): ChartDataset[] {
const monthlyData = [...this.licenseService.selectedLicenseReport.monthlyData];
const dataUntilCurrentMonth = getDataUntilCurrentMonth(monthlyData);
if (monthlyData.length === 1 || isCurrentMonthAndYear(monthlyData[0].startDate)) {
if (monthlyData.length === 1 || isCurrentMonth(monthlyData[0].startDate)) {
const empty = { numberOfAnalyzedPages: null } as ILicenseData;
dataUntilCurrentMonth.splice(0, 0, empty);
monthlyData.splice(0, 0, empty);

View File

@ -44,7 +44,7 @@ export const getLabelsFromLicense = (license: ILicenseReport) => {
monthIterator = monthIterator.add(1, 'month');
}
if (startMonth.month() === endMonth.month() || startMonth.month() === currentMonth) {
if (startMonth.isSame(endMonth, 'month') || isCurrentMonth(startMonth.toDate())) {
result.splice(0, 0, '');
}
@ -55,6 +55,6 @@ export const getDataUntilCurrentMonth = (monthlyData: ILicenseData[]) => {
return monthlyData.filter(data => dayjs(data.startDate).isSameOrBefore(dayjs(Date.now()), 'month'));
};
export const isCurrentMonthAndYear = (date: Date | string) => {
return dayjs(date).isSame(dayjs(Date.now()), 'month') && dayjs(date).isSame(dayjs(Date.now()), 'year');
export const isCurrentMonth = (date: Date | string) => {
return dayjs(date).isSame(dayjs(Date.now()), 'month');
};

View File

@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { IqserDialog } from '@common-ui/dialog/iqser-dialog.service';
import { getConfig, Toaster } from '@iqser/common-ui';
import { getConfig } from '@iqser/common-ui';
import { List, log } from '@iqser/common-ui/lib/utils';
import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@ -15,7 +15,6 @@ import {
type IRemoveRedactionRequest,
IResizeRequest,
} from '@red/domain';
import { CommentsApiService } from '@services/comments-api.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { PermissionsService } from '@services/permissions.service';
import { firstValueFrom, Observable } from 'rxjs';
@ -52,7 +51,6 @@ import { NON_READABLE_CONTENT } from '../dialogs/manual-redaction-dialog/manual-
@Injectable()
export class AnnotationActionsService {
readonly #isDocumine = getConfig().IS_DOCUMINE;
readonly #commentsApiService = inject(CommentsApiService);
constructor(
private readonly _manualRedactionService: ManualRedactionService,
@ -67,7 +65,6 @@ export class AnnotationActionsService {
private readonly _skippedService: SkippedService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _permissionsService: PermissionsService,
private readonly _toaster: Toaster,
) {}
removeHighlights(highlights: AnnotationWrapper[]): void {
@ -126,7 +123,11 @@ export class AnnotationActionsService {
}
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => {
const body = { annotationId: annotation.id, type: result.type ?? annotation.type };
const body: IRecategorizationRequest = {
annotationId: annotation.id,
type: result.type ?? annotation.type,
comment: result.comment,
};
if (!this.#isDocumine) {
return {
...body,
@ -149,16 +150,6 @@ export class AnnotationActionsService {
)
.pipe(log()),
);
if (result.comment) {
try {
for (const a of annotations) {
await this.#commentsApiService.add(result.comment, a.id, dossierId, fileId);
}
} catch (error) {
this._toaster.rawError(error.error.message);
}
}
}
async removeRedaction(redactions: AnnotationWrapper[], permissions: AnnotationPermissions) {

View File

@ -1,62 +1,64 @@
<div class="cell filename">
<div [matTooltip]="item.filename" class="table-item-title heading" matTooltipPosition="above">
<span
*ngIf="item.highlights.filename; else defaultFilename"
[innerHTML]="sanitize(item.highlights.filename[0])"
class="highlights"
></span>
<ng-template #defaultFilename>{{ item.filename }}</ng-template>
</div>
<ng-container *ngIf="item.highlights['sections.text'] as highlights">
<div *ngIf="highlights.length > 0" class="small-label">
<span [innerHTML]="sanitize(highlights[0])" class="highlights"></span>
<a [routerLink]="item.routerLink" class="item-link">
<div class="cell filename">
<div [matTooltip]="item.filename" class="table-item-title heading" matTooltipPosition="above">
<span
*ngIf="item.highlights.filename; else defaultFilename"
[innerHTML]="sanitize(item.highlights.filename[0])"
class="highlights"
></span>
<ng-template #defaultFilename>{{ item.filename }}</ng-template>
</div>
<div *ngIf="highlights.length > 1" class="small-label">
<span [innerHTML]="sanitize(highlights[1])" class="highlights"></span>
<ng-container *ngIf="item.highlights['sections.text'] as highlights">
<div *ngIf="highlights.length > 0" class="small-label">
<span [innerHTML]="sanitize(highlights[0])" class="highlights"></span>
</div>
<div *ngIf="highlights.length > 1" class="small-label">
<span [innerHTML]="sanitize(highlights[1])" class="highlights"></span>
</div>
</ng-container>
<div *ngIf="item.unmatched?.length && item.unmatched as unmatched" class="small-label">
<span>
{{ 'search-screen.missing' | translate }}:<span *ngFor="let term of unmatched"
>&nbsp;<s>{{ term }}</s></span
>.&nbsp;{{ 'search-screen.must-contain' | translate }}:
<span (click)="mustContain.emit(term)" *ngFor="let term of unmatched" iqserStopPropagation
>&nbsp;<u>{{ term }}</u></span
>
</span>
</div>
</ng-container>
<div *ngIf="item.unmatched?.length && item.unmatched as unmatched" class="small-label">
<span>
{{ 'search-screen.missing' | translate }}:<span *ngFor="let term of unmatched"
>&nbsp;<s>{{ term }}</s></span
>.&nbsp;{{ 'search-screen.must-contain' | translate }}:
<span (click)="mustContain.emit(term)" *ngFor="let term of unmatched" iqserStopPropagation
>&nbsp;<u>{{ term }}</u></span
>
</span>
</div>
</div>
<div class="cell">
<iqser-initials-avatar [user]="item.assignee" [withName]="true"></iqser-initials-avatar>
</div>
<div class="cell">
<iqser-status-bar
[configs]="[
{
color: item.status,
label: fileStatusTranslations[item.status] | translate,
length: 1,
cssClass: 'all-caps-label'
}
]"
[small]="true"
></iqser-status-bar>
</div>
<div class="cell small-label full-opacity stats-subtitle">
<div>
<mat-icon *ngIf="item.archived" svgIcon="red:archive"></mat-icon>
<a [routerLink]="routerLink" iqserStopPropagation> {{ item.dossierName }}</a>
<div class="cell">
<iqser-initials-avatar [user]="item.assignee" [withName]="true"></iqser-initials-avatar>
</div>
</div>
<div class="cell small-label stats-subtitle">
<div>
<mat-icon svgIcon="iqser:pages"></mat-icon>
{{ item.numberOfPages }}
<div class="cell">
<iqser-status-bar
[configs]="[
{
color: item.status,
label: fileStatusTranslations[item.status] | translate,
length: 1,
cssClass: 'all-caps-label',
},
]"
[small]="true"
></iqser-status-bar>
</div>
</div>
<div class="cell small-label full-opacity stats-subtitle">
<div>
<mat-icon *ngIf="item.archived" svgIcon="red:archive"></mat-icon>
<a [routerLink]="routerLink" iqserStopPropagation> {{ item.dossierName }}</a>
</div>
</div>
<div class="cell small-label stats-subtitle">
<div>
<mat-icon svgIcon="iqser:pages"></mat-icon>
{{ item.numberOfPages }}
</div>
</div>
</a>

View File

@ -7,3 +7,8 @@
background-color: var(--iqser-highlight-color);
}
}
a.item-link {
display: contents;
@include common-mixins.clear-a;
}

View File

@ -18,12 +18,15 @@
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:entries"></mat-icon>
{{
dictionary.entries.length +
dictionary.falsePositiveEntries.length +
dictionary.falseRecommendationEntries.length
}}
entries
<span
[translateParams]="{
count:
dictionary.entries.length +
dictionary.falsePositiveEntries.length +
dictionary.falseRecommendationEntries.length,
}"
translate="edit-dossier-dialog.dictionary.entries-count"
></span>
</div>
</div>
</div>
@ -36,12 +39,12 @@
<div class="flex-align-items-center">
{{ selectedDictionary?.label }}
<iqser-circle-button
*ngIf="selectedDictionary.dossierDictionaryOnly && selectedDictionary.hasDictionary"
(action)="openEditDictionaryModal()"
*ngIf="selectedDictionary.dossierDictionaryOnly && selectedDictionary.hasDictionary"
[size]="20"
[tooltip]="'edit-dossier-dialog.dictionary.edit-button-tooltip' | translate"
icon="iqser:edit"
class="p-left-8"
icon="iqser:edit"
></iqser-circle-button>
</div>
<div class="small-label stats-subtitle">
@ -68,23 +71,6 @@
</div>
</div>
</div>
<div *ngIf="!selectedDictionary.hint" [class.read-only]="!canEdit" class="header-right flex">
<iqser-icon-button
(click)="selectEntryType(entryTypes.ENTRY)"
[active]="activeEntryType === entryTypes.ENTRY"
[label]="'edit-dossier-dialog.dictionary.to-redact' | translate"
></iqser-icon-button>
<iqser-icon-button
(click)="selectEntryType(entryTypes.FALSE_POSITIVE)"
[active]="activeEntryType === entryTypes.FALSE_POSITIVE"
[label]="'edit-dossier-dialog.dictionary.false-positives' | translate"
></iqser-icon-button>
<iqser-icon-button
(click)="selectEntryType(entryTypes.FALSE_RECOMMENDATION)"
[active]="activeEntryType === entryTypes.FALSE_RECOMMENDATION"
[label]="'edit-dossier-dialog.dictionary.false-recommendations' | translate"
></iqser-icon-button>
</div>
</div>
<redaction-dictionary-manager
@ -97,6 +83,32 @@
[selectedDictionaryTypeLabel]="selectedDictionary.label"
[selectedDictionaryType]="selectedDictionary.type"
[withFloatingActions]="false"
></redaction-dictionary-manager>
>
<ng-container slot="typeSwitch">
<div *ngIf="!selectedDictionary.hint" [class.read-only]="!canEdit" class="header-right flex">
<iqser-icon-button
(click)="selectEntryType(entryTypes.ENTRY)"
[active]="activeEntryType === entryTypes.ENTRY"
[label]="'edit-dossier-dialog.dictionary.to-redact' | translate: { count: selectedDictionary.entries.length }"
></iqser-icon-button>
<iqser-icon-button
(click)="selectEntryType(entryTypes.FALSE_POSITIVE)"
[active]="activeEntryType === entryTypes.FALSE_POSITIVE"
[label]="
'edit-dossier-dialog.dictionary.false-positives'
| translate: { count: selectedDictionary.falsePositiveEntries.length }
"
></iqser-icon-button>
<iqser-icon-button
(click)="selectEntryType(entryTypes.FALSE_RECOMMENDATION)"
[active]="activeEntryType === entryTypes.FALSE_RECOMMENDATION"
[label]="
'edit-dossier-dialog.dictionary.false-recommendations'
| translate: { count: selectedDictionary.falseRecommendationEntries.length }
"
></iqser-icon-button>
</div>
</ng-container>
</redaction-dictionary-manager>
</div>
</div>

View File

@ -42,12 +42,16 @@ export class EditDossierDownloadPackageComponent
{
#existsWatermarks$: Observable<boolean>;
form: FormGroup;
downloadTypes: { key: DownloadFileType; label: string }[] = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(
(type: DownloadFileType) => ({
key: type,
label: downloadTypesTranslations[type],
}),
);
downloadTypes: { key: DownloadFileType; label: string }[] = [
'ORIGINAL',
'PREVIEW',
'OPTIMIZED_PREVIEW',
'DELTA_PREVIEW',
'REDACTED',
].map((type: DownloadFileType) => ({
key: type,
label: downloadTypesTranslations[type],
}));
availableReportTypes: IReportTemplate[] = [];
readonly roles = Roles;
@Input() dossier: Dossier;

View File

@ -1,29 +1,7 @@
<div class="content-container">
<div class="actions-bar">
<div class="flex-align-items-center mr-32">
<div class="iqser-input-group w-250">
<input
#inputElement
(keyup)="searchChanged(searchText)"
[(ngModel)]="searchText"
[class.with-matches]="searchText.length > 0"
[placeholder]="'dictionary-overview.search' | translate"
type="text"
/>
<div class="input-icons">
<div *ngIf="searchText.length === 0" class="no-input">
<mat-icon svgIcon="iqser:search"></mat-icon>
</div>
<div *ngIf="searchText.length > 0" class="with-input">
{{ currentMatch + '/' + findMatches.length }}
<mat-icon (click)="previousSearchMatch()" class="pointer" svgIcon="red:arrow-up"></mat-icon>
<mat-icon (click)="nextSearchMatch()" class="pointer" svgIcon="iqser:arrow-down"></mat-icon>
<mat-icon (click)="revert(); inputElement.focus()" class="pointer" svgIcon="iqser:close"></mat-icon>
</div>
</div>
</div>
<ng-content select="[slot=typeSwitch]"></ng-content>
<iqser-circle-button
(action)="download()"
@ -33,6 +11,13 @@
class="ml-8"
icon="iqser:download"
></iqser-circle-button>
<iqser-circle-button
(action)="editor.openFindPanel()"
[matTooltip]="'dictionary-overview.search' | translate"
class="ml-8"
icon="iqser:search"
></iqser-circle-button>
</div>
<div class="compare">

View File

@ -8,9 +8,7 @@ import { DossierTemplatesService } from '@services/dossier-templates/dossier-tem
import { EditorComponent } from '@shared/components/editor/editor.component';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { saveAs } from 'file-saver';
import { Debounce, List } from '@iqser/common-ui/lib/utils';
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import FindMatch = monaco.editor.FindMatch;
import { List } from '@iqser/common-ui/lib/utils';
import { firstValueFrom } from 'rxjs';
import { MatIcon } from '@angular/material/icon';
import { NgForOf, NgIf } from '@angular/common';
@ -23,7 +21,6 @@ import { MatOption, MatSelect } from '@angular/material/select';
import { MatDivider } from '@angular/material/divider';
const COMPARE_ENTRIES_ERROR = 'compare-entries-error';
const SMOOTH_SCROLL = 0;
const HELP_MODE_KEYS = {
dictionary: 'dictionary_entity',
'false-positive': 'false_recommendations_entity',
@ -53,8 +50,6 @@ const HELP_MODE_KEYS = {
],
})
export class DictionaryManagerComponent implements OnChanges, OnInit {
private _searchDecorations: string[] = [];
readonly #currentTab = window.location.href.split('/').pop();
@Input() type: DictionaryType = 'dictionary';
@Input() entityType?: string;
@Input() currentDossierId: string;
@ -73,11 +68,8 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
readonly iconButtonTypes = IconButtonTypes;
dossiers: Dossier[];
dossierTemplates: DossierTemplate[] = this.dossierTemplatesService.all;
currentMatch = 0;
findMatches: FindMatch[] = [];
diffEditorText = '';
showDiffEditor = false;
searchText = '';
selectDossier = { dossierName: _('dictionary-overview.compare.select-dossier') } as Dossier;
selectDictionary = {
label: _('dictionary-overview.compare.select-dictionary'),
@ -85,10 +77,11 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
selectDossierTemplate = { name: _('dictionary-overview.compare.select-dossier-template') } as DossierTemplate;
compare = false;
dictionaries: List<Dictionary> = this.#dictionaries;
protected initialDossierTemplateId: string;
readonly #currentTab = window.location.href.split('/').pop();
#dossierTemplate = this.dossierTemplatesService.all[0];
#dossier = this.selectDossier;
#dictionary = this.selectDictionary;
protected initialDossierTemplateId: string;
constructor(
private readonly _dictionaryService: DictionaryService,
@ -99,11 +92,6 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
readonly dossierTemplatesService: DossierTemplatesService,
) {}
ngOnInit() {
this.initialDossierTemplateId = this.currentDossierTemplateId;
this.#updateDropdownsOptions();
}
get selectedDossierTemplate() {
return this.#dossierTemplate;
}
@ -150,6 +138,7 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
if (dictionary.type) {
this.selectedDictionaryType = dictionary.type;
this.#dictionary = dictionary;
console.log(dictionary);
this.#onDossierChanged(this.#dossier.dossierTemplateId).then(entries => this.#updateDiffEditorText(entries));
}
}
@ -185,6 +174,11 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
);
}
ngOnInit() {
this.initialDossierTemplateId = this.currentDossierTemplateId;
this.#updateDropdownsOptions();
}
download(): void {
const content = this.editor.currentEntries.join('\n');
const blob = new Blob([content], {
@ -195,35 +189,6 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
revert() {
this.editor?.revert();
this.searchText = '';
this.searchChanged('');
}
@Debounce()
searchChanged(text: string) {
this.findMatches = this.#getMatches(text.toLowerCase());
this.#applySearchDecorations();
this.currentMatch = 0;
this.nextSearchMatch();
}
nextSearchMatch(): void {
if (this.findMatches.length <= 0) {
return;
}
this.currentMatch = this.currentMatch < this.findMatches.length ? this.currentMatch + 1 : 1;
this.#scrollToCurrentMatch();
}
previousSearchMatch(): void {
if (this.findMatches.length <= 0) {
return;
}
this.currentMatch = this.currentMatch > 1 ? this.currentMatch - 1 : this.findMatches.length;
this.#scrollToCurrentMatch();
}
toggleCompareMode() {
@ -246,30 +211,6 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
}
}
#applySearchDecorations() {
this._searchDecorations = this.editor.codeEditor?.deltaDecorations(this._searchDecorations, []) || [];
const decorations = this.findMatches.map(match => this.#getSearchDecoration(match));
this._searchDecorations = this.editor.codeEditor?.deltaDecorations(this._searchDecorations, decorations) || [];
}
#getMatches(text: string): FindMatch[] {
const model = this.editor.codeEditor?.getModel();
return model?.findMatches(text, false, false, false, null, false) || [];
}
#getSearchDecoration(match: FindMatch): IModelDeltaDecoration {
return { range: match.range, options: { inlineClassName: 'search-marker' } };
}
#scrollToCurrentMatch(): void {
const range = this.findMatches[this.currentMatch - 1].range;
this.editor.codeEditor.setSelection(range);
this.editor.codeEditor.revealLineInCenter(range.startLineNumber, SMOOTH_SCROLL);
}
async #onDossierChanged(dossierTemplateId: string, dossierId?: string) {
let dictionary: IDictionary;
if (dossierId === 'template') {

View File

@ -5,15 +5,15 @@ import { Subject } from 'rxjs';
import { debounceTime, filter, tap } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { List, OnChange } from '@iqser/common-ui/lib/utils';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import ICodeEditor = monaco.editor.ICodeEditor;
import IDiffEditor = monaco.editor.IDiffEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import ILineChange = monaco.editor.ILineChange;
import IEditorMouseEvent = monaco.editor.IEditorMouseEvent;
import { MonacoEditorModule, MonacoStandaloneCodeEditor, MonacoStandaloneDiffEditor } from '@materia-ui/ngx-monaco-editor';
import { FormsModule } from '@angular/forms';
import { NgIf } from '@angular/common';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import IDiffEditor = monaco.editor.IDiffEditor;
import ICodeEditor = monaco.editor.ICodeEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import ILineChange = monaco.editor.ILineChange;
import IEditorMouseEvent = monaco.editor.IEditorMouseEvent;
const MIN_WORD_LENGTH = 2;
const lineChangeToDecoration = ({ originalEndLineNumber, originalStartLineNumber }: ILineChange) =>
@ -35,10 +35,6 @@ const notZero = (lineChange: ILineChange) => lineChange.originalEndLineNumber !=
imports: [MonacoEditorModule, FormsModule, NgIf],
})
export class EditorComponent implements OnInit, OnChanges {
#initialEntriesSet = new Set<string>();
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
protected readonly _editorTextChanged$ = new Subject<string>();
@Input() showDiffEditor = false;
@Input() diffEditorText: string;
@Input() @OnChange<List, EditorComponent>('revert') initialEntries: List;
@ -51,6 +47,10 @@ export class EditorComponent implements OnInit, OnChanges {
editorOptions: IStandaloneEditorConstructionOptions = {};
codeEditor: ICodeEditor;
value: string;
protected readonly _editorTextChanged$ = new Subject<string>();
#initialEntriesSet = new Set<string>();
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
constructor(
private readonly _loadingService: LoadingService,
@ -84,6 +84,11 @@ export class EditorComponent implements OnInit, OnChanges {
return this.currentEntries.length;
}
async openFindPanel(): Promise<void> {
const editor = this.showDiffEditor ? this._diffEditor.getOriginalEditor() : this.codeEditor;
await editor.getAction('actions.find').run();
}
onPaste(event: ClipboardEvent) {
if ((event.target as HTMLTextAreaElement).ariaRoleDescription === 'editor') {
this._loadingService.start();

View File

@ -71,12 +71,16 @@ export class AddDossierDialogComponent extends BaseDialogComponent implements On
readonly roles = Roles;
readonly iconButtonTypes = IconButtonTypes;
hasDueDate = false;
readonly downloadTypes: { key: DownloadFileType; label: string }[] = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(
(type: DownloadFileType) => ({
key: type,
label: downloadTypesTranslations[type],
}),
);
readonly downloadTypes: { key: DownloadFileType; label: string }[] = [
'ORIGINAL',
'PREVIEW',
'OPTIMIZED_PREVIEW',
'DELTA_PREVIEW',
'REDACTED',
].map((type: DownloadFileType) => ({
key: type,
label: downloadTypesTranslations[type],
}));
dossierTemplates: IDossierTemplate[];
availableReportTypes: IReportTemplate[] = [];
dossierTemplateId: string;

View File

@ -90,7 +90,7 @@ export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialog
}
get #formDownloadTypes() {
return ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED']
return ['ORIGINAL', 'PREVIEW', 'OPTIMIZED_PREVIEW', 'DELTA_PREVIEW', 'REDACTED']
.map((type: DownloadFileType) => ({
key: type,
label: downloadTypesForDownloadTranslations[type],

View File

@ -1,7 +1,7 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { EntitiesService, getConfig, isIqserDevMode, QueryParam, Toaster } from '@iqser/common-ui';
import { EntitiesService, isIqserDevMode, Toaster } from '@iqser/common-ui';
import { List } from '@iqser/common-ui/lib/utils';
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IDictionary, IUpdateDictionary, SuperTypes } from '@red/domain';
import { firstValueFrom, forkJoin, Observable } from 'rxjs';
@ -112,23 +112,18 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
}
entriesToAdd.push(entry);
}
const deletedEntries: Array<string> = [];
const entriesToDelete: Array<string> = [];
const entriesSet = new Set(entries);
for (let i = 0; i < initialEntries.length; i++) {
const entry = initialEntries.at(i);
if (entriesSet.has(entry)) {
continue;
}
deletedEntries.push(entry);
entriesToDelete.push(entry);
}
try {
if (deletedEntries.length) {
await this.#deleteEntries(deletedEntries, dossierTemplateId, type, dictionaryEntryType, dossierId);
}
if (entriesToAdd.length) {
await this.#addEntries(entriesToAdd, dossierTemplateId, type, dictionaryEntryType, dossierId);
}
await this.#updateEntries(entriesToAdd, entriesToDelete, dossierTemplateId, type, dictionaryEntryType, dossierId);
if (showToast) {
this._toaster.success(_('dictionary-overview.success.generic'));
@ -249,30 +244,20 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
return forkJoin(requests);
}
/**
* Add dictionary entries with entry type.
*/
#addEntries(entries: List, dossierTemplateId: string, type: string, dictionaryEntryType: DictionaryEntryType, dossierId: string) {
const queryParams: List<QueryParam> = [
{ key: 'dossierId', value: dossierId },
{ key: 'dictionaryEntryType', value: dictionaryEntryType },
];
const url = `${this._defaultModelPath}/${type}/${dossierTemplateId}`;
return firstValueFrom(this._post(entries, url, queryParams));
}
/**
* Delete dictionary entries with entry type.
*/
#deleteEntries(entries: List, dossierTemplateId: string, type: string, dictionaryEntryType: DictionaryEntryType, dossierId: string) {
#updateEntries(
entriesToAdd: List,
entriesToDelete: List,
dossierTemplateId: string,
type: string,
dictionaryEntryType: DictionaryEntryType,
dossierId: string,
) {
const queryParams = [
{ key: 'dossierId', value: dossierId },
{ key: 'dictionaryEntryType', value: dictionaryEntryType },
];
const url = `${this._defaultModelPath}/delete/${type}/${dossierTemplateId}`;
return firstValueFrom(this._post(entries, url, queryParams));
const url = `${this._defaultModelPath}/update/${type}/${dossierTemplateId}`;
return firstValueFrom(this._post({ entriesToAdd, entriesToDelete }, url, queryParams));
}
#extractDossierLevelTypes(dossierId: string) {

View File

@ -401,11 +401,7 @@ export class PermissionsService {
}
#canReanalyseFile(file: File, dossier: Dossier): boolean {
return (
dossier.isActive &&
((this.isAssigneeOrApprover(file, dossier) && file.analysisRequired) ||
(file.isError && (this.isOwner(dossier) || this.isFileAssignee(file))))
);
return dossier.isActive && ((this.isAssigneeOrApprover(file, dossier) && file.analysisRequired) || file.isError);
}
#canEnableAutoAnalysis(file: File, dossier: Dossier): boolean {

View File

@ -4,6 +4,7 @@ import { DownloadFileType } from '@red/domain';
export const downloadTypesTranslations: { [key in DownloadFileType]: string } = {
ORIGINAL: _('download-type.original'),
PREVIEW: _('download-type.preview'),
OPTIMIZED_PREVIEW: _('download-type.optimized-preview'),
REDACTED: _('download-type.redacted'),
ANNOTATED: _('download-type.annotated'),
FLATTEN: _('download-type.flatten'),
@ -13,6 +14,7 @@ export const downloadTypesTranslations: { [key in DownloadFileType]: string } =
export const downloadTypesForDownloadTranslations: { [key in DownloadFileType]: string } = {
ORIGINAL: _('download-type.original'),
PREVIEW: _('download-type.preview'),
OPTIMIZED_PREVIEW: _('download-type.optimized-preview'),
REDACTED: _('download-type.redacted-only'),
ANNOTATED: _('download-type.annotated'),
FLATTEN: _('download-type.flatten'),

View File

@ -1167,6 +1167,7 @@
"delta-preview": "Delta-PDF",
"flatten": "Verflachte PDF",
"label": "{length} Dokumenten{length, plural, one{typ} other{typen}}",
"optimized-preview": "Optimized Preview PDF",
"original": "Optimierte PDF",
"preview": "Vorschau-PDF",
"redacted": "Geschwärzte PDF",
@ -1233,11 +1234,12 @@
"title": "{label} bearbeiten"
},
"entries": "{length} {length, plural, one{Eintrag} other{Einträge}}",
"entries-count": "",
"false-positive-entries": "{length} {length, plural, one{Falsch-Positiver} other{Falsch-Positive}}",
"false-positives": "Falsch-Positive",
"false-positives": "Falsch-Positive ({count})",
"false-recommendation-entries": "{length} {length, plural, one{falsche Empfehlung} other{falsche Empfehlungen}}",
"false-recommendations": "Falsche Empfehlungen",
"to-redact": "Schwärzungen"
"false-recommendations": "Falsche Empfehlungen ({count})",
"to-redact": "Schwärzungen ({count})"
},
"general-info": {
"form": {

View File

@ -1167,6 +1167,7 @@
"delta-preview": "Delta PDF",
"flatten": "Flatten PDF",
"label": "{length} document {length, plural, one{version} other{versions}}",
"optimized-preview": "Optimized Preview PDF",
"original": "Optimized PDF",
"preview": "Preview PDF",
"redacted": "Redacted PDF",
@ -1233,11 +1234,12 @@
"title": "Edit {label}"
},
"entries": "{length} {length, plural, one{entry} other{entries}} to redact",
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positive-entries": "{length} false positive {length, plural, one{entry} other{entries}}",
"false-positives": "False positives",
"false-positives": "False positives ({count})",
"false-recommendation-entries": "{length} false recommendation {length, plural, one{entry} other{entries}}",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
"false-recommendations": "False recommendations ({count})",
"to-redact": "Entries ({count})"
},
"general-info": {
"form": {
@ -1263,7 +1265,7 @@
"choose-download": "Select the documents for your download:",
"dictionary": "Dictionaries",
"dossier-attributes": "Dossier attributes",
"dossier-dictionary": "Dictionaries",
"dossier-dictionary": "Dossier entries",
"dossier-info": "Dossier info",
"download-package": "Download package",
"general-info": "General information",

View File

@ -1167,6 +1167,7 @@
"delta-preview": "Delta PDF",
"flatten": "PDF verflachen",
"label": "{length} document {length, plural, one{version} other{versions}}",
"optimized-preview": "Optimized Preview PDF",
"original": "Optimiertes PDF",
"preview": "PDF-Vorschau",
"redacted": "geschwärztes PDF",
@ -1233,11 +1234,12 @@
"title": ""
},
"entries": "{length} {length, plural, one{entry} other{entries}} to {hint, select, true{annotate} other{redact}}",
"entries-count": "",
"false-positive-entries": "{length} false positive {length, plural, one{entry} other{entries}}",
"false-positives": "False positives",
"false-positives": "False positives ({count})",
"false-recommendation-entries": "{length} false recommendation {length, plural, one{entry} other{entries}}",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
"false-recommendations": "False recommendations ({count})",
"to-redact": "To redact ({count})"
},
"general-info": {
"form": {
@ -1651,7 +1653,7 @@
},
"app-name": {
"label": "Name der Applikation",
"placeholder": "RedactManager"
"placeholder": "DocuMine"
},
"form": {
"auth": "Authentifizierung aktivieren",

View File

@ -1167,6 +1167,7 @@
"delta-preview": "Delta PDF",
"flatten": "Flatten PDF",
"label": "{length} document {length, plural, one{version} other{versions}}",
"optimized-preview": "Optimized Preview PDF",
"original": "Optimized PDF",
"preview": "Preview PDF",
"redacted": "Redacted PDF",
@ -1233,11 +1234,12 @@
"title": ""
},
"entries": "{length} {length, plural, one{entry} other{entries}} to {hint, select, true{annotate} other{redact}}",
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positive-entries": "{length} false positive {length, plural, one{entry} other{entries}}",
"false-positives": "False positives",
"false-positives": "False positives ({count})",
"false-recommendation-entries": "{length} false recommendation {length, plural, one{entry} other{entries}}",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
"false-recommendations": "False recommendations ({count})",
"to-redact": "To redact ({count})"
},
"general-info": {
"form": {
@ -1651,7 +1653,7 @@
},
"app-name": {
"label": "Display name",
"placeholder": "RedactManager"
"placeholder": "DocuMine"
},
"form": {
"auth": "Enable authentication",

View File

@ -3,6 +3,7 @@ export const DownloadFileTypes = {
FLATTEN: 'FLATTEN',
ORIGINAL: 'ORIGINAL',
PREVIEW: 'PREVIEW',
OPTIMIZED_PREVIEW: 'OPTIMIZED_PREVIEW',
REDACTED: 'REDACTED',
DELTA_PREVIEW: 'DELTA_PREVIEW',
} as const;