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() {
|
||||
return this.data.files.reduce((prev, file) => prev && this.permissionsService.canUnassignUser(file), true);
|
||||
return this.permissionsService.canUnassignUser(this.data.files);
|
||||
}
|
||||
|
||||
/** Initialize the form with:
|
||||
|
||||
@ -1,78 +1,4 @@
|
||||
<ng-container (longPress)="forceReanalysisAction($event)" *ngIf="selectedFiles.length" redactionLongPress>
|
||||
<iqser-circle-button
|
||||
(action)="delete()"
|
||||
*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>
|
||||
<redaction-expandable-file-actions [actions]="buttons" [buttonType]="buttonType" [maxWidth]="maxWidth" [tooltipPosition]="'above'">
|
||||
</redaction-expandable-file-actions>
|
||||
</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 { Dossier, File } from '@red/domain';
|
||||
import { FileAssignService } from '../../../../shared/services/file-assign.service';
|
||||
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
|
||||
import { CircleButtonTypes, ConfirmationDialogInput, LoadingService, Required } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Action, ActionTypes, Dossier, File } from '@red/domain';
|
||||
import { CircleButtonType, CircleButtonTypes, Required } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { BulkActionsService } from '../../services/bulk-actions.service';
|
||||
|
||||
@Component({
|
||||
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'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DossierOverviewBulkActionsComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
export class DossierOverviewBulkActionsComponent implements OnChanges {
|
||||
@Input() @Required() dossier: Dossier;
|
||||
@Input() @Required() selectedFiles: File[];
|
||||
@Input() buttonType: CircleButtonType = CircleButtonTypes.dark;
|
||||
@Input() maxWidth: number;
|
||||
|
||||
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(
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _bulkActionsService: BulkActionsService,
|
||||
) {}
|
||||
|
||||
get allSelectedFilesCanBeAssignedIntoSameState() {
|
||||
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
|
||||
(acc, file) => acc && (file.isUnderReview || file.isNew),
|
||||
true,
|
||||
);
|
||||
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
||||
return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
|
||||
private get _buttons(): Action[] {
|
||||
return [
|
||||
{
|
||||
type: ActionTypes.circleBtn,
|
||||
action: () => this._bulkActionsService.delete(this.selectedFiles),
|
||||
tooltip: _('dossier-overview.bulk.delete'),
|
||||
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() {
|
||||
return (
|
||||
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');
|
||||
ngOnChanges() {
|
||||
this._setup();
|
||||
}
|
||||
|
||||
forceReanalysisAction($event: LongPressEvent) {
|
||||
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
||||
this._setup();
|
||||
}
|
||||
|
||||
delete() {
|
||||
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(
|
||||
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();
|
||||
},
|
||||
private _setup() {
|
||||
if (this.selectedFiles.length) {
|
||||
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
|
||||
(acc, file) => acc && (file.isUnderReview || file.isNew),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
this._loadingService.start();
|
||||
await this._filesService
|
||||
.setApprovedFor(
|
||||
this.selectedFiles.map(f => f.id),
|
||||
this.dossier.id,
|
||||
)
|
||||
.toPromise();
|
||||
this._loadingService.stop();
|
||||
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
||||
this._canMoveToSameState = allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
|
||||
|
||||
this.canAssign =
|
||||
this._canMoveToSameState &&
|
||||
this.selectedFiles.reduce(
|
||||
(acc, file) => (acc && this._permissionsService.canAssignUser(file)) || this._permissionsService.canUnassignUser(file),
|
||||
true,
|
||||
);
|
||||
this.canAssignToSelf = this._canMoveToSameState && this._permissionsService.canAssignToSelf(this.selectedFiles);
|
||||
|
||||
this.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';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-workload-column',
|
||||
templateUrl: './file-workload-column.component.html',
|
||||
styleUrls: ['./file-workload-column.component.scss'],
|
||||
selector: 'redaction-file-workload',
|
||||
templateUrl: './file-workload.component.html',
|
||||
styleUrls: ['./file-workload.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileWorkloadColumnComponent {
|
||||
export class FileWorkloadComponent {
|
||||
@Input() file: File;
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService, private readonly _dossiersService: DossiersService) {}
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<ng-container *ngIf="!file.isError">
|
||||
<div class="cell">
|
||||
<redaction-file-workload-column [file]="file"></redaction-file-workload-column>
|
||||
<redaction-file-workload [file]="file"></redaction-file-workload>
|
||||
</div>
|
||||
|
||||
<div class="user-column cell">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="all-caps-label" translate="view-mode.view-as"></div>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="configService.listingMode = listingModes.table"
|
||||
(action)="setListingMode(listingModes.table)"
|
||||
[attr.aria-expanded]="mode === listingModes.table"
|
||||
[tooltip]="'view-mode.list' | translate"
|
||||
[greySelected]="true"
|
||||
@ -10,7 +10,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="configService.listingMode = listingModes.workflow"
|
||||
(action)="setListingMode(listingModes.workflow)"
|
||||
[attr.aria-expanded]="mode === listingModes.workflow"
|
||||
[tooltip]="'view-mode.workflow' | translate"
|
||||
[greySelected]="true"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
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({
|
||||
selector: 'redaction-view-mode-selection',
|
||||
@ -12,5 +13,10 @@ export class ViewModeSelectionComponent {
|
||||
readonly listingModes = ListingModes;
|
||||
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>
|
||||
<div class="details-wrapper">
|
||||
<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 }}
|
||||
</div>
|
||||
|
||||
@ -13,5 +13,29 @@
|
||||
</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>
|
||||
|
||||
@ -1,34 +1,60 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
.workflow-item {
|
||||
padding: 10px;
|
||||
padding: 10px 10px 8px 10px;
|
||||
|
||||
> div {
|
||||
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-actions {
|
||||
margin-top: 10px;
|
||||
display: none;
|
||||
&:hover .filename {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&: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 { File } from '@red/domain';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { File, IFileAttributeConfig } from '@red/domain';
|
||||
import { Debounce, Required } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-workflow-item',
|
||||
@ -7,6 +8,25 @@ import { File } from '@red/domain';
|
||||
styleUrls: ['./workflow-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class WorkflowItemComponent {
|
||||
@Input() file: File;
|
||||
export class WorkflowItemComponent implements OnInit {
|
||||
@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,
|
||||
ListingMode,
|
||||
ListingModes,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
TableColumnConfig,
|
||||
WorkflowConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { FileAssignService } from '../../shared/services/file-assign.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -24,9 +22,9 @@ import { annotationFilterChecker, RedactionFilterSorter } from '@utils/index';
|
||||
import { workloadTranslations } from '../../translations/workload-translations';
|
||||
import * as moment from 'moment';
|
||||
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 noop from 'lodash/noop';
|
||||
import { BulkActionsService } from './services/bulk-actions.service';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
@ -34,15 +32,12 @@ export class ConfigService {
|
||||
private readonly _listingMode$ = new BehaviorSubject<ListingMode>(ListingModes.table);
|
||||
|
||||
constructor(
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _appConfigService: AppConfigService,
|
||||
private readonly _bulkActionsService: BulkActionsService,
|
||||
) {
|
||||
this.listingMode$ = this._listingMode$.asObservable();
|
||||
}
|
||||
@ -63,34 +58,45 @@ export class ConfigService {
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.NEW],
|
||||
key: WorkflowFileStatuses.NEW,
|
||||
enterFn: this._unassignFn,
|
||||
enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file),
|
||||
enterFn: noop,
|
||||
enterPredicate: () => false,
|
||||
color: '#D3D5DA',
|
||||
entities: new BehaviorSubject([]),
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
|
||||
enterFn: this._underReviewFn,
|
||||
enterPredicate: (file: File) =>
|
||||
this._permissionsService.canSetUnderReview(file) ||
|
||||
this._permissionsService.canAssignToSelf(file) ||
|
||||
this._permissionsService.canAssignUser(file),
|
||||
enterFn: async (files: File[]) => {
|
||||
if (files[0].workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL) {
|
||||
await this._bulkActionsService.backToUnderReview(files);
|
||||
} else {
|
||||
await this._bulkActionsService.assignToMe(files);
|
||||
}
|
||||
},
|
||||
enterPredicate: (files: File[]) =>
|
||||
this._permissionsService.canSetUnderReview(files) ||
|
||||
this._permissionsService.canAssignToSelf(files) ||
|
||||
this._permissionsService.canAssignUser(files),
|
||||
key: WorkflowFileStatuses.UNDER_REVIEW,
|
||||
color: '#FDBD00',
|
||||
entities: new BehaviorSubject([]),
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
|
||||
enterFn: this._underApprovalFn,
|
||||
enterPredicate: (file: File) =>
|
||||
this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file),
|
||||
enterFn: (files: File[]) => this._bulkActionsService.setToUnderApproval(files),
|
||||
enterPredicate: (files: File[]) =>
|
||||
this._permissionsService.canSetUnderApproval(files) || this._permissionsService.canUndoApproval(files),
|
||||
key: WorkflowFileStatuses.UNDER_APPROVAL,
|
||||
color: '#374C81',
|
||||
entities: new BehaviorSubject([]),
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
|
||||
enterFn: this._approveFn,
|
||||
enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved,
|
||||
enterFn: (files: File[]) => this._bulkActionsService.approve(files),
|
||||
enterPredicate: (files: File[]) =>
|
||||
this._permissionsService.isReadyForApproval(files) && this._permissionsService.canBeApproved(files),
|
||||
key: WorkflowFileStatuses.APPROVED,
|
||||
color: '#48C9F7',
|
||||
entities: new BehaviorSubject([]),
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -323,7 +329,7 @@ export class ConfigService {
|
||||
|
||||
_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[] {
|
||||
const recentPeriod = this._appConfigService.values.RECENT_PERIOD_IN_HOURS;
|
||||
@ -361,31 +367,4 @@ export class ConfigService {
|
||||
private _openEditDossierDialog($event: MouseEvent, dossierId: string) {
|
||||
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 { ConfigService } from './config.service';
|
||||
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 { WorkflowItemComponent } from './components/workflow-item/workflow-item.component';
|
||||
import { ScreenHeaderComponent } from './components/screen-header/screen-header.component';
|
||||
import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component';
|
||||
import { FileNameColumnComponent } from './components/table-item/file-name-column/file-name-column.component';
|
||||
import { AddedColumnComponent } from './components/table-item/added-column/added-column.component';
|
||||
import { BulkActionsService } from './services/bulk-actions.service';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -36,7 +37,7 @@ const routes: Routes = [
|
||||
DossierOverviewBulkActionsComponent,
|
||||
DossierDetailsComponent,
|
||||
DossierDetailsStatsComponent,
|
||||
FileWorkloadColumnComponent,
|
||||
FileWorkloadComponent,
|
||||
TableItemComponent,
|
||||
FileStatsComponent,
|
||||
WorkflowItemComponent,
|
||||
@ -45,7 +46,7 @@ const routes: Routes = [
|
||||
FileNameColumnComponent,
|
||||
AddedColumnComponent,
|
||||
],
|
||||
providers: [ConfigService],
|
||||
providers: [ConfigService, BulkActionsService],
|
||||
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, SharedDossiersModule, IqserIconsModule, TranslateModule],
|
||||
})
|
||||
export class DossierOverviewModule {}
|
||||
|
||||
@ -32,16 +32,16 @@
|
||||
(noDataAction)="fileInput.click()"
|
||||
*ngIf="mode === listingModes.workflow"
|
||||
[addElementIcon]="'iqser:upload'"
|
||||
[bulkActions]="bulkActions"
|
||||
[config]="workflowConfig"
|
||||
[itemClasses]="{ disabled: disabledFn }"
|
||||
[itemHeight]="'56px'"
|
||||
[itemTemplate]="workflowItemTemplate"
|
||||
[noDataButtonIcon]="'iqser:upload'"
|
||||
[noDataButtonLabel]="'dossier-overview.no-data.action' | translate"
|
||||
[noDataIcon]="'iqser:document'"
|
||||
[noDataText]="'dossier-overview.no-data.title' | translate"
|
||||
[showNoDataButton]="true"
|
||||
addElementColumn="UNASSIGNED"
|
||||
addElementColumn="NEW"
|
||||
></iqser-workflow>
|
||||
</div>
|
||||
|
||||
@ -54,10 +54,12 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ng-template #bulkActions>
|
||||
<ng-template #bulkActions let-maxWidth="maxWidth">
|
||||
<redaction-dossier-overview-bulk-actions
|
||||
[buttonType]="(configService.listingMode$ | async) === listingModes.table ? circleButtonTypes.dark : circleButtonTypes.primary"
|
||||
[dossier]="dossier"
|
||||
[selectedFiles]="this.listingService.selected"
|
||||
[maxWidth]="maxWidth"
|
||||
[selectedFiles]="listingService.selectedEntities$ | async"
|
||||
></redaction-dossier-overview-bulk-actions>
|
||||
</ng-template>
|
||||
|
||||
@ -77,5 +79,5 @@
|
||||
<input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" />
|
||||
|
||||
<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>
|
||||
|
||||
@ -123,7 +123,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
}
|
||||
|
||||
disabledFn = (file: File) => file.excluded;
|
||||
|
||||
lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;
|
||||
|
||||
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 {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
@ -48,13 +48,13 @@ export class UserManagementComponent implements OnChanges {
|
||||
|
||||
private get _assignOrChangeReviewerTooltip(): string {
|
||||
return this.file.assignee
|
||||
? this.translateService.instant('file-preview.change-reviewer')
|
||||
: this.translateService.instant('file-preview.assign-reviewer');
|
||||
? this.translateService.instant(_('file-preview.change-reviewer'))
|
||||
: this.translateService.instant(_('file-preview.assign-reviewer'));
|
||||
}
|
||||
|
||||
private get _assignTooltip(): string {
|
||||
return this.file.isUnderApproval
|
||||
? this.translateService.instant('dossier-overview.assign-approver')
|
||||
? this.translateService.instant(_('dossier-overview.assign-approver'))
|
||||
: this._assignOrChangeReviewerTooltip;
|
||||
}
|
||||
|
||||
@ -68,9 +68,9 @@ export class UserManagementComponent implements OnChanges {
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.file, this.dossier);
|
||||
this.canAssignUser = this.permissionsService.canAssignUser(this.file, this.dossier);
|
||||
this.canUnassignUser = this.permissionsService.canUnassignUser(this.file, this.dossier);
|
||||
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.file);
|
||||
this.canAssignUser = this.permissionsService.canAssignUser(this.file);
|
||||
this.canUnassignUser = this.permissionsService.canUnassignUser(this.file);
|
||||
this.canAssignOrUnassign = this.canAssignUser || this.canUnassignUser;
|
||||
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 { TranslateService } from '@ngx-translate/core';
|
||||
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 { DossiersService } from '@services/entity-services/dossiers.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 { MultiSelectService } from './services/multi-select.service';
|
||||
import { DocumentInfoService } from './services/document-info.service';
|
||||
import { ReanalysisService } from '../../../../services/reanalysis.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import PDFNet = Core.PDFNet;
|
||||
|
||||
@ -78,7 +78,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
hideSkipped = false;
|
||||
displayPdfViewer = false;
|
||||
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
|
||||
@ViewChild('fileActions') fileActions: FileActionsComponent;
|
||||
readonly dossierId: string;
|
||||
readonly canPerformAnnotationActions$: Observable<boolean>;
|
||||
readonly dossier$: Observable<Dossier>;
|
||||
@ -117,6 +116,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
readonly excludedPagesService: ExcludedPagesService,
|
||||
readonly viewModeService: ViewModeService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
@ -241,7 +241,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||
if (file?.analysisRequired) {
|
||||
await this.fileActions.reanalyseFile();
|
||||
await this._reanalysisService.reanalyzeFilesForDossier([this.fileId], this.dossierId, true).toPromise();
|
||||
}
|
||||
|
||||
this.displayPdfViewer = true;
|
||||
|
||||
@ -16,153 +16,12 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-template #actions (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
||||
<div *ngIf="file" class="file-actions" iqserHelpMode="document-features">
|
||||
<iqser-circle-button
|
||||
(action)="openDocument()"
|
||||
*ngIf="showOpenDocument"
|
||||
<div class="file-actions" iqserHelpMode="document-features">
|
||||
<redaction-expandable-file-actions
|
||||
[actions]="buttons"
|
||||
[buttonType]="buttonType"
|
||||
[maxWidth]="maxWidth"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.open-document' | translate"
|
||||
[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>
|
||||
></redaction-expandable-file-actions>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -3,9 +3,7 @@
|
||||
|
||||
.file-actions {
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
color: variables.$grey-1;
|
||||
@include common-mixins.no-scroll-bar;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 2px;
|
||||
@ -27,14 +25,6 @@ iqser-status-bar {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
mat-slide-toggle {
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
line-height: 33px;
|
||||
margin-left: 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.spinning-icon {
|
||||
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 { File } from '@red/domain';
|
||||
import { Action, ActionTypes, File } from '@red/domain';
|
||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
import {
|
||||
AutoUnsubscribe,
|
||||
@ -25,6 +37,7 @@ import { Router } from '@angular/router';
|
||||
import { ExcludedPagesService } from '../../../screens/file-preview-screen/services/excluded-pages.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { DocumentInfoService } from '../../../screens/file-preview-screen/services/document-info.service';
|
||||
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-actions',
|
||||
@ -36,8 +49,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@Input() file: File;
|
||||
@Input() @Required() file: File;
|
||||
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
||||
@Input() maxWidth: number;
|
||||
@Output() readonly ocredFile = new EventEmitter<void>();
|
||||
|
||||
toggleTooltip?: string;
|
||||
@ -56,16 +70,20 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
canToggleAnalysis: boolean;
|
||||
showStatusBar: boolean;
|
||||
showOpenDocument: boolean;
|
||||
showReanalyseFilePreview: boolean;
|
||||
showReanalyseDossierOverview: boolean;
|
||||
analysisForced: boolean;
|
||||
isDossierOverview = false;
|
||||
isDossierOverviewList = false;
|
||||
isDossierOverviewWorkflow = false;
|
||||
isFilePreview = false;
|
||||
tooltipPosition: IqserTooltipPosition;
|
||||
buttons: Action[];
|
||||
@ViewChild(ExpandableFileActionsComponent) _expandableActionsComponent: ExpandableFileActionsComponent;
|
||||
|
||||
constructor(
|
||||
@Optional() readonly excludedPagesService: ExcludedPagesService,
|
||||
@Optional() readonly documentInfoService: DocumentInfoService,
|
||||
@Optional() private readonly _excludedPagesService: ExcludedPagesService,
|
||||
@Optional() private readonly _documentInfoService: DocumentInfoService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
@ -82,6 +100,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
super();
|
||||
}
|
||||
|
||||
@HostBinding('class.keep-visible') get expanded() {
|
||||
return this._expandableActionsComponent?.expanded;
|
||||
}
|
||||
|
||||
private get _toggleTooltip(): string {
|
||||
if (!this.currentUser.isManager) {
|
||||
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');
|
||||
}
|
||||
|
||||
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() {
|
||||
this._dossiersService.getEntityChanged$(this.file.dossierId).pipe(tap(() => this._setup()));
|
||||
}
|
||||
@ -98,57 +230,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
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) {
|
||||
$event.stopPropagation();
|
||||
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();
|
||||
this._loadingService.start();
|
||||
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();
|
||||
}
|
||||
|
||||
async setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
||||
await this._fileAssignService.assignReviewer($event, this.file, ignoreDialogChanges);
|
||||
private async _setFileUnderReview($event: MouseEvent) {
|
||||
await this._fileAssignService.assignReviewer($event, this.file, true);
|
||||
}
|
||||
|
||||
async toggleAnalysis() {
|
||||
private async _toggleAnalysis() {
|
||||
this._loadingService.start();
|
||||
await this._reanalysisService.toggleAnalysis(this.file.dossierId, this.file.fileId, !this.file.excluded).toPromise();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
forceReanalysisAction($event: LongPressEvent) {
|
||||
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
||||
}
|
||||
|
||||
private _setup() {
|
||||
this.isDossierOverviewList = this.type === 'dossier-overview-list';
|
||||
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.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() {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
[disabled]="disabled || !canDownloadFiles"
|
||||
[tooltipClass]="tooltipClass"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="tooltip"
|
||||
[tooltip]="tooltip | translate: { count: this.files.length }"
|
||||
[type]="type"
|
||||
icon="iqser:download"
|
||||
></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 { File } from '@red/domain';
|
||||
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
||||
import { CircleButtonType, CircleButtonTypes, List, Toaster } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CircleButtonType, CircleButtonTypes, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
export type MenuState = 'OPEN' | 'CLOSED';
|
||||
@ -14,28 +13,25 @@ export type MenuState = 'OPEN' | 'CLOSED';
|
||||
styleUrls: ['./file-download-btn.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileDownloadBtnComponent {
|
||||
@Input() files: List<File>;
|
||||
export class FileDownloadBtnComponent implements OnChanges {
|
||||
@Input() files: File[];
|
||||
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
|
||||
@Input() type: CircleButtonType = CircleButtonTypes.default;
|
||||
@Input() tooltipClass: string;
|
||||
@Input() disabled = false;
|
||||
|
||||
tooltip: string;
|
||||
canDownloadFiles: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _translateService: TranslateService,
|
||||
) {}
|
||||
|
||||
get canDownloadFiles() {
|
||||
return this.files.length > 0 && this.files.reduce((acc, file) => acc && this._permissionsService.canDownloadFiles(file), true);
|
||||
}
|
||||
|
||||
get tooltip() {
|
||||
return this.canDownloadFiles
|
||||
? this._translateService.instant('dossier-overview.download-file')
|
||||
: this._translateService.instant('dossier-overview.download-file-disabled', { count: this.files.length });
|
||||
ngOnChanges(): void {
|
||||
this.canDownloadFiles = this._permissionsService.canDownloadFiles(this.files);
|
||||
this.tooltip = this.canDownloadFiles ? _('dossier-overview.download-file') : _('dossier-overview.download-file-disabled');
|
||||
}
|
||||
|
||||
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 { TeamMembersComponent } from './components/team-members/team-members.component';
|
||||
import { EditorComponent } from './components/editor/editor.component';
|
||||
import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component';
|
||||
|
||||
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
|
||||
|
||||
@ -39,6 +40,7 @@ const components = [
|
||||
AssignUserDropdownComponent,
|
||||
TypeFilterComponent,
|
||||
TeamMembersComponent,
|
||||
ExpandableFileActionsComponent,
|
||||
|
||||
...buttons,
|
||||
];
|
||||
|
||||
@ -22,58 +22,57 @@ export class PermissionsService {
|
||||
return this.isReviewerOrApprover(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
|
||||
}
|
||||
|
||||
canReanalyseFile(file: File): boolean {
|
||||
return this.isReviewerOrApprover(file) || file.isNew || (file.isError && file.isNew);
|
||||
canReanalyseFile(file: File | File[]): boolean {
|
||||
const files = file instanceof File ? [file] : file;
|
||||
return files.reduce((acc, _file) => this._canReanalyseFile(_file) && acc, true);
|
||||
}
|
||||
|
||||
isFileAssignee(file: File): boolean {
|
||||
return file.assignee === this._userService.currentUser.id;
|
||||
}
|
||||
|
||||
// https://jira.iqser.com/browse/RED-2787
|
||||
canDeleteFile(file: File): boolean {
|
||||
const dossier = this._getDossier(file);
|
||||
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)))
|
||||
);
|
||||
canDeleteFile(file: File | File[]): boolean {
|
||||
const files = file instanceof File ? [file] : file;
|
||||
const dossier = this._getDossier(files[0]);
|
||||
return files.reduce((acc, _file) => this._canDeleteFile(_file, dossier) && acc, true);
|
||||
}
|
||||
|
||||
canAssignToSelf(file: File, dossier = this._getDossier(file)): boolean {
|
||||
const precondition = this.isDossierMember(dossier) && !this.isFileAssignee(file) && !file.isError && !file.isProcessing;
|
||||
return precondition && (file.isNew || file.isUnderReview || (file.isUnderApproval && this.isApprover(dossier)));
|
||||
canAssignToSelf(file: File | File[]): boolean {
|
||||
const files = file instanceof File ? [file] : file;
|
||||
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 {
|
||||
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;
|
||||
canAssignUser(file: File | File[]): boolean {
|
||||
const files = file instanceof File ? [file] : file;
|
||||
const dossier = this._getDossier(files[0]);
|
||||
return files.reduce((acc, _file) => this._canAssignUser(_file, dossier) && acc, true);
|
||||
}
|
||||
|
||||
canUnassignUser(file: File, dossier = this._getDossier(file)): boolean {
|
||||
return (file.isUnderReview || file.isUnderApproval) && (this.isFileAssignee(file) || this.isApprover(dossier));
|
||||
canUnassignUser(file: File | File[]): boolean {
|
||||
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 {
|
||||
return file.isUnderApproval && this.isApprover(dossier);
|
||||
canSetUnderReview(file: File | File[]): boolean {
|
||||
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 {
|
||||
return this.canSetUnderReview(file);
|
||||
canBeApproved(file: File | File[]): boolean {
|
||||
const files = file instanceof File ? [file] : file;
|
||||
return files.reduce((acc, _file) => this._canBeApproved(_file) && acc, true);
|
||||
}
|
||||
|
||||
canSetUnderApproval(file: File): boolean {
|
||||
return file.isUnderReview && this.isReviewerOrApprover(file);
|
||||
isReadyForApproval(files: File | File[]): boolean {
|
||||
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 {
|
||||
@ -97,21 +96,22 @@ export class PermissionsService {
|
||||
return (file?.isUnderReview || file?.isUnderApproval) && this.isFileAssignee(file);
|
||||
}
|
||||
|
||||
canUndoApproval(file: File): boolean {
|
||||
return file.isApproved && this.isApprover(this._getDossier(file));
|
||||
canUndoApproval(file: File | File[]): boolean {
|
||||
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 {
|
||||
return (file.isUnderReview || file.isUnderApproval) && this.isFileAssignee(file);
|
||||
}
|
||||
|
||||
canDownloadFiles(file: File): boolean {
|
||||
const dossier = this._getDossier(file);
|
||||
if (!dossier) {
|
||||
canDownloadFiles(files: File[]): boolean {
|
||||
if (files.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file.isApproved && this.isApprover(dossier);
|
||||
const dossier = this._getDossier(files[0]);
|
||||
return this.isApprover(dossier) && files.reduce((prev, file) => prev && file.isApproved, true);
|
||||
}
|
||||
|
||||
canDeleteDossier(dossier: Dossier): boolean {
|
||||
@ -136,6 +136,59 @@ export class PermissionsService {
|
||||
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 {
|
||||
return this._dossiersService.find(file.dossierId);
|
||||
}
|
||||
|
||||
@ -45,8 +45,8 @@ export class ReanalysisService extends GenericService<unknown> {
|
||||
}
|
||||
|
||||
@Validate()
|
||||
ocrFiles(@RequiredParam() body: List, @RequiredParam() dossierId: string) {
|
||||
return this._post(body, `ocr/reanalyze/${dossierId}/bulk`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
|
||||
ocrFiles(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string) {
|
||||
return this._post(fileIds, `ocr/reanalyze/${dossierId}/bulk`).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
|
||||
}
|
||||
|
||||
@Validate()
|
||||
|
||||
@ -728,7 +728,6 @@
|
||||
},
|
||||
"ocr-file": "OCR Document",
|
||||
"ocr-performed": "OCR was performed for this file.",
|
||||
"open-document": "Open Document",
|
||||
"quick-filters": {
|
||||
"assigned-to-me": "Assigned to me",
|
||||
"assigned-to-others": "Assigned to others",
|
||||
@ -1653,5 +1652,13 @@
|
||||
},
|
||||
"title": "Watermark"
|
||||
},
|
||||
"workflow": {
|
||||
"selection": {
|
||||
"all": "All",
|
||||
"count": "{count} selected",
|
||||
"none": "None",
|
||||
"select": "Select"
|
||||
}
|
||||
},
|
||||
"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 hasNone: boolean;
|
||||
readonly isNew: boolean;
|
||||
// readonly isUnassigned: boolean;
|
||||
readonly isError: boolean;
|
||||
readonly isProcessing: 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 './colors';
|
||||
export * from './view-mode';
|
||||
export * from './expandable-file-actions';
|
||||
|
||||
@ -69,7 +69,6 @@
|
||||
"@angular/cli": "13.0.3",
|
||||
"@angular/compiler-cli": "13.0.2",
|
||||
"@angular/language-service": "13.0.2",
|
||||
"@biesbjerg/ngx-translate-extract": "^7.0.4",
|
||||
"@nrwl/cli": "13.2.3",
|
||||
"@nrwl/cypress": "13.2.3",
|
||||
"@nrwl/eslint-plugin-nx": "13.2.3",
|
||||
@ -84,6 +83,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "4.33.0",
|
||||
"@typescript-eslint/parser": "4.33.0",
|
||||
"axios": "^0.24.0",
|
||||
"@bartholomej/ngx-translate-extract": "^8.0.1",
|
||||
"cypress": "^6.9.1",
|
||||
"cypress-file-upload": "^5.0.8",
|
||||
"cypress-keycloak": "^1.7.0",
|
||||
|
||||
154
yarn.lock
154
yarn.lock
@ -225,7 +225,7 @@
|
||||
tslib "^2.3.0"
|
||||
yargs "^17.2.1"
|
||||
|
||||
"@angular/compiler@13.0.2":
|
||||
"@angular/compiler@13.0.2", "@angular/compiler@^13.0.2":
|
||||
version "13.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-13.0.2.tgz#5bc1bfc1931f1ff2813f8fff8b8ceaa57b47d717"
|
||||
integrity sha512-EvIFT8y5VNICrnPgiamv/z9hfQ7KjLCM52g4ssXGCeGPVj58OEfslEc3jO4BCJG7xuLm7dCuSRV0pBlJNTSYFg==
|
||||
@ -1283,6 +1283,23 @@
|
||||
"@babel/helper-validator-identifier" "^7.15.7"
|
||||
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":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
@ -1295,22 +1312,6 @@
|
||||
dependencies:
|
||||
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":
|
||||
version "0.8.0"
|
||||
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"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-align@^3.0.0:
|
||||
ansi-align@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
||||
integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==
|
||||
@ -2816,6 +2817,13 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "0.1.1"
|
||||
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"
|
||||
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:
|
||||
version "0.1.0"
|
||||
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"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
boxen@^5.0.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
|
||||
integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==
|
||||
boxen@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-6.2.1.tgz#b098a2278b2cd2845deef2dff2efc38d329b434d"
|
||||
integrity sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==
|
||||
dependencies:
|
||||
ansi-align "^3.0.0"
|
||||
ansi-align "^3.0.1"
|
||||
camelcase "^6.2.0"
|
||||
chalk "^4.1.0"
|
||||
cli-boxes "^2.2.1"
|
||||
string-width "^4.2.2"
|
||||
type-fest "^0.20.2"
|
||||
widest-line "^3.1.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
chalk "^4.1.2"
|
||||
cli-boxes "^3.0.0"
|
||||
string-width "^5.0.1"
|
||||
type-fest "^2.5.0"
|
||||
widest-line "^4.0.1"
|
||||
wrap-ansi "^8.0.1"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
@ -3747,7 +3760,7 @@ chalk@^3.0.0:
|
||||
ansi-styles "^4.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"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
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"
|
||||
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
|
||||
|
||||
cli-boxes@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
|
||||
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
|
||||
cli-boxes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145"
|
||||
integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==
|
||||
|
||||
cli-cursor@^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"
|
||||
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:
|
||||
version "2.0.16"
|
||||
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"
|
||||
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:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||
@ -6628,7 +6641,7 @@ getpass@^0.1.1:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
gettext-parser@^4.0.4:
|
||||
gettext-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-4.2.0.tgz#9327140f76b122d44f0e8cb9338fd855667d9434"
|
||||
integrity sha512-aMgPyjC9W5Mz9tbFU8DcQ7GYMXoFWq633kaWGt4imlcpBWzDIWk7HY7nCSZTCJxyjRaLq9L/NEjMKkZ9gR630Q==
|
||||
@ -6677,7 +6690,7 @@ glob@7.1.4:
|
||||
once "^1.3.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"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
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"
|
||||
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:
|
||||
version "2.1.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -12145,6 +12163,15 @@ string-width@^2.1.1:
|
||||
is-fullwidth-code-point "^2.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:
|
||||
version "3.1.3"
|
||||
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:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.0:
|
||||
strip-ansi@^7.0.0, strip-ansi@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
|
||||
integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
|
||||
@ -12324,7 +12351,7 @@ supports-color@^8.0.0, supports-color@^8.1.1:
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-hyperlinks@^2.0.0:
|
||||
supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
|
||||
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"
|
||||
yallist "^4.0.0"
|
||||
|
||||
terminal-link@^2.0.0, terminal-link@^2.1.1:
|
||||
terminal-link@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
|
||||
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"
|
||||
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:
|
||||
version "1.4.5"
|
||||
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"
|
||||
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:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
@ -13321,12 +13366,12 @@ wide-align@^1.1.2:
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2 || 3 || 4"
|
||||
|
||||
widest-line@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
|
||||
integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==
|
||||
widest-line@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2"
|
||||
integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==
|
||||
dependencies:
|
||||
string-width "^4.0.0"
|
||||
string-width "^5.0.1"
|
||||
|
||||
wildcard@^2.0.0:
|
||||
version "2.0.0"
|
||||
@ -13371,6 +13416,15 @@ wrap-ansi@^7.0.0:
|
||||
string-width "^4.1.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:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user