Compare commits

...

32 Commits

Author SHA1 Message Date
Nicoleta Panaghiu
d883472ed3 RED-10405: added labels and filtering for separate file pending types. 2024-11-13 11:02:20 +02:00
Adina Țeudan
de238c0a8e RED-10422 - improved number of requests with help of new changes endpoint 2024-11-11 18:21:24 +02:00
Adina Țeudan
67c9e54df0 RED-10414 Removed offset and fixed notification polling 2024-11-06 15:57:12 +02:00
Adina Țeudan
7ac3d3e5b6 Updated common-ui 2024-11-06 15:56:01 +02:00
Nicoleta Panaghiu
44050c0373 RED-10258: add all available fonts. 2024-11-05 11:17:06 +02:00
Nicoleta Panaghiu
67e50478e7 RED-9985: prevent filter categories from collapsing upon selection. 2024-09-26 11:55:52 +03:00
Nicoleta Panaghiu
edd709df6f RED-9993: fixed license labels. 2024-09-23 15:58:01 +03:00
Nicoleta Panaghiu
05b6f88d64 RED-9900: removed help mode helper elements from hidden buttons. 2024-09-19 17:36:31 +03:00
Nicoleta Panaghiu
7027de6f6e RED-10050: fixed navigating to doc with filtered annotations issue. 2024-09-18 13:54:39 +03:00
Nicoleta Panaghiu
7beddc6138 RED-9993: fixed license information. 2024-09-13 11:03:20 +03:00
Valentin Mihai
71980486a2 RED-10016 - DM: Permanent loading triggerd when trying to edit the component 2024-09-10 16:08:40 +03:00
Nicoleta Panaghiu
1081f8ad1c RED-9372: update common ui. 2024-09-10 11:43:26 +03:00
Nicoleta Panaghiu
435b9f16a9 RED-9372: fixed scrolling on dossier details. 2024-09-10 11:39:52 +03:00
Nicoleta Panaghiu
52f124adff RED-9372: update common ui. 2024-09-05 17:20:47 +03:00
Nicoleta Panaghiu
1171c0d78f RED-9372: fixed annotation details moving on hover. 2024-09-05 17:19:53 +03:00
Nicoleta Panaghiu
6cedf8afb0 RED-9950: increased component management section width by 5%. 2024-09-05 14:59:45 +03:00
Valentin Mihai
1b2f14ec89 RED-9962 - RM-163: Components values are not updated in the UI until page is refreshed 2024-09-04 16:22:30 +03:00
Nicoleta Panaghiu
dc66b8a708 RED-9950: decreased component management width. 2024-09-04 14:41:00 +03:00
Nicoleta Panaghiu
e2cd71dd24 RED-9950: removed more margins. 2024-09-03 12:24:04 +03:00
Nicoleta Panaghiu
3777c12134 RED-9950: increased document viewer space. 2024-08-29 17:29:14 +03:00
Nicoleta Panaghiu
3bfa4796b0 RED-9951: made document info scrollable & fixed paginator position. 2024-08-29 13:04:51 +03:00
Nicoleta Panaghiu
c0c6232f0a RED-9887: fixed save button being disabled. 2024-08-28 13:23:01 +03:00
Nicoleta Panaghiu
d52b8b0d87 RED-9946: added tooltip with valueDescription. 2024-08-27 15:10:36 +03:00
Nicoleta Panaghiu
61b869675e RED-9887: fixed component fields cancel and revert actions. 2024-08-27 14:58:43 +03:00
Nicoleta Panaghiu
be743fa9d7 RED-9824: fixed toaster notification message for edit annotation action. 2024-08-21 11:36:19 +03:00
Marius Schummer
e5a3366ec7 Correction of translation wording 2024-08-20 15:37:54 +02:00
Nicoleta Panaghiu
4a49e68c4a RED-9889: escape html in component values. 2024-08-20 15:13:14 +03:00
Marius Schummer
15a675515d Adjustments of default messages for empty annotation column (relating to RED-9788) 2024-08-15 16:10:27 +02:00
Valentin Mihai
3f8a53b86c RED-9260 - Component Management UI 2024-08-15 16:51:21 +03:00
Valentin Mihai
ae0510add8 RED-9201 - UI for Component Mapping Tables 2024-08-14 19:34:00 +03:00
Nicoleta Panaghiu
ca51e31de8 RED-9777: added help button on edit and remove annotation dialogs. 2024-08-14 11:48:46 +03:00
Nicoleta Panaghiu
02addbbc16 RED-9874: added user preference for overwrite file option. 2024-08-13 15:20:10 +03:00
339 changed files with 1325 additions and 453 deletions

View File

