Pull request #319: RED-2830
Merge in RED/ui from RED-2830 to master * commit '02da8e5a02e7d414550900e66acecf58f1ad9692': File bulk actions service, permissions for multiple files, workflow done Updated common Minor updates Fix some stuff, break other stuff More workflow updates (multi drag almost done) Workflow bulk actions Reset selection on view mode change Some fixes, workflow bulk actions WIP Workflow multi select Update workflow UI
This commit is contained in:
commit
b99bd42cca
@ -66,7 +66,7 @@ export class AssignReviewerApproverDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get _canUnassignFiles() {
|
private get _canUnassignFiles() {
|
||||||
return this.data.files.reduce((prev, file) => prev && this.permissionsService.canUnassignUser(file), true);
|
return this.permissionsService.canUnassignUser(this.data.files);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize the form with:
|
/** Initialize the form with:
|
||||||
|
|||||||
@ -1,78 +1,4 @@
|
|||||||
<ng-container (longPress)="forceReanalysisAction($event)" *ngIf="selectedFiles.length" redactionLongPress>
|
<ng-container (longPress)="forceReanalysisAction($event)" *ngIf="selectedFiles.length" redactionLongPress>
|
||||||
<iqser-circle-button
|
<redaction-expandable-file-actions [actions]="buttons" [buttonType]="buttonType" [maxWidth]="maxWidth" [tooltipPosition]="'above'">
|
||||||
(action)="delete()"
|
</redaction-expandable-file-actions>
|
||||||
*ngIf="canDelete"
|
|
||||||
[tooltip]="'dossier-overview.bulk.delete' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="iqser:trash"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="assign()"
|
|
||||||
*ngIf="canAssign"
|
|
||||||
[tooltip]="assignTooltip"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="red:assign"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="assignToMe()"
|
|
||||||
*ngIf="canAssignToSelf"
|
|
||||||
[tooltip]="'dossier-overview.assign-me' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="red:assign-me"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setToUnderApproval()"
|
|
||||||
*ngIf="canSetToUnderApproval"
|
|
||||||
[tooltip]="'dossier-overview.under-approval' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="red:ready-for-approval"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setToUnderReview()"
|
|
||||||
*ngIf="canSetToUnderReview"
|
|
||||||
[tooltip]="'dossier-overview.under-review' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="red:undo"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<redaction-file-download-btn [files]="selectedFiles"></redaction-file-download-btn>
|
|
||||||
|
|
||||||
<!-- Approved-->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="approveDocuments()"
|
|
||||||
*ngIf="isReadyForApproval"
|
|
||||||
[disabled]="!canApprove"
|
|
||||||
[tooltip]="canApprove ? ('dossier-overview.approve' | translate) : ('dossier-overview.approve-disabled' | translate)"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="red:approved"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- Back to approval -->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setToUnderApproval()"
|
|
||||||
*ngIf="canUndoApproval"
|
|
||||||
[tooltip]="'dossier-overview.under-approval' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="red:undo"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="ocr()"
|
|
||||||
*ngIf="canOcr"
|
|
||||||
[tooltip]="'dossier-overview.ocr-file' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="iqser:ocr"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="reanalyse()"
|
|
||||||
*ngIf="canReanalyse && analysisForced"
|
|
||||||
[tooltip]="'dossier-overview.bulk.reanalyse' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="iqser:refresh"
|
|
||||||
></iqser-circle-button>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@ -1,16 +1,11 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { Dossier, File } from '@red/domain';
|
import { Action, ActionTypes, Dossier, File } from '@red/domain';
|
||||||
import { FileAssignService } from '../../../../shared/services/file-assign.service';
|
import { CircleButtonType, CircleButtonTypes, Required } from '@iqser/common-ui';
|
||||||
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
|
|
||||||
import { CircleButtonTypes, ConfirmationDialogInput, LoadingService, Required } from '@iqser/common-ui';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
import { BulkActionsService } from '../../services/bulk-actions.service';
|
||||||
import { ReanalysisService } from '@services/reanalysis.service';
|
|
||||||
import { FilesService } from '@services/entity-services/files.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-dossier-overview-bulk-actions',
|
selector: 'redaction-dossier-overview-bulk-actions',
|
||||||
@ -18,204 +13,153 @@ import { FilesService } from '@services/entity-services/files.service';
|
|||||||
styleUrls: ['./dossier-overview-bulk-actions.component.scss'],
|
styleUrls: ['./dossier-overview-bulk-actions.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class DossierOverviewBulkActionsComponent {
|
export class DossierOverviewBulkActionsComponent implements OnChanges {
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
|
||||||
@Input() @Required() dossier: Dossier;
|
@Input() @Required() dossier: Dossier;
|
||||||
@Input() @Required() selectedFiles: File[];
|
@Input() @Required() selectedFiles: File[];
|
||||||
|
@Input() buttonType: CircleButtonType = CircleButtonTypes.dark;
|
||||||
|
@Input() maxWidth: number;
|
||||||
|
|
||||||
analysisForced: boolean;
|
analysisForced: boolean;
|
||||||
|
canAssignToSelf: boolean;
|
||||||
|
canAssign: boolean;
|
||||||
|
canDelete: boolean;
|
||||||
|
canReanalyse: boolean;
|
||||||
|
canOcr: boolean;
|
||||||
|
canSetToUnderReview: boolean;
|
||||||
|
canSetToUnderApproval: boolean;
|
||||||
|
isReadyForApproval: boolean;
|
||||||
|
canApprove: boolean;
|
||||||
|
canUndoApproval: boolean;
|
||||||
|
assignTooltip: string;
|
||||||
|
buttons: Action[];
|
||||||
|
|
||||||
|
private _canMoveToSameState: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _dialogService: DossiersDialogService,
|
|
||||||
private readonly _fileManagementService: FileManagementService,
|
|
||||||
private readonly _reanalysisService: ReanalysisService,
|
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _fileAssignService: FileAssignService,
|
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
private readonly _translateService: TranslateService,
|
|
||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _filesService: FilesService,
|
private readonly _bulkActionsService: BulkActionsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get allSelectedFilesCanBeAssignedIntoSameState() {
|
private get _buttons(): Action[] {
|
||||||
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
|
return [
|
||||||
(acc, file) => acc && (file.isUnderReview || file.isNew),
|
{
|
||||||
true,
|
type: ActionTypes.circleBtn,
|
||||||
);
|
action: () => this._bulkActionsService.delete(this.selectedFiles),
|
||||||
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
tooltip: _('dossier-overview.bulk.delete'),
|
||||||
return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
|
icon: 'iqser:trash',
|
||||||
|
show: this.canDelete,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._bulkActionsService.assign(this.selectedFiles),
|
||||||
|
tooltip: this.assignTooltip,
|
||||||
|
icon: 'red:assign',
|
||||||
|
show: this.canAssign,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._bulkActionsService.assignToMe(this.selectedFiles),
|
||||||
|
tooltip: _('dossier-overview.assign-me'),
|
||||||
|
icon: 'red:assign-me',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._bulkActionsService.backToUnderReview(this.selectedFiles),
|
||||||
|
tooltip: _('dossier-overview.under-review'),
|
||||||
|
icon: 'red:undo',
|
||||||
|
show: this.canSetToUnderReview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.downloadBtn,
|
||||||
|
show: true,
|
||||||
|
files: this.selectedFiles,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._bulkActionsService.approve(this.selectedFiles),
|
||||||
|
disabled: !this.canApprove,
|
||||||
|
tooltip: this.canApprove ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'),
|
||||||
|
icon: 'red:approved',
|
||||||
|
show: this.isReadyForApproval,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._bulkActionsService.setToUnderApproval(this.selectedFiles),
|
||||||
|
tooltip: _('dossier-overview.under-approval'),
|
||||||
|
icon: 'red:undo',
|
||||||
|
show: this.canUndoApproval,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._bulkActionsService.ocr(this.selectedFiles),
|
||||||
|
tooltip: _('dossier-overview.ocr-file'),
|
||||||
|
icon: 'iqser:ocr',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
].filter(btn => btn.show);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAssignToSelf() {
|
ngOnChanges() {
|
||||||
return (
|
this._setup();
|
||||||
this.allSelectedFilesCanBeAssignedIntoSameState &&
|
|
||||||
this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignToSelf(file), true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canAssign() {
|
|
||||||
return (
|
|
||||||
this.allSelectedFilesCanBeAssignedIntoSameState &&
|
|
||||||
this.selectedFiles.reduce(
|
|
||||||
(acc, file) => (acc && this._permissionsService.canAssignUser(file)) || this._permissionsService.canUnassignUser(file),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canDelete() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canDeleteFile(file), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canReanalyse() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canReanalyseFile(file), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canOcr() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canSetToUnderReview() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canSetToUnderApproval() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isReadyForApproval() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canApprove() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && file.canBeApproved, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canUndoApproval() {
|
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
get assignTooltip() {
|
|
||||||
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
|
||||||
return allFilesAreUnderApproval
|
|
||||||
? this._translateService.instant('dossier-overview.assign-approver')
|
|
||||||
: this._translateService.instant('dossier-overview.assign-reviewer');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
forceReanalysisAction($event: LongPressEvent) {
|
forceReanalysisAction($event: LongPressEvent) {
|
||||||
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
||||||
|
this._setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
private _setup() {
|
||||||
this._dialogService.openDialog(
|
if (this.selectedFiles.length) {
|
||||||
'confirm',
|
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
|
||||||
null,
|
(acc, file) => acc && (file.isUnderReview || file.isNew),
|
||||||
new ConfirmationDialogInput({
|
true,
|
||||||
title: _('confirmation-dialog.delete-file.title'),
|
|
||||||
question: _('confirmation-dialog.delete-file.question'),
|
|
||||||
}),
|
|
||||||
async () => {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._fileManagementService
|
|
||||||
.delete(
|
|
||||||
this.selectedFiles.map(item => item.fileId),
|
|
||||||
this.dossier.dossierId,
|
|
||||||
)
|
|
||||||
.toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setToUnderApproval() {
|
|
||||||
// If more than 1 approver - show dialog and ask who to assign
|
|
||||||
if (this.dossier.approverIds.length > 1) {
|
|
||||||
this._assignFiles('approver', true);
|
|
||||||
} else {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._filesService
|
|
||||||
.setUnderApprovalFor(
|
|
||||||
this.selectedFiles.map(f => f.id),
|
|
||||||
this.dossier.id,
|
|
||||||
this.dossier.approverIds[0],
|
|
||||||
)
|
|
||||||
.toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async reanalyse() {
|
|
||||||
this._loadingService.start();
|
|
||||||
const fileIds = this.selectedFiles.filter(file => file.analysisRequired).map(file => file.fileId);
|
|
||||||
await this._reanalysisService.reanalyzeFilesForDossier(fileIds, this.dossier.id).toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async ocr() {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._reanalysisService
|
|
||||||
.ocrFiles(
|
|
||||||
this.selectedFiles.map(f => f.fileId),
|
|
||||||
this.dossier.id,
|
|
||||||
)
|
|
||||||
.toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async setToUnderReview() {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._filesService
|
|
||||||
.setUnderReviewFor(
|
|
||||||
this.selectedFiles.map(f => f.id),
|
|
||||||
this.dossier.id,
|
|
||||||
)
|
|
||||||
.toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async approveDocuments(): Promise<void> {
|
|
||||||
const foundUpdatedFile = this.selectedFiles.find(file => file.hasUpdates);
|
|
||||||
if (foundUpdatedFile) {
|
|
||||||
this._dialogService.openDialog(
|
|
||||||
'confirm',
|
|
||||||
null,
|
|
||||||
new ConfirmationDialogInput({
|
|
||||||
title: _('confirmation-dialog.approve-multiple-files.title'),
|
|
||||||
question: _('confirmation-dialog.approve-multiple-files.question'),
|
|
||||||
}),
|
|
||||||
async () => {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._filesService
|
|
||||||
.setApprovedFor(
|
|
||||||
this.selectedFiles.map(f => f.id),
|
|
||||||
this.dossier.id,
|
|
||||||
)
|
|
||||||
.toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
} else {
|
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
||||||
this._loadingService.start();
|
this._canMoveToSameState = allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
|
||||||
await this._filesService
|
|
||||||
.setApprovedFor(
|
this.canAssign =
|
||||||
this.selectedFiles.map(f => f.id),
|
this._canMoveToSameState &&
|
||||||
this.dossier.id,
|
this.selectedFiles.reduce(
|
||||||
)
|
(acc, file) => (acc && this._permissionsService.canAssignUser(file)) || this._permissionsService.canUnassignUser(file),
|
||||||
.toPromise();
|
true,
|
||||||
this._loadingService.stop();
|
);
|
||||||
|
this.canAssignToSelf = this._canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles);
|
||||||
|
|
||||||
|
this.canDelete = this._permissionsService.canDeleteFile(this.selectedFiles);
|
||||||
|
|
||||||
|
this.canReanalyse = this._permissionsService.canReanalyseFile(this.selectedFiles);
|
||||||
|
|
||||||
|
this.canOcr = this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true);
|
||||||
|
|
||||||
|
this.canSetToUnderReview = this._permissionsService.canSetUnderReview(this.selectedFiles);
|
||||||
|
|
||||||
|
this.canSetToUnderApproval = this._permissionsService.canSetUnderApproval(this.selectedFiles);
|
||||||
|
|
||||||
|
this.isReadyForApproval = this._permissionsService.isReadyForApproval(this.selectedFiles);
|
||||||
|
|
||||||
|
this.canApprove = this._permissionsService.canBeApproved(this.selectedFiles);
|
||||||
|
|
||||||
|
this.canUndoApproval = this._permissionsService.canUndoApproval(this.selectedFiles);
|
||||||
|
|
||||||
|
this.assignTooltip = allFilesAreUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer');
|
||||||
|
|
||||||
|
this.buttons = this._buttons;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async assignToMe() {
|
|
||||||
await this._fileAssignService.assignToMe(this.selectedFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
assign() {
|
|
||||||
const mode = this.selectedFiles[0].isUnderApproval ? 'approver' : 'reviewer';
|
|
||||||
this._assignFiles(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _assignFiles(mode: 'reviewer' | 'approver', ignoreChanged = false) {
|
|
||||||
const data = { mode, files: this.selectedFiles, ignoreChanged };
|
|
||||||
this._dialogService.openDialog('assignFile', null, data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import { File } from '@red/domain';
|
|||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-file-workload-column',
|
selector: 'redaction-file-workload',
|
||||||
templateUrl: './file-workload-column.component.html',
|
templateUrl: './file-workload.component.html',
|
||||||
styleUrls: ['./file-workload-column.component.scss'],
|
styleUrls: ['./file-workload.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class FileWorkloadColumnComponent {
|
export class FileWorkloadComponent {
|
||||||
@Input() file: File;
|
@Input() file: File;
|
||||||
|
|
||||||
constructor(private readonly _appStateService: AppStateService, private readonly _dossiersService: DossiersService) {}
|
constructor(private readonly _appStateService: AppStateService, private readonly _dossiersService: DossiersService) {}
|
||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<ng-container *ngIf="!file.isError">
|
<ng-container *ngIf="!file.isError">
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<redaction-file-workload-column [file]="file"></redaction-file-workload-column>
|
<redaction-file-workload [file]="file"></redaction-file-workload>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user-column cell">
|
<div class="user-column cell">
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="all-caps-label" translate="view-mode.view-as"></div>
|
<div class="all-caps-label" translate="view-mode.view-as"></div>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="configService.listingMode = listingModes.table"
|
(action)="setListingMode(listingModes.table)"
|
||||||
[attr.aria-expanded]="mode === listingModes.table"
|
[attr.aria-expanded]="mode === listingModes.table"
|
||||||
[tooltip]="'view-mode.list' | translate"
|
[tooltip]="'view-mode.list' | translate"
|
||||||
[greySelected]="true"
|
[greySelected]="true"
|
||||||
@ -10,7 +10,7 @@
|
|||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="configService.listingMode = listingModes.workflow"
|
(action)="setListingMode(listingModes.workflow)"
|
||||||
[attr.aria-expanded]="mode === listingModes.workflow"
|
[attr.aria-expanded]="mode === listingModes.workflow"
|
||||||
[tooltip]="'view-mode.workflow' | translate"
|
[tooltip]="'view-mode.workflow' | translate"
|
||||||
[greySelected]="true"
|
[greySelected]="true"
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { ConfigService } from '../../config.service';
|
import { ConfigService } from '../../config.service';
|
||||||
import { CircleButtonTypes, ListingModes } from '@iqser/common-ui';
|
import { CircleButtonTypes, ListingMode, ListingModes, ListingService } from '@iqser/common-ui';
|
||||||
|
import { File } from '@red/domain';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-view-mode-selection',
|
selector: 'redaction-view-mode-selection',
|
||||||
@ -12,5 +13,10 @@ export class ViewModeSelectionComponent {
|
|||||||
readonly listingModes = ListingModes;
|
readonly listingModes = ListingModes;
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
|
|
||||||
constructor(readonly configService: ConfigService) {}
|
constructor(readonly configService: ConfigService, private readonly _listingService: ListingService<File>) {}
|
||||||
|
|
||||||
|
setListingMode(listingMode: ListingMode): void {
|
||||||
|
this.configService.listingMode = listingMode;
|
||||||
|
this._listingService.setSelected([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<div class="workflow-item">
|
<div class="workflow-item">
|
||||||
<div>
|
<div class="details-wrapper">
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div [matTooltip]="file.filename" class="filename" matTooltipPosition="above">
|
<div [matTooltip]="file.filename" [routerLink]="file.routerLink" class="filename pointer" matTooltipPosition="above">
|
||||||
{{ file.filename }}
|
{{ file.filename }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -13,5 +13,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-file-actions *ngIf="!file.isProcessing" [file]="file" type="dossier-overview-workflow"></redaction-file-actions>
|
<div *ngFor="let config of displayedAttributes" class="small-label mt-4">
|
||||||
|
{{ file.fileAttributes.attributeIdToValue[config.id] || '-' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<redaction-file-workload [file]="file"></redaction-file-workload>
|
||||||
|
|
||||||
|
<div class="file-actions">
|
||||||
|
<div
|
||||||
|
*ngIf="file.isProcessing"
|
||||||
|
class="spinning-icon mr-8"
|
||||||
|
matTooltip="{{ 'file-status.processing' | translate }}"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
>
|
||||||
|
<mat-icon svgIcon="red:reanalyse"></mat-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div #actionsWrapper class="actions-wrapper">
|
||||||
|
<redaction-file-actions
|
||||||
|
*ngIf="!file.isProcessing"
|
||||||
|
[file]="file"
|
||||||
|
[maxWidth]="width"
|
||||||
|
type="dossier-overview-workflow"
|
||||||
|
></redaction-file-actions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,34 +1,60 @@
|
|||||||
@use 'common-mixins';
|
@use 'common-mixins';
|
||||||
|
|
||||||
.workflow-item {
|
.workflow-item {
|
||||||
padding: 10px;
|
padding: 10px 10px 8px 10px;
|
||||||
|
|
||||||
> div {
|
&:hover .filename {
|
||||||
display: flex;
|
text-decoration: underline;
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.details {
|
|
||||||
max-width: calc(100% - 28px);
|
|
||||||
|
|
||||||
.filename {
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 18px;
|
|
||||||
@include common-mixins.line-clamp(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
redaction-file-actions {
|
|
||||||
margin-top: 10px;
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover redaction-file-actions {
|
&:hover redaction-file-actions {
|
||||||
display: block;
|
display: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.details-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.details {
|
||||||
|
max-width: calc(100% - 28px);
|
||||||
|
|
||||||
|
.filename {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 18px;
|
||||||
|
@include common-mixins.line-clamp(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redaction-file-workload {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: block;
|
||||||
|
min-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-actions {
|
||||||
|
margin-top: 8px;
|
||||||
|
min-height: 34px;
|
||||||
|
overflow: hidden;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
redaction-file-actions:not(.keep-visible) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-4 {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
import { File } from '@red/domain';
|
import { File, IFileAttributeConfig } from '@red/domain';
|
||||||
|
import { Debounce, Required } from '@iqser/common-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-workflow-item',
|
selector: 'redaction-workflow-item',
|
||||||
@ -7,6 +8,25 @@ import { File } from '@red/domain';
|
|||||||
styleUrls: ['./workflow-item.component.scss'],
|
styleUrls: ['./workflow-item.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class WorkflowItemComponent {
|
export class WorkflowItemComponent implements OnInit {
|
||||||
@Input() file: File;
|
@Input() @Required() file!: File;
|
||||||
|
@Input() @Required() displayedAttributes!: IFileAttributeConfig[];
|
||||||
|
width: number;
|
||||||
|
|
||||||
|
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
||||||
|
|
||||||
|
constructor(private readonly _changeRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const _observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
||||||
|
this._updateItemWidth(entries[0]);
|
||||||
|
});
|
||||||
|
_observer.observe(this._actionsWrapper.nativeElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Debounce(30)
|
||||||
|
private _updateItemWidth(entry: ResizeObserverEntry): void {
|
||||||
|
this.width = entry.contentRect.width;
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,14 +7,12 @@ import {
|
|||||||
List,
|
List,
|
||||||
ListingMode,
|
ListingMode,
|
||||||
ListingModes,
|
ListingModes,
|
||||||
LoadingService,
|
|
||||||
NestedFilter,
|
NestedFilter,
|
||||||
TableColumnConfig,
|
TableColumnConfig,
|
||||||
WorkflowConfig,
|
WorkflowConfig,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||||
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
|
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
|
||||||
import { FileAssignService } from '../../shared/services/file-assign.service';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -24,9 +22,9 @@ import { annotationFilterChecker, RedactionFilterSorter } from '@utils/index';
|
|||||||
import { workloadTranslations } from '../../translations/workload-translations';
|
import { workloadTranslations } from '../../translations/workload-translations';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { ConfigService as AppConfigService } from '@services/config.service';
|
import { ConfigService as AppConfigService } from '@services/config.service';
|
||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
|
||||||
import { FilesService } from '@services/entity-services/files.service';
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import noop from 'lodash/noop';
|
||||||
|
import { BulkActionsService } from './services/bulk-actions.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConfigService {
|
export class ConfigService {
|
||||||
@ -34,15 +32,12 @@ export class ConfigService {
|
|||||||
private readonly _listingMode$ = new BehaviorSubject<ListingMode>(ListingModes.table);
|
private readonly _listingMode$ = new BehaviorSubject<ListingMode>(ListingModes.table);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _fileAssignService: FileAssignService,
|
|
||||||
private readonly _filesService: FilesService,
|
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
private readonly _dossiersService: DossiersService,
|
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _dialogService: DossiersDialogService,
|
private readonly _dialogService: DossiersDialogService,
|
||||||
private readonly _appConfigService: AppConfigService,
|
private readonly _appConfigService: AppConfigService,
|
||||||
|
private readonly _bulkActionsService: BulkActionsService,
|
||||||
) {
|
) {
|
||||||
this.listingMode$ = this._listingMode$.asObservable();
|
this.listingMode$ = this._listingMode$.asObservable();
|
||||||
}
|
}
|
||||||
@ -63,34 +58,45 @@ export class ConfigService {
|
|||||||
{
|
{
|
||||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.NEW],
|
label: workflowFileStatusTranslations[WorkflowFileStatuses.NEW],
|
||||||
key: WorkflowFileStatuses.NEW,
|
key: WorkflowFileStatuses.NEW,
|
||||||
enterFn: this._unassignFn,
|
enterFn: noop,
|
||||||
enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file),
|
enterPredicate: () => false,
|
||||||
color: '#D3D5DA',
|
color: '#D3D5DA',
|
||||||
|
entities: new BehaviorSubject([]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
|
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
|
||||||
enterFn: this._underReviewFn,
|
enterFn: async (files: File[]) => {
|
||||||
enterPredicate: (file: File) =>
|
if (files[0].workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL) {
|
||||||
this._permissionsService.canSetUnderReview(file) ||
|
await this._bulkActionsService.backToUnderReview(files);
|
||||||
this._permissionsService.canAssignToSelf(file) ||
|
} else {
|
||||||
this._permissionsService.canAssignUser(file),
|
await this._bulkActionsService.assignToMe(files);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enterPredicate: (files: File[]) =>
|
||||||
|
this._permissionsService.canSetUnderReview(files) ||
|
||||||
|
this._permissionsService.canAssignToSelf(files) ||
|
||||||
|
this._permissionsService.canAssignUser(files),
|
||||||
key: WorkflowFileStatuses.UNDER_REVIEW,
|
key: WorkflowFileStatuses.UNDER_REVIEW,
|
||||||
color: '#FDBD00',
|
color: '#FDBD00',
|
||||||
|
entities: new BehaviorSubject([]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
|
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
|
||||||
enterFn: this._underApprovalFn,
|
enterFn: (files: File[]) => this._bulkActionsService.setToUnderApproval(files),
|
||||||
enterPredicate: (file: File) =>
|
enterPredicate: (files: File[]) =>
|
||||||
this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file),
|
this._permissionsService.canSetUnderApproval(files) || this._permissionsService.canUndoApproval(files),
|
||||||
key: WorkflowFileStatuses.UNDER_APPROVAL,
|
key: WorkflowFileStatuses.UNDER_APPROVAL,
|
||||||
color: '#374C81',
|
color: '#374C81',
|
||||||
|
entities: new BehaviorSubject([]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
|
label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
|
||||||
enterFn: this._approveFn,
|
enterFn: (files: File[]) => this._bulkActionsService.approve(files),
|
||||||
enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved,
|
enterPredicate: (files: File[]) =>
|
||||||
|
this._permissionsService.isReadyForApproval(files) && this._permissionsService.canBeApproved(files),
|
||||||
key: WorkflowFileStatuses.APPROVED,
|
key: WorkflowFileStatuses.APPROVED,
|
||||||
color: '#48C9F7',
|
color: '#48C9F7',
|
||||||
|
entities: new BehaviorSubject([]),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -323,7 +329,7 @@ export class ConfigService {
|
|||||||
|
|
||||||
_unassignedChecker = (file: File) => !file.assignee;
|
_unassignedChecker = (file: File) => !file.assignee;
|
||||||
|
|
||||||
_assignedToOthersChecker = (file: File) => !file.isNew && file.assignee !== this._userService.currentUser.id;
|
_assignedToOthersChecker = (file: File) => file.assignee && file.assignee !== this._userService.currentUser.id;
|
||||||
|
|
||||||
private _quickFilters(entities: File[]): NestedFilter[] {
|
private _quickFilters(entities: File[]): NestedFilter[] {
|
||||||
const recentPeriod = this._appConfigService.values.RECENT_PERIOD_IN_HOURS;
|
const recentPeriod = this._appConfigService.values.RECENT_PERIOD_IN_HOURS;
|
||||||
@ -361,31 +367,4 @@ export class ConfigService {
|
|||||||
private _openEditDossierDialog($event: MouseEvent, dossierId: string) {
|
private _openEditDossierDialog($event: MouseEvent, dossierId: string) {
|
||||||
this._dialogService.openDialog('editDossier', $event, { dossierId });
|
this._dialogService.openDialog('editDossier', $event, { dossierId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _unassignFn = async (file: File) => {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._filesService.setUnassigned([file.fileId], file.dossierId).toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
};
|
|
||||||
|
|
||||||
private _underReviewFn = async (file: File) => {
|
|
||||||
await this._fileAssignService.assignReviewer(null, file, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _underApprovalFn = async (file: File) => {
|
|
||||||
const dossier = this._dossiersService.find(file.dossierId);
|
|
||||||
if (dossier.approverIds.length > 1) {
|
|
||||||
await this._fileAssignService.assignApprover(null, file, true);
|
|
||||||
} else {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._filesService.setUnderApprovalFor([file.id], dossier.dossierId, dossier.approverIds[0]).toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _approveFn = async (file: File) => {
|
|
||||||
this._loadingService.start();
|
|
||||||
await this._filesService.setApprovedFor([file.id], file.dossierId).toPromise();
|
|
||||||
this._loadingService.stop();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,13 +11,14 @@ import { DossierDetailsStatsComponent } from './components/dossier-details-stats
|
|||||||
import { TableItemComponent } from './components/table-item/table-item.component';
|
import { TableItemComponent } from './components/table-item/table-item.component';
|
||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { SharedDossiersModule } from '../../shared/shared-dossiers.module';
|
import { SharedDossiersModule } from '../../shared/shared-dossiers.module';
|
||||||
import { FileWorkloadColumnComponent } from './components/table-item/file-workload-column/file-workload-column.component';
|
import { FileWorkloadComponent } from './components/table-item/file-workload/file-workload.component';
|
||||||
import { FileStatsComponent } from './components/file-stats/file-stats.component';
|
import { FileStatsComponent } from './components/file-stats/file-stats.component';
|
||||||
import { WorkflowItemComponent } from './components/workflow-item/workflow-item.component';
|
import { WorkflowItemComponent } from './components/workflow-item/workflow-item.component';
|
||||||
import { ScreenHeaderComponent } from './components/screen-header/screen-header.component';
|
import { ScreenHeaderComponent } from './components/screen-header/screen-header.component';
|
||||||
import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component';
|
import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component';
|
||||||
import { FileNameColumnComponent } from './components/table-item/file-name-column/file-name-column.component';
|
import { FileNameColumnComponent } from './components/table-item/file-name-column/file-name-column.component';
|
||||||
import { AddedColumnComponent } from './components/table-item/added-column/added-column.component';
|
import { AddedColumnComponent } from './components/table-item/added-column/added-column.component';
|
||||||
|
import { BulkActionsService } from './services/bulk-actions.service';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ const routes: Routes = [
|
|||||||
DossierOverviewBulkActionsComponent,
|
DossierOverviewBulkActionsComponent,
|
||||||
DossierDetailsComponent,
|
DossierDetailsComponent,
|
||||||
DossierDetailsStatsComponent,
|
DossierDetailsStatsComponent,
|
||||||
FileWorkloadColumnComponent,
|
FileWorkloadComponent,
|
||||||
TableItemComponent,
|
TableItemComponent,
|
||||||
FileStatsComponent,
|
FileStatsComponent,
|
||||||
WorkflowItemComponent,
|
WorkflowItemComponent,
|
||||||
@ -45,7 +46,7 @@ const routes: Routes = [
|
|||||||
FileNameColumnComponent,
|
FileNameColumnComponent,
|
||||||
AddedColumnComponent,
|
AddedColumnComponent,
|
||||||
],
|
],
|
||||||
providers: [ConfigService],
|
providers: [ConfigService, BulkActionsService],
|
||||||
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule],
|
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule],
|
||||||
})
|
})
|
||||||
export class DossierOverviewModule {}
|
export class DossierOverviewModule {}
|
||||||
|
|||||||
@ -32,16 +32,16 @@
|
|||||||
(noDataAction)="fileInput.click()"
|
(noDataAction)="fileInput.click()"
|
||||||
*ngIf="mode === listingModes.workflow"
|
*ngIf="mode === listingModes.workflow"
|
||||||
[addElementIcon]="'iqser:upload'"
|
[addElementIcon]="'iqser:upload'"
|
||||||
|
[bulkActions]="bulkActions"
|
||||||
[config]="workflowConfig"
|
[config]="workflowConfig"
|
||||||
[itemClasses]="{ disabled: disabledFn }"
|
[itemClasses]="{ disabled: disabledFn }"
|
||||||
[itemHeight]="'56px'"
|
|
||||||
[itemTemplate]="workflowItemTemplate"
|
[itemTemplate]="workflowItemTemplate"
|
||||||
[noDataButtonIcon]="'iqser:upload'"
|
[noDataButtonIcon]="'iqser:upload'"
|
||||||
[noDataButtonLabel]="'dossier-overview.no-data.action' | translate"
|
[noDataButtonLabel]="'dossier-overview.no-data.action' | translate"
|
||||||
[noDataIcon]="'iqser:document'"
|
[noDataIcon]="'iqser:document'"
|
||||||
[noDataText]="'dossier-overview.no-data.title' | translate"
|
[noDataText]="'dossier-overview.no-data.title' | translate"
|
||||||
[showNoDataButton]="true"
|
[showNoDataButton]="true"
|
||||||
addElementColumn="UNASSIGNED"
|
addElementColumn="NEW"
|
||||||
></iqser-workflow>
|
></iqser-workflow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -54,10 +54,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<ng-template #bulkActions>
|
<ng-template #bulkActions let-maxWidth="maxWidth">
|
||||||
<redaction-dossier-overview-bulk-actions
|
<redaction-dossier-overview-bulk-actions
|
||||||
|
[buttonType]="(configService.listingMode$ | async) === listingModes.table ? circleButtonTypes.dark : circleButtonTypes.primary"
|
||||||
[dossier]="dossier"
|
[dossier]="dossier"
|
||||||
[selectedFiles]="this.listingService.selected"
|
[maxWidth]="maxWidth"
|
||||||
|
[selectedFiles]="listingService.selectedEntities$ | async"
|
||||||
></redaction-dossier-overview-bulk-actions>
|
></redaction-dossier-overview-bulk-actions>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
@ -77,5 +79,5 @@
|
|||||||
<input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" />
|
<input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" />
|
||||||
|
|
||||||
<ng-template #workflowItemTemplate let-entity="entity">
|
<ng-template #workflowItemTemplate let-entity="entity">
|
||||||
<redaction-workflow-item [file]="entity"></redaction-workflow-item>
|
<redaction-workflow-item [displayedAttributes]="displayedAttributes" [file]="entity"></redaction-workflow-item>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@ -123,7 +123,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
|||||||
}
|
}
|
||||||
|
|
||||||
disabledFn = (file: File) => file.excluded;
|
disabledFn = (file: File) => file.excluded;
|
||||||
|
|
||||||
lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;
|
lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
|
|||||||
@ -0,0 +1,140 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Dossier, File } from '@red/domain';
|
||||||
|
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||||
|
import { ConfirmationDialogInput, LoadingService } from '@iqser/common-ui';
|
||||||
|
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||||
|
import { FilesService } from '@services/entity-services/files.service';
|
||||||
|
import { FileAssignService } from '../../../shared/services/file-assign.service';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { ReanalysisService } from '../../../../../services/reanalysis.service';
|
||||||
|
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BulkActionsService {
|
||||||
|
constructor(
|
||||||
|
private readonly _dialogService: DossiersDialogService,
|
||||||
|
private readonly _loadingService: LoadingService,
|
||||||
|
private readonly _dossiersService: DossiersService,
|
||||||
|
private readonly _filesService: FilesService,
|
||||||
|
private readonly _fileAssignService: FileAssignService,
|
||||||
|
private readonly _reanalysisService: ReanalysisService,
|
||||||
|
private readonly _fileManagementService: FileManagementService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async setToUnderApproval(files: File[]) {
|
||||||
|
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, 'approver', true);
|
||||||
|
} else {
|
||||||
|
this._loadingService.start();
|
||||||
|
await this._filesService
|
||||||
|
.setUnderApprovalFor(
|
||||||
|
files.map(f => f.id),
|
||||||
|
dossier.id,
|
||||||
|
dossier.approverIds[0],
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async assignToMe(files: File[]) {
|
||||||
|
await this._fileAssignService.assignToMe(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ocr(files: File[]) {
|
||||||
|
this._loadingService.start();
|
||||||
|
await this._reanalysisService
|
||||||
|
.ocrFiles(
|
||||||
|
files.map(f => f.fileId),
|
||||||
|
files[0].dossierId,
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(files: File[]) {
|
||||||
|
this._dialogService.openDialog(
|
||||||
|
'confirm',
|
||||||
|
null,
|
||||||
|
new ConfirmationDialogInput({
|
||||||
|
title: _('confirmation-dialog.delete-file.title'),
|
||||||
|
question: _('confirmation-dialog.delete-file.question'),
|
||||||
|
}),
|
||||||
|
async () => {
|
||||||
|
this._loadingService.start();
|
||||||
|
await this._fileManagementService
|
||||||
|
.delete(
|
||||||
|
files.map(item => item.fileId),
|
||||||
|
files[0].dossierId,
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async reanalyse(files: File[]) {
|
||||||
|
this._loadingService.start();
|
||||||
|
const fileIds = files.filter(file => file.analysisRequired).map(file => file.fileId);
|
||||||
|
await this._reanalysisService.reanalyzeFilesForDossier(fileIds, files[0].dossierId).toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
async backToUnderReview(files: File[]): Promise<void> {
|
||||||
|
this._loadingService.start();
|
||||||
|
await this._filesService
|
||||||
|
.setUnderReviewFor(
|
||||||
|
files.map(f => f.id),
|
||||||
|
files[0].dossierId,
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
async approve(files: File[]): Promise<void> {
|
||||||
|
const foundUpdatedFile = files.find(file => file.hasUpdates);
|
||||||
|
if (foundUpdatedFile) {
|
||||||
|
this._dialogService.openDialog(
|
||||||
|
'confirm',
|
||||||
|
null,
|
||||||
|
new ConfirmationDialogInput({
|
||||||
|
title: _('confirmation-dialog.approve-multiple-files.title'),
|
||||||
|
question: _('confirmation-dialog.approve-multiple-files.question'),
|
||||||
|
}),
|
||||||
|
async () => {
|
||||||
|
this._loadingService.start();
|
||||||
|
await this._filesService
|
||||||
|
.setApprovedFor(
|
||||||
|
files.map(f => f.id),
|
||||||
|
files[0].dossierId,
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._loadingService.start();
|
||||||
|
await this._filesService
|
||||||
|
.setApprovedFor(
|
||||||
|
files.map(f => f.id),
|
||||||
|
files[0].dossierId,
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assign(files: File[], mode: 'reviewer' | 'approver' = files[0].isUnderApproval ? 'approver' : 'reviewer') {
|
||||||
|
this._assignFiles(files, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDossier(files: File[]): Dossier {
|
||||||
|
return this._dossiersService.find(files[0].dossierId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _assignFiles(files: File[], mode: 'reviewer' | 'approver', ignoreChanged = false) {
|
||||||
|
this._dialogService.openDialog('assignFile', null, { mode, files, ignoreChanged });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,30 +46,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.multi-select {
|
|
||||||
min-height: 40px;
|
|
||||||
background: variables.$primary;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 8px 0 16px;
|
|
||||||
color: variables.$white;
|
|
||||||
|
|
||||||
.selected-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
iqser-round-checkbox .wrapper.inactive {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.all-caps-label {
|
|
||||||
margin: 0 16px 0 8px;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotations-wrapper {
|
.annotations-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@ -48,13 +48,13 @@ export class UserManagementComponent implements OnChanges {
|
|||||||
|
|
||||||
private get _assignOrChangeReviewerTooltip(): string {
|
private get _assignOrChangeReviewerTooltip(): string {
|
||||||
return this.file.assignee
|
return this.file.assignee
|
||||||
? this.translateService.instant('file-preview.change-reviewer')
|
? this.translateService.instant(_('file-preview.change-reviewer'))
|
||||||
: this.translateService.instant('file-preview.assign-reviewer');
|
: this.translateService.instant(_('file-preview.assign-reviewer'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _assignTooltip(): string {
|
private get _assignTooltip(): string {
|
||||||
return this.file.isUnderApproval
|
return this.file.isUnderApproval
|
||||||
? this.translateService.instant('dossier-overview.assign-approver')
|
? this.translateService.instant(_('dossier-overview.assign-approver'))
|
||||||
: this._assignOrChangeReviewerTooltip;
|
: this._assignOrChangeReviewerTooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,9 +68,9 @@ export class UserManagementComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.file, this.dossier);
|
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.file);
|
||||||
this.canAssignUser = this.permissionsService.canAssignUser(this.file, this.dossier);
|
this.canAssignUser = this.permissionsService.canAssignUser(this.file);
|
||||||
this.canUnassignUser = this.permissionsService.canUnassignUser(this.file, this.dossier);
|
this.canUnassignUser = this.permissionsService.canUnassignUser(this.file);
|
||||||
this.canAssignOrUnassign = this.canAssignUser || this.canUnassignUser;
|
this.canAssignOrUnassign = this.canAssignUser || this.canUnassignUser;
|
||||||
this.canAssign = this.canAssignToSelf || this.canAssignOrUnassign;
|
this.canAssign = this.canAssignToSelf || this.canAssignOrUnassign;
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,6 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
|||||||
import { clearStamps, stampPDFPage } from '@utils/page-stamper';
|
import { clearStamps, stampPDFPage } from '@utils/page-stamper';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { handleFilterDelta } from '@utils/filter-utils';
|
import { handleFilterDelta } from '@utils/filter-utils';
|
||||||
import { FileActionsComponent } from '../../shared/components/file-actions/file-actions.component';
|
|
||||||
import { FilesService } from '@services/entity-services/files.service';
|
import { FilesService } from '@services/entity-services/files.service';
|
||||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||||
@ -55,6 +54,7 @@ import { ExcludedPagesService } from './services/excluded-pages.service';
|
|||||||
import { ViewModeService } from './services/view-mode.service';
|
import { ViewModeService } from './services/view-mode.service';
|
||||||
import { MultiSelectService } from './services/multi-select.service';
|
import { MultiSelectService } from './services/multi-select.service';
|
||||||
import { DocumentInfoService } from './services/document-info.service';
|
import { DocumentInfoService } from './services/document-info.service';
|
||||||
|
import { ReanalysisService } from '../../../../services/reanalysis.service';
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
import PDFNet = Core.PDFNet;
|
import PDFNet = Core.PDFNet;
|
||||||
|
|
||||||
@ -78,7 +78,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
hideSkipped = false;
|
hideSkipped = false;
|
||||||
displayPdfViewer = false;
|
displayPdfViewer = false;
|
||||||
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
|
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
|
||||||
@ViewChild('fileActions') fileActions: FileActionsComponent;
|
|
||||||
readonly dossierId: string;
|
readonly dossierId: string;
|
||||||
readonly canPerformAnnotationActions$: Observable<boolean>;
|
readonly canPerformAnnotationActions$: Observable<boolean>;
|
||||||
readonly dossier$: Observable<Dossier>;
|
readonly dossier$: Observable<Dossier>;
|
||||||
@ -117,6 +116,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _filesMapService: FilesMapService,
|
private readonly _filesMapService: FilesMapService,
|
||||||
private readonly _dossiersService: DossiersService,
|
private readonly _dossiersService: DossiersService,
|
||||||
|
private readonly _reanalysisService: ReanalysisService,
|
||||||
readonly excludedPagesService: ExcludedPagesService,
|
readonly excludedPagesService: ExcludedPagesService,
|
||||||
readonly viewModeService: ViewModeService,
|
readonly viewModeService: ViewModeService,
|
||||||
readonly multiSelectService: MultiSelectService,
|
readonly multiSelectService: MultiSelectService,
|
||||||
@ -241,7 +241,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||||
if (file?.analysisRequired) {
|
if (file?.analysisRequired) {
|
||||||
await this.fileActions.reanalyseFile();
|
await this._reanalysisService.reanalyzeFilesForDossier([this.fileId], this.dossierId, true).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.displayPdfViewer = true;
|
this.displayPdfViewer = true;
|
||||||
|
|||||||
@ -16,153 +16,12 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #actions (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
<ng-template #actions (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
||||||
<div *ngIf="file" class="file-actions" iqserHelpMode="document-features">
|
<div class="file-actions" iqserHelpMode="document-features">
|
||||||
<iqser-circle-button
|
<redaction-expandable-file-actions
|
||||||
(action)="openDocument()"
|
[actions]="buttons"
|
||||||
*ngIf="showOpenDocument"
|
[buttonType]="buttonType"
|
||||||
|
[maxWidth]="maxWidth"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="'dossier-overview.open-document' | translate"
|
></redaction-expandable-file-actions>
|
||||||
[type]="buttonType"
|
|
||||||
icon="iqser:collapse"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="openDeleteFileDialog($event)"
|
|
||||||
*ngIf="showDelete"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.delete.action' | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="iqser:trash"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="assign($event)"
|
|
||||||
*ngIf="showAssign"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="assignTooltip | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="red:assign"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="assignToMe($event)"
|
|
||||||
*ngIf="showAssignToSelf"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.assign-me' | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="red:assign-me"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- download redacted file-->
|
|
||||||
<redaction-file-download-btn
|
|
||||||
[files]="[file]"
|
|
||||||
[tooltipClass]="'small'"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[type]="buttonType"
|
|
||||||
></redaction-file-download-btn>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="documentInfoService.toggle()"
|
|
||||||
*ngIf="documentInfoService"
|
|
||||||
[attr.aria-expanded]="documentInfoService.shown$ | async"
|
|
||||||
[tooltip]="'file-preview.document-info' | translate"
|
|
||||||
icon="red:status-info"
|
|
||||||
tooltipPosition="below"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="excludedPagesService.toggle()"
|
|
||||||
*ngIf="excludedPagesService"
|
|
||||||
[attr.aria-expanded]="excludedPagesService.shown$ | async"
|
|
||||||
[showDot]="!!file.excludedPages?.length"
|
|
||||||
[tooltip]="'file-preview.exclude-pages' | translate"
|
|
||||||
icon="red:exclude-pages"
|
|
||||||
tooltipPosition="below"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- Ready for approval-->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setFileUnderApproval($event)"
|
|
||||||
*ngIf="showUnderApproval"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.under-approval' | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="red:ready-for-approval"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- Back to review -->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setFileUnderReview($event, true)"
|
|
||||||
*ngIf="showUnderReview"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.under-review' | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="red:undo"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- Approved-->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setFileApproved($event)"
|
|
||||||
*ngIf="showApprove"
|
|
||||||
[disabled]="!file.canBeApproved"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="file.canBeApproved ? ('dossier-overview.approve' | translate) : ('dossier-overview.approve-disabled' | translate)"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="red:approved"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- Back to approval -->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="setFileUnderApproval($event)"
|
|
||||||
*ngIf="showUndoApproval"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.under-approval' | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="red:undo"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="ocrFile($event)"
|
|
||||||
*ngIf="showOCR"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.ocr-file' | translate"
|
|
||||||
[type]="buttonType"
|
|
||||||
icon="iqser:ocr"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- reanalyse file preview -->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="reanalyseFile($event)"
|
|
||||||
*ngIf="canReanalyse && isFilePreview && analysisForced"
|
|
||||||
[tooltip]="'file-preview.reanalyse-notification' | translate"
|
|
||||||
[type]="circleButtonTypes.warn"
|
|
||||||
icon="iqser:refresh"
|
|
||||||
tooltipClass="warn small"
|
|
||||||
tooltipPosition="below"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- reanalyse file listing -->
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="reanalyseFile($event)"
|
|
||||||
*ngIf="canReanalyse && isDossierOverview && analysisForced"
|
|
||||||
[tooltipPosition]="tooltipPosition"
|
|
||||||
[tooltip]="'dossier-overview.reanalyse.action' | translate"
|
|
||||||
[type]="circleButtonTypes.dark"
|
|
||||||
icon="iqser:refresh"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<!-- exclude from redaction -->
|
|
||||||
<div class="iqser-input-group">
|
|
||||||
<mat-slide-toggle
|
|
||||||
(change)="toggleAnalysis()"
|
|
||||||
(click)="$event.stopPropagation()"
|
|
||||||
[checked]="!file?.excluded"
|
|
||||||
[class.mr-24]="isDossierOverviewList"
|
|
||||||
[disabled]="!canToggleAnalysis"
|
|
||||||
[matTooltipPosition]="tooltipPosition"
|
|
||||||
[matTooltip]="toggleTooltip | translate"
|
|
||||||
color="primary"
|
|
||||||
></mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
.file-actions {
|
.file-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow-y: auto;
|
|
||||||
color: variables.$grey-1;
|
color: variables.$grey-1;
|
||||||
@include common-mixins.no-scroll-bar;
|
|
||||||
|
|
||||||
> *:not(:last-child) {
|
> *:not(:last-child) {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
@ -27,14 +25,6 @@ iqser-status-bar {
|
|||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-slide-toggle {
|
|
||||||
height: 34px;
|
|
||||||
width: 34px;
|
|
||||||
line-height: 33px;
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinning-icon {
|
.spinning-icon {
|
||||||
margin: 0 12px 0 11px;
|
margin: 0 12px 0 11px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,18 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Optional, Output } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Optional,
|
||||||
|
Output,
|
||||||
|
ViewChild,
|
||||||
|
} from '@angular/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { File } from '@red/domain';
|
import { Action, ActionTypes, File } from '@red/domain';
|
||||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||||
import {
|
import {
|
||||||
AutoUnsubscribe,
|
AutoUnsubscribe,
|
||||||
@ -25,6 +37,7 @@ import { Router } from '@angular/router';
|
|||||||
import { ExcludedPagesService } from '../../../screens/file-preview-screen/services/excluded-pages.service';
|
import { ExcludedPagesService } from '../../../screens/file-preview-screen/services/excluded-pages.service';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { DocumentInfoService } from '../../../screens/file-preview-screen/services/document-info.service';
|
import { DocumentInfoService } from '../../../screens/file-preview-screen/services/document-info.service';
|
||||||
|
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-file-actions',
|
selector: 'redaction-file-actions',
|
||||||
@ -36,8 +49,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
readonly circleButtonTypes = CircleButtonTypes;
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
|
|
||||||
@Input() file: File;
|
@Input() @Required() file: File;
|
||||||
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
||||||
|
@Input() maxWidth: number;
|
||||||
@Output() readonly ocredFile = new EventEmitter<void>();
|
@Output() readonly ocredFile = new EventEmitter<void>();
|
||||||
|
|
||||||
toggleTooltip?: string;
|
toggleTooltip?: string;
|
||||||
@ -56,16 +70,20 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
canToggleAnalysis: boolean;
|
canToggleAnalysis: boolean;
|
||||||
showStatusBar: boolean;
|
showStatusBar: boolean;
|
||||||
showOpenDocument: boolean;
|
showOpenDocument: boolean;
|
||||||
|
showReanalyseFilePreview: boolean;
|
||||||
|
showReanalyseDossierOverview: boolean;
|
||||||
analysisForced: boolean;
|
analysisForced: boolean;
|
||||||
isDossierOverview = false;
|
isDossierOverview = false;
|
||||||
isDossierOverviewList = false;
|
isDossierOverviewList = false;
|
||||||
isDossierOverviewWorkflow = false;
|
isDossierOverviewWorkflow = false;
|
||||||
isFilePreview = false;
|
isFilePreview = false;
|
||||||
tooltipPosition: IqserTooltipPosition;
|
tooltipPosition: IqserTooltipPosition;
|
||||||
|
buttons: Action[];
|
||||||
|
@ViewChild(ExpandableFileActionsComponent) _expandableActionsComponent: ExpandableFileActionsComponent;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Optional() readonly excludedPagesService: ExcludedPagesService,
|
@Optional() private readonly _excludedPagesService: ExcludedPagesService,
|
||||||
@Optional() readonly documentInfoService: DocumentInfoService,
|
@Optional() private readonly _documentInfoService: DocumentInfoService,
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _dossiersService: DossiersService,
|
private readonly _dossiersService: DossiersService,
|
||||||
private readonly _dialogService: DossiersDialogService,
|
private readonly _dialogService: DossiersDialogService,
|
||||||
@ -82,6 +100,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostBinding('class.keep-visible') get expanded() {
|
||||||
|
return this._expandableActionsComponent?.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
private get _toggleTooltip(): string {
|
private get _toggleTooltip(): string {
|
||||||
if (!this.currentUser.isManager) {
|
if (!this.currentUser.isManager) {
|
||||||
return _('file-preview.toggle-analysis.only-managers');
|
return _('file-preview.toggle-analysis.only-managers');
|
||||||
@ -90,6 +112,116 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
return this.file?.excluded ? _('file-preview.toggle-analysis.enable') : _('file-preview.toggle-analysis.disable');
|
return this.file?.excluded ? _('file-preview.toggle-analysis.enable') : _('file-preview.toggle-analysis.disable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _buttons(): Action[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._openDeleteFileDialog($event),
|
||||||
|
tooltip: _('dossier-overview.delete.action'),
|
||||||
|
icon: 'iqser:trash',
|
||||||
|
show: this.showDelete,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._assign($event),
|
||||||
|
tooltip: this.assignTooltip,
|
||||||
|
icon: 'red:assign',
|
||||||
|
show: this.showAssign,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._assignToMe($event),
|
||||||
|
tooltip: _('dossier-overview.assign-me'),
|
||||||
|
icon: 'red:assign-me',
|
||||||
|
show: this.showAssignToSelf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.downloadBtn,
|
||||||
|
show: true,
|
||||||
|
files: [this.file],
|
||||||
|
tooltipClass: 'small',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._documentInfoService.toggle(),
|
||||||
|
tooltip: _('file-preview.document-info'),
|
||||||
|
ariaExpanded: this._documentInfoService?.shown$,
|
||||||
|
icon: 'red:status-info',
|
||||||
|
show: !!this._documentInfoService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: () => this._excludedPagesService.toggle(),
|
||||||
|
tooltip: _('file-preview.exclude-pages'),
|
||||||
|
ariaExpanded: this._excludedPagesService?.shown$,
|
||||||
|
showDot: !!this.file.excludedPages?.length,
|
||||||
|
icon: 'red:exclude-pages',
|
||||||
|
show: !!this._excludedPagesService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._setFileUnderApproval($event),
|
||||||
|
tooltip: _('dossier-overview.under-approval'),
|
||||||
|
icon: 'red:ready-for-approval',
|
||||||
|
show: this.showUnderApproval,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._setFileUnderReview($event),
|
||||||
|
tooltip: _('dossier-overview.under-review'),
|
||||||
|
icon: 'red:undo',
|
||||||
|
show: this.showUnderReview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this.setFileApproved($event),
|
||||||
|
tooltip: this.file.canBeApproved ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'),
|
||||||
|
icon: 'red:approved',
|
||||||
|
disabled: !this.file.canBeApproved,
|
||||||
|
show: this.showApprove,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._setFileUnderApproval($event),
|
||||||
|
tooltip: _('dossier-overview.under-approval'),
|
||||||
|
icon: 'red:undo',
|
||||||
|
show: this.showUndoApproval,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._ocrFile($event),
|
||||||
|
tooltip: _('dossier-overview.ocr-file'),
|
||||||
|
icon: 'iqser:ocr',
|
||||||
|
show: this.showOCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._reanalyseFile($event),
|
||||||
|
tooltip: _('file-preview.reanalyse-notification'),
|
||||||
|
buttonType: CircleButtonTypes.warn,
|
||||||
|
tooltipClass: 'warn small',
|
||||||
|
icon: 'iqser:refresh',
|
||||||
|
show: this.showReanalyseFilePreview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.circleBtn,
|
||||||
|
action: $event => this._reanalyseFile($event),
|
||||||
|
tooltip: _('dossier-overview.reanalyse.action'),
|
||||||
|
icon: 'iqser:refresh',
|
||||||
|
show: this.showReanalyseDossierOverview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActionTypes.toggle,
|
||||||
|
action: () => this._toggleAnalysis(),
|
||||||
|
disabled: !this.canToggleAnalysis,
|
||||||
|
tooltip: this.toggleTooltip,
|
||||||
|
class: { 'mr-24': this.isDossierOverviewList },
|
||||||
|
checked: !this.file.excluded,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
].filter(btn => btn.show);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._dossiersService.getEntityChanged$(this.file.dossierId).pipe(tap(() => this._setup()));
|
this._dossiersService.getEntityChanged$(this.file.dossierId).pipe(tap(() => this._setup()));
|
||||||
}
|
}
|
||||||
@ -98,57 +230,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
this._setup();
|
this._setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
openDocument() {
|
|
||||||
this._router.navigate([this.file.routerLink]).then();
|
|
||||||
}
|
|
||||||
|
|
||||||
openDeleteFileDialog($event: MouseEvent) {
|
|
||||||
this._dialogService.openDialog(
|
|
||||||
'confirm',
|
|
||||||
$event,
|
|
||||||
new ConfirmationDialogInput({
|
|
||||||
title: _('confirmation-dialog.delete-file.title'),
|
|
||||||
question: _('confirmation-dialog.delete-file.question'),
|
|
||||||
}),
|
|
||||||
async () => {
|
|
||||||
this._loadingService.start();
|
|
||||||
try {
|
|
||||||
const dossier = this._dossiersService.find(this.file.dossierId);
|
|
||||||
await this._fileManagementService.delete([this.file.fileId], this.file.dossierId).toPromise();
|
|
||||||
await this._router.navigate([dossier.routerLink]);
|
|
||||||
} catch (error) {
|
|
||||||
this._toaster.error(_('error.http.generic'), { params: error });
|
|
||||||
}
|
|
||||||
this._loadingService.stop();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assign($event: MouseEvent) {
|
|
||||||
const mode = this.file.isUnderApproval ? 'approver' : 'reviewer';
|
|
||||||
const files = [this.file];
|
|
||||||
const withCurrentUserAsDefault = true;
|
|
||||||
const withUnassignedOption = true;
|
|
||||||
this._dialogService.openDialog('assignFile', $event, { mode, files, withCurrentUserAsDefault, withUnassignedOption });
|
|
||||||
}
|
|
||||||
|
|
||||||
async assignToMe($event: MouseEvent) {
|
|
||||||
$event.stopPropagation();
|
|
||||||
await this._fileAssignService.assignToMe([this.file]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async reanalyseFile($event?: MouseEvent) {
|
|
||||||
if ($event) {
|
|
||||||
$event.stopPropagation();
|
|
||||||
}
|
|
||||||
await this._reanalysisService.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, true).toPromise();
|
|
||||||
}
|
|
||||||
|
|
||||||
async setFileUnderApproval($event: MouseEvent) {
|
|
||||||
$event.stopPropagation();
|
|
||||||
await this._fileAssignService.assignApprover($event, this.file, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setFileApproved($event: MouseEvent) {
|
async setFileApproved($event: MouseEvent) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
if (!this.file.hasUpdates) {
|
if (!this.file.hasUpdates) {
|
||||||
@ -169,7 +250,59 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ocrFile($event: MouseEvent) {
|
forceReanalysisAction($event: LongPressEvent) {
|
||||||
|
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
||||||
|
this._setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openDeleteFileDialog($event: MouseEvent) {
|
||||||
|
this._dialogService.openDialog(
|
||||||
|
'confirm',
|
||||||
|
$event,
|
||||||
|
new ConfirmationDialogInput({
|
||||||
|
title: _('confirmation-dialog.delete-file.title'),
|
||||||
|
question: _('confirmation-dialog.delete-file.question'),
|
||||||
|
}),
|
||||||
|
async () => {
|
||||||
|
this._loadingService.start();
|
||||||
|
try {
|
||||||
|
const dossier = this._dossiersService.find(this.file.dossierId);
|
||||||
|
await this._fileManagementService.delete([this.file.fileId], this.file.dossierId).toPromise();
|
||||||
|
await this._router.navigate([dossier.routerLink]);
|
||||||
|
} catch (error) {
|
||||||
|
this._toaster.error(_('error.http.generic'), { params: error });
|
||||||
|
}
|
||||||
|
this._loadingService.stop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _assign($event: MouseEvent) {
|
||||||
|
const mode = this.file.isUnderApproval ? 'approver' : 'reviewer';
|
||||||
|
const files = [this.file];
|
||||||
|
const withCurrentUserAsDefault = true;
|
||||||
|
const withUnassignedOption = true;
|
||||||
|
this._dialogService.openDialog('assignFile', $event, { mode, files, withCurrentUserAsDefault, withUnassignedOption });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _assignToMe($event: MouseEvent) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
await this._fileAssignService.assignToMe([this.file]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _reanalyseFile($event?: MouseEvent) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
await this._reanalysisService.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, true).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _setFileUnderApproval($event: MouseEvent) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
await this._fileAssignService.assignApprover($event, this.file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _ocrFile($event: MouseEvent) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId).toPromise();
|
await this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId).toPromise();
|
||||||
@ -177,20 +310,16 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
private async _setFileUnderReview($event: MouseEvent) {
|
||||||
await this._fileAssignService.assignReviewer($event, this.file, ignoreDialogChanges);
|
await this._fileAssignService.assignReviewer($event, this.file, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleAnalysis() {
|
private async _toggleAnalysis() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this._reanalysisService.toggleAnalysis(this.file.dossierId, this.file.fileId, !this.file.excluded).toPromise();
|
await this._reanalysisService.toggleAnalysis(this.file.dossierId, this.file.fileId, !this.file.excluded).toPromise();
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
forceReanalysisAction($event: LongPressEvent) {
|
|
||||||
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setup() {
|
private _setup() {
|
||||||
this.isDossierOverviewList = this.type === 'dossier-overview-list';
|
this.isDossierOverviewList = this.type === 'dossier-overview-list';
|
||||||
this.isDossierOverviewWorkflow = this.type === 'dossier-overview-workflow';
|
this.isDossierOverviewWorkflow = this.type === 'dossier-overview-workflow';
|
||||||
@ -220,7 +349,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
|||||||
(this._permissionsService.canAssignUser(this.file) || this._permissionsService.canUnassignUser(this.file)) &&
|
(this._permissionsService.canAssignUser(this.file) || this._permissionsService.canUnassignUser(this.file)) &&
|
||||||
this.isDossierOverview;
|
this.isDossierOverview;
|
||||||
|
|
||||||
this.showOpenDocument = this.file.canBeOpened && this.isDossierOverviewWorkflow;
|
this.showReanalyseFilePreview = this.canReanalyse && this.isFilePreview && this.analysisForced;
|
||||||
|
this.showReanalyseDossierOverview = this.canReanalyse && this.isDossierOverview && this.analysisForced;
|
||||||
|
|
||||||
|
this.buttons = this._buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setFileApproved() {
|
private async _setFileApproved() {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
[disabled]="disabled || !canDownloadFiles"
|
[disabled]="disabled || !canDownloadFiles"
|
||||||
[tooltipClass]="tooltipClass"
|
[tooltipClass]="tooltipClass"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="tooltip"
|
[tooltip]="tooltip | translate: { count: this.files.length }"
|
||||||
[type]="type"
|
[type]="type"
|
||||||
icon="iqser:download"
|
icon="iqser:download"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { File } from '@red/domain';
|
import { File } from '@red/domain';
|
||||||
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
||||||
import { CircleButtonType, CircleButtonTypes, List, Toaster } from '@iqser/common-ui';
|
import { CircleButtonType, CircleButtonTypes, Toaster } from '@iqser/common-ui';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
|
||||||
export type MenuState = 'OPEN' | 'CLOSED';
|
export type MenuState = 'OPEN' | 'CLOSED';
|
||||||
@ -14,28 +13,25 @@ export type MenuState = 'OPEN' | 'CLOSED';
|
|||||||
styleUrls: ['./file-download-btn.component.scss'],
|
styleUrls: ['./file-download-btn.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class FileDownloadBtnComponent {
|
export class FileDownloadBtnComponent implements OnChanges {
|
||||||
@Input() files: List<File>;
|
@Input() files: File[];
|
||||||
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
|
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
|
||||||
@Input() type: CircleButtonType = CircleButtonTypes.default;
|
@Input() type: CircleButtonType = CircleButtonTypes.default;
|
||||||
@Input() tooltipClass: string;
|
@Input() tooltipClass: string;
|
||||||
@Input() disabled = false;
|
@Input() disabled = false;
|
||||||
|
|
||||||
|
tooltip: string;
|
||||||
|
canDownloadFiles: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _fileDownloadService: FileDownloadService,
|
private readonly _fileDownloadService: FileDownloadService,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _translateService: TranslateService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get canDownloadFiles() {
|
ngOnChanges(): void {
|
||||||
return this.files.length > 0 && this.files.reduce((acc, file) => acc && this._permissionsService.canDownloadFiles(file), true);
|
this.canDownloadFiles = this._permissionsService.canDownloadFiles(this.files);
|
||||||
}
|
this.tooltip = this.canDownloadFiles ? _('dossier-overview.download-file') : _('dossier-overview.download-file-disabled');
|
||||||
|
|
||||||
get tooltip() {
|
|
||||||
return this.canDownloadFiles
|
|
||||||
? this._translateService.instant('dossier-overview.download-file')
|
|
||||||
: this._translateService.instant('dossier-overview.download-file-disabled', { count: this.files.length });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadFiles($event: MouseEvent) {
|
async downloadFiles($event: MouseEvent) {
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
<ng-container *ngFor="let btn of displayedButtons">
|
||||||
|
<iqser-circle-button
|
||||||
|
(action)="btn.action($event)"
|
||||||
|
*ngIf="btn.type === 'circleBtn'"
|
||||||
|
[attr.aria-expanded]="btn.ariaExpanded && btn.ariaExpanded | async"
|
||||||
|
[disabled]="btn.disabled"
|
||||||
|
[icon]="btn.icon"
|
||||||
|
[showDot]="btn.showDot"
|
||||||
|
[tooltipClass]="btn.tooltipClass"
|
||||||
|
[tooltipPosition]="tooltipPosition"
|
||||||
|
[tooltip]="btn.tooltip | translate"
|
||||||
|
[type]="btn.buttonType || buttonType"
|
||||||
|
></iqser-circle-button>
|
||||||
|
|
||||||
|
<!-- download redacted file-->
|
||||||
|
<redaction-file-download-btn
|
||||||
|
*ngIf="btn.type === 'downloadBtn'"
|
||||||
|
[files]="btn.files"
|
||||||
|
[tooltipClass]="btn.tooltipClass"
|
||||||
|
[tooltipPosition]="tooltipPosition"
|
||||||
|
[type]="buttonType"
|
||||||
|
></redaction-file-download-btn>
|
||||||
|
|
||||||
|
<!-- exclude from redaction -->
|
||||||
|
<div *ngIf="btn.type === 'toggle'" class="iqser-input-group">
|
||||||
|
<mat-slide-toggle
|
||||||
|
(change)="btn.action()"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
[checked]="btn.checked"
|
||||||
|
[disabled]="btn.disabled"
|
||||||
|
[matTooltipPosition]="tooltipPosition"
|
||||||
|
[matTooltip]="btn.tooltip | translate"
|
||||||
|
[ngClass]="btn.class"
|
||||||
|
color="primary"
|
||||||
|
></mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<iqser-circle-button
|
||||||
|
(menuClosed)="expanded = false"
|
||||||
|
(menuOpened)="expanded = true"
|
||||||
|
*ngIf="hiddenButtons.length > 0"
|
||||||
|
[attr.aria-expanded]="expanded"
|
||||||
|
[icon]="'iqser:more-actions'"
|
||||||
|
[matMenuTriggerFor]="hiddenButtonsMenu"
|
||||||
|
[type]="buttonType"
|
||||||
|
></iqser-circle-button>
|
||||||
|
|
||||||
|
<mat-menu #hiddenButtonsMenu="matMenu" class="padding-bottom-8">
|
||||||
|
<button (click)="btn.action($event)" *ngFor="let btn of hiddenButtons" [disabled]="btn.disabled" mat-menu-item>
|
||||||
|
<ng-container *ngIf="btn.type === 'circleBtn'">
|
||||||
|
<mat-icon [svgIcon]="btn.icon"></mat-icon>
|
||||||
|
{{ btn.tooltip | translate }}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="btn.type === 'downloadBtn'">
|
||||||
|
<mat-icon svgIcon="iqser:download"></mat-icon>
|
||||||
|
{{ 'dossier-overview.download-file' | translate }}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="btn.type === 'toggle'">
|
||||||
|
<mat-slide-toggle [checked]="btn.checked" [disabled]="btn.disabled" class="ml-0" color="primary"></mat-slide-toggle>
|
||||||
|
{{ btn.tooltip | translate }}
|
||||||
|
</ng-container>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
:host {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-slide-toggle {
|
||||||
|
height: 34px;
|
||||||
|
width: 34px;
|
||||||
|
line-height: 33px;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-menu-item {
|
||||||
|
mat-icon {
|
||||||
|
color: inherit;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
color: rgba(var(--iqser-accent-rgb), 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-0 {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
|
import { Action, ActionTypes, File } from '@red/domain';
|
||||||
|
import { CircleButtonType, IqserTooltipPosition, Toaster } from '@iqser/common-ui';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
||||||
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-expandable-file-actions',
|
||||||
|
templateUrl: './expandable-file-actions.component.html',
|
||||||
|
styleUrls: ['./expandable-file-actions.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ExpandableFileActionsComponent implements OnChanges {
|
||||||
|
@Input() maxWidth: number;
|
||||||
|
@Input() actions: Action[];
|
||||||
|
@Input() buttonType: CircleButtonType;
|
||||||
|
@Input() tooltipPosition: IqserTooltipPosition;
|
||||||
|
|
||||||
|
displayedButtons: Action[];
|
||||||
|
hiddenButtons: Action[];
|
||||||
|
expanded = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _fileDownloadService: FileDownloadService,
|
||||||
|
private readonly _toaster: Toaster,
|
||||||
|
private readonly _permissionsService: PermissionsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes.actions || changes.maxWidth) {
|
||||||
|
if (this.maxWidth) {
|
||||||
|
const count = Math.floor(this.maxWidth / 36) || 1;
|
||||||
|
if (count >= this.actions.length) {
|
||||||
|
this.displayedButtons = [...this.actions];
|
||||||
|
this.hiddenButtons = [];
|
||||||
|
} else {
|
||||||
|
this.displayedButtons = this.actions.slice(0, count - 1);
|
||||||
|
this.hiddenButtons = this.actions.slice(count - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.displayedButtons = [...this.actions];
|
||||||
|
this.hiddenButtons = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.actions) {
|
||||||
|
// Patch download button
|
||||||
|
const downloadBtn = this.actions.find(btn => btn.type === ActionTypes.downloadBtn);
|
||||||
|
if (downloadBtn) {
|
||||||
|
downloadBtn.action = $event => this._downloadFiles($event, downloadBtn.files);
|
||||||
|
downloadBtn.disabled = !this._permissionsService.canDownloadFiles(downloadBtn.files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _downloadFiles($event: MouseEvent, files: File[]) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
const dossierId = files[0].dossierId;
|
||||||
|
const filesIds = files.map(f => f.fileId);
|
||||||
|
await this._fileDownloadService.downloadFiles(filesIds, dossierId).toPromise();
|
||||||
|
this._toaster.info(_('download-status.queued'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ import { NamePipe } from './pipes/name.pipe';
|
|||||||
import { TypeFilterComponent } from './components/type-filter/type-filter.component';
|
import { TypeFilterComponent } from './components/type-filter/type-filter.component';
|
||||||
import { TeamMembersComponent } from './components/team-members/team-members.component';
|
import { TeamMembersComponent } from './components/team-members/team-members.component';
|
||||||
import { EditorComponent } from './components/editor/editor.component';
|
import { EditorComponent } from './components/editor/editor.component';
|
||||||
|
import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component';
|
||||||
|
|
||||||
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
|
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ const components = [
|
|||||||
AssignUserDropdownComponent,
|
AssignUserDropdownComponent,
|
||||||
TypeFilterComponent,
|
TypeFilterComponent,
|
||||||
TeamMembersComponent,
|
TeamMembersComponent,
|
||||||
|
ExpandableFileActionsComponent,
|
||||||
|
|
||||||
...buttons,
|
...buttons,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -22,58 +22,57 @@ export class PermissionsService {
|
|||||||
return this.isReviewerOrApprover(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
|
return this.isReviewerOrApprover(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
|
||||||
}
|
}
|
||||||
|
|
||||||
canReanalyseFile(file: File): boolean {
|
canReanalyseFile(file: File | File[]): boolean {
|
||||||
return this.isReviewerOrApprover(file) || file.isNew || (file.isError && file.isNew);
|
const files = file instanceof File ? [file] : file;
|
||||||
|
return files.reduce((acc, _file) => this._canReanalyseFile(_file) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
isFileAssignee(file: File): boolean {
|
isFileAssignee(file: File): boolean {
|
||||||
return file.assignee === this._userService.currentUser.id;
|
return file.assignee === this._userService.currentUser.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://jira.iqser.com/browse/RED-2787
|
canDeleteFile(file: File | File[]): boolean {
|
||||||
canDeleteFile(file: File): boolean {
|
const files = file instanceof File ? [file] : file;
|
||||||
const dossier = this._getDossier(file);
|
const dossier = this._getDossier(files[0]);
|
||||||
return (
|
return files.reduce((acc, _file) => this._canDeleteFile(_file, dossier) && acc, true);
|
||||||
file.isNew ||
|
|
||||||
(file.isUnderReview && !file.assignee && this.isDossierMember(dossier)) ||
|
|
||||||
(file.isUnderApproval && !file.assignee && this.isApprover(dossier)) ||
|
|
||||||
(file.assignee && !file.isApproved && (this.isFileAssignee(file) || this.isOwner(dossier)))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canAssignToSelf(file: File, dossier = this._getDossier(file)): boolean {
|
canAssignToSelf(file: File | File[]): boolean {
|
||||||
const precondition = this.isDossierMember(dossier) && !this.isFileAssignee(file) && !file.isError && !file.isProcessing;
|
const files = file instanceof File ? [file] : file;
|
||||||
return precondition && (file.isNew || file.isUnderReview || (file.isUnderApproval && this.isApprover(dossier)));
|
const dossier = this._getDossier(files[0]);
|
||||||
|
return files.reduce((acc, _file) => this._canAssignToSelf(_file, dossier) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAssignUser(file: File, dossier = this._getDossier(file)): boolean {
|
canAssignUser(file: File | File[]): boolean {
|
||||||
const precondition = !file.isProcessing && !file.isError && !file.isApproved && this.isApprover(dossier);
|
const files = file instanceof File ? [file] : file;
|
||||||
|
const dossier = this._getDossier(files[0]);
|
||||||
if (precondition) {
|
return files.reduce((acc, _file) => this._canAssignUser(_file, dossier) && acc, true);
|
||||||
if ((file.isNew || file.isUnderReview) && dossier.hasReviewers) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (file.isUnderApproval && dossier.approverIds.length > 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canUnassignUser(file: File, dossier = this._getDossier(file)): boolean {
|
canUnassignUser(file: File | File[]): boolean {
|
||||||
return (file.isUnderReview || file.isUnderApproval) && (this.isFileAssignee(file) || this.isApprover(dossier));
|
const files = file instanceof File ? [file] : file;
|
||||||
|
const dossier = this._getDossier(files[0]);
|
||||||
|
return files.reduce((acc, _file) => this._canUnassignUser(_file, dossier) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canSetUnderReview(file: File, dossier = this._getDossier(file)): boolean {
|
canSetUnderReview(file: File | File[]): boolean {
|
||||||
return file.isUnderApproval && this.isApprover(dossier);
|
const files = file instanceof File ? [file] : file;
|
||||||
|
const dossier = this._getDossier(files[0]);
|
||||||
|
return this.isApprover(dossier) && files.reduce((acc, _file) => this._canSetUnderReview(_file) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
isReadyForApproval(file: File): boolean {
|
canBeApproved(file: File | File[]): boolean {
|
||||||
return this.canSetUnderReview(file);
|
const files = file instanceof File ? [file] : file;
|
||||||
|
return files.reduce((acc, _file) => this._canBeApproved(_file) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canSetUnderApproval(file: File): boolean {
|
isReadyForApproval(files: File | File[]): boolean {
|
||||||
return file.isUnderReview && this.isReviewerOrApprover(file);
|
return this.canSetUnderReview(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
canSetUnderApproval(file: File | File[]): boolean {
|
||||||
|
const files = file instanceof File ? [file] : file;
|
||||||
|
return files.reduce((acc, _file) => this._canSetUnderApproval(_file) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
isOwner(dossier: Dossier, user = this._userService.currentUser): boolean {
|
isOwner(dossier: Dossier, user = this._userService.currentUser): boolean {
|
||||||
@ -97,21 +96,22 @@ export class PermissionsService {
|
|||||||
return (file?.isUnderReview || file?.isUnderApproval) && this.isFileAssignee(file);
|
return (file?.isUnderReview || file?.isUnderApproval) && this.isFileAssignee(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
canUndoApproval(file: File): boolean {
|
canUndoApproval(file: File | File[]): boolean {
|
||||||
return file.isApproved && this.isApprover(this._getDossier(file));
|
const files = file instanceof File ? [file] : file;
|
||||||
|
const dossier = this._getDossier(files[0]);
|
||||||
|
return files.reduce((acc, _file) => this._canUndoApproval(_file, dossier) && acc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canMarkPagesAsViewed(file: File): boolean {
|
canMarkPagesAsViewed(file: File): boolean {
|
||||||
return (file.isUnderReview || file.isUnderApproval) && this.isFileAssignee(file);
|
return (file.isUnderReview || file.isUnderApproval) && this.isFileAssignee(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
canDownloadFiles(file: File): boolean {
|
canDownloadFiles(files: File[]): boolean {
|
||||||
const dossier = this._getDossier(file);
|
if (files.length === 0) {
|
||||||
if (!dossier) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const dossier = this._getDossier(files[0]);
|
||||||
return file.isApproved && this.isApprover(dossier);
|
return this.isApprover(dossier) && files.reduce((prev, file) => prev && file.isApproved, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canDeleteDossier(dossier: Dossier): boolean {
|
canDeleteDossier(dossier: Dossier): boolean {
|
||||||
@ -136,6 +136,59 @@ export class PermissionsService {
|
|||||||
return (comment.user === this._userService.currentUser.id || this.isApprover(dossier)) && !file.isApproved;
|
return (comment.user === this._userService.currentUser.id || this.isApprover(dossier)) && !file.isApproved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://jira.iqser.com/browse/RED-2787
|
||||||
|
private _canDeleteFile(file: File, dossier: Dossier): boolean {
|
||||||
|
return (
|
||||||
|
file.isNew ||
|
||||||
|
(file.isUnderReview && !file.assignee && this.isDossierMember(dossier)) ||
|
||||||
|
(file.isUnderApproval && !file.assignee && this.isApprover(dossier)) ||
|
||||||
|
(file.assignee && !file.isApproved && (this.isFileAssignee(file) || this.isOwner(dossier)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canReanalyseFile(file: File): boolean {
|
||||||
|
return this.isReviewerOrApprover(file) || file.isNew || (file.isError && file.isNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canAssignToSelf(file: File, dossier: Dossier): boolean {
|
||||||
|
const precondition = this.isDossierMember(dossier) && !this.isFileAssignee(file) && !file.isError && !file.isProcessing;
|
||||||
|
return precondition && (file.isNew || file.isUnderReview || (file.isUnderApproval && this.isApprover(dossier)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canSetUnderApproval(file: File): boolean {
|
||||||
|
return file.isUnderReview && this.isReviewerOrApprover(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canUndoApproval(file: File, dossier: Dossier): boolean {
|
||||||
|
return file.isApproved && this.isApprover(dossier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canBeApproved(file: File): boolean {
|
||||||
|
return file.canBeApproved;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canAssignUser(file: File, dossier: Dossier) {
|
||||||
|
const precondition = !file.isProcessing && !file.isError && !file.isApproved && this.isApprover(dossier);
|
||||||
|
|
||||||
|
if (precondition) {
|
||||||
|
if ((file.isNew || file.isUnderReview) && dossier.hasReviewers) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (file.isUnderApproval && dossier.approverIds.length > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canUnassignUser(file: File, dossier: Dossier) {
|
||||||
|
return (file.isUnderReview || file.isUnderApproval) && (this.isFileAssignee(file) || this.isApprover(dossier));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _canSetUnderReview(file: File): boolean {
|
||||||
|
return file.isUnderApproval;
|
||||||
|
}
|
||||||
|
|
||||||
private _getDossier(file: File): Dossier {
|
private _getDossier(file: File): Dossier {
|
||||||
return this._dossiersService.find(file.dossierId);
|
return this._dossiersService.find(file.dossierId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,8 +45,8 @@ export class ReanalysisService extends GenericService<unknown> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Validate()
|
@Validate()
|
||||||
ocrFiles(@RequiredParam() body: List, @RequiredParam() dossierId: string) {
|
ocrFiles(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string) {
|
||||||
return this._post(body, `ocr/reanalyze/${dossierId}/bulk`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
|
return this._post(fileIds, `ocr/reanalyze/${dossierId}/bulk`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Validate()
|
@Validate()
|
||||||
|
|||||||
@ -728,7 +728,6 @@
|
|||||||
},
|
},
|
||||||
"ocr-file": "OCR Document",
|
"ocr-file": "OCR Document",
|
||||||
"ocr-performed": "OCR was performed for this file.",
|
"ocr-performed": "OCR was performed for this file.",
|
||||||
"open-document": "Open Document",
|
|
||||||
"quick-filters": {
|
"quick-filters": {
|
||||||
"assigned-to-me": "Assigned to me",
|
"assigned-to-me": "Assigned to me",
|
||||||
"assigned-to-others": "Assigned to others",
|
"assigned-to-others": "Assigned to others",
|
||||||
@ -1653,5 +1652,13 @@
|
|||||||
},
|
},
|
||||||
"title": "Watermark"
|
"title": "Watermark"
|
||||||
},
|
},
|
||||||
|
"workflow": {
|
||||||
|
"selection": {
|
||||||
|
"all": "All",
|
||||||
|
"count": "{count} selected",
|
||||||
|
"none": "None",
|
||||||
|
"select": "Select"
|
||||||
|
}
|
||||||
|
},
|
||||||
"yesterday": "Yesterday"
|
"yesterday": "Yesterday"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit fb53b06cfc8fa2c846e7d1a173ad6f0e19f90d02
|
Subproject commit 28a6b735f700351f36c2583de4b581fe5fb8106b
|
||||||
@ -45,7 +45,6 @@ export class File extends Entity<IFile> implements IFile {
|
|||||||
readonly hintsOnly: boolean;
|
readonly hintsOnly: boolean;
|
||||||
readonly hasNone: boolean;
|
readonly hasNone: boolean;
|
||||||
readonly isNew: boolean;
|
readonly isNew: boolean;
|
||||||
// readonly isUnassigned: boolean;
|
|
||||||
readonly isError: boolean;
|
readonly isError: boolean;
|
||||||
readonly isProcessing: boolean;
|
readonly isProcessing: boolean;
|
||||||
readonly isApproved: boolean;
|
readonly isApproved: boolean;
|
||||||
|
|||||||
27
libs/red-domain/src/lib/shared/expandable-file-actions.ts
Normal file
27
libs/red-domain/src/lib/shared/expandable-file-actions.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { CircleButtonType } from '@iqser/common-ui';
|
||||||
|
import { File } from '@red/domain';
|
||||||
|
|
||||||
|
export type ActionType = 'circleBtn' | 'downloadBtn' | 'toggle';
|
||||||
|
|
||||||
|
export const ActionTypes = {
|
||||||
|
circleBtn: 'circleBtn' as ActionType,
|
||||||
|
downloadBtn: 'downloadBtn' as ActionType,
|
||||||
|
toggle: 'toggle' as ActionType,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Action {
|
||||||
|
action?: (...args: any[]) => void;
|
||||||
|
tooltip?: string;
|
||||||
|
icon?: string;
|
||||||
|
show?: boolean;
|
||||||
|
ariaExpanded?: Observable<boolean>;
|
||||||
|
showDot?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
buttonType?: CircleButtonType;
|
||||||
|
tooltipClass?: string;
|
||||||
|
checked?: boolean;
|
||||||
|
class?: { [key: string]: boolean };
|
||||||
|
files?: File[];
|
||||||
|
type: ActionType;
|
||||||
|
}
|
||||||
@ -5,3 +5,4 @@ export * from './watermark';
|
|||||||
export * from './default-color-type';
|
export * from './default-color-type';
|
||||||
export * from './colors';
|
export * from './colors';
|
||||||
export * from './view-mode';
|
export * from './view-mode';
|
||||||
|
export * from './expandable-file-actions';
|
||||||
|
|||||||
@ -69,7 +69,6 @@
|
|||||||
"@angular/cli": "13.0.3",
|
"@angular/cli": "13.0.3",
|
||||||
"@angular/compiler-cli": "13.0.2",
|
"@angular/compiler-cli": "13.0.2",
|
||||||
"@angular/language-service": "13.0.2",
|
"@angular/language-service": "13.0.2",
|
||||||
"@biesbjerg/ngx-translate-extract": "^7.0.4",
|
|
||||||
"@nrwl/cli": "13.2.3",
|
"@nrwl/cli": "13.2.3",
|
||||||
"@nrwl/cypress": "13.2.3",
|
"@nrwl/cypress": "13.2.3",
|
||||||
"@nrwl/eslint-plugin-nx": "13.2.3",
|
"@nrwl/eslint-plugin-nx": "13.2.3",
|
||||||
@ -84,6 +83,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "4.33.0",
|
"@typescript-eslint/eslint-plugin": "4.33.0",
|
||||||
"@typescript-eslint/parser": "4.33.0",
|
"@typescript-eslint/parser": "4.33.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
|
"@bartholomej/ngx-translate-extract": "^8.0.1",
|
||||||
"cypress": "^6.9.1",
|
"cypress": "^6.9.1",
|
||||||
"cypress-file-upload": "^5.0.8",
|
"cypress-file-upload": "^5.0.8",
|
||||||
"cypress-keycloak": "^1.7.0",
|
"cypress-keycloak": "^1.7.0",
|
||||||
|
|||||||
154
yarn.lock
154
yarn.lock
@ -225,7 +225,7 @@
|
|||||||
tslib "^2.3.0"
|
tslib "^2.3.0"
|
||||||
yargs "^17.2.1"
|
yargs "^17.2.1"
|
||||||
|
|
||||||
"@angular/compiler@13.0.2":
|
"@angular/compiler@13.0.2", "@angular/compiler@^13.0.2":
|
||||||
version "13.0.2"
|
version "13.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-13.0.2.tgz#5bc1bfc1931f1ff2813f8fff8b8ceaa57b47d717"
|
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-13.0.2.tgz#5bc1bfc1931f1ff2813f8fff8b8ceaa57b47d717"
|
||||||
integrity sha512-EvIFT8y5VNICrnPgiamv/z9hfQ7KjLCM52g4ssXGCeGPVj58OEfslEc3jO4BCJG7xuLm7dCuSRV0pBlJNTSYFg==
|
integrity sha512-EvIFT8y5VNICrnPgiamv/z9hfQ7KjLCM52g4ssXGCeGPVj58OEfslEc3jO4BCJG7xuLm7dCuSRV0pBlJNTSYFg==
|
||||||
@ -1283,6 +1283,23 @@
|
|||||||
"@babel/helper-validator-identifier" "^7.15.7"
|
"@babel/helper-validator-identifier" "^7.15.7"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@bartholomej/ngx-translate-extract@^8.0.1":
|
||||||
|
version "8.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@bartholomej/ngx-translate-extract/-/ngx-translate-extract-8.0.1.tgz#4d9cc6ffbc2ce7f34d88cd15b28da2f382f58f43"
|
||||||
|
integrity sha512-mf/G8Xjz865xjLekZ6U0q5g6BO9ZF++tn3bDs1bPPOAKCw2BjBmwbMOftfGOdHYrVcSTa0yl2sqafZqYa4+waA==
|
||||||
|
dependencies:
|
||||||
|
"@angular/compiler" "^13.0.2"
|
||||||
|
"@phenomnomnominal/tsquery" "^4.1.1"
|
||||||
|
boxen "^6.2.1"
|
||||||
|
colorette "^2.0.16"
|
||||||
|
flat "^5.0.2"
|
||||||
|
gettext-parser "^4.2.0"
|
||||||
|
glob "^7.2.0"
|
||||||
|
mkdirp "^1.0.4"
|
||||||
|
path "^0.12.7"
|
||||||
|
terminal-link "^3.0.0"
|
||||||
|
yargs "^17.2.1"
|
||||||
|
|
||||||
"@bcoe/v8-coverage@^0.2.3":
|
"@bcoe/v8-coverage@^0.2.3":
|
||||||
version "0.2.3"
|
version "0.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
@ -1295,22 +1312,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.9.0"
|
tslib "^1.9.0"
|
||||||
|
|
||||||
"@biesbjerg/ngx-translate-extract@^7.0.4":
|
|
||||||
version "7.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@biesbjerg/ngx-translate-extract/-/ngx-translate-extract-7.0.4.tgz#98190aa798dfe78a9f33904256891e76634fd52c"
|
|
||||||
integrity sha512-33hR94Fu26LK7Z+ImW2IdZiHfOcAzyIs1CdkUXg/536z2MqxBYqPoI9Ghsk6RTEfnsGa65wMgOcDXn7Ilhp8ew==
|
|
||||||
dependencies:
|
|
||||||
"@phenomnomnominal/tsquery" "^4.1.1"
|
|
||||||
boxen "^5.0.1"
|
|
||||||
colorette "^1.2.2"
|
|
||||||
flat "^5.0.2"
|
|
||||||
gettext-parser "^4.0.4"
|
|
||||||
glob "^7.1.6"
|
|
||||||
mkdirp "^1.0.4"
|
|
||||||
path "^0.12.7"
|
|
||||||
terminal-link "^2.1.1"
|
|
||||||
yargs "^16.2.0"
|
|
||||||
|
|
||||||
"@cspotcode/source-map-consumer@0.8.0":
|
"@cspotcode/source-map-consumer@0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
|
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
|
||||||
@ -2792,7 +2793,7 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.8.0:
|
|||||||
require-from-string "^2.0.2"
|
require-from-string "^2.0.2"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ansi-align@^3.0.0:
|
ansi-align@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
||||||
integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==
|
integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==
|
||||||
@ -2816,6 +2817,13 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.21.3"
|
type-fest "^0.21.3"
|
||||||
|
|
||||||
|
ansi-escapes@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6"
|
||||||
|
integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==
|
||||||
|
dependencies:
|
||||||
|
type-fest "^1.0.2"
|
||||||
|
|
||||||
ansi-gray@^0.1.1:
|
ansi-gray@^0.1.1:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251"
|
resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251"
|
||||||
@ -2872,6 +2880,11 @@ ansi-styles@^5.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
||||||
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
||||||
|
|
||||||
|
ansi-styles@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3"
|
||||||
|
integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==
|
||||||
|
|
||||||
ansi-wrap@0.1.0:
|
ansi-wrap@0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
|
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
|
||||||
@ -3377,19 +3390,19 @@ boolbase@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||||
|
|
||||||
boxen@^5.0.1:
|
boxen@^6.2.1:
|
||||||
version "5.1.2"
|
version "6.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
|
resolved "https://registry.yarnpkg.com/boxen/-/boxen-6.2.1.tgz#b098a2278b2cd2845deef2dff2efc38d329b434d"
|
||||||
integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==
|
integrity sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-align "^3.0.0"
|
ansi-align "^3.0.1"
|
||||||
camelcase "^6.2.0"
|
camelcase "^6.2.0"
|
||||||
chalk "^4.1.0"
|
chalk "^4.1.2"
|
||||||
cli-boxes "^2.2.1"
|
cli-boxes "^3.0.0"
|
||||||
string-width "^4.2.2"
|
string-width "^5.0.1"
|
||||||
type-fest "^0.20.2"
|
type-fest "^2.5.0"
|
||||||
widest-line "^3.1.0"
|
widest-line "^4.0.1"
|
||||||
wrap-ansi "^7.0.0"
|
wrap-ansi "^8.0.1"
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
@ -3747,7 +3760,7 @@ chalk@^3.0.0:
|
|||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
|
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||||
@ -3862,10 +3875,10 @@ clean-stack@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
|
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
|
||||||
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
|
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
|
||||||
|
|
||||||
cli-boxes@^2.2.1:
|
cli-boxes@^3.0.0:
|
||||||
version "2.2.1"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
|
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145"
|
||||||
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
|
integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==
|
||||||
|
|
||||||
cli-cursor@^1.0.2:
|
cli-cursor@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -4008,11 +4021,6 @@ color-support@^1.1.2, color-support@^1.1.3:
|
|||||||
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
|
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
|
||||||
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
|
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
|
||||||
|
|
||||||
colorette@^1.2.2:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
|
|
||||||
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
|
|
||||||
|
|
||||||
colorette@^2.0.10, colorette@^2.0.16:
|
colorette@^2.0.10, colorette@^2.0.16:
|
||||||
version "2.0.16"
|
version "2.0.16"
|
||||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da"
|
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da"
|
||||||
@ -5173,6 +5181,11 @@ emoji-regex@^8.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||||
|
|
||||||
|
emoji-regex@^9.2.2:
|
||||||
|
version "9.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
|
||||||
|
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
|
||||||
|
|
||||||
emojis-list@^3.0.0:
|
emojis-list@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||||
@ -6628,7 +6641,7 @@ getpass@^0.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
assert-plus "^1.0.0"
|
assert-plus "^1.0.0"
|
||||||
|
|
||||||
gettext-parser@^4.0.4:
|
gettext-parser@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-4.2.0.tgz#9327140f76b122d44f0e8cb9338fd855667d9434"
|
resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-4.2.0.tgz#9327140f76b122d44f0e8cb9338fd855667d9434"
|
||||||
integrity sha512-aMgPyjC9W5Mz9tbFU8DcQ7GYMXoFWq633kaWGt4imlcpBWzDIWk7HY7nCSZTCJxyjRaLq9L/NEjMKkZ9gR630Q==
|
integrity sha512-aMgPyjC9W5Mz9tbFU8DcQ7GYMXoFWq633kaWGt4imlcpBWzDIWk7HY7nCSZTCJxyjRaLq9L/NEjMKkZ9gR630Q==
|
||||||
@ -6677,7 +6690,7 @@ glob@7.1.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@7.2.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
glob@7.2.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||||
@ -7465,6 +7478,11 @@ is-fullwidth-code-point@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
|
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
|
||||||
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
|
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
|
||||||
|
|
||||||
|
is-fullwidth-code-point@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88"
|
||||||
|
integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
|
||||||
|
|
||||||
is-generator-fn@^2.0.0:
|
is-generator-fn@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
||||||
@ -12128,7 +12146,7 @@ string-width@^1.0.1:
|
|||||||
is-fullwidth-code-point "^1.0.0"
|
is-fullwidth-code-point "^1.0.0"
|
||||||
strip-ansi "^3.0.0"
|
strip-ansi "^3.0.0"
|
||||||
|
|
||||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@ -12145,6 +12163,15 @@ string-width@^2.1.1:
|
|||||||
is-fullwidth-code-point "^2.0.0"
|
is-fullwidth-code-point "^2.0.0"
|
||||||
strip-ansi "^4.0.0"
|
strip-ansi "^4.0.0"
|
||||||
|
|
||||||
|
string-width@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.0.1.tgz#0d8158335a6cfd8eb95da9b6b262ce314a036ffd"
|
||||||
|
integrity sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==
|
||||||
|
dependencies:
|
||||||
|
emoji-regex "^9.2.2"
|
||||||
|
is-fullwidth-code-point "^4.0.0"
|
||||||
|
strip-ansi "^7.0.1"
|
||||||
|
|
||||||
string.prototype.padend@^3.0.0:
|
string.prototype.padend@^3.0.0:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1"
|
resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1"
|
||||||
@ -12212,7 +12239,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^5.0.1"
|
ansi-regex "^5.0.1"
|
||||||
|
|
||||||
strip-ansi@^7.0.0:
|
strip-ansi@^7.0.0, strip-ansi@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
|
||||||
integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
|
integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
|
||||||
@ -12324,7 +12351,7 @@ supports-color@^8.0.0, supports-color@^8.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
supports-hyperlinks@^2.0.0:
|
supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
|
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
|
||||||
integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==
|
integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==
|
||||||
@ -12393,7 +12420,7 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.2:
|
|||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
terminal-link@^2.0.0, terminal-link@^2.1.1:
|
terminal-link@^2.0.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
|
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
|
||||||
integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==
|
integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==
|
||||||
@ -12401,6 +12428,14 @@ terminal-link@^2.0.0, terminal-link@^2.1.1:
|
|||||||
ansi-escapes "^4.2.1"
|
ansi-escapes "^4.2.1"
|
||||||
supports-hyperlinks "^2.0.0"
|
supports-hyperlinks "^2.0.0"
|
||||||
|
|
||||||
|
terminal-link@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-3.0.0.tgz#91c82a66b52fc1684123297ce384429faf72ac5c"
|
||||||
|
integrity sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==
|
||||||
|
dependencies:
|
||||||
|
ansi-escapes "^5.0.0"
|
||||||
|
supports-hyperlinks "^2.2.0"
|
||||||
|
|
||||||
terser-webpack-plugin@^1.4.3:
|
terser-webpack-plugin@^1.4.3:
|
||||||
version "1.4.5"
|
version "1.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
|
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
|
||||||
@ -12761,6 +12796,16 @@ type-fest@^0.21.3:
|
|||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
||||||
|
|
||||||
|
type-fest@^1.0.2:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
|
||||||
|
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
|
||||||
|
|
||||||
|
type-fest@^2.5.0:
|
||||||
|
version "2.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.8.0.tgz#39d7c9f9c508df8d6ce1cf5a966b0e6568dcc50d"
|
||||||
|
integrity sha512-O+V9pAshf9C6loGaH0idwsmugI2LxVNR7DtS40gVo2EXZVYFgz9OuNtOhgHLdHdapOEWNdvz9Ob/eeuaWwwlxA==
|
||||||
|
|
||||||
type-is@~1.6.17, type-is@~1.6.18:
|
type-is@~1.6.17, type-is@~1.6.18:
|
||||||
version "1.6.18"
|
version "1.6.18"
|
||||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||||
@ -13321,12 +13366,12 @@ wide-align@^1.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
string-width "^1.0.2 || 2 || 3 || 4"
|
string-width "^1.0.2 || 2 || 3 || 4"
|
||||||
|
|
||||||
widest-line@^3.1.0:
|
widest-line@^4.0.1:
|
||||||
version "3.1.0"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
|
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2"
|
||||||
integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==
|
integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width "^4.0.0"
|
string-width "^5.0.1"
|
||||||
|
|
||||||
wildcard@^2.0.0:
|
wildcard@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@ -13371,6 +13416,15 @@ wrap-ansi@^7.0.0:
|
|||||||
string-width "^4.1.0"
|
string-width "^4.1.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
|
wrap-ansi@^8.0.1:
|
||||||
|
version "8.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.0.1.tgz#2101e861777fec527d0ea90c57c6b03aac56a5b3"
|
||||||
|
integrity sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^6.1.0"
|
||||||
|
string-width "^5.0.1"
|
||||||
|
strip-ansi "^7.0.1"
|
||||||
|
|
||||||
wrappy@1:
|
wrappy@1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user