Merge branch 'RED-3313'

This commit is contained in:
Adina Țeudan 2022-03-09 13:48:47 +02:00
commit 953fcbb7ce
158 changed files with 1761 additions and 1214 deletions

View File

@ -6,8 +6,9 @@ import { BaseScreenComponent } from '@components/base-screen/base-screen.compone
import { RouteReuseStrategy, RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
import { DossiersGuard } from '@guards/dossiers.guard';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { DossiersGuard } from '@guards/dossiers.guard';
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
const routes: Routes = [
{
@ -38,6 +39,18 @@ const routes: Routes = [
data: {
routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, DossiersGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
},
},
{
path: 'main/archive',
component: BaseScreenComponent,
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, DossiersGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
},
},
{
@ -54,6 +67,16 @@ const routes: Routes = [
routeGuards: [AuthGuard, RedRoleGuard],
},
},
{
path: 'main/search',
component: BaseScreenComponent,
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, DossiersGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
{
path: '**',
redirectTo: 'main/dossiers',

View File

@ -20,7 +20,7 @@ import { AppRoutingModule } from './app-routing.module';
import { SharedModule } from '@shared/shared.module';
import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module';
import { PlatformLocation } from '@angular/common';
import { BASE_HREF } from './tokens';
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE, BASE_HREF } from './tokens';
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { GlobalErrorHandler } from '@utils/global-error-handler.service';
import { REDMissingTranslationHandler } from '@utils/missing-translations-handler';
@ -44,6 +44,8 @@ import { GeneralSettingsService } from '@services/general-settings.service';
import { BreadcrumbsComponent } from '@components/breadcrumbs/breadcrumbs.component';
import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader {
return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`);
@ -144,6 +146,14 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
useFactory: (configService: ConfigService) => configService.values.MAX_RETRIES_ON_SERVER_ERROR,
deps: [ConfigService],
},
{
provide: ACTIVE_DOSSIERS_SERVICE,
useExisting: ActiveDossiersService,
},
{
provide: ARCHIVED_DOSSIERS_SERVICE,
useExisting: ArchivedDossiersService,
},
DatePipe,
],
bootstrap: [AppComponent],

View File

@ -3,7 +3,7 @@
<div class="top-bar">
<div *ngIf="!currentUser.isUser" class="menu-placeholder"></div>
<div *ngIf="currentUser.isUser" class="menu flex-2 visible-lg breadcrumbs-container">
<div *ngIf="currentUser.isUser" class="flex-2 visible-lg breadcrumbs-container">
<redaction-breadcrumbs></redaction-breadcrumbs>
</div>
@ -14,7 +14,7 @@
<div class="app-name">{{ titleService.getTitle() }}</div>
</div>
<div class="menu right flex-2">
<div class="actions flex-2">
<div class="buttons">
<redaction-spotlight-search
*ngIf="(isSearchScreen$ | async) === false && (currentUser.isUser || currentUser.isManager)"

View File

@ -1,3 +1,9 @@
redaction-spotlight-search {
margin-right: 16px !important;
}
.actions {
display: flex;
align-items: center;
justify-content: flex-end;
}

View File

@ -19,7 +19,7 @@ interface MenuItem {
}
const isNavigationStart = event => event instanceof NavigationStart;
const isSearchScreen = url => url.includes('/main/dossiers') && url.includes('/search');
const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
@Component({
templateUrl: './base-screen.component.html',
@ -57,7 +57,12 @@ export class BaseScreenComponent {
action: (query): void => this._searchThisDossier(query),
},
{
text: this._translateService.instant('search.entire-platform'),
text: this._translateService.instant('search.active-dossiers'),
icon: 'red:enter',
action: (query): void => this._search(query, [], true),
},
{
text: this._translateService.instant('search.all-dossiers'),
icon: 'red:enter',
action: (query): void => this._search(query, []),
},
@ -80,12 +85,12 @@ export class BaseScreenComponent {
) {}
private get _hideSearchThisDossier() {
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.routerLink;
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.options?.routerLink;
if (!routerLink) {
return true;
}
const isDossierOverview = routerLink.includes('dossiers') && routerLink.length === 3;
const isDossierOverview = (routerLink.includes('dossiers') || routerLink.includes('archive')) && routerLink.length === 3;
return !isDossierOverview;
}
@ -93,13 +98,13 @@ export class BaseScreenComponent {
return item.name;
}
private _search(query: string, dossierIds: string[]) {
const queryParams = { query, dossierIds: dossierIds.join(',') };
this._router.navigate(['main/dossiers/search'], { queryParams }).then();
private _search(query: string, dossierIds: string[], onlyActive = false) {
const queryParams = { query, dossierIds: dossierIds.join(','), onlyActive };
this._router.navigate(['main/search'], { queryParams }).then();
}
private _searchThisDossier(query: string) {
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.routerLink;
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.options?.routerLink;
if (!routerLink) {
return this._search(query, []);
}

View File

@ -9,16 +9,36 @@
<mat-icon *ngIf="!first" svgIcon="iqser:arrow-right"></mat-icon>
<a
[class.clamp]="breadcrumb.clamp"
*ngIf="is(breadcrumb, 'text')"
[class.clamp]="breadcrumb.options.clamp"
[id]="first ? 'navigateToActiveDossiers' : ''"
[matTooltip]="breadcrumb.clamp && (breadcrumb.name$ | async)"
[routerLinkActiveOptions]="breadcrumb.routerLinkActiveOptions || { exact: false }"
[routerLink]="breadcrumb.routerLink"
[matTooltip]="breadcrumb.options.clamp && (breadcrumb.name$ | async)"
[routerLinkActiveOptions]="breadcrumb.options.routerLinkActiveOptions || { exact: false }"
[routerLink]="breadcrumb.options.routerLink"
class="breadcrumb"
routerLinkActive="active"
>
{{ breadcrumb.name$ | async }}
</a>
<ng-container *ngIf="is(breadcrumb, 'dropdown')">
<button [matMenuTriggerFor]="dropdownMenu" class="dropdown-breadcrumb" mat-button>
<span>{{ breadcrumb.name$ | async }}</span>
<mat-icon svgIcon="iqser:arrow-down"></mat-icon>
</button>
<mat-menu #dropdownMenu="matMenu" class="padding-bottom-8">
<a
*ngFor="let option of breadcrumb.options.options"
[routerLink]="option.options.routerLink"
mat-menu-item
routerLinkActive="active"
>
{{ option.name$ | async }}
<mat-icon class="checkmark" svgIcon="iqser:check"></mat-icon>
</a>
</mat-menu>
</ng-container>
</ng-container>
</ng-template>
</div>

View File

@ -1,10 +1,14 @@
:host {
display: flex;
align-items: center;
overflow: hidden;
overflow: visible;
height: 100%;
> .breadcrumbs > *:not(:last-child) {
margin-right: 6px;
}
.dropdown-breadcrumb {
margin-left: -16px;
}
}

View File

@ -1,12 +1,17 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { BreadcrumbsService } from '@services/breadcrumbs.service';
import { Component } from '@angular/core';
import { Breadcrumb, BreadcrumbDisplayType, BreadcrumbsService } from '@services/breadcrumbs.service';
/** Removed on push change detection because [aria-expanded] doesn't seem to work correctly on the dropdown button */
@Component({
selector: 'redaction-breadcrumbs',
templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BreadcrumbsComponent {
constructor(readonly breadcrumbsService: BreadcrumbsService) {}
is(breadcrumb: Breadcrumb, type: BreadcrumbDisplayType): boolean {
return breadcrumb.type === type;
}
}

View File

@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@shared/pipes/date.pipe';
import { UserService } from '@services/user.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { NotificationsService } from '@services/notifications.service';
import { Notification } from '@red/domain';
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
@ -33,7 +33,7 @@ export class NotificationsComponent extends AutoUnsubscribe implements OnInit {
private readonly _translateService: TranslateService,
private readonly _userService: UserService,
private readonly _notificationsService: NotificationsService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _datePipe: DatePipe,
) {
super();

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DICTIONARY_TYPE, DOSSIER_TEMPLATE_ID } from '@utils/constants';
import { DictionariesMapService } from '../services/entity-services/dictionaries-map.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
@Injectable({ providedIn: 'root' })
export class DictionaryExistsGuard implements CanActivate {

View File

@ -1,15 +1,15 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector, ProviderToken } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { FilesService } from '@services/entity-services/files.service';
import { firstValueFrom } from 'rxjs';
import { DOSSIER_ID } from '@utils/constants';
import { DossiersService } from '../services/dossiers/dossiers.service';
@Injectable({ providedIn: 'root' })
export class DossierFilesGuard implements CanActivate {
constructor(
private readonly _dossiersService: DossiersService,
private readonly _injector: Injector,
private readonly _filesMapService: FilesMapService,
private readonly _filesService: FilesService,
private readonly _router: Router,
@ -17,14 +17,16 @@ export class DossierFilesGuard implements CanActivate {
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const dossierId = route.paramMap.get(DOSSIER_ID);
const token: ProviderToken<DossiersService> = route.data.dossiersService;
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
if (!this._dossiersService.has(dossierId)) {
await this._router.navigate(['/main', 'dossiers']);
if (!dossiersService.has(dossierId)) {
await this._router.navigate(['/main', dossiersService.routerPath]);
return false;
}
if (!this._filesMapService.has(dossierId)) {
await firstValueFrom(this._filesService.loadAll(dossierId));
await firstValueFrom(this._filesService.loadAll(dossierId, dossiersService.routerPath));
}
return true;
}

View File

@ -1,14 +1,29 @@
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { firstValueFrom } from 'rxjs';
import { Injectable, Injector, ProviderToken } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
import { firstValueFrom, forkJoin } from 'rxjs';
import { take } from 'rxjs/operators';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
@Injectable({ providedIn: 'root' })
export class DossiersGuard implements CanActivate {
constructor(private readonly _dossiersService: DossiersService) {}
constructor(
private readonly _injector: Injector,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
) {}
async canActivate(): Promise<boolean> {
await firstValueFrom(this._dossiersService.loadAll());
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const token: ProviderToken<DossiersService> = route.data.dossiersService;
if (token) {
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
await firstValueFrom(dossiersService.loadAll());
} else {
const services = [this._archivedDossiersService, this._activeDossiersService];
const loading$ = forkJoin(services.map(service => service.loadAll().pipe(take(1))));
await firstValueFrom(loading$);
}
return true;
}
}

View File

@ -1,22 +1,25 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector, ProviderToken } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
import { DossiersService } from '../services/dossiers/dossiers.service';
@Injectable({ providedIn: 'root' })
export class FilePreviewGuard implements CanActivate {
constructor(
private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService,
private readonly _router: Router,
private readonly _injector: Injector,
) {}
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const token: ProviderToken<DossiersService> = route.data.dossiersService;
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
const dossierId = route.paramMap.get(DOSSIER_ID);
const fileId = route.paramMap.get(FILE_ID);
const dossier = this._dossiersService.find(dossierId);
const dossier = dossiersService.find(dossierId);
if (!this._filesMapService.get(dossierId, fileId)) {
await this._router.navigate([dossier.routerLink]);

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { TrashDossiersService } from '@services/entity-services/trash-dossiers.service';
@Injectable({ providedIn: 'root' })
export class TrashDossiersGuard implements CanActivate {
constructor(private readonly _trashDossiersService: TrashDossiersService) {}
async canActivate(): Promise<boolean> {
await firstValueFrom(this._trashDossiersService.loadAll());
return true;
}
}

View File

@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }];

View File

@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }];

View File

@ -17,11 +17,12 @@ import { TrashScreenComponent } from './screens/trash/trash-screen.component';
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
import { DossierTemplatesGuard } from '../../guards/dossier-templates.guard';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { DICTIONARY_TYPE, DOSSIER_TEMPLATE_ID } from '@utils/constants';
import { DossierTemplateExistsGuard } from '../../guards/dossier-template-exists.guard';
import { DictionaryExistsGuard } from '../../guards/dictionary-exists.guard';
import { DossierTemplateExistsGuard } from '@guards/dossier-template-exists.guard';
import { DictionaryExistsGuard } from '@guards/dictionary-exists.guard';
import { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component';
import { TrashDossiersGuard } from '@guards/trash-dossiers.guard';
const routes: Routes = [
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
@ -199,7 +200,7 @@ const routes: Routes = [
component: TrashScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard],
routeGuards: [AuthGuard, RedRoleGuard, TrashDossiersGuard],
requiredRoles: ['RED_MANAGER'],
},
},

View File

@ -15,7 +15,6 @@ import { ColorPickerModule } from 'ngx-color-picker';
import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
import { AddEditDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
import { AddEditDictionaryDialogComponent } from './dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
import { ConfirmDeleteAttributeDialogComponent } from './dialogs/confirm-delete-attribute-dialog/confirm-delete-attribute-dialog.component';
import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component';
import { ComboChartComponent, ComboSeriesVerticalComponent } from './components/combo-chart';
import { NgxChartsModule } from '@swimlane/ngx-charts';
@ -24,7 +23,6 @@ import { GeneralConfigScreenComponent } from './screens/general-config/general-c
import { SmtpAuthDialogComponent } from './dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
import { UsersStatsComponent } from './components/users-stats/users-stats.component';
import { ConfirmDeleteUsersDialogComponent } from './dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component';
import { FileAttributesCsvImportDialogComponent } from './dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component';
import { ActiveFieldsListingComponent } from './dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component';
import { AdminSideNavComponent } from './admin-side-nav/admin-side-nav.component';
@ -55,11 +53,9 @@ const dialogs = [
AddEditDossierTemplateDialogComponent,
AddEditDictionaryDialogComponent,
AddEditFileAttributeDialogComponent,
ConfirmDeleteAttributeDialogComponent,
EditColorDialogComponent,
SmtpAuthDialogComponent,
AddEditUserDialogComponent,
ConfirmDeleteUsersDialogComponent,
FileAttributesConfigurationsDialogComponent,
FileAttributesCsvImportDialogComponent,
AddEditDossierAttributeDialogComponent,

View File

@ -1,4 +1,4 @@
<div class="menu flex-2 visible-lg breadcrumbs-container">
<div class="flex-2 visible-lg breadcrumbs-container">
<ng-container *ngIf="dossierTemplate$ | async as dossierTemplate">
<a
*ngIf="root || dossierTemplate"

View File

@ -10,9 +10,8 @@ import { BaseDialogComponent } from '@iqser/common-ui';
styleUrls: ['./add-edit-user-dialog.component.scss'],
})
export class AddEditUserDialogComponent extends BaseDialogComponent {
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
resettingPassword = false;
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
constructor(
protected readonly _injector: Injector,
@ -21,14 +20,6 @@ export class AddEditUserDialogComponent extends BaseDialogComponent {
) {
super(_injector, _dialogRef, !!user);
}
X;
toggleResetPassword() {
this.resettingPassword = !this.resettingPassword;
}
async save(): Promise<void> {
await this._userDetailsComponent.save();
}
get changed(): boolean {
return this._userDetailsComponent.changed;
@ -38,6 +29,14 @@ export class AddEditUserDialogComponent extends BaseDialogComponent {
return this._userDetailsComponent.valid;
}
toggleResetPassword() {
this.resettingPassword = !this.resettingPassword;
}
async save(): Promise<void> {
await this._userDetailsComponent.save();
}
closeDialog(event) {
this._dialogRef.close(event);
}

View File

@ -4,7 +4,7 @@ import { AdminDialogService } from '../../../services/admin-dialog.service';
import { BaseFormComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { rolesTranslations } from '../../../../../translations/roles-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { User } from '@red/domain';
import { IProfileUpdateRequest, User } from '@red/domain';
import { UserService } from '@services/user.service';
import { HttpStatusCode } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
@ -83,7 +83,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges
async save() {
this._loadingService.start();
const userData = { ...this.form.getRawValue(), roles: this.activeRoles };
const userData: IProfileUpdateRequest = { ...this.form.getRawValue(), roles: this.activeRoles };
if (!this.user) {
await firstValueFrom(this.userService.create(userData))
@ -105,9 +105,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges
}
delete() {
this._dialogService.openDialog('deleteUsers', null, [this.user.id], () => {
this.closeDialog.emit(true);
});
this._dialogService.deleteUsers([this.user.id], null, () => this.closeDialog.emit(true));
}
private _getForm(): FormGroup {

View File

@ -1,39 +0,0 @@
<section class="dialog">
<div class="dialog-header heading-l">
{{ 'confirm-delete-file-attribute.title' | translate: translateArgs }}
</div>
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
<div translate="confirm-delete-file-attribute.toast-error"></div>
<a (click)="showToast = false" class="toast-close-button">
<mat-icon svgIcon="iqser:close"></mat-icon>
</a>
</div>
<div class="dialog-content">
<div class="heading" translate="confirm-delete-file-attribute.warning"></div>
<ng-container *ngFor="let checkbox of checkboxes; let idx = index">
<mat-checkbox [(ngModel)]="checkbox.value" [class.error]="!checkbox.value && showToast" color="primary">
{{ checkbox.label }}
</mat-checkbox>
</ng-container>
<ul class="templates-container flex">
<li *ngFor="let template of data.templates" class="small-label">{{ template.fileName }}</li>
</ul>
</div>
<div class="dialog-actions">
<button (click)="deleteFileAttribute()" color="primary" mat-flat-button>
{{ 'confirm-delete-file-attribute.delete' | translate: { type: type } }}
</button>
<div
(click)="this.dialogRef.close()"
[translateParams]="{ type: type }"
[translate]="'confirm-delete-file-attribute.cancel'"
class="all-caps-label cancel"
></div>
</div>
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
</section>

View File

@ -1,27 +0,0 @@
@use 'variables';
.dialog-header {
color: variables.$primary;
}
.heading {
margin-bottom: 24px;
}
mat-checkbox {
width: 100%;
&:not(:last-of-type) {
margin-bottom: 6px;
}
}
.templates-container {
padding: 0 45px;
margin-top: 0;
flex-direction: column;
li {
margin: 5px 0;
}
}

View File

@ -1,89 +0,0 @@
import { Component, Inject } from '@angular/core';
import { DossierAttributeConfig, FileAttributeConfig, IReportTemplate } from '@red/domain';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
const isFileAttributeConfig = (value: DossierAttributeConfig | FileAttributeConfig): value is FileAttributeConfig =>
value instanceof FileAttributeConfig;
interface CheckBox {
value: boolean;
label: string;
}
interface DialogData {
attribute: FileAttributeConfig | DossierAttributeConfig;
templates: IReportTemplate[];
}
@Component({
selector: 'redaction-confirm-delete-attribute-dialog',
templateUrl: './confirm-delete-attribute-dialog.component.html',
styleUrls: ['./confirm-delete-attribute-dialog.component.scss'],
})
export class ConfirmDeleteAttributeDialogComponent {
checkboxes: CheckBox[];
showToast = false;
constructor(
private readonly _translateService: TranslateService,
readonly dialogRef: MatDialogRef<ConfirmDeleteAttributeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogData,
) {
this.checkboxes = this.checkBoxConfig;
}
get checkBoxConfig(): CheckBox[] {
const checkBoxes = isFileAttributeConfig(this.data.attribute) ? this._fileAttributeCheckboxes : this._dossierAttributeCheckboxes;
if (this.data.templates.length !== 0) {
checkBoxes.push({
value: false,
label: this._translateService.instant('confirm-delete-file-attribute.impacted-report', {
count: this.data.templates.length,
}),
});
}
return checkBoxes;
}
get valid() {
return this.checkboxes.reduce((acc, currentValue) => acc && currentValue.value, true);
}
get type(): 'bulk' | 'single' {
return this.data.attribute ? 'single' : 'bulk';
}
get translateArgs() {
return {
type: this.type,
name: this.data.attribute?.label,
};
}
private get _fileAttributeCheckboxes(): CheckBox[] {
return [
{
value: false,
label: this._translateService.instant('confirm-delete-file-attribute.file-impacted-documents', { type: this.type }),
},
{ value: false, label: this._translateService.instant('confirm-delete-file-attribute.file-lost-details') },
];
}
private get _dossierAttributeCheckboxes(): CheckBox[] {
return [
{ value: false, label: this._translateService.instant('confirm-delete-file-attribute.dossier-impacted-documents') },
{ value: false, label: this._translateService.instant('confirm-delete-file-attribute.dossier-lost-details') },
];
}
deleteFileAttribute() {
if (this.valid) {
this.dialogRef.close(true);
} else {
this.showToast = true;
}
}
}

View File

@ -1,47 +0,0 @@
<section class="dialog">
<div
[translateParams]="{ usersCount: userIds.length }"
[translate]="'confirm-delete-users.title'"
class="dialog-header heading-l"
></div>
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
<div translate="confirm-delete-users.toast-error"></div>
<a (click)="showToast = false" class="toast-close-button">
<mat-icon svgIcon="iqser:close"></mat-icon>
</a>
</div>
<div class="dialog-content">
<div class="heading" translate="confirm-delete-users.warning"></div>
<mat-checkbox
*ngFor="let checkbox of checkboxes; let idx = index"
[(ngModel)]="checkbox.value"
[class.error]="!checkbox.value && showToast"
color="primary"
>
{{
checkbox.label
| translate
: {
dossiersCount: dossiersCount,
usersCount: userIds.length
}
}}
</mat-checkbox>
</div>
<div class="dialog-actions">
<button (click)="deleteUser()" color="primary" mat-flat-button>
{{ 'confirm-delete-users.delete' | translate: { usersCount: userIds.length } }}
</button>
<div
(click)="cancel()"
[translateParams]="{ usersCount: userIds.length }"
[translate]="'confirm-delete-users.cancel'"
class="all-caps-label cancel"
></div>
</div>
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
</section>

View File

@ -1,17 +0,0 @@
@use 'variables';
.dialog-header {
color: variables.$primary;
}
.heading {
margin-bottom: 24px;
}
mat-checkbox {
width: 100%;
&:not(:last-of-type) {
margin-bottom: 6px;
}
}

View File

@ -1,54 +0,0 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { List, LoadingService } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { UserService } from '@services/user.service';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'redaction-confirm-delete-users-dialog',
templateUrl: './confirm-delete-users-dialog.component.html',
styleUrls: ['./confirm-delete-users-dialog.component.scss'],
})
export class ConfirmDeleteUsersDialogComponent {
readonly checkboxes = [
{ value: false, label: _('confirm-delete-users.impacted-dossiers') },
{ value: false, label: _('confirm-delete-users.impacted-documents') },
];
showToast = false;
dossiersCount: number;
constructor(
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
@Inject(MAT_DIALOG_DATA) readonly userIds: List<string>,
readonly dialogRef: MatDialogRef<ConfirmDeleteUsersDialogComponent>,
) {
this.dossiersCount = this._dossierCount;
}
get valid() {
return this.checkboxes[0].value && this.checkboxes[1].value;
}
private get _dossierCount(): number {
return this._dossiersService.all.filter(dw => this.userIds.reduce((prev, userId) => prev || dw.memberIds.includes(userId), false))
.length;
}
async deleteUser() {
if (this.valid) {
this._loadingService.start();
await firstValueFrom(this._userService.delete(this.userIds));
this.dialogRef.close(true);
} else {
this.showToast = true;
}
}
cancel() {
this.dialogRef.close();
}
}

View File

@ -65,6 +65,12 @@
</div>
</ng-template>
<ng-template #impactedTemplates let-data="data">
<ul class="templates-container flex">
<li *ngFor="let template of data.templates" class="small-label">{{ template.fileName }}</li>
</ul>
</ng-template>
<ng-template #tableItemTemplate let-entity="entity">
<div *ngIf="cast(entity) as attribute">
<div class="cell">
@ -89,7 +95,7 @@
></iqser-circle-button>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event, attribute)"
(action)="openConfirmDeleteAttributeDialog($event, [attribute])"
[tooltip]="'dossier-attributes-listing.action.delete' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:trash"

View File

@ -0,0 +1,9 @@
.templates-container {
padding: 0 45px;
margin-top: 0;
flex-direction: column;
li {
margin: 5px 0;
}
}

View File

@ -1,4 +1,4 @@
import { Component, forwardRef, Injector, OnInit } from '@angular/core';
import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core';
import {
CircleButtonTypes,
DefaultListingServicesTmp,
@ -39,6 +39,7 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
{ label: _('dossier-attributes-listing.table-col-names.placeholder'), width: '2fr' },
{ label: _('dossier-attributes-listing.table-col-names.type'), sortByKey: 'type' },
];
@ViewChild('impactedTemplates') impactedTemplatesRef: TemplateRef<unknown>;
readonly #dossierTemplateId: string;
constructor(
@ -58,16 +59,11 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
await this._loadData();
}
async openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: DossierAttributeConfig) {
const dossierTemplateId = this.#dossierTemplateId;
const resp = await firstValueFrom(
this._reportTemplateService.getTemplatesByPlaceholder(dossierTemplateId, dossierAttribute.placeholder),
);
this._dialogService.openDialog('deleteAttribute', $event, { attribute: dossierAttribute, templates: resp }, async () => {
async openConfirmDeleteAttributeDialog($event: MouseEvent, attributes: DossierAttributeConfig[] = this.listingService.selected) {
await this._dialogService.deleteAttributes(attributes, this.#dossierTemplateId, this.impactedTemplatesRef, $event, async () => {
this._loadingService.start();
const ids = dossierAttribute ? [dossierAttribute.id] : this.listingService.selected.map(item => item.id);
await firstValueFrom(this._dossierAttributesService.delete(ids, dossierTemplateId));
const ids = attributes.map(a => a.id);
await firstValueFrom(this._dossierAttributesService.delete(ids, this.#dossierTemplateId));
await this._loadData();
});
}

View File

@ -11,15 +11,15 @@ import {
} from '@iqser/common-ui';
import { DossierState, IDossierState } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '../../../../services/entity-services/dossiers.service';
import { DossierStateService } from '../../../../services/entity-services/dossier-state.service';
import { ActiveDossiersService } from '../../../../services/dossiers/active-dossiers.service';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { firstValueFrom } from 'rxjs';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { UserService } from '../../../../services/user.service';
import { UserService } from '@services/user.service';
import { HttpStatusCode } from '@angular/common/http';
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { ActivatedRoute } from '@angular/router';
import { DossierTemplatesService } from '../../../../services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
@Component({
templateUrl: './dossier-states-listing-screen.component.html',
@ -33,7 +33,6 @@ import { DossierTemplatesService } from '../../../../services/entity-services/do
export class DossierStatesListingScreenComponent extends ListingComponent<DossierState> implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly #dossierTemplateId: string;
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-states-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<DossierState>[] = [
@ -42,11 +41,12 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
{ label: _('dossier-states-listing.table-col-names.dossiers-count') },
];
chartData: DoughnutChartConfig[];
readonly #dossierTemplateId: string;
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
readonly dossierStateService: DossierStateService,
private readonly _dialogService: AdminDialogService,
private readonly _userService: UserService,
@ -99,7 +99,7 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
async #createNewDossierStateAndRefreshView(newValue: IDossierState): Promise<void> {
this._loadingService.start();
await firstValueFrom(this.dossierStateService.setDossierState(newValue)).catch(error => {
await firstValueFrom(this.dossierStateService.updateDossierState(newValue)).catch(error => {
if (error.status === HttpStatusCode.Conflict) {
this._toaster.error(_('dossier-states-listing.error.conflict'));
} else {
@ -112,7 +112,8 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
async #loadData(): Promise<void> {
this._loadingService.start();
await firstValueFrom(this._dossiersService.loadAll());
// TODO: Move this in service; dossiers states service should be a mapping service
await firstValueFrom(this._activeDossiersService.loadAll());
try {
const dossierStates = this.dossierStateService.all.filter(d => d.dossierTemplateId === this.#dossierTemplateId);
@ -130,6 +131,6 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
}
#setStatesCount(dossierStates: DossierState[]): void {
dossierStates.forEach(state => (state.dossierCount = this._dossiersService.getCountWithState(state.dossierStatusId)));
dossierStates.forEach(state => (state.dossierCount = this._activeDossiersService.getCountWithState(state.dossierStatusId)));
}
}

View File

@ -37,6 +37,12 @@
</div>
</section>
<ng-template #impactedTemplates let-data="data">
<ul class="templates-container flex">
<li *ngFor="let template of data.templates" class="small-label">{{ template.fileName }}</li>
</ul>
</ng-template>
<ng-template #bulkActions>
<iqser-circle-button
(click)="openConfirmDeleteAttributeDialog($event)"
@ -126,7 +132,7 @@
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event, attribute)"
(action)="openConfirmDeleteAttributeDialog($event, [attribute])"
[tooltip]="'file-attributes-listing.action.delete' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:trash"

View File

@ -1,4 +1,14 @@
import { ChangeDetectionStrategy, Component, ElementRef, forwardRef, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
forwardRef,
Injector,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
} from '@angular/core';
import { AdminDialogService } from '../../services/admin-dialog.service';
import {
CircleButtonTypes,
@ -50,6 +60,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
rightIconTooltip: _('file-attributes-listing.table-col-names.primary-info-tooltip'),
},
];
@ViewChild('impactedTemplates') impactedTemplatesRef: TemplateRef<unknown>;
private _existingConfiguration: IFileAttributesConfig;
@ViewChild('fileInput') private _fileInput: ElementRef;
readonly #dossierTemplateId: string;
@ -93,25 +104,15 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
});
}
async openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
const dossierTemplateId = this.#dossierTemplateId;
const resp = await firstValueFrom(
this._reportTemplateService.getTemplatesByPlaceholder(dossierTemplateId, fileAttribute.placeholder),
);
this._dialogService.openDialog('deleteAttribute', $event, { attribute: fileAttribute, templates: resp }, async () => {
async openConfirmDeleteAttributeDialog(
$event: MouseEvent,
attributes: FileAttributeConfig[] = this.listingService.selected,
): Promise<void> {
await this._dialogService.deleteAttributes(attributes, this.#dossierTemplateId, this.impactedTemplatesRef, $event, async () => {
this._loadingService.start();
if (fileAttribute) {
await firstValueFrom(this._fileAttributesService.deleteFileAttributes([fileAttribute.id], dossierTemplateId));
} else {
await firstValueFrom(
this._fileAttributesService.deleteFileAttributes(
this.listingService.selected.map(f => f.id),
dossierTemplateId,
),
);
}
await this._dossierTemplatesService.refreshDossierTemplate(dossierTemplateId);
const ids = attributes.map(a => a.id);
await firstValueFrom(this._fileAttributesService.deleteFileAttributes(ids, this.#dossierTemplateId));
await firstValueFrom(this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId));
await this._loadData();
});
}
@ -148,7 +149,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
}
this._loadingService.stop();
});
await this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId);
await firstValueFrom(this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId));
await this._loadData();
}

View File

@ -1,8 +1,9 @@
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, forwardRef, Injector } from '@angular/core';
import {
CircleButtonTypes,
ConfirmationDialogInput,
DefaultListingServices,
DefaultListingServicesTmp,
EntitiesService,
getLeftDateTime,
IListable,
ListingComponent,
@ -11,35 +12,33 @@ import {
TableColumnConfig,
TitleColors,
} from '@iqser/common-ui';
import { ConfigService } from '@services/config.service';
import * as moment from 'moment';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { RouterHistoryService } from '@services/router-history.service';
import { IDossier } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
interface DossierListItem extends IDossier, IListable {
readonly canRestore: boolean;
readonly canHardDelete: boolean;
readonly restoreDate: string;
}
import { TrashDossier } from '@red/domain';
import { TrashDossiersService } from '@services/entity-services/trash-dossiers.service';
@Component({
templateUrl: './trash-screen.component.html',
styleUrls: ['./trash-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => TrashScreenComponent) }],
providers: [
...DefaultListingServicesTmp,
{
provide: EntitiesService,
useExisting: TrashDossiersService,
},
{ provide: ListingComponent, useExisting: forwardRef(() => TrashScreenComponent) },
],
})
export class TrashScreenComponent extends ListingComponent<DossierListItem> implements OnInit {
export class TrashScreenComponent extends ListingComponent<TrashDossier> {
readonly circleButtonTypes = CircleButtonTypes;
readonly tableHeaderLabel = _('trash.table-header.title');
readonly canRestoreSelected$ = this._canRestoreSelected$;
readonly canHardDeleteSelected$ = this._canHardDeleteSelected$;
readonly tableColumnConfigs: TableColumnConfig<DossierListItem>[] = [
readonly tableColumnConfigs: TableColumnConfig<TrashDossier>[] = [
{ 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' },
@ -49,13 +48,16 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _permissionsService: PermissionsService,
private readonly _dossiersService: DossiersService,
private readonly _trashDossiersService: TrashDossiersService,
readonly routerHistoryService: RouterHistoryService,
private readonly _configService: ConfigService,
private readonly _adminDialogService: AdminDialogService,
) {
super(_injector);
this.sortingService.setSortingOption({
column: 'softDeletedTime',
order: SortingOrders.desc,
});
}
private get _canRestoreSelected$(): Observable<boolean> {
@ -72,17 +74,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
);
}
disabledFn = (dossier: DossierListItem) => !dossier.canRestore;
async ngOnInit(): Promise<void> {
this._loadingService.start();
await this._loadDossiersData();
this.sortingService.setSortingOption({
column: 'softDeletedTime',
order: SortingOrders.desc,
});
this._loadingService.stop();
}
disabledFn = (dossier: TrashDossier) => !dossier.canRestore;
hardDelete(dossiers = this.listingService.selected): void {
const data = new ConfirmationDialogInput({
@ -95,60 +87,13 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
},
});
this._adminDialogService.openDialog('confirm', null, data, () => {
this._loadingService.loadWhile(this._hardDelete(dossiers));
const dossierIds: string[] = dossiers.map(d => d.id);
this._loadingService.loadWhile(this._trashDossiersService.hardDelete(dossierIds));
});
}
restore(dossiers = this.listingService.selected): void {
this._loadingService.loadWhile(this._restore(dossiers));
}
private _getRestoreDate(softDeletedTime: string): string {
return moment(softDeletedTime).add(this._configService.values.DELETE_RETENTION_HOURS, 'hours').toISOString();
}
private async _loadDossiersData(): Promise<void> {
this.entitiesService.setEntities(this._toListItems(await this._dossiersService.getDeleted()).filter(d => d.canRestore));
}
private _canRestoreDossier(restoreDate: string): boolean {
const { daysLeft, hoursLeft, minutesLeft } = getLeftDateTime(restoreDate);
return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft > 0;
}
private _toListItems(dossiers: IDossier[]): DossierListItem[] {
return dossiers.map(dossier => this._toListItem(dossier));
}
private _toListItem(dossier: IDossier): DossierListItem {
const restoreDate = this._getRestoreDate(dossier.softDeletedTime);
return {
id: dossier.dossierId,
...dossier,
searchKey: dossier.dossierName,
restoreDate,
canRestore: this._canRestoreDossier(restoreDate),
canHardDelete: this._permissionsService.canDeleteDossier(dossier),
// Because of migrations, for some this is not set
softDeletedTime: dossier.softDeletedTime || '-',
};
}
private async _restore(dossiers: DossierListItem[]): Promise<void> {
const dossierIds = dossiers.map(d => d.id);
await this._dossiersService.restore(dossierIds);
this._removeFromList(dossierIds);
}
private async _hardDelete(dossiers: DossierListItem[]) {
const dossierIds = dossiers.map(d => d.id);
await this._dossiersService.hardDelete(dossierIds);
this._removeFromList(dossierIds);
}
private _removeFromList(ids: string[]): void {
const entities = this.entitiesService.all.filter(e => !ids.includes(e.id));
this.entitiesService.setEntities(entities);
const dossierIds: string[] = dossiers.map(d => d.id);
this._loadingService.loadWhile(this._trashDossiersService.restore(dossierIds));
}
}

View File

@ -80,7 +80,7 @@ export class UserListingScreenComponent extends ListingComponent<User> implement
}
openDeleteUsersDialog(userIds: string[], $event?: MouseEvent) {
this._dialogService.openDialog('deleteUsers', $event, userIds, async () => {
this._dialogService.deleteUsers(userIds, $event, async () => {
await this._loadData();
});
}

View File

@ -1,31 +1,42 @@
import { Injectable } from '@angular/core';
import { Injectable, TemplateRef } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
import { AddEditDictionaryDialogComponent } from '../dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
import { AddEditDossierTemplateDialogComponent } from '../dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
import { ConfirmDeleteAttributeDialogComponent } from '../dialogs/confirm-delete-attribute-dialog/confirm-delete-attribute-dialog.component';
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
import { AddEditUserDialogComponent } from '../dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
import { ConfirmDeleteUsersDialogComponent } from '../dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component';
import { FileAttributesCsvImportDialogComponent } from '../dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component';
import { AddEditDossierAttributeDialogComponent } from '../dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component';
import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } from '@iqser/common-ui';
import {
ConfirmationDialogComponent,
ConfirmationDialogInput,
ConfirmOptions,
DialogConfig,
DialogService,
largeDialogConfig,
LoadingService,
TitleColors,
} from '@iqser/common-ui';
import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
import { FileAttributesConfigurationsDialogComponent } from '../dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component';
import { AddEditDossierStateDialogComponent } from '../dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component';
import { ConfirmDeleteDossierStateDialogComponent } from '../dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { firstValueFrom, forkJoin } from 'rxjs';
import { ActiveDossiersService } from '../../../services/dossiers/active-dossiers.service';
import { UserService } from '../../../services/user.service';
import { IDossierAttributeConfig, IFileAttributeConfig, IReportTemplate } from '@red/domain';
import { ReportTemplateService } from '../../../services/report-template.service';
type DialogType =
| 'confirm'
| 'addEditDictionary'
| 'editColor'
| 'addEditFileAttribute'
| 'deleteAttribute'
| 'importFileAttributes'
| 'fileAttributesConfigurations'
| 'addEditUser'
| 'deleteUsers'
| 'smtpAuthConfig'
| 'addEditDossierTemplate'
| 'addEditDossierAttribute'
@ -52,10 +63,6 @@ export class AdminDialogService extends DialogService<DialogType> {
component: AddEditFileAttributeDialogComponent,
dialogConfig: { autoFocus: true },
},
deleteAttribute: {
component: ConfirmDeleteAttributeDialogComponent,
dialogConfig: { disableClose: false },
},
fileAttributesConfigurations: {
component: FileAttributesConfigurationsDialogComponent,
},
@ -63,10 +70,6 @@ export class AdminDialogService extends DialogService<DialogType> {
component: FileAttributesCsvImportDialogComponent,
dialogConfig: { ...largeDialogConfig, ...{ disableClose: false } },
},
deleteUsers: {
component: ConfirmDeleteUsersDialogComponent,
dialogConfig: { autoFocus: true, disableClose: false },
},
addEditUser: {
component: AddEditUserDialogComponent,
dialogConfig: { autoFocus: true },
@ -94,7 +97,88 @@ export class AdminDialogService extends DialogService<DialogType> {
},
};
constructor(protected readonly _dialog: MatDialog) {
constructor(
protected readonly _dialog: MatDialog,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _loadingService: LoadingService,
private readonly _userService: UserService,
private readonly _reportTemplateService: ReportTemplateService,
) {
super(_dialog);
}
deleteUsers(userIds: string[], $event?: MouseEvent, cb?: () => Promise<void> | void): void {
const data = new ConfirmationDialogInput({
title: _('confirm-delete-users.title'),
question: _('confirm-delete-users.warning'),
confirmationText: _('confirm-delete-users.delete'),
denyText: _('confirm-delete-users.cancel'),
titleColor: TitleColors.WARN,
translateParams: { usersCount: 1, dossiersCount: this._getUsersDossiersCount(userIds) },
checkboxes: [
{ value: false, label: _('confirm-delete-users.impacted-dossiers') },
{ value: false, label: _('confirm-delete-users.impacted-documents') },
],
toastMessage: _('confirm-delete-users.toast-error'),
});
this.openDialog('confirm', $event, data, async result => {
if (result === ConfirmOptions.CONFIRM) {
this._loadingService.start();
await firstValueFrom(this._userService.delete(userIds));
await cb();
this._loadingService.stop();
}
});
}
async deleteAttributes(
attributes: IFileAttributeConfig[] | IDossierAttributeConfig[],
dossierTemplateId: string,
impactedTemplatesRef: TemplateRef<unknown>,
$event?: MouseEvent,
cb?: () => Promise<void> | void,
): Promise<void> {
this._loadingService.start();
const templates$ = attributes
.map(a => a.placeholder)
.map(placeholder => this._reportTemplateService.getTemplatesByPlaceholder(dossierTemplateId, placeholder as string));
const templates: IReportTemplate[] = (await firstValueFrom(forkJoin(templates$))).flat();
const templateIds = new Set<string>(templates.map(t => t.templateId));
const uniqueTemplates = Array.from(templateIds).map(id => templates.find(t => t.templateId === id));
this._loadingService.stop();
const data = new ConfirmationDialogInput({
title: _('confirm-delete-file-attribute.title'),
question: _('confirm-delete-file-attribute.warning'),
confirmationText: _('confirm-delete-file-attribute.delete'),
denyText: _('confirm-delete-file-attribute.cancel'),
titleColor: TitleColors.WARN,
checkboxes: [
{
value: false,
label: 'confirm-delete-file-attribute.file-impacted-documents',
},
{ value: false, label: 'confirm-delete-file-attribute.file-lost-details' },
],
toastMessage: _('confirm-delete-file-attribute.toast-error'),
translateParams: { reportsCount: uniqueTemplates.length, count: attributes.length, name: attributes[0].label },
});
if (templates.length) {
data.checkboxes.push({
value: false,
label: _('confirm-delete-file-attribute.impacted-report'),
extraContent: impactedTemplatesRef,
extraContentData: { templates: uniqueTemplates },
});
}
this.openDialog('confirm', $event, data, async () => {
await cb();
});
}
private _getUsersDossiersCount(userIds: string[]): number {
return this._activeDossiersService.all.filter(dw => userIds.reduce((prev, userId) => prev || dw.memberIds.includes(userId), false))
.length;
}
}

View File

@ -0,0 +1,44 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BreadcrumbTypes } from '@red/domain';
import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
import { CompositeRouteGuard } from '@iqser/common-ui';
import { ARCHIVED_DOSSIERS_SERVICE } from '../../tokens';
import { DossierFilesGuard } from '@guards/dossier-files-guard';
import { FilePreviewGuard } from '../../guards/file-preview.guard';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: ArchivedDossiersScreenComponent,
data: { breadcrumbs: [BreadcrumbTypes.archive] },
},
{
path: `:${DOSSIER_ID}`,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [DossierFilesGuard],
breadcrumbs: [BreadcrumbTypes.archive, BreadcrumbTypes.dossier],
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
},
loadChildren: () => import('../dossier/screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
},
{
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [DossierFilesGuard, FilePreviewGuard],
breadcrumbs: [BreadcrumbTypes.archive, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
},
loadChildren: () => import('../dossier/screens/file-preview-screen/file-preview.module').then(m => m.FilePreviewModule),
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ArchiveRoutingModule {}

View File

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component';
import { ArchiveRoutingModule } from './archive-routing.module';
import { TableItemComponent } from './components/table-item/table-item.component';
import { ConfigService } from './services/config.service';
import { SharedModule } from '../shared/shared.module';
const components = [TableItemComponent];
const screens = [ArchivedDossiersScreenComponent];
@NgModule({
declarations: [...components, ...screens],
imports: [CommonModule, ArchiveRoutingModule, SharedModule],
providers: [ConfigService],
})
export class ArchiveModule {}

View File

@ -0,0 +1,13 @@
<div class="cell">
<redaction-dossiers-listing-dossier-name [dossierStats]="stats$ | async" [dossier]="dossier"></redaction-dossiers-listing-dossier-name>
</div>
<div class="cell small-label">{{ dossier.archivedTime | date: 'd MMM. yyyy' }}</div>
<div class="cell user-column">
<redaction-initials-avatar [user]="dossier.ownerId" [withName]="true"></redaction-initials-avatar>
</div>
<div class="cell">
<redaction-dossier-status [dossier]="dossier"></redaction-dossier-status>
</div>

View File

@ -0,0 +1,28 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { BehaviorSubject, Observable } from 'rxjs';
import { DossierStatsService } from '../../../../services/dossiers/dossier-stats.service';
import { switchMap } from 'rxjs/operators';
@Component({
selector: 'redaction-table-item [dossier]',
templateUrl: './table-item.component.html',
styleUrls: ['./table-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TableItemComponent implements OnChanges {
@Input() dossier!: Dossier;
readonly stats$: Observable<DossierStats>;
readonly #ngOnChanges$ = new BehaviorSubject<string>(undefined);
constructor(readonly dossierStatsService: DossierStatsService) {
this.stats$ = this.#ngOnChanges$.pipe(switchMap(dossierId => this.dossierStatsService.watch$(dossierId)));
}
ngOnChanges() {
if (this.dossier) {
this.#ngOnChanges$.next(this.dossier.dossierId);
}
}
}

View File

@ -0,0 +1,21 @@
<section>
<iqser-page-header></iqser-page-header>
<div class="overlay-shadow"></div>
<div class="content-inner">
<div class="content-container">
<iqser-table
[itemSize]="80"
[noDataText]="'archived-dossiers-listing.no-data.title' | translate"
[noMatchText]="'archived-dossiers-listing.no-match.title' | translate"
[tableColumnConfigs]="tableColumnConfigs"
noDataIcon="red:folder"
></iqser-table>
</div>
</div>
</section>
<ng-template #tableItemTemplate let-dossier="entity">
<redaction-table-item [dossier]="dossier"></redaction-table-item>
</ng-template>

View File

@ -0,0 +1,26 @@
import { ChangeDetectionStrategy, Component, forwardRef, Injector } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../../services/config.service';
import { DefaultListingServicesTmp, EntitiesService, ListingComponent } from '@iqser/common-ui';
import { ArchivedDossiersService } from '../../../../services/dossiers/archived-dossiers.service';
import { Dossier } from '@red/domain';
@Component({
selector: 'redaction-archived-dossiers-screen',
templateUrl: './archived-dossiers-screen.component.html',
styleUrls: ['./archived-dossiers-screen.component.scss'],
providers: [
...DefaultListingServicesTmp,
{ provide: EntitiesService, useExisting: ArchivedDossiersService },
{ provide: ListingComponent, useExisting: forwardRef(() => ArchivedDossiersScreenComponent) },
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArchivedDossiersScreenComponent extends ListingComponent<Dossier> {
readonly tableColumnConfigs = this._configService.tableConfig;
readonly tableHeaderLabel = _('archived-dossiers-listing.table-header.title');
constructor(protected readonly _injector: Injector, private readonly _configService: ConfigService) {
super(_injector);
}
}

View File

@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { TableColumnConfig } from '@iqser/common-ui';
import { Dossier } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Injectable()
export class ConfigService {
get tableConfig(): TableColumnConfig<Dossier>[] {
return [
{ label: _('archived-dossiers-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' },
{ label: _('archived-dossiers-listing.table-col-names.last-modified'), sortByKey: 'archivedTime' },
{ label: _('archived-dossiers-listing.table-col-names.owner'), class: 'user-column' },
{ label: _('archived-dossiers-listing.table-col-names.dossier-status'), class: 'flex-end', width: '2fr' },
];
}
}

View File

@ -5,7 +5,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import * as moment from 'moment';
import { downloadTypesTranslations } from '../../../../translations/download-types-translations';
import { BaseDialogComponent, IconButtonTypes, SaveOptions } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { ReportTemplateService } from '@services/report-template.service';
import { firstValueFrom } from 'rxjs';
@ -29,7 +29,7 @@ export class AddDossierDialogComponent extends BaseDialogComponent {
availableReportTypes = [];
constructor(
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _formBuilder: FormBuilder,
private readonly _reportTemplateController: ReportTemplateService,
@ -61,7 +61,7 @@ export class AddDossierDialogComponent extends BaseDialogComponent {
reportTemplateValueMapper = (reportTemplate: IReportTemplate) => reportTemplate.templateId;
async save(options?: SaveOptions) {
const savedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(this._formToObject()));
const savedDossier = await firstValueFrom(this._activeDossiersService.createOrUpdate(this._formToObject()));
if (savedDossier) {
this._dialogRef.close({ dossier: savedDossier, addMembers: options?.addMembers });
}

View File

@ -6,7 +6,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Dossier, File } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FilesService } from '@services/entity-services/files.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { PermissionsService } from '@services/permissions.service';
import { firstValueFrom } from 'rxjs';
@ -30,14 +30,14 @@ export class AssignReviewerApproverDialogComponent {
readonly userService: UserService,
private readonly _toaster: Toaster,
private readonly _formBuilder: FormBuilder,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _filesService: FilesService,
private readonly _loadingService: LoadingService,
readonly permissionsService: PermissionsService,
private readonly _dialogRef: MatDialogRef<AssignReviewerApproverDialogComponent, boolean>,
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
) {
this.dossier = this._dossiersService.find(this.data.files[0].dossierId);
this.dossier = this._activeDossiersService.find(this.data.files[0].dossierId);
this.form = this._getForm();
}
@ -102,28 +102,11 @@ export class AssignReviewerApproverDialogComponent {
this._loadingService.start();
try {
if (!this.selectedUser) {
await firstValueFrom(
this._filesService.setUnassigned(
this.data.files.map(f => f.fileId),
this.dossier.id,
),
);
await firstValueFrom(this._filesService.setUnassigned(this.data.files, this.dossier.id));
} else if (this.data.mode === 'reviewer') {
await firstValueFrom(
this._filesService.setReviewerFor(
this.data.files.map(f => f.fileId),
this.dossier.id,
this.selectedUser,
),
);
await firstValueFrom(this._filesService.setReviewerFor(this.data.files, this.dossier.id, this.selectedUser));
} else {
await firstValueFrom(
this._filesService.setUnderApprovalFor(
this.data.files.map(f => f.fileId),
this.dossier.id,
this.selectedUser,
),
);
await firstValueFrom(this._filesService.setUnderApprovalFor(this.data.files, this.dossier.id, this.selectedUser));
}
} catch (error) {
this._toaster.error(_('error.http.generic'), { params: error });

View File

@ -3,7 +3,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { PermissionsService } from '@services/permissions.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { Dossier } from '@red/domain';
import { BaseDialogComponent } from '@iqser/common-ui';
@ -23,7 +23,8 @@ export class ChangeLegalBasisDialogComponent extends BaseDialogComponent impleme
constructor(
private readonly _justificationsService: JustificationsService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _permissionsService: PermissionsService,
private readonly _formBuilder: FormBuilder,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef<ChangeLegalBasisDialogComponent>,

View File

@ -3,7 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { FilesService } from '@services/entity-services/files.service';
import { BaseDialogComponent } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';
@ -19,16 +19,16 @@ export class DocumentInfoDialogComponent extends BaseDialogComponent implements
private readonly _dossier: Dossier;
constructor(
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _formBuilder: FormBuilder,
private readonly _fileAttributesService: FileAttributesService,
private readonly _filesService: FilesService,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef<DocumentInfoDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly data: File,
@Inject(MAT_DIALOG_DATA) readonly file: File,
) {
super(_injector, _dialogRef);
this._dossier = this._dossiersService.find(this.data.dossierId);
this._dossier = this._activeDossiersService.find(this.file.dossierId);
}
async ngOnInit() {
@ -42,11 +42,11 @@ export class DocumentInfoDialogComponent extends BaseDialogComponent implements
async save() {
const attributeIdToValue = {
...this.data.fileAttributes?.attributeIdToValue,
...this.file.fileAttributes?.attributeIdToValue,
...this.form.getRawValue(),
};
await firstValueFrom(this._fileAttributesService.setFileAttributes({ attributeIdToValue }, this.data.dossierId, this.data.fileId));
await firstValueFrom(this._filesService.reload(this.data.dossierId, this.data.fileId));
await firstValueFrom(this._fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId));
await firstValueFrom(this._filesService.reload(this.file.dossierId, this.file));
this._dialogRef.close(true);
}
@ -55,7 +55,7 @@ export class DocumentInfoDialogComponent extends BaseDialogComponent implements
this.attributes.reduce(
(acc, attr) => ({
...acc,
[attr.id]: [this.data.fileAttributes?.attributeIdToValue[attr.id]],
[attr.id]: [this.file.fileAttributes?.attributeIdToValue[attr.id]],
}),
{},
),

View File

@ -50,7 +50,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
}
get disabled(): boolean {
return !this._permissionsService.isOwner(this.dossier);
return !this._permissionsService.canHardDeleteOrRestore(this.dossier);
}
get valid(): boolean {

View File

@ -7,6 +7,7 @@ import {
DefaultListingServices,
getLeftDateTime,
IListable,
IRouterPath,
ListingComponent,
LoadingService,
SortingOrders,
@ -21,11 +22,11 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { FilesService } from '@services/entity-services/files.service';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { workflowFileStatusTranslations } from '../../../translations/file-status-translations';
import { workflowFileStatusTranslations } from '../../../../../translations/file-status-translations';
import { PermissionsService } from '@services/permissions.service';
import { UserService } from '@services/user.service';
interface FileListItem extends IFile, IListable {
interface FileListItem extends IFile, IListable, IRouterPath {
readonly canHardDelete: boolean;
readonly canRestore: boolean;
readonly restoreDate: string;
@ -129,14 +130,13 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
disabledFn = (file: FileListItem) => !file.canRestore;
private async _restore(files: FileListItem[]): Promise<void> {
const fileIds = files.map(f => f.fileId);
await firstValueFrom(this._fileManagementService.restore(fileIds, this.dossier.id));
const fileIds = files.map(f => f.id);
await firstValueFrom(this._fileManagementService.restore(files, this.dossier.id));
this._removeFromList(fileIds);
await firstValueFrom(this._filesService.loadAll(files[0].dossierId));
}
private async _hardDelete(files: FileListItem[]) {
const fileIds = files.map(f => f.fileId);
const fileIds = files.map(f => f.id);
await firstValueFrom(this._fileManagementService.hardDelete(this.dossier.id, fileIds));
this._removeFromList(fileIds);
}
@ -151,7 +151,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
}
private _toListItem(_file: IFile): FileListItem {
const file = new File(_file, this._userService.getNameForId(_file.assignee));
const file = new File(_file, this._userService.getNameForId(_file.assignee), this.dossier.routerPath);
const restoreDate = this._getRestoreDate(_file.softDeleted);
return {
id: file.fileId,
@ -164,7 +164,10 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
}
private _canPerformActions(file: File): boolean {
return this._userService.currentUser.isManager || this._permissionsService.canDeleteFile(file);
return (
this._permissionsService.canHardDeleteOrRestore(this.dossier) &&
(this._userService.currentUser.isManager || this._permissionsService.canDeleteFile(file))
);
}
private _canRestore(file: File, restoreDate: string): boolean {

View File

@ -5,10 +5,10 @@ import { PermissionsService } from '@services/permissions.service';
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
import { DictionaryService } from '@shared/services/dictionary.service';
import { CircleButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FormBuilder, FormGroup } from '@angular/forms';
import { firstValueFrom } from 'rxjs';
import { DossiersService } from '@services/dossiers/dossiers.service';
@Component({
selector: 'redaction-edit-dossier-dictionary',
@ -57,8 +57,8 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
async ngOnInit() {
this._loadingService.start();
this.canEdit = this._permissionsService.canEditDossier(this.dossier);
this.canEditDisplayName = this._permissionsService.isOwner(this.dossier);
this.canEdit = this._permissionsService.canEditDossierDictionary(this.dossier);
this.canEditDisplayName = this._permissionsService.canEditDossierDictionaryDisplayName(this.dossier);
await this._updateDossierDictionary();
this.form = this._getForm();
this._loadingService.stop();
@ -121,7 +121,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
addToDictionaryAction: [
{
value: this.dossierDictionary.addToDictionaryAction,
disabled: !this._permissionsService.isOwner(this.dossier),
disabled: !this._permissionsService.canEditDossierDictionaryAddAction(this.dossier),
},
],
});

View File

@ -1,17 +1,31 @@
<form *ngIf="form" [formGroup]="form">
<redaction-select
[label]="'report-type.label' | translate: { length: reportTypesLength }"
[optionTemplate]="reportTemplateOptionTemplate"
[options]="availableReportTypes"
[valueMapper]="reportTemplateValueMapper"
class="mr-16"
formControlName="reportTemplateIds"
></redaction-select>
<redaction-select
[label]="'download-type.label' | translate: { length: downloadFileTypesLength }"
[options]="downloadTypes"
formControlName="downloadFileTypes"
></redaction-select>
<div>
<redaction-select
[label]="'report-type.label' | translate: { length: reportTypesLength }"
[optionTemplate]="reportTemplateOptionTemplate"
[options]="availableReportTypes"
[valueMapper]="reportTemplateValueMapper"
class="mr-16"
formControlName="reportTemplateIds"
></redaction-select>
<redaction-select
[label]="'download-type.label' | translate: { length: downloadFileTypesLength }"
[options]="downloadTypes"
formControlName="downloadFileTypes"
></redaction-select>
</div>
<div class="mt-32 mb-14">
<mat-checkbox class="watermark" color="primary" formControlName="watermarkEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark' | translate }}
</mat-checkbox>
</div>
<div>
<mat-checkbox class="watermark-preview" color="primary" formControlName="watermarkPreviewEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark-preview' | translate }}
</mat-checkbox>
</div>
</form>
<ng-template #reportTemplateOptionTemplate let-option="option">

View File

@ -10,4 +10,9 @@ redaction-select {
form {
height: calc(100% - 44px);
display: flex;
flex-direction: column;
> div {
display: flex;
}
}

View File

@ -3,10 +3,10 @@ import { Dossier, DownloadFileType, IReportTemplate } from '@red/domain';
import { FormBuilder, FormGroup } from '@angular/forms';
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { downloadTypesTranslations } from '../../../../../translations/download-types-translations';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ReportTemplateService } from '@services/report-template.service';
import { PermissionsService } from '@services/permissions.service';
import { firstValueFrom } from 'rxjs';
import { DossiersService } from '@services/dossiers/dossiers.service';
@Component({
selector: 'redaction-edit-dossier-download-package',
@ -40,9 +40,17 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
return this.form.controls['downloadFileTypes']?.value?.length || 0;
}
get changed() {
if (this.form) {
for (const key of Object.keys(this.form.getRawValue())) {
get changed(): boolean {
if (!this.form) {
return false;
}
for (const key of Object.keys(this.form.getRawValue())) {
if (key.startsWith('watermark')) {
if (this.dossier[key] !== this.form.get(key).value) {
return true;
}
} else {
if (this.dossier[key].length !== this.form.get(key).value.length) {
return true;
}
@ -86,6 +94,8 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
...this.dossier,
downloadFileTypes: this.form.get('downloadFileTypes').value,
reportTemplateIds: this.form.get('reportTemplateIds').value,
watermarkEnabled: this.form.get('watermarkEnabled').value,
watermarkPreviewEnabled: this.form.get('watermarkPreviewEnabled').value,
};
const updatedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(dossier));
@ -96,6 +106,8 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
this.form.reset({
downloadFileTypes: this.dossier.downloadFileTypes,
reportTemplateIds: this.dossier.reportTemplateIds,
watermarkEnabled: this.dossier.watermarkEnabled,
watermarkPreviewEnabled: this.dossier.watermarkPreviewEnabled,
});
}
@ -104,6 +116,8 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
{
reportTemplateIds: [this.dossier.reportTemplateIds],
downloadFileTypes: [this.dossier.downloadFileTypes],
watermarkEnabled: [this.dossier.watermarkEnabled],
watermarkPreviewEnabled: [this.dossier.watermarkPreviewEnabled],
},
{
validators: control =>

View File

@ -5,13 +5,10 @@
<div class="dialog-content">
<iqser-side-nav [title]="'edit-dossier-dialog.side-nav-title' | translate">
<div
(click)="changeTab(item.key)"
*ngFor="let item of navItems"
[class.active]="item.key === activeNav"
[translate]="item.sideNavTitle || item.title"
class="item"
></div>
<div (click)="changeTab(item.key)" *ngFor="let item of navItems" [class.active]="item.key === activeNav" class="item">
{{ item.sideNavTitle || item.title | translate }}
<mat-icon *ngIf="item.readonly" svgIcon="red:read-only"></mat-icon>
</div>
</iqser-side-nav>
<div>
@ -20,6 +17,11 @@
{{ activeNavItem.title | translate }}
</div>
<div *ngIf="activeNavItem.readonly" class="read-only all-caps-label primary">
<mat-icon class="mr-8" svgIcon="red:read-only"></mat-icon>
{{ 'readonly' | translate }}
</div>
<redaction-edit-dossier-general-info
*ngIf="activeNav === 'dossierInfo'"
[dossier]="dossier"
@ -53,9 +55,9 @@
(click)="save()"
[disabled]="disabled || !valid || !changed"
color="primary"
id="editDossierSaveButton"
mat-flat-button
type="button"
id="editDossierSaveButton"
>
{{ 'edit-dossier-dialog.actions.save' | translate }}
</button>
@ -72,5 +74,5 @@
</div>
</div>
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()" id="editDossierCloseButton"></iqser-circle-button>
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close" id="editDossierCloseButton"></iqser-circle-button>
</section>

View File

@ -41,3 +41,15 @@ redaction-edit-dossier-dictionary {
.stats-subtitle {
margin-top: 6px;
}
.read-only {
display: flex;
position: absolute;
right: 32px;
top: 108px;
mat-icon {
height: 14px;
width: 14px;
}
}

View File

@ -10,21 +10,30 @@ import { EditDossierAttributesComponent } from './attributes/edit-dossier-attrib
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { EditDossierDeletedDocumentsComponent } from './deleted-documents/edit-dossier-deleted-documents.component';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { EditDossierTeamComponent } from './edit-dossier-team/edit-dossier-team.component';
import { PermissionsService } from '@services/permissions.service';
import { UserService } from '@services/user.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes' | 'deletedDocuments';
interface NavItem {
key: Section;
title?: string;
sideNavTitle?: string;
readonly?: boolean;
}
@Component({
templateUrl: './edit-dossier-dialog.component.html',
styleUrls: ['./edit-dossier-dialog.component.scss'],
providers: [dossiersServiceProvider],
})
export class EditDossierDialogComponent extends BaseDialogComponent implements AfterViewInit {
readonly navItems: { key: Section; title?: string; sideNavTitle?: string }[];
navItems: NavItem[] = [];
readonly iconButtonTypes = IconButtonTypes;
activeNav: Section;
readonly dossier$: Observable<Dossier>;
@ -40,7 +49,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
constructor(
private readonly _toaster: Toaster,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: DossiersService,
private readonly _changeRef: ChangeDetectorRef,
private readonly _loadingService: LoadingService,
private readonly _permissionsService: PermissionsService,
@ -54,48 +63,16 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
},
) {
super(_injector, _dialogRef, true);
this.navItems = [
{
key: 'dossierInfo',
title: _('edit-dossier-dialog.nav-items.general-info'),
sideNavTitle: _('edit-dossier-dialog.nav-items.dossier-info'),
},
{
key: 'downloadPackage',
title: _('edit-dossier-dialog.nav-items.choose-download'),
sideNavTitle: _('edit-dossier-dialog.nav-items.download-package'),
},
{
key: 'dossierDictionary',
sideNavTitle: _('edit-dossier-dialog.nav-items.dictionary'),
title: _('edit-dossier-dialog.nav-items.dossier-dictionary'),
},
{
key: 'members',
title: _('edit-dossier-dialog.nav-items.team-members'),
sideNavTitle: _('edit-dossier-dialog.nav-items.members'),
},
{
key: 'dossierAttributes',
title: _('edit-dossier-dialog.nav-items.dossier-attributes'),
},
{
key: 'deletedDocuments',
sideNavTitle: _('edit-dossier-dialog.nav-items.deleted-documents'),
},
];
this.dossier$ = this._dossiersService.getEntityChanged$(_data.dossierId).pipe(tap(dossier => (this._dossier = dossier)));
this.dossier$ = this._activeDossiersService.getEntityChanged$(_data.dossierId).pipe(
tap(dossier => {
this._dossier = dossier;
this._initializeNavItems();
}),
);
this.activeNav = _data.section || 'dossierInfo';
}
ngAfterViewInit() {
if (!this._dossier.ownerId) {
this._toaster.error(_('edit-dossier-dialog.missing-owner'));
}
}
get activeNavItem(): { key: string; title?: string } {
get activeNavItem(): NavItem {
return this.navItems.find(item => item.key === this.activeNav);
}
@ -137,6 +114,12 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
return this.activeComponent?.disabled;
}
ngAfterViewInit() {
if (!this._dossier.ownerId) {
this._toaster.error(_('edit-dossier-dialog.missing-owner'));
}
}
async save(options?: SaveOptions) {
this._loadingService.start();
const result = await this.activeComponent.save();
@ -171,4 +154,40 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
this.activeNav = key;
}
}
private _initializeNavItems(): void {
this.navItems = [
{
key: 'dossierInfo',
title: _('edit-dossier-dialog.nav-items.general-info'),
sideNavTitle: _('edit-dossier-dialog.nav-items.dossier-info'),
readonly: !this._dossier.isActive,
},
{
key: 'downloadPackage',
title: _('edit-dossier-dialog.nav-items.choose-download'),
sideNavTitle: _('edit-dossier-dialog.nav-items.download-package'),
},
{
key: 'dossierDictionary',
sideNavTitle: _('edit-dossier-dialog.nav-items.dictionary'),
title: _('edit-dossier-dialog.nav-items.dossier-dictionary'),
readonly: !this._permissionsService.canEditDossierDictionary(this._dossier),
},
{
key: 'members',
title: _('edit-dossier-dialog.nav-items.team-members'),
sideNavTitle: _('edit-dossier-dialog.nav-items.members'),
},
{
key: 'dossierAttributes',
title: _('edit-dossier-dialog.nav-items.dossier-attributes'),
readonly: !this._permissionsService.canEditDossierAttributes(this._dossier),
},
{
key: 'deletedDocuments',
sideNavTitle: _('edit-dossier-dialog.nav-items.deleted-documents'),
},
];
}
}

View File

@ -1,12 +1,12 @@
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { UserService } from '@services/user.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { Dossier, IDossierRequest } from '@red/domain';
import { AutoUnsubscribe } from '@iqser/common-ui';
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
@Component({
selector: 'redaction-edit-dossier-team',

View File

@ -38,18 +38,6 @@
type="text"
></textarea>
</div>
<div>
<mat-checkbox class="watermark" color="primary" formControlName="watermarkEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark' | translate }}
</mat-checkbox>
</div>
<div>
<mat-checkbox class="watermark-preview" color="primary" formControlName="watermarkPreviewEnabled">
{{ 'edit-dossier-dialog.general-info.form.watermark-preview' | translate }}
</mat-checkbox>
</div>
</div>
<div class="flex fields-container">
@ -69,7 +57,7 @@
<mat-checkbox
(change)="hasDueDate = !hasDueDate"
[checked]="hasDueDate"
[disabled]="!permissionsService.canEditDossier(dossier)"
[disabled]="form.get('dueDate').disabled"
class="filter-menu-checkbox"
color="primary"
>
@ -95,5 +83,13 @@
[type]="iconButtonTypes.dark"
icon="iqser:trash"
></iqser-icon-button>
<iqser-icon-button
(action)="archiveDossier()"
*ngIf="permissionsService.canArchiveDossier(dossier)"
[label]="'dossier-listing.archive.action' | translate"
[type]="iconButtonTypes.dark"
icon="red:archive"
></iqser-icon-button>
</div>
</form>

View File

@ -8,15 +8,17 @@ import { PermissionsService } from '@services/permissions.service';
import { Router } from '@angular/router';
import { MatDialogRef } from '@angular/material/dialog';
import { EditDossierDialogComponent } from '../edit-dossier-dialog.component';
import { ConfirmationDialogInput, IconButtonTypes, TitleColors, Toaster } from '@iqser/common-ui';
import { ConfirmationDialogInput, ConfirmOptions, IconButtonTypes, LoadingService, TitleColors, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { firstValueFrom } from 'rxjs';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { DOSSIER_TEMPLATE_ID } from '@utils/constants';
import { TranslateService } from '@ngx-translate/core';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { TrashDossiersService } from '@services/entity-services/trash-dossiers.service';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
@Component({
selector: 'redaction-edit-dossier-general-info',
@ -39,13 +41,16 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
private readonly _dossierStateService: DossierStateService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossiersService: DossiersService,
private readonly _trashDossiersService: TrashDossiersService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _formBuilder: FormBuilder,
private readonly _dialogService: DossiersDialogService,
private readonly _router: Router,
private readonly _editDossierDialogRef: MatDialogRef<EditDossierDialogComponent>,
private readonly _toaster: Toaster,
private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService,
private readonly _archivedDossiersService: ArchivedDossiersService,
) {}
get changed(): boolean {
@ -102,8 +107,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
dossierTemplateId: this.dossier.dossierTemplateId,
dossierStatusId: this.dossier.dossierStatusId,
description: this.dossier.description,
watermarkEnabled: this.dossier.watermarkEnabled,
watermarkPreviewEnabled: this.dossier.watermarkPreviewEnabled,
dueDate: this.dossier.dueDate,
});
}
@ -113,8 +116,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
...this.dossier,
dossierName: this.form.get('dossierName').value,
description: this.form.get('description').value,
watermarkEnabled: this.form.get('watermarkEnabled').value,
watermarkPreviewEnabled: this.form.get('watermarkPreviewEnabled').value,
dueDate: this.hasDueDate ? this.form.get('dueDate').value : undefined,
dossierTemplateId: this.form.get(DOSSIER_TEMPLATE_ID).value,
dossierStatusId: this.form.get('dossierStatusId').value,
@ -129,7 +130,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
title: _('confirmation-dialog.delete-dossier.title'),
titleColor: TitleColors.WARN,
question: _('confirmation-dialog.delete-dossier.question'),
// details: _('confirmation-dialog.delete-dossier.details'),
confirmationText: _('confirmation-dialog.delete-dossier.confirmation-text'),
requireInput: true,
denyText: _('confirmation-dialog.delete-dossier.deny-text'),
@ -139,34 +139,56 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
},
});
this._dialogService.openDialog('confirm', null, data, async () => {
await firstValueFrom(this._dossiersService.delete(this.dossier));
this._loadingService.start();
await firstValueFrom(this._trashDossiersService.delete(this.dossier));
this._editDossierDialogRef.close();
this._router.navigate(['main', 'dossiers']).then(() => this.#notifyDossierDeleted());
await this._router.navigate(['main', 'dossiers']);
this._loadingService.stop();
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: this.dossier });
});
}
archiveDossier() {
const data = new ConfirmationDialogInput({
title: _('confirm-archive-dossier.title'),
details: _('confirm-archive-dossier.details'),
question: _('confirm-archive-dossier.warning'),
confirmationText: _('confirm-archive-dossier.archive'),
denyText: _('confirm-archive-dossier.cancel'),
titleColor: TitleColors.WARN,
translateParams: { ...this.dossier },
checkboxes: [{ value: false, label: _('confirm-archive-dossier.checkbox.documents') }],
toastMessage: _('confirm-archive-dossier.toast-error'),
});
this._dialogService.openDialog('confirm', null, data, async result => {
if (result === ConfirmOptions.CONFIRM) {
this._loadingService.start();
await firstValueFrom(this._archivedDossiersService.archive([this.dossier]));
await this._router.navigate(['main', 'dossiers']);
this._toaster.success(_('dossier-listing.archive.archive-succeeded'), { params: this.dossier });
this._editDossierDialogRef.close();
this._loadingService.stop();
}
});
}
#getForm(): FormGroup {
const formFieldWithArchivedCheck = value => ({ value, disabled: !this.dossier.isActive });
return this._formBuilder.group({
dossierName: [this.dossier.dossierName, Validators.required],
dossierName: [formFieldWithArchivedCheck(this.dossier.dossierName), Validators.required],
dossierTemplateId: [
{
value: this.dossier.dossierTemplateId,
disabled: this._dossierStatsService.get(this.dossier.dossierId).hasFiles,
disabled: this._dossierStatsService.get(this.dossier.dossierId).hasFiles || !this.dossier.isActive,
},
Validators.required,
],
dossierStatusId: [{ value: this.dossier.dossierStatusId, disabled: !this.states.length }],
description: [this.dossier.description],
dueDate: [this.dossier.dueDate],
watermarkEnabled: [this.dossier.watermarkEnabled],
watermarkPreviewEnabled: [this.dossier.watermarkPreviewEnabled],
dossierStatusId: [{ value: this.dossier.dossierStatusId, disabled: !this.states.length || !this.dossier.isActive }],
description: [formFieldWithArchivedCheck(this.dossier.description)],
dueDate: [formFieldWithArchivedCheck(this.dossier.dueDate)],
});
}
#notifyDossierDeleted() {
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: { dossierName: this.dossier.dossierName } });
}
#filterInvalidDossierTemplates() {
this.dossierTemplates = this._dossierTemplatesService.all.filter(r => {
if (this.dossier?.dossierTemplateId === r.dossierTemplateId) {

View File

@ -6,7 +6,7 @@ import { TranslateService } from '@ngx-translate/core';
import { UserService } from '@services/user.service';
import { ManualAnnotationService } from '../../services/manual-annotation.service';
import { PermissionsService } from '@services/permissions.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { Dossier, ILegalBasisChangeRequest } from '@red/domain';
import { firstValueFrom } from 'rxjs';
@ -27,7 +27,7 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
legalOptions: LegalBasisOption[] = [];
constructor(
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _notificationService: Toaster,

View File

@ -6,7 +6,7 @@ import { ManualAnnotationService } from '../../services/manual-annotation.servic
import { PermissionsService } from '@services/permissions.service';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { Dictionary, Dossier, IAddRedactionRequest } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionaryService } from '@shared/services/dictionary.service';
import { BaseDialogComponent, CircleButtonTypes } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';
@ -36,14 +36,15 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
private readonly _formBuilder: FormBuilder,
private readonly _justificationsService: JustificationsService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _dossiersService: DossiersService,
private readonly _permissionsService: PermissionsService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dictionaryService: DictionaryService,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; dossierId: string },
) {
super(_injector, _dialogRef);
this._dossier = this._dossiersService.find(this.data.dossierId);
this._dossier = this._activeDossiersService.find(this.data.dossierId);
this.isFalsePositiveRequest = this.data.manualRedactionEntryWrapper.type === 'FALSE_POSITIVE';
this.isDictionaryRequest = this.data.manualRedactionEntryWrapper.type === 'DICTIONARY' || this.isFalsePositiveRequest;

View File

@ -1,23 +1,20 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SearchScreenComponent } from './screens/search-screen/search-screen.component';
import { FilePreviewGuard } from '@guards/file-preview.guard';
import { DossierFilesGuard } from '@guards/dossier-files-guard';
import { CompositeRouteGuard } from '@iqser/common-ui';
import { BreadcrumbTypes } from '@red/domain';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
import { ACTIVE_DOSSIERS_SERVICE } from '../../tokens';
const routes: Routes = [
{
path: 'search',
component: SearchScreenComponent,
},
{
path: `:${DOSSIER_ID}`,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [DossierFilesGuard],
breadcrumbs: [BreadcrumbTypes.main, BreadcrumbTypes.dossier],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
},
loadChildren: () => import('./screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
},
@ -27,6 +24,7 @@ const routes: Routes = [
data: {
routeGuards: [DossierFilesGuard, FilePreviewGuard],
breadcrumbs: [BreadcrumbTypes.main, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
},
loadChildren: () => import('./screens/file-preview-screen/file-preview.module').then(m => m.FilePreviewModule),
},

View File

@ -1,66 +1,31 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AddDossierDialogComponent } from './dialogs/add-dossier-dialog/add-dossier-dialog.component';
import { AssignReviewerApproverDialogComponent } from './dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
import { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
import { ForceAnnotationDialogComponent } from './dialogs/force-redaction-dialog/force-annotation-dialog.component';
import { RemoveAnnotationsDialogComponent } from './dialogs/remove-annotations-dialog/remove-annotations-dialog.component';
import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component';
import { SharedModule } from '@shared/shared.module';
import { DossiersRoutingModule } from './dossiers-routing.module';
import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module';
import { DossiersDialogService } from './services/dossiers-dialog.service';
import { ManualAnnotationService } from './services/manual-annotation.service';
import { AnnotationProcessingService } from './services/annotation-processing.service';
import { EditDossierDialogComponent } from './dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
import { EditDossierGeneralInfoComponent } from './dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component';
import { EditDossierDownloadPackageComponent } from './dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component';
import { EditDossierDictionaryComponent } from './dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component';
import { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { RecategorizeImageDialogComponent } from './dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
import { EditDossierAttributesComponent } from './dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.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 { OverlayModule } from '@angular/cdk/overlay';
import { SharedDossiersModule } from './shared/shared-dossiers.module';
import { ResizeAnnotationDialogComponent } from './dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
import { EditDossierTeamComponent } from './dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component';
import { HighlightActionDialogComponent } from './screens/file-preview-screen/dialogs/highlight-action-dialog/highlight-action-dialog.component';
import { ColorPickerModule } from 'ngx-color-picker';
const screens = [SearchScreenComponent];
const dialogs = [
AddDossierDialogComponent,
EditDossierDialogComponent,
ManualAnnotationDialogComponent,
ForceAnnotationDialogComponent,
RemoveAnnotationsDialogComponent,
ResizeAnnotationDialogComponent,
DocumentInfoDialogComponent,
AssignReviewerApproverDialogComponent,
ChangeLegalBasisDialogComponent,
RecategorizeImageDialogComponent,
HighlightActionDialogComponent,
];
const components = [
EditDossierGeneralInfoComponent,
EditDossierDownloadPackageComponent,
EditDossierDictionaryComponent,
EditDossierAttributesComponent,
EditDossierTeamComponent,
EditDossierDeletedDocumentsComponent,
...screens,
...dialogs,
];
const services = [DossiersDialogService, ManualAnnotationService, AnnotationProcessingService];
@NgModule({
declarations: [...components],
providers: [...services],
declarations: [...dialogs],
imports: [
CommonModule,
SharedModule,

View File

@ -182,7 +182,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
this.#canToggleAnalysis = this._permissionsService.canToggleAnalysis(this.selectedFiles);
this.#canOcr = this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true);
this.#canOcr = this._permissionsService.canOcrFile(this.selectedFiles);
this.#canSetToUnderReview = this._permissionsService.canSetUnderReview(this.selectedFiles) && !isWorkflow;

View File

@ -5,7 +5,7 @@ import { DossierTemplatesService } from '@services/entity-services/dossier-templ
import { FilesService } from '@services/entity-services/files.service';
import { firstValueFrom, Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStatsService } from '../../../../../../services/dossiers/dossier-stats.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
@Component({
@ -43,7 +43,7 @@ export class DossierDetailsStatsComponent implements OnInit {
openEditDossierDialog(section: string): void {
const data = { dossierId: this.dossier.dossierId, section };
this._dialogService.openDialog('editDossier', null, data, async () => {
await firstValueFrom(this._filesService.loadAll(this.dossier.dossierId));
await firstValueFrom(this._filesService.loadAll(this.dossier.dossierId, this.dossier.routerPath));
});
}
}

View File

@ -3,17 +3,17 @@ import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/si
import { TranslateChartService } from '@services/translate-chart.service';
import { UserService } from '@services/user.service';
import { FilterService, ProgressBarConfigModel, shareLast, Toaster } from '@iqser/common-ui';
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { workflowFileStatusTranslations } from '../../../../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Dossier, DossierAttributeWithValue, DossierStats, IDossierRequest, StatusSorter, User } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router';
import { firstValueFrom, Observable } from 'rxjs';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStatsService } from '../../../../../../services/dossiers/dossier-stats.service';
import { map, pluck, switchMap } from 'rxjs/operators';
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
import { FilesService } from '@services/entity-services/files.service';
import { DOSSIER_ID } from '@utils/constants';
import { DossiersService } from '../../../../../../services/dossiers/dossiers.service';
@Component({
selector: 'redaction-dossier-details',
@ -37,19 +37,19 @@ export class DossierDetailsComponent {
statusConfig$: Observable<ProgressBarConfigModel[]>;
constructor(
readonly dossiersService: DossiersService,
readonly translateChartService: TranslateChartService,
readonly filterService: FilterService,
private readonly _dossiersService: DossiersService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _userService: UserService,
private readonly _filesService: FilesService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _toaster: Toaster,
private readonly _dialogService: DossiersDialogService,
activatedRoute: ActivatedRoute,
private readonly _activatedRoute: ActivatedRoute,
) {
this.dossierId = activatedRoute.snapshot.paramMap.get(DOSSIER_ID);
this.dossier$ = this.dossiersService.getEntityChanged$(this.dossierId).pipe(shareLast());
this.dossierId = _activatedRoute.snapshot.paramMap.get(DOSSIER_ID);
this.dossier$ = this._dossiersService.getEntityChanged$(this.dossierId).pipe(shareLast());
this.dossierStats$ = this.dossier$.pipe(
pluck('dossierId'),
switchMap(dossierId => this._dossierStatsService.watch$(dossierId)),
@ -65,7 +65,7 @@ export class DossierDetailsComponent {
async assignOwner(user: User | string, dossier: Dossier) {
const owner = typeof user === 'string' ? this._userService.find(user) : user;
const dossierRequest: IDossierRequest = { ...dossier, ownerId: owner.id };
await firstValueFrom(this.dossiersService.createOrUpdate(dossierRequest));
await firstValueFrom(this._dossiersService.createOrUpdate(dossierRequest));
const ownerName = this._userService.getNameForId(owner.id);
const dossierName = dossier.dossierName;

View File

@ -1,5 +1,5 @@
<iqser-page-header
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
(closeAction)="router.navigate(['/main', dossiersService.routerPath])"
[actionConfigs]="actionConfigs"
[helpModeKey]="'document'"
[showCloseButton]="true"
@ -8,17 +8,18 @@
<redaction-file-download-btn
[disabled]="listingService.areSomeSelected$ | async"
[files]="entitiesService.all$ | async"
tooltipPosition="below"
iqserHelpMode="edit_dossier_in_dossier"
tooltipPosition="below"
></redaction-file-download-btn>
<iqser-circle-button
(action)="downloadDossierAsCSV()"
*ngIf="permissionsService.canDownloadCsvReport(dossier)"
[disabled]="listingService.areSomeSelected$ | async"
[tooltip]="'dossier-overview.header-actions.download-csv' | translate"
icon="iqser:csv"
tooltipPosition="below"
iqserHelpMode="edit_dossier_in_dossier"
tooltipPosition="below"
></iqser-circle-button>
<iqser-circle-button
@ -34,12 +35,13 @@
<iqser-circle-button
(action)="upload.emit()"
*ngIf="permissionsService.canUploadFiles(dossier)"
[tooltip]="'dossier-overview.header-actions.upload-document' | translate"
[type]="circleButtonTypes.primary"
class="ml-14"
icon="iqser:upload"
tooltipPosition="below"
iqserHelpMode="edit_dossier_in_dossier"
tooltipPosition="below"
></iqser-circle-button>
</iqser-page-header>

View File

@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { RouterHistoryService } from '@services/router-history.service';
import {
ActionConfig,
CircleButtonTypes,
@ -21,14 +20,16 @@ import { UserService } from '@services/user.service';
import { ConfigService } from '../../config.service';
import { firstValueFrom } from 'rxjs';
import { PrimaryFileAttributeService } from '../../../../../../services/primary-file-attribute.service';
import { Router } from '@angular/router';
import { DossiersService } from '../../../../../../services/dossiers/dossiers.service';
@Component({
selector: 'redaction-screen-header',
templateUrl: './screen-header.component.html',
styleUrls: ['./screen-header.component.scss'],
selector: 'redaction-dossier-overview-screen-header',
templateUrl: './dossier-overview-screen-header.component.html',
styleUrls: ['./dossier-overview-screen-header.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ScreenHeaderComponent implements OnInit {
export class DossierOverviewScreenHeaderComponent implements OnInit {
@Input() @Required() dossier: Dossier;
@Output() @Required() readonly upload = new EventEmitter<void>();
readonly circleButtonTypes = CircleButtonTypes;
@ -37,12 +38,13 @@ export class ScreenHeaderComponent implements OnInit {
constructor(
private readonly _toaster: Toaster,
readonly configService: ConfigService,
readonly router: Router,
readonly dossiersService: DossiersService,
private readonly _userService: UserService,
readonly listingService: ListingService<File>,
readonly sortingService: SortingService<File>,
readonly permissionsService: PermissionsService,
readonly entitiesService: EntitiesService<File>,
readonly routerHistoryService: RouterHistoryService,
private readonly _reanalysisService: ReanalysisService,
private readonly _loadingService: LoadingService,
private readonly _primaryFileAttributeService: PrimaryFileAttributeService,
@ -55,7 +57,7 @@ export class ScreenHeaderComponent implements OnInit {
async reanalyseDossier() {
this._loadingService.start();
try {
await firstValueFrom(this._reanalysisService.reanalyzeDossier(this.dossier.dossierId, true));
await firstValueFrom(this._reanalysisService.reanalyzeDossier(this.dossier, true));
this._toaster.success(_('dossier-overview.reanalyse-dossier.success'));
} catch (e) {
this._toaster.error(_('dossier-overview.reanalyse-dossier.error'));

View File

@ -1,8 +1,8 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { File } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { UserService } from '@services/user.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { DossiersService } from '../../../../../../../services/dossiers/dossiers.service';
@Component({
selector: 'redaction-file-workload',
@ -44,6 +44,7 @@ export class FileWorkloadComponent {
}
private _getDictionaryColor(type: string) {
return this._dictionariesMapService.getDictionaryColor(type, this._dossiersService.find(this.file.dossierId).dossierTemplateId);
const dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId;
return this._dictionariesMapService.getDictionaryColor(type, dossierTemplateId);
}
}

View File

@ -12,7 +12,7 @@ import {
WorkflowConfig,
} from '@iqser/common-ui';
import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { PermissionsService } from '@services/permissions.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
@ -164,7 +164,7 @@ export class ConfigService {
checkedRequiredFilters: () => NestedFilter[],
checkedNotRequiredFilters: () => NestedFilter[],
) {
const allDistinctWorkflowFileStatuses = new Set<string>();
const allDistinctWorkflowFileStatuses = new Set<WorkflowFileStatus>();
const allDistinctPeople = new Set<string>();
const allDistinctAddedDates = new Set<string>();
const allDistinctNeedsWork = new Set<string>();
@ -328,7 +328,9 @@ export class ConfigService {
}
_recentlyModifiedChecker = (file: File) =>
moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
moment(file.lastUpdated)
.add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS as number, 'hours')
.isAfter(moment());
_assignedToMeChecker = (file: File) => file.assignee === this._userService.currentUser.id;

View File

@ -9,15 +9,13 @@ import { DossierOverviewBulkActionsComponent } from './components/bulk-actions/d
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';
import { FileWorkloadComponent } from './components/table-item/file-workload/file-workload.component';
import { FileStatsComponent } from './components/file-stats/file-stats.component';
import { WorkflowItemComponent } from './components/workflow-item/workflow-item.component';
import { ScreenHeaderComponent } from './components/screen-header/screen-header.component';
import { DossierOverviewScreenHeaderComponent } from './components/screen-header/dossier-overview-screen-header.component';
import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component';
import { FileNameColumnComponent } from './components/table-item/file-name-column/file-name-column.component';
import { BulkActionsService } from './services/bulk-actions.service';
const routes: Routes = [
{
@ -40,11 +38,10 @@ const routes: Routes = [
TableItemComponent,
FileStatsComponent,
WorkflowItemComponent,
ScreenHeaderComponent,
DossierOverviewScreenHeaderComponent,
ViewModeSelectionComponent,
FileNameColumnComponent,
],
providers: [ConfigService, BulkActionsService],
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule],
})
export class DossierOverviewModule {}

View File

@ -1,6 +1,9 @@
<ng-container *ngIf="dossier$ | async as dossier">
<section>
<redaction-screen-header (upload)="fileInput.click()" [dossier]="dossier"></redaction-screen-header>
<redaction-dossier-overview-screen-header
(upload)="fileInput.click()"
[dossier]="dossier"
></redaction-dossier-overview-screen-header>
<div class="overlay-shadow"></div>
@ -18,7 +21,7 @@
[noDataText]="'dossier-overview.no-data.title' | translate"
[noMatchText]="'dossier-overview.no-match.title' | translate"
[selectionEnabled]="true"
[showNoDataButton]="true"
[showNoDataButton]="permissionsService.canUploadFiles(dossier)"
[tableColumnConfigs]="tableColumnConfigs"
[tableItemClasses]="{ disabled: disabledFn, 'last-opened': lastOpenedFn }"
helpModeKey="document-list"

View File

@ -38,17 +38,25 @@ import { PermissionsService } from '@services/permissions.service';
import { ActivatedRoute, Router } from '@angular/router';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { ConfigService } from '../config.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { FilesService } from '@services/entity-services/files.service';
import { DOSSIER_ID } from '@utils/constants';
import { BulkActionsService } from '../services/bulk-actions.service';
import { DossiersService } from '../../../../../services/dossiers/dossiers.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
@Component({
templateUrl: './dossier-overview-screen.component.html',
styleUrls: ['./dossier-overview-screen.component.scss'],
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }],
providers: [
...DefaultListingServices,
ConfigService,
BulkActionsService,
{ provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) },
dossiersServiceProvider,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossierOverviewScreenComponent extends ListingComponent<File> implements OnInit, OnDestroy, OnAttach {
@ -75,8 +83,8 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
constructor(
protected readonly _injector: Injector,
private readonly _router: Router,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
private readonly _loadingService: LoadingService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _fileUploadService: FileUploadService,
private readonly _filesService: FilesService,
@ -87,12 +95,12 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
private readonly _userPreferenceService: UserPreferenceService,
private readonly _fileMapService: FilesMapService,
private readonly _errorService: ErrorService,
private readonly _route: ActivatedRoute,
readonly permissionsService: PermissionsService,
readonly configService: ConfigService,
readonly activatedRoute: ActivatedRoute,
) {
super(_injector);
this.dossierId = activatedRoute.snapshot.paramMap.get(DOSSIER_ID);
this.dossierId = _route.snapshot.paramMap.get(DOSSIER_ID);
this.dossier$ = this._dossiersService
.getEntityChanged$(this.dossierId)
.pipe(tap(dossier => (this.dossierTemplateId = dossier.dossierTemplateId)));
@ -125,7 +133,9 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
)
.subscribe();
this._fileDropOverlayService.initFileDropHandling(this.dossierId);
if (this.currentDossier.isActive) {
this._fileDropOverlayService.initFileDropHandling(this.dossierId);
}
this.addSubscription = this.configService.listingMode$.subscribe(() => {
this._computeAllFilters();
@ -134,7 +144,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
this.addSubscription = this._dossiersService.dossierFileChanges$
.pipe(
filter(dossierId => dossierId === this.dossierId),
switchMap(dossierId => this._filesService.loadAll(dossierId)),
switchMap(dossierId => this._filesService.loadAll(dossierId, this._dossiersService.routerPath)),
)
.subscribe();

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { Dossier, File } from '@red/domain';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { ConfirmationDialogInput, LoadingService } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../services/dossiers/active-dossiers.service';
import { FilesService } from '@services/entity-services/files.service';
import { FileAssignService } from '../../../shared/services/file-assign.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -15,7 +15,7 @@ export class BulkActionsService {
constructor(
private readonly _dialogService: DossiersDialogService,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _filesService: FilesService,
private readonly _fileAssignService: FileAssignService,
private readonly _reanalysisService: ReanalysisService,
@ -29,13 +29,7 @@ export class BulkActionsService {
this._assignFiles(files, 'approver', true);
} else {
this._loadingService.start();
await firstValueFrom(
this._filesService.setUnderApprovalFor(
files.map(f => f.id),
dossier.id,
dossier.approverIds[0],
),
);
await firstValueFrom(this._filesService.setUnderApprovalFor(files, dossier.id, dossier.approverIds[0]));
this._loadingService.stop();
}
}
@ -46,12 +40,7 @@ export class BulkActionsService {
async ocr(files: File[]) {
this._loadingService.start();
await firstValueFrom(
this._reanalysisService.ocrFiles(
files.map(f => f.fileId),
files[0].dossierId,
),
);
await firstValueFrom(this._reanalysisService.ocrFiles(files, files[0].dossierId));
this._loadingService.stop();
}
@ -65,12 +54,7 @@ export class BulkActionsService {
}),
async () => {
this._loadingService.start();
await firstValueFrom(
this._fileManagementService.delete(
files.map(item => item.fileId),
files[0].dossierId,
),
);
await firstValueFrom(this._fileManagementService.delete(files, files[0].dossierId));
this._loadingService.stop();
},
);
@ -78,9 +62,8 @@ export class BulkActionsService {
async reanalyse(files: File[]) {
this._loadingService.start();
const fileIds = files.map(file => file.fileId);
await firstValueFrom(
this._reanalysisService.reanalyzeFilesForDossier(fileIds, files[0].dossierId, { force: true, triggeredByUser: true }),
this._reanalysisService.reanalyzeFilesForDossier(files, files[0].dossierId, { force: true, triggeredByUser: true }),
);
this._loadingService.stop();
}
@ -93,24 +76,13 @@ export class BulkActionsService {
async toggleAnalysis(files: File[], excluded: boolean) {
this._loadingService.start();
await firstValueFrom(
this._reanalysisService.toggleAnalysis(
files[0].dossierId,
files.map(f => f.id),
excluded,
),
);
await firstValueFrom(this._reanalysisService.toggleAnalysis(files[0].dossierId, files, excluded));
this._loadingService.stop();
}
async backToUnderReview(files: File[]): Promise<void> {
this._loadingService.start();
await firstValueFrom(
this._filesService.setUnderReviewFor(
files.map(f => f.id),
files[0].dossierId,
),
);
await firstValueFrom(this._filesService.setUnderReviewFor(files, files[0].dossierId));
this._loadingService.stop();
}
@ -135,23 +107,13 @@ export class BulkActionsService {
}),
async () => {
this._loadingService.start();
await firstValueFrom(
this._filesService.setApprovedFor(
files.map(f => f.id),
files[0].dossierId,
),
);
await firstValueFrom(this._filesService.setApprovedFor(files, files[0].dossierId));
this._loadingService.stop();
},
);
} else {
this._loadingService.start();
await firstValueFrom(
this._filesService.setApprovedFor(
files.map(f => f.id),
files[0].dossierId,
),
);
await firstValueFrom(this._filesService.setApprovedFor(files, files[0].dossierId));
this._loadingService.stop();
}
}
@ -161,7 +123,7 @@ export class BulkActionsService {
}
private _getDossier(files: File[]): Dossier {
return this._dossiersService.find(files[0].dossierId);
return this._activeDossiersService.find(files[0].dossierId);
}
private _assignFiles(files: File[], mode: 'reviewer' | 'approver', ignoreChanged = false, withUnassignedOption = false) {

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { DossierStats, StatusSorter } from '../../../../../../../../../../libs/red-domain/src';
import { DossierStats, StatusSorter } from '@red/domain';
import { List, StatusBarConfig } from '@iqser/common-ui';
@Component({

View File

@ -2,15 +2,15 @@
<iqser-circle-button
(action)="openEditDossierDialog($event, dossier.dossierId)"
*ngIf="currentUser.isUser"
[icon]="currentUser.isManager ? 'iqser:edit' : 'red:info'"
[scrollableParentView]="scrollableParentView"
[tooltip]="(currentUser.isManager ? 'dossier-listing.edit.action' : 'dossier-listing.dossier-info.action') | translate"
[type]="circleButtonTypes.dark"
[icon]="currentUser.isManager ? 'iqser:edit' : 'red:info'"
iqserHelpMode="edit_dossier"
[scrollableParentView]="scrollableParentView"
></iqser-circle-button>
<iqser-circle-button
(action)="reanalyseDossier($event, dossier.dossierId)"
(action)="reanalyseDossier($event, dossier)"
*ngIf="displayReanalyseBtn"
[tooltip]="'dossier-listing.reanalyse.action' | translate"
[type]="circleButtonTypes.dark"
@ -19,8 +19,8 @@
<redaction-file-download-btn
[files]="files"
[scrollableParentView]="scrollableParentView"
[type]="circleButtonTypes.dark"
iqserHelpMode="download_dossier"
[scrollableParentView]="scrollableParentView"
></redaction-file-download-btn>
</div>

View File

@ -8,7 +8,7 @@ import { LongPressEvent } from '@shared/directives/long-press.directive';
import { UserPreferenceService } from '@services/user-preference.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { ReanalysisService } from '@services/reanalysis.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
import { firstValueFrom } from 'rxjs';
@Component({
@ -32,13 +32,17 @@ export class DossiersListingActionsComponent implements OnChanges {
constructor(
private readonly _reanalysisService: ReanalysisService,
private readonly _userService: UserService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
readonly permissionsService: PermissionsService,
readonly filesMapService: FilesMapService,
private readonly _dialogService: DossiersDialogService,
private readonly _userPreferenceService: UserPreferenceService,
) {}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.VIRTUAL_SCROLL;
}
ngOnChanges() {
this.files = this.filesMapService.get(this.dossier.dossierId);
this.displayReanalyseBtn = this.permissionsService.displayReanalyseBtn(this.dossier) && this.analysisForced;
@ -52,12 +56,8 @@ export class DossiersListingActionsComponent implements OnChanges {
this._dialogService.openDialog('editDossier', $event, { dossierId });
}
async reanalyseDossier($event: MouseEvent, id: string): Promise<void> {
async reanalyseDossier($event: MouseEvent, dossier: Dossier): Promise<void> {
$event.stopPropagation();
await firstValueFrom(this._reanalysisService.reanalyzeDossier(id));
}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.VIRTUAL_SCROLL;
await firstValueFrom(this._reanalysisService.reanalyzeDossier(dossier));
}
}

View File

@ -4,10 +4,10 @@
[config]="config"
[radius]="80"
[strokeWidth]="15"
[subtitle]="'dossier-listing.stats.charts.dossiers' | translate: { count: dossiersService.all.length }"
[subtitle]="'dossier-listing.stats.charts.dossiers' | translate: { count: activeDossiersService.all.length }"
></redaction-simple-doughnut-chart>
<div *ngIf="dossiersService.generalStats$ | async as stats" class="dossier-stats-container">
<div *ngIf="activeDossiersService.generalStats$ | async as stats" class="dossier-stats-container">
<div class="dossier-stats-item">
<mat-icon svgIcon="red:needs-work"></mat-icon>
<div>

View File

@ -1,14 +1,14 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { FilterService, mapEach } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
import { combineLatest, Observable } from 'rxjs';
import { DossierStats, FileCountPerWorkflowStatus, StatusSorter } from '@red/domain';
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { workflowFileStatusTranslations } from '../../../../../../translations/file-status-translations';
import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service';
import { DossierStatsService } from '../../../../../../services/dossiers/dossier-stats.service';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { TranslateService } from '@ngx-translate/core';
@Component({
@ -23,25 +23,25 @@ export class DossiersListingDetailsComponent {
constructor(
readonly filterService: FilterService,
readonly dossiersService: DossiersService,
readonly activeDossiersService: ActiveDossiersService,
private readonly _dossierStatsMap: DossierStatsService,
private readonly _translateChartService: TranslateChartService,
private readonly _dossierStateService: DossierStateService,
private readonly _translateService: TranslateService,
) {
this.documentsChartData$ = this.dossiersService.all$.pipe(
this.documentsChartData$ = this.activeDossiersService.all$.pipe(
mapEach(dossier => _dossierStatsMap.watch$(dossier.dossierId)),
switchMap(stats$ => combineLatest(stats$)),
filter(stats => !stats.some(s => s === undefined)),
map(stats => this._toChartData(stats)),
);
this.dossiersChartData$ = this.dossiersService.all$.pipe(map(() => this._toDossierChartData()));
this.dossiersChartData$ = this.activeDossiersService.all$.pipe(map(() => this._toDossierChartData()));
}
private _toDossierChartData(): DoughnutChartConfig[] {
this._dossierStateService.all.forEach(
state => (state.dossierCount = this.dossiersService.getCountWithState(state.dossierStatusId)),
state => (state.dossierCount = this.activeDossiersService.getCountWithState(state.dossierStatusId)),
);
const configArray: DoughnutChartConfig[] = [
...this._dossierStateService.all
@ -54,7 +54,7 @@ export class DossiersListingDetailsComponent {
.values(),
];
const notAssignedLength = this.dossiersService.all.length - configArray.map(v => v.value).reduce((acc, val) => acc + val, 0);
const notAssignedLength = this.activeDossiersService.all.length - configArray.map(v => v.value).reduce((acc, val) => acc + val, 0);
configArray.push({
value: notAssignedLength,
label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder'),

View File

@ -1,13 +0,0 @@
<ng-container *ngIf="dossier.dossierStatusId && currentState">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ currentState.name }}</div>
<redaction-small-chip [color]="currentState.color"></redaction-small-chip>
</div>
</ng-container>
<ng-container *ngIf="!dossier.dossierStatusId || !currentState">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ 'edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate }}</div>
<redaction-small-chip [color]="'#E2E4E9'"></redaction-small-chip>
</div>
</ng-container>

View File

@ -21,7 +21,7 @@
</div>
<div class="cell">
<redaction-dossiers-listing-status [dossier]="dossier"></redaction-dossiers-listing-status>
<redaction-dossier-status [dossier]="dossier"></redaction-dossier-status>
<redaction-dossiers-listing-actions [dossier]="dossier" [stats]="stats"></redaction-dossiers-listing-actions>
</div>

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStatsService } from '../../../../../../services/dossiers/dossier-stats.service';
import { BehaviorSubject, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';

View File

@ -5,12 +5,12 @@ 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 { workflowFileStatusTranslations } from '../../translations/file-status-translations';
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { dossierMemberChecker, dossierStateChecker, dossierTemplateChecker, RedactionFilterSorter } from '@utils/index';
import { workloadTranslations } from '../../translations/workload-translations';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStateService } from '../../../../services/entity-services/dossier-state.service';
import { DossierStatsService } from '../../../../services/dossiers/dossier-stats.service';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
@Injectable()
export class ConfigService {

View File

@ -7,12 +7,10 @@ import { RouterModule, Routes } 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';
import { DossierWorkloadColumnComponent } from './components/dossier-workload-column/dossier-workload-column.component';
import { DossiersListingStatusComponent } from './components/dossiers-listing-status/dossiers-listing-status.component';
import { DossierDocumentsStatusComponent } from './components/dossier-documents-status/dossier-documents-status.component';
const routes: Routes = [
@ -29,10 +27,8 @@ const routes: Routes = [
DossiersListingScreenComponent,
DossiersListingActionsComponent,
DossiersListingDetailsComponent,
DossiersListingDossierNameComponent,
DossierWorkloadColumnComponent,
TableItemComponent,
DossiersListingStatusComponent,
DossierDocumentsStatusComponent,
],
providers: [ConfigService],

View File

@ -8,7 +8,7 @@ import { DossiersDialogService } from '../../../services/dossiers-dialog.service
import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach, TableComponent } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../config.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../services/dossiers/active-dossiers.service';
import { FilesService } from '@services/entity-services/files.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { tap } from 'rxjs/operators';
@ -18,7 +18,7 @@ import { tap } from 'rxjs/operators';
styleUrls: ['./dossiers-listing-screen.component.scss'],
providers: [
...DefaultListingServicesTmp,
{ provide: EntitiesService, useExisting: DossiersService },
{ provide: EntitiesService, useExisting: ActiveDossiersService },
{ provide: ListingComponent, useExisting: forwardRef(() => DossiersListingScreenComponent) },
],
changeDetection: ChangeDetectionStrategy.OnPush,
@ -40,7 +40,7 @@ export class DossiersListingScreenComponent extends ListingComponent<Dossier> im
protected readonly _injector: Injector,
private readonly _userService: UserService,
readonly permissionsService: PermissionsService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dialogService: DossiersDialogService,
private readonly _translateChartService: TranslateChartService,
private readonly _configService: ConfigService,
@ -55,7 +55,7 @@ export class DossiersListingScreenComponent extends ListingComponent<Dossier> im
}
ngOnInit(): void {
this.addSubscription = this._dossiersService.all$.pipe(tap(() => this._computeAllFilters())).subscribe();
this.addSubscription = this._activeDossiersService.all$.pipe(tap(() => this._computeAllFilters())).subscribe();
}
ngOnAttach(): void {

View File

@ -72,6 +72,10 @@ export class AnnotationActionsComponent implements OnChanges {
return this.annotations?.length === 1 && this.annotations?.[0].resizing;
}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.ANNOTATIONS_LIST;
}
async ngOnChanges(): Promise<void> {
await this._setPermissions();
}
@ -115,10 +119,6 @@ export class AnnotationActionsComponent implements OnChanges {
this.annotationActionsService.cancelResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.ANNOTATIONS_LIST;
}
private async _setPermissions() {
const dossier = await this._state.dossier;
this.annotationPermissions = AnnotationPermissions.forUser(

View File

@ -3,7 +3,7 @@ import { DossiersDialogService } from '../../../../services/dossiers-dialog.serv
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DocumentInfoService } from '../../services/document-info.service';
import { combineLatest, Observable, switchMap } from 'rxjs';
import { PermissionsService } from '../../../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { map } from 'rxjs/operators';
import { File } from '@red/domain';

View File

@ -40,7 +40,7 @@
<div class="flex-center">
<mat-icon class="primary-white" svgIcon="red:read-only"></mat-icon>
<span class="read-only-text" translate="readonly"></span>
<span [translate]="(state.dossier$ | async).isActive ? 'readonly' : 'readonly-archived'" class="read-only-text"></span>
</div>
</div>

View File

@ -67,7 +67,7 @@ export class PageExclusionComponent {
endPage,
};
});
const excludePages$ = this._reanalysisService.excludePages({ pageRanges }, this._state.dossierId, this._state.fileId);
const excludePages$ = this._reanalysisService.excludePages({ pageRanges }, file.dossierId, file);
await firstValueFrom(excludePages$);
this._inputComponent.reset();
} catch (e) {
@ -78,12 +78,13 @@ export class PageExclusionComponent {
async includePagesRange(range: IPageRange): Promise<void> {
this._loadingService.start();
const file = await this._state.file;
const includePages$ = this._reanalysisService.includePages(
{
pageRanges: [range],
},
this._state.dossierId,
this._state.fileId,
file.dossierId,
file,
);
await firstValueFrom(includePages$);
this._inputComponent.reset();

View File

@ -3,7 +3,7 @@ import { Dossier, File, StatusBarConfigs, User } from '@red/domain';
import { List, LoadingService, Toaster } from '@iqser/common-ui';
import { PermissionsService } from '@services/permissions.service';
import { FileAssignService } from '../../../../shared/services/file-assign.service';
import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { workflowFileStatusTranslations } from '../../../../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
import { FilesService } from '@services/entity-services/files.service';
@ -11,7 +11,7 @@ import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, combineLatest, firstValueFrom, Observable, switchMap } from 'rxjs';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
@Component({
selector: 'redaction-user-management',
@ -42,9 +42,9 @@ export class UserManagementComponent {
readonly loadingService: LoadingService,
readonly translateService: TranslateService,
readonly stateService: FilePreviewStateService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
) {
this._dossier$ = this.stateService.file$.pipe(switchMap(file => this._dossiersService.getEntityChanged$(file.dossierId)));
this._dossier$ = this.stateService.file$.pipe(switchMap(file => this._activeDossiersService.getEntityChanged$(file.dossierId)));
this.statusBarConfig$ = this.stateService.file$.pipe(map(file => [{ length: 1, color: file.workflowStatus }]));
this.assignTooltip$ = this.stateService.file$.pipe(
map(file =>
@ -96,15 +96,15 @@ export class UserManagementComponent {
const assigneeId = typeof user === 'string' ? user : user?.id;
const reviewerName = this.userService.getNameForId(assigneeId);
const { dossierId, fileId, filename } = file;
const { dossierId, filename } = file;
this.loadingService.start();
if (!assigneeId) {
await firstValueFrom(this.filesService.setUnassigned([fileId], dossierId));
await firstValueFrom(this.filesService.setUnassigned([file], dossierId));
} else if (file.isNew || file.isUnderReview) {
await firstValueFrom(this.filesService.setReviewerFor([fileId], dossierId, assigneeId));
await firstValueFrom(this.filesService.setReviewerFor([file], dossierId, assigneeId));
} else if (file.isUnderApproval) {
await firstValueFrom(this.filesService.setUnderApprovalFor([fileId], dossierId, assigneeId));
await firstValueFrom(this.filesService.setUnderApprovalFor([file], dossierId, assigneeId));
}
this.loadingService.stop();

View File

@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { PermissionsService } from '@services/permissions.service';
import { Dictionary, Dossier } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../../services/dossiers/active-dossiers.service';
import { BaseDialogComponent } from '@iqser/common-ui';
import { DictionaryService } from '@shared/services/dictionary.service';
import { ManualAnnotationService } from '../../../../services/manual-annotation.service';
@ -33,14 +33,14 @@ export class AcceptRecommendationDialogComponent extends BaseDialogComponent imp
private readonly _formBuilder: FormBuilder,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _permissionsService: PermissionsService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dictionaryService: DictionaryService,
protected readonly _injector: Injector,
protected readonly _dialogRef: MatDialogRef<AcceptRecommendationDialogComponent, AcceptRecommendationReturnType>,
@Inject(MAT_DIALOG_DATA) readonly data: AcceptRecommendationData,
) {
super(_injector, _dialogRef);
this._dossier = this._dossiersService.find(this.data.dossierId);
this._dossier = this._activeDossiersService.find(this.data.dossierId);
this.isDocumentAdmin = this._permissionsService.isApprover(this._dossier);
this.form = this._getForm();
this.initialFormValue = this.form.getRawValue();

View File

@ -10,6 +10,9 @@ import { FilePreviewStateService } from './services/file-preview-state.service';
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
import { AnnotationReferencesService } from './services/annotation-references.service';
import { FilterService } from '@iqser/common-ui';
import { ManualAnnotationService } from '../../services/manual-annotation.service';
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
import { PageRotationService } from './services/page-rotation.service';
import { PdfViewer } from './services/pdf-viewer.service';
@ -28,4 +31,7 @@ export const filePreviewScreenProviders = [
AnnotationReferencesService,
PageRotationService,
PdfViewer,
ManualAnnotationService,
AnnotationProcessingService,
dossiersServiceProvider,
];

View File

@ -34,7 +34,6 @@ import { clearStamps, stampPDFPage } from '@utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';
import { handleFilterDelta } from '@utils/filter-utils';
import { FilesService } from '@services/entity-services/files.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service';
@ -50,6 +49,7 @@ import { FilePreviewStateService } from './services/file-preview-state.service';
import { FileDataModel } from '../../../../models/file/file-data.model';
import { filePreviewScreenProviders } from './file-preview-providers';
import { ManualAnnotationService } from '../../services/manual-annotation.service';
import { DossiersService } from '../../../../services/dossiers/dossiers.service';
import { PageRotationService } from './services/page-rotation.service';
import { ComponentCanDeactivate } from '../../../../guards/can-deactivate.guard';
import Annotation = Core.Annotations.Annotation;
@ -254,7 +254,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const file = await this.stateService.file;
if (file?.analysisRequired && !file.excludedFromAutomaticAnalysis) {
const reanalyzeFiles = this._reanalysisService.reanalyzeFilesForDossier([this.fileId], this.dossierId, { force: true });
const reanalyzeFiles = this._reanalysisService.reanalyzeFilesForDossier([file], this.dossierId, { force: true });
await firstValueFrom(reanalyzeFiles);
}
@ -449,7 +449,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async annotationsChangedByReviewAction(annotation?: AnnotationWrapper) {
this.multiSelectService.deactivate();
const fileReloaded = await firstValueFrom(this._filesService.reload(this.dossierId, this.fileId));
const file = await this.stateService.file;
const fileReloaded = await firstValueFrom(this._filesService.reload(this.dossierId, file));
if (!fileReloaded) {
await this._reloadAnnotationsForPage(annotation?.pageNumber ?? this.activeViewerPage);
}
@ -574,7 +575,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private _subscribeToFileUpdates(): void {
this.addActiveScreenSubscription = timer(0, 5000)
.pipe(switchMap(() => this._filesService.reload(this.dossierId, this.fileId)))
.pipe(
switchMap(() => this.stateService.file$),
switchMap(file => this._filesService.reload(this.dossierId, file)),
)
.subscribe();
this.addActiveScreenSubscription = this._dossiersService

View File

@ -14,7 +14,7 @@ import { Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IR
import { toPosition } from '../../../utils/pdf-calculation.utils';
import { AnnotationDrawService } from './annotation-draw.service';
import { translateQuads } from '@utils/pdf-coordinates';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '../../../../../services/dossiers/active-dossiers.service';
import {
AcceptRecommendationData,
AcceptRecommendationDialogComponent,
@ -38,12 +38,12 @@ export class AnnotationActionsService {
private readonly _dialogService: DossiersDialogService,
private readonly _dialog: MatDialog,
private readonly _annotationDrawService: AnnotationDrawService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _screenStateService: FilePreviewStateService,
) {}
private get _dossier(): Dossier {
return this._dossiersService.find(this._screenStateService.dossierId);
return this._activeDossiersService.find(this._screenStateService.dossierId);
}
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {

View File

@ -3,7 +3,6 @@ import { Core, WebViewerInstance } from '@pdftron/webviewer';
import { hexToRgb } from '@utils/functions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { UserPreferenceService } from '@services/user-preference.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { RedactionLogService } from '../../../services/redaction-log.service';
import { environment } from '@environments/environment';
@ -11,6 +10,7 @@ import { IRectangle, ISectionGrid, ISectionRectangle } from '@red/domain';
import { SkippedService } from './skipped.service';
import { firstValueFrom } from 'rxjs';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { DossiersService } from '../../../../../services/dossiers/dossiers.service';
import Annotation = Core.Annotations.Annotation;
@Injectable()

View File

@ -1,15 +1,16 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector } from '@angular/core';
import { BehaviorSubject, firstValueFrom, from, Observable, pairwise, switchMap } from 'rxjs';
import { FileDataModel } from '@models/file/file-data.model';
import { Dossier, File } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { PermissionsService } from '../../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { boolFactory } from '@iqser/common-ui';
import { filter, startWith } from 'rxjs/operators';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
import { DossiersService } from '../../../../../services/dossiers/dossiers.service';
import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
import { wipeFilesCache } from '../../../../../../../../../libs/red-cache/src';
@Injectable()
@ -28,19 +29,19 @@ export class FilePreviewStateService {
readonly #fileData$ = new BehaviorSubject<FileDataModel | undefined>(undefined);
constructor(
dossiersService: DossiersService,
filesMapService: FilesMapService,
permissionsService: PermissionsService,
activatedRoute: ActivatedRoute,
private readonly _fileManagementService: FileManagementService,
private readonly _injector: Injector,
private readonly _route: ActivatedRoute,
private readonly _filesMapService: FilesMapService,
private readonly _permissionsService: PermissionsService,
) {
this.fileId = activatedRoute.snapshot.paramMap.get(FILE_ID);
this.dossierId = activatedRoute.snapshot.paramMap.get(DOSSIER_ID);
this.dossierTemplateId = dossiersService.find(this.dossierId).dossierTemplateId;
this.fileId = _route.snapshot.paramMap.get(FILE_ID);
this.dossierId = _route.snapshot.paramMap.get(DOSSIER_ID);
this.dossierTemplateId = this._dossiersService.find(this.dossierId).dossierTemplateId;
this.dossier$ = dossiersService.getEntityChanged$(this.dossierId);
this.file$ = filesMapService.watch$(this.dossierId, this.fileId);
[this.isReadonly$, this.isWritable$] = boolFactory(this.file$, file => !permissionsService.canPerformAnnotationActions(file));
this.dossier$ = this._dossiersService.getEntityChanged$(this.dossierId);
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
[this.isReadonly$, this.isWritable$] = boolFactory(this.file$, file => !_permissionsService.canPerformAnnotationActions(file));
this.fileData$ = this.#fileData$.asObservable().pipe(filter(value => !!value));
this.blob$ = this.#blob$;
@ -66,6 +67,10 @@ export class FilePreviewStateService {
return firstValueFrom(this.blob$);
}
private get _dossiersService(): DossiersService {
return dossiersServiceResolver(this._injector);
}
get #blob$() {
return this.file$.pipe(
startWith(undefined),

View File

@ -17,7 +17,7 @@ import { AnnotationActionMode } from '../models/annotation-action-mode.model';
import { annotationActionsTranslations } from '../translations/annotation-actions-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { Observable } from 'rxjs';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
@ -32,7 +32,7 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _toaster: Toaster,
private readonly _permissionsService: PermissionsService,
private readonly _dossiersService: DossiersService,
private readonly _activeDossiersService: ActiveDossiersService,
protected readonly _injector: Injector,
) {
super(_injector, 'manualRedaction');
@ -372,7 +372,7 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
}
private _dossier(dossierId: string): Dossier {
return this._dossiersService.find(dossierId);
return this._activeDossiersService.find(dossierId);
}
private _getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) {

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