Update workflow UI
This commit is contained in:
parent
bf859b1f91
commit
9845267cc5
@ -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">
|
||||
|
||||
@ -13,5 +13,18 @@
|
||||
</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">
|
||||
<redaction-file-actions
|
||||
*ngIf="!file.isProcessing"
|
||||
[file]="file"
|
||||
[maxWidth]="width - 20"
|
||||
type="dossier-overview-workflow"
|
||||
></redaction-file-actions>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
.workflow-item {
|
||||
padding: 10px;
|
||||
padding: 10px 10px 8px 10px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
@ -23,12 +23,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
redaction-file-actions {
|
||||
redaction-file-workload {
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.file-actions {
|
||||
margin-top: 8px;
|
||||
min-height: 34px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
redaction-file-actions:not(.keep-visible) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .filename {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:hover redaction-file-actions {
|
||||
display: block;
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { File } from '@red/domain';
|
||||
import { File, IFileAttributeConfig } from '@red/domain';
|
||||
import { Required } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-workflow-item',
|
||||
@ -8,5 +9,7 @@ import { File } from '@red/domain';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class WorkflowItemComponent {
|
||||
@Input() file: File;
|
||||
@Input() @Required() file!: File;
|
||||
@Input() @Required() displayedAttributes!: IFileAttributeConfig[];
|
||||
@Input() width: number;
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ 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';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
@ -63,8 +64,8 @@ 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',
|
||||
},
|
||||
{
|
||||
@ -362,12 +363,6 @@ export class ConfigService {
|
||||
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);
|
||||
};
|
||||
|
||||
@ -11,7 +11,7 @@ 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';
|
||||
@ -36,7 +36,7 @@ const routes: Routes = [
|
||||
DossierOverviewBulkActionsComponent,
|
||||
DossierDetailsComponent,
|
||||
DossierDetailsStatsComponent,
|
||||
FileWorkloadColumnComponent,
|
||||
FileWorkloadComponent,
|
||||
TableItemComponent,
|
||||
FileStatsComponent,
|
||||
WorkflowItemComponent,
|
||||
|
||||
@ -34,14 +34,13 @@
|
||||
[addElementIcon]="'iqser:upload'"
|
||||
[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>
|
||||
|
||||
@ -76,6 +75,6 @@
|
||||
|
||||
<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>
|
||||
<ng-template #workflowItemTemplate let-entity="entity" let-itemWidth="itemWidth">
|
||||
<redaction-workflow-item [displayedAttributes]="displayedAttributes" [file]="entity" [width]="itemWidth"></redaction-workflow-item>
|
||||
</ng-template>
|
||||
|
||||
@ -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,
|
||||
@ -239,7 +239,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">
|
||||
<iqser-circle-button
|
||||
(action)="openDocument()"
|
||||
*ngIf="showOpenDocument"
|
||||
<div class="file-actions">
|
||||
<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,4 +1,16 @@
|
||||
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 { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
@ -25,6 +37,8 @@ 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 { Action, ActionTypes } from '@shared/components/expandable-file-actions/types';
|
||||
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-actions',
|
||||
@ -36,8 +50,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 +71,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 +101,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 +113,124 @@ 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: () => this._openDocument(),
|
||||
tooltip: _('dossier-overview.open-document'),
|
||||
icon: 'iqser:collapse',
|
||||
show: this.showOpenDocument,
|
||||
},
|
||||
{
|
||||
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',
|
||||
disabled: !this._permissionsService.canDownloadFiles([this.file]),
|
||||
},
|
||||
{
|
||||
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 +239,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 +259,62 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
);
|
||||
}
|
||||
|
||||
async ocrFile($event: MouseEvent) {
|
||||
forceReanalysisAction($event: LongPressEvent) {
|
||||
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
||||
}
|
||||
|
||||
private _openDocument() {
|
||||
this._router.navigate([this.file.routerLink]).then();
|
||||
}
|
||||
|
||||
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 +322,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';
|
||||
@ -221,6 +362,11 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
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() {
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } 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 { CircleButtonType, CircleButtonTypes, Toaster } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@ -15,7 +15,7 @@ export type MenuState = 'OPEN' | 'CLOSED';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileDownloadBtnComponent {
|
||||
@Input() files: List<File>;
|
||||
@Input() files: File[];
|
||||
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
|
||||
@Input() type: CircleButtonType = CircleButtonTypes.default;
|
||||
@Input() tooltipClass: string;
|
||||
@ -29,7 +29,7 @@ export class FileDownloadBtnComponent {
|
||||
) {}
|
||||
|
||||
get canDownloadFiles() {
|
||||
return this.files.length > 0 && this.files.reduce((acc, file) => acc && this._permissionsService.canDownloadFiles(file), true);
|
||||
return this._permissionsService.canDownloadFiles(this.files);
|
||||
}
|
||||
|
||||
get tooltip() {
|
||||
|
||||
@ -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:plus'"
|
||||
[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,38 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Action } from './types';
|
||||
import { CircleButtonType, IqserTooltipPosition } from '@iqser/common-ui';
|
||||
|
||||
@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;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.actions || changes.maxWidth) {
|
||||
if (this.maxWidth) {
|
||||
const count = Math.floor(this.maxWidth / 36);
|
||||
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 = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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?: Function;
|
||||
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;
|
||||
}
|
||||
@ -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,
|
||||
];
|
||||
|
||||
@ -99,13 +99,12 @@ export class PermissionsService {
|
||||
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 {
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 0a861bf60d47504a0c8cba019671da05dcdb3a28
|
||||
Subproject commit 4a27531b8e52cea707909f5d93f68b303e587945
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user