diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.scss
similarity index 77%
rename from apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.scss
rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.scss
index 5359d5727..501b10239 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.scss
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.scss
@@ -7,39 +7,17 @@
:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item {
&.last-opened {
- > .selection-column {
+ .selection-column {
padding-left: 6px !important;
border-left: 4px solid variables.$primary;
}
- > div {
+ .selection-column,
+ .cell,
+ .scrollbar-placeholder {
animation: red-fading-background 3s 1;
}
}
-
- > div.cell {
- .error {
- color: variables.$primary;
- }
-
- .table-item-title {
- max-width: 25vw;
- }
-
- .primary-attribute {
- padding-top: 6px;
- @include common-mixins.line-clamp(1);
- }
-
- &.extend-cols {
- grid-column-end: span 3;
- align-items: flex-end;
- }
-
- &.status-container {
- align-items: flex-end;
- }
- }
}
.right-container {
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts
new file mode 100644
index 000000000..87ffe450e
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts
@@ -0,0 +1,280 @@
+import {
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ forwardRef,
+ HostListener,
+ Injector,
+ OnDestroy,
+ OnInit,
+ TemplateRef,
+ ViewChild
+} from '@angular/core';
+import { FileStatus, IFileAttributeConfig } from '@redaction/red-ui-http';
+import { AppStateService } from '@state/app-state.service';
+import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
+import { FileUploadModel } from '@upload-download/model/file-upload.model';
+import { FileUploadService } from '@upload-download/services/file-upload.service';
+import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
+import { TranslateService } from '@ngx-translate/core';
+import * as moment from 'moment';
+import { DossierDetailsComponent } from '../components/dossier-details/dossier-details.component';
+import { File } from '@models/file/file';
+import { UserService } from '@services/user.service';
+import { timer } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { convertFiles, Files, handleFileDrop, OnAttach, OnDetach } from '@utils/index';
+import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
+import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
+import {
+ CircleButtonTypes,
+ DefaultListingServices,
+ ListingComponent,
+ ListingModes,
+ LoadingService,
+ NestedFilter,
+ TableColumnConfig,
+ TableComponent,
+ Toaster,
+ WorkflowConfig
+} from '@iqser/common-ui';
+import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
+import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import { PermissionsService } from '@services/permissions.service';
+import { RouterHistoryService } from '@services/router-history.service';
+import { Dossier } from '@state/model/dossier';
+import { Router } from '@angular/router';
+import { FileAttributesService } from '../../../services/file-attributes.service';
+import { ConfigService as AppConfigService } from '@services/config.service';
+import { ConfigService } from '../config.service';
+
+@Component({
+ templateUrl: './dossier-overview-screen.component.html',
+ styleUrls: ['./dossier-overview-screen.component.scss'],
+ providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }]
+})
+export class DossierOverviewScreenComponent extends ListingComponent
implements OnInit, OnDestroy, OnDetach, OnAttach {
+ readonly listingModes = ListingModes;
+ readonly circleButtonTypes = CircleButtonTypes;
+ readonly currentUser = this._userService.currentUser;
+ currentDossier = this._appStateService.activeDossier;
+ readonly tableHeaderLabel = _('dossier-overview.table-header.title');
+ collapsedDetails = false;
+ dossierAttributes: DossierAttributeWithValue[] = [];
+ fileAttributeConfigs: IFileAttributeConfig[];
+ tableColumnConfigs: readonly TableColumnConfig[];
+ readonly workflowConfig: WorkflowConfig = this._configService.workflowConfig(() => this.reloadDossiers());
+ readonly actionConfigs: readonly ActionConfig[] = this._configService.actionConfig;
+ @ViewChild(DossierDetailsComponent, { static: false }) private readonly _dossierDetailsComponent: DossierDetailsComponent;
+ private _lastScrolledIndex: number;
+ @ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true })
+ private readonly _needsWorkFilterTemplate: TemplateRef;
+ @ViewChild('fileInput') private readonly _fileInput: ElementRef;
+ @ViewChild(TableComponent) private readonly _tableComponent: TableComponent;
+
+ constructor(
+ private readonly _toaster: Toaster,
+ protected readonly _injector: Injector,
+ private readonly _router: Router,
+ private readonly _userService: UserService,
+ readonly permissionsService: PermissionsService,
+ private readonly _loadingService: LoadingService,
+ private readonly _appStateService: AppStateService,
+ readonly routerHistoryService: RouterHistoryService,
+ private readonly _appConfigService: AppConfigService,
+ private readonly _translateService: TranslateService,
+ private readonly _dialogService: DossiersDialogService,
+ private readonly _changeDetectorRef: ChangeDetectorRef,
+ private readonly _fileUploadService: FileUploadService,
+ private readonly _statusOverlayService: StatusOverlayService,
+ private readonly _fileDropOverlayService: FileDropOverlayService,
+ private readonly _dossierAttributesService: DossierAttributesService,
+ private readonly _fileAttributesService: FileAttributesService,
+ private readonly _configService: ConfigService
+ ) {
+ super(_injector);
+ this._loadEntitiesFromState();
+ this.fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig(
+ this.currentDossier.dossierTemplateId
+ )?.fileAttributeConfigs;
+ this.tableColumnConfigs = this._configService.tableConfig(this.displayedAttributes);
+ }
+
+ get checkedRequiredFilters(): NestedFilter[] {
+ return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
+ }
+
+ get checkedNotRequiredFilters(): NestedFilter[] {
+ return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
+ }
+
+ get displayedInFileListAttributes() {
+ return this.fileAttributeConfigs?.filter(config => config.displayedInFileList) || [];
+ }
+
+ get displayedAttributes(): IFileAttributeConfig[] {
+ return this.displayedInFileListAttributes.filter(c => c.displayedInFileList);
+ }
+
+ actionPerformed(action?: string, file?: File) {
+ this.calculateData();
+
+ if (action === 'navigate') {
+ this._router.navigate([file.routerLink]);
+ }
+ }
+
+ disabledFn = (fileStatus: File) => fileStatus.excluded;
+
+ lastOpenedFn = (fileStatus: File) => fileStatus.lastOpened;
+
+ async ngOnInit(): Promise {
+ this._loadingService.start();
+ try {
+ this._fileDropOverlayService.initFileDropHandling();
+
+ this.calculateData();
+
+ this.addSubscription = timer(0, 7500).subscribe(async () => {
+ await this._appStateService.reloadActiveDossierFilesIfNecessary();
+ this._loadEntitiesFromState();
+ });
+
+ this.addSubscription = this._appStateService.fileChanged$.subscribe(() => {
+ this.calculateData();
+ });
+
+ this.addSubscription = this._appStateService.dossierTemplateChanged$.subscribe(() => {
+ this.fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig(
+ this.currentDossier.dossierTemplateId
+ )?.fileAttributeConfigs;
+ });
+
+ this.addSubscription = this._tableComponent.scrollViewport.scrolledIndexChange
+ .pipe(tap(index => (this._lastScrolledIndex = index)))
+ .subscribe();
+
+ this.dossierAttributes = await this._dossierAttributesService.getValues(this.currentDossier);
+ } catch (e) {
+ console.log('Error from dossier overview screen: ', e);
+ } finally {
+ this._loadingService.stop();
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._fileDropOverlayService.cleanupFileDropHandling();
+ super.ngOnDestroy();
+ }
+
+ async ngOnAttach() {
+ await this._appStateService.reloadActiveDossierFiles();
+ this._loadEntitiesFromState();
+ await this.ngOnInit();
+ this._tableComponent.scrollViewport.scrollToIndex(this._lastScrolledIndex, 'smooth');
+ }
+
+ ngOnDetach() {
+ this.ngOnDestroy();
+ }
+
+ async reanalyseDossier() {
+ try {
+ await this._appStateService.reanalyzeDossier();
+ await this.reloadDossiers();
+ this._toaster.success(_('dossier-overview.reanalyse-dossier.success'));
+ } catch (e) {
+ this._toaster.error(_('dossier-overview.reanalyse-dossier.error'));
+ }
+ }
+
+ async reloadDossiers() {
+ await this._appStateService.getFiles(this.currentDossier, false);
+ this.calculateData();
+ }
+
+ calculateData(): void {
+ if (!this._appStateService.activeDossierId) {
+ return;
+ }
+
+ this._loadEntitiesFromState();
+ this._computeAllFilters();
+
+ this._dossierDetailsComponent?.calculateChartConfig();
+ this._changeDetectorRef.detectChanges();
+ }
+
+ @HostListener('drop', ['$event'])
+ onDrop(event: DragEvent): void {
+ handleFileDrop(event, this.currentDossier, this._uploadFiles.bind(this));
+ }
+
+ @HostListener('dragover', ['$event'])
+ onDragOver(event): void {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+
+ async uploadFiles(files: Files): Promise {
+ await this._uploadFiles(convertFiles(files, this.currentDossier));
+ this._fileInput.nativeElement.value = null;
+ }
+
+ async bulkActionPerformed(): Promise {
+ this.entitiesService.setSelected([]);
+ await this.reloadDossiers();
+ }
+
+ openAssignDossierMembersDialog(): void {
+ const data = { dossier: this.currentDossier, section: 'members' };
+ this._dialogService.openDialog('editDossier', null, data, async () => await this.reloadDossiers());
+ }
+
+ openDossierDictionaryDialog() {
+ const data = { dossier: this.currentDossier, section: 'dossierDictionary' };
+ this._dialogService.openDialog('editDossier', null, data, async () => {
+ await this.reloadDossiers();
+ });
+ }
+
+ toggleCollapsedDetails() {
+ this.collapsedDetails = !this.collapsedDetails;
+ }
+
+ recentlyModifiedChecker = (file: File) =>
+ moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
+
+ private _loadEntitiesFromState() {
+ this.currentDossier = this._appStateService.activeDossier;
+ if (this.currentDossier) {
+ this.entitiesService.setEntities(this.currentDossier.files);
+ }
+ }
+
+ private async _uploadFiles(files: FileUploadModel[]) {
+ const fileCount = await this._fileUploadService.uploadFiles(files);
+ if (fileCount) {
+ this._statusOverlayService.openUploadStatusOverlay();
+ }
+ }
+
+ private _computeAllFilters() {
+ if (!this.currentDossier) {
+ return;
+ }
+
+ const filterGroups = this._configService.filterGroups(
+ this.entitiesService.all,
+ this.fileAttributeConfigs,
+ this._needsWorkFilterTemplate,
+ () => this.checkedRequiredFilters,
+ () => this.checkedNotRequiredFilters
+ );
+
+ for (const filterGroup of filterGroups) {
+ this.filterService.addFilterGroup(filterGroup);
+ }
+ }
+}
diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.html
rename to apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html
diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.scss
rename to apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts
similarity index 84%
rename from apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.ts
rename to apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts
index 9493e4fee..5b9c6c153 100644
--- a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-actions/dossier-listing-actions.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts
@@ -1,19 +1,19 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
-import { Dossier } from '../../../../state/model/dossier';
import { StatusSorter } from '@utils/sorters/status-sorter';
-import { AppStateService } from '@state/app-state.service';
-import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { CircleButtonTypes, StatusBarConfig } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
+import { AppStateService } from '@state/app-state.service';
+import { Dossier } from '@state/model/dossier';
+import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
@Component({
- selector: 'redaction-dossier-listing-actions',
- templateUrl: './dossier-listing-actions.component.html',
- styleUrls: ['./dossier-listing-actions.component.scss'],
+ selector: 'redaction-dossiers-listing-actions',
+ templateUrl: './dossiers-listing-actions.component.html',
+ styleUrls: ['./dossiers-listing-actions.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class DossierListingActionsComponent {
+export class DossiersListingActionsComponent {
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
@@ -27,20 +27,6 @@ export class DossierListingActionsComponent {
private readonly _userService: UserService
) {}
- openEditDossierDialog($event: MouseEvent, dossier: Dossier): void {
- this._dialogService.openDialog('editDossier', $event, {
- dossier,
- afterSave: () => this.actionPerformed.emit()
- });
- }
-
- reanalyseDossier($event: MouseEvent, dossier: Dossier): void {
- $event.stopPropagation();
- this.appStateService.reanalyzeDossier(dossier).then(() => {
- this.appStateService.loadAllDossiers().then(() => this.actionPerformed.emit());
- });
- }
-
get statusConfig(): readonly StatusBarConfig[] {
if (!this.dossier) {
return [];
@@ -60,4 +46,18 @@ export class DossierListingActionsComponent {
.sort(StatusSorter.byStatus)
.map(status => ({ length: obj[status], color: status }));
}
+
+ openEditDossierDialog($event: MouseEvent, dossier: Dossier): void {
+ this._dialogService.openDialog('editDossier', $event, {
+ dossier,
+ afterSave: () => this.actionPerformed.emit()
+ });
+ }
+
+ reanalyseDossier($event: MouseEvent, dossier: Dossier): void {
+ $event.stopPropagation();
+ this.appStateService.reanalyzeDossier(dossier).then(() => {
+ this.appStateService.loadAllDossiers().then(() => this.actionPerformed.emit());
+ });
+ }
}
diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.html
rename to apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html
diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.scss
rename to apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts
similarity index 72%
rename from apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.ts
rename to apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts
index 3c94dc235..dfaf1c60a 100644
--- a/apps/red-ui/src/app/modules/dossier/components/dossier-listing-details/dossier-listing-details.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts
@@ -4,12 +4,12 @@ import { AppStateService } from '@state/app-state.service';
import { FilterService } from '@iqser/common-ui';
@Component({
- selector: 'redaction-dossier-listing-details',
- templateUrl: './dossier-listing-details.component.html',
- styleUrls: ['./dossier-listing-details.component.scss'],
+ selector: 'redaction-dossiers-listing-details',
+ templateUrl: './dossiers-listing-details.component.html',
+ styleUrls: ['./dossiers-listing-details.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class DossierListingDetailsComponent {
+export class DossiersListingDetailsComponent {
@Input() dossiersChartData: DoughnutChartConfig[];
@Input() documentsChartData: DoughnutChartConfig[];
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.html
new file mode 100644
index 000000000..30b70e884
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.html
@@ -0,0 +1,31 @@
+
+ {{ dossier.dossierName }}
+
+
+
+
+ {{ getDossierTemplateNameFor(dossier.dossierTemplateId) }}
+
+
+
+
+
+ {{ dossier.filesLength }}
+
+
+
+ {{ dossier.totalNumberOfPages }}
+
+
+
+ {{ dossier.memberIds.length }}
+
+
+
+ {{ dossier.date | date: 'mediumDate' }}
+
+
+
+ {{ dossier.dueDate | date: 'mediumDate' }}
+
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
new file mode 100644
index 000000000..2ba4cc448
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts
@@ -0,0 +1,18 @@
+import { Component, Input } from '@angular/core';
+import { Dossier } from '@state/model/dossier';
+import { AppStateService } from '@state/app-state.service';
+
+@Component({
+ selector: 'redaction-dossiers-listing-dossier-name',
+ templateUrl: './dossiers-listing-dossier-name.component.html',
+ styleUrls: ['./dossiers-listing-dossier-name.component.scss']
+})
+export class DossiersListingDossierNameComponent {
+ @Input() dossier: Dossier;
+
+ constructor(private readonly _appStateService: AppStateService) {}
+
+ getDossierTemplateNameFor(dossierTemplateId: string): string {
+ return this._appStateService.getDossierTemplateById(dossierTemplateId).name;
+ }
+}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html
new file mode 100644
index 000000000..352e0354e
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.scss
new file mode 100644
index 000000000..ed7edfb31
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.scss
@@ -0,0 +1,3 @@
+.status-container {
+ width: 160px;
+}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts
new file mode 100644
index 000000000..7c2e63ce1
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts
@@ -0,0 +1,15 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { Dossier } from '@state/model/dossier';
+import { Required } from '@iqser/common-ui';
+
+@Component({
+ selector: 'redaction-table-item',
+ templateUrl: './table-item.component.html',
+ styleUrls: ['./table-item.component.scss']
+})
+export class TableItemComponent {
+ @Input() @Required() dossier!: Dossier;
+ @Output() readonly calculateData = new EventEmitter();
+
+ constructor() {}
+}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts
new file mode 100644
index 000000000..aa8395e22
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts
@@ -0,0 +1,213 @@
+import { Injectable, TemplateRef } from '@angular/core';
+import { IFilterGroup, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
+import { Dossier } from '@state/model/dossier';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import { TranslateService } from '@ngx-translate/core';
+import { UserPreferenceService } from '@services/user-preference.service';
+import { UserService } from '@services/user.service';
+import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
+import { User } from '@models/user';
+import { fileStatusTranslations } from '../../translations/file-status-translations';
+import {
+ annotationFilterChecker,
+ dossierMemberChecker,
+ dossierStatusChecker,
+ dossierTemplateChecker,
+ RedactionFilterSorter,
+ StatusSorter
+} from '@utils/index';
+import { workloadTranslations } from '../../translations/workload-translations';
+import { AppStateService } from '@state/app-state.service';
+
+@Injectable()
+export class ConfigService {
+ constructor(
+ private readonly _translateService: TranslateService,
+ private readonly _userPreferenceService: UserPreferenceService,
+ private readonly _userService: UserService,
+ private readonly _appStateService: AppStateService
+ ) {}
+
+ get tableConfig(): TableColumnConfig[] {
+ return [
+ { label: _('dossier-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' },
+ { label: _('dossier-listing.table-col-names.needs-work') },
+ { label: _('dossier-listing.table-col-names.owner'), class: 'user-column' },
+ { label: _('dossier-listing.table-col-names.status'), class: 'flex-end', width: 'auto' }
+ ];
+ }
+
+ get _currentUser(): User {
+ return this._userService.currentUser;
+ }
+
+ private get _quickFilters(): NestedFilter[] {
+ const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers');
+ const filters = [
+ {
+ id: 'my-dossiers',
+ label: myDossiersLabel,
+ checker: (dw: Dossier) => {
+ console.log(dw.ownerId, this._currentUser.id);
+ return dw.ownerId === this._currentUser.id;
+ }
+ },
+ {
+ id: 'to-approve',
+ label: this._translateService.instant('dossier-listing.quick-filters.to-approve'),
+ checker: (dw: Dossier) => dw.approverIds.includes(this._currentUser.id)
+ },
+ {
+ id: 'to-review',
+ label: this._translateService.instant('dossier-listing.quick-filters.to-review'),
+ checker: (dw: Dossier) => dw.memberIds.includes(this._currentUser.id)
+ },
+ {
+ id: 'other',
+ label: this._translateService.instant('dossier-listing.quick-filters.other'),
+ checker: (dw: Dossier) => !dw.memberIds.includes(this._currentUser.id)
+ }
+ ].map(filter => new NestedFilter(filter));
+
+ return filters.filter(f => f.label === myDossiersLabel || this._userPreferenceService.areDevFeaturesEnabled);
+ }
+
+ buttonsConfig(addDossier: () => void): ButtonConfig[] {
+ return [
+ {
+ label: _('dossier-listing.add-new'),
+ action: addDossier,
+ hide: !this._currentUser.isManager,
+ icon: 'red:plus',
+ type: 'primary'
+ }
+ ];
+ }
+
+ filterGroups(entities: Dossier[], needsWorkFilterTemplate: TemplateRef) {
+ const allDistinctFileStatus = new Set();
+ const allDistinctPeople = new Set();
+ const allDistinctNeedsWork = new Set();
+ const allDistinctDossierTemplates = new Set();
+
+ const filterGroups: IFilterGroup[] = [];
+
+ entities?.forEach(entry => {
+ // all people
+ entry.memberIds.forEach(f => allDistinctPeople.add(f));
+ // Needs work
+ entry.files.forEach(file => {
+ allDistinctFileStatus.add(file.status);
+ if (file.analysisRequired) {
+ allDistinctNeedsWork.add('analysis');
+ }
+ if (entry.hintsOnly) {
+ allDistinctNeedsWork.add('hint');
+ }
+ if (entry.hasRedactions) {
+ allDistinctNeedsWork.add('redaction');
+ }
+ if (entry.hasSuggestions) {
+ allDistinctNeedsWork.add('suggestion');
+ }
+ if (entry.hasNone) {
+ allDistinctNeedsWork.add('none');
+ }
+ });
+
+ allDistinctDossierTemplates.add(entry.dossierTemplateId);
+ });
+
+ const statusFilters = [...allDistinctFileStatus].map(
+ status =>
+ new NestedFilter({
+ id: status,
+ label: this._translateService.instant(fileStatusTranslations[status])
+ })
+ );
+
+ filterGroups.push({
+ slug: 'statusFilters',
+ label: this._translateService.instant('filters.status'),
+ icon: 'red:status',
+ filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
+ checker: dossierStatusChecker
+ });
+
+ const peopleFilters = [...allDistinctPeople].map(
+ userId =>
+ new NestedFilter({
+ id: userId,
+ label: this._userService.getNameForId(userId)
+ })
+ );
+
+ filterGroups.push({
+ slug: 'peopleFilters',
+ label: this._translateService.instant('filters.people'),
+ icon: 'red:user',
+ filters: peopleFilters,
+ checker: dossierMemberChecker
+ });
+
+ const needsWorkFilters = [...allDistinctNeedsWork].map(
+ type =>
+ new NestedFilter({
+ id: type,
+ label: workloadTranslations[type]
+ })
+ );
+
+ filterGroups.push({
+ slug: 'needsWorkFilters',
+ label: this._translateService.instant('filters.needs-work'),
+ icon: 'red:needs-work',
+ filterTemplate: needsWorkFilterTemplate,
+ filters: needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id]),
+ checker: annotationFilterChecker,
+ matchAll: true
+ });
+
+ const dossierTemplateFilters = [...allDistinctDossierTemplates].map(
+ id =>
+ new NestedFilter({
+ id: id,
+ label: this._appStateService.getDossierTemplateById(id).name
+ })
+ );
+
+ filterGroups.push({
+ slug: 'dossierTemplateFilters',
+ label: this._translateService.instant('filters.dossier-templates'),
+ icon: 'red:template',
+ hide: dossierTemplateFilters.length <= 1,
+ filters: dossierTemplateFilters,
+ checker: dossierTemplateChecker
+ });
+
+ const quickFilters = this._quickFilters;
+ filterGroups.push({
+ slug: 'quickFilters',
+ filters: quickFilters,
+ checker: (dw: Dossier) => quickFilters.reduce((acc, f) => acc || (f.checked && f.checker(dw)), false)
+ });
+
+ const dossierFilters = entities.map(
+ dossier =>
+ new NestedFilter({
+ id: dossier.dossierName,
+ label: dossier.dossierName
+ })
+ );
+ filterGroups.push({
+ slug: 'dossierNameFilter',
+ label: this._translateService.instant('dossier-listing.filters.label'),
+ icon: 'red:folder',
+ filters: dossierFilters,
+ filterceptionPlaceholder: this._translateService.instant('dossier-listing.filters.search'),
+ checker: keyChecker('dossierName')
+ });
+
+ return filterGroups;
+ }
+}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts
new file mode 100644
index 000000000..0b6b2aa21
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts
@@ -0,0 +1,34 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { IqserIconsModule } from '@iqser/common-ui';
+import { TranslateModule } from '@ngx-translate/core';
+import { DossiersListingScreenComponent } from './screen/dossiers-listing-screen.component';
+import { RouterModule } from '@angular/router';
+import { DossiersListingActionsComponent } from './components/dossiers-listing-actions/dossiers-listing-actions.component';
+import { SharedModule } from '@shared/shared.module';
+import { DossiersListingDetailsComponent } from './components/dossiers-listing-details/dossiers-listing-details.component';
+import { DossiersListingDossierNameComponent } from './components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component';
+import { ConfigService } from './config.service';
+import { TableItemComponent } from './components/table-item/table-item.component';
+import { SharedDossiersModule } from '../../shared/shared-dossiers.module';
+
+const routes = [
+ {
+ path: '',
+ component: DossiersListingScreenComponent,
+ pathMatch: 'full'
+ }
+];
+
+@NgModule({
+ declarations: [
+ DossiersListingScreenComponent,
+ DossiersListingActionsComponent,
+ DossiersListingDetailsComponent,
+ DossiersListingDossierNameComponent,
+ TableItemComponent
+ ],
+ providers: [ConfigService],
+ imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule]
+})
+export class DossiersListingModule {}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.html
new file mode 100644
index 000000000..82ee8a3a1
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.scss
new file mode 100644
index 000000000..c3c3a92e5
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.scss
@@ -0,0 +1,14 @@
+.right-container {
+ display: flex;
+ width: 466px;
+ min-width: 466px;
+ padding-right: 11px;
+
+ &.has-scrollbar:hover {
+ padding-right: 0;
+ }
+
+ redaction-dossiers-listing-details {
+ min-width: 466px;
+ }
+}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts
new file mode 100644
index 000000000..d48cebecf
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts
@@ -0,0 +1,141 @@
+import { AfterViewInit, Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
+import { DossierStatuses } from '@redaction/red-ui-http';
+import { AppStateService } from '@state/app-state.service';
+import { Dossier } from '@state/model/dossier';
+import { UserService } from '@services/user.service';
+import { PermissionsService } from '@services/permissions.service';
+import { TranslateChartService } from '@services/translate-chart.service';
+import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
+import { timer } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { Router } from '@angular/router';
+import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
+import { groupBy, OnAttach, OnDetach, StatusSorter } from '@utils/index';
+import { DefaultListingServices, ListingComponent, TableComponent } from '@iqser/common-ui';
+import { fileStatusTranslations } from '../../../translations/file-status-translations';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import { ConfigService } from '../config.service';
+
+@Component({
+ templateUrl: './dossiers-listing-screen.component.html',
+ styleUrls: ['./dossiers-listing-screen.component.scss'],
+ providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossiersListingScreenComponent) }]
+})
+export class DossiersListingScreenComponent
+ extends ListingComponent
+ implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach
+{
+ readonly currentUser = this._userService.currentUser;
+ readonly tableColumnConfigs = this._configService.tableConfig;
+ readonly tableHeaderLabel = _('dossier-listing.table-header.title');
+ readonly buttonConfigs = this._configService.buttonsConfig(() => this.openAddDossierDialog());
+ dossiersChartData: DoughnutChartConfig[] = [];
+ documentsChartData: DoughnutChartConfig[] = [];
+ private _lastScrolledIndex: number;
+ @ViewChild('needsWorkFilterTemplate', {
+ read: TemplateRef,
+ static: true
+ })
+ private readonly _needsWorkFilterTemplate: TemplateRef;
+ @ViewChild(TableComponent) private readonly _tableComponent: TableComponent;
+
+ constructor(
+ private readonly _router: Router,
+ protected readonly _injector: Injector,
+ private readonly _userService: UserService,
+ readonly permissionsService: PermissionsService,
+ private readonly _appStateService: AppStateService,
+ private readonly _dialogService: DossiersDialogService,
+ private readonly _translateChartService: TranslateChartService,
+ private readonly _configService: ConfigService
+ ) {
+ super(_injector);
+ this._appStateService.reset();
+ this._loadEntitiesFromState();
+ }
+
+ private get _activeDossiersCount(): number {
+ return this.entitiesService.all.filter(p => p.status === DossierStatuses.ACTIVE).length;
+ }
+
+ private get _inactiveDossiersCount(): number {
+ return this.entitiesService.all.length - this._activeDossiersCount;
+ }
+
+ ngOnInit(): void {
+ this.calculateData();
+
+ this.addSubscription = timer(0, 10000).subscribe(async () => {
+ await this._appStateService.loadAllDossiers();
+ this._loadEntitiesFromState();
+ this.calculateData();
+ });
+
+ this.addSubscription = this._appStateService.fileChanged$.subscribe(() => {
+ this.calculateData();
+ });
+ }
+
+ ngAfterViewInit(): void {
+ this.addSubscription = this._tableComponent.scrollViewport.scrolledIndexChange
+ .pipe(tap(index => (this._lastScrolledIndex = index)))
+ .subscribe();
+ }
+
+ ngOnAttach(): void {
+ this._appStateService.reset();
+ this._loadEntitiesFromState();
+ this.ngOnInit();
+ this.ngAfterViewInit();
+ this._tableComponent.scrollViewport.scrollToIndex(this._lastScrolledIndex, 'smooth');
+ }
+
+ ngOnDetach(): void {
+ this.ngOnDestroy();
+ }
+
+ openAddDossierDialog(): void {
+ this._dialogService.openDialog('addDossier', null, null, async addResponse => {
+ await this._router.navigate([`/main/dossiers/${addResponse.dossier.id}`]);
+ if (addResponse.addMembers) {
+ this._dialogService.openDialog('editDossier', null, {
+ dossier: addResponse.dossier,
+ section: 'members'
+ });
+ }
+ });
+ }
+
+ calculateData(): void {
+ this._computeAllFilters();
+
+ this.dossiersChartData = [
+ { value: this._activeDossiersCount, color: 'ACTIVE', label: _('active') },
+ { value: this._inactiveDossiersCount, color: 'DELETED', label: _('archived') }
+ ];
+ const groups = groupBy(this._appStateService.aggregatedFiles, 'status');
+ this.documentsChartData = [];
+
+ for (const status of Object.keys(groups)) {
+ this.documentsChartData.push({
+ value: groups[status].length,
+ color: status,
+ label: fileStatusTranslations[status],
+ key: status
+ });
+ }
+ this.documentsChartData.sort(StatusSorter.byStatus);
+ this.documentsChartData = this._translateChartService.translateStatus(this.documentsChartData);
+ }
+
+ private _loadEntitiesFromState() {
+ this.entitiesService.setEntities(this._appStateService.allDossiers);
+ }
+
+ private _computeAllFilters() {
+ const filterGroups = this._configService.filterGroups(this.entitiesService.all, this._needsWorkFilterTemplate);
+ for (const filterGroup of filterGroups) {
+ this.filterService.addFilterGroup(filterGroup);
+ }
+ }
+}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
index 39ee44aa1..e4dd75f46 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
@@ -18,7 +18,7 @@ import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
-import { FileActionService } from '../../services/file-action.service';
+import { FileActionService } from '../../shared/services/file-action.service';
import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { File } from '@models/file/file';
@@ -38,7 +38,7 @@ import { TranslateService } from '@ngx-translate/core';
import { fileStatusTranslations } from '../../translations/file-status-translations';
import { handleFilterDelta } from '@utils/filter-utils';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
-import { FileActionsComponent } from '../../components/file-actions/file-actions.component';
+import { FileActionsComponent } from '../../shared/components/file-actions/file-actions.component';
import { User } from '@models/user';
import { FilesService } from '../../services/files.service';
import Annotation = Core.Annotations.Annotation;
diff --git a/apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.html b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.html
rename to apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html
diff --git a/apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.scss b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.scss
rename to apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts
similarity index 99%
rename from apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.ts
rename to apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts
index 01c179aa4..1231ca8c9 100644
--- a/apps/red-ui/src/app/modules/dossier/components/file-actions/file-actions.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts
@@ -2,8 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, S
import { PermissionsService } from '@services/permissions.service';
import { File } from '@models/file/file';
import { AppStateService } from '@state/app-state.service';
-import { FileActionService } from '../../services/file-action.service';
-import { DossiersDialogService } from '../../services/dossiers-dialog.service';
+import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes, LoadingService, Required, StatusBarConfig, Toaster } from '@iqser/common-ui';
import { FileManagementControllerService, FileStatus } from '@redaction/red-ui-http';
@@ -12,6 +11,7 @@ import { UserService } from '@services/user.service';
import { filter } from 'rxjs/operators';
import { UserPreferenceService } from '@services/user-preference.service';
import { LongPressEvent } from '@shared/directives/long-press.directive';
+import { FileActionService } from '../../services/file-action.service';
@Component({
selector: 'redaction-file-actions',
diff --git a/apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.html b/apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.html
rename to apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.html
diff --git a/apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.scss b/apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.scss
rename to apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.ts
similarity index 96%
rename from apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.ts
rename to apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.ts
index b04bb5e76..27040f512 100644
--- a/apps/red-ui/src/app/modules/dossier/components/needs-work-badge/needs-work-badge.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/components/needs-work-badge/needs-work-badge.component.ts
@@ -1,7 +1,7 @@
import { Component, Input } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { File } from '@models/file/file';
-import { Dossier } from '../../../../state/model/dossier';
+import { Dossier } from '@state/model/dossier';
@Component({
selector: 'redaction-needs-work-badge',
diff --git a/apps/red-ui/src/app/modules/dossier/services/file-action.service.ts b/apps/red-ui/src/app/modules/dossier/shared/services/file-action.service.ts
similarity index 96%
rename from apps/red-ui/src/app/modules/dossier/services/file-action.service.ts
rename to apps/red-ui/src/app/modules/dossier/shared/services/file-action.service.ts
index c32aa4e20..c6fc526b0 100644
--- a/apps/red-ui/src/app/modules/dossier/services/file-action.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/services/file-action.service.ts
@@ -4,10 +4,10 @@ import { UserService } from '@services/user.service';
import { ReanalysisControllerService } from '@redaction/red-ui-http';
import { File } from '@models/file/file';
import { PermissionsService } from '@services/permissions.service';
-import { DossiersDialogService } from './dossiers-dialog.service';
+import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
-import { FilesService } from './files.service';
+import { FilesService } from '../../services/files.service';
@Injectable()
export class FileActionService {
diff --git a/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts b/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts
new file mode 100644
index 000000000..b1c4b967e
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FileActionService } from './services/file-action.service';
+import { FileActionsComponent } from './components/file-actions/file-actions.component';
+import { NeedsWorkBadgeComponent } from './components/needs-work-badge/needs-work-badge.component';
+import { IqserIconsModule } from '@iqser/common-ui';
+import { SharedModule } from '@shared/shared.module';
+
+const components = [FileActionsComponent, NeedsWorkBadgeComponent];
+
+@NgModule({
+ declarations: [...components],
+ exports: [...components],
+ providers: [FileActionService],
+ imports: [CommonModule, IqserIconsModule, SharedModule]
+})
+export class SharedDossiersModule {}
diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members/team-members.component.html b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/team-members/team-members.component.html
rename to apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.html
diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members/team-members.component.scss b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/team-members/team-members.component.scss
rename to apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members/team-members.component.ts b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.ts
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/team-members/team-members.component.ts
rename to apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.ts
diff --git a/apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.html b/apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.html
rename to apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.html
diff --git a/apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.scss b/apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.scss
rename to apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.scss
diff --git a/apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.ts b/apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.ts
similarity index 94%
rename from apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.ts
rename to apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.ts
index 9d2866f9d..39c71c592 100644
--- a/apps/red-ui/src/app/modules/dossier/components/type-filter/type-filter.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/type-filter/type-filter.component.ts
@@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core';
-import { AppStateService } from '@state/app-state.service';
+import { AppStateService } from '../../../../state/app-state.service';
import { INestedFilter } from '@iqser/common-ui';
@Component({
diff --git a/apps/red-ui/src/app/modules/shared/shared.module.ts b/apps/red-ui/src/app/modules/shared/shared.module.ts
index 92d75e3b7..6545ecbbb 100644
--- a/apps/red-ui/src/app/modules/shared/shared.module.ts
+++ b/apps/red-ui/src/app/modules/shared/shared.module.ts
@@ -19,13 +19,15 @@ import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { SelectComponent } from './components/select/select.component';
import { NavigateLastDossiersScreenDirective } from './directives/navigate-last-dossiers-screen.directive';
import { DictionaryManagerComponent } from './components/dictionary-manager/dictionary-manager.component';
-import { SideNavComponent } from '@shared/components/side-nav/side-nav.component';
+import { SideNavComponent } from './components/side-nav/side-nav.component';
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { AssignUserDropdownComponent } from './components/assign-user-dropdown/assign-user-dropdown.component';
import { PageHeaderComponent } from './components/page-header/page-header.component';
-import { DatePipe } from '@shared/pipes/date.pipe';
-import { LongPressDirective } from '@shared/directives/long-press.directive';
-import { NamePipe } from '@shared/pipes/name.pipe';
+import { DatePipe } from './pipes/date.pipe';
+import { LongPressDirective } from './directives/long-press.directive';
+import { NamePipe } from './pipes/name.pipe';
+import { TypeFilterComponent } from './components/type-filter/type-filter.component';
+import { TeamMembersComponent } from './components/team-members/team-members.component';
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
@@ -42,6 +44,8 @@ const components = [
DictionaryManagerComponent,
AssignUserDropdownComponent,
PageHeaderComponent,
+ TypeFilterComponent,
+ TeamMembersComponent,
...buttons
];
diff --git a/apps/red-ui/src/app/utils/index.ts b/apps/red-ui/src/app/utils/index.ts
new file mode 100644
index 000000000..15c2674bb
--- /dev/null
+++ b/apps/red-ui/src/app/utils/index.ts
@@ -0,0 +1,17 @@
+export * from './sorters/redaction-filter-sorter';
+export * from './sorters/status-sorter';
+export * from './sorters/super-type-sorter';
+
+export * from './api-path-interceptor';
+export * from './configuration.initializer';
+export * from './custom-route-reuse.strategy';
+export * from './date-inputs-utils';
+export * from './file-download-utils';
+export * from './file-drop-utils';
+export * from './filter-utils';
+export * from './functions';
+export * from './global-error-handler.service';
+export * from './missing-translations-handler';
+export * from './page-stamper';
+export * from './pdf-coordinates';
+export * from './pruning-translation-loader';
diff --git a/libs/common-ui b/libs/common-ui
index 54eb8173c..85d22fbcc 160000
--- a/libs/common-ui
+++ b/libs/common-ui
@@ -1 +1 @@
-Subproject commit 54eb8173cf7ce35f29f4e9df309954bbd1cf7a5c
+Subproject commit 85d22fbcc9b3673d11dbf542e3b6ee16c7791d84