From ef5e5ded18652f398aafdfab2f11a547d2d8b2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Thu, 30 Sep 2021 02:00:47 +0300 Subject: [PATCH 1/4] Refactor dossier listing & overview --- .../team-members-manager.component.ts | 16 +- ...sign-reviewer-approver-dialog.component.ts | 1 - .../dossier/dossiers-routing.module.ts | 10 +- .../app/modules/dossier/dossiers.module.ts | 27 +- .../dossier-listing-screen.component.html | 86 --- .../dossier-listing-screen.component.scss | 22 - .../dossier-listing-screen.component.ts | 336 ---------- .../dossier-overview-screen.component.ts | 601 ------------------ ...ssier-overview-bulk-actions.component.html | 0 ...ssier-overview-bulk-actions.component.scss | 0 ...dossier-overview-bulk-actions.component.ts | 12 +- .../dossier-details-stats.component.html | 0 .../dossier-details-stats.component.scss | 0 .../dossier-details-stats.component.ts | 2 +- .../dossier-details.component.html | 0 .../dossier-details.component.scss | 0 .../dossier-details.component.ts | 5 +- .../table-item/table-item.component.html | 70 ++ .../table-item/table-item.component.scss | 26 + .../table-item/table-item.component.ts | 19 + .../dossier-overview/config.service.ts | 364 +++++++++++ .../dossier-overview.module.ts | 34 + .../dossier-overview-screen.component.html | 98 +-- .../dossier-overview-screen.component.scss | 30 +- .../dossier-overview-screen.component.ts | 280 ++++++++ .../dossiers-listing-actions.component.html} | 0 .../dossiers-listing-actions.component.scss} | 0 .../dossiers-listing-actions.component.ts} | 42 +- .../dossiers-listing-details.component.html} | 0 .../dossiers-listing-details.component.scss} | 0 .../dossiers-listing-details.component.ts} | 8 +- ...ssiers-listing-dossier-name.component.html | 31 + ...ssiers-listing-dossier-name.component.scss | 0 ...dossiers-listing-dossier-name.component.ts | 18 + .../table-item/table-item.component.html | 12 + .../table-item/table-item.component.scss | 3 + .../table-item/table-item.component.ts | 15 + .../dossiers-listing/config.service.ts | 213 +++++++ .../dossiers-listing.module.ts | 34 + .../dossiers-listing-screen.component.html | 36 ++ .../dossiers-listing-screen.component.scss | 14 + .../dossiers-listing-screen.component.ts | 141 ++++ .../file-preview-screen.component.ts | 4 +- .../file-actions/file-actions.component.html | 0 .../file-actions/file-actions.component.scss | 0 .../file-actions/file-actions.component.ts | 4 +- .../needs-work-badge.component.html | 0 .../needs-work-badge.component.scss | 0 .../needs-work-badge.component.ts | 2 +- .../services/file-action.service.ts | 4 +- .../dossier/shared/shared-dossiers.module.ts | 17 + .../team-members/team-members.component.html | 0 .../team-members/team-members.component.scss | 0 .../team-members/team-members.component.ts | 0 .../type-filter/type-filter.component.html | 0 .../type-filter/type-filter.component.scss | 0 .../type-filter/type-filter.component.ts | 2 +- .../src/app/modules/shared/shared.module.ts | 12 +- apps/red-ui/src/app/utils/index.ts | 17 + libs/common-ui | 2 +- 60 files changed, 1423 insertions(+), 1247 deletions(-) delete mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.html delete mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.scss delete mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts delete mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.ts rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/bulk-actions/dossier-overview-bulk-actions.component.html (100%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/bulk-actions/dossier-overview-bulk-actions.component.scss (100%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/bulk-actions/dossier-overview-bulk-actions.component.ts (94%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/dossier-details-stats/dossier-details-stats.component.html (100%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/dossier-details-stats/dossier-details-stats.component.scss (100%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/dossier-details-stats/dossier-details-stats.component.ts (93%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/dossier-details/dossier-details.component.html (100%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/dossier-details/dossier-details.component.scss (100%) rename apps/red-ui/src/app/modules/dossier/{ => screens/dossier-overview}/components/dossier-details/dossier-details.component.ts (95%) create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.scss create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.ts create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts rename apps/red-ui/src/app/modules/dossier/screens/{dossier-overview-screen => dossier-overview/screen}/dossier-overview-screen.component.html (66%) rename apps/red-ui/src/app/modules/dossier/screens/{dossier-overview-screen => dossier-overview/screen}/dossier-overview-screen.component.scss (77%) create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts rename apps/red-ui/src/app/modules/dossier/{components/dossier-listing-actions/dossier-listing-actions.component.html => screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html} (100%) rename apps/red-ui/src/app/modules/dossier/{components/dossier-listing-actions/dossier-listing-actions.component.scss => screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.scss} (100%) rename apps/red-ui/src/app/modules/dossier/{components/dossier-listing-actions/dossier-listing-actions.component.ts => screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts} (84%) rename apps/red-ui/src/app/modules/dossier/{components/dossier-listing-details/dossier-listing-details.component.html => screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html} (100%) rename apps/red-ui/src/app/modules/dossier/{components/dossier-listing-details/dossier-listing-details.component.scss => screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss} (100%) rename apps/red-ui/src/app/modules/dossier/{components/dossier-listing-details/dossier-listing-details.component.ts => screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.ts} (72%) create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.html create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.scss create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component.ts create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.scss create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.html create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.scss create mode 100644 apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts rename apps/red-ui/src/app/modules/dossier/{ => shared}/components/file-actions/file-actions.component.html (100%) rename apps/red-ui/src/app/modules/dossier/{ => shared}/components/file-actions/file-actions.component.scss (100%) rename apps/red-ui/src/app/modules/dossier/{ => shared}/components/file-actions/file-actions.component.ts (99%) rename apps/red-ui/src/app/modules/dossier/{ => shared}/components/needs-work-badge/needs-work-badge.component.html (100%) rename apps/red-ui/src/app/modules/dossier/{ => shared}/components/needs-work-badge/needs-work-badge.component.scss (100%) rename apps/red-ui/src/app/modules/dossier/{ => shared}/components/needs-work-badge/needs-work-badge.component.ts (96%) rename apps/red-ui/src/app/modules/dossier/{ => shared}/services/file-action.service.ts (96%) create mode 100644 apps/red-ui/src/app/modules/dossier/shared/shared-dossiers.module.ts rename apps/red-ui/src/app/modules/{dossier => shared}/components/team-members/team-members.component.html (100%) rename apps/red-ui/src/app/modules/{dossier => shared}/components/team-members/team-members.component.scss (100%) rename apps/red-ui/src/app/modules/{dossier => shared}/components/team-members/team-members.component.ts (100%) rename apps/red-ui/src/app/modules/{dossier => shared}/components/type-filter/type-filter.component.html (100%) rename apps/red-ui/src/app/modules/{dossier => shared}/components/type-filter/type-filter.component.scss (100%) rename apps/red-ui/src/app/modules/{dossier => shared}/components/type-filter/type-filter.component.ts (94%) create mode 100644 apps/red-ui/src/app/utils/index.ts diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts index be0aef561..be438a273 100644 --- a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts @@ -4,7 +4,7 @@ import { AppStateService } from '@state/app-state.service'; import { UserService } from '@services/user.service'; import { Toaster } from '@iqser/common-ui'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Dossier } from '../../../../state/model/dossier'; +import { Dossier } from '@state/model/dossier'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @Component({ @@ -119,6 +119,13 @@ export class TeamMembersManagerComponent implements OnInit { this._loadData(); } + setMembersSelectOptions(): void { + this.membersSelectOptions = this.userService.eligibleUsers + .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(this.searchQuery.toLowerCase())) + .filter(user => this.selectedOwnerId !== user.id) + .map(user => user.id); + } + private _updateChanged() { if (this.dossier.ownerId !== this.selectedOwnerId) { this.changed = true; @@ -138,13 +145,6 @@ export class TeamMembersManagerComponent implements OnInit { this.selectedReviewersList = this.selectedMembersList.filter(m => this.selectedApproversList.indexOf(m) === -1); } - setMembersSelectOptions(): void { - this.membersSelectOptions = this.userService.eligibleUsers - .filter(user => this.userService.getNameForId(user.id).toLowerCase().includes(this.searchQuery.toLowerCase())) - .filter(user => this.selectedOwnerId !== user.id) - .map(user => user.id); - } - private _loadData() { this.teamForm = this._formBuilder.group({ owner: [this.dossier?.ownerId, Validators.required], diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts index 7bbbd4ff5..3a49970ba 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts @@ -69,7 +69,6 @@ export class AssignReviewerApproverDialogComponent { const selectedUser = this.selectedSingleUser; if (this.data.mode === 'reviewer') { - console.log('assign reviewer'); await this._filesService .setReviewerFor( this.data.files.map(f => f.fileId), diff --git a/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts b/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts index b695f17eb..33a456ffe 100644 --- a/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts +++ b/apps/red-ui/src/app/modules/dossier/dossiers-routing.module.ts @@ -1,13 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { DossierListingScreenComponent } from './screens/dossier-listing-screen/dossier-listing-screen.component'; import { CompositeRouteGuard } from '@guards/composite-route.guard'; import { AuthGuard } from '../auth/auth.guard'; import { RedRoleGuard } from '../auth/red-role.guard'; import { AppStateGuard } from '@state/app-state.guard'; import { SearchScreenComponent } from './screens/search-screen/search-screen.component'; import { FilePreviewScreenComponent } from './screens/file-preview-screen/file-preview-screen.component'; -import { DossierOverviewScreenComponent } from './screens/dossier-overview-screen/dossier-overview-screen.component'; const routes: Routes = [ { @@ -20,12 +18,12 @@ const routes: Routes = [ }, { path: ':dossierId', - component: DossierOverviewScreenComponent, canActivate: [CompositeRouteGuard], data: { routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], reuse: true - } + }, + loadChildren: () => import('./screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule) }, { path: ':dossierId/file/:fileId', @@ -39,12 +37,12 @@ const routes: Routes = [ { path: '', pathMatch: 'full', - component: DossierListingScreenComponent, canActivate: [CompositeRouteGuard], data: { routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], reuse: true - } + }, + loadChildren: () => import('./screens/dossiers-listing/dossiers-listing.module').then(m => m.DossiersListingModule) } ]; diff --git a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts index 4daae1ee8..c4b617945 100644 --- a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts +++ b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts @@ -1,7 +1,5 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { DossierListingScreenComponent } from './screens/dossier-listing-screen/dossier-listing-screen.component'; -import { DossierOverviewScreenComponent } from './screens/dossier-overview-screen/dossier-overview-screen.component'; import { FilePreviewScreenComponent } from './screens/file-preview-screen/file-preview-screen.component'; import { AddDossierDialogComponent } from './dialogs/add-dossier-dialog/add-dossier-dialog.component'; import { AssignReviewerApproverDialogComponent } from './dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component'; @@ -11,17 +9,9 @@ import { RemoveAnnotationsDialogComponent } from './dialogs/remove-annotations-d import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component'; import { PdfViewerComponent } from './components/pdf-viewer/pdf-viewer.component'; import { CommentsComponent } from './components/comments/comments.component'; -import { DossierDetailsComponent } from './components/dossier-details/dossier-details.component'; import { PageIndicatorComponent } from './components/page-indicator/page-indicator.component'; -import { NeedsWorkBadgeComponent } from './components/needs-work-badge/needs-work-badge.component'; import { AnnotationActionsComponent } from './components/annotation-actions/annotation-actions.component'; -import { DossierListingDetailsComponent } from './components/dossier-listing-details/dossier-listing-details.component'; -import { FileActionsComponent } from './components/file-actions/file-actions.component'; import { TypeAnnotationIconComponent } from './components/type-annotation-icon/type-annotation-icon.component'; -import { TypeFilterComponent } from './components/type-filter/type-filter.component'; -import { DossierOverviewBulkActionsComponent } from './components/bulk-actions/dossier-overview-bulk-actions.component'; -import { TeamMembersComponent } from './components/team-members/team-members.component'; -import { DossierListingActionsComponent } from './components/dossier-listing-actions/dossier-listing-actions.component'; import { DocumentInfoComponent } from './components/document-info/document-info.component'; import { FileWorkloadComponent } from './components/file-workload/file-workload.component'; import { SharedModule } from '@shared/shared.module'; @@ -29,7 +19,6 @@ import { DossiersRoutingModule } from './dossiers-routing.module'; import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module'; import { DossiersDialogService } from './services/dossiers-dialog.service'; import { AnnotationActionsService } from './services/annotation-actions.service'; -import { FileActionService } from './services/file-action.service'; import { PdfViewerDataService } from './services/pdf-viewer-data.service'; import { ManualAnnotationService } from './services/manual-annotation.service'; import { AnnotationDrawService } from './services/annotation-draw.service'; @@ -46,14 +35,14 @@ import { PageExclusionComponent } from './components/page-exclusion/page-exclusi import { RecategorizeImageDialogComponent } from './dialogs/recategorize-image-dialog/recategorize-image-dialog.component'; import { EditDossierAttributesComponent } from './dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component'; import { DossiersService } from './services/dossiers.service'; -import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component'; import { SearchScreenComponent } from './screens/search-screen/search-screen.component'; import { EditDossierDeletedDocumentsComponent } from './dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component'; import { AnnotationsListComponent } from './components/file-workload/components/annotations-list/annotations-list.component'; import { AnnotationSourceComponent } from './components/file-workload/components/annotation-source/annotation-source.component'; import { OverlayModule } from '@angular/cdk/overlay'; +import { SharedDossiersModule } from './shared/shared-dossiers.module'; -const screens = [DossierListingScreenComponent, DossierOverviewScreenComponent, FilePreviewScreenComponent, SearchScreenComponent]; +const screens = [FilePreviewScreenComponent, SearchScreenComponent]; const dialogs = [ AddDossierDialogComponent, @@ -70,17 +59,9 @@ const dialogs = [ const components = [ PdfViewerComponent, CommentsComponent, - DossierDetailsComponent, PageIndicatorComponent, - NeedsWorkBadgeComponent, AnnotationActionsComponent, - DossierListingDetailsComponent, TypeAnnotationIconComponent, - TypeFilterComponent, - DossierOverviewBulkActionsComponent, - FileActionsComponent, - TeamMembersComponent, - DossierListingActionsComponent, DocumentInfoComponent, FileWorkloadComponent, EditDossierGeneralInfoComponent, @@ -90,7 +71,6 @@ const components = [ EditDossierAttributesComponent, TeamMembersManagerComponent, PageExclusionComponent, - DossierDetailsStatsComponent, EditDossierDeletedDocumentsComponent, AnnotationsListComponent, AnnotationSourceComponent, @@ -102,7 +82,6 @@ const components = [ const services = [ DossiersService, DossiersDialogService, - FileActionService, AnnotationActionsService, ManualAnnotationService, PdfViewerDataService, @@ -114,6 +93,6 @@ const services = [ @NgModule({ declarations: [...components], providers: [...services], - imports: [CommonModule, SharedModule, FileUploadDownloadModule, DossiersRoutingModule, OverlayModule] + imports: [CommonModule, SharedModule, SharedDossiersModule, FileUploadDownloadModule, DossiersRoutingModule, OverlayModule] }) export class DossiersModule {} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.html deleted file mode 100644 index a7d8f738f..000000000 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.html +++ /dev/null @@ -1,86 +0,0 @@ -
- - -
- -
-
- -
- -
- -
-
-
- - - - - - -
-
- {{ 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/dossier-listing-screen/dossier-listing-screen.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.scss deleted file mode 100644 index cb00faaf2..000000000 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -:host { - ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - &.status-container { - width: 160px; - } - } - - .right-container { - display: flex; - width: 466px; - min-width: 466px; - padding-right: 11px; - - &.has-scrollbar:hover { - padding-right: 0; - } - - redaction-dossier-listing-details { - min-width: 466px; - } - } -} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts deleted file mode 100644 index 8b4b7661f..000000000 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-listing-screen/dossier-listing-screen.component.ts +++ /dev/null @@ -1,336 +0,0 @@ -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 { UserService } from '@services/user.service'; -import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; -import { groupBy } from '@utils/functions'; -import { TranslateService } from '@ngx-translate/core'; -import { Dossier } from '@state/model/dossier'; -import { timer } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { TranslateChartService } from '@services/translate-chart.service'; -import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter'; -import { StatusSorter } from '@utils/sorters/status-sorter'; -import { Router } from '@angular/router'; -import { DossiersDialogService } from '../../services/dossiers-dialog.service'; -import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy'; -import { UserPreferenceService } from '@services/user-preference.service'; -import { ButtonConfig } from '@shared/components/page-header/models/button-config.model'; -import { DefaultListingServices, keyChecker, ListingComponent, NestedFilter, TableColumnConfig, TableComponent } from '@iqser/common-ui'; -import { workloadTranslations } from '../../translations/workload-translations'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { fileStatusTranslations } from '../../translations/file-status-translations'; -import { annotationFilterChecker, dossierMemberChecker, dossierStatusChecker, dossierTemplateChecker } from '@utils/filter-utils'; -import { PermissionsService } from '@services/permissions.service'; - -@Component({ - templateUrl: './dossier-listing-screen.component.html', - styleUrls: ['./dossier-listing-screen.component.scss'], - providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierListingScreenComponent) }] -}) -export class DossierListingScreenComponent - extends ListingComponent - implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach -{ - readonly currentUser = this._userService.currentUser; - readonly tableHeaderLabel = _('dossier-listing.table-header.title'); - readonly buttonConfigs: readonly ButtonConfig[] = [ - { - label: _('dossier-listing.add-new'), - action: (): void => this.openAddDossierDialog(), - hide: !this.currentUser.isManager, - icon: 'red:plus', - type: 'primary' - } - ]; - tableColumnConfigs: TableColumnConfig[]; - dossiersChartData: DoughnutChartConfig[] = []; - documentsChartData: DoughnutChartConfig[] = []; - @ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef; - @ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef; - @ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef; - @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef; - 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 _translateService: TranslateService, - private readonly _dialogService: DossiersDialogService, - private readonly _translateChartService: TranslateChartService, - private readonly _userPreferenceService: UserPreferenceService - ) { - 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; - } - - getDossierTemplateNameFor(dossierTemplateId: string): string { - return this._appStateService.getDossierTemplateById(dossierTemplateId).name; - } - - ngOnInit(): void { - this._configureTableColumns(); - 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 _configureTableColumns() { - this.tableColumnConfigs = [ - { - label: _('dossier-listing.table-col-names.name'), - sortByKey: 'searchKey', - template: this.nameTemplate, - width: '2fr' - }, - { - label: _('dossier-listing.table-col-names.needs-work'), - template: this.needsWorkTemplate - }, - { - label: _('dossier-listing.table-col-names.owner'), - class: 'user-column', - template: this.ownerTemplate - }, - { - label: _('dossier-listing.table-col-names.status'), - class: 'flex-end', - template: this.statusTemplate, - width: 'auto' - } - ]; - } - - private _loadEntitiesFromState() { - this.entitiesService.setEntities(this._appStateService.allDossiers); - } - - private _computeAllFilters() { - const allDistinctFileStatus = new Set(); - const allDistinctPeople = new Set(); - const allDistinctNeedsWork = new Set(); - const allDistinctDossierTemplates = new Set(); - - this.entitiesService.all?.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]) - }) - ); - - this.filterService.addFilterGroup({ - 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) - }) - ); - - this.filterService.addFilterGroup({ - 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] - }) - ); - - this.filterService.addFilterGroup({ - slug: 'needsWorkFilters', - label: this._translateService.instant('filters.needs-work'), - icon: 'red:needs-work', - filterTemplate: this._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 - }) - ); - - this.filterService.addFilterGroup({ - slug: 'dossierTemplateFilters', - label: this._translateService.instant('filters.dossier-templates'), - icon: 'red:template', - hide: dossierTemplateFilters.length <= 1, - filters: dossierTemplateFilters, - checker: dossierTemplateChecker - }); - - const quickFilters = this._createQuickFilters(); - this.filterService.addFilterGroup({ - slug: 'quickFilters', - filters: quickFilters, - checker: (dw: Dossier) => quickFilters.reduce((acc, f) => acc || (f.checked && f.checker(dw)), false) - }); - - const dossierFilters = this.entitiesService.all.map( - dossier => - new NestedFilter({ - id: dossier.dossierName, - label: dossier.dossierName - }) - ); - this.filterService.addFilterGroup({ - 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') - }); - } - - private _createQuickFilters(): NestedFilter[] { - const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers'); - const filters: NestedFilter[] = [ - { - id: 'my-dossiers', - label: myDossiersLabel, - checker: (dw: Dossier) => 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); - } -} 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 deleted file mode 100644 index a75cf483d..000000000 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.ts +++ /dev/null @@ -1,601 +0,0 @@ -import { - ChangeDetectorRef, - Component, - ElementRef, - forwardRef, - HostListener, - Injector, - OnDestroy, - OnInit, - TemplateRef, - ViewChild -} from '@angular/core'; -import { FileStatus, FileStatuses, 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 { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter'; -import { StatusSorter } from '@utils/sorters/status-sorter'; -import { convertFiles, Files, handleFileDrop } from '@utils/file-drop-utils'; -import { DossiersDialogService } from '../../services/dossiers-dialog.service'; -import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy'; -import { ConfigService } from '@services/config.service'; -import { ActionConfig } from '@shared/components/page-header/models/action-config.model'; -import { - CircleButtonTypes, - DefaultListingServices, - INestedFilter, - keyChecker, - 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 { workloadTranslations } from '../../translations/workload-translations'; -import { fileStatusTranslations } from '../../translations/file-status-translations'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { annotationFilterChecker } from '@utils/filter-utils'; -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 { FileActionService } from '../../services/file-action.service'; -import { FileAttributesService } from '../../services/file-attributes.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'); - readonly actionConfigs: readonly ActionConfig[] = [ - { - label: this._translateService.instant('dossier-overview.header-actions.edit'), - action: ($event): void => this.openEditDossierDialog($event), - icon: 'iqser:edit', - hide: !this.currentUser.isManager - } - ]; - tableColumnConfigs: readonly TableColumnConfig[] = []; - collapsedDetails = false; - dossierAttributes: DossierAttributeWithValue[] = []; - fileAttributeConfigs: IFileAttributeConfig[]; - @ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef; - @ViewChild('addedOnTemplate', { static: true }) addedOnTemplate: TemplateRef; - @ViewChild('attributeTemplate', { static: true }) attributeTemplate: TemplateRef; - @ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef; - @ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef; - @ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef; - @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef; - readonly workflowConfig: WorkflowConfig; - @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 _configService: ConfigService, - 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 _fileActionService: FileActionService, - private readonly _fileAttributesService: FileAttributesService - ) { - super(_injector); - this._loadEntitiesFromState(); - this.fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig( - this.currentDossier.dossierTemplateId - )?.fileAttributeConfigs; - this.workflowConfig = { - columnIdentifierFn: entity => entity.status, - itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`, - columns: [ - { - label: fileStatusTranslations[FileStatuses.UNASSIGNED], - key: FileStatuses.UNASSIGNED, - enterFn: this.unassignFn, - enterPredicate: () => false, - color: '#D3D5DA' - }, - { - label: fileStatusTranslations[FileStatuses.UNDER_REVIEW], - enterFn: this.underReviewFn, - enterPredicate: (file: File) => - this.permissionsService.canSetUnderReview(file) || - this.permissionsService.canAssignToSelf(file) || - this.permissionsService.canAssignUser(file), - key: FileStatuses.UNDER_REVIEW, - color: '#FDBD00' - }, - { - label: fileStatusTranslations[FileStatuses.UNDER_APPROVAL], - enterFn: this.underApprovalFn, - enterPredicate: (file: File) => - this.permissionsService.canSetUnderApproval(file) || this.permissionsService.canUndoApproval(file), - key: FileStatuses.UNDER_APPROVAL, - color: '#374C81' - }, - { - label: fileStatusTranslations[FileStatuses.APPROVED], - enterFn: this.approveFn, - enterPredicate: (file: File) => this.permissionsService.isReadyForApproval(file), - key: FileStatuses.APPROVED, - color: '#48C9F7' - } - ] - }; - } - - get checkedRequiredFilters() { - return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked); - } - - get checkedNotRequiredFilters() { - return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked); - } - - get displayedInFileListAttributes() { - return this.fileAttributeConfigs?.filter(config => config.displayedInFileList) || []; - } - - unassignFn = async (file: File) => { - // TODO - console.log('unassign', file); - }; - - underReviewFn = (file: File) => { - this._fileActionService.assignFile('reviewer', null, file, () => this._loadingService.loadWhile(this.reloadDossiers()), true); - }; - - underApprovalFn = async (file: File) => { - if (this._appStateService.activeDossier.approverIds.length > 1) { - this._fileActionService.assignFile('approver', null, file, () => this._loadingService.loadWhile(this.reloadDossiers()), true); - } else { - this._loadingService.start(); - await this._fileActionService.setFilesUnderApproval([file]).toPromise(); - await this.reloadDossiers(); - this._loadingService.stop(); - } - }; - - approveFn = async (file: File) => { - this._loadingService.start(); - await this._fileActionService.setFilesApproved([file]).toPromise(); - await this.reloadDossiers(); - this._loadingService.stop(); - }; - - 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._configureTableColumns(); - 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(); - } - - openEditDossierDialog($event: MouseEvent) { - this._dialogService.openDialog('editDossier', $event, { - dossier: this.currentDossier - }); - } - - 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._configService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment()); - - private _configureTableColumns() { - const dynamicColumns: TableColumnConfig[] = []; - for (const config of this.displayedInFileListAttributes) { - if (config.displayedInFileList) { - dynamicColumns.push({ label: config.label, notTranslatable: true, template: this.attributeTemplate, extra: config }); - } - } - this.tableColumnConfigs = [ - { - label: _('dossier-overview.table-col-names.name'), - sortByKey: 'searchKey', - template: this.filenameTemplate, - width: '3fr' - }, - { - label: _('dossier-overview.table-col-names.added-on'), - sortByKey: 'added', - template: this.addedOnTemplate, - width: '2fr' - }, - ...dynamicColumns, - { - label: _('dossier-overview.table-col-names.needs-work'), - template: this.needsWorkTemplate - }, - { - label: _('dossier-overview.table-col-names.assigned-to'), - class: 'user-column', - sortByKey: 'reviewerName', - template: this.reviewerTemplate, - width: '2fr' - }, - { - label: _('dossier-overview.table-col-names.pages'), - sortByKey: 'numberOfPages', - template: this.pagesTemplate - }, - { - label: _('dossier-overview.table-col-names.status'), - class: 'flex-end', - sortByKey: 'statusSort', - template: this.statusTemplate - } - ]; - } - - 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 allDistinctFileStatuses = new Set(); - const allDistinctPeople = new Set(); - const allDistinctAddedDates = new Set(); - const allDistinctNeedsWork = new Set(); - - const dynamicFilters = new Map>(); - - this.entitiesService.all.forEach(file => { - allDistinctPeople.add(file.currentReviewer); - allDistinctFileStatuses.add(file.status); - allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY')); - - if (file.analysisRequired) { - allDistinctNeedsWork.add('analysis'); - } - if (file.hintsOnly) { - allDistinctNeedsWork.add('hint'); - } - if (file.hasRedactions) { - allDistinctNeedsWork.add('redaction'); - } - if (file.hasSuggestions) { - allDistinctNeedsWork.add('suggestion'); - } - if (file.hasUpdates) { - allDistinctNeedsWork.add('updated'); - } - if (file.hasImages) { - allDistinctNeedsWork.add('image'); - } - if (file.hasNone) { - allDistinctNeedsWork.add('none'); - } - if (file.hasAnnotationComments) { - allDistinctNeedsWork.add('comment'); - } - - // extract values for dynamic filters - this.fileAttributeConfigs.forEach(config => { - if (config.filterable) { - const filterKey = `${config.id}:${config.label}`; - let filters = dynamicFilters.get(filterKey); - if (!filters) { - dynamicFilters.set(filterKey, new Set()); - filters = dynamicFilters.get(filterKey); - } - let filterValue = file.fileAttributes?.attributeIdToValue[config.id]; - if (!filterValue) { - filterValue = '-'; - file.fileAttributes.attributeIdToValue[config.id] = '-'; - } - filters.add(filterValue); - } - }); - }); - - const statusFilters = [...allDistinctFileStatuses].map( - status => - new NestedFilter({ - id: status, - label: this._translateService.instant(fileStatusTranslations[status]) - }) - ); - - this.filterService.addFilterGroup({ - slug: 'statusFilters', - label: this._translateService.instant('filters.status'), - icon: 'red:status', - filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]), - checker: keyChecker('status') - }); - - const peopleFilters: NestedFilter[] = []; - if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) { - allDistinctPeople.delete(undefined); - allDistinctPeople.delete(null); - peopleFilters.push( - new NestedFilter({ - id: null, - label: this._translateService.instant('initials-avatar.unassigned') - }) - ); - } - allDistinctPeople.forEach(userId => { - peopleFilters.push( - new NestedFilter({ - id: userId, - label: this._userService.getNameForId(userId) - }) - ); - }); - this.filterService.addFilterGroup({ - slug: 'peopleFilters', - label: this._translateService.instant('filters.assigned-people'), - icon: 'red:user', - filters: peopleFilters, - checker: keyChecker('currentReviewer') - }); - - const needsWorkFilters = [...allDistinctNeedsWork].map( - item => - new NestedFilter({ - id: item, - label: workloadTranslations[item] - }) - ); - - this.filterService.addFilterGroup({ - slug: 'needsWorkFilters', - label: this._translateService.instant('filters.needs-work'), - icon: 'red:needs-work', - filterTemplate: this._needsWorkFilterTemplate, - filters: needsWorkFilters.sort(RedactionFilterSorter.byKey), - checker: annotationFilterChecker, - matchAll: true - }); - - dynamicFilters.forEach((filterValue: Set, filterKey: string) => { - const id = filterKey.split(':')[0]; - const key = filterKey.split(':')[1]; - this.filterService.addFilterGroup({ - slug: key, - label: key, - icon: 'red:template', - filters: [...filterValue].map( - (value: string) => - new NestedFilter({ - id: value, - label: value === '-' ? this._translateService.instant('filters.empty') : value - }) - ), - checker: (input: File, filter: INestedFilter) => filter.id === input.fileAttributes.attributeIdToValue[id] - }); - }); - - this.filterService.addFilterGroup({ - slug: 'quickFilters', - filters: this._createQuickFilters(), - checker: (file: File) => - this.checkedRequiredFilters.reduce((acc, f) => acc && f.checker(file), true) && - (this.checkedNotRequiredFilters.length === 0 || - this.checkedNotRequiredFilters.reduce((acc, f) => acc || f.checker(file), false)) - }); - - const filesNamesFilters = this.entitiesService.all.map( - file => - new NestedFilter({ - id: file.filename, - label: file.filename - }) - ); - - this.filterService.addFilterGroup({ - slug: 'filesNamesFilter', - label: this._translateService.instant('dossier-overview.filters.label'), - icon: 'red:document', - filters: filesNamesFilters, - checker: keyChecker('filename'), - filterceptionPlaceholder: this._translateService.instant('dossier-overview.filters.search') - }); - } - - private _createQuickFilters(): NestedFilter[] { - let quickFilters: INestedFilter[] = []; - if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) { - const recentPeriod = this._configService.values.RECENT_PERIOD_IN_HOURS; - quickFilters = [ - { - id: 'recent', - label: this._translateService.instant('dossier-overview.quick-filters.recent', { - hours: recentPeriod - }), - required: true, - checker: this.recentlyModifiedChecker - } - ]; - } - - return [ - ...quickFilters, - { - id: 'assigned-to-me', - label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'), - checker: (file: File) => file.currentReviewer === this.currentUser.id - }, - { - id: 'unassigned', - label: this._translateService.instant('dossier-overview.quick-filters.unassigned'), - checker: (file: File) => !file.currentReviewer - }, - { - id: 'assigned-to-others', - label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'), - checker: (file: File) => !!file.currentReviewer && file.currentReviewer !== this.currentUser.id - } - ].map(filter => new NestedFilter(filter)); - } -} diff --git a/apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html similarity index 100% rename from apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.html rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html diff --git a/apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.scss rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.scss diff --git a/apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts similarity index 94% rename from apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.ts rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts index f98e39318..8f2b6f6eb 100644 --- a/apps/red-ui/src/app/modules/dossier/components/bulk-actions/dossier-overview-bulk-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts @@ -1,13 +1,13 @@ import { Component, EventEmitter, Output } from '@angular/core'; -import { AppStateService } from '@state/app-state.service'; +import { AppStateService } from '../../../../../../state/app-state.service'; import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http'; -import { PermissionsService } from '@services/permissions.service'; -import { File } from '@models/file/file'; -import { FileActionService } from '../../services/file-action.service'; +import { PermissionsService } from '../../../../../../services/permissions.service'; +import { File } from '../../../../../../models/file/file'; +import { FileActionService } from '../../../../shared/services/file-action.service'; import { Observable } from 'rxjs'; -import { DossiersDialogService } from '../../services/dossiers-dialog.service'; +import { DossiersDialogService } from '../../../../services/dossiers-dialog.service'; import { CircleButtonTypes, EntitiesService, LoadingService } from '@iqser/common-ui'; -import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; +import { ConfirmationDialogInput } from '../../../../../shared/dialogs/confirmation-dialog/confirmation-dialog.component'; import { TranslateService } from '@ngx-translate/core'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.html similarity index 100% rename from apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.html rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.html diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.scss rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.scss diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts similarity index 93% rename from apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.ts rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts index 045dec7e8..dcfb93f0e 100644 --- a/apps/red-ui/src/app/modules/dossier/components/dossier-details-stats/dossier-details-stats.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts @@ -3,7 +3,7 @@ import { DossierAttributeWithValue } from '@models/dossier-attributes.model'; import { AppStateService } from '@state/app-state.service'; import { Dossier } from '@state/model/dossier'; import { IDossierTemplate } from '@redaction/red-ui-http'; -import { DossiersDialogService } from '../../services/dossiers-dialog.service'; +import { DossiersDialogService } from '../../../../services/dossiers-dialog.service'; @Component({ selector: 'redaction-dossier-details-stats', diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.html similarity index 100% rename from apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.html rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.html diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.scss rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.scss diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts similarity index 95% rename from apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts index 6d1fa343f..c291dce12 100644 --- a/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.ts @@ -1,13 +1,12 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; -import { groupBy } from '@utils/functions'; +import { groupBy, StatusSorter } from '@utils/index'; import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { TranslateChartService } from '@services/translate-chart.service'; -import { StatusSorter } from '@utils/sorters/status-sorter'; import { UserService } from '@services/user.service'; import { FilterService, Toaster } from '@iqser/common-ui'; import { DossierAttributeWithValue } from '@models/dossier-attributes.model'; -import { fileStatusTranslations } from '../../translations/file-status-translations'; +import { fileStatusTranslations } from '../../../../translations/file-status-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { List } from '@redaction/red-ui-http'; import { User } from '@models/user'; diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html new file mode 100644 index 000000000..43de75f4a --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html @@ -0,0 +1,70 @@ +
+
+
+ {{ file.filename }} +
+
+
+
+ + {{ file.primaryAttribute }} + +
+
+ +
+ +
+
+ {{ file.added | date: 'd MMM. yyyy, hh:mm a' }} +
+
+ +
+ {{ file.fileAttributes.attributeIdToValue[config.id] }} +
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ + {{ file.numberOfPages }} +
+
+
+ +
+
+
+
+ + + +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.scss new file mode 100644 index 000000000..8450b2bf1 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.scss @@ -0,0 +1,26 @@ +@use 'common-mixins'; +@use 'variables'; + +.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; + } +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.ts new file mode 100644 index 000000000..3507e73cb --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.ts @@ -0,0 +1,19 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; +import { File } from '@models/file/file'; +import { Required } from '@iqser/common-ui'; +import { IFileAttributeConfig } from '@redaction/red-ui-http'; + +@Component({ + selector: 'redaction-table-item', + templateUrl: './table-item.component.html', + styleUrls: ['./table-item.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class TableItemComponent { + @Input() @Required() file!: File; + @Input() @Required() statsTemplate!: TemplateRef; + @Input() @Required() displayedAttributes!: IFileAttributeConfig[]; + @Output() readonly calculateData = new EventEmitter(); + + constructor() {} +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts new file mode 100644 index 000000000..af6a37ec1 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts @@ -0,0 +1,364 @@ +import { Injectable, TemplateRef } from '@angular/core'; +import { IFilterGroup, INestedFilter, keyChecker, LoadingService, NestedFilter, TableColumnConfig, WorkflowConfig } from '@iqser/common-ui'; +import { File } from '@models/file/file'; +import { fileStatusTranslations } from '../../translations/file-status-translations'; +import { FileStatus, FileStatuses, IFileAttributeConfig } from '@redaction/red-ui-http'; +import { FileActionService } from '../../shared/services/file-action.service'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { TranslateService } from '@ngx-translate/core'; +import { UserService } from '@services/user.service'; +import { DossiersDialogService } from '../../services/dossiers-dialog.service'; +import { annotationFilterChecker, RedactionFilterSorter, StatusSorter } from '@utils/index'; +import { workloadTranslations } from '../../translations/workload-translations'; +import * as moment from 'moment'; +import { ConfigService as AppConfigService } from '@services/config.service'; + +@Injectable() +export class ConfigService { + constructor( + private readonly _fileActionService: FileActionService, + private readonly _loadingService: LoadingService, + private readonly _appStateService: AppStateService, + private readonly _permissionsService: PermissionsService, + private readonly _translateService: TranslateService, + private readonly _userService: UserService, + private readonly _dialogService: DossiersDialogService, + private readonly _appConfigService: AppConfigService + ) {} + + get actionConfig() { + return [ + { + label: this._translateService.instant('dossier-overview.header-actions.edit'), + action: $event => this._openEditDossierDialog($event), + icon: 'iqser:edit', + hide: !this._userService.currentUser.isManager + } + ]; + } + + tableConfig(displayedAttributes: IFileAttributeConfig[]): TableColumnConfig[] { + const dynamicColumns: TableColumnConfig[] = displayedAttributes.map(config => ({ + label: config.label, + notTranslatable: true + })); + return [ + { + label: _('dossier-overview.table-col-names.name'), + sortByKey: 'searchKey', + width: '3fr' + }, + { + label: _('dossier-overview.table-col-names.added-on'), + sortByKey: 'added', + width: '2fr' + }, + ...dynamicColumns, + { + label: _('dossier-overview.table-col-names.needs-work') + }, + { + label: _('dossier-overview.table-col-names.assigned-to'), + class: 'user-column', + sortByKey: 'reviewerName', + width: '2fr' + }, + { + label: _('dossier-overview.table-col-names.pages'), + sortByKey: 'numberOfPages' + }, + { + label: _('dossier-overview.table-col-names.status'), + class: 'flex-end', + sortByKey: 'statusSort' + } + ]; + } + + workflowConfig(reloadDossiers: () => Promise): WorkflowConfig { + return { + columnIdentifierFn: entity => entity.status, + itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`, + columns: [ + { + label: fileStatusTranslations[FileStatuses.UNASSIGNED], + key: FileStatuses.UNASSIGNED, + enterFn: this._unassignFn(reloadDossiers), + enterPredicate: () => false, + color: '#D3D5DA' + }, + { + label: fileStatusTranslations[FileStatuses.UNDER_REVIEW], + enterFn: this._underReviewFn(reloadDossiers), + enterPredicate: (file: File) => + this._permissionsService.canSetUnderReview(file) || + this._permissionsService.canAssignToSelf(file) || + this._permissionsService.canAssignUser(file), + key: FileStatuses.UNDER_REVIEW, + color: '#FDBD00' + }, + { + label: fileStatusTranslations[FileStatuses.UNDER_APPROVAL], + enterFn: this._underApprovalFn(reloadDossiers), + enterPredicate: (file: File) => + this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file), + key: FileStatuses.UNDER_APPROVAL, + color: '#374C81' + }, + { + label: fileStatusTranslations[FileStatuses.APPROVED], + enterFn: this._approveFn(reloadDossiers), + enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file), + key: FileStatuses.APPROVED, + color: '#48C9F7' + } + ] + }; + } + + filterGroups( + entities: File[], + fileAttributeConfigs: IFileAttributeConfig[], + needsWorkFilterTemplate: TemplateRef, + checkedRequiredFilters: () => NestedFilter[], + checkedNotRequiredFilters: () => NestedFilter[] + ) { + const allDistinctFileStatuses = new Set(); + const allDistinctPeople = new Set(); + const allDistinctAddedDates = new Set(); + const allDistinctNeedsWork = new Set(); + + const dynamicFilters = new Map>(); + + const filterGroups: IFilterGroup[] = []; + + entities.forEach(file => { + allDistinctPeople.add(file.currentReviewer); + allDistinctFileStatuses.add(file.status); + allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY')); + + if (file.analysisRequired) { + allDistinctNeedsWork.add('analysis'); + } + if (file.hintsOnly) { + allDistinctNeedsWork.add('hint'); + } + if (file.hasRedactions) { + allDistinctNeedsWork.add('redaction'); + } + if (file.hasSuggestions) { + allDistinctNeedsWork.add('suggestion'); + } + if (file.hasUpdates) { + allDistinctNeedsWork.add('updated'); + } + if (file.hasImages) { + allDistinctNeedsWork.add('image'); + } + if (file.hasNone) { + allDistinctNeedsWork.add('none'); + } + if (file.hasAnnotationComments) { + allDistinctNeedsWork.add('comment'); + } + + // extract values for dynamic filters + fileAttributeConfigs.forEach(config => { + if (config.filterable) { + const filterKey = `${config.id}:${config.label}`; + let filters = dynamicFilters.get(filterKey); + if (!filters) { + dynamicFilters.set(filterKey, new Set()); + filters = dynamicFilters.get(filterKey); + } + let filterValue = file.fileAttributes?.attributeIdToValue[config.id]; + if (!filterValue) { + filterValue = '-'; + file.fileAttributes.attributeIdToValue[config.id] = '-'; + } + filters.add(filterValue); + } + }); + }); + + const statusFilters = [...allDistinctFileStatuses].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: keyChecker('status') + }); + + const peopleFilters: NestedFilter[] = []; + if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) { + allDistinctPeople.delete(undefined); + allDistinctPeople.delete(null); + peopleFilters.push( + new NestedFilter({ + id: null, + label: this._translateService.instant('initials-avatar.unassigned') + }) + ); + } + allDistinctPeople.forEach(userId => { + peopleFilters.push( + new NestedFilter({ + id: userId, + label: this._userService.getNameForId(userId) + }) + ); + }); + filterGroups.push({ + slug: 'peopleFilters', + label: this._translateService.instant('filters.assigned-people'), + icon: 'red:user', + filters: peopleFilters, + checker: keyChecker('currentReviewer') + }); + + const needsWorkFilters = [...allDistinctNeedsWork].map( + item => + new NestedFilter({ + id: item, + label: workloadTranslations[item] + }) + ); + + filterGroups.push({ + slug: 'needsWorkFilters', + label: this._translateService.instant('filters.needs-work'), + icon: 'red:needs-work', + filterTemplate: needsWorkFilterTemplate, + filters: needsWorkFilters.sort(RedactionFilterSorter.byKey), + checker: annotationFilterChecker, + matchAll: true + }); + + dynamicFilters.forEach((filterValue: Set, filterKey: string) => { + const id = filterKey.split(':')[0]; + const key = filterKey.split(':')[1]; + filterGroups.push({ + slug: key, + label: key, + icon: 'red:template', + filters: [...filterValue].map( + (value: string) => + new NestedFilter({ + id: value, + label: value === '-' ? this._translateService.instant('filters.empty') : value + }) + ), + checker: (input: File, filter: INestedFilter) => filter.id === input.fileAttributes.attributeIdToValue[id] + }); + }); + + filterGroups.push({ + slug: 'quickFilters', + filters: this._quickFilters(entities), + checker: (file: File) => + checkedRequiredFilters().reduce((acc, f) => acc && f.checker(file), true) && + (checkedNotRequiredFilters().length === 0 || checkedNotRequiredFilters().reduce((acc, f) => acc || f.checker(file), false)) + }); + + const filesNamesFilters = entities.map( + file => + new NestedFilter({ + id: file.filename, + label: file.filename + }) + ); + + filterGroups.push({ + slug: 'filesNamesFilter', + label: this._translateService.instant('dossier-overview.filters.label'), + icon: 'red:document', + filters: filesNamesFilters, + checker: keyChecker('filename'), + filterceptionPlaceholder: this._translateService.instant('dossier-overview.filters.search') + }); + + return filterGroups; + } + + _recentlyModifiedChecker = (file: File) => + moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment()); + + private _quickFilters(entities: File[]): NestedFilter[] { + let quickFilters: INestedFilter[] = []; + if (entities.filter(this._recentlyModifiedChecker).length > 0) { + const recentPeriod = this._appConfigService.values.RECENT_PERIOD_IN_HOURS; + quickFilters = [ + { + id: 'recent', + label: this._translateService.instant('dossier-overview.quick-filters.recent', { + hours: recentPeriod + }), + required: true, + checker: this._recentlyModifiedChecker + } + ]; + } + + return [ + ...quickFilters, + { + id: 'assigned-to-me', + label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'), + checker: (file: File) => file.currentReviewer === this._userService.currentUser.id + }, + { + id: 'unassigned', + label: this._translateService.instant('dossier-overview.quick-filters.unassigned'), + checker: (file: File) => !file.currentReviewer + }, + { + id: 'assigned-to-others', + label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'), + checker: (file: File) => !!file.currentReviewer && file.currentReviewer !== this._userService.currentUser.id + } + ].map(filter => new NestedFilter(filter)); + } + + private _openEditDossierDialog($event: MouseEvent) { + this._dialogService.openDialog('editDossier', $event, { + dossier: this._appStateService.activeDossier + }); + } + + private _unassignFn = (reloadDossiers: () => Promise) => async (file: File) => { + // TODO + console.log('unassign', file); + }; + + private _underReviewFn = (reloadDossiers: () => Promise) => (file: File) => { + this._fileActionService.assignFile('reviewer', null, file, () => this._loadingService.loadWhile(reloadDossiers()), true); + }; + + private _underApprovalFn = (reloadDossiers: () => Promise) => async (file: File) => { + if (this._appStateService.activeDossier.approverIds.length > 1) { + this._fileActionService.assignFile('approver', null, file, () => this._loadingService.loadWhile(reloadDossiers()), true); + } else { + this._loadingService.start(); + await this._fileActionService.setFilesUnderApproval([file]).toPromise(); + await reloadDossiers(); + this._loadingService.stop(); + } + }; + + private _approveFn = (reloadDossiers: () => Promise) => async (file: File) => { + this._loadingService.start(); + await this._fileActionService.setFilesApproved([file]).toPromise(); + await reloadDossiers(); + this._loadingService.stop(); + }; +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts new file mode 100644 index 000000000..76b7ee087 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/dossier-overview.module.ts @@ -0,0 +1,34 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from '@shared/shared.module'; +import { IqserIconsModule } from '@iqser/common-ui'; +import { TranslateModule } from '@ngx-translate/core'; +import { DossierOverviewScreenComponent } from './screen/dossier-overview-screen.component'; +import { DossierOverviewBulkActionsComponent } from './components/bulk-actions/dossier-overview-bulk-actions.component'; +import { DossierDetailsComponent } from './components/dossier-details/dossier-details.component'; +import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component'; +import { TableItemComponent } from './components/table-item/table-item.component'; +import { ConfigService } from './config.service'; +import { SharedDossiersModule } from '../../shared/shared-dossiers.module'; + +const routes = [ + { + path: '', + component: DossierOverviewScreenComponent, + pathMatch: 'full' + } +]; + +@NgModule({ + declarations: [ + DossierOverviewScreenComponent, + DossierOverviewBulkActionsComponent, + DossierDetailsComponent, + DossierDetailsStatsComponent, + TableItemComponent + ], + providers: [ConfigService], + imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule] +}) +export class DossierOverviewModule {} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html similarity index 66% rename from apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.html rename to apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html index fb56c35c2..d85606bcb 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html @@ -91,95 +91,6 @@ - -
-
-
- {{ file.filename }} -
-
-
-
- - {{ file.primaryAttribute }} - -
-
- -
-
- - -
-
- {{ file.added | date: 'd MMM. yyyy, hh:mm a' }} -
-
-
- - -
- {{ file.fileAttributes.attributeIdToValue[config.id] }} -
-
- - - -
- -
- -
- -
-
- - -
- -
-
- - -
-
-
- - {{ file.numberOfPages }} -
-
-
-
- - -
-
-
-
- - - -
-
-
@@ -199,6 +110,15 @@
+ + + +
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 From 0f93837529787f8d6cdd207911bf2436cef5bf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Fri, 1 Oct 2021 15:09:23 +0300 Subject: [PATCH 2/4] Downloads & trash refactor --- .../downloads-list-screen.component.html | 95 ++++++++-------- .../downloads-list-screen.component.ts | 23 ++-- .../screens/trash/trash-screen.component.html | 102 +++++++++--------- .../screens/trash/trash-screen.component.scss | 3 + .../screens/trash/trash-screen.component.ts | 39 ++----- .../dossier-overview/config.service.ts | 2 +- libs/common-ui | 2 +- 7 files changed, 113 insertions(+), 153 deletions(-) diff --git a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html index 0b07b15ba..10f9185cb 100644 --- a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html +++ b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html @@ -7,7 +7,6 @@
- -
-
- {{ download.filename }} + +
+
+
+ {{ download.filename }} +
+
+ +
+
+ {{ download.size }} +
+
+ +
+
+ {{ download.creationDate | date: 'd MMM. yyyy, hh:mm a' }} +
+
+ +
+
+ {{ download.status }} +
+
+ +
+ +
+ + + + +
- - -
-
- {{ download.size }} -
-
-
- - -
-
- {{ download.creationDate | date: 'd MMM. yyyy, hh:mm a' }} -
-
-
- - -
-
- {{ download.status }} -
-
-
- - -
- - - - - -
-
diff --git a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts index 41207582a..1bb7b56ac 100644 --- a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts +++ b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, forwardRef, Injector, OnInit } from '@angular/core'; import { FileDownloadService } from '@upload-download/services/file-download.service'; import { DownloadStatusWrapper } from '@upload-download/model/download-status.wrapper'; import { DownloadControllerService } from '@redaction/red-ui-http'; @@ -16,11 +16,12 @@ import { RouterHistoryService } from '@services/router-history.service'; export class DownloadsListScreenComponent extends ListingComponent implements OnInit { readonly circleButtonTypes = CircleButtonTypes; readonly tableHeaderLabel = _('downloads-list.table-header.title'); - tableColumnConfigs: TableColumnConfig[]; - @ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef; - @ViewChild('sizeTemplate', { static: true }) sizeTemplate: TemplateRef; - @ViewChild('creationDateTemplate', { static: true }) creationDateTemplate: TemplateRef; - @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('downloads-list.table-col-names.name'), width: '2fr' }, + { label: _('downloads-list.table-col-names.size') }, + { label: _('downloads-list.table-col-names.date') }, + { label: _('downloads-list.table-col-names.status') } + ]; constructor( protected readonly _injector: Injector, @@ -33,7 +34,6 @@ export class DownloadsListScreenComponent extends ListingComponent { @@ -50,15 +50,6 @@ export class DownloadsListScreenComponent extends ListingComponent d.storageId); await this._downloadControllerService.deleteDownload({ storageIds }).toPromise(); diff --git a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html index 74c84e850..772400920 100644 --- a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.html @@ -40,62 +40,58 @@ > - -
-
- {{ entity.dossierName }} + +
+
+
+ {{ entity.dossierName }} +
+
+
+ + {{ entity.memberIds.length }} +
+
+ + {{ entity.date | date: 'mediumDate' }} +
+
+ + {{ entity.dueDate | date: 'mediumDate' }} +
+
-
-
- - {{ entity.memberIds.length }} + +
+ +
+ +
+ + {{ entity.softDeletedTime | date: 'd MMM. yyyy, hh:mm a' }} + +
+ +
+
+ {{ entity.restoreDate | date: 'timeFromNow' }}
-
- - {{ entity.date | date: 'mediumDate' }} -
-
- - {{ entity.dueDate | date: 'mediumDate' }} +
+ + +
- - -
- -
-
- - -
- - {{ entity.softDeletedTime | date: 'd MMM. yyyy, hh:mm a' }} - -
-
- - -
-
- {{ entity.restoreDate | date: 'timeFromNow' }} -
-
- - - -
-
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss index e69de29bb..b0ed00292 100644 --- a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.scss @@ -0,0 +1,3 @@ +.stats-subtitle { + margin-top: 4px; +} diff --git a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts index bed1882f5..bdec52e11 100644 --- a/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/trash/trash-screen.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit } from '@angular/core'; import { IDossier } from '@redaction/red-ui-http'; import { CircleButtonTypes, @@ -35,11 +35,12 @@ export class TrashScreenComponent extends ListingComponent impl readonly circleButtonTypes = CircleButtonTypes; readonly tableHeaderLabel = _('trash.table-header.title'); readonly canRestoreSelected$ = this._canRestoreSelected$; - tableColumnConfigs: TableColumnConfig[]; - @ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef; - @ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef; - @ViewChild('deletedTimeTemplate', { static: true }) deletedTimeTemplate: TemplateRef; - @ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('trash.table-col-names.name'), sortByKey: 'searchKey' }, + { label: _('trash.table-col-names.owner'), class: 'user-column' }, + { label: _('trash.table-col-names.deleted-on'), sortByKey: 'softDeletedTime' }, + { label: _('trash.table-col-names.time-to-restore'), sortByKey: 'softDeletedTime' } + ]; constructor( protected readonly _injector: Injector, @@ -62,7 +63,6 @@ export class TrashScreenComponent extends ListingComponent impl disabledFn = (dossier: DossierListItem) => !dossier.canRestore; async ngOnInit(): Promise { - this._configureTableColumns(); this._loadingService.start(); await this._loadDossiersData(); this.sortingService.setSortingOption({ @@ -95,31 +95,6 @@ export class TrashScreenComponent extends ListingComponent impl this._loadingService.loadWhile(this._restore(dossiers)); } - private _configureTableColumns() { - this.tableColumnConfigs = [ - { - label: _('trash.table-col-names.name'), - sortByKey: 'searchKey', - template: this.filenameTemplate - }, - { - label: _('trash.table-col-names.owner'), - class: 'user-column', - template: this.ownerTemplate - }, - { - label: _('trash.table-col-names.deleted-on'), - sortByKey: 'softDeletedTime', - template: this.deletedTimeTemplate - }, - { - label: _('trash.table-col-names.time-to-restore'), - sortByKey: 'softDeletedTime', - template: this.restoreDateTemplate - } - ]; - } - private _getRestoreDate(softDeletedTime: string): string { return moment(softDeletedTime).add(this._configService.values.DELETE_RETENTION_HOURS, 'hours').toISOString(); } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts index af6a37ec1..5c390ea88 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts @@ -110,7 +110,7 @@ export class ConfigService { { label: fileStatusTranslations[FileStatuses.APPROVED], enterFn: this._approveFn(reloadDossiers), - enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file), + enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved, key: FileStatuses.APPROVED, color: '#48C9F7' } diff --git a/libs/common-ui b/libs/common-ui index 85d22fbcc..551db967e 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 85d22fbcc9b3673d11dbf542e3b6ee16c7791d84 +Subproject commit 551db967e2d747932579464f00e4e497c91dc628 From ed3eba90836bea4c57d6c16930fea1d4f3491d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Fri, 1 Oct 2021 15:51:00 +0300 Subject: [PATCH 3/4] Default colors, dictionaries, dossier templates --- .../downloads-list-screen.component.html | 35 ++++---- .../default-colors-screen.component.html | 40 ++++----- .../default-colors-screen.component.scss | 18 ++-- .../default-colors-screen.component.ts | 26 ++---- .../dictionary-listing-screen.component.html | 89 +++++++++---------- .../dictionary-listing-screen.component.scss | 63 +++++++------ .../dictionary-listing-screen.component.ts | 34 ++----- ...er-templates-listing-screen.component.html | 52 +++++------ ...er-templates-listing-screen.component.scss | 20 ++--- ...sier-templates-listing-screen.component.ts | 39 ++------ libs/common-ui | 2 +- 11 files changed, 164 insertions(+), 254 deletions(-) diff --git a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html index 10f9185cb..6f8dc3f51 100644 --- a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html +++ b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html @@ -11,7 +11,6 @@ [itemSize]="80" [noDataText]="'downloads-list.no-data.title' | translate" [selectionEnabled]="true" - emptyColumnWidth="auto" noDataIcon="red:download" >
@@ -52,27 +51,25 @@
{{ download.status }}
-
-
+
+ -
- + - - - + +
diff --git a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html index 460f9c2c9..f70c2dd2b 100644 --- a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html @@ -20,31 +20,31 @@
- +
- -
-
-
-
+ +
+
+
+
- -
-
-
-
+
+
+
- -
- +
+
+ +
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.scss index c24b7ea42..5adbb8506 100644 --- a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.scss @@ -1,15 +1,9 @@ -:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - &:not(.scrollbar-placeholder) { - padding-left: 24px; - } +.color-wrapper { + align-items: center; - &.color-wrapper { - align-items: center; - - .color-square { - width: 16px; - height: 16px; - min-width: 16px; - } + .color-square { + width: 16px; + height: 16px; + min-width: 16px; } } diff --git a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts index 384cda27c..fb6ac773b 100644 --- a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; import { Colors } from '@redaction/red-ui-http'; import { ActivatedRoute } from '@angular/router'; @@ -29,13 +29,14 @@ interface ListItem extends IListable { providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DefaultColorsScreenComponent) }] }) export class DefaultColorsScreenComponent extends ListingComponent implements OnInit { - @ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef; - @ViewChild('colorTemplate', { static: true }) colorTemplate: TemplateRef; readonly circleButtonTypes = CircleButtonTypes; readonly currentUser = this._userService.currentUser; readonly translations = defaultColorsTranslations; readonly tableHeaderLabel = _('default-colors-screen.table-header.title'); - tableColumnConfigs: TableColumnConfig[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('default-colors-screen.table-col-names.key'), sortByKey: 'searchKey', width: '2fr' }, + { label: _('default-colors-screen.table-col-names.color'), class: 'flex-center' } + ]; private _colorsObj: Colors; constructor( @@ -52,7 +53,6 @@ export class DefaultColorsScreenComponent extends ListingComponent imp } async ngOnInit() { - this._configureTableColumns(); await this._loadColors(); } @@ -72,22 +72,6 @@ export class DefaultColorsScreenComponent extends ListingComponent imp ); } - private _configureTableColumns() { - this.tableColumnConfigs = [ - { - label: _('default-colors-screen.table-col-names.key'), - sortByKey: 'searchKey', - template: this.nameTemplate, - width: '2fr' - }, - { - label: _('default-colors-screen.table-col-names.color'), - class: 'flex-center', - template: this.colorTemplate - } - ]; - } - private async _loadColors() { this._loadingService.start(); const data = await this._appStateService.loadColors(this._appStateService.activeDossierTemplateId).toPromise(); diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html index 77d1ff07a..b54ae5350 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.html @@ -22,7 +22,6 @@
- -
-
-
-
- {{ entity.label }} + +
+
+
+
+
+ {{ dict.label }} +
+
+
+ + {{ dict.entries?.length }} +
+
+ + {{ 'dictionary-listing.case-sensitive' | translate }} +
+
-
-
- - {{ entity.entries?.length }} -
-
- - {{ 'dictionary-listing.case-sensitive' | translate }} -
+
+ +
+ {{ dict.rank }} +
+ +
+ +
+ +
+
+ + +
- - -
- {{ dict.rank }} -
-
- - -
- -
-
- - -
- - - -
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.scss index 93f51990a..40a7ed6bb 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.scss @@ -1,39 +1,36 @@ -:host { - ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - flex-direction: row; - align-items: center; - justify-content: flex-start; +.cell { + flex-direction: row !important; + align-items: center; - &.center { - justify-content: center; - } - - .color-square { - width: 16px; - height: 16px; - min-width: 16px; - margin-right: 16px; - } - - .dict-name { - z-index: 1; - max-width: 100%; - } - - .stats-subtitle { - margin-top: 4px; - } + &:not(.center) { + justify-content: flex-start !important; } - .right-container { - display: flex; - width: 353px; - min-width: 353px; - justify-content: center; - padding: 50px 20px 30px 20px; + .color-square { + width: 16px; + height: 16px; + min-width: 16px; + margin-right: 16px; + } - &.has-scrollbar:hover { - padding-right: 9px; - } + .dict-name { + z-index: 1; + max-width: 100%; + } + + .stats-subtitle { + margin-top: 4px; + } +} + +.right-container { + display: flex; + width: 353px; + min-width: 353px; + justify-content: center; + padding: 50px 20px 30px 20px; + + &.has-scrollbar:hover { + padding-right: 9px; } } diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts index 6d9ed2807..cb7270617 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, forwardRef, Injector, OnInit } from '@angular/core'; import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { AppStateService } from '@state/app-state.service'; import { catchError, defaultIfEmpty, tap } from 'rxjs/operators'; @@ -36,11 +36,12 @@ export class DictionaryListingScreenComponent extends ListingComponent[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('dictionary-listing.table-col-names.type'), sortByKey: 'searchKey', width: '2fr' }, + { label: _('dictionary-listing.table-col-names.order-of-importance'), sortByKey: 'rank', class: 'flex-center' }, + { label: _('dictionary-listing.table-col-names.hint-redaction'), class: 'flex-center' } + ]; chartData: DoughnutChartConfig[] = []; - @ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef; - @ViewChild('rankTemplate', { static: true }) rankTemplate: TemplateRef; - @ViewChild('iconTemplate', { static: true }) iconTemplate: TemplateRef; constructor( protected readonly _injector: Injector, @@ -58,7 +59,6 @@ export class DictionaryListingScreenComponent extends ListingComponent !d.virtual); diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.html index 6bf3cf816..cd4d5bc2a 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.html @@ -61,37 +61,33 @@
- -
-
- {{ dossierTemplate.name }} -
-
-
- - {{ 'dossier-templates-listing.dictionaries' | translate: { length: dossierTemplate.dictionariesCount } }} + +
+
+
+ {{ dossierTemplate.name }} +
+
+
+ + {{ 'dossier-templates-listing.dictionaries' | translate: { length: dossierTemplate.dictionariesCount } }} +
-
-
- -
- -
-
- - -
- {{ dossierTemplate.dateAdded | date: 'd MMM. yyyy' }} -
-
- - -
-
- {{ dossierTemplate.dateModified | date: 'd MMM. yyyy' }} +
+ +
+ +
+ {{ dossierTemplate.dateAdded | date: 'd MMM. yyyy' }} +
+ +
+
+ {{ dossierTemplate.dateModified | date: 'd MMM. yyyy' }} +
+
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.scss index 009e0d63a..232ac1f4c 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.scss @@ -1,15 +1,7 @@ -:host { - ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - .stats-subtitle { - margin-top: 4px; - } - - .table-item-title { - max-width: 100%; - } - } - - .page-header .actions > *:not(:last-child) { - margin-right: 16px; - } +.stats-subtitle { + margin-top: 4px; +} + +.table-item-title { + max-width: 100%; } diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.ts index f7a9989ca..ef14dcb22 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-template-listing/dossier-templates-listing-screen.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; import { UserPreferenceService } from '@services/user-preference.service'; import { AdminDialogService } from '../../services/admin-dialog.service'; @@ -31,11 +31,12 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent[]; - @ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef; - @ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef; - @ViewChild('dateAddedTemplate', { static: true }) dateAddedTemplate: TemplateRef; - @ViewChild('dateModifiedTemplate', { static: true }) dateModifiedTemplate: TemplateRef; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('dossier-templates-listing.table-col-names.name'), sortByKey: 'searchKey' }, + { label: _('dossier-templates-listing.table-col-names.created-by'), class: 'user-column' }, + { label: _('dossier-templates-listing.table-col-names.created-on'), sortByKey: 'dateAdded' }, + { label: _('dossier-templates-listing.table-col-names.modified-on'), sortByKey: 'dateModified' } + ]; constructor( protected readonly _injector: Injector, @@ -52,7 +53,6 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent d.dossierTemplateId)) { await this._dossierTemplateControllerService .deleteDossierTemplates(templateIds) diff --git a/libs/common-ui b/libs/common-ui index 551db967e..c0b445b06 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 551db967e2d747932579464f00e4e497c91dc628 +Subproject commit c0b445b06ed86a07c4b9aa1803b29b9384021d54 From 6c6a42b68b9c7c55ae48c74ce30ba75a9c7aec76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Fri, 1 Oct 2021 20:44:18 +0300 Subject: [PATCH 4/4] Refactor complete --- .../active-fields-listing.component.html | 89 +++++++------- .../active-fields-listing.component.scss | 28 ++--- .../active-fields-listing.component.ts | 80 ++++--------- .../screens/audit/audit-screen.component.html | 34 +++--- .../screens/audit/audit-screen.component.ts | 36 ++---- ...r-attributes-listing-screen.component.html | 63 +++++----- ...ier-attributes-listing-screen.component.ts | 33 +----- ...e-attributes-listing-screen.component.html | 109 ++++++++--------- ...e-attributes-listing-screen.component.scss | 6 +- ...ile-attributes-listing-screen.component.ts | 75 +++--------- .../user-listing-screen.component.html | 67 +++++------ .../user-listing-screen.component.scss | 4 - .../user-listing-screen.component.ts | 23 ++-- ...t-dossier-deleted-documents.component.html | 72 ++++++----- ...t-dossier-deleted-documents.component.scss | 8 +- ...dit-dossier-deleted-documents.component.ts | 40 ++----- .../search-screen.component.html | 112 +++++++++--------- .../search-screen.component.scss | 10 +- .../search-screen/search-screen.component.ts | 29 ++--- apps/red-ui/src/assets/i18n/en.json | 2 +- 20 files changed, 356 insertions(+), 564 deletions(-) diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html index fba752dd7..dd8dbc600 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html @@ -1,5 +1,4 @@ - -
- -
-
+ +
+
+ +
- -
- -
-
+
+
+ + + + {{ translations[type] | translate }} + + + +
+
- -
-
- - - - {{ translations[type] | translate }} - - - +
+ +
+ +
+ +
+ +
+
+ +
- - -
- -
-
- - -
- -
-
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss index d87b9c85d..053574610 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss @@ -30,19 +30,19 @@ cdk-virtual-scroll-viewport { height: calc(100% - 80px) !important; - - .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - iqser-editable-input:not(.editing) { - padding-left: 12px; - } - - iqser-editable-input::ng-deep .edit-button { - display: none; - } - - &:hover iqser-editable-input::ng-deep .edit-button { - display: block; - } - } + } +} + +.cell { + iqser-editable-input:not(.editing) { + padding-left: 12px; + } + + iqser-editable-input::ng-deep .edit-button { + display: none; + } + + &:hover iqser-editable-input::ng-deep .edit-button { + display: block; } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts index b267c26a1..faa19b678 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts @@ -1,16 +1,4 @@ -import { - Component, - EventEmitter, - forwardRef, - Injector, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, - TemplateRef, - ViewChild -} from '@angular/core'; +import { Component, EventEmitter, forwardRef, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Field } from '../file-attributes-csv-import-dialog.component'; import { FileAttributeConfigTypes } from '@redaction/red-ui-http'; import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui'; @@ -23,16 +11,35 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; styleUrls: ['./active-fields-listing.component.scss'], providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => ActiveFieldsListingComponent) }] }) -export class ActiveFieldsListingComponent extends ListingComponent implements OnChanges, OnInit { +export class ActiveFieldsListingComponent extends ListingComponent implements OnChanges { readonly circleButtonTypes = CircleButtonTypes; readonly translations = fileAttributeTypesTranslations; readonly tableHeaderLabel = _('file-attributes-csv-import.table-header.title'); - tableColumnConfigs: TableColumnConfig[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { + label: _('file-attributes-csv-import.table-col-names.name'), + class: 'name', + width: 'minmax(0, 350px)' + }, + { + label: _('file-attributes-csv-import.table-col-names.type'), + width: '150px' + }, + { + label: _('file-attributes-csv-import.table-col-names.read-only'), + class: 'flex-center', + leftIcon: 'red:read-only', + width: 'auto' + }, + { + label: _('file-attributes-csv-import.table-col-names.primary'), + class: 'flex-center', + rightIcon: 'red:status-info', + rightIconTooltip: _('file-attributes-csv-import.table-col-names.primary-info-tooltip'), + width: 'auto' + } + ]; readonly typeOptions = Object.keys(FileAttributeConfigTypes); - @ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef; - @ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef; - @ViewChild('readonlyTemplate', { static: true }) readonlyTemplate: TemplateRef; - @ViewChild('primaryTemplate', { static: true }) primaryTemplate: TemplateRef; @Input() entities: Field[]; @Output() readonly entitiesChange = new EventEmitter(); @Output() readonly setHoveredColumn = new EventEmitter(); @@ -42,10 +49,6 @@ export class ActiveFieldsListingComponent extends ListingComponent implem super(_injector); } - ngOnInit(): void { - this._configureTableColumns(); - } - ngOnChanges(changes: SimpleChanges): void { if (changes.entities) { this.entitiesService.setEntities(this.entities); @@ -80,35 +83,4 @@ export class ActiveFieldsListingComponent extends ListingComponent implem itemMouseEnterFn = (field: Field) => this.setHoveredColumn.emit(field.csvColumn); itemMouseLeaveFn = () => this.setHoveredColumn.emit(); - - private _configureTableColumns() { - this.tableColumnConfigs = [ - { - label: _('file-attributes-csv-import.table-col-names.name'), - class: 'name', - template: this.labelTemplate, - width: 'minmax(0, 350px)' - }, - { - label: _('file-attributes-csv-import.table-col-names.type'), - template: this.typeTemplate, - width: '150px' - }, - { - label: _('file-attributes-csv-import.table-col-names.read-only'), - class: 'flex-center', - leftIcon: 'red:read-only', - template: this.readonlyTemplate, - width: 'auto' - }, - { - label: _('file-attributes-csv-import.table-col-names.primary'), - class: 'flex-center', - rightIcon: 'red:status-info', - rightIconTooltip: _('file-attributes-csv-import.table-col-names.primary-info-tooltip'), - template: this.primaryTemplate, - width: 'auto' - } - ]; - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html index 9311d73f9..0f97a58ed 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html @@ -97,24 +97,20 @@
- -
- {{ log.message }} + +
+
+ {{ log.message }} +
+ +
+ {{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }} +
+ +
+ +
+ +
- - -
- {{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }} -
-
- - -
- -
-
- - -
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts index 635d4b141..ad2c4c828 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, forwardRef, Injector, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { AuditControllerService, AuditResponse, AuditSearchRequest, IAudit } from '@redaction/red-ui-http'; import { Moment } from 'moment'; @@ -22,15 +22,16 @@ export class AuditScreenComponent extends ListingComponent implements OnD readonly ALL_USERS = _('audit-screen.all-users'); readonly translations = auditCategoriesTranslations; readonly currentUser = this._userService.currentUser; - @ViewChild('messageTemplate', { static: true }) messageTemplate: TemplateRef; - @ViewChild('dateTemplate', { static: true }) dateTemplate: TemplateRef; - @ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef; - @ViewChild('categoryTemplate', { static: true }) categoryTemplate: TemplateRef; filterForm: FormGroup; categories: string[] = []; userIds: Set; logs: AuditResponse; - tableColumnConfigs: TableColumnConfig[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('audit-screen.table-col-names.message') }, + { label: _('audit-screen.table-col-names.date') }, + { label: _('audit-screen.table-col-names.user'), class: 'user-column' }, + { label: _('audit-screen.table-col-names.category') } + ]; readonly tableHeaderLabel = _('audit-screen.table-header.title'); private _previousFrom: Moment; private _previousTo: Moment; @@ -69,32 +70,9 @@ export class AuditScreenComponent extends ListingComponent implements OnD } async ngOnInit() { - this._configureTableColumns(); await this._fetchData(); } - private _configureTableColumns() { - this.tableColumnConfigs = [ - { - label: _('audit-screen.table-col-names.message'), - template: this.messageTemplate - }, - { - label: _('audit-screen.table-col-names.date'), - template: this.dateTemplate - }, - { - label: _('audit-screen.table-col-names.user'), - class: 'user-column', - template: this.userTemplate - }, - { - label: _('audit-screen.table-col-names.category'), - template: this.categoryTemplate - } - ]; - } - private _updateDateFilters(value): boolean { if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) { return true; diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.html index 9635022f1..def1dfab3 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.html @@ -22,7 +22,6 @@
- -
- + +
+
+ {{ attribute.label }} +
- -
-
- - -
- {{ attribute.label }} -
-
- - -
- {{ attribute.placeholder }} -
-
- - -
- {{ translations[attribute.type] | translate }} +
+ {{ attribute.placeholder }} +
+ +
+ {{ translations[attribute.type] | translate }} +
+ +
+
+ + + +
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.ts index 09cd2466f..d087bb15a 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, forwardRef, Injector, OnInit } from '@angular/core'; import { CircleButtonTypes, DefaultListingServices, @@ -31,10 +31,11 @@ export class DossierAttributesListingScreenComponent extends ListingComponent[]; - @ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef; - @ViewChild('placeholderTemplate', { static: true }) placeholderTemplate: TemplateRef; - @ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('dossier-attributes-listing.table-col-names.label'), sortByKey: 'label', width: '2fr' }, + { label: _('dossier-attributes-listing.table-col-names.placeholder'), width: '2fr' }, + { label: _('dossier-attributes-listing.table-col-names.type'), sortByKey: 'type' } + ]; constructor( protected readonly _injector: Injector, @@ -50,7 +51,6 @@ export class DossierAttributesListingScreenComponent extends ListingComponent - -
- - -
-
- - -
- {{ attribute.label }} -
-
- - -
-
- - -
- -
-
- - -
- {{ attribute.csvColumnHeader }} -
-
- - -
- -
-
- - -
- -
-
- - -
- + +
+
+ {{ attribute.label }} +
+ +
+ +
+ +
+ +
+ {{ attribute.csvColumnHeader }} +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.scss index 6b4d12879..14a44a51a 100644 --- a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.scss @@ -1,10 +1,6 @@ @use 'common-mixins'; -:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - &.center { - align-items: center; - } - +.cell { &.label span { @include common-mixins.line-clamp(1); } diff --git a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts index 70f0e6f2d..0a277efda 100644 --- a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts @@ -1,14 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - forwardRef, - Injector, - OnDestroy, - OnInit, - TemplateRef, - ViewChild -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, forwardRef, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { FileAttributesConfig, IFileAttributeConfig } from '@redaction/red-ui-http'; import { AppStateService } from '@state/app-state.service'; import { ActivatedRoute } from '@angular/router'; @@ -42,14 +32,20 @@ export class FileAttributesListingScreenComponent extends ListingComponent[]; - @ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef; - @ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef; - @ViewChild('readonlyTemplate', { static: true }) readonlyTemplate: TemplateRef; - @ViewChild('csvColumnHeaderTemplate', { static: true }) csvColumnHeaderTemplate: TemplateRef; - @ViewChild('filterableTemplate', { static: true }) filterableTemplate: TemplateRef; - @ViewChild('displayedInFileListTemplate', { static: true }) displayedInFileListTemplate: TemplateRef; - @ViewChild('primaryAttributeTemplate', { static: true }) primaryAttributeTemplate: TemplateRef; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('file-attributes-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' }, + { label: _('file-attributes-listing.table-col-names.type'), sortByKey: 'type' }, + { label: _('file-attributes-listing.table-col-names.read-only'), sortByKey: 'editable', class: 'flex-center' }, + { label: _('file-attributes-listing.table-col-names.csv-column') }, + { label: _('file-attributes-listing.table-col-names.filterable'), class: 'flex-center' }, + { label: _('file-attributes-listing.table-col-names.displayed-in-file-list'), class: 'flex-center' }, + { + label: _('file-attributes-listing.table-col-names.primary'), + class: 'flex-center', + rightIcon: 'red:status-info', + rightIconTooltip: _('file-attributes-listing.table-col-names.primary-info-tooltip') + } + ]; private _existingConfiguration: FileAttributesConfig; @ViewChild('fileInput') private _fileInput: ElementRef; @@ -67,7 +63,6 @@ export class FileAttributesListingScreenComponent extends ListingComponent
- -
- -
-
- - -
{{ user.email || '-' }}
-
- - -
- -
-
- - -
{{ getDisplayRoles(user) }}
-
- - -
- - + +
+
+ +
+ +
{{ user.email || '-' }}
+ +
+ +
+ +
{{ getDisplayRoles(user) }}
+ +
+
+ + +
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss index 5a09cdf01..097d651bc 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss @@ -1,7 +1,3 @@ -:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell.center { - align-items: center; -} - .right-container { display: flex; width: 353px; diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts index fa92bdd2f..ff0e8f244 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Injector, OnInit, QueryList, TemplateRef, ViewChild, ViewChildren } from '@angular/core'; +import { Component, forwardRef, Injector, OnInit, QueryList, ViewChildren } from '@angular/core'; import { UserService } from '@services/user.service'; import { UserControllerService } from '@redaction/red-ui-http'; import { AdminDialogService } from '../../services/admin-dialog.service'; @@ -32,13 +32,14 @@ export class UserListingScreenComponent extends ListingComponent implement readonly currentUser = this.userService.currentUser; readonly canDeleteSelected$ = this._canDeleteSelected$; readonly tableHeaderLabel = _('user-listing.table-header.title'); - tableColumnConfigs: TableColumnConfig[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('user-listing.table-col-names.name'), width: '2fr' }, + { label: _('user-listing.table-col-names.email') }, + { label: _('user-listing.table-col-names.active'), class: 'flex-center' }, + { label: _('user-listing.table-col-names.roles') } + ]; collapsedDetails = false; chartData: DoughnutChartConfig[] = []; - @ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef; - @ViewChild('emailTemplate', { static: true }) emailTemplate: TemplateRef; - @ViewChild('activeTemplate', { static: true }) activeTemplate: TemplateRef; - @ViewChild('rolesTemplate', { static: true }) rolesTemplate: TemplateRef; @ViewChildren(InitialsAvatarComponent) private readonly _avatars: QueryList; @@ -60,7 +61,6 @@ export class UserListingScreenComponent extends ListingComponent implement } async ngOnInit() { - this._configureTableColumns(); await this._loadData(); } @@ -96,15 +96,6 @@ export class UserListingScreenComponent extends ListingComponent implement this.openDeleteUsersDialog(this.entitiesService.all.filter(u => this.isSelected(u))); } - private _configureTableColumns() { - this.tableColumnConfigs = [ - { label: _('user-listing.table-col-names.name'), template: this.nameTemplate, width: '2fr' }, - { label: _('user-listing.table-col-names.email'), template: this.emailTemplate }, - { label: _('user-listing.table-col-names.active'), class: 'flex-center', template: this.activeTemplate }, - { label: _('user-listing.table-col-names.roles'), template: this.rolesTemplate } - ]; - } - private async _loadData() { this.entitiesService.setEntities(await this.userService.loadAllUsers()); await this.userService.loadAllUsers(); diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html index 019278fc4..81c1f8810 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.html @@ -34,45 +34,41 @@ > - -
- {{ file.filename }} -
-
+ +
+
+ {{ file.filename }} +
- -
-
- - {{ file.numberOfPages }} -
-
-
- - -
- {{ file.softDeleted | date: 'exactDate' }} -
-
- - -
-
{{ file.restoreDate | date: 'timeFromNow' }}
-
- - - +
+
+ + {{ file.numberOfPages }} +
+
+ +
+ {{ file.softDeleted | date: 'exactDate' }} +
+ +
+
{{ file.restoreDate | date: 'timeFromNow' }}
+
+ + + +
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss index d7bf4a0af..305ab6377 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.scss @@ -9,8 +9,8 @@ :host ::ng-deep iqser-table cdk-virtual-scroll-viewport { height: calc(100% - 81px) !important; - - .cdk-virtual-scroll-content-wrapper .table-item > div.cell.filename span { - @include common-mixins.line-clamp(1); - } +} + +.cell.filename span { + @include common-mixins.line-clamp(1); } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts index 920cff75e..f408458d9 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, forwardRef, Injector, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Injector, Input, OnInit, Output } from '@angular/core'; import { EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { Dossier } from '@state/model/dossier'; import { @@ -42,14 +42,15 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.name'), width: '3fr' }, + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.pages') }, + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.deleted-on'), sortByKey: 'softDeleted', width: '2fr' }, + { label: _('edit-dossier-dialog.deleted-documents.table-col-names.time-to-restore'), sortByKey: 'softDeleted', width: '2fr' } + ]; readonly tableHeaderLabel = _('edit-dossier-dialog.deleted-documents.table-header.label'); readonly circleButtonTypes = CircleButtonTypes; readonly deleteRetentionHours = this._configService.values.DELETE_RETENTION_HOURS; - @ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef; - @ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef; - @ViewChild('deletedDateTemplate', { static: true }) deletedDateTemplate: TemplateRef; - @ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef; constructor( protected readonly _injector: Injector, @@ -89,7 +90,6 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent !file.canRestore; - private _configureTableColumns() { - this.tableColumnConfigs = [ - { - label: _('edit-dossier-dialog.deleted-documents.table-col-names.name'), - template: this.filenameTemplate, - width: '3fr' - }, - { - label: _('edit-dossier-dialog.deleted-documents.table-col-names.pages'), - template: this.pagesTemplate - }, - { - label: _('edit-dossier-dialog.deleted-documents.table-col-names.deleted-on'), - template: this.deletedDateTemplate, - sortByKey: 'softDeleted', - width: '2fr' - }, - { - label: _('edit-dossier-dialog.deleted-documents.table-col-names.time-to-restore'), - template: this.restoreDateTemplate, - sortByKey: 'softDeleted', - width: '2fr' - } - ]; - } - private async _restore(files: FileListItem[]): Promise { const fileIds = files.map(f => f.fileId); await this._fileManagementController.restoreFiles(fileIds, this.dossier.id).toPromise(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html index f8c54b661..01c25929a 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.html @@ -21,66 +21,64 @@
- -
-
- - {{ item.filename }} -
- - -
- + +
+
+
+ + {{ item.filename }}
-
- + + +
+ +
+
+ +
+
+ +
+ + {{ 'search-screen.missing' | translate }}: {{ term }}. {{ 'search-screen.must-contain' | translate }}: +  {{ term }} +
- +
-
- - {{ 'search-screen.missing' | translate }}: {{ term }}. {{ 'search-screen.must-contain' | translate }}: -  {{ term }} - -
-
- - - -
- -
-
- - -
- {{ item.dossierName }} -
-
- - -
-
- - {{ item.numberOfPages }} +
+ +
+ +
+ {{ item.dossierName }} +
+ +
+
+ + {{ item.numberOfPages }} +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.scss b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.scss index da9d39c30..25f846e32 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.scss +++ b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.scss @@ -1,11 +1,9 @@ @use 'common-mixins'; -:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell { - .highlights { - @include common-mixins.line-clamp(1); +.cell .highlights::ng-deep { + @include common-mixins.line-clamp(1); - em { - background-color: #fffcc4; - } + em { + background-color: #fffcc4; } } diff --git a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts index 7ffcb6273..bba7db62b 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/search-screen/search-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, forwardRef, Injector, OnDestroy } from '@angular/core'; import { DefaultListingServices, IListable, @@ -40,17 +40,17 @@ interface SearchInput { styleUrls: ['./search-screen.component.scss'], providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => SearchScreenComponent) }] }) -export class SearchScreenComponent extends ListingComponent implements OnDestroy, OnInit { +export class SearchScreenComponent extends ListingComponent implements OnDestroy { readonly fileStatusTranslations = fileStatusTranslations; readonly searchPositions = SearchPositions; - @ViewChild('filenameTemplate', { static: true }) readonly filenameTemplate: TemplateRef; - @ViewChild('statusTemplate', { static: true }) readonly statusTemplate: TemplateRef; - @ViewChild('dossierTemplate', { static: true }) readonly dossierTemplate: TemplateRef; - @ViewChild('pagesTemplate', { static: true }) readonly pagesTemplate: TemplateRef; - readonly tableHeaderLabel = _('search-screen.table-header'); - tableColumnConfigs: TableColumnConfig[]; + readonly tableColumnConfigs: TableColumnConfig[] = [ + { label: _('search-screen.cols.document'), width: '2fr' }, + { label: _('search-screen.cols.status') }, + { label: _('search-screen.cols.dossier') }, + { label: _('search-screen.cols.pages'), width: 'auto' } + ]; readonly search$ = new BehaviorSubject(null); readonly searchResults$: Observable = this.search$.asObservable().pipe( switchMap(query => this._search(query)), @@ -108,19 +108,6 @@ export class SearchScreenComponent extends ListingComponent implements this._router.navigate([], { queryParams }).then(); } - ngOnInit(): void { - this._configureTableColumns(); - } - - private _configureTableColumns() { - this.tableColumnConfigs = [ - { label: _('search-screen.cols.document'), template: this.filenameTemplate, width: '2fr' }, - { label: _('search-screen.cols.status'), template: this.statusTemplate }, - { label: _('search-screen.cols.dossier'), template: this.dossierTemplate }, - { label: _('search-screen.cols.pages'), template: this.pagesTemplate, width: 'auto' } - ]; - } - private _search(searchInput: SearchInput): Observable { return this._searchControllerService.search({ dossierIds: [...searchInput.dossierIds], diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index ebc96a5b5..3bc887fcb 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -1375,7 +1375,7 @@ "table-header": "{length} search {length, plural, one{result} other{results}}" }, "search": { - "entire-platform": "accross all dossiers", + "entire-platform": "across all dossiers", "placeholder": "Search documents...", "this-dossier": "in this dossier" },