Compare commits

...

12 Commits

Author SHA1 Message Date
Dominique Eifländer
f182be3db0 Merge branch 'RED10758' into 'master'
RED-10758: Fix remove and recategorize request in applicationType DocuMine

See merge request redactmanager/red-ui!767
2025-01-29 09:30:57 +01:00
Rosario Allegro
81a32f6d58 RED-10758: Fix remove and recategorize request in applicationType DocuMine 2025-01-28 15:34:17 +01:00
Dan Percic
82552b1748 Merge branch 'VM/RED-9580' into 'master'
RED-9580 - added getOne method for stats service to call new users stats...

Closes RED-9580

See merge request redactmanager/red-ui!765
2024-12-13 11:29:39 +01:00
Valentin Mihai
af7a45d739 RED-9580 - added getOne method for stats service to call new users stats endpoint and updated data from "delete user dialog" with new backend data 2024-12-13 12:03:17 +02:00
Valentin-Gabriel Mihai
21013a6fe5 Merge branch 'RED-9885' into 'master'
RED-9885: fixed resize bar behavior & style.

See merge request redactmanager/red-ui!764
2024-12-12 18:13:07 +01:00
Nicoleta Panaghiu
c81ad67844 RED-9885: fixed resize bar behavior & style. 2024-12-12 17:47:56 +02:00
Valentin-Gabriel Mihai
13797f1fb3 Merge branch 'RED-10030' into 'master'
RED-10030: persist the keyColumn value even if csv mapping is disabled.

See merge request redactmanager/red-ui!763
2024-12-12 15:48:47 +01:00
Nicoleta Panaghiu
ef5cd39b16 RED-10030: persist the keyColumn value even if csv mapping is disabled. 2024-12-12 15:59:36 +02:00
Valentin-Gabriel Mihai
e133e944e3 Merge branch 'RED-10589' into 'master'
RED-10589 -  removed file workload from workflow item on documine.

See merge request redactmanager/red-ui!762
2024-12-12 09:34:31 +01:00
Nicoleta Panaghiu
2ffb5bbb63 RED-10589 - removed file workload from workflow item on documine. 2024-12-12 10:30:56 +02:00
Valentin Mihai
0f16644944 Merge branch 'master' into VM/RED-9580 2024-12-11 19:24:25 +02:00
Valentin Mihai
bfe409305c RED-9580 - use new user stats endpoint for everything related to User Management 2024-12-04 15:47:30 +02:00
17 changed files with 132 additions and 73 deletions

View File

@ -57,6 +57,7 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo
if (supportCsvMapping) { if (supportCsvMapping) {
return { return {
...this.#configuration, ...this.#configuration,
keyColumn: this.form.get('keyColumn').value,
filenameMappingColumnHeaderName: this.form.get('keyColumn').value, filenameMappingColumnHeaderName: this.form.get('keyColumn').value,
delimiter: this.form.get('delimiter').value, delimiter: this.form.get('delimiter').value,
encoding: this.form.get('encodingType').value, encoding: this.form.get('encodingType').value,
@ -66,13 +67,14 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo
return { return {
...this.#configuration, ...this.#configuration,
filenameMappingColumnHeaderName: '', filenameMappingColumnHeaderName: '',
keyColumn: this.form.get('keyColumn').value,
}; };
} }
#getForm() { #getForm() {
return this._formBuilder.group({ return this._formBuilder.group({
supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName], supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName],
keyColumn: [this.#configuration.filenameMappingColumnHeaderName || '', [Validators.required]], keyColumn: [this.#configuration.filenameMappingColumnHeaderName || this.#configuration.keyColumn || '', [Validators.required]],
delimiter: [this.#configuration.delimiter || '', [Validators.required]], delimiter: [this.#configuration.delimiter || '', [Validators.required]],
encodingType: [this.#configuration.encoding || FileAttributeEncodingTypes['UTF-8'], [Validators.required]], encodingType: [this.#configuration.encoding || FileAttributeEncodingTypes['UTF-8'], [Validators.required]],
}); });

View File

@ -85,6 +85,7 @@ export default class FileAttributesListingScreenComponent extends ListingCompone
}, },
]; ];
readonly roles = Roles; readonly roles = Roles;
keyColumnValue: string = '';
constructor( constructor(
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
@ -171,13 +172,13 @@ export default class FileAttributesListingScreenComponent extends ListingCompone
FileAttributesConfigurationsDialogComponent, FileAttributesConfigurationsDialogComponent,
{ {
...defaultDialogConfig, ...defaultDialogConfig,
data: this.#existingConfiguration, data: { ...this.#existingConfiguration, keyColumn: this.keyColumnValue },
}, },
); );
const configuration = await firstValueFrom(ref.afterClosed()); const configuration = await firstValueFrom(ref.afterClosed());
if (configuration) { if (configuration) {
this.keyColumnValue = configuration.keyColumn;
await this.#setConfigAndLoadData(configuration); await this.#setConfigAndLoadData(configuration);
} }
} }