@ -31,6 +31,11 @@
{{ 'preferences-screen.form.help-mode-dialog' | translate }}
</mat-checkbox>
</div>
<div class="iqser-input-group">
<mat-checkbox color="primary" formControlName="overwriteFileOption">
{{ 'preferences-screen.form.overwrite-file-option' | translate }}
</mat-checkbox>
</div>
</ng-container>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import {
@ -27,6 +27,7 @@ interface PreferencesForm {
// warnings preferences
loadAllAnnotationsWarning: boolean;
helpModeDialog: boolean;
overwriteFileOption: boolean;
[k: string]: any;
}
@ -57,6 +58,12 @@ const Screens = {
],
})
export class PreferencesComponent extends BaseFormComponent implements OnInit {
readonly #formBuilder = inject(FormBuilder);
readonly #permissionsService = inject(IqserPermissionsService);
readonly #changeRef = inject(ChangeDetectorRef);
readonly #loadingService = inject(LoadingService);
readonly #userPreferenceService = inject(UserPreferenceService);
readonly form: FormGroup<AsControl<PreferencesForm>>;
readonly currentScreen: Screen;
readonly screens = Screens;
@ -65,76 +72,87 @@ export class PreferencesComponent extends BaseFormComponent implements OnInit {
readonly config = getConfig();
readonly isIqserDevMode = isIqserDevMode();
constructor(
route: ActivatedRoute,
readonly userPreferenceService: UserPreferenceService,
private readonly _formBuilder: FormBuilder,
private readonly _permissionsService: IqserPermissionsService,
private readonly _changeRef: ChangeDetectorRef,
private readonly _loadingService: LoadingService,
) {
get #isOverwriteFileOptionActive() {
return !(this.#userPreferenceService.getOverwriteFileOption() === 'undefined');
}
constructor(route: ActivatedRoute) {
super();
this.form = this._formBuilder.group({
this.form = this.#formBuilder.group({
// preferences
autoExpandFiltersOnActions: [this.userPreferenceService.getAutoExpandFiltersOnActions()],
tableExtractionType: [this.userPreferenceService.getTableExtractionType()],
autoExpandFiltersOnActions: [this.#userPreferenceService.getAutoExpandFiltersOnActions()],
tableExtractionType: [this.#userPreferenceService.getTableExtractionType()],
// warnings preferences
loadAllAnnotationsWarning: [this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
helpModeDialog: [this.userPreferenceService.getBool(KEYS.helpModeDialog)],
loadAllAnnotationsWarning: [this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
helpModeDialog: [this.#userPreferenceService.getBool(KEYS.helpModeDialog)],
overwriteFileOption: [this.#isOverwriteFileOptionActive],
});
if (!this._permissionsService.has(Roles.managePreferences)) {
if (!this.#permissionsService.has(Roles.managePreferences)) {
this.form.disable();
}
if (!this._permissionsService.has(Roles.getTables)) {
if (!this.#permissionsService.has(Roles.getTables)) {
this.form.controls.tableExtractionType.disable();
}
if (!this.#isOverwriteFileOptionActive) {
this.form.controls.overwriteFileOption.disable();
}
this.initialFormValue = this.form.getRawValue();
this.currentScreen = route.snapshot.data.screen;
}
ngOnInit() {
this._loadingService.stop();
this.#loadingService.stop();
}
async save(): Promise<any> {
if (this.form.controls.autoExpandFiltersOnActions.value !== this.userPreferenceService.getAutoExpandFiltersOnActions()) {
await this.userPreferenceService.toggleAutoExpandFiltersOnActions();
if (this.form.controls.autoExpandFiltersOnActions.value !== this.#userPreferenceService.getAutoExpandFiltersOnActions()) {
await this.#userPreferenceService.toggleAutoExpandFiltersOnActions();
}
if (this.form.controls.tableExtractionType.value !== this.userPreferenceService.getTableExtractionType()) {
await this.userPreferenceService.save(PreferencesKeys.tableExtractionType, this.form.controls.tableExtractionType.value);
if (this.form.controls.tableExtractionType.value !== this.#userPreferenceService.getTableExtractionType()) {
await this.#userPreferenceService.save(PreferencesKeys.tableExtractionType, this.form.controls.tableExtractionType.value);
}
if (
this.form.controls.loadAllAnnotationsWarning.value !==
this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)
this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)
) {
await this.userPreferenceService.save(
await this.#userPreferenceService.save(
PreferencesKeys.loadAllAnnotationsWarning,
String(this.form.controls.loadAllAnnotationsWarning.value),
);
}
if (this.form.controls.helpModeDialog.value !== this.userPreferenceService.getBool(KEYS.helpModeDialog)) {
await this.userPreferenceService.save(KEYS.helpModeDialog, String(this.form.controls.helpModeDialog.value));
if (this.form.controls.helpModeDialog.value !== this.#userPreferenceService.getBool(KEYS.helpModeDialog)) {
await this.#userPreferenceService.save(KEYS.helpModeDialog, String(this.form.controls.helpModeDialog.value));
}
await this.userPreferenceService.reload();
if (this.form.controls.overwriteFileOption.enabled && !this.form.controls.overwriteFileOption.value) {
await this.#userPreferenceService.saveOverwriteFileOption('undefined');
}
await this.#userPreferenceService.reload();
this.#patchValues();
this.initialFormValue = this.form.getRawValue();
this._changeRef.markForCheck();
this.#changeRef.markForCheck();
}
#patchValues() {
this.form.patchValue({
autoExpandFiltersOnActions: this.userPreferenceService.getAutoExpandFiltersOnActions(),
tableExtractionType: this.userPreferenceService.getTableExtractionType(),
loadAllAnnotationsWarning: this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning),
helpModeDialog: this.userPreferenceService.getBool(KEYS.helpModeDialog),
autoExpandFiltersOnActions: this.#userPreferenceService.getAutoExpandFiltersOnActions(),
tableExtractionType: this.#userPreferenceService.getTableExtractionType(),
loadAllAnnotationsWarning: this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning),
helpModeDialog: this.#userPreferenceService.getBool(KEYS.helpModeDialog),
overwriteFileOption: this.#isOverwriteFileOptionActive,
});
if (!this.#isOverwriteFileOptionActive) {
this.form.controls.overwriteFileOption.disable();
}
}
}

View File

@ -50,9 +50,11 @@
icon="iqser:trash"
></iqser-circle-button>
}
@if (selectedComponent?.id === component.id) {
<mat-icon class="arrow-right" svgIcon="red:arrow-right"></mat-icon>
}
<mat-icon
[class.not-visible]="selectedComponent?.id !== component.id"
class="arrow-right"
svgIcon="red:arrow-right"
></mat-icon>
</div>
</div>
</div>

View File

@ -38,6 +38,10 @@
.arrow-right {
transform: scale(0.7);
&.not-visible {
visibility: hidden;
}
}
}
}
@ -45,6 +49,8 @@
.content-container {
background-color: var(--iqser-grey-6);
height: 100vh;
display: flex;
flex-direction: column;
.content-header {
display: flex;
@ -61,6 +67,7 @@
}
.content {
flex: 1;
display: flex;
gap: 20px;
overflow: hidden;
@ -68,7 +75,8 @@
.components-list {
flex: 1;
background-color: var(--iqser-white);
width: 100%;
display: flex;
flex-direction: column;
.header {
height: 30px;

View File

@ -30,7 +30,7 @@
<div
class="row"
[matTooltip]="'add-edit-component-mapping.disabled-file-options' | translate"
[matTooltipDisabled]="!disabledFileOptions"
[matTooltipDisabled]="!form.get('encoding')?.disabled"
[matTooltipPosition]="'above'"
>
<div class="iqser-input-group required w-150">
@ -40,13 +40,12 @@
formControlName="delimiter"
name="delimiter"
type="text"
[class.disabled-file-options]="disabledFileOptions"
/>
</div>
<div class="iqser-input-group required w-150">
<label translate="add-edit-component-mapping.form.encoding-type"></label>
<mat-form-field [class.disabled-file-options]="disabledFileOptions">
<mat-form-field>
<mat-select formControlName="encoding">
<mat-option *ngFor="let type of encodingTypeOptions" [value]="type">
{{ translations[type] | translate }}

View File

@ -18,11 +18,6 @@
font-size: 15px;
}
}
.disabled-file-options {
opacity: 0.5;
pointer-events: none;
}
}
.row:last-child {

View File

@ -52,7 +52,6 @@ export class AddEditComponentMappingDialogComponent
{
protected readonly encodingTypeOptions = Object.keys(FileAttributeEncodingTypes);
protected readonly translations = fileAttributeEncodingTypesTranslations;
#fileChanged = false;
activeFile: File;
form!: UntypedFormGroup;
@ -73,13 +72,14 @@ export class AddEditComponentMappingDialogComponent
const file = new Blob([fileContent.body as Blob], { type: 'text/csv' });
this.form.get('file').setValue(file);
this.initialFormValue = this.form.getRawValue();
this.#disableEncodingAndDelimiter();
}
}
changeFile(file: File) {
this.#fileChanged = true;
this.form.get('file').setValue(file);
this.form.get('fileName').setValue(file?.name);
this.#enableEncodingAndDelimiter();
}
save() {
@ -96,7 +96,13 @@ export class AddEditComponentMappingDialogComponent
});
}
get disabledFileOptions() {
return this.initialFormValue?.file && !this.#fileChanged;
#disableEncodingAndDelimiter() {
this.form.get('encoding').disable();
this.form.get('delimiter').disable();
}
#enableEncodingAndDelimiter() {
this.form.get('encoding').enable();
this.form.get('delimiter').enable();
}
}

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);
@ -60,11 +60,8 @@ export class LicenseAnalysisCapacityUsageComponent {
},
{
data: dataUntilCurrentMonth.map((month, monthIndex) =>
month.analysedFilesBytes
? month.analysedFilesBytes +
monthlyData.slice(0, monthIndex).reduce((acc, curr) => acc + (curr.analysedFilesBytes ?? 0), 0)
: 0,
data: dataUntilCurrentMonth.map((_, monthIndex) =>
monthlyData.slice(0, monthIndex + 1).reduce((acc, curr) => acc + (curr.analysedFilesBytes ?? 0), 0),
),
label: this._translateService.instant('license-info-screen.analysis-capacity-usage.analyzed-cumulative'),
yAxisID: 'y1',

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);
@ -63,11 +63,8 @@ export class LicensePageUsageComponent {
order: 1,
},
{
data: dataUntilCurrentMonth.map((month, monthIndex) =>
month.numberOfAnalyzedPages
? month.numberOfAnalyzedPages +
monthlyData.slice(0, monthIndex).reduce((acc, curr) => acc + (curr.numberOfAnalyzedPages ?? 0), 0)
: 0,
data: dataUntilCurrentMonth.map((_, monthIndex) =>
monthlyData.slice(0, monthIndex + 1).reduce((acc, curr) => acc + (curr.numberOfAnalyzedPages ?? 0), 0),
),
label: this._translateService.instant('license-info-screen.page-usage.cumulative-pages'),
yAxisID: 'y1',

View File

@ -6,7 +6,6 @@ import { ComplexFillTarget } from 'chart.js/dist/types';
const monthNames = dayjs.monthsShort();
const currentMonth = dayjs(Date.now()).month();
const currentYear = dayjs(Date.now()).year();
export const verboseDate = (date: Dayjs) => `${monthNames[date.month()]} ${date.year()}`;
@ -45,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, '');
}
@ -53,9 +52,9 @@ export const getLabelsFromLicense = (license: ILicenseReport) => {
};
export const getDataUntilCurrentMonth = (monthlyData: ILicenseData[]) => {
return monthlyData.filter(data => dayjs(data.startDate).month() <= currentMonth && dayjs(data.startDate).year() <= currentYear);
return monthlyData.filter(data => dayjs(data.startDate).isSameOrBefore(dayjs(Date.now()), 'month'));
};
export const isCurrentMonthAndYear = (date: Date | string) => {
return dayjs(date).month() === currentMonth && dayjs(date).year() === currentYear;
export const isCurrentMonth = (date: Date | string) => {
return dayjs(date).isSame(dayjs(Date.now()), 'month');
};

View File

@ -4,7 +4,6 @@ import { ChangeDetectorRef, Component, ElementRef, inject, OnInit, ViewChild } f
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatIcon } from '@angular/material/icon';
import { MatSlider, MatSliderThumb } from '@angular/material/slider';
import { MatTooltip } from '@angular/material/tooltip';
import { Router } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { environment } from '@environments/environment';
@ -46,6 +45,7 @@ import { ColorPickerModule } from 'ngx-color-picker';
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { PaginatorComponent } from '../paginator/paginator.component';
import { MatTooltip } from '@angular/material/tooltip';
export const DEFAULT_WATERMARK: Partial<IWatermark> = {
text: 'Watermark',
@ -85,11 +85,11 @@ interface WatermarkForm {
HasScrollbarDirective,
NgForOf,
NgClass,
MatTooltip,
MatIcon,
MatSlider,
ColorPickerModule,
MatSliderThumb,
MatTooltip,
],
})
export class WatermarkScreenComponent implements OnInit {
@ -269,7 +269,7 @@ export class WatermarkScreenComponent implements OnInit {
});
if (environment.production) {
this.instance.Core.setCustomFontURL('https://' + window.location.host + this.#convertPath('/assets/pdftron'));
this.instance.Core.setCustomFontURL(window.location.origin + this.#convertPath('/assets/pdftron/fonts'));
}
this.#disableElements();

View File

@ -61,7 +61,7 @@
*ngFor="let config of statusConfig"
[attr.help-mode-key]="'dashboard_in_dossier'"
[config]="config"
filterKey="processingTypeFilters"
[filterKey]="PendingTypes[config.id] ? 'pendingTypeFilters' : 'processingTypeFilters'"
></iqser-progress-bar>
</div>

View File

@ -21,7 +21,9 @@ import {
DossierAttributeWithValue,
DossierStats,
File,
FileErrorCodes,
IDossierRequest,
PendingTypes,
ProcessingTypes,
StatusSorter,
User,
@ -74,6 +76,7 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
#currentChartSubtitleIndex = 0;
readonly #dossierId = getParam(DOSSIER_ID);
protected readonly circleButtonTypes = CircleButtonTypes;
protected readonly PendingTypes = PendingTypes;
@Input() dossierAttributes: DossierAttributeWithValue[];
@Output() readonly toggleCollapse = new EventEmitter();
editingOwner = false;
@ -153,6 +156,10 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
}
#calculateStatusConfig(stats: DossierStats): ProgressBarConfigModel[] {
const files = this._filesMapService.get(this.#dossierId);
const numberOfRulesLockedFiles = files.filter(file => file.errorCode === FileErrorCodes.LOCKED_RULES).length;
const numberOfTimeoutFiles = files.filter(file => file.errorCode === FileErrorCodes.RULES_EXECUTION_TIMEOUT).length;
const numberOfUnknownErrorFiles = stats.processingStats.pending - numberOfRulesLockedFiles - numberOfTimeoutFiles;
return [
{
id: ProcessingTypes.pending,
@ -161,6 +168,27 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
count: stats.processingStats.pending,
icon: 'red:reanalyse',
},
{
id: PendingTypes.lockedRules,
label: _('processing-status.pending-locked-rules'),
total: stats.numberOfFiles,
count: numberOfRulesLockedFiles,
icon: 'red:reanalyse',
},
{
id: PendingTypes.timeout,
label: _('processing-status.pending-timeout'),
total: stats.numberOfFiles,
count: numberOfTimeoutFiles,
icon: 'red:reanalyse',
},
{
id: PendingTypes.unknown,
label: _('processing-status.pending-unknown'),
total: stats.numberOfFiles,
count: numberOfUnknownErrorFiles,
icon: 'red:reanalyse',
},
{
id: ProcessingTypes.ocr,
label: _('processing-status.ocr'),

View File

@ -39,7 +39,12 @@
</ng-container>
<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>
<div
*ngIf="file.isError"
class="small-label error"
translate="dossier-overview.file-listing.file-entry.file-error"
[translateParams]="{ errorCode: file.errorCode }"
></div>
<div *ngIf="file.isUnprocessed" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>

View File

@ -24,6 +24,7 @@ import {
FileAttributeConfigType,
FileAttributeConfigTypes,
IFileAttributeConfig,
PendingType,
ProcessingType,
StatusSorter,
User,
@ -184,6 +185,7 @@ export class ConfigService {
const allDistinctPeople = new Set<string>();
const allDistinctNeedsWork = new Set<string>();
const allDistinctProcessingTypes = new Set<ProcessingType>();
const allDistinctPendingTypes = new Set<PendingType>();
const dynamicFilters = new Map<string, { type: FileAttributeConfigType; filterValue: Set<string> }>();
@ -216,6 +218,7 @@ export class ConfigService {
}
allDistinctProcessingTypes.add(file.processingType);
allDistinctPendingTypes.add(file.pendingType);
// extract values for dynamic filters
fileAttributeConfigs.forEach(config => {
@ -317,6 +320,14 @@ export class ConfigService {
hide: true,
});
const pendingTypesFilters = [...allDistinctPendingTypes].map(item => new NestedFilter({ id: item, label: item }));
filterGroups.push({
slug: 'pendingTypeFilters',
filters: pendingTypesFilters,
checker: (file: File, filter: INestedFilter) => file.pendingType === filter.id,
hide: true,
});
dynamicFilters.forEach((value: { filterValue: Set<string>; type: FileAttributeConfigType }, filterKey: string) => {
const id = filterKey.split(':')[0];
const key = filterKey.split(':')[1];

View File

@ -56,7 +56,7 @@
</iqser-workflow>
</div>
<div *ngIf="dossierAttributes$ | async" [class.collapsed]="collapsedDetails" class="right-container" iqserHasScrollbar>
<div *ngIf="dossierAttributes$ | async" [class.collapsed]="collapsedDetails" class="right-container">
<redaction-dossier-details
(toggleCollapse)="collapsedDetails = !collapsedDetails"
[dossierAttributes]="dossierAttributes"

View File

@ -25,10 +25,7 @@
width: 375px;
min-width: 375px;
padding: 16px 24px 16px 24px;
&.has-scrollbar:hover {
padding-right: 13px;
}
overflow-y: auto;
redaction-dossier-details {
width: 100%;

View File

@ -41,7 +41,7 @@ import { Roles } from '@users/roles';
import { UserPreferenceService } from '@users/user-preference.service';
import { convertFiles, Files, handleFileDrop } from '@utils/index';
import { merge, Observable } from 'rxjs';
import { filter, skip, switchMap, tap } from 'rxjs/operators';
import { filter, map, skip, switchMap, tap } from 'rxjs/operators';
import { ConfigService } from '../config.service';
import { BulkActionsService } from '../services/bulk-actions.service';
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
@ -145,8 +145,9 @@ export default class DossierOverviewScreenComponent extends ListingComponent<Fil
get #dossierFilesChange$() {
return this._dossiersService.dossierFileChanges$.pipe(
filter(dossierId => dossierId === this.dossierId && !!this._dossiersCacheService.get(dossierId)),
switchMap(dossierId => this._filesService.loadAll(dossierId)),
map(changes => changes[this.dossierId]),
filter(changes => !!changes && !!this._dossiersCacheService.get(this.dossierId)),
switchMap(changes => this._filesService.loadByIds({ [this.dossierId]: changes }).pipe(map(files => files[this.dossierId]))),
);
}

View File

@ -2,7 +2,7 @@
display: flex;
position: absolute;
top: 6px;
right: 19px;
right: 8px;
}
.popover {

View File

@ -3,21 +3,13 @@
:host {
width: 100%;
position: relative;
overflow: hidden;
&:hover {
overflow-y: auto;
@include common-mixins.scroll-bar;
}
overflow-y: auto;
@include common-mixins.scroll-bar;
&.has-scrollbar:hover redaction-annotation-wrapper::ng-deep,
&::ng-deep.documine-wrapper {
.annotation {
padding-right: 5px;
}
redaction-annotation-details {
right: 8px;
}
}
}

View File

@ -18,13 +18,10 @@
.right-content {
flex-direction: column;
height: calc(100% - 71px);
@include common-mixins.scroll-bar;
overflow: hidden;
&:hover {
overflow: auto;
}
overflow-y: auto;
&.has-scrollbar .section {
padding-right: 13px;

View File

@ -3,12 +3,16 @@
@if (!editing) {
<div class="value">
<div class="text">
@for (componentValue of entry.componentValues; track componentValue) {
<span [innerHTML]="transformNewLines(componentValue.value ?? componentValue.originalValue)"></span>
@for (componentValue of currentEntry().componentValues; track componentValue) {
<span
[innerHTML]="transformNewLines(componentValue.value ?? componentValue.originalValue) | replaceNbsp"
[matTooltip]="componentValue.valueDescription"
[matTooltipPositionAtOrigin]="true"
></span>
}
</div>
<div class="actions">
@if (canEdit) {
@if (canEdit()) {
<iqser-circle-button
(action)="edit()"
[tooltip]="'component-management.actions.edit' | translate"
@ -16,7 +20,7 @@
[class.help-mode]="helpModeService.isHelpModeActive()"
icon="iqser:edit"
></iqser-circle-button>
@if (hasUpdatedValues) {
@if (hasUpdatedValues()) {
<div class="changes-dot"></div>
}
}
@ -24,11 +28,11 @@
</div>
} @else {
<div (cdkDropListDropped)="drop($event)" cdkDropList>
@for (value of entry.componentValues; track value) {
@for (value of currentEntry().componentValues; track value) {
<div cdkDrag class="editing-value">
<mat-icon
[class.hidden-button]="entry.componentValues.length === 1"
[attr.help-mode-key]="'change_component_order'"
[class.hidden-button]="currentEntry().componentValues.length === 1"
[attr.help-mode-key]="currentEntry().componentValues.length > 1 ? 'change_component_order' : null"
cdkDragHandle
class="draggable"
svgIcon="red:draggable-dots"
@ -39,8 +43,8 @@
<iqser-circle-button
(action)="removeValue($index)"
[tooltip]="'component-management.actions.delete' | translate"
[class.hidden-button]="entry.componentValues.length === 1"
[attr.help-mode-key]="'remove_component_value'"
[class.hidden-button]="currentEntry().componentValues.length === 1"
[attr.help-mode-key]="currentEntry().componentValues.length > 1 ? 'remove_component_value' : null"
class="remove-value"
icon="iqser:trash"
></iqser-circle-button>
@ -54,9 +58,9 @@
[label]="'component-management.actions.save' | translate"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
<div (click)="deselect($event)" class="all-caps-label cancel" translate="component-management.actions.cancel"></div>
<div (click)="cancel($event)" class="all-caps-label cancel" translate="component-management.actions.cancel"></div>
<div class="flex right">
@if (hasUpdatedValues && canEdit) {
@if (hasUpdatedValues() && canEdit()) {
<iqser-circle-button
(action)="undo()"
[tooltip]="'component-management.actions.undo' | translate"

View File

@ -2,12 +2,13 @@
display: flex;
flex-direction: row;
padding: 10px 0 10px 0;
margin-left: 26px;
margin-right: 26px;
margin-left: 14px;
margin-right: 20px;
position: relative;
.component {
width: 40%;
margin-right: 8px;
}
.value {
@ -72,13 +73,13 @@
}
.value {
margin-right: 26px;
margin-right: 20px;
}
}
&:hover {
.component {
margin-left: 26px;
margin-left: 14px;
}
.value {
@ -94,7 +95,7 @@
border-left: 4px solid var(--iqser-primary);
.component {
margin-left: 22px;
margin-left: 10px;
}
.arrow-right {

View File

@ -1,6 +1,6 @@
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { AsyncPipe, KeyValuePipe, NgClass, NgForOf, NgIf } from '@angular/common';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, computed, input, OnInit, output, signal, WritableSignal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatIcon } from '@angular/material/icon';
import { CircleButtonComponent, HelpModeService, IconButtonComponent, IconButtonTypes, IqserDialog } from '@iqser/common-ui';
@ -8,6 +8,9 @@ import { TranslateModule } from '@ngx-translate/core';
import { IComponentLogEntry, IComponentValue } from '@red/domain';
import { RevertValueDialogComponent } from '../../dialogs/docu-mine/revert-value-dialog/revert-value-dialog.component';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { escapeHtml } from '@common-ui/utils';
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
import { MatTooltip } from '@angular/material/tooltip';
@Component({
selector: 'redaction-editable-structured-component-value [entry] [canEdit]',
@ -28,20 +31,24 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
CdkDragHandle,
FormsModule,
AsyncPipe,
ReplaceNbspPipe,
MatTooltip,
],
})
export class EditableStructuredComponentValueComponent implements OnInit {
readonly entry = input<IComponentLogEntry>();
currentEntry: WritableSignal<IComponentLogEntry>;
readonly canEdit = input<boolean>();
readonly deselectLast = output();
readonly overrideValue = output<IComponentLogEntry>();
readonly revertOverride = output<string>();
hasUpdatedValues = computed(() => this.currentEntry().overridden);
selected = false;
valueBeforeCurrentEdit: IComponentValue[];
protected entryLabel: string;
protected editing = false;
protected hasUpdatedValues = false;
protected initialEntry: IComponentLogEntry;
protected readonly iconButtonTypes = IconButtonTypes;
@Input() entry: IComponentLogEntry;
@Input() canEdit: boolean;
@Output() readonly deselectLast = new EventEmitter();
@Output() readonly overrideValue = new EventEmitter<IComponentLogEntry>();
@Output() readonly revertOverride = new EventEmitter<string>();
selected = false;
constructor(
readonly helpModeService: HelpModeService,
@ -50,38 +57,26 @@ export class EditableStructuredComponentValueComponent implements OnInit {
) {}
get disabled() {
for (let i = 0; i < this.entry.componentValues.length; i++) {
if (this.entry.componentValues[i].value !== this.initialEntry.componentValues[i]?.value) {
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.entry.componentValues.length === this.initialEntry.componentValues.length;
}
get #hasUpdatedValues() {
for (const value of this.entry.componentValues) {
if (value.originalValue === null && value.value === '') {
continue;
}
if (value.originalValue !== value.value) {
return true;
}
}
return false;
return this.currentEntry().componentValues.length === this.initialEntry.componentValues.length;
}
get #initialEntry() {
return JSON.parse(JSON.stringify(this.entry));
return JSON.parse(JSON.stringify(this.entry()));
}
ngOnInit() {
this.currentEntry = signal(this.entry());
this.reset();
}
reset() {
this.initialEntry = this.#initialEntry;
this.hasUpdatedValues = this.#hasUpdatedValues;
this.entryLabel = this.parseName(this.entry.name);
this.entryLabel = this.parseName(this.currentEntry().name);
this.deselect();
}
@ -93,11 +88,12 @@ export class EditableStructuredComponentValueComponent implements OnInit {
}
this.deselectLast.emit();
this.selected = true;
this._state.componentReferenceIds = this.#getUniqueReferencesIds(this.entry.componentValues);
this._state.componentReferenceIds = this.#getUniqueReferencesIds(this.currentEntry().componentValues);
}
}
edit() {
this.valueBeforeCurrentEdit = JSON.parse(JSON.stringify([...this.currentEntry().componentValues]));
this.deselectLast.emit();
this.selected = true;
this.editing = true;
@ -111,37 +107,48 @@ export class EditableStructuredComponentValueComponent implements OnInit {
this._state.componentReferenceIds = null;
}
cancel($event?: MouseEvent) {
this.currentEntry.update(value => ({ ...value, componentValues: this.valueBeforeCurrentEdit }));
this.deselect($event);
}
removeValue(index: number) {
this.entry.componentValues.splice(index, 1);
this.currentEntry.update(value => ({ ...value, componentValues: value.componentValues.filter((_, i) => i !== index) }));
}
save() {
this.entry.overridden = true;
this.overrideValue.emit(this.entry);
this.currentEntry.update(value => ({ ...value, overridden: true }));
this.overrideValue.emit(this.currentEntry());
this.reset();
}
async undo() {
const dialog = this._iqserDialog.openDefault(RevertValueDialogComponent, { data: { entry: this.entry }, width: '800px' });
const dialog = this._iqserDialog.openDefault(RevertValueDialogComponent, { data: { entry: this.currentEntry() }, width: '800px' });
const result = await dialog.result();
if (result) {
this.revertOverride.emit(this.entry.name);
this.revertOverride.emit(this.currentEntry().name);
this.reset();
}
}
add() {
this.entry.componentValues.push({
componentRuleId: null,
entityReferences: [],
originalValue: null,
value: '',
valueDescription: '',
});
this.currentEntry.update(value => ({
...value,
componentValues: [
...value.componentValues,
{
componentRuleId: null,
entityReferences: [],
originalValue: null,
value: '',
valueDescription: '',
},
],
}));
}
drop(event: CdkDragDrop<string>) {
moveItemInArray(this.entry.componentValues, event.previousIndex, event.currentIndex);
moveItemInArray(this.currentEntry().componentValues, event.previousIndex, event.currentIndex);
}
parseName(name: string) {
@ -149,7 +156,7 @@ export class EditableStructuredComponentValueComponent implements OnInit {
}
transformNewLines(value: string) {
return value.replace(/\n/g, '<br>');
return escapeHtml(value).replace(/\n/g, '<br>');
}
#getUniqueReferencesIds(values: IComponentValue[]) {
@ -164,7 +171,7 @@ export class EditableStructuredComponentValueComponent implements OnInit {
#updateTextAreaHeight() {
setTimeout(() => {
for (let i = 0; i < this.entry.componentValues.length; i++) {
for (let i = 0; i < this.currentEntry().componentValues.length; i++) {
const textArea = document.getElementById(`value-input-${i}`);
textArea.style.height = 'auto';
textArea.style.height = `${textArea.scrollHeight}px`;

View File

@ -43,7 +43,7 @@
flex-direction: column;
&.documine-width {
width: calc(var(--documine-workload-content-width));
width: calc(var(--documine-workload-content-width) - 55px);
border-right: 1px solid var(--iqser-separator);
z-index: 1;
@ -79,7 +79,7 @@
.quick-navigation {
height: 100%;
border-right: 1px solid var(--iqser-separator);
min-width: var(--qiuck-navigation-width);
min-width: var(--quick-navigation-width);
overflow: hidden;
display: flex;
flex-direction: column;

View File

@ -36,7 +36,7 @@ import { workloadTranslations } from '@translations/workload-translations';
import { UserPreferenceService } from '@users/user-preference.service';
import { getLocalStorageDataByFileId } from '@utils/local-storage';
import { combineLatest, delay, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { filter, map, tap } from 'rxjs/operators';
import scrollIntoView from 'scroll-into-view-if-needed';
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
import { REDDocumentViewer } from '../../../pdf-viewer/services/document-viewer.service';
@ -187,6 +187,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
const secondary$ = this.filterService.getFilterModels$('secondaryFilters');
return combineLatest([
this._documentViewer.loaded$,
this.fileDataService.all$,
primary$,
secondary$,
@ -195,7 +196,8 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this._pageRotationService.rotations$,
]).pipe(
delay(0),
map(([annotations, primary, secondary, componentReferenceIds]) =>
filter(([loaded]) => loaded),
map(([, annotations, primary, secondary, componentReferenceIds]) =>
this.#filterAnnotations(annotations, primary, secondary, componentReferenceIds),
),
map(annotations => this.#mapListItemsFromAnnotationWrapperArray(annotations)),

View File

@ -1,3 +1,5 @@
@use 'common-mixins';
.components-header {
display: flex;
flex-direction: row;
@ -24,18 +26,19 @@ mat-icon {
display: flex;
flex-direction: column;
font-size: 12px;
overflow: scroll;
overflow-x: hidden;
overflow-y: scroll;
height: calc(100% - 40px);
@include common-mixins.scroll-bar;
.component-row {
display: flex;
flex-direction: column;
margin-left: 13px;
margin-right: 13px;
.header {
display: flex;
padding: 10px 26px;
padding: 10px 14px;
font-weight: 600;
:first-child {
@ -47,9 +50,9 @@ mat-icon {
&:not(:last-child) {
border-bottom: 1px solid var(--iqser-separator);
}
border-bottom: 1px solid var(--iqser-separator);
margin-left: 26px;
margin-right: 26px;
margin-right: 13px;
}
}
}

View File

@ -1,4 +1,4 @@
import { Component, Input, OnInit, signal, ViewChildren } from '@angular/core';
import { Component, effect, Input, OnInit, signal, ViewChildren } from '@angular/core';
import { List } from '@common-ui/utils';
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
import { ComponentLogEntry, Dictionary, File, IComponentLogEntry, WorkflowFileStatuses } from '@red/domain';
@ -11,6 +11,7 @@ import { map } from 'rxjs/operators';
import { toObservable } from '@angular/core/rxjs-interop';
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
@Component({
selector: 'redaction-structured-component-management',
@ -33,7 +34,13 @@ export class StructuredComponentManagementComponent implements OnInit {
private readonly _loadingService: LoadingService,
private readonly _componentLogFilterService: ComponentLogFilterService,
private readonly _filterService: FilterService,
) {}
private readonly _state: FilePreviewStateService,
) {
effect(async () => {
this._state.file();
await this.#loadData();
});
}
get canEdit() {
return this.file.workflowStatus !== WorkflowFileStatuses.APPROVED;
@ -75,7 +82,6 @@ export class StructuredComponentManagementComponent implements OnInit {
}
async #loadData(): Promise<void> {
this._loadingService.start();
const componentLogData = await firstValueFrom(
this._componentLogService.getComponentLogData(
this.file.dossierTemplateId,

View File

@ -52,6 +52,7 @@
</iqser-icon-button>
<div [translate]="'edit-redaction.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button></iqser-help-button>
</div>
</form>

View File

@ -1,6 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import {
CircleButtonComponent,
HasScrollbarDirective,
HelpButtonComponent,
IconButtonComponent,
IconButtonTypes,
IqserDialogComponent,
} from '@iqser/common-ui';
import { Dictionary, Dossier, SuperTypes } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
@ -33,6 +40,7 @@ import { MatDialogClose } from '@angular/material/dialog';
IconButtonComponent,
CircleButtonComponent,
MatDialogClose,
HelpButtonComponent,
],
})
export class EditAnnotationDialogComponent

View File

@ -60,6 +60,7 @@
</iqser-icon-button>
<div [translate]="'remove-annotation.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button></iqser-help-button>
</div>
</form>

View File

@ -6,7 +6,14 @@ import { MatDialogClose } from '@angular/material/dialog';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import {
CircleButtonComponent,
HasScrollbarDirective,
HelpButtonComponent,
IconButtonComponent,
IconButtonTypes,
IqserDialogComponent,
} from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
import { getRemoveRedactionOptions } from '../../../utils/dialog-options';
import { RemoveAnnotationData, RemoveAnnotationOption, RemoveAnnotationOptions, RemoveAnnotationResult } from '../../../utils/dialog-types';
@ -28,6 +35,7 @@ import { RemoveAnnotationData, RemoveAnnotationOption, RemoveAnnotationOptions,
CircleButtonComponent,
MatDialogClose,
NgIf,
HelpButtonComponent,
],
})
export class RemoveAnnotationDialogComponent extends IqserDialogComponent<

View File

@ -39,7 +39,7 @@
}
&.documine-container {
width: 60%;
width: calc(100% - var(--structured-component-management-width));
}
}

View File

@ -32,6 +32,10 @@ export class DocumentInfoService {
},
{ allowSignalWrites: true },
);
effect(() => {
document.body.style.setProperty('--quick-navigation-width', this.shown() ? '350px' : '61px');
});
}
fileAttributes$(fileId: string, dossierId: string, dossierTemplateId: string) {

View File

@ -128,7 +128,8 @@ export class FilePreviewStateService {
get #dossierFilesChange$() {
return this._dossiersService.dossierFileChanges$.pipe(
filter(dossierId => dossierId === this.dossierId),
map(changes => changes[this.dossierId]),
filter(fileIds => fileIds && fileIds.length > 0),
map(() => true),
);
}

View File

@ -14,7 +14,7 @@
align-items: center;
&.documine-pagination {
left: calc(100% - (var(--documine-viewer-width) / 2) - var(--qiuck-navigation-width));
left: calc(100% - (var(--documine-viewer-width) / 2) - var(--quick-navigation-width));
}
> div {

View File

@ -147,7 +147,7 @@ export class PdfViewer {
this.#instance = await this.#getInstance(htmlElement);
if (environment.production) {
this.#instance.Core.setCustomFontURL('https://' + window.location.host + this.#convertPath('/assets/pdftron'));
this.#instance.Core.setCustomFontURL(window.location.origin + this.#convertPath('/assets/pdftron/fonts'));
}
await this.runWithCleanup(async () => {

View File

@ -10,6 +10,7 @@ import utc from 'dayjs/plugin/utc';
import localeData from 'dayjs/plugin/localeData';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
export interface DayJsDateAdapterOptions {
/**
@ -232,6 +233,7 @@ export class CustomDateAdapter extends DateAdapter<Dayjs> {
dayjs.extend(localizedFormat);
dayjs.extend(customParseFormat);
dayjs.extend(localeData);
dayjs.extend(isSameOrBefore);
this.setLocale(dateLocale);
}

View File

@ -15,6 +15,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { HeadersConfiguration } from '@iqser/common-ui/lib/utils';
import { LicenseService } from '@services/license.service';
import { LicenseFeatures } from '../../admin/screens/license/utils/constants';
import { UserPreferenceService } from '@users/user-preference.service';
export interface ActiveUpload {
subscription: Subscription;
@ -43,6 +44,7 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
private readonly _errorMessageService: ErrorMessageService,
private readonly _licenseService: LicenseService,
private readonly _toaster: Toaster,
private readonly _userPreferenceService: UserPreferenceService,
) {
super();
const fileFetch$ = this.#fetchFiles$.pipe(
@ -76,7 +78,7 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
const maxSizeBytes = maxSizeMB * 1024 * 1024;
const dossierFiles = this._filesMapService.get(dossierId);
const supportMsOfficeFormats = this._licenseService.getFeature(LicenseFeatures.SUPPORT_MS_OFFICE_FORMATS)?.value as boolean;
let option: OverwriteFileOption = localStorage.getItem('overwriteFileOption') as OverwriteFileOption;
let option: OverwriteFileOption | 'undefined' = this._userPreferenceService.getOverwriteFileOption();
for (let idx = 0; idx < files.length; ++idx) {
const file = files[idx];
let currentOption = option;
@ -95,14 +97,14 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
}
}
} else if (dossierFiles.find(pf => pf.filename === file.file.name)) {
if (!option) {
if (option === 'undefined') {
const res = await this._dialogService.openOverwriteFileDialog(file.file.name);
if (res.cancel) {
return;
}
if (res.rememberChoice) {
localStorage.setItem('overwriteFileOption', res.option);
await this._userPreferenceService.saveOverwriteFileOption(res.option);
}
currentOption = res.option;

View File

@ -1,17 +1,16 @@
import { GenericService, LAST_CHECKED_OFFSET, QueryParam, ROOT_CHANGES_KEY } from '@iqser/common-ui';
import { Dossier, DossierStats, IDossierChanges } from '@red/domain';
import { forkJoin, Observable, of, Subscription, throwError, timer } from 'rxjs';
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { GenericService, ROOT_CHANGES_KEY } from '@iqser/common-ui';
import { Dossier, DossierStats, IChangesDetails } from '@red/domain';
import { forkJoin, Observable, Subscription, timer } from 'rxjs';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import { NGXLogger } from 'ngx-logger';
import { ActiveDossiersService } from './active-dossiers.service';
import { ArchivedDossiersService } from './archived-dossiers.service';
import { inject, Injectable, OnDestroy } from '@angular/core';
import { DashboardStatsService } from '../dossier-templates/dashboard-stats.service';
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
import { List } from '@iqser/common-ui/lib/utils';
import { Router } from '@angular/router';
import { filterEventsOnPages } from '@utils/operators';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
@Injectable({ providedIn: 'root' })
export class DossiersChangesService extends GenericService<Dossier> implements OnDestroy {
@ -19,40 +18,39 @@ export class DossiersChangesService extends GenericService<Dossier> implements O
readonly #activeDossiersService = inject(ActiveDossiersService);
readonly #archivedDossiersService = inject(ArchivedDossiersService);
readonly #dashboardStatsService = inject(DashboardStatsService);
readonly #dossierStatsService = inject(DossierStatsService);
readonly #logger = inject(NGXLogger);
readonly #router = inject(Router);
protected readonly _defaultModelPath = 'dossier';
loadOnlyChanged(): Observable<IDossierChanges> {
const removeIfNotFound = (id: string) =>
catchError((error: unknown) => {
if (error instanceof HttpErrorResponse && error.status === HttpStatusCode.NotFound) {
this.#activeDossiersService.remove(id);
this.#archivedDossiersService.remove(id);
return of([]);
}
return throwError(() => error);
});
loadOnlyChanged(): Observable<IChangesDetails> {
const load = (changes: IChangesDetails) => this.#load(changes.dossierChanges.map(d => d.dossierId));
const load = (changes: IDossierChanges) =>
changes.map(change => this.#load(change.dossierId).pipe(removeIfNotFound(change.dossierId)));
const loadStats = (change: IChangesDetails) => {
const dossierStatsToLoad = new Set<string>();
change.dossierChanges.forEach(dossierChange => dossierStatsToLoad.add(dossierChange.dossierId));
change.fileChanges.forEach(fileChange => dossierStatsToLoad.add(fileChange.dossierId));
return this.#dossierStatsService.getFor(Array.from(dossierStatsToLoad));
};
return this.hasChangesDetails$().pipe(
tap(changes => this.#logger.info('[DOSSIERS_CHANGES] Found changes', changes)),
switchMap(dossierChanges =>
forkJoin([...load(dossierChanges), this.#dashboardStatsService.loadAll().pipe(take(1))]).pipe(map(() => dossierChanges)),
forkJoin([load(dossierChanges), loadStats(dossierChanges), this.#dashboardStatsService.loadAll().pipe(take(1))]).pipe(
map(() => dossierChanges),
),
),
);
}
hasChangesDetails$(): Observable<IDossierChanges> {
hasChangesDetails$(): Observable<IChangesDetails> {
const body = { value: this._lastCheckedForChanges.get(ROOT_CHANGES_KEY) };
const dateBeforeRequest = new Date(Date.now() - LAST_CHECKED_OFFSET).toISOString();
const dateBeforeRequest = new Date().toISOString();
this.#logger.info('[DOSSIERS_CHANGES] Check with Last Checked Date', body.value);
return this._post<IDossierChanges>(body, `${this._defaultModelPath}/changes/details`).pipe(
filter(changes => changes.length > 0),
return this._post<IChangesDetails>(body, `${this._defaultModelPath}/changes/details/v2`).pipe(
filter(changes => changes.dossierChanges.length > 0 || changes.fileChanges.length > 0),
tap(() => this._lastCheckedForChanges.set(ROOT_CHANGES_KEY, dateBeforeRequest)),
tap(() => this.#logger.info('[DOSSIERS_CHANGES] Save Last Checked Date value', dateBeforeRequest)),
);
@ -75,17 +73,27 @@ export class DossiersChangesService extends GenericService<Dossier> implements O
this.#subscription.unsubscribe();
}
#load(id: string): Observable<DossierStats[]> {
const queryParams: List<QueryParam> = [{ key: 'includeArchived', value: true }];
return super._getOne([id], this._defaultModelPath, queryParams).pipe(
map(entity => new Dossier(entity)),
switchMap((dossier: Dossier) => {
if (dossier.isArchived) {
this.#activeDossiersService.remove(dossier.id);
return this.#archivedDossiersService.updateDossier(dossier);
}
this.#archivedDossiersService.remove(dossier.id);
return this.#activeDossiersService.updateDossier(dossier);
getByIds(ids: string[]) {
return super._post<Record<string, Dossier>>({ value: ids }, `${this._defaultModelPath}/by-id`);
}
#load(ids: string[]): Observable<Dossier[]> {
return this.getByIds(ids).pipe(
map(entity => {
return Object.values(entity).map(dossier => new Dossier(dossier));
}),
map((dossiers: Dossier[]) => {
const archivedDossiers = dossiers.filter(dossier => dossier.isArchived);
const deletedDossiers = dossiers.filter(dossier => dossier.isSoftDeleted);
const activeDossiers = dossiers.filter(dossier => !dossier.isArchived && !dossier.isSoftDeleted);
archivedDossiers.forEach(dossier => this.#activeDossiersService.remove(dossier.id));
activeDossiers.forEach(dossier => this.#archivedDossiersService.remove(dossier.id));
deletedDossiers.forEach(dossier => this.#activeDossiersService.remove(dossier.id));
this.#activeDossiersService.updateDossiers(activeDossiers);
this.#archivedDossiersService.updateDossiers(archivedDossiers);
return dossiers;
}),
);
}

View File

@ -1,5 +1,5 @@
import { EntitiesService, Toaster } from '@iqser/common-ui';
import { Dossier, DossierStats, IDossier, IDossierChanges, IDossierRequest } from '@red/domain';
import { Dossier, DossierFileChanges, DossierStats, IChangesDetails, IDossier, IDossierRequest } from '@red/domain';
import { Observable, of, Subject } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { inject } from '@angular/core';
@ -17,7 +17,7 @@ export abstract class DossiersService extends EntitiesService<IDossier, Dossier>
protected readonly _toaster = inject(Toaster);
protected readonly _entityClass = Dossier;
protected abstract readonly _defaultModelPath: string;
readonly dossierFileChanges$ = new Subject<string>();
readonly dossierFileChanges$ = new Subject<DossierFileChanges>();
abstract readonly routerPath: string;
createOrUpdate(dossier: IDossierRequest): Observable<Dossier> {
@ -52,7 +52,18 @@ export abstract class DossiersService extends EntitiesService<IDossier, Dossier>
return this._dossierStatsService.getFor([dossier.id]);
}
emitFileChanges(dossierChanges: IDossierChanges): void {
dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId));
updateDossiers(dossier: Dossier[]): void {
dossier.forEach(d => this.replace(d));
}
emitFileChanges(changes: IChangesDetails): void {
const changeModel: DossierFileChanges = {};
changes.fileChanges.forEach(change => {
if (!changeModel[change.dossierId]) {
changeModel[change.dossierId] = [];
}
changeModel[change.dossierId].push(change.fileId);
});
this.dossierFileChanges$.next(changeModel);
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { EntitiesService, isArray, QueryParam } from '@iqser/common-ui';
import { List, mapEach } from '@iqser/common-ui/lib/utils';
import { File, IFile } from '@red/domain';
import { DossierFileChanges, File, IFile } from '@red/domain';
import { UserService } from '@users/user.service';
import { NGXLogger } from 'ngx-logger';
import { firstValueFrom } from 'rxjs';
@ -27,8 +27,35 @@ export class FilesService extends EntitiesService<IFile, File> {
super();
}
loadByIds(dossierFileChanges: DossierFileChanges) {
const filesByDossier$ = super
._post<{ value: Record<string, IFile[]> }>({ value: dossierFileChanges }, `${this._defaultModelPath}/by-id`)
.pipe(
map(response => {
const filesByDossier = response.value;
const result: Record<string, File[]> = {};
for (const key of Object.keys(filesByDossier)) {
result[key] = filesByDossier[key].map(file => new File(file, this._userService.getName(file.assignee)));
result[key].forEach(file => this._logger.info('[FILE] Loaded', file));
}
return result;
}),
);
return filesByDossier$.pipe(
tap(files => {
for (const key of Object.keys(files)) {
const notDeletedFiles = files[key].filter(file => !file.deleted);
const deletedFiles = files[key].filter(file => file.deleted);
this._filesMapService.replace(key, notDeletedFiles);
deletedFiles.map(file => file.id).forEach(id => this._filesMapService.delete(key, id));
}
}),
);
}
/** Reload dossier files + stats. */
loadAll(dossierId: string) {
console.log('loadAll');
const files$ = this.getFor(dossierId).pipe(
mapEach(file => new File(file, this._userService.getName(file.assignee))),
tap(file => this._logger.info('[FILE] Loaded', file)),

View File

@ -1,7 +1,7 @@
import { inject, Injectable, OnDestroy } from '@angular/core';
import { EntitiesService, getConfig, QueryParam } from '@iqser/common-ui';
import { TranslateService } from '@ngx-translate/core';
import { EMPTY, firstValueFrom, iif, merge, Observable, of, Subscription, timer } from 'rxjs';
import { EMPTY, firstValueFrom, merge, Observable, of, Subscription, timer } from 'rxjs';
import { AppConfig, Dossier, INotification, Notification, NotificationTypes } from '@red/domain';
import { map, switchMap, tap } from 'rxjs/operators';
import { notificationsTranslations } from '@translations/notifications-translations';
@ -141,6 +141,14 @@ export class NotificationsService extends EntitiesService<INotification, Notific
}
#loadNotificationsIfChanged(): Observable<Notification[]> {
return this.hasChanges$().pipe(switchMap(changed => iif(() => changed, this.loadAll(), EMPTY)));
return this.hasChanges$().pipe(
switchMap(changed => {
if (changed) {
return this.loadAll();
} else {
return EMPTY;
}
}),
);
}
}

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { IqserUserPreferenceService, ListingMode } from '@iqser/common-ui';
import { SystemDefaultOption, SystemDefaultType } from '../modules/account/utils/dialog-defaults';
import { RedactOrHintOption, RemoveRedactionOption } from '../modules/file-preview/utils/dialog-types';
import { OverwriteFileOption } from '@red/domain';
export const PreferencesKeys = {
dossierRecent: 'Dossier-Recent',
@ -21,6 +22,7 @@ export const PreferencesKeys = {
removeRedactionDefaultExtraOption: 'Remove-Redaction-Default-Extra',
removeRecommendationDefaultExtraOption: 'Remove-Recommendation-Default-Extra',
removeHintDefaultExtraOption: 'Remove-Hint-Default-Extra',
rememberOverwriteFileOption: 'Remember-Overwrite-File',
} as const;
@Injectable({
@ -169,4 +171,12 @@ export class UserPreferenceService extends IqserUserPreferenceService {
async saveRemoveRecommendationDefaultExtraOption(defaultOption: boolean | string): Promise<void> {
await this.save(PreferencesKeys.removeRecommendationDefaultExtraOption, defaultOption.toString());
}
getOverwriteFileOption(): OverwriteFileOption | 'undefined' {
return this._getAttribute(PreferencesKeys.rememberOverwriteFileOption, 'undefined') as OverwriteFileOption | 'undefined';
}
async saveOverwriteFileOption(preference: OverwriteFileOption | 'undefined') {
await this.save(PreferencesKeys.rememberOverwriteFileOption, preference);
}
}

View File

@ -273,9 +273,6 @@
"watermarks": "Wasserzeichen"
},
"analysis-disabled": "",
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotation-actions": {
"accept-recommendation": {
"label": "Empfehlung annehmen"
@ -331,14 +328,14 @@
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet."
},
"remove": {
"error": "Entfernen der Schwärzung fehlgeschlagen: {error}",
"success": "Schwärzung wurde entfernt"
},
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"remove": {
"error": "Entfernen der Schwärzung fehlgeschlagen: {error}",
"success": "Schwärzung wurde entfernt"
},
"undo": {
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
"success": "Rücksetzung erfolgreich"
@ -351,15 +348,15 @@
"remove-highlights": {
"label": "Ausgewählte Markierungen entfernen"
},
"resize": {
"label": "Größe ändern"
},
"resize-accept": {
"label": "Neue Größe speichern"
},
"resize-cancel": {
"label": "Größenänderung abbrechen"
},
"resize": {
"label": "Größe ändern"
},
"see-references": {
"label": "Referenzen anzeigen"
},
@ -393,6 +390,9 @@
"skipped": "Ignorierte Schwärzung",
"text-highlight": "Markierung"
},
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotations": "Annotationen",
"archived-dossiers-listing": {
"no-data": {
@ -587,8 +587,7 @@
"success": {
"generic": ""
},
"title": "",
"warning-text": ""
"title": ""
},
"configurations": "Konfiguration",
"confirm-archive-dossier": {
@ -637,18 +636,14 @@
"warning": "Warnung: Wiederherstellung des Benutzers nicht möglich."
},
"confirmation-dialog": {
"approve-file": {
"question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.<br><br>Möchten Sie es trotzdem freigeben?",
"title": "Warnung!"
},
"approve-file-without-analysis": {
"confirmationText": "Ohne Analyse freigeben",
"denyText": "Abbrechen",
"question": "Analyse zur Erkennung neuer Schwärzungen erforderlich.",
"title": "Warnung!"
},
"approve-multiple-files": {
"question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen, die im Zuge einer Reanalyse hinzugefügt wurden.<br><br>Möchen Sie die Dateien wirklich freigeben?",
"approve-file": {
"question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.<br><br>Möchten Sie es trotzdem freigeben?",
"title": "Warnung!"
},
"approve-multiple-files-without-analysis": {
@ -657,6 +652,10 @@
"question": "Für mindestens eine Datei ist ein Analyselauf zur Erkennung neuer Schwärzungen erforderlich.",
"title": "Warnung"
},
"approve-multiple-files": {
"question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen, die im Zuge einer Reanalyse hinzugefügt wurden.<br><br>Möchen Sie die Dateien wirklich freigeben?",
"title": "Warnung!"
},
"assign-file-to-me": {
"question": {
"multiple": "Dieses Dokument wird gerade von einer anderen Person geprüft.<br><br>Möchten Sie sich die Datei dennoch zuweisen?",
@ -990,7 +989,7 @@
"download-file-disabled": "Download: Sie müssen Genehmiger im Dossier sein und die initiale Verarbeitung {count, plural, one{der Datei} other{der Dateien}} muss abgeschlossen sein.",
"file-listing": {
"file-entry": {
"file-error": "Reanalyse erforderlich",
"file-error": "Reanalyse erforderlich ({errorCode, select, RULES_EXECUTION_TIMEOUT{Zeitlimit für Regeln} LOCKED_RULES{Regeln gesperrt} other{unbekannt}})",
"file-pending": "Ausstehend ..."
}
},
@ -1026,13 +1025,13 @@
"recent": "Neu ({hours} h)",
"unassigned": "Keinem Bearbeiter zugewiesen"
},
"reanalyse": {
"action": "Datei analysieren"
},
"reanalyse-dossier": {
"error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Dateien für Reanalyse vorgesehen."
},
"reanalyse": {
"action": "Datei analysieren"
},
"report-download": "",
"start-auto-analysis": "Auto-Analyse aktivieren",
"stop-auto-analysis": "Auto-Analyse anhalten",
@ -1066,8 +1065,7 @@
"dossier-states": "{count, plural, one{Dossier-Status} other{Dossier-Status}}"
},
"error": {
"conflict": "Es gibt bereits einen Dossier-Status mit diesem Namen.",
"generic": "Speichern des Dossier-Status fehlgeschlagen."
"conflict": "Es gibt bereits einen Dossier-Status mit diesem Namen."
},
"no-data": {
"title": "Es wurde noch kein Dossier-Status angelegt."
@ -1103,14 +1101,6 @@
"total-documents": "Dokumente",
"total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}"
},
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-templates-listing": {
"action": {
"clone": "Vorlage klonen",
@ -1145,6 +1135,14 @@
"title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}"
}
},
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-watermark-selector": {
"heading": "Wasserzeichen auf Dokumenten",
"no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.",
@ -1340,15 +1338,6 @@
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
}
},
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"entity-rules-screen": {
"error": {
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
@ -1360,22 +1349,30 @@
"generic": "Die Entitätsregeln wurden aktualisiert."
},
"title": "Entitätsregeln-Editor",
"warning-text": "Warnung: experimentelle Funktion!",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden"
},
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"error": {
"deleted-entity": {
"dossier": {
"action": "Zurück zur Übersicht",
"label": "Dieses Dossier wurde gelöscht!"
},
"file": {
"action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
},
"file": {
"action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!"
}
},
"file-preview": {
@ -1393,12 +1390,6 @@
},
"exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr",
"file": "Datei",
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attribute-encoding-types": {
"ascii": "ASCII",
"iso": "ISO-8859-1",
@ -1409,6 +1400,12 @@
"number": "Nummer",
"text": "Freier Text"
},
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attributes-configurations": {
"cancel": "Abbrechen",
"form": {
@ -1453,7 +1450,7 @@
"save": {
"error": "Erstellung der Datei-Attribute fehlgeschlagen.",
"label": "Attribute speichern",
"success": "{count} Datei-{count, plural, one{Attribut} other{Attribute}} erfolgreich erstellt!"
"success": "{count} Datei-{count, plural, one{Attribut} other{Attribute}} erfolgreich erstellt."
},
"search": {
"placeholder": "Nach Spaltennamen suchen..."
@ -1626,15 +1623,6 @@
"csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert."
}
},
"filter": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
"hint": "Nur Hinweise",
"image": "Bilder",
"none": "Keine Annotationen",
"redaction": "Schwärzungen",
"updated": "Aktualisiert"
},
"filter-menu": {
"filter-options": "Filteroptionen",
"filter-types": "Filter",
@ -1644,6 +1632,15 @@
"unseen-pages": "Nur Annotationen auf ungesehenen Seiten",
"with-comments": "Nur Annotationen mit Kommentaren"
},
"filter": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
"hint": "Nur Hinweise",
"image": "Bilder",
"none": "Keine Annotationen",
"redaction": "Schwärzungen",
"updated": "Aktualisiert"
},
"filters": {
"assigned-people": "Bearbeiter",
"documents-status": "Dokumentenstatus",
@ -1922,13 +1919,6 @@
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
},
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"notifications-screen": {
"category": {
"email-notifications": "E-Mail-Benachrichtigungen",
@ -1942,6 +1932,7 @@
"dossier": "Benachrichtigungen zu Dossiers",
"other": "Andere Benachrichtigungen"
},
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"options": {
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde",
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde",
@ -1959,7 +1950,6 @@
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
},
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"schedule": {
"daily": "Tägliche Zusammenfassung",
"instant": "Sofort",
@ -1967,6 +1957,13 @@
},
"title": "Benachrichtigungseinstellungen"
},
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"ocr": {
"confirmation-dialog": {
"cancel": "Abbrechen",
@ -2049,7 +2046,7 @@
"auto-expand-filters-on-action": "Filter ausgehend von meinen Aktionen automatisch anpassen",
"help-mode-dialog": "Dialog zur Aktivierung des Hilfemodus",
"load-all-annotations-warning": "Warnung bei gleichzeitigem Laden aller Annotationen in der Miniaturansicht",
"open-structured-view-by-default": "Strukturierte Komponentenansicht standardmäßig anzeigen",
"overwrite-file-option": "",
"table-extraction-type": "Art der Tabellenextraktion"
},
"label": "Präferenzen",
@ -2058,16 +2055,19 @@
"warnings-label": "Dialoge und Meldungen",
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
},
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"processing-status": {
"ocr": "OCR",
"pending": "Ausstehend",
"pending-locked-rules": "Ausstehend (Regeln gesperrt)",
"pending-timeout": "Ausstehend (Zeitlimit für Regeln)",
"pending-unknown": "Ausstehend (unbekannt)",
"processed": "Verarbeitet",
"processing": "Verarbeitung läuft"
},
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"readonly": "Lesemodus",
"readonly-archived": "Lesemodus (archiviert)",
"redact-text": {
@ -2297,18 +2297,12 @@
"inactive": "Inaktiv",
"manager-admin": "Manager & Admin",
"no-role": "Keine Rolle definiert",
"red-admin": "Anwendungsadmin",
"red-manager": "Manager",
"red-admin": "{count, plural, one{Anwendungsadmin} other{Anwendungsadmins}}",
"red-manager": "{count, plural, one{Manager} other{Manager}}",
"red-user": "Benutzer",
"red-user-admin": "Benutzeradmin",
"regular": "regulärer Benutzer"
},
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"search-screen": {
"cols": {
"assignee": "Bearbeiter",
@ -2332,6 +2326,12 @@
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
},
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"seconds": "Sekunden",
"size": "Größe",
"smtp-auth-config": {
@ -2587,4 +2587,4 @@
}
},
"yesterday": "Gestern"
}
}

View File

@ -989,7 +989,7 @@
"download-file-disabled": "To download, ensure you are an approver in the dossier, and the {count, plural, one{file has undergone} other{files have undergone}} initial processing.",
"file-listing": {
"file-entry": {
"file-error": "Re-processing required",
"file-error": "Re-processing required ({errorCode, select, RULES_EXECUTION_TIMEOUT{Rules timeout} LOCKED_RULES{Rules locked} other{unknown}})",
"file-pending": "Pending..."
}
},
@ -2046,6 +2046,7 @@
"auto-expand-filters-on-action": "Auto-expand filters on my actions",
"help-mode-dialog": "Help mode activation dialog",
"load-all-annotations-warning": "Warning regarding simultaneous loading of all annotations in thumbnails",
"overwrite-file-option": "Preferred action when re-uploading an already existing file",
"table-extraction-type": "Table extraction type"
},
"label": "Preferences",
@ -2057,6 +2058,9 @@
"processing-status": {
"ocr": "OCR",
"pending": "Pending",
"pending-locked-rules": "Pending (Rules locked)",
"pending-timeout": "Pending (Rules timeout)",
"pending-unknown": "Pending (unknown)",
"processed": "Processed",
"processing": "Processing"
},
@ -2583,4 +2587,4 @@
}
},
"yesterday": "Yesterday"
}
}

View File

@ -273,9 +273,6 @@
"watermarks": "Watermarks"
},
"analysis-disabled": "Analysis disabled",
"annotation": {
"pending": "(Pending analysis)"
},
"annotation-actions": {
"accept-recommendation": {
"label": "Empfehlung annehmen"
@ -324,21 +321,21 @@
"success": "Schwärzung eingefügt!"
},
"recategorize-annotation": {
"error": "",
"success": ""
"error": "Bearbeiten des Typs fehlgeschlagen: {error}",
"success": "Annotation wurde bearbeitet: {changes} geändert."
},
"recategorize-image": {
"error": "Rekategorisierung des Bildes gescheitert: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet."
},
"remove": {
"error": "Fehler beim Entfernen der Schwärzung: {error}",
"success": "Schwärzung entfernt!"
},
"remove-hint": {
"error": "Failed to remove hint: {error}",
"success": "Hint removed!"
},
"remove": {
"error": "Fehler beim Entfernen der Schwärzung: {error}",
"success": "Schwärzung entfernt!"
},
"undo": {
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
"success": "erfolgreich Rückgängig gemacht"
@ -351,15 +348,15 @@
"remove-highlights": {
"label": "Remove selected earmarks"
},
"resize": {
"label": "Größe ändern"
},
"resize-accept": {
"label": "Größe speichern"
},
"resize-cancel": {
"label": "Größenänderung abbrechen"
},
"resize": {
"label": "Größe ändern"
},
"see-references": {
"label": "See references"
},
@ -393,6 +390,9 @@
"skipped": "Übersprungen",
"text-highlight": "Earmark"
},
"annotation": {
"pending": "(Pending analysis)"
},
"annotations": "Annotations",
"archived-dossiers-listing": {
"no-data": {
@ -587,8 +587,7 @@
"success": {
"generic": "Component rules updated!"
},
"title": "Component rule editor",
"warning-text": "Warning: experimental feature!"
"title": "Component rule editor"
},
"configurations": "Einstellungen",
"confirm-archive-dossier": {
@ -637,18 +636,14 @@
"warning": "Achtung: Diese Aktion kann nicht rückgängig gemacht werden!"
},
"confirmation-dialog": {
"approve-file": {
"question": "Dieses Dokument enthält ungesehene Änderungen. Möchten Sie es trotzdem genehmigen?",
"title": "Warnung!"
},
"approve-file-without-analysis": {
"confirmationText": "Approve without analysis",
"denyText": "Cancel",
"question": "Analysis required to detect new components.",
"title": "Warning!"
},
"approve-multiple-files": {
"question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen. Möchten Sie sie trotzdem genehmigen?",
"approve-file": {
"question": "Dieses Dokument enthält ungesehene Änderungen. Möchten Sie es trotzdem genehmigen?",
"title": "Warnung!"
},
"approve-multiple-files-without-analysis": {
@ -657,6 +652,10 @@
"question": "Analysis required to detect new components for at least one file.",
"title": "Warning"
},
"approve-multiple-files": {
"question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen. Möchten Sie sie trotzdem genehmigen?",
"title": "Warnung!"
},
"assign-file-to-me": {
"question": {
"multiple": "Dieses Dokument wird gerade von einer anderen Person geprüft. Möchten Sie Reviewer werden und sich selbst dem Dokument zuweisen?",
@ -990,7 +989,7 @@
"download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be initially processed in order to download.",
"file-listing": {
"file-entry": {
"file-error": "Reanalyse erforderlich",
"file-error": "Reanalyse erforderlich ({errorCode, select, RULES_EXECUTION_TIMEOUT{Zeitlimit für Regeln} LOCKED_RULES{Regeln gesperrt} other{unbekannt}})",
"file-pending": "Ausstehend ..."
}
},
@ -1026,13 +1025,13 @@
"recent": "Neu ({hours} h)",
"unassigned": "Niemandem zugewiesen"
},
"reanalyse": {
"action": "Datei analysieren"
},
"reanalyse-dossier": {
"error": "Die Dateien konnten nicht für eine Reanalyse eingeplant werden. Bitte versuchen Sie es erneut.",
"success": "Dateien für Reanalyse vorgesehen."
},
"reanalyse": {
"action": "Datei analysieren"
},
"report-download": "Report download",
"start-auto-analysis": "Enable auto-analysis",
"stop-auto-analysis": "Stop auto-analysis",
@ -1066,8 +1065,7 @@
"dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}"
},
"error": {
"conflict": "Dossier state with this name already exists!",
"generic": "Failed to save dossier state!"
"conflict": "Dossier state with this name already exists!"
},
"no-data": {
"title": "There are no dossier states."
@ -1103,14 +1101,6 @@
"total-documents": "Anzahl der Dokumente",
"total-people": "<strong>{count}</strong> {count, plural, one{user} other {users}}"
},
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Active",
"inactive": "Inactive",
"incomplete": "Incomplete"
}
},
"dossier-templates-listing": {
"action": {
"clone": "Clone template",
@ -1145,6 +1135,14 @@
"title": "{length} dossier {length, plural, one{template} other{templates}}"
}
},
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Active",
"inactive": "Inactive",
"incomplete": "Incomplete"
}
},
"dossier-watermark-selector": {
"heading": "Watermarks on documents",
"no-watermark": "There is no watermark defined for the dossier template.<br>Contact your app admin to define one.",
@ -1340,15 +1338,6 @@
"title": "{length} {length, plural, one{entity} other{entities}}"
}
},
"entity": {
"info": {
"actions": {
"revert": "Revert",
"save": "Save changes"
},
"heading": "Edit entity"
}
},
"entity-rules-screen": {
"error": {
"generic": "Something went wrong... Entity rules update failed!"
@ -1360,22 +1349,30 @@
"generic": "Entity rules updated!"
},
"title": "Entity rule editor",
"warning-text": "Warning: experimental feature!",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} found in rules"
},
"entity": {
"info": {
"actions": {
"revert": "Revert",
"save": "Save changes"
},
"heading": "Edit entity"
}
},
"error": {
"deleted-entity": {
"dossier": {
"action": "Zurück zur Übersicht",
"label": "Dieses Dossier wurde gelöscht!"
},
"file": {
"action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
},
"file": {
"action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht."
}
},
"file-preview": {
@ -1393,12 +1390,6 @@
},
"exact-date": "{day} {month} {year} um {hour}:{minute} Uhr",
"file": "Datei",
"file-attribute": {
"update": {
"error": "Failed to update file attribute value!",
"success": "File attribute value has been updated successfully!"
}
},
"file-attribute-encoding-types": {
"ascii": "ASCII",
"iso": "ISO-8859-1",
@ -1409,6 +1400,12 @@
"number": "Nummer",
"text": "Freier Text"
},
"file-attribute": {
"update": {
"error": "Failed to update file attribute value!",
"success": "File attribute value has been updated successfully!"
}
},
"file-attributes-configurations": {
"cancel": "Cancel",
"form": {
@ -1626,15 +1623,6 @@
"csv": "File attributes were imported successfully from uploaded CSV file."
}
},
"filter": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
"hint": "Nut Hinweise",
"image": "Bilder",
"none": "Keine Anmerkungen",
"redaction": "Geschwärzt",
"updated": "Aktualisiert"
},
"filter-menu": {
"filter-options": "Filteroptionen",
"filter-types": "Filter",
@ -1644,6 +1632,15 @@
"unseen-pages": "Nur Anmerkungen auf unsichtbaren Seiten",
"with-comments": "Nur Anmerkungen mit Kommentaren"
},
"filter": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
"hint": "Nut Hinweise",
"image": "Bilder",
"none": "Keine Anmerkungen",
"redaction": "Geschwärzt",
"updated": "Aktualisiert"
},
"filters": {
"assigned-people": "Beauftragt",
"documents-status": "Documents state",
@ -1922,13 +1919,6 @@
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
},
"notifications": {
"button-text": "Notifications",
"deleted-dossier": "Deleted dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Mark as {type, select, read{read} unread{unread} other{}}"
},
"notifications-screen": {
"category": {
"email-notifications": "E-Mail Benachrichtigungen",
@ -1942,6 +1932,7 @@
"dossier": "Dossierbezogene Benachrichtigungen",
"other": "Andere Benachrichtigungen"
},
"options-title": "Wählen Sie aus, in welcher Kategorie Sie benachrichtigt werden möchten",
"options": {
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen bin",
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Überprüfer zugewiesen bin",
@ -1959,7 +1950,6 @@
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
},
"options-title": "Wählen Sie aus, in welcher Kategorie Sie benachrichtigt werden möchten",
"schedule": {
"daily": "Tägliche Zusammenfassung",
"instant": "Sofortig",
@ -1967,6 +1957,13 @@
},
"title": "Benachrichtigungseinstellungen"
},
"notifications": {
"button-text": "Notifications",
"deleted-dossier": "Deleted dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Mark as {type, select, read{read} unread{unread} other{}}"
},
"ocr": {
"confirmation-dialog": {
"cancel": "Cancel",
@ -2049,7 +2046,7 @@
"auto-expand-filters-on-action": "Auto expand filters on my actions",
"help-mode-dialog": "Help Mode Dialog",
"load-all-annotations-warning": "Warning regarding loading all annotations at once in file preview",
"open-structured-view-by-default": "Display Component View by default when opening a document",
"overwrite-file-option": "",
"table-extraction-type": "Table extraction type"
},
"label": "Preferences",
@ -2058,16 +2055,19 @@
"warnings-label": "Prompts and dialogs",
"warnings-subtitle": "Do not show again options"
},
"processing-status": {
"ocr": "OCR",
"pending": "Ausstehend",
"pending-locked-rules": "Ausstehend (Regeln gesperrt)",
"pending-timeout": "Ausstehend (Zeitlimit für Regeln)",
"pending-unknown": "Ausstehend (unbekannt)",
"processed": "Verarbeitet",
"processing": "Verarbeitung läuft"
},
"processing": {
"basic": "Processing",
"ocr": "OCR"
},
"processing-status": {
"ocr": "OCR",
"pending": "Pending",
"processed": "Processed",
"processing": "Processing"
},
"readonly": "Lesemodus",
"readonly-archived": "Read only (archived)",
"redact-text": {
@ -2220,7 +2220,7 @@
}
}
},
"invalid-upload": "Ungültiges Upload-Format ausgewählt! Unterstützt werden Dokumente im .xlsx- und im .docx-Format",
"invalid-upload": "Ungültiges Upload-Format ausgewählt. Unterstützte Formate: .xlsx- und .docx",
"multi-file-report": "(Mehrere Dateien)",
"report-documents": "Dokumente für den Bericht",
"setup": "Click the upload button on the right to upload your component report templates.",
@ -2303,12 +2303,6 @@
"red-user-admin": "Benutzer-Admin",
"regular": "Regulär"
},
"search": {
"active-dossiers": "ganze Plattform",
"all-dossiers": "all documents",
"placeholder": "Nach Dokumenten oder Dokumenteninhalt suchen",
"this-dossier": "in diesem Dossier"
},
"search-screen": {
"cols": {
"assignee": "Bevollmächtigter",
@ -2332,6 +2326,12 @@
"no-match": "Keine Dokumente entsprechen Ihren aktuellen Filtern.",
"table-header": "{length} search {length, plural, one{result} other{results}}"
},
"search": {
"active-dossiers": "ganze Plattform",
"all-dossiers": "all documents",
"placeholder": "Nach Dokumenten oder Dokumenteninhalt suchen",
"this-dossier": "in diesem Dossier"
},
"seconds": "seconds",
"size": "Size",
"smtp-auth-config": {
@ -2587,4 +2587,4 @@
}
},
"yesterday": "Gestern"
}
}

View File

@ -321,8 +321,8 @@
"success": "Annotation added!"
},
"recategorize-annotation": {
"error": "",
"success": ""
"error": "Failed to edit type: {error}",
"success": "Annotation was edited: Changed {changes}."
},
"recategorize-image": {
"error": "Failed to recategorize image: {error}",
@ -989,7 +989,7 @@
"download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be initially processed in order to download.",
"file-listing": {
"file-entry": {
"file-error": "Re-processing required",
"file-error": "Re-processing required ({errorCode, select, RULES_EXECUTION_TIMEOUT{Rules timeout} LOCKED_RULES{Rules locked} other{unknown}})",
"file-pending": "Pending..."
}
},
@ -1531,7 +1531,7 @@
},
"last-assignee": "Last assignee",
"no-data": {
"title": "There are no annotations on this page."
"title": "This page does not contain annotations for the selected component or filter."
},
"quick-nav": {
"jump-first": "Jump to first page",
@ -1548,7 +1548,7 @@
"jump-to-next": "Jump to next",
"jump-to-previous": "Jump to previous",
"label": "Workload",
"no-annotations": "There are no annotations on the selected page or for the selected component.",
"no-annotations": "There are no annotations for the selected component.",
"page-is": "This page is",
"reset": "reset",
"select": "Select",
@ -1556,7 +1556,7 @@
"select-none": "None",
"show-skipped": "Show skipped in document",
"the-filters": "the filters",
"wrong-filters": "The selected filter combination is not possible. Please adjust or"
"wrong-filters": "No annotations for the selected filter combination. Please adjust or"
},
"document-info": {
"close": "Close document info",
@ -2046,6 +2046,7 @@
"auto-expand-filters-on-action": "Auto expand filters on my actions",
"help-mode-dialog": "Help Mode Dialog",
"load-all-annotations-warning": "Warning regarding loading all annotations at once in file preview",
"overwrite-file-option": "Preferred action when re-uploading of an already existing file",
"table-extraction-type": "Table extraction type"
},
"label": "Preferences",
@ -2057,6 +2058,9 @@
"processing-status": {
"ocr": "OCR",
"pending": "Pending",
"pending-locked-rules": "Pending (Rules locked)",
"pending-timeout": "Pending (Rules timeout)",
"pending-unknown": "Pending (unknown)",
"processed": "Processed",
"processing": "Processing"
},
@ -2583,4 +2587,4 @@
}
},
"yesterday": "Yesterday"
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,205 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.-------------------------------------------------------------------
------------[ Log Start: 2019-Oct-10 19:32:29.143159]--------------
System: (Windows, AMD64), PDFNetVersion: 7.1.0.71627
-------------------------------------------------------------------
[ INFO 19:32:29.143159, fontapp, WebFontCreator.cpp(679)]: logging enabled

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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