Merge branch 'dan' into 'master'
RED-7421 show correct user options in assign dialog See merge request redactmanager/red-ui!101
This commit is contained in:
commit
2be891c6d3
@ -187,12 +187,8 @@ export class DossierOverviewBulkActionsComponent implements OnChanges {
|
||||
|
||||
this.#canAssign =
|
||||
this.#canMoveToSameState &&
|
||||
this.selectedFiles.reduce(
|
||||
(acc, file) =>
|
||||
(acc && this._permissionsService.canAssignUser(file, this.dossier)) ||
|
||||
this._permissionsService.canUnassignUser(file, this.dossier),
|
||||
true,
|
||||
);
|
||||
(this._permissionsService.canAssignUser(this.selectedFiles, this.dossier) ||
|
||||
this._permissionsService.canUnassignUser(this.selectedFiles, this.dossier));
|
||||
this.#canAssignToSelf = this.#canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles, this.dossier);
|
||||
|
||||
this.#canDelete = this._permissionsService.canSoftDeleteFile(this.selectedFiles, this.dossier);
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||
import { getConfig } from '@iqser/common-ui';
|
||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-table-item [file] [dossier] [displayedAttributes] [dossierTemplateId]',
|
||||
selector: 'redaction-table-item',
|
||||
templateUrl: './table-item.component.html',
|
||||
styleUrls: ['./table-item.component.scss'],
|
||||
})
|
||||
export class TableItemComponent {
|
||||
@Input() file: File;
|
||||
@Input() dossier: Dossier;
|
||||
@Input() displayedAttributes: IFileAttributeConfig[];
|
||||
@Input() dossierTemplateId: string;
|
||||
@Input({ required: true }) file: File;
|
||||
@Input({ required: true }) dossier: Dossier;
|
||||
@Input({ required: true }) displayedAttributes: IFileAttributeConfig[];
|
||||
@Input({ required: true }) dossierTemplateId: string;
|
||||
|
||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core';
|
||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||
import { HelpModeService } from '@iqser/common-ui';
|
||||
import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-workflow-item [file] [dossier] [displayedAttributes]',
|
||||
selector: 'redaction-workflow-item',
|
||||
templateUrl: './workflow-item.component.html',
|
||||
styleUrls: ['./workflow-item.component.scss'],
|
||||
})
|
||||
export class WorkflowItemComponent implements OnInit {
|
||||
width: number;
|
||||
trackBy = trackByFactory();
|
||||
|
||||
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
||||
|
||||
@Input() file: File;
|
||||
@Input() dossier: Dossier;
|
||||
@Input() displayedAttributes: IFileAttributeConfig[];
|
||||
width: number;
|
||||
readonly trackBy = trackByFactory();
|
||||
@Input({ required: true }) file: File;
|
||||
@Input({ required: true }) dossier: Dossier;
|
||||
@Input({ required: true }) displayedAttributes: IFileAttributeConfig[];
|
||||
|
||||
constructor(
|
||||
readonly fileAttributesService: FileAttributesService,
|
||||
|
||||
@ -1,20 +1,5 @@
|
||||
import { Component, ElementRef, HostListener, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import {
|
||||
Dossier,
|
||||
DOSSIER_ID,
|
||||
DossierAttributeConfig,
|
||||
DossierAttributeWithValue,
|
||||
File,
|
||||
IFileAttributeConfig,
|
||||
WorkflowFileStatus,
|
||||
} from '@red/domain';
|
||||
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
||||
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
||||
import { FileUploadService } from '@upload-download/services/file-upload.service';
|
||||
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
|
||||
import { merge, Observable } from 'rxjs';
|
||||
import { filter, skip, switchMap, tap } from 'rxjs/operators';
|
||||
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
CustomError,
|
||||
@ -28,21 +13,36 @@ import {
|
||||
TableComponent,
|
||||
WorkflowConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { DossierAttributesService } from '@services/entity-services/dossier-attributes.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
import { ConfigService } from '../config.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { BulkActionsService } from '../services/bulk-actions.service';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||
import { Roles } from '@users/roles';
|
||||
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
import { getParam, OnAttach, OnDetach, shareLast } from '@iqser/common-ui/lib/utils';
|
||||
import {
|
||||
Dossier,
|
||||
DOSSIER_ID,
|
||||
DossierAttributeConfig,
|
||||
DossierAttributeWithValue,
|
||||
File,
|
||||
IFileAttributeConfig,
|
||||
WorkflowFileStatus,
|
||||
} from '@red/domain';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { DossierAttributesService } from '@services/entity-services/dossier-attributes.service';
|
||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
||||
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
||||
import { FileUploadService } from '@upload-download/services/file-upload.service';
|
||||
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
||||
import { merge, Observable } from 'rxjs';
|
||||
import { filter, skip, switchMap, tap } from 'rxjs/operators';
|
||||
import { ConfigService } from '../config.service';
|
||||
import { BulkActionsService } from '../services/bulk-actions.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-overview-screen.component.html',
|
||||
@ -55,7 +55,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef;
|
||||
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
|
||||
private _fileAttributeConfigs: IFileAttributeConfig[];
|
||||
#fileAttributeConfigs: IFileAttributeConfig[];
|
||||
readonly listingModes = ListingModes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
|
||||
@ -216,8 +216,8 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
|
||||
#updateFileAttributes() {
|
||||
const attributes = this._fileAttributesService.getFileAttributeConfig(this.#dossier.dossierTemplateId);
|
||||
this._fileAttributeConfigs = attributes?.fileAttributeConfigs || [];
|
||||
this.displayedInFileListAttributes = this._fileAttributeConfigs.filter(config => config.displayedInFileList);
|
||||
this.#fileAttributeConfigs = attributes?.fileAttributeConfigs || [];
|
||||
this.displayedInFileListAttributes = this.#fileAttributeConfigs.filter(config => config.displayedInFileList);
|
||||
this.displayedAttributes = this.displayedInFileListAttributes.filter(c => c.displayedInFileList);
|
||||
this.displayedWorkflowAttributes = this.#getDisplayedWorkflowAttributes(this.displayedAttributes);
|
||||
this.tableColumnConfigs = this.configService.tableConfig(this.displayedAttributes);
|
||||
@ -227,7 +227,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
#getDisplayedWorkflowAttributes(displayedAttributes: IFileAttributeConfig[]): IFileAttributeConfig[] {
|
||||
let primaryAttribute = displayedAttributes.find(c => c.primaryAttribute);
|
||||
if (!primaryAttribute) {
|
||||
primaryAttribute = this._fileAttributeConfigs.find(config => config.primaryAttribute);
|
||||
primaryAttribute = this.#fileAttributeConfigs.find(config => config.primaryAttribute);
|
||||
return primaryAttribute ? [primaryAttribute, ...displayedAttributes] : displayedAttributes;
|
||||
}
|
||||
return displayedAttributes.sort((c1, c2) => (c1.primaryAttribute ? -1 : c2.primaryAttribute ? 1 : 0));
|
||||
@ -243,7 +243,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
#computeAllFilters() {
|
||||
const filterGroups = this.configService.filterGroups(
|
||||
this.entitiesService.all,
|
||||
this._fileAttributeConfigs,
|
||||
this.#fileAttributeConfigs,
|
||||
this.#dossier.dossierTemplateId,
|
||||
this._needsWorkFilterTemplate,
|
||||
() => this.checkedRequiredFilters,
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Dossier, File, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { DossiersDialogService } from '../../shared-dossiers/services/dossiers-dialog.service';
|
||||
import { IConfirmationDialogData, LoadingService } from '@iqser/common-ui';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { IConfirmationDialogData, IqserDialog, LoadingService } from '@iqser/common-ui';
|
||||
import { Dossier, File, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { FileManagementService } from '@services/files/file-management.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { AssignReviewerApproverDialogComponent } from '../../shared-dossiers/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
||||
import { DossiersDialogService } from '../../shared-dossiers/services/dossiers-dialog.service';
|
||||
import { FileAssignService } from '../../shared-dossiers/services/file-assign.service';
|
||||
|
||||
@Injectable()
|
||||
export class BulkActionsService {
|
||||
constructor(
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
private readonly _filesService: FilesService,
|
||||
@ -23,10 +25,10 @@ export class BulkActionsService {
|
||||
) {}
|
||||
|
||||
async setToUnderApproval(files: File[]) {
|
||||
const dossier = this._getDossier(files);
|
||||
const dossier = this.#getDossier(files);
|
||||
// If more than 1 approver - show dialog and ask who to assign
|
||||
if (dossier.approverIds.length > 1) {
|
||||
this._assignFiles(files, WorkflowFileStatuses.UNDER_APPROVAL, true);
|
||||
this.#assignFiles(files, WorkflowFileStatuses.UNDER_APPROVAL, true);
|
||||
} else {
|
||||
this._loadingService.start();
|
||||
await this._filesService.setUnderApproval(files, dossier.approverIds[0]);
|
||||
@ -79,7 +81,7 @@ export class BulkActionsService {
|
||||
|
||||
async backToUnderReview(files: File[]): Promise<void> {
|
||||
this._loadingService.start();
|
||||
await this._filesService.setUnderReviewFor(files);
|
||||
await this._filesService.setUnderReview(files);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
@ -121,14 +123,22 @@ export class BulkActionsService {
|
||||
}
|
||||
|
||||
assign(files: File[]): void {
|
||||
this._assignFiles(files, files[0].workflowStatus, false, true);
|
||||
this.#assignFiles(files, files[0].workflowStatus, false, true);
|
||||
}
|
||||
|
||||
private _getDossier(files: File[]): Dossier {
|
||||
#getDossier(files: File[]): Dossier {
|
||||
return this._activeDossiersService.find(files[0].dossierId);
|
||||
}
|
||||
|
||||
private _assignFiles(files: File[], targetStatus: WorkflowFileStatus, ignoreChanged = false, withUnassignedOption = false): void {
|
||||
this._dialogService.openDialog('assignFile', { targetStatus, files, ignoreChanged, withUnassignedOption });
|
||||
#assignFiles(files: File[], targetStatus: WorkflowFileStatus, ignoreChanged = false, withUnassignedOption = false): void {
|
||||
this._iqserDialog.openDefault(AssignReviewerApproverDialogComponent, {
|
||||
data: {
|
||||
targetStatus,
|
||||
files,
|
||||
ignoreChanged,
|
||||
withUnassignedOption,
|
||||
},
|
||||
disableClose: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { File, User } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { File, User } from '@red/domain';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@users/user.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { FileAssignService } from '../../../shared-dossiers/services/file-assign.service';
|
||||
import { moveElementInArray } from '@utils/functions';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { FileAssignService } from '../../../shared-dossiers/services/file-assign.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-management',
|
||||
@ -22,9 +22,7 @@ export class UserManagementComponent {
|
||||
protected readonly _canUnassignUser = computed(() => this.permissionsService.canUnassignUser(this.state.file(), this.state.dossier()));
|
||||
protected readonly _canAssignOrUnassign = computed(() => this._canAssignUser() || this._canUnassignUser());
|
||||
protected readonly _canAssign = computed(() => this._canAssignToSelf() || this._canAssignOrUnassign());
|
||||
protected readonly _canAssignReviewer = computed(
|
||||
() => this._canAssignUser() && !this.state.file().assignee && this.state.dossier().hasReviewers,
|
||||
);
|
||||
protected readonly _canAssignReviewer = computed(() => this._canAssignUser() && !this.state.file().assignee);
|
||||
protected readonly _usersOptions = computed(() => {
|
||||
const file = this.state.file();
|
||||
const dossier = this.state.dossier();
|
||||
|
||||
@ -2,7 +2,15 @@ import { ChangeDetectorRef, Component, HostBinding, Injector, Input, OnChanges,
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
import { Router } from '@angular/router';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { CircleButtonTypes, getConfig, IConfirmationDialogData, IqserPermissionsService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getConfig,
|
||||
IConfirmationDialogData,
|
||||
IqserDialog,
|
||||
IqserPermissionsService,
|
||||
LoadingService,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { IqserTooltipPositions } from '@iqser/common-ui/lib/utils';
|
||||
@ -24,6 +32,7 @@ import { ExcludedPagesService } from '../../../file-preview/services/excluded-pa
|
||||
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
||||
import { ViewerHeaderService } from '../../../pdf-viewer/services/viewer-header.service';
|
||||
import { ROTATION_ACTION_BUTTONS } from '../../../pdf-viewer/utils/constants';
|
||||
import { AssignReviewerApproverDialogComponent } from '../../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
import { FileAssignService } from '../../services/file-assign.service';
|
||||
|
||||
@ -77,6 +86,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
private readonly _changeRef: ChangeDetectorRef,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
private readonly _tenantsService: TenantsService,
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
@ -349,7 +359,14 @@ export class FileActionsComponent implements OnChanges {
|
||||
const targetStatus = this.file.workflowStatus;
|
||||
const withCurrentUserAsDefault = true;
|
||||
const withUnassignedOption = true;
|
||||
this._dialogService.openDialog('assignFile', { targetStatus, files, withCurrentUserAsDefault, withUnassignedOption });
|
||||
this._iqserDialog.openDefault(AssignReviewerApproverDialogComponent, {
|
||||
data: {
|
||||
targetStatus,
|
||||
files,
|
||||
withCurrentUserAsDefault,
|
||||
withUnassignedOption,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async #assignToMe() {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { IconButtonTypes, IqserDialogComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { File, User, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
@ -22,7 +21,11 @@ class DialogData {
|
||||
@Component({
|
||||
templateUrl: './assign-reviewer-approver-dialog.component.html',
|
||||
})
|
||||
export class AssignReviewerApproverDialogComponent {
|
||||
export class AssignReviewerApproverDialogComponent extends IqserDialogComponent<
|
||||
AssignReviewerApproverDialogComponent,
|
||||
DialogData,
|
||||
boolean
|
||||
> {
|
||||
readonly #dossier = this._activeDossiersService.find(this.data.files[0].dossierId);
|
||||
readonly #isApprover = this.permissionsService.isApprover(this.#dossier);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
@ -39,9 +42,9 @@ export class AssignReviewerApproverDialogComponent {
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _dialogRef: MatDialogRef<AssignReviewerApproverDialogComponent, boolean>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
|
||||
) {}
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
get selectedUser(): string {
|
||||
const value = this.form.controls.user.value;
|
||||
@ -71,18 +74,13 @@ export class AssignReviewerApproverDialogComponent {
|
||||
|
||||
get #userOptions() {
|
||||
const unassignUser = this.#canUnassignUser && this.data.withUnassignedOption ? ['undefined'] : [];
|
||||
const cannotAssignUser = !this.permissionsService.canAssignUser(this.data.files, this.#dossier);
|
||||
|
||||
if (this.mode === 'reviewer') {
|
||||
if (this.#dossier.hasReviewers && cannotAssignUser) {
|
||||
return [...unassignUser];
|
||||
}
|
||||
|
||||
return this.#customSort([...this.#dossier.memberIds, ...unassignUser]);
|
||||
const cannotAssignUser = !this.permissionsService.canAssignUser(this.data.files, this.#dossier, this.data.targetStatus);
|
||||
if (cannotAssignUser) {
|
||||
return [...unassignUser];
|
||||
}
|
||||
|
||||
if (this.#dossier.approverIds.length > 1 && cannotAssignUser) {
|
||||
return [...unassignUser];
|
||||
if (this.mode === 'reviewer') {
|
||||
return this.#customSort([...this.#dossier.memberIds, ...unassignUser]);
|
||||
}
|
||||
|
||||
return this.#customSort([...this.#dossier.approverIds, ...unassignUser]);
|
||||
@ -97,12 +95,22 @@ export class AssignReviewerApproverDialogComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
const files = this.data.files;
|
||||
const hasOnlyOneFile = this.hasOnlyOneFile(files);
|
||||
if (this.mode === 'reviewer') {
|
||||
return this.data.files.length === 1 ? this.data.files[0].assignee : this.currentUser.id;
|
||||
if (!hasOnlyOneFile) {
|
||||
return this.currentUser.id;
|
||||
}
|
||||
|
||||
if (files[0].assignee) {
|
||||
return files[0].assignee;
|
||||
}
|
||||
|
||||
return this.data.withCurrentUserAsDefault ? this.currentUser.id : undefined;
|
||||
}
|
||||
|
||||
const assignee = this.data.files[0].assignee;
|
||||
if (this.data.files.length === 1 && this.permissionsService.isApprover(this.#dossier, assignee)) {
|
||||
const assignee = files[0].assignee;
|
||||
if (hasOnlyOneFile && this.permissionsService.isApprover(this.#dossier, assignee)) {
|
||||
return assignee;
|
||||
}
|
||||
|
||||
@ -117,6 +125,10 @@ export class AssignReviewerApproverDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
hasOnlyOneFile(files: File[]): files is [File] {
|
||||
return files.length === 1;
|
||||
}
|
||||
|
||||
async save() {
|
||||
this._loadingService.start();
|
||||
const selectedUser = this.selectedUser;
|
||||
@ -134,7 +146,7 @@ export class AssignReviewerApproverDialogComponent {
|
||||
}
|
||||
|
||||
this._loadingService.stop();
|
||||
this._dialogRef.close(true);
|
||||
this.close(true);
|
||||
}
|
||||
|
||||
#customSort(ids: string[]) {
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
|
||||
import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
||||
import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } from '@iqser/common-ui';
|
||||
import { ImportRedactionsDialogComponent } from '../../file-preview/dialogs/import-redactions-dialog/import-redactions-dialog';
|
||||
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
|
||||
|
||||
type DialogType = 'confirm' | 'editDossier' | 'assignFile' | 'importRedactions';
|
||||
type DialogType = 'confirm' | 'editDossier' | 'importRedactions';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -20,10 +19,6 @@ export class DossiersDialogService extends DialogService<DialogType> {
|
||||
component: EditDossierDialogComponent,
|
||||
dialogConfig: { ...largeDialogConfig },
|
||||
},
|
||||
assignFile: {
|
||||
component: AssignReviewerApproverDialogComponent,
|
||||
dialogConfig: { disableClose: false },
|
||||
},
|
||||
importRedactions: {
|
||||
component: ImportRedactionsDialogComponent,
|
||||
dialogConfig: { disableClose: false },
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Dossier, File, User, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { DossiersDialogService } from './dossiers-dialog.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { IConfirmationDialogData, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { IConfirmationDialogData, IqserDialog, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { Dossier, File, User, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
||||
import { DossiersDialogService } from './dossiers-dialog.service';
|
||||
|
||||
const atLeastOneAssignee = (files: File[]) => files.reduce((acc, fs) => acc || !!fs.assignee, false);
|
||||
|
||||
@ -19,6 +20,7 @@ export class FileAssignService {
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
) {}
|
||||
|
||||
@ -26,7 +28,7 @@ export class FileAssignService {
|
||||
const assignReq = async () => {
|
||||
this._loadingService.start();
|
||||
if (files[0].isNew) {
|
||||
await this._makeAssignFileRequest(this.currentUser.id, 'UNDER_REVIEW', files);
|
||||
await this.#makeAssignFileRequest(this.currentUser.id, 'UNDER_REVIEW', files);
|
||||
} else {
|
||||
await this._filesService.setAssignee(files, this.currentUser.id);
|
||||
}
|
||||
@ -49,40 +51,46 @@ export class FileAssignService {
|
||||
}
|
||||
|
||||
async assignReviewer(file: File, ignoreChanged = false): Promise<void> {
|
||||
await this._assignFile(WorkflowFileStatuses.UNDER_REVIEW, file, ignoreChanged);
|
||||
await this.#assignFile(WorkflowFileStatuses.UNDER_REVIEW, file, ignoreChanged);
|
||||
}
|
||||
|
||||
async assignApprover(file: File, ignoreChanged = false): Promise<void> {
|
||||
await this._assignFile(WorkflowFileStatuses.UNDER_APPROVAL, file, ignoreChanged);
|
||||
await this.#assignFile(WorkflowFileStatuses.UNDER_APPROVAL, file, ignoreChanged);
|
||||
}
|
||||
|
||||
private async _assignFile(targetStatus: WorkflowFileStatus, file: File, ignoreChanged = false): Promise<void> {
|
||||
async #assignFile(targetStatus: WorkflowFileStatus, file: File, ignoreChanged = false): Promise<void> {
|
||||
const currentUserId = this.currentUser.id;
|
||||
const currentDossier = this._activeDossiersService.find(file.dossierId);
|
||||
const eligibleUsersIds = this._getUserIds(targetStatus, currentDossier);
|
||||
const eligibleUsersIds = this.#getUserIds(targetStatus, currentDossier);
|
||||
let userId: string;
|
||||
|
||||
if (file.isNew) {
|
||||
await this._makeAssignFileRequest(currentUserId, targetStatus, [file]);
|
||||
} else if (file.assignee === currentUserId) {
|
||||
if (eligibleUsersIds.includes(currentUserId)) {
|
||||
await this._makeAssignFileRequest(currentUserId, targetStatus, [file]);
|
||||
} else if (eligibleUsersIds.length === 1) {
|
||||
await this._makeAssignFileRequest(eligibleUsersIds[0], targetStatus, [file]);
|
||||
} else {
|
||||
const data = { targetStatus, files: [file], ignoreChanged };
|
||||
this._dialogService.openDialog('assignFile', data);
|
||||
}
|
||||
} else {
|
||||
if (eligibleUsersIds.length === 1) {
|
||||
await this._makeAssignFileRequest(eligibleUsersIds[0], targetStatus, [file]);
|
||||
} else {
|
||||
const data = { targetStatus, files: [file], ignoreChanged, withCurrentUserAsDefault: true };
|
||||
this._dialogService.openDialog('assignFile', data);
|
||||
}
|
||||
userId = currentUserId;
|
||||
}
|
||||
|
||||
if (file.assignee === currentUserId) {
|
||||
if (eligibleUsersIds.includes(currentUserId)) {
|
||||
userId = currentUserId;
|
||||
}
|
||||
} else if (eligibleUsersIds.length === 1) {
|
||||
userId = eligibleUsersIds[0];
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
return this.#makeAssignFileRequest(userId, targetStatus, [file]);
|
||||
}
|
||||
|
||||
this._iqserDialog.openDefault(AssignReviewerApproverDialogComponent, {
|
||||
data: {
|
||||
targetStatus,
|
||||
files: [file],
|
||||
ignoreChanged,
|
||||
withCurrentUserAsDefault: file.assignee !== currentUserId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async _makeAssignFileRequest(userId: string, targetStatus: WorkflowFileStatus, files: File[]) {
|
||||
async #makeAssignFileRequest(userId: string, targetStatus: WorkflowFileStatus, files: File[]) {
|
||||
this._loadingService.start();
|
||||
|
||||
try {
|
||||
@ -100,7 +108,7 @@ export class FileAssignService {
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
private _getUserIds(targetStatus: WorkflowFileStatus, dossier: Dossier) {
|
||||
#getUserIds(targetStatus: WorkflowFileStatus, dossier: Dossier) {
|
||||
return targetStatus === WorkflowFileStatuses.UNDER_APPROVAL || targetStatus === WorkflowFileStatuses.APPROVED
|
||||
? dossier.approverIds
|
||||
: dossier.memberIds;
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { EntitiesService, isArray, QueryParam } from '@iqser/common-ui';
|
||||
import { List, mapEach } from '@iqser/common-ui/lib/utils';
|
||||
import { File, IFile } from '@red/domain';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { UserService } from '@users/user.service';
|
||||
import { FilesMapService } from './files-map.service';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { map, switchMap, tap } from 'rxjs/operators';
|
||||
import { DossierStatsService } from '../dossiers/dossier-stats.service';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { List, mapEach } from '@iqser/common-ui/lib/utils';
|
||||
import { FilesMapService } from './files-map.service';
|
||||
|
||||
const asList = <T>(value: T | List<T>): List<T> => (isArray(value) ? value : [value]);
|
||||
|
||||
@ -73,7 +73,7 @@ export class FilesService extends EntitiesService<IFile, File> {
|
||||
return this.#makePost(_files, `${this._defaultModelPath}/approved/${_files[0].dossierId}/bulk`);
|
||||
}
|
||||
|
||||
async setUnderReviewFor(files: File | List<File>) {
|
||||
async setUnderReview(files: File | List<File>) {
|
||||
const _files = asList(files);
|
||||
return this.#makePost(_files, `${this._defaultModelPath}/under-review/${_files[0].dossierId}/bulk`);
|
||||
}
|
||||
|
||||
@ -10,6 +10,8 @@ import {
|
||||
IComment,
|
||||
IDossier,
|
||||
ProcessingFileStatuses,
|
||||
WorkflowFileStatus,
|
||||
WorkflowFileStatuses,
|
||||
} from '@red/domain';
|
||||
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
|
||||
import { FeaturesService } from '@services/features.service';
|
||||
@ -179,11 +181,11 @@ export class PermissionsService {
|
||||
);
|
||||
}
|
||||
|
||||
canAssignUser(file: File | File[], dossier: Dossier): boolean {
|
||||
canAssignUser(file: File | File[], dossier: Dossier, nextStatus?: WorkflowFileStatus): boolean {
|
||||
const files = file instanceof File ? [file] : file;
|
||||
return (
|
||||
this._iqserPermissionsService.has(Roles.setReviewer) &&
|
||||
files.reduce((acc, _file) => this.#canAssignUser(_file, dossier) && acc, true)
|
||||
files.reduce((acc, _file) => this.#canAssignUser(_file, dossier, nextStatus) && acc, true)
|
||||
);
|
||||
}
|
||||
|
||||
@ -468,30 +470,48 @@ export class PermissionsService {
|
||||
return dossier.isActive && fileCanBeSetUnderApproval && this.isApprover(dossier);
|
||||
}
|
||||
|
||||
#assignmentPrecondition(file: File, dossier: Dossier): boolean {
|
||||
#fileIsOk(file: File, dossier: Dossier) {
|
||||
return dossier.isActive && !file.isError && !file.isProcessing && !!file.lastProcessed;
|
||||
}
|
||||
|
||||
#canAssignToSelf(file: File, dossier: Dossier): boolean {
|
||||
const precondition = this.#assignmentPrecondition(file, dossier) && !this.isFileAssignee(file);
|
||||
const precondition = this.#fileIsOk(file, dossier) && !this.isFileAssignee(file);
|
||||
return precondition && (this.isApprover(dossier) || (this.isDossierMember(dossier) && (file.isNew || file.isUnderReview)));
|
||||
}
|
||||
|
||||
#canAssignUser(file: File, dossier: Dossier) {
|
||||
const precondition = this.#assignmentPrecondition(file, dossier);
|
||||
|
||||
if (precondition) {
|
||||
if ((file.isNew || file.isUnderReview) && dossier.hasReviewers && this.isDossierMember(dossier)) {
|
||||
return true;
|
||||
}
|
||||
if ((file.isUnderApproval || file.isApproved) && dossier.approverIds.length > 1 && this.isApprover(dossier)) {
|
||||
return true;
|
||||
}
|
||||
#canAssignUser(file: File, dossier: Dossier, nextStatus?: WorkflowFileStatus) {
|
||||
if (!this.#fileIsOk(file, dossier)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (nextStatus === WorkflowFileStatuses.UNDER_REVIEW) {
|
||||
return this.#canAssignReviewer(file, dossier);
|
||||
}
|
||||
|
||||
if (nextStatus === WorkflowFileStatuses.UNDER_APPROVAL || nextStatus === WorkflowFileStatuses.APPROVED) {
|
||||
return this.#canAssignApprover(file, dossier);
|
||||
}
|
||||
|
||||
if ((file.isNew || file.isUnderReview) && this.isDossierMember(dossier)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((file.isUnderApproval || file.isApproved) && dossier.approverIds.length > 1 && this.isApprover(dossier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#canAssignReviewer(file: File, dossier: Dossier) {
|
||||
const fileStatesForReviewer = file.isNew || file.isUnderReview || file.isUnderApproval;
|
||||
return fileStatesForReviewer && this.isDossierMember(dossier);
|
||||
}
|
||||
|
||||
#canAssignApprover(file: File, dossier: Dossier) {
|
||||
const fileStatesForApprover = file.isUnderReview || file.isUnderApproval || file.isApproved;
|
||||
return fileStatesForApprover && dossier.hasApprovers && this.isApprover(dossier);
|
||||
}
|
||||
|
||||
#canUnassignUser(file: File, dossier: Dossier) {
|
||||
return this.#assignmentPrecondition(file, dossier) && !!file.assignee && this.isAssigneeOrApprover(file, dossier);
|
||||
return this.#fileIsOk(file, dossier) && !!file.assignee && this.isAssigneeOrApprover(file, dossier);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 8d4a68f92cc74c54382ff898c39fab0cd8e0f72c
|
||||
Subproject commit 2f2ee530b15e9e84dc28c3a23340cfb8b8b94319
|
||||
@ -23,7 +23,7 @@ export class Dossier implements IDossier, IListable {
|
||||
readonly watermarkId?: number;
|
||||
readonly previewWatermarkId?: number;
|
||||
readonly archivedTime: string;
|
||||
readonly hasReviewers: boolean;
|
||||
readonly hasApprovers: boolean;
|
||||
readonly routerLink: string;
|
||||
readonly dossiersListRouterLink: string;
|
||||
readonly id: string;
|
||||
@ -51,7 +51,7 @@ export class Dossier implements IDossier, IListable {
|
||||
this.watermarkId = dossier.watermarkId;
|
||||
this.previewWatermarkId = dossier.previewWatermarkId;
|
||||
this.archivedTime = dossier.archivedTime;
|
||||
this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
|
||||
this.hasApprovers = !!this.approverIds && this.approverIds.length > 1;
|
||||
|
||||
this.isSoftDeleted = this.softDeletedTime !== null;
|
||||
this.isArchived = this.archivedTime !== null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user