View File

@ -22,6 +22,8 @@ import { ConfigureCertificateDialogComponent } from '../dialogs/configure-digita
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component'; import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component'; import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component'; import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
import { UserStatsService } from './user-stats.service';
import { result } from 'lodash-es';
type DialogType = type DialogType =
| 'confirm' | 'confirm'
@ -73,19 +75,26 @@ export class AdminDialogService extends DialogService<DialogType> {
private readonly _activeDossiersService: ActiveDossiersService, private readonly _activeDossiersService: ActiveDossiersService,
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
private readonly _userService: UserService, private readonly _userService: UserService,
private readonly _userStatsService: UserStatsService,
private readonly _reportTemplateService: ReportTemplateService, private readonly _reportTemplateService: ReportTemplateService,
) { ) {
super(_dialog); super(_dialog);
} }
deleteUsers(userIds: string[], cb?: () => Promise<void> | void): void { async deleteUsers(userIds: string[], cb?: () => Promise<void> | void): Promise<void> {
const userStats = await firstValueFrom(this._userStatsService.getOne(userIds[0]));
const data: IConfirmationDialogData = { const data: IConfirmationDialogData = {
title: _('confirm-delete-users.title'), title: _('confirm-delete-users.title'),
question: _('confirm-delete-users.warning'), question: _('confirm-delete-users.warning'),
confirmationText: _('confirm-delete-users.delete'), confirmationText: _('confirm-delete-users.delete'),
denyText: _('confirm-delete-users.cancel'), denyText: _('confirm-delete-users.cancel'),
titleColor: TitleColors.WARN, titleColor: TitleColors.WARN,
translateParams: { usersCount: 1, dossiersCount: this._getUsersDossiersCount(userIds) }, translateParams: {
usersCount: 1,
dossiersCount: userStats.numberOfDossierOwnerships,
documentsCount: userStats.numberOfAssignedFiles,
},
checkboxes: [ checkboxes: [
{ value: false, label: _('confirm-delete-users.impacted-dossiers') }, { value: false, label: _('confirm-delete-users.impacted-dossiers') },
{ value: false, label: _('confirm-delete-users.impacted-documents') }, { value: false, label: _('confirm-delete-users.impacted-documents') },

View File

@ -0,0 +1,10 @@
import { StatsService } from '@iqser/common-ui';
import { IUserStats, USER_ID, UserStats } from '@red/domain';
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UserStatsService extends StatsService<UserStats, IUserStats> {
protected readonly _primaryKey = USER_ID;
protected readonly _entityClass = UserStats;
protected readonly _defaultModelPath = 'user-stats';
}

View File

@ -27,7 +27,9 @@
<redaction-file-attribute [dossier]="dossier" [fileAttribute]="config" [file]="file"></redaction-file-attribute> <redaction-file-attribute [dossier]="dossier" [fileAttribute]="config" [file]="file"></redaction-file-attribute>
</div> </div>
<redaction-file-workload [file]="file"></redaction-file-workload> @if (!isDocumine) {
<redaction-file-workload [file]="file"></redaction-file-workload>
}
<div class="file-actions overflow-visible"> <div class="file-actions overflow-visible">
<redaction-processing-indicator [file]="file" class="mr-8"></redaction-processing-indicator> <redaction-processing-indicator [file]="file" class="mr-8"></redaction-processing-indicator>

View File

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, computed, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core';
import { DisableStopPropagationDirective, HelpModeService } from '@iqser/common-ui'; import { DisableStopPropagationDirective, getConfig, HelpModeService } from '@iqser/common-ui';
import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils'; import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils';
import { Dossier, File, IFileAttributeConfig } from '@red/domain'; import { Dossier, File, IFileAttributeConfig } from '@red/domain';
import { FileAttributesService } from '@services/entity-services/file-attributes.service'; import { FileAttributesService } from '@services/entity-services/file-attributes.service';
@ -36,6 +36,7 @@ import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
export class WorkflowItemComponent implements OnInit { export class WorkflowItemComponent implements OnInit {
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef; @ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
width: number; width: number;
readonly isDocumine = getConfig().IS_DOCUMINE;
readonly trackBy = trackByFactory(); readonly trackBy = trackByFactory();
@Input({ required: true }) file: File; @Input({ required: true }) file: File;
@Input({ required: true }) dossier: Dossier; @Input({ required: true }) dossier: Dossier;

View File

@ -58,9 +58,9 @@ redaction-pdf-viewer.hidden {
} }
.resize { .resize {
background: #444857; background: var(--iqser-grey-4);
height: 100%; height: 100%;
width: 14px; width: 10px;
cursor: col-resize; cursor: col-resize;
position: relative; position: relative;
z-index: 10; z-index: 10;
@ -74,7 +74,7 @@ redaction-pdf-viewer.hidden {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 3px; width: 3px;
height: 15px; height: 15px;
border-inline: 1px solid #fff; border-inline: 1px solid var(--iqser-grey-1);
} }
@media only screen and (max-width: 1015px) { @media only screen and (max-width: 1015px) {

View File

@ -122,6 +122,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
readonly fileId = this.state.fileId; readonly fileId = this.state.fileId;
readonly dossierId = this.state.dossierId; readonly dossierId = this.state.dossierId;
readonly resizeHandle = viewChild<ElementRef>('resize'); readonly resizeHandle = viewChild<ElementRef>('resize');
#overlayElement: HTMLDivElement | null = null;
constructor( constructor(
readonly pdf: PdfViewer, readonly pdf: PdfViewer,
@ -177,16 +178,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._fileDataService.loadAnnotations(file).then(); this._fileDataService.loadAnnotations(file).then();
} }
effect( effect(() => {
() => { if (this._documentViewer.loaded()) {
if (this._documentViewer.loaded()) { this._pageRotationService.clearRotations();
this._pageRotationService.clearRotations(); this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS); this.viewerReady().then();
this.viewerReady().then(); }
} });
},
{ allowSignalWrites: true },
);
effect(() => { effect(() => {
this.state.updateExcludedPagesStyle(); this.state.updateExcludedPagesStyle();
@ -252,10 +250,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
onDragStart(event: CdkDragStart) { onDragStart(event: CdkDragStart) {
event.event.preventDefault(); event.event.preventDefault();
if (!this.isDocumine) return; if (!this.isDocumine) return;
const contentInnerElement = document.body.getElementsByClassName('content-inner').item(0) as HTMLElement; this.#createDragOverlay();
if (contentInnerElement) {
contentInnerElement.classList.add('dragging');
}
} }
onDragMove(event: CdkDragMove) { onDragMove(event: CdkDragMove) {
@ -269,10 +264,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
onDragEnd(event: CdkDragEnd) { onDragEnd(event: CdkDragEnd) {
event.event.preventDefault(); event.event.preventDefault();
if (!this.isDocumine) return; if (!this.isDocumine) return;
const contentInnerElement = document.body.getElementsByClassName('content-inner').item(0) as HTMLElement; this.#removeDragOverlay();
if (contentInnerElement) {
contentInnerElement.classList.remove('dragging');
}
} }
deleteEarmarksOnViewChange$() { deleteEarmarksOnViewChange$() {
@ -311,7 +303,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
handleEscInsideViewer($event: KeyboardEvent) { handleEscInsideViewer($event: KeyboardEvent) {
$event.preventDefault(); $event.preventDefault();
if (!!this._annotationManager.selected[0]) { if (this._annotationManager.selected[0]) {
const doesHaveWrapper = this._fileDataService.find(this._annotationManager.selected[0]?.Id); const doesHaveWrapper = this._fileDataService.find(this._annotationManager.selected[0]?.Id);
if (!doesHaveWrapper) { if (!doesHaveWrapper) {
this._annotationManager.delete(this._annotationManager.selected[0]?.Id); this._annotationManager.delete(this._annotationManager.selected[0]?.Id);
@ -332,24 +324,17 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
@Bind() @Bind()
handleViewerClick(event: MouseEvent) { handleViewerClick(event: MouseEvent) {
this._ngZone.run(() => { this._ngZone.run(() => {
if (event.isTrusted) { if (!event.isTrusted) return;
const clickedElement = event.target as HTMLElement; const clickedElement = event.target as HTMLElement;
const actionIconClicked = ANNOTATION_ACTION_ICONS.some(action => const actionIconClicked = ANNOTATION_ACTION_ICONS.some(action => (clickedElement as HTMLImageElement).src?.includes(action));
(clickedElement as HTMLImageElement).src?.includes(action), const actionClicked = ANNOTATION_ACTIONS.some(action => clickedElement.getAttribute('aria-label')?.includes(action));
); if (!this._multiSelectService.active() || actionIconClicked || actionClicked) return;
const actionClicked = ANNOTATION_ACTIONS.some(action => clickedElement.getAttribute('aria-label')?.includes(action)); if (clickedElement.querySelector('#selectionrect') || clickedElement.id === `pageWidgetContainer${this.pdf.currentPage()}`) {
if (this._multiSelectService.active() && !actionIconClicked && !actionClicked) { if (!this._annotationManager.selected.length) {
if ( this._multiSelectService.deactivate();
clickedElement.querySelector('#selectionrect') ||
clickedElement.id === `pageWidgetContainer${this.pdf.currentPage()}`
) {
if (!this._annotationManager.selected.length) {
this._multiSelectService.deactivate();
}
} else {
this._multiSelectService.deactivate();
}
} }
} else {
this._multiSelectService.deactivate();
} }
}); });
} }
@ -471,7 +456,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
#getPixelsInPercentage(pixels: number) { #getPixelsInPercentage(pixels: number) {
return (pixels / window.screen.width) * 100; return (pixels / document.body.getBoundingClientRect().width) * 100;
} }
async #updateViewMode(): Promise<void> { async #updateViewMode(): Promise<void> {
@ -902,32 +887,30 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const components = this._componentLogService.all; const components = this._componentLogService.all;
const filteredComponentIds = untracked(this.state.componentReferenceIds); const filteredComponentIds = untracked(this.state.componentReferenceIds);
if (filteredComponentIds && annotationFilters) { if (!filteredComponentIds || !annotationFilters) return annotationFilters;
const filteredComponentIdsSet = new Set(filteredComponentIds);
const references = new Set<string>(); const filteredComponentIdsSet = new Set(filteredComponentIds);
for (const component of components) {
for (const componentValue of component.componentValues) { const references = new Set<string>();
for (const reference of componentValue.entityReferences) { for (const component of components) {
if (filteredComponentIdsSet.has(reference.id)) { for (const componentValue of component.componentValues) {
references.add(reference.type); for (const reference of componentValue.entityReferences) {
} if (filteredComponentIdsSet.has(reference.id)) {
references.add(reference.type);
} }
} }
} }
return annotationFilters
.map(filter => {
const filteredChildren = filter.children.filter(c => references.has(c.label.replace(/ /g, '_').toLowerCase()));
if (filteredChildren.length) {
return { ...filter, children: filteredChildren };
}
return null;
})
.filter(f => f !== null);
} }
return annotationFilters; return annotationFilters
.map(filter => {
const filteredChildren = filter.children.filter(c => references.has(c.label.replace(/ /g, '_').toLowerCase()));
if (filteredChildren.length) {
return { ...filter, children: filteredChildren };
}
return null;
})
.filter(f => f !== null);
} }
#updateViewerPosition() { #updateViewerPosition() {
@ -942,4 +925,27 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
document.getElementById('viewer')?.classList?.add('redaction-viewer'); document.getElementById('viewer')?.classList?.add('redaction-viewer');
} }
#createDragOverlay() {
if (this.#overlayElement || document.body.contains(this.#overlayElement)) {
return;
}
this.#overlayElement = document.createElement('div');
this.#overlayElement.style.position = 'fixed';
this.#overlayElement.style.top = '0';
this.#overlayElement.style.left = '0';
this.#overlayElement.style.width = '100%';
this.#overlayElement.style.height = '100%';
this.#overlayElement.style.zIndex = '9999';
this.#overlayElement.style.background = 'transparent';
this.#overlayElement.style.pointerEvents = 'all';
document.body.appendChild(this.#overlayElement);
}
#removeDragOverlay() {
if (!this.#overlayElement || !document.body.contains(this.#overlayElement)) return;
document.body.removeChild(this.#overlayElement);
this.#overlayElement = null;
}
} }

View File

@ -137,7 +137,11 @@ export class AnnotationActionsService {
let recategorizeBody: List<IRecategorizationRequest> | IBulkRecategorizationRequest; let recategorizeBody: List<IRecategorizationRequest> | IBulkRecategorizationRequest;
if (result.option === RedactOrHintOptions.ONLY_HERE || result.option === RectangleRedactOptions.ONLY_THIS_PAGE) { if (
result.option === RedactOrHintOptions.ONLY_HERE ||
result.option === RectangleRedactOptions.ONLY_THIS_PAGE ||
this.#isDocumine
) {
recategorizeBody = annotations.map(annotation => { recategorizeBody = annotations.map(annotation => {
const body: IRecategorizationRequest = { const body: IRecategorizationRequest = {
annotationId: annotation.id, annotationId: annotation.id,
@ -167,6 +171,7 @@ export class AnnotationActionsService {
}; };
} }
result.pageNumbers = result.pageNumbers || [];
await this.#processObsAndEmit( await this.#processObsAndEmit(
this._manualRedactionService.recategorizeRedactions( this._manualRedactionService.recategorizeRedactions(
recategorizeBody, recategorizeBody,
@ -606,6 +611,7 @@ export class AnnotationActionsService {
redactions: AnnotationWrapper[], redactions: AnnotationWrapper[],
dialogResult: RemoveRedactionResult, dialogResult: RemoveRedactionResult,
): List<IRemoveRedactionRequest | IBulkLocalRemoveRequest> { ): List<IRemoveRedactionRequest | IBulkLocalRemoveRequest> {
dialogResult.pageNumbers = dialogResult.pageNumbers || [];
if (dialogResult.bulkLocal || !!dialogResult.pageNumbers.length) { if (dialogResult.bulkLocal || !!dialogResult.pageNumbers.length) {
return dialogResult.positions.map((position, index) => ({ return dialogResult.positions.map((position, index) => ({
value: redactions[index].value, value: redactions[index].value,

View File

@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { StatsService } from '@iqser/common-ui'; import { StatsService } from '@iqser/common-ui';
import { DashboardStats, DOSSIER_ID, DossierStats, IDossierStats } from '@red/domain'; import { DOSSIER_ID, DossierStats, IDossierStats } from '@red/domain';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { UserService } from '@users/user.service'; import { UserService } from '@users/user.service';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';

View File

@ -642,7 +642,7 @@
"confirm-delete-users": { "confirm-delete-users": {
"cancel": "Keep {usersCount, plural, one{user} other{users}}", "cancel": "Keep {usersCount, plural, one{user} other{users}}",
"delete": "Delete {usersCount, plural, one{user} other{users}}", "delete": "Delete {usersCount, plural, one{user} other{users}}",
"impacted-documents": "All documents pending review from the {usersCount, plural, one{user} other{users}} will be impacted", "impacted-documents": "{documentsCount} {documentsCount, plural, one{document} other{documents}} will be impacted",
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted", "impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace", "title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
"toast-error": "Please confirm that you understand the consequences of this action.", "toast-error": "Please confirm that you understand the consequences of this action.",

View File

@ -642,7 +642,7 @@
"confirm-delete-users": { "confirm-delete-users": {
"cancel": "Keep {usersCount, plural, one{user} other{users}}", "cancel": "Keep {usersCount, plural, one{user} other{users}}",
"delete": "Delete {usersCount, plural, one{user} other{users}}", "delete": "Delete {usersCount, plural, one{user} other{users}}",
"impacted-documents": "All documents pending review from the {usersCount, plural, one{user} other{users}} will be impacted", "impacted-documents": "{documentsCount} {documentsCount, plural, one{document} other{documents}} will be impacted",
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted", "impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace", "title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
"toast-error": "Please confirm that you understand the consequences of this action.", "toast-error": "Please confirm that you understand the consequences of this action.",

@ -1 +1 @@
Subproject commit 58382ddfeed1d6d4d61f03abd1bbb0dd840afa94 Subproject commit 7f13fa62d3d2b346c609bc4978cff75f37f1ee6b

View File

@ -0,0 +1 @@
export const USER_ID = 'userId';

View File

@ -1,3 +1,6 @@
export * from './user.model'; export * from './user.model';
export * from './profile'; export * from './profile';
export * from './types'; export * from './types';
export * from './user-stats';
export * from './user-stats.model';
export * from './constants';

View File

@ -0,0 +1,13 @@
import { IUserStats } from './user-stats';
export class UserStats implements IUserStats {
readonly numberOfDossierMemberships: number;
readonly numberOfDossierOwnerships: number;
readonly numberOfAssignedFiles: number;
constructor(userStats: IUserStats) {
this.numberOfAssignedFiles = userStats.numberOfAssignedFiles;
this.numberOfDossierOwnerships = userStats.numberOfAssignedFiles;
this.numberOfAssignedFiles = userStats.numberOfAssignedFiles;
}
}

View File

@ -0,0 +1,5 @@
export interface IUserStats {
numberOfDossierMemberships: number;
numberOfDossierOwnerships: number;
numberOfAssignedFiles: number;
}