Merge branch 'master' into VM/RED-6801

This commit is contained in:
Valentin Mihai 2023-07-19 22:41:00 +03:00
commit fa4238b06c
30 changed files with 354 additions and 257 deletions

View File

@ -144,7 +144,7 @@ export const appModuleFactory = (config: AppConfig) => {
enabled: false,
},
PDF: {
enabled: true,
enabled: false,
},
FILE: {
enabled: true,
@ -155,6 +155,15 @@ export const appModuleFactory = (config: AppConfig) => {
STATS: {
enabled: false,
},
REDACTION_LOG: {
enabled: false,
},
VIEWED_PAGES: {
enabled: false,
},
PAGES: {
enabled: false,
},
},
} as ILoggerConfig,
},

View File

@ -41,6 +41,7 @@ export class ChartComponent implements OnChanges {
text: this.yAxisLabel,
padding: { bottom: 20 },
},
min: 0,
},
y1: {
display: this.secondaryAxis,
@ -50,6 +51,7 @@ export class ChartComponent implements OnChanges {
text: this.yAxisLabelRight,
padding: { bottom: 20 },
},
min: 0,
},
},
plugins: {

View File

@ -0,0 +1,62 @@
<div class="grid-container">
<div class="section-title all-caps-label" translate="license-info-screen.capacity-details"></div>
<div class="row">
<div translate="license-info-screen.capacity.active-documents"></div>
<div>{{ licenseService.currentLicenseReport.activeFilesUploadedBytes | size }}</div>
</div>
<div class="row">
<div translate="license-info-screen.capacity.archived-documents"></div>
<div>{{ licenseService.currentLicenseReport.archivedFilesUploadedBytes | size }}</div>
</div>
<div class="row">
<div translate="license-info-screen.capacity.trash-documents"></div>
<div>{{ licenseService.currentLicenseReport.trashFilesUploadedBytes | size }}</div>
</div>
<div class="row">
<div translate="license-info-screen.capacity.all-documents"></div>
<div>
{{ licenseService.currentLicenseReport.totalFilesUploadedBytes | size }}
<ng-container *ngIf="uploadedBytesCapacityPercentage !== -1">
({{ uploadedBytesCapacityPercentage | number : '1.0-2' }}%)
</ng-container>
</div>
</div>
<div class="donut-chart-wrapper pl-20">
<redaction-donut-chart
*ngIf="uploadedBytesCapacityPercentage !== -1"
[config]="donutChartConfig"
[direction]="'row'"
[radius]="80"
[strokeWidth]="15"
[subtitleTemplate]="capacitySubtitles"
[totalType]="'sum'"
[valueFormatter]="size"
></redaction-donut-chart>
</div>
<div class="row">
<redaction-chart
*ngIf="data$ | async as data"
[datasets]="data.datasets"
[labels]="data.labels"
[reverseLegend]="true"
[valueFormatter]="formatSize"
></redaction-chart>
</div>
</div>
<ng-template #capacitySubtitles>
<span *ngIf="uploadedBytesCapacityPercentage <= 100; else exceeded">
{{ 'license-info-screen.capacity.storage-capacity' | translate }}
</span>
<ng-template #exceeded>
<span class="exceeded-capacity">
{{ 'license-info-screen.capacity.exceeded-capacity' | translate }}
</span>
</ng-template>
</ng-template>

View File

@ -0,0 +1,49 @@
:host {
display: contents;
}
.grid-container {
width: calc(100% - 40px);
display: grid;
grid-template-columns: 1fr 2fr 2fr;
margin: 20px;
.donut-chart-wrapper {
grid-row: 2 / span 4;
grid-column: 3;
width: fit-content;
}
.row {
display: contents;
> div {
padding: 8px 20px;
&:first-of-type {
font-weight: 600;
}
}
&:hover {
> div {
background-color: var(--iqser-alt-background);
}
}
}
.section-title {
grid-column: span 3;
padding: 20px 20px 8px;
margin-bottom: 8px;
border-bottom: 1px solid var(--iqser-separator);
}
redaction-chart {
grid-column: span 3;
}
}
.exceeded-capacity {
color: var(--iqser-red-1);
}

View File

@ -10,15 +10,15 @@ import { getLabelsFromLicense, getLineConfig } from '../../utils/functions';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'red-license-storage',
templateUrl: './license-storage.component.html',
styleUrls: ['./license-storage.component.scss'],
selector: 'red-license-capacity',
templateUrl: './license-capacity.component.html',
styleUrls: ['./license-capacity.component.scss'],
})
export class LicenseStorageComponent {
export class LicenseCapacityComponent {
readonly formatSize = size;
uploadedBytesCapacityPercentage = -1;
donutChartConfig: DonutChartConfig[] = [];
readonly data$ = this.licenseService.licenseData$.pipe(
readonly data$ = this.licenseService.selectedLicense$.pipe(
map(() => this.licenseService.currentLicenseReport),
tap(license => {
this.uploadedBytesCapacityPercentage = this.#getUploadedBytesCapacityPercentage(license);
@ -44,22 +44,22 @@ export class LicenseStorageComponent {
{
value: license.activeFilesUploadedBytes,
color: ChartGreen,
label: this._translateService.instant(_('license-info-screen.storage.active-documents')),
label: this._translateService.instant(_('license-info-screen.capacity.active-documents')),
},
{
value: license.archivedFilesUploadedBytes,
color: ChartBlue,
label: this._translateService.instant(_('license-info-screen.storage.archived-documents')),
label: this._translateService.instant(_('license-info-screen.capacity.archived-documents')),
},
{
value: license.trashFilesUploadedBytes,
color: ChartRed,
label: this._translateService.instant(_('license-info-screen.storage.trash-documents')),
label: this._translateService.instant(_('license-info-screen.capacity.trash-documents')),
},
{
value: this.licenseService.uploadedBytesCapacity - license.totalFilesUploadedBytes,
value: Math.max(this.licenseService.uploadedBytesCapacity - license.totalFilesUploadedBytes, 0),
color: ChartGrey,
label: this._translateService.instant(_('license-info-screen.storage.unused')),
label: this._translateService.instant(_('license-info-screen.capacity.unused')),
},
];
}
@ -70,25 +70,25 @@ export class LicenseStorageComponent {
return [
{
data: monthlyData.flatMap(d => d.activeFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.storage.active-documents'),
label: this._translateService.instant('license-info-screen.capacity.active-documents'),
...getLineConfig(ChartGreen, false, 'origin'),
stack: 'storage',
},
{
data: monthlyData.flatMap(d => d.archivedFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.storage.archived-documents'),
label: this._translateService.instant('license-info-screen.capacity.archived-documents'),
...getLineConfig(ChartBlue, false, '-1'),
stack: 'storage',
},
{
data: monthlyData.flatMap(d => d.trashFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.storage.trash-documents'),
label: this._translateService.instant('license-info-screen.capacity.trash-documents'),
...getLineConfig(ChartRed, false, '-1'),
stack: 'storage',
},
{
data: monthlyData.flatMap(d => d.activeFilesUploadedBytes + d.archivedFilesUploadedBytes + d.trashFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.storage.all-documents'),
label: this._translateService.instant('license-info-screen.capacity.all-documents'),
...getLineConfig(ChartBlack, true, false),
borderWidth: 2,
},

View File

@ -1,48 +0,0 @@
<div class="section-title all-caps-label" translate="license-info-screen.storage-details"></div>
<div class="row">
<div translate="license-info-screen.storage.active-documents"></div>
<div>{{ licenseService.currentLicenseReport.activeFilesUploadedBytes | size }}</div>
</div>
<div class="row">
<div translate="license-info-screen.storage.archived-documents"></div>
<div>{{ licenseService.currentLicenseReport.archivedFilesUploadedBytes | size }}</div>
</div>
<div class="row">
<div translate="license-info-screen.storage.trash-documents"></div>
<div>{{ licenseService.currentLicenseReport.trashFilesUploadedBytes | size }}</div>
</div>
<div class="row">
<div translate="license-info-screen.storage.all-documents"></div>
<div>
{{ licenseService.currentLicenseReport.totalFilesUploadedBytes | size }}
<ng-container *ngIf="uploadedBytesCapacityPercentage !== -1">
({{ uploadedBytesCapacityPercentage | number : '1.0-2' }}%)
</ng-container>
</div>
</div>
<div class="donut-chart-wrapper pl-20">
<redaction-donut-chart
*ngIf="uploadedBytesCapacityPercentage !== -1"
[config]="donutChartConfig"
[direction]="'row'"
[radius]="80"
[strokeWidth]="15"
[subtitles]="['license-info-screen.storage.storage-capacity' | translate]"
[totalType]="'sum'"
[valueFormatter]="size"
></redaction-donut-chart>
</div>
<div class="row">
<redaction-chart
*ngIf="data$ | async as data"
[datasets]="data.datasets"
[labels]="data.labels"
[reverseLegend]="true"
[valueFormatter]="formatSize"
></redaction-chart>
</div>

View File

@ -1,38 +0,0 @@
:host {
display: contents;
}
.donut-chart-wrapper {
grid-row: 11 / span 4;
grid-column: 3;
width: fit-content;
}
.row {
display: contents;
> div {
padding: 8px 20px;
&:first-of-type {
font-weight: 600;
}
}
&:hover {
> div {
background-color: var(--iqser-alt-background);
}
}
}
.section-title {
grid-column: span 3;
padding: 20px 20px 8px;
margin-bottom: 8px;
border-bottom: 1px solid var(--iqser-separator);
}
redaction-chart {
grid-column: span 3;
}

View File

@ -1,41 +1,43 @@
<div class="section-title all-caps-label" translate="license-info-screen.usage-details"></div>
<div class="grid-container">
<div class="section-title all-caps-label" translate="license-info-screen.usage-details"></div>
<div class="row">
<div translate="license-info-screen.current-analyzed"></div>
<div>
{{ licenseService.analyzedPagesInCurrentLicensingPeriod }}
({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%)
<div class="row">
<div translate="license-info-screen.current-analyzed"></div>
<div>
{{ licenseService.analyzedPagesInCurrentLicensingPeriod }}
({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%)
</div>
</div>
<div class="row">
<div translate="license-info-screen.ocr-analyzed-pages"></div>
<div>{{ licenseService.currentLicenseReport.numberOfOcrPages }}</div>
</div>
<div *ngIf="!!licenseService.unlicensedPages" class="row">
<div translate="license-info-screen.unlicensed-analyzed"></div>
<div>{{ licenseService.unlicensedPages }}</div>
</div>
<div class="row">
<div [innerHTML]="'license-info-screen.total-analyzed' | translate"></div>
<div>{{ licenseService.allLicensesReport.numberOfAnalyzedPages }}</div>
</div>
<div class="row">
<div [innerHTML]="'license-info-screen.total-ocr-analyzed' | translate"></div>
<div>{{ licenseService.allLicensesReport.numberOfOcrPages }}</div>
</div>
<div class="row">
<redaction-chart
*ngIf="data$ | async as data"
[datasets]="data.datasets"
[labels]="data.labels"
[secondaryAxis]="true"
[yAxisLabelRight]="'license-info-screen.pages.total-pages' | translate"
[yAxisLabel]="'license-info-screen.pages.pages-per-month' | translate"
></redaction-chart>
</div>
</div>
<div class="row">
<div translate="license-info-screen.ocr-analyzed-pages"></div>
<div>{{ licenseService.currentLicenseReport.numberOfOcrPages }}</div>
</div>
<div *ngIf="!!licenseService.unlicensedPages" class="row">
<div translate="license-info-screen.unlicensed-analyzed"></div>
<div>{{ licenseService.unlicensedPages }}</div>
</div>
<div class="row">
<div [innerHTML]="'license-info-screen.total-analyzed' | translate"></div>
<div>{{ licenseService.allLicensesReport.numberOfAnalyzedPages }}</div>
</div>
<div class="row">
<div [innerHTML]="'license-info-screen.total-ocr-analyzed' | translate"></div>
<div>{{ licenseService.allLicensesReport.numberOfOcrPages }}</div>
</div>
<div class="row">
<redaction-chart
*ngIf="data$ | async as data"
[datasets]="data.datasets"
[labels]="data.labels"
[secondaryAxis]="true"
[yAxisLabelRight]="'license-info-screen.pages.total-pages' | translate"
[yAxisLabel]="'license-info-screen.pages.pages-per-month' | translate"
></redaction-chart>
</div>

View File

@ -2,35 +2,42 @@
display: contents;
}
.row {
display: contents;
.grid-container {
width: calc(100% - 40px);
display: grid;
grid-template-columns: 1fr 2fr 2fr;
margin: 20px;
> div {
padding: 8px 20px;
.row {
display: contents;
&:first-of-type {
font-weight: 600;
}
&:nth-child(2) {
grid-column: span 2;
}
}
&:hover {
> div {
background-color: var(--iqser-alt-background);
padding: 8px 20px;
&:first-of-type {
font-weight: 600;
}
&:nth-child(2) {
grid-column: span 2;
}
}
&:hover {
> div {
background-color: var(--iqser-alt-background);
}
}
}
}
.section-title {
grid-column: span 3;
padding: 20px 20px 8px;
margin-bottom: 8px;
border-bottom: 1px solid var(--iqser-separator);
}
.section-title {
grid-column: span 3;
padding: 20px 20px 8px;
margin-bottom: 8px;
border-bottom: 1px solid var(--iqser-separator);
}
redaction-chart {
grid-column: span 3;
redaction-chart {
grid-column: span 3;
}
}

View File

@ -61,9 +61,14 @@
<div>{{ licenseService.totalLicensedNumberOfPages }}</div>
</div>
<red-license-storage></red-license-storage>
<red-license-usage></red-license-usage>
<div *ngIf="licenseService.uploadedBytesCapacity" class="row">
<div>{{ 'license-info-screen.licensed-capacity' | translate }}</div>
<div>{{ licenseService.uploadedBytesCapacity | size }}</div>
</div>
</div>
<red-license-usage></red-license-usage>
<red-license-capacity></red-license-capacity>
</div>
</div>
</div>

View File

@ -10,7 +10,7 @@ import { CommonModule } from '@angular/common';
import { LicenseService } from '@services/license.service';
import { ChartComponent } from './components/chart/chart.component';
import { NgChartsModule } from 'ng2-charts';
import { LicenseStorageComponent } from './components/license-storage/license-storage.component';
import { LicenseCapacityComponent } from './components/license-storage/license-capacity.component';
import { LicenseUsageComponent } from './components/license-usage/license-usage.component';
import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
@ -25,7 +25,7 @@ const routes: Routes = [
];
@NgModule({
declarations: [LicenseScreenComponent, LicenseSelectComponent, ChartComponent, LicenseStorageComponent, LicenseUsageComponent],
declarations: [LicenseScreenComponent, LicenseSelectComponent, ChartComponent, LicenseCapacityComponent, LicenseUsageComponent],
imports: [
RouterModule.forChild(routes),
CommonModule,

View File

@ -1,5 +1,10 @@
<ng-container *ngIf="configService.listingMode$ | async as mode">
<div (click)="editFileAttribute($event)" [ngClass]="{ 'workflow-attribute': mode === 'workflow' }" class="file-attribute">
<div
(mousedown)="handleControlClick($event)"
(click)="editFileAttribute($event)"
[ngClass]="{ 'workflow-attribute': mode === 'workflow' }"
class="file-attribute"
>
<div [ngClass]="{ 'workflow-value': mode === 'workflow' }" class="value">
<b *ngIf="mode === 'workflow' && !isInEditMode"> {{ fileAttribute.label }}: </b>
<span

View File

@ -82,6 +82,12 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
}
}
handleControlClick($event: MouseEvent) {
if ($event.ctrlKey) {
$event.stopPropagation();
}
}
ngOnDestroy() {
this.#subscriptions.unsubscribe();
}

View File

@ -1,4 +1,4 @@
import { Component, inject, Input } from '@angular/core';
import { AfterViewInit, Component, inject, Input } from '@angular/core';
import { List } from '@iqser/common-ui/lib/utils';
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
import { MultiSelectService } from '../../services/multi-select.service';
@ -7,13 +7,14 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
import { ViewedPage } from '@red/domain';
import scrollIntoView from 'scroll-into-view-if-needed';
@Component({
selector: 'redaction-pages',
templateUrl: './pages.component.html',
styleUrls: ['./pages.component.scss'],
})
export class PagesComponent {
export class PagesComponent implements AfterViewInit {
readonly #state = inject(FilePreviewStateService);
readonly #multiSelectService = inject(MultiSelectService);
readonly #listingService = inject(AnnotationsListingService);
@ -22,6 +23,22 @@ export class PagesComponent {
@Input({ required: true }) displayedAnnotations: Map<number, AnnotationWrapper[]>;
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
ngAfterViewInit() {
setTimeout(() => {
this.scrollToLastViewedPage();
}, 100);
}
scrollToLastViewedPage() {
const currentPdfPage = this._pdf.currentPage();
scrollIntoView(document.getElementById(`quick-nav-page-${currentPdfPage}`), {
behavior: 'smooth',
scrollMode: 'if-needed',
block: 'start',
inline: 'start',
});
}
pageSelectedByClick($event: number): void {
this._pdf.navigateTo($event);
}

View File

@ -43,14 +43,14 @@ export function chronologicallyBy<T>(property: (x: T) => string) {
@Injectable()
export class FileDataService extends EntitiesService<AnnotationWrapper, AnnotationWrapper> {
readonly #annotations = signal<AnnotationWrapper[]>([]);
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
#originalViewedPages: ViewedPage[] = [];
protected readonly _entityClass = AnnotationWrapper;
missingTypes = new Set<string>();
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
readonly annotations: Signal<AnnotationWrapper[]>;
readonly annotations$: Observable<AnnotationWrapper[]>;
protected readonly _entityClass = AnnotationWrapper;
readonly #annotations = signal<AnnotationWrapper[]>([]);
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
#originalViewedPages: ViewedPage[] = [];
constructor(
private readonly _state: FilePreviewStateService,
@ -155,12 +155,12 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
async #loadViewedPages(file: File) {
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
this._viewedPagesMapService.set(file.fileId, []);
this._logger.info('[VIEWED-PAGES] Cannot mark pages as viewed, skip loading viewed pages');
this._logger.info('[VIEWED_PAGES] Cannot mark pages as viewed, skip loading viewed pages');
return;
}
this.#originalViewedPages = await this._viewedPagesService.load(file.dossierId, file.fileId);
this._logger.info('[VIEWED-PAGES] Loaded viewed pages', this.#originalViewedPages);
this._logger.info('[VIEWED_PAGES] Loaded viewed pages', this.#originalViewedPages);
this._viewedPagesMapService.set(file.fileId, this.#originalViewedPages);
}

View File

@ -48,7 +48,7 @@ export class PdfProxyService {
readonly #visibilityIcon = this._convertPath('/assets/icons/general/visibility.svg');
readonly #falsePositiveIcon = this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg');
readonly #addRedactionIcon = this._iqserPermissionsService.has(Roles.getRss)
? this._convertPath('/assets/icons/general/pdftron-action-add-component.svg')
? this._convertPath('/assets/icons/general/pdftron-action-add-annotation.svg')
: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg');
readonly #addHintIcon = this._convertPath('/assets/icons/general/pdftron-action-add-hint.svg');
readonly annotationSelected$ = this.#annotationSelected$;

View File

@ -21,6 +21,10 @@
<div class="heading-xl">{{ getFormattedValue(displayedDataTotal) }}</div>
<div *ngIf="subtitles.length === 1" class="mt-5">{{ subtitles[0] }}</div>
<div *ngIf="subtitleTemplate as t" class="mt-5">
<ng-container *ngTemplateOutlet="t"></ng-container>
</div>
<mat-select
(selectionChange)="subtitleChanged.emit(subtitles.indexOf($event.value))"
*ngIf="subtitles.length > 1"

View File

@ -1,9 +1,9 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Optional, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, OnInit, Optional, Output, TemplateRef } from '@angular/core';
import { DonutChartConfig } from '@red/domain';
import { IqserHelpModeModule } from '@iqser/common-ui';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { AsyncPipe, NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
import { MatSelectModule } from '@angular/material/select';
import { FilterService, INestedFilter } from '@iqser/common-ui/lib/filtering';
import { get, shareLast } from '@iqser/common-ui/lib/utils';
@ -14,10 +14,10 @@ import { StatusBarComponent } from '@iqser/common-ui/lib/shared';
templateUrl: './donut-chart.component.html',
styleUrls: ['./donut-chart.component.scss'],
standalone: true,
imports: [NgForOf, NgIf, MatSelectModule, IqserHelpModeModule, StatusBarComponent, AsyncPipe],
imports: [NgForOf, NgIf, MatSelectModule, IqserHelpModeModule, StatusBarComponent, AsyncPipe, NgTemplateOutlet],
})
export class DonutChartComponent implements OnChanges, OnInit {
@Input() subtitles: string[];
@Input() subtitles: string[] = [];
@Input() config: DonutChartConfig[] = [];
@Input() radius = 85;
@Input() strokeWidth = 20;
@ -27,6 +27,7 @@ export class DonutChartComponent implements OnChanges, OnInit {
@Input() filterKey;
@Input() helpModeKey;
@Input() valueFormatter?: (value: number) => string;
@Input() subtitleTemplate?: TemplateRef<any>;
@Output() readonly subtitleChanged = new EventEmitter<number>();
@ -36,10 +37,6 @@ export class DonutChartComponent implements OnChanges, OnInit {
size = 0;
filters$: Observable<INestedFilter[]>;
constructor(@Optional() readonly filterService: FilterService) {
// TODO: move this component to a separate module, split into smaller components, improve filters
}
get circumference(): number {
return 2 * Math.PI * this.radius;
}
@ -52,6 +49,10 @@ export class DonutChartComponent implements OnChanges, OnInit {
return this.totalType === 'sum' ? this.dataTotal : this.config.length;
}
constructor(@Optional() readonly filterService: FilterService) {
// TODO: move this component to a separate module, split into smaller components, improve filters
}
ngOnInit() {
const filterModels$ = this.filterService?.getFilterModels$(this.filterKey).pipe(
map(filters => filters ?? []),

View File

@ -76,12 +76,11 @@ export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialog
}
get invalidDownload() {
const hasReportTypes = this.form.controls.reportTemplateIds.value.length;
const downloadFileTypes = this.form.controls.downloadFileTypes.value;
const onlyRedactedVersionSelected =
downloadFileTypes.length && downloadFileTypes.every(type => type === DownloadFileTypes.REDACTED);
return !hasReportTypes || (onlyRedactedVersionSelected && !this.hasApprovedFiles);
return onlyRedactedVersionSelected && !this.hasApprovedFiles;
}
override get valid() {

View File

@ -35,7 +35,7 @@ export class DossiersChangesService extends GenericService<Dossier> implements O
changes.map(change => this.#load(change.dossierId).pipe(removeIfNotFound(change.dossierId)));
return this.hasChangesDetails$().pipe(
tap(changes => this.#logger.info('[CHANGES] Dossier changes', changes)),
tap(changes => this.#logger.info('[DOSSIERS_CHANGES] Found changes', changes)),
switchMap(dossierChanges =>
forkJoin([...load(dossierChanges), this.#dashboardStatsService.loadAll().pipe(take(1))]).pipe(map(() => dossierChanges)),
),
@ -45,9 +45,13 @@ export class DossiersChangesService extends GenericService<Dossier> implements O
hasChangesDetails$(): Observable<IDossierChanges> {
const body = { value: this._lastCheckedForChanges.get(ROOT_CHANGES_KEY) };
const dateBeforeRequest = new Date(Date.now() - LAST_CHECKED_OFFSET).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),
tap(() => this._lastCheckedForChanges.set(ROOT_CHANGES_KEY, dateBeforeRequest)),
tap(() => this.#logger.info('[DOSSIERS_CHANGES] Save Last Checked Date value', dateBeforeRequest)),
);
}

View File

@ -40,11 +40,6 @@ const defaultOnError: ILicenses = {
providedIn: 'root',
})
export class LicenseService extends GenericService<ILicenseReport> {
readonly #licenseData$ = new BehaviorSubject<ILicenses | undefined>(undefined);
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
readonly #logger = inject(NGXLogger);
readonly #toaster = inject(Toaster);
protected readonly _defaultModelPath = 'report';
readonly licenseData$: Observable<ILicenses>;
readonly selectedLicense$: Observable<ILicense>;
activeLicenseId: string;
@ -53,29 +48,21 @@ export class LicenseService extends GenericService<ILicenseReport> {
allLicensesReport: ILicenseReport = {};
unlicensedPages = 0;
analyzedPagesInCurrentLicensingPeriod = 0;
uploadedBytesCapacity = 0;
constructor() {
super();
this.selectedLicense$ = this.#selectedLicense$.pipe(filter(license => !!license));
this.licenseData$ = this.#licenseData$.pipe(
filter(licenses => !!licenses),
tap(data => (this.activeLicenseId = data.activeLicense)),
tap(() => {
const activeLicense = this.activeLicense;
if (!activeLicense) {
return;
}
const uploadedBytesCapacity = this.activeLicense.features.find(f => f.name === 'uploadedBytesCapacity')?.value;
this.uploadedBytesCapacity = uploadedBytesCapacity ? parseInt(uploadedBytesCapacity, 10) : 0;
}),
);
}
protected readonly _defaultModelPath = 'report';
readonly #licenseData$ = new BehaviorSubject<ILicenses | undefined>(undefined);
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
readonly #logger = inject(NGXLogger);
readonly #toaster = inject(Toaster);
get selectedLicense() {
return this.#selectedLicense$.value;
}
get uploadedBytesCapacity(): number {
const uploadedBytesCapacity = this.selectedLicense.features.find(f => f.name === 'uploadedBytesCapacity')?.value || '0';
return parseInt(uploadedBytesCapacity, 10);
}
get activeLicense() {
if (!this.#licenseData$.value) {
return undefined;
@ -94,6 +81,15 @@ export class LicenseService extends GenericService<ILicenseReport> {
return activeLicense.features.find(f => f.name === 'pdftron').value;
}
constructor() {
super();
this.selectedLicense$ = this.#selectedLicense$.pipe(filter(license => !!license));
this.licenseData$ = this.#licenseData$.pipe(
filter(licenses => !!licenses),
tap(data => (this.activeLicenseId = data.activeLicense)),
);
}
async loadLicenseData(license: ILicense = this.selectedLicense) {
this.totalLicensedNumberOfPages = this.getTotalLicensedNumberOfPages(license);

View File

@ -473,7 +473,6 @@
"heading-with-name-and-link": "Ihr Benutzer verfügt nicht über die erforderlichen RED-*-Rollen, um auf diese Applikation zugreifen zu können. Bitte kontaktieren Sie <a href={adminUrl} target=_blank >{adminName}</a>, um den Zugang anzufordern!",
"logout": "Ausloggen"
},
"by": "von",
"change-legal-basis-dialog": {
"actions": {
"cancel": "Abbrechen",
@ -1639,6 +1638,16 @@
},
"license-info-screen": {
"backend-version": "Backend-Version der Anwendung",
"capacity-details": "",
"capacity": {
"active-documents": "",
"all-documents": "",
"archived-documents": "",
"exceeded-capacity": "",
"storage-capacity": "",
"trash-documents": "",
"unused": ""
},
"copyright-claim-text": "Copyright © 2020 - {currentYear} knecon AG (powered by IQSER)",
"copyright-claim-title": "Copyright",
"current-analyzed": "In aktuellem Lizenzzeitraum analysierte Seiten",
@ -1654,6 +1663,7 @@
"end-user-license-text": "Die Nutzung dieses Produkts unterliegt den Bedingungen der Endbenutzer-Lizenzvereinbarung für den RedactManager, sofern darin nichts anderweitig festgelegt.",
"end-user-license-title": "Endbenutzer-Lizenzvereinbarung",
"license-title": "",
"licensed-capacity": "",
"licensed-page-count": "Anzahl der lizenzierten Seiten",
"licensed-to": "Lizenziert für",
"licensing-details": "Lizenzdetails",
@ -1668,15 +1678,6 @@
"active": "Aktiv",
"inactive": ""
},
"storage-details": "",
"storage": {
"active-documents": "",
"all-documents": "",
"archived-documents": "",
"storage-capacity": "",
"trash-documents": "",
"unused": ""
},
"total-analyzed": "Seit {date} insgesamt analysierte Seiten",
"total-ocr-analyzed": "",
"unlicensed-analyzed": "Über Lizenz hinaus analysierte Seiten",

View File

@ -473,7 +473,6 @@
"heading-with-name-and-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >{adminName}</a> to assign appropriate roles.",
"logout": "Logout"
},
"by": "by",
"change-legal-basis-dialog": {
"actions": {
"cancel": "Cancel",
@ -1639,6 +1638,16 @@
},
"license-info-screen": {
"backend-version": "Backend Application Version",
"capacity-details": "Capacity Details",
"capacity": {
"active-documents": "Active Documents",
"all-documents": "Retention Capacity Used",
"archived-documents": "Archived Documents",
"exceeded-capacity": "Exceeded Capacity",
"storage-capacity": "Capacity",
"trash-documents": "Documents in Trash",
"unused": "Unused Storage"
},
"copyright-claim-text": "Copyright © 2020 - {currentYear} knecon AG (powered by IQSER)",
"copyright-claim-title": "Copyright Claim",
"current-analyzed": "Analyzed Pages in Licensing Period",
@ -1654,6 +1663,7 @@
"end-user-license-text": "The use of this product is subject to the terms of the Redaction End User Agreement, unless otherwise specified therein.",
"end-user-license-title": "End User License Agreement",
"license-title": "License Title",
"licensed-capacity": "Licensed Capacity",
"licensed-page-count": "Licensed Pages",
"licensed-to": "Licensed to",
"licensing-details": "Licensing Details",
@ -1668,15 +1678,6 @@
"active": "Active",
"inactive": "Inactive"
},
"storage-details": "Storage Details",
"storage": {
"active-documents": "Active Documents",
"all-documents": "Total Storage Used",
"archived-documents": "Archived Documents",
"storage-capacity": "Storage Capacity",
"trash-documents": "Documents in Trash",
"unused": "Unused Storage"
},
"total-analyzed": "Total Analyzed Pages",
"total-ocr-analyzed": "Total OCR Processed Pages",
"unlicensed-analyzed": "Unlicensed Analyzed Pages",

View File

@ -473,7 +473,6 @@
"heading-with-name-and-link": "Ihr Benutzer verfügt nicht über die erforderlichen RED-*-Rollen, um auf diese Applikation zugreifen zu können. Bitte kontaktieren Sie <a href={adminUrl} target=_blank >{adminName}</a>, um den Zugang anzufordern!",
"logout": "Ausloggen"
},
"by": "von",
"change-legal-basis-dialog": {
"actions": {
"cancel": "Abbrechen",
@ -1639,6 +1638,16 @@
},
"license-info-screen": {
"backend-version": "Backend-Version der Anwendung",
"capacity-details": "",
"capacity": {
"active-documents": "",
"all-documents": "",
"archived-documents": "",
"exceeded-capacity": "",
"storage-capacity": "",
"trash-documents": "",
"unused": ""
},
"copyright-claim-text": "Copyright © 2020 - {currentYear} knecon AG (powered by IQSER)",
"copyright-claim-title": "Copyright",
"current-analyzed": "In aktuellem Lizenzzeitraum analysierte Seiten",
@ -1654,6 +1663,7 @@
"end-user-license-text": "Die Nutzung dieses Produkts unterliegt den Bedingungen der Endbenutzer-Lizenzvereinbarung für den RedactManager, sofern darin nichts anderweitig festgelegt.",
"end-user-license-title": "Endbenutzer-Lizenzvereinbarung",
"license-title": "",
"licensed-capacity": "",
"licensed-page-count": "Anzahl der lizenzierten Seiten",
"licensed-to": "Lizenziert für",
"licensing-details": "Lizenzdetails",
@ -1668,15 +1678,6 @@
"active": "Aktiv",
"inactive": ""
},
"storage-details": "",
"storage": {
"active-documents": "",
"all-documents": "",
"archived-documents": "",
"storage-capacity": "",
"trash-documents": "",
"unused": ""
},
"total-analyzed": "Seit {date} insgesamt analysierte Seiten",
"total-ocr-analyzed": "",
"unlicensed-analyzed": "Über Lizenz hinaus analysierte Seiten",

View File

@ -473,7 +473,6 @@
"heading-with-name-and-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >{adminName}</a> to assign appropriate roles.",
"logout": "Logout"
},
"by": "by",
"change-legal-basis-dialog": {
"actions": {
"cancel": "Cancel",
@ -1639,6 +1638,16 @@
},
"license-info-screen": {
"backend-version": "Backend Application Version",
"capacity-details": "Capacity Details",
"capacity": {
"active-documents": "Active Documents",
"all-documents": "Retention Capacity Used",
"archived-documents": "Archived Documents",
"exceeded-capacity": "Exceeded Capacity",
"storage-capacity": "Capacity",
"trash-documents": "Documents in Trash",
"unused": "Unused Storage"
},
"copyright-claim-text": "Copyright © 2020 - {currentYear} knecon AG (powered by IQSER)",
"copyright-claim-title": "Copyright Claim",
"current-analyzed": "Analyzed Pages in Licensing Period",
@ -1654,6 +1663,7 @@
"end-user-license-text": "The use of this product is subject to the terms of the Component End User Agreement, unless otherwise specified therein.",
"end-user-license-title": "End User License Agreement",
"license-title": "License Title",
"licensed-capacity": "Licensed Capacity",
"licensed-page-count": "Licensed pages",
"licensed-to": "Licensed to",
"licensing-details": "Licensing Details",
@ -1668,15 +1678,6 @@
"active": "Active",
"inactive": "Inactive"
},
"storage-details": "Storage Details",
"storage": {
"active-documents": "Active Documents",
"all-documents": "Total Storage Used",
"archived-documents": "Archived Documents",
"storage-capacity": "Storage Capacity",
"trash-documents": "Documents in Trash",
"unused": "Unused Storage"
},
"total-analyzed": "Total Analyzed Pages Since {date}",
"total-ocr-analyzed": "Total OCR Processed Pages Since {date}",
"unlicensed-analyzed": "Unlicensed Analyzed Pages",
@ -1941,7 +1942,7 @@
"title": "Redact text"
}
},
"redaction-abbreviation": "C",
"redaction-abbreviation": "A",
"references": "{count} {count, plural, one{reference} other{references}}",
"remove-redaction": {
"dialog": {

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Redacted" stroke="none" stroke-width="1">
<rect fill="#283241" height="16" id="Rectangle" width="16" x="0" y="0"></rect>
<text fill="#FFFFFF" font-family="Inter-SemiBold, Inter" font-size="11" font-weight="500"
id="H">
<tspan x="4.421875" y="12">A</tspan>
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 519 B

View File

@ -2,8 +2,8 @@
<svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Redacted" stroke="none" stroke-width="1">
<circle fill="#94989f" cx="8" cy="8" r="8" />
<text fill="#FFFFFF" font-family="Arial, Helvetica, sans-serif" font-size="11" font-weight="500"
<circle cx="8" cy="8" fill="#94989f" r="8" />
<text fill="#FFFFFF" font-family="Inter-SemiBold, Inter" font-size="11" font-weight="500"
id="H">
<tspan x="4" y="12">H</tspan>
</text>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 479 B

View File

@ -3,7 +3,7 @@
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Redacted" stroke="none" stroke-width="1">
<rect fill="#283241" height="16" id="Rectangle" width="16" x="0" y="0"></rect>
<text fill="#FFFFFF" font-family="Arial, Helvetica, sans-serif" font-size="11" font-weight="500"
<text fill="#FFFFFF" font-family="Inter-SemiBold, Inter" font-size="11" font-weight="500"
id="H">
<tspan x="4.421875" y="12">R</tspan>
</text>

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 519 B

View File

@ -1,4 +1,4 @@
FROM node:18.12-buster as builder
FROM node:20.4-buster as builder
WORKDIR /ng-app

@ -1 +1 @@
Subproject commit e69f8b3f0df8701bbc3dbc79e2239aaad3acc889
Subproject commit fd4491b2a39792ffe3ef76155c1572a45e38406e