diff --git a/apps/red-ui/src/app/components/breadcrumbs/breadcrumbs.component.html b/apps/red-ui/src/app/components/breadcrumbs/breadcrumbs.component.html index ddc8502a3..0399c8f59 100644 --- a/apps/red-ui/src/app/components/breadcrumbs/breadcrumbs.component.html +++ b/apps/red-ui/src/app/components/breadcrumbs/breadcrumbs.component.html @@ -13,6 +13,7 @@ [routerLink]="breadcrumb.routerLink" class="breadcrumb" routerLinkActive="active" + [id]="first ? 'navigateToActiveDossiers' : ''" > {{ breadcrumb.name$ | async }} diff --git a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts index f4351eaed..bb0a2a0e8 100644 --- a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts @@ -21,6 +21,7 @@ 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 { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component'; const routes: Routes = [ { path: '', redirectTo: 'dossier-templates', pathMatch: 'full' }, @@ -114,6 +115,14 @@ const routes: Routes = [ routeGuards: [AuthGuard, RedRoleGuard], }, }, + { + path: 'dossier-states', + component: DossierStatesListingScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + }, { path: 'default-colors', component: DefaultColorsScreenComponent, diff --git a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts b/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts index 7578ddd03..0be9e0c68 100644 --- a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts +++ b/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts @@ -62,6 +62,7 @@ export class AdminSideNavComponent implements OnInit { { screen: 'watermark', label: _('watermark') }, { screen: 'file-attributes', label: _('file-attributes') }, { screen: 'dossier-attributes', label: _('dossier-attributes') }, + { screen: 'dossier-states', label: _('dossier-states') }, { screen: 'reports', label: _('reports') }, { screen: 'justifications', label: _('justifications') }, ], diff --git a/apps/red-ui/src/app/modules/admin/admin.module.ts b/apps/red-ui/src/app/modules/admin/admin.module.ts index c36811d81..f4087d8b1 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -46,6 +46,10 @@ import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form. import { FileAttributesConfigurationsDialogComponent } from './dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component'; import { SharedAdminModule } from './shared/shared-admin.module'; import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component'; +import { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component'; +import { AddEditDossierStateDialogComponent } from './dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component'; +import { A11yModule } from '@angular/cdk/a11y'; +import { ConfirmDeleteDossierStateDialogComponent } from './dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component'; const dialogs = [ AddEditDossierTemplateDialogComponent, @@ -95,8 +99,22 @@ const components = [ ]; @NgModule({ - declarations: [...components], + declarations: [ + ...components, + DossierStatesListingScreenComponent, + AddEditDossierStateDialogComponent, + ConfirmDeleteDossierStateDialogComponent, + ], providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService], - imports: [CommonModule, SharedModule, AdminRoutingModule, SharedAdminModule, NgxChartsModule, ColorPickerModule, MonacoEditorModule], + imports: [ + CommonModule, + SharedModule, + AdminRoutingModule, + SharedAdminModule, + NgxChartsModule, + ColorPickerModule, + MonacoEditorModule, + A11yModule, + ], }) export class AdminModule {} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html new file mode 100644 index 000000000..8eabdf913 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html @@ -0,0 +1,55 @@ +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+
+ +
+ +
+
+ + +
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss new file mode 100644 index 000000000..8bd9b16aa --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss @@ -0,0 +1,5 @@ +.iqser-input-group:nth-child(2) { + width: fit-content; + margin-top: 0; + margin-left: 16px; +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts new file mode 100644 index 000000000..0a3ec2ad4 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts @@ -0,0 +1,45 @@ +import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core'; +import { BaseDialogComponent } from '../../../../../../../../libs/common-ui/src'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { IDossierState } from '@red/domain'; + +interface DialogData { + readonly dossierState: IDossierState; + readonly dossierTemplateId: string; +} + +@Component({ + selector: 'redaction-add-edit-dossier-state-dialog', + templateUrl: './add-edit-dossier-state-dialog.component.html', + styleUrls: ['./add-edit-dossier-state-dialog.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddEditDossierStateDialogComponent extends BaseDialogComponent { + constructor( + private readonly _formBuilder: FormBuilder, + protected readonly _injector: Injector, + protected readonly _dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly data: DialogData, + ) { + super(_injector, _dialogRef); + this.form = this.#getForm(); + this.initialFormValue = this.form.getRawValue(); + } + + save(): void { + const dossierState: IDossierState = { + dossierStatusId: this.data.dossierState?.dossierStatusId, + dossierTemplateId: this.data.dossierTemplateId, + ...this.form.getRawValue(), + }; + this._dialogRef.close(dossierState); + } + + #getForm(): FormGroup { + return this._formBuilder.group({ + name: [this.data.dossierState?.name, Validators.required], + color: [this.data.dossierState?.color, Validators.required], + }); + } +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html new file mode 100644 index 000000000..332c07a5e --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html @@ -0,0 +1,39 @@ +
+
+ {{ 'confirm-delete-dossier-state.title' | translate }} +
+ +
+
{{ 'confirm-delete-dossier-state.warning' | translate: translateArgs }}
+ + +
{{ 'confirm-delete-dossier-state.suggestion' | translate }}
+ +
+
+
+ + + {{ 'confirm-delete-dossier-state.form.status-placeholder' | translate }} + + {{ state.name }} + + +
+
+
+
+
+ +
+ +
+
+ + +
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss new file mode 100644 index 000000000..616e7b39c --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss @@ -0,0 +1,16 @@ +@use 'variables'; + +.replacement-suggestion { + font-size: 13px; + line-height: 18px; + color: variables.$grey-1; + margin-bottom: 24px; +} + +.dialog-header { + color: variables.$primary; +} + +.heading { + margin-bottom: 8px; +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts new file mode 100644 index 000000000..6088cb114 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts @@ -0,0 +1,53 @@ +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { IDossierState } from '@red/domain'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { FormBuilder, FormGroup } from '@angular/forms'; + +interface DialogData { + readonly toBeDeletedState: IDossierState; + readonly otherStates: IDossierState[]; + readonly dossierCount: number; +} + +@Component({ + selector: 'redaction-confirm-delete-dossier-state-dialog', + templateUrl: './confirm-delete-dossier-state-dialog.component.html', + styleUrls: ['./confirm-delete-dossier-state-dialog.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ConfirmDeleteDossierStateDialogComponent { + readonly form: FormGroup; + + constructor( + private readonly _formBuilder: FormBuilder, + readonly dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly data: DialogData, + ) { + this.form = this.#getForm(); + } + + get translateArgs() { + return { + name: this.data.toBeDeletedState.name, + count: this.data.dossierCount, + }; + } + + get replaceDossierStatusId(): string { + return this.form.get('replaceDossierStatusId').value; + } + + get label(): string { + return this.replaceDossierStatusId ? 'confirm-delete-dossier-state.delete-replace' : 'confirm-delete-dossier-state.delete'; + } + + get afterCloseValue(): string | true { + return this.replaceDossierStatusId ?? true; + } + + #getForm(): FormGroup { + return this._formBuilder.group({ + replaceDossierStatusId: [null], + }); + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.html new file mode 100644 index 000000000..624230e58 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.html @@ -0,0 +1,97 @@ + +
+ + +
+
+ + + +
+ +
+ +
+ +
+
+
+ + +
+ + + +
+
+ + +
+
+
+
+
{{ state.name }}
+
+
+ +
+ {{ state.dossierCount }} +
+ +
+
+ + +
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.scss new file mode 100644 index 000000000..ef7a2530e --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.scss @@ -0,0 +1,18 @@ +@use 'variables'; + +.dossier-state-square { + height: 16px; + width: 16px; + margin-right: 16px; +} + +.state-name { + font-size: 16px; + font-weight: 600; + line-height: 20px; + color: variables.$grey-1; +} + +.right-container { + padding: 50px 26px 0; +} diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.ts new file mode 100644 index 000000000..646b0f020 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.ts @@ -0,0 +1,126 @@ +import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnDestroy, OnInit } from '@angular/core'; +import { + CircleButtonTypes, + DefaultListingServices, + IconButtonTypes, + ListingComponent, + LoadingService, + TableColumnConfig, + Toaster, +} from '../../../../../../../../libs/common-ui/src'; +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 { firstValueFrom } from 'rxjs'; +import { AdminDialogService } from '../../services/admin-dialog.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 { ActivatedRoute } from '@angular/router'; +import { DossierTemplatesService } from '../../../../services/entity-services/dossier-templates.service'; + +@Component({ + templateUrl: './dossier-states-listing-screen.component.html', + styleUrls: ['./dossier-states-listing-screen.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + ...DefaultListingServices, + { provide: ListingComponent, useExisting: forwardRef(() => DossierStatesListingScreenComponent) }, + ], +}) +export class DossierStatesListingScreenComponent extends ListingComponent 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[] = [ + { label: _('dossier-states-listing.table-col-names.name'), sortByKey: 'searchKey' }, + { label: _('dossier-states-listing.table-col-names.dossiers-count'), sortByKey: 'dossierCount' }, + ]; + chartData: DoughnutChartConfig[]; + + constructor( + protected readonly _injector: Injector, + private readonly _loadingService: LoadingService, + private readonly _dossiersService: DossiersService, + readonly dossierStateService: DossierStateService, + private readonly _dialogService: AdminDialogService, + private readonly _userService: UserService, + private readonly _toaster: Toaster, + private readonly _route: ActivatedRoute, + private readonly _dossierTemplatesService: DossierTemplatesService, + ) { + super(_injector); + this.#dossierTemplateId = _route.snapshot.paramMap.get('dossierTemplateId'); + } + + ngOnInit(): Promise { + return this.#loadData(); + } + + openAddEditStateDialog($event: MouseEvent, dossierState?: IDossierState) { + const data = { + dossierState, + dossierTemplateId: this.#dossierTemplateId, + }; + this._dialogService.openDialog('addEditDossierState', $event, data, async (newValue: IDossierState) => { + await this.#createNewDossierStateAndRefreshView(newValue); + }); + } + + openConfirmDeleteStateDialog($event: MouseEvent, dossierState: IDossierState) { + const templateId = this.#dossierTemplateId; + const data = { + toBeDeletedState: dossierState, + otherStates: this.entitiesService.all.filter(state => state.dossierStatusId !== dossierState.dossierStatusId), + dossierCount: dossierState.dossierCount, + }; + this._dialogService.openDialog('deleteDossierState', $event, data, async (value: string | true) => { + if (value) { + if (typeof value === 'string') { + await firstValueFrom(this.dossierStateService.deleteAndReplace(dossierState.dossierStatusId, value)); + } else { + await firstValueFrom(this.dossierStateService.delete(dossierState.dossierStatusId)); + } + } + + await this._dossierTemplatesService.refreshDossierTemplate(templateId); + await this.#loadData(); + }); + } + + async #createNewDossierStateAndRefreshView(newValue: IDossierState): Promise { + this._loadingService.start(); + await firstValueFrom(this.dossierStateService.setDossierState(newValue)).catch(error => { + if (error.status === HttpStatusCode.Conflict) { + this._toaster.error(_('dossier-states-listing.error.conflict')); + } else { + this._toaster.error(_('dossier-states-listing.error.generic')); + } + }); + await this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId); + await this.#loadData(); + } + + async #loadData(): Promise { + this._loadingService.start(); + await firstValueFrom(this._dossiersService.loadAll()); + + try { + const dossierStates = this.dossierStateService.all.filter(d => d.dossierTemplateId === this.#dossierTemplateId); + this.#setStatesCount(dossierStates); + this.chartData = this.dossierStateService.all.map(state => { + return { value: state.dossierCount, label: state.name, key: state.name, color: state.color }; + }); + + this.entitiesService.setEntities(dossierStates || []); + } catch (e) {} + this._loadingService.stop(); + } + + #setStatesCount(dossierStates: DossierState[]): void { + dossierStates.forEach(state => (state.dossierCount = this._dossiersService.getCountWithState(state.dossierStatusId))); + } +} diff --git a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts index 1ee12ff67..38fd85808 100644 --- a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts +++ b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts @@ -13,6 +13,8 @@ import { AddEditDossierAttributeDialogComponent } from '../dialogs/add-edit-doss import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } 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'; type DialogType = | 'confirm' @@ -27,7 +29,9 @@ type DialogType = | 'smtpAuthConfig' | 'addEditDossierTemplate' | 'addEditDossierAttribute' - | 'uploadDictionary'; + | 'uploadDictionary' + | 'addEditDossierState' + | 'deleteDossierState'; @Injectable() export class AdminDialogService extends DialogService { @@ -82,6 +86,12 @@ export class AdminDialogService extends DialogService { uploadDictionary: { component: UploadDictionaryDialogComponent, }, + addEditDossierState: { + component: AddEditDossierStateDialogComponent, + }, + deleteDossierState: { + component: ConfirmDeleteDossierStateDialogComponent, + }, }; constructor(protected readonly _dialog: MatDialog) { diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html index 47e3f849e..9c062fa53 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html @@ -8,6 +8,7 @@ @@ -16,7 +17,12 @@
{{ 'add-dossier-dialog.form.template' | translate }} - +
- @@ -98,11 +104,12 @@ [label]="'add-dossier-dialog.actions.save-and-add-members' | translate" [type]="iconButtonTypes.dark" icon="red:assign" + id="createDossierEditTeamButton" >
- + diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.html index 6aec3866c..fe48130cc 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.html @@ -10,7 +10,7 @@
-
+
{{ 'add-edit-dictionary.form.add-to-dictionary-action' | translate }} @@ -19,7 +19,7 @@
-
+
-
- + diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html index bb9c665dc..a098fb74b 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html @@ -2,7 +2,7 @@
{{ 'assign-dossier-owner.dialog.single-user' | translate }} - + {{ userId | name }} diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html index 0af613641..4d60d4b0f 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html @@ -1,71 +1,89 @@
-
- - -
+
+
+
+ + +
-
- - {{ 'edit-dossier-dialog.general-info.form.template' | translate }} - - + + {{ 'edit-dossier-dialog.general-info.form.template' | translate }} + + + {{ dossierTemplate.name }} + + + +
+ +
+ + +
+ +
+ + {{ 'edit-dossier-dialog.general-info.form.watermark' | translate }} + +
+ +
+ + {{ 'edit-dossier-dialog.general-info.form.watermark-preview' | translate }} + +
+
+ +
+
+ + + +
+ +
{{ state.name }}
+
+
+
+
+ +
+ - {{ dossierTemplate.name }} - - - -
+ {{ 'edit-dossier-dialog.general-info.form.due-date' | translate }} + -
- - -
- -
- - {{ 'edit-dossier-dialog.general-info.form.watermark' | translate }} - -
- -
- - {{ 'edit-dossier-dialog.general-info.form.watermark-preview' | translate }} - -
- -
- - {{ 'edit-dossier-dialog.general-info.form.due-date' | translate }} - - -
- - - - - +
+ + + + + +
+
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.scss b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.scss index 695782375..cadbd6f7a 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.scss +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.scss @@ -23,3 +23,15 @@ border-top: none; padding: 0; } + +.fields-container { + flex-direction: column; + + &:first-child { + margin-right: 40px; + } +} + +redaction-small-chip { + margin-right: 8px; +} diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts index 8b9d48bf1..622aca251 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import * as moment from 'moment'; -import { Dossier, IDossierRequest, IDossierTemplate } from '@red/domain'; +import { Dossier, DossierState, IDossierRequest, IDossierTemplate } from '@red/domain'; import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; import { PermissionsService } from '@services/permissions.service'; @@ -14,7 +14,9 @@ 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 { 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'; @Component({ selector: 'redaction-edit-dossier-general-info', @@ -29,9 +31,12 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti form: FormGroup; hasDueDate: boolean; dossierTemplates: IDossierTemplate[]; + states: DossierState[]; + currentStatus: DossierState; constructor( readonly permissionsService: PermissionsService, + private readonly _dossierStateService: DossierStateService, private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossiersService: DossiersService, private readonly _dossierStatsService: DossierStatsService, @@ -40,6 +45,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti private readonly _router: Router, private readonly _editDossierDialogRef: MatDialogRef, private readonly _toaster: Toaster, + private readonly _translateService: TranslateService, ) {} get changed(): boolean { @@ -67,12 +73,26 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti return this.hasDueDate && this.form.get('dueDate').value === null; } + get statusPlaceholder(): string { + if (this.states.length === 0) { + return this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.no-status-placeholder'); + } + + return ( + this.currentStatus?.name ?? this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder') + ); + } + ngOnInit() { - this._filterInvalidDossierTemplates(); - this.form = this._getForm(); + this.#filterInvalidDossierTemplates(); + this.form = this.#getForm(); if (!this.permissionsService.canEditDossier(this.dossier)) { this.form.disable(); } + this.states = this._dossierStateService.all.filter(s => s.dossierTemplateId === this.dossier.dossierTemplateId); + if (this.dossier.dossierStatusId) { + this.currentStatus = this._dossierStateService.all.find(s => s.dossierStatusId === this.dossier.dossierStatusId); + } this.hasDueDate = !!this.dossier.dueDate; } @@ -80,6 +100,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti this.form.reset({ dossierName: this.dossier.dossierName, dossierTemplateId: this.dossier.dossierTemplateId, + dossierStatusId: this.dossier.dossierStatusId, description: this.dossier.description, watermarkEnabled: this.dossier.watermarkEnabled, watermarkPreviewEnabled: this.dossier.watermarkPreviewEnabled, @@ -96,6 +117,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti 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, } as IDossierRequest; try { await firstValueFrom(this._dossiersService.createOrUpdate(dossier)); @@ -122,11 +144,11 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti this._dialogService.openDialog('confirm', null, data, async () => { await firstValueFrom(this._dossiersService.delete(this.dossier)); this._editDossierDialogRef.close(); - this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted()); + this._router.navigate(['main', 'dossiers']).then(() => this.#notifyDossierDeleted()); }); } - private _getForm(): FormGroup { + #getForm(): FormGroup { return this._formBuilder.group({ dossierName: [this.dossier.dossierName, Validators.required], dossierTemplateId: [ @@ -136,6 +158,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti }, Validators.required, ], + dossierStatusId: [this.dossier.dossierStatusId], description: [this.dossier.description], dueDate: [this.dossier.dueDate], watermarkEnabled: [this.dossier.watermarkEnabled], @@ -143,11 +166,11 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti }); } - private _notifyDossierDeleted() { + #notifyDossierDeleted() { this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: { dossierName: this.dossier.dossierName } }); } - private _filterInvalidDossierTemplates() { + #filterInvalidDossierTemplates() { this.dossierTemplates = this._dossierTemplatesService.all.filter(r => { if (this.dossier?.dossierTemplateId === r.dossierTemplateId) { return true; diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts index 9dbbe5eb8..5fc82072b 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts @@ -10,6 +10,7 @@ import { DossiersService } from '@services/entity-services/dossiers.service'; import { JustificationsService } from '@services/entity-services/justifications.service'; import { Dossier, ILegalBasisChangeRequest } from '@red/domain'; import { firstValueFrom } from 'rxjs'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; export interface LegalBasisOption { label?: string; @@ -37,11 +38,11 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen private readonly _permissionsService: PermissionsService, protected readonly _injector: Injector, protected readonly _dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) private readonly _data: { readonly dossier: Dossier; readonly hint: boolean }, + @Inject(MAT_DIALOG_DATA) + private readonly _data: { readonly dossier: Dossier; readonly hint: boolean; annotations: AnnotationWrapper[] }, ) { super(_injector, _dialogRef); this.form = this._getForm(); - this.initialFormValue = this.form.getRawValue(); } get isHintDialog() { @@ -59,6 +60,14 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen })); this.legalOptions.sort((a, b) => a.label.localeCompare(b.label)); + + // Set pre-existing reason if it exists + const existingReason = this.legalOptions.find(option => option.legalBasis === this._data.annotations[0].legalBasis); + if (!this._data.hint && existingReason) { + this.form.patchValue({ reason: existingReason }, { emitEvent: false }); + } + + this.initialFormValue = this.form.getRawValue(); } save() { diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts index 90d3354dd..72d1e1ae3 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts @@ -19,24 +19,26 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { @Input() @Required() selectedFiles: File[]; @Input() buttonType: CircleButtonType = CircleButtonTypes.dark; @Input() maxWidth: number; - - analysisForced: boolean; - canAssignToSelf: boolean; - canAssign: boolean; - canDelete: boolean; - canReanalyse: boolean; - canDisableAutoAnalysis: boolean; - canEnableAutoAnalysis: boolean; - canOcr: boolean; - canSetToUnderReview: boolean; - canSetToUnderApproval: boolean; - isReadyForApproval: boolean; - canApprove: boolean; - canUndoApproval: boolean; - assignTooltip: string; buttons: Action[]; - private _canMoveToSameState: boolean; + #analysisForced: boolean; + #canAssignToSelf: boolean; + #canAssign: boolean; + #canDelete: boolean; + #canReanalyse: boolean; + #canDisableAutoAnalysis: boolean; + #canEnableAutoAnalysis: boolean; + #canOcr: boolean; + #canSetToUnderReview: boolean; + #canSetToUnderApproval: boolean; + #isReadyForApproval: boolean; + #canApprove: boolean; + #canUndoApproval: boolean; + #canToggleAnalysis: boolean; + #assignTooltip: string; + #toggleAnalysisTooltip: string; + #allFilesAreExcluded: boolean; + #canMoveToSameState: boolean; constructor( private readonly _permissionsService: PermissionsService, @@ -52,35 +54,35 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { action: () => this._bulkActionsService.delete(this.selectedFiles), tooltip: _('dossier-overview.bulk.delete'), icon: 'iqser:trash', - show: this.canDelete, + show: this.#canDelete, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.assign(this.selectedFiles), - tooltip: this.assignTooltip, + tooltip: this.#assignTooltip, icon: 'red:assign', - show: this.canAssign, + show: this.#canAssign, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.assignToMe(this.selectedFiles), tooltip: _('dossier-overview.assign-me'), icon: 'red:assign-me', - show: this.canAssignToSelf, + show: this.#canAssignToSelf, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles), tooltip: _('dossier-overview.under-approval'), icon: 'red:ready-for-approval', - show: this.canSetToUnderApproval, + show: this.#canSetToUnderApproval, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.backToUnderReview(this.selectedFiles), tooltip: _('dossier-overview.under-review'), icon: 'red:undo', - show: this.canSetToUnderReview, + show: this.#canSetToUnderReview, }, { type: ActionTypes.downloadBtn, @@ -90,45 +92,53 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.approve(this.selectedFiles), - disabled: !this.canApprove, - tooltip: this.canApprove ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'), + disabled: !this.#canApprove, + tooltip: this.#canApprove ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'), icon: 'red:approved', - show: this.isReadyForApproval, + show: this.#isReadyForApproval, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles), tooltip: _('dossier-overview.under-approval'), icon: 'red:undo', - show: this.canUndoApproval, + show: this.#canUndoApproval, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.ocr(this.selectedFiles), tooltip: _('dossier-overview.ocr-file'), icon: 'iqser:ocr', - show: this.canOcr, + show: this.#canOcr, }, { type: ActionTypes.circleBtn, action: () => this._bulkActionsService.reanalyse(this.selectedFiles), tooltip: _('dossier-overview.bulk.reanalyse'), icon: 'iqser:refresh', - show: this.canReanalyse && (this.analysisForced || this.canEnableAutoAnalysis), + show: this.#canReanalyse && (this.#analysisForced || this.#canEnableAutoAnalysis), }, { type: ActionTypes.circleBtn, - action: $event => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles), + action: () => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles), tooltip: _('dossier-overview.disable-auto-analysis'), icon: 'red:stop', - show: this.canDisableAutoAnalysis, + show: this.#canDisableAutoAnalysis, }, { type: ActionTypes.circleBtn, - action: $event => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles), + action: () => this._bulkActionsService.toggleAutomaticAnalysis(this.selectedFiles), tooltip: _('dossier-overview.enable-auto-analysis'), icon: 'red:play', - show: this.canEnableAutoAnalysis, + show: this.#canEnableAutoAnalysis, + }, + + { + type: ActionTypes.toggle, + action: () => this._bulkActionsService.toggleAnalysis(this.selectedFiles, !this.#allFilesAreExcluded), + tooltip: this.#toggleAnalysisTooltip, + checked: !this.#allFilesAreExcluded, + show: this.#canToggleAnalysis, }, ].filter(btn => btn.show); } @@ -138,7 +148,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { } forceReanalysisAction($event: LongPressEvent) { - this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled; + this.#analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled; this._setup(); } @@ -151,37 +161,44 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { true, ); const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true); - this._canMoveToSameState = allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval; + this.#allFilesAreExcluded = this.selectedFiles.reduce((acc, file) => acc && file.excluded, true); + this.#canMoveToSameState = allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval; - this.canAssign = - this._canMoveToSameState && + this.#canAssign = + this.#canMoveToSameState && this.selectedFiles.reduce( (acc, file) => (acc && this._permissionsService.canAssignUser(file)) || this._permissionsService.canUnassignUser(file), true, ); - this.canAssignToSelf = this._canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles); + this.#canAssignToSelf = this.#canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles); - this.canDelete = this._permissionsService.canDeleteFile(this.selectedFiles); + this.#canDelete = this._permissionsService.canDeleteFile(this.selectedFiles); - this.canReanalyse = this._permissionsService.canReanalyseFile(this.selectedFiles); + this.#canReanalyse = this._permissionsService.canReanalyseFile(this.selectedFiles); - this.canDisableAutoAnalysis = this._permissionsService.canDisableAutoAnalysis(this.selectedFiles); + this.#canDisableAutoAnalysis = this._permissionsService.canDisableAutoAnalysis(this.selectedFiles); - this.canEnableAutoAnalysis = this._permissionsService.canEnableAutoAnalysis(this.selectedFiles); + this.#canEnableAutoAnalysis = this._permissionsService.canEnableAutoAnalysis(this.selectedFiles); - this.canOcr = this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true); + this.#canToggleAnalysis = this._permissionsService.canToggleAnalysis(this.selectedFiles); - this.canSetToUnderReview = this._permissionsService.canSetUnderReview(this.selectedFiles) && !isWorkflow; + this.#canOcr = this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true); - this.canSetToUnderApproval = this._permissionsService.canSetUnderApproval(this.selectedFiles) && !isWorkflow; + this.#canSetToUnderReview = this._permissionsService.canSetUnderReview(this.selectedFiles) && !isWorkflow; - this.isReadyForApproval = this._permissionsService.isReadyForApproval(this.selectedFiles) && !isWorkflow; + this.#canSetToUnderApproval = this._permissionsService.canSetUnderApproval(this.selectedFiles) && !isWorkflow; - this.canApprove = this._permissionsService.canBeApproved(this.selectedFiles) && !isWorkflow; + this.#isReadyForApproval = this._permissionsService.isReadyForApproval(this.selectedFiles) && !isWorkflow; - this.canUndoApproval = this._permissionsService.canUndoApproval(this.selectedFiles) && !isWorkflow; + this.#canApprove = this._permissionsService.canBeApproved(this.selectedFiles) && !isWorkflow; - this.assignTooltip = allFilesAreUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'); + this.#canUndoApproval = this._permissionsService.canUndoApproval(this.selectedFiles) && !isWorkflow; + + this.#assignTooltip = allFilesAreUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'); + + this.#toggleAnalysisTooltip = this.#allFilesAreExcluded + ? _('file-preview.toggle-analysis.enable') + : _('file-preview.toggle-analysis.disable'); this.buttons = this._buttons; } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.html index 62fdd4772..183ebc0d0 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/dossier-details/dossier-details.component.html @@ -5,7 +5,7 @@
-
{{ dossier.dossierName }}
+
{{ dossier.dossierName }}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts index b72dbe887..d7bbdd4a5 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts @@ -91,6 +91,18 @@ export class BulkActionsService { this._loadingService.stop(); } + async toggleAnalysis(files: File[], excluded: boolean) { + this._loadingService.start(); + await firstValueFrom( + this._reanalysisService.toggleAnalysis( + files[0].dossierId, + files.map(f => f.id), + excluded, + ), + ); + this._loadingService.stop(); + } + async backToUnderReview(files: File[]): Promise { this._loadingService.start(); await firstValueFrom( diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.html new file mode 100644 index 000000000..3025a99af --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.html @@ -0,0 +1 @@ + diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.ts new file mode 100644 index 000000000..00fc9084f --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossier-documents-status/dossier-documents-status.component.ts @@ -0,0 +1,24 @@ +import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; +import { DossierStats, StatusSorter } from '../../../../../../../../../../libs/red-domain/src'; +import { List, StatusBarConfig } from '../../../../../../../../../../libs/common-ui/src'; + +@Component({ + selector: 'redaction-dossier-documents-status', + templateUrl: './dossier-documents-status.component.html', + styleUrls: ['./dossier-documents-status.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DossierDocumentsStatusComponent implements OnChanges { + @Input() stats: DossierStats; + statusBarConfig: List>; + + private get _statusConfig(): List> { + const { fileCountPerWorkflowStatus } = this.stats; + const statuses = Object.keys(fileCountPerWorkflowStatus).sort(StatusSorter.byStatus); + return statuses.map(status => ({ length: fileCountPerWorkflowStatus[status], color: status })); + } + + ngOnChanges(): void { + this.statusBarConfig = this._statusConfig; + } +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html index 50c92fc4b..ef389d4e0 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html @@ -1,5 +1,3 @@ - -
> { - const { fileCountPerWorkflowStatus } = this.stats; - const statuses = Object.keys(fileCountPerWorkflowStatus).sort(StatusSorter.byStatus); - return statuses.map(status => ({ length: fileCountPerWorkflowStatus[status], color: status })); - } - ngOnChanges() { - this.statusBarConfig = this._statusConfig; this.files = this.filesMapService.get(this.dossier.dossierId); this.displayReanalyseBtn = this.permissionsService.displayReanalyseBtn(this.dossier) && this.analysisForced; } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html index 1371e0061..75ad9ceb1 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.html @@ -26,7 +26,7 @@
-
+
_dossierStatsMap.watch$(dossier.dossierId)), @@ -37,12 +40,19 @@ export class DossiersListingDetailsComponent { } private async _toDossierChartData(dossiers: Dossier[]): Promise { + const config: DoughnutChartConfig[] = []; + this._dossierStateService.all.forEach(state => { + state.dossierCount = this.dossiersService.getCountWithState(state.dossierStatusId); + config.push({ value: state.dossierCount, label: state.name, color: state.color }); + }); + const notAssignedLength = this.dossiersService.all.length - config.map(v => v.value).reduce((acc, val) => acc + val, 0); + config.push({ + value: notAssignedLength, + label: this._translateService.instant('edit-dossier-dialog.general-info.form.dossier-status.placeholder'), + color: '#E2E4E9', + }); // TODO: deleted dossiers count should come with stats - // const deletedDossiers = await this.dossiersService.getDeleted(); - return [ - { value: dossiers.length, color: 'ACTIVE', label: _('active') }, - // { value: deletedDossiers.length, color: 'DELETED', label: _('archived') }, - ]; + return config; } private _toChartData(stats: DossierStats[]) { diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.html new file mode 100644 index 000000000..1ec18fba9 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.html @@ -0,0 +1,13 @@ + +
+
{{ currentState.name }}
+ +
+
+ + +
+
{{ 'edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate }}
+ +
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.scss b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.scss new file mode 100644 index 000000000..7d5a74298 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.scss @@ -0,0 +1,16 @@ +@use 'variables'; + +.dossier-status-container { + justify-content: flex-end; + width: 100%; +} + +redaction-small-chip { + margin-left: 8px; +} + +.dossier-status-text { + font-size: 13px; + line-height: 16px; + color: variables.$grey-1; +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.ts new file mode 100644 index 000000000..e3994df3d --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-status/dossiers-listing-status.component.ts @@ -0,0 +1,31 @@ +import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core'; +import { Dossier } from '../../../../../../../../../../libs/red-domain/src'; +import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service'; +import { DossierState } from '@red/domain'; + +@Component({ + selector: 'redaction-dossiers-listing-status', + templateUrl: './dossiers-listing-status.component.html', + styleUrls: ['./dossiers-listing-status.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DossiersListingStatusComponent implements OnInit, OnChanges { + @Input() dossier: Dossier; + currentState: DossierState; + + constructor(private readonly _dossierStateService: DossierStateService) {} + + ngOnInit(): void { + this.#setState(); + } + + ngOnChanges(): void { + this.#setState(); + } + + #setState(): void { + if (this.dossier.dossierStatusId) { + this.currentState = this._dossierStateService.all.find(s => s.dossierStatusId === this.dossier.dossierStatusId); + } + } +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html index fcff19968..7ad67122e 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.html @@ -12,6 +12,12 @@
+ +
+ +
+ +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts index d1e991d3c..0151c3320 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/table-item/table-item.component.ts @@ -14,15 +14,15 @@ export class TableItemComponent implements OnChanges { @Input() dossier!: Dossier; readonly stats$: Observable; - private readonly _ngOnChanges$ = new BehaviorSubject(undefined); + readonly #ngOnChanges$ = new BehaviorSubject(undefined); constructor(readonly dossierStatsService: DossierStatsService) { - this.stats$ = this._ngOnChanges$.pipe(switchMap(dossierId => this.dossierStatsService.watch$(dossierId))); + this.stats$ = this.#ngOnChanges$.pipe(switchMap(dossierId => this.dossierStatsService.watch$(dossierId))); } ngOnChanges() { if (this.dossier) { - this._ngOnChanges$.next(this.dossier.dossierId); + this.#ngOnChanges$.next(this.dossier.dossierId); } } } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts index db45899c2..d46135b13 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts @@ -26,7 +26,8 @@ export class ConfigService { { label: _('dossier-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' }, { label: _('dossier-listing.table-col-names.needs-work') }, { label: _('dossier-listing.table-col-names.owner'), class: 'user-column' }, - { label: _('dossier-listing.table-col-names.status'), class: 'flex-end', width: 'auto' }, + { label: _('dossier-listing.table-col-names.documents-status'), class: 'flex-end', width: 'auto' }, + { label: _('dossier-listing.table-col-names.dossier-status'), class: 'flex-end' }, ]; } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts index f04907531..29af016b3 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/dossiers-listing.module.ts @@ -12,6 +12,8 @@ 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 = [ { @@ -30,6 +32,8 @@ const routes: Routes = [ DossiersListingDossierNameComponent, DossierWorkloadColumnComponent, TableItemComponent, + DossiersListingStatusComponent, + DossierDocumentsStatusComponent, ], providers: [ConfigService], imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule], diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html index 6517b62ee..74c0f571b 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html @@ -92,7 +92,7 @@
-
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts index dc226a6cc..2e06a8c49 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts @@ -1,11 +1,9 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core'; import { PermissionsService } from '@services/permissions.service'; import { ConfigService } from '@services/config.service'; -import { DossiersService } from '@services/entity-services/dossiers.service'; import { ViewedPagesService } from '@services/entity-services/viewed-pages.service'; import { IViewedPage } from '@red/domain'; import { AutoUnsubscribe } from '@iqser/common-ui'; -import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { firstValueFrom } from 'rxjs'; @@ -20,6 +18,7 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy @Input() showDottedIcon = false; @Input() number: number; @Input() activeSelection = false; + @Input() viewedPages: IViewedPage[] = []; @Output() readonly pageSelected = new EventEmitter(); @@ -28,8 +27,6 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy constructor( private readonly _viewedPagesService: ViewedPagesService, - private readonly _filesMapService: FilesMapService, - private readonly _dossiersService: DossiersService, private readonly _configService: ConfigService, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _permissionService: PermissionsService, @@ -39,7 +36,7 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy } get activePage() { - return this._viewedPages.find(p => p.page === this.number); + return this.viewedPages.find(p => p.page === this.number); } get dossierId() { @@ -50,10 +47,6 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy return this._stateService.fileId; } - private get _viewedPages(): IViewedPage[] { - return this._stateService.fileData?.viewedPages || []; - } - ngOnChanges() { this._setReadState(); return this.handlePageRead(); @@ -104,7 +97,7 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy if (this.activePage) { this.activePage.showAsUnseen = false; } else { - this._viewedPages.push({ page: this.number, fileId: this.fileId }); + this.viewedPages.push({ page: this.number, fileId: this.fileId }); } this._setReadState(); } @@ -113,8 +106,8 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy const removePage$ = this._viewedPagesService.removePage(this.dossierId, this.fileId, this.number); await firstValueFrom(removePage$); - this._viewedPages.splice( - this._viewedPages.findIndex(p => p.page === this.number), + this.viewedPages.splice( + this.viewedPages.findIndex(p => p.page === this.number), 1, ); this._setReadState(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts index 05e5a6e32..9f0682b63 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/annotation-actions.service.ts @@ -77,7 +77,7 @@ export class AnnotationActionsService { hint: boolean = false, ) { const { dossierId, fileId } = this._screenStateService; - const data = { dossier: this._dossier, hint }; + const data = { dossier: this._dossier, annotations, hint }; this._dialogService.openDialog('forceAnnotation', $event, data, (request: ILegalBasisChangeRequest) => { annotations.forEach(annotation => { this._processObsAndEmit( diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts index e084f178c..8b4d73884 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts @@ -345,7 +345,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, private async _toggleAnalysis() { this._loadingService.start(); - await firstValueFrom(this._reanalysisService.toggleAnalysis(this.file.dossierId, this.file.fileId, !this.file.excluded)); + await firstValueFrom(this._reanalysisService.toggleAnalysis(this.file.dossierId, [this.file.fileId], !this.file.excluded)); this._loadingService.stop(); } diff --git a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts index bd5230711..d91417945 100644 --- a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts @@ -23,7 +23,7 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit { @Input() radius = 85; @Input() strokeWidth = 20; @Input() direction: 'row' | 'column' = 'column'; - @Input() totalType: 'sum' | 'count' = 'sum'; + @Input() totalType: 'sum' | 'count' | 'simpleLabel' = 'sum'; @Input() counterText: string; @Input() filterKey = 'statusFilters'; @Input() helpModeKey: 'filter_for_status'; @@ -97,7 +97,11 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit { } getLabel({ label, value }: DoughnutChartConfig): string { - return this.totalType === 'sum' ? `${value} ${label}` : `${label} (${value} ${this.counterText})`; + return this.totalType === 'simpleLabel' + ? `${label}` + : this.totalType === 'sum' + ? `${value} ${label}` + : `${label} (${value} ${this.counterText})`; } selectValue(key: string): void { diff --git a/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts b/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts new file mode 100644 index 000000000..a3c2edc54 --- /dev/null +++ b/apps/red-ui/src/app/services/entity-services/dossier-state.service.ts @@ -0,0 +1,41 @@ +import { Injectable, Injector } from '@angular/core'; +import { EntitiesService, mapEach, RequiredParam, Validate } from '@iqser/common-ui'; +import { DossierState, IDossierState } from '@red/domain'; +import { forkJoin, Observable, switchMap } from 'rxjs'; +import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; +import { map, tap } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root', +}) +export class DossierStateService extends EntitiesService { + constructor(protected readonly _injector: Injector, private readonly _dossierTemplatesService: DossierTemplatesService) { + super(_injector, DossierState, 'dossier-status'); + } + + @Validate() + setDossierState(@RequiredParam() body: IDossierState) { + return this._post(body, this._defaultModelPath); + } + + @Validate() + loadAllForTemplate(@RequiredParam() templateId: string) { + return this.loadAll(`${this._defaultModelPath}/dossier-template/${templateId}`); + } + + loadAllForAllTemplates(): Observable { + return this._dossierTemplatesService.all$.pipe( + mapEach(template => template.dossierTemplateId), + mapEach(id => this.loadAllForTemplate(id)), + switchMap(all => forkJoin(all)), + map(value => value.flatMap(item => item)), + tap(value => this.setEntities(value)), + ); + } + + @Validate() + deleteAndReplace(@RequiredParam() dossierStatusId: string, @RequiredParam() replaceDossierStatusId: string) { + const url = `${this._defaultModelPath}/${dossierStatusId}?replaceDossierStatusId=${replaceDossierStatusId}`; + return this.delete({}, url); + } +} diff --git a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts index 0babadb88..4787fdb30 100644 --- a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts @@ -7,6 +7,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; import { CHANGED_CHECK_INTERVAL } from '@utils/constants'; +import { DossierStateService } from '@services/entity-services/dossier-state.service'; export interface IDossiersStats { totalPeople: number; @@ -37,6 +38,7 @@ export class DossiersService extends EntitiesService { private readonly _toaster: Toaster, protected readonly _injector: Injector, private readonly _dossierStatsService: DossierStatsService, + private readonly _dossierStateService: DossierStateService, ) { super(_injector, Dossier, 'dossier'); @@ -54,6 +56,7 @@ export class DossiersService extends EntitiesService { mapEach(entity => new Dossier(entity)), /* Load stats before updating entities */ switchMap(dossiers => this._dossierStatsService.getFor(dossierIds(dossiers)).pipe(mapTo(dossiers))), + switchMap(dossiers => this._dossierStateService.loadAllForAllTemplates().pipe(mapTo(dossiers))), tap(dossiers => this.setEntities(dossiers)), ); } @@ -108,6 +111,10 @@ export class DossiersService extends EntitiesService { return firstValueFrom(super.delete(body, 'deleted-dossiers/hard-delete', body)); } + getCountWithState(dossierStatusId: string): number { + return this.all.filter(dossier => dossier.dossierStatusId === dossierStatusId).length; + } + private _emitFileChanges(changes: ChangesDetails): void { changes.dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId)); } diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts index c1242d5c4..21404c135 100644 --- a/apps/red-ui/src/app/services/permissions.service.ts +++ b/apps/red-ui/src/app/services/permissions.service.ts @@ -23,8 +23,10 @@ export class PermissionsService { return ((file.isUnderReview || file.isNew) && this.isDossierMember(dossier)) || (file.isUnderApproval && this.isApprover(dossier)); } - canToggleAnalysis(file: File): boolean { - return this.isFileAssignee(file) && (file.isNew || file.isUnderReview || file.isUnderApproval); + canToggleAnalysis(file: File | File[]): boolean { + const files = file instanceof File ? [file] : file; + const sameState = new Set(files.map(f => f.excluded)).size === 1; + return sameState && files.reduce((acc, _file) => this._canToggleAnalysis(_file) && acc, true); } canReanalyseFile(file: File | File[]): boolean { @@ -152,6 +154,10 @@ export class PermissionsService { return (comment.user === this._userService.currentUser.id || this.isApprover(dossier)) && !file.isApproved; } + private _canToggleAnalysis(file: File): boolean { + return this.isFileAssignee(file) && (file.isNew || file.isUnderReview || file.isUnderApproval); + } + // https://jira.iqser.com/browse/RED-2787 private _canDeleteFile(file: File, dossier: Dossier): boolean { return ( diff --git a/apps/red-ui/src/app/services/reanalysis.service.ts b/apps/red-ui/src/app/services/reanalysis.service.ts index a636a7b60..03d002626 100644 --- a/apps/red-ui/src/app/services/reanalysis.service.ts +++ b/apps/red-ui/src/app/services/reanalysis.service.ts @@ -49,13 +49,13 @@ export class ReanalysisService extends GenericService { } @Validate() - toggleAnalysis(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, excluded?: boolean) { + toggleAnalysis(@RequiredParam() dossierId: string, @RequiredParam() fileIds: string[], excluded?: boolean) { const queryParams: QueryParam[] = []; if (excluded) { queryParams.push({ key: 'excluded', value: excluded }); } - return this._post({}, `toggle-analysis/${dossierId}/${fileId}`, queryParams).pipe( + return this._post(fileIds, `toggle-analysis/${dossierId}/bulk`, queryParams).pipe( switchMap(() => this._filesService.loadAll(dossierId)), ); } diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json index 21819e941..b228549e1 100644 --- a/apps/red-ui/src/assets/i18n/de.json +++ b/apps/red-ui/src/assets/i18n/de.json @@ -605,6 +605,7 @@ "title": "Datei-Attribute anlegen" }, "dossier": "Dossier", + "dossier-states": "", "dossier-attribute-types": { "date": "Datum", "image": "Bild", diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 9a64201c7..df128effe 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -8,7 +8,6 @@ "all": "All", "none": "None" }, - "active": "Active", "add-dossier-dialog": { "actions": { "save": "Save", @@ -72,6 +71,16 @@ "save": "Save Attribute", "title": "{type, select, edit{Edit {name}} create{Add New} other{}} Dossier Attribute" }, + "add-edit-dossier-state": { + "form": { + "color": "Hex Color", + "color-placeholder": "#", + "name": "Status Name", + "name-placeholder": "Enter Name" + }, + "save": "Save Status", + "title": "{type, select, edit{Edit {name}} create{Create} other{}} Dossier Status" + }, "add-edit-dossier-template": { "error": { "conflict": "Failed to create dossier template: a dossier template with the same name already exists.", @@ -296,8 +305,8 @@ "suggestion-add": "Suggested redaction", "suggestion-add-dictionary": "Suggested dictionary add", "suggestion-change-legal-basis": "Suggested change legal basis", - "suggestion-force-hint": "", - "suggestion-force-redaction": "Suggestion force hint", + "suggestion-force-hint": "Suggestion force hint", + "suggestion-force-redaction": "Suggestion force redaction", "suggestion-recategorize-image": "Suggested recategorize image", "suggestion-remove": "Suggested local removal", "suggestion-remove-dictionary": "Suggested dictionary removal", @@ -399,6 +408,18 @@ } }, "configurations": "Configurations", + "confirm-delete-dossier-state": { + "cancel": "Cancel", + "delete-replace": "Delete and Replace", + "delete": "Delete only", + "form": { + "status": "Replace Status", + "status-placeholder": "Choose another status" + }, + "suggestion": "Would you like to replace the states of the Dossiers with another status?", + "title": "Delete Dossier Status", + "warning": "The {name} status is assigned to {count} {count, plural, one{Dossier} other{Dossiers}}." + }, "confirm-delete-file-attribute": { "cancel": "Keep {type, select, single{Attribute} bulk{Attributes} other{}}", "delete": "Delete {type, select, single{Attribute} bulk{Attributes} other{}}", @@ -696,10 +717,11 @@ "total-people": "Total users" }, "table-col-names": { + "documents-status": "Documents Status", + "dossier-status": "Dossier Status", "name": "Name", "needs-work": "Workload", - "owner": "Owner", - "status": "Status" + "owner": "Owner" }, "table-header": { "title": "{length} active {length, plural, one{Dossier} other{Dossiers}}" @@ -803,6 +825,35 @@ "under-review": "Under Review", "upload-files": "Drag & drop files anywhere..." }, + "dossier-states": "Dossier States", + "dossier-states-listing": { + "action": { + "delete": "Delete Status", + "edit": "Edit Status" + }, + "add-new": "New Status", + "chart": { + "dossier-states": "{count, plural, one{Dossier State} other{Dossier States}}" + }, + "error": { + "conflict": "Dossier State with this name already exists!", + "generic": "Failed to add Dossier State" + }, + "no-data": { + "title": "There are no dossier states." + }, + "no-match": { + "title": "No dossier states match your current filters." + }, + "search": "Search...", + "table-col-names": { + "dossiers-count": "Dossiers Count", + "name": "Name" + }, + "table-header": { + "title": "{length} dossier {length, plural, one{state} other{states}}" + } + }, "dossier-template-info": "Info", "dossier-template-info-screen": { "created-by": "Created by", @@ -957,6 +1008,11 @@ "label": "Description", "placeholder": "Enter Description" }, + "dossier-status": { + "label": "Dossier Status", + "no-status-placeholder": "This dossier template has no states", + "placeholder": "Undefined" + }, "due-date": "Due Date", "name": { "label": "Dossier Name", @@ -1633,8 +1689,8 @@ "no-time-left": "Time to restore already passed" }, "toggle-auto-analysis-message": { - "success": "{toggleOperation} automatic processing.", - "error": "Something went wrong." + "error": "Something went wrong.", + "success": "{toggleOperation} automatic processing." }, "top-bar": { "navigation-items": { diff --git a/bamboo-specs/src/main/java/buildjob/PlanSpec.java b/bamboo-specs/src/main/java/buildjob/PlanSpec.java index 2ca8f1b37..f9fdee0a0 100644 --- a/bamboo-specs/src/main/java/buildjob/PlanSpec.java +++ b/bamboo-specs/src/main/java/buildjob/PlanSpec.java @@ -48,6 +48,7 @@ public class PlanSpec { .userPermissions("atlbamboo", PermissionType.EDIT, PermissionType.VIEW, PermissionType.ADMIN, PermissionType.CLONE, PermissionType.BUILD) .userPermissions("tbejan", PermissionType.ADMIN, PermissionType.EDIT, PermissionType.VIEW, PermissionType.CLONE, PermissionType.BUILD) .groupPermissions("devplant", PermissionType.EDIT, PermissionType.VIEW, PermissionType.BUILD) + .groupPermissions("Documentation", PermissionType.VIEW) .loggedInUserPermissions(PermissionType.VIEW).anonymousUserPermissionView(); return new PlanPermissions(planIdentifier.getProjectKey(), planIdentifier.getPlanKey()).permissions(permission); } diff --git a/libs/red-domain/src/index.ts b/libs/red-domain/src/index.ts index aee8ff159..e2d4bf0e4 100644 --- a/libs/red-domain/src/index.ts +++ b/libs/red-domain/src/index.ts @@ -19,3 +19,4 @@ export * from './lib/configuration'; export * from './lib/signature'; export * from './lib/legal-basis'; export * from './lib/dossier-stats'; +export * from './lib/dossier-state'; diff --git a/libs/red-domain/src/lib/dossier-state/dossier-state.model.ts b/libs/red-domain/src/lib/dossier-state/dossier-state.model.ts new file mode 100644 index 000000000..242a34ace --- /dev/null +++ b/libs/red-domain/src/lib/dossier-state/dossier-state.model.ts @@ -0,0 +1,28 @@ +import { IListable } from '@iqser/common-ui'; +import { IDossierState } from './dossier-state'; + +export class DossierState implements IDossierState, IListable { + readonly description: string; + readonly dossierStatusId: string; + readonly dossierTemplateId: string; + readonly name: string; + readonly color: string; + dossierCount?: number; + + constructor(dossierState: IDossierState) { + this.description = dossierState.description; + this.dossierStatusId = dossierState.dossierStatusId; + this.dossierTemplateId = dossierState.dossierTemplateId; + this.name = dossierState.name; + this.color = dossierState.color; + this.dossierCount = dossierState.dossierCount; + } + + get id(): string { + return this.dossierStatusId; + } + + get searchKey(): string { + return this.name; + } +} diff --git a/libs/red-domain/src/lib/dossier-state/dossier-state.ts b/libs/red-domain/src/lib/dossier-state/dossier-state.ts new file mode 100644 index 000000000..f7491e23b --- /dev/null +++ b/libs/red-domain/src/lib/dossier-state/dossier-state.ts @@ -0,0 +1,8 @@ +export interface IDossierState { + description: string; + dossierStatusId: string; + dossierTemplateId: string; + name: string; + color: string; + dossierCount?: number; +} diff --git a/libs/red-domain/src/lib/dossier-state/index.ts b/libs/red-domain/src/lib/dossier-state/index.ts new file mode 100644 index 000000000..166b90570 --- /dev/null +++ b/libs/red-domain/src/lib/dossier-state/index.ts @@ -0,0 +1,2 @@ +export * from './dossier-state'; +export * from './dossier-state.model'; diff --git a/libs/red-domain/src/lib/dossiers/dossier.model.ts b/libs/red-domain/src/lib/dossiers/dossier.model.ts index 8c8ab6e5b..e2a79d550 100644 --- a/libs/red-domain/src/lib/dossiers/dossier.model.ts +++ b/libs/red-domain/src/lib/dossiers/dossier.model.ts @@ -11,6 +11,7 @@ export class Dossier implements IDossier, IListable { readonly approverIds: List; readonly reportTemplateIds: List; readonly dossierName: string; + readonly dossierStatusId: string; readonly date: string; readonly dueDate?: string; readonly description?: string; @@ -29,6 +30,7 @@ export class Dossier implements IDossier, IListable { this.date = dossier.date; this.description = dossier.description; this.dossierName = dossier.dossierName; + this.dossierStatusId = dossier.dossierStatusId; this.dossierTemplateId = dossier.dossierTemplateId; this.downloadFileTypes = dossier.downloadFileTypes; this.dueDate = dossier.dueDate; diff --git a/libs/red-domain/src/lib/dossiers/dossier.ts b/libs/red-domain/src/lib/dossiers/dossier.ts index 9b4883801..1ff219033 100644 --- a/libs/red-domain/src/lib/dossiers/dossier.ts +++ b/libs/red-domain/src/lib/dossiers/dossier.ts @@ -7,6 +7,7 @@ export interface IDossier { readonly date: string; readonly description?: string; readonly dossierId: string; + readonly dossierStatusId: string; readonly dossierName: string; readonly dossierTemplateId: string; readonly downloadFileTypes?: List; diff --git a/package.json b/package.json index 7ecfcc778..c2f6e6561 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.229.0", + "version": "3.236.0", "private": true, "license": "MIT", "scripts": { diff --git a/paligo-theme.tar.gz b/paligo-theme.tar.gz index bce044f60..8b823f63c 100644 Binary files a/paligo-theme.tar.gz and b/paligo-theme.tar.gz differ