Added new file statuses

This commit is contained in:
Adina Țeudan 2021-11-15 23:01:31 +02:00
parent 29f55d4608
commit 71cd722bfa
19 changed files with 110 additions and 132 deletions

View File

@ -4,7 +4,7 @@ import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/si
import { TranslateChartService } from '@services/translate-chart.service'; import { TranslateChartService } from '@services/translate-chart.service';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
import { FilterService, shareLast, Toaster } from '@iqser/common-ui'; import { FilterService, shareLast, Toaster } from '@iqser/common-ui';
import { fileStatusTranslations } from '../../../../translations/file-status-translations'; import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Dossier, DossierAttributeWithValue, DossierStats, IDossierRequest, StatusSorter, User } from '@red/domain'; import { Dossier, DossierAttributeWithValue, DossierStats, IDossierRequest, StatusSorter, User } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
@ -61,7 +61,7 @@ export class DossierDetailsComponent {
const documentsChartData: DoughnutChartConfig[] = Object.keys(stats.fileCountPerWorkflowStatus).map(status => ({ const documentsChartData: DoughnutChartConfig[] = Object.keys(stats.fileCountPerWorkflowStatus).map(status => ({
value: stats.fileCountPerWorkflowStatus[status], value: stats.fileCountPerWorkflowStatus[status],
color: status, color: status,
label: fileStatusTranslations[status], label: workflowFileStatusTranslations[status],
key: status, key: status,
})); }));
documentsChartData.sort((a, b) => StatusSorter.byStatus(a.key, b.key)); documentsChartData.sort((a, b) => StatusSorter.byStatus(a.key, b.key));

View File

@ -49,12 +49,11 @@
<div [class.extend-cols]="file.isError" class="status-container cell"> <div [class.extend-cols]="file.isError" class="status-container cell">
<div *ngIf="file.isError" class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div> <div *ngIf="file.isError" class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div>
<div *ngIf="file.isPending" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div> <div *ngIf="file.isPending" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
<div *ngIf="file.isProcessing" class="small-label loading" translate="dossier-overview.file-listing.file-entry.file-processing"></div>
<iqser-status-bar <iqser-status-bar
*ngIf="file.isWorkable" *ngIf="file.isWorkable"
[configs]="[ [configs]="[
{ {
color: file.status, color: file.workflowStatus,
length: 1 length: 1
} }
]" ]"

View File

@ -12,8 +12,8 @@ import {
TableColumnConfig, TableColumnConfig,
WorkflowConfig, WorkflowConfig,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { File, FileStatus, FileStatuses, IFileAttributeConfig, StatusSorter } from '@red/domain'; import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
import { FileActionService } from '../../shared/services/file-action.service'; import { FileActionService } from '../../shared/services/file-action.service';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
@ -92,41 +92,41 @@ export class ConfigService {
]; ];
} }
workflowConfig(reloadDossiers: () => Promise<void>): WorkflowConfig<File, FileStatus> { workflowConfig(reloadDossiers: () => Promise<void>): WorkflowConfig<File, WorkflowFileStatus> {
return { return {
columnIdentifierFn: entity => entity.status, columnIdentifierFn: entity => entity.workflowStatus,
itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`, itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
columns: [ columns: [
{ {
label: fileStatusTranslations[FileStatuses.UNASSIGNED], label: workflowFileStatusTranslations[WorkflowFileStatuses.UNASSIGNED],
key: FileStatuses.UNASSIGNED, key: WorkflowFileStatuses.UNASSIGNED,
enterFn: this._unassignFn(reloadDossiers), enterFn: this._unassignFn(reloadDossiers),
enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file), enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file),
color: '#D3D5DA', color: '#D3D5DA',
}, },
{ {
label: fileStatusTranslations[FileStatuses.UNDER_REVIEW], label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
enterFn: this._underReviewFn(reloadDossiers), enterFn: this._underReviewFn(reloadDossiers),
enterPredicate: (file: File) => enterPredicate: (file: File) =>
this._permissionsService.canSetUnderReview(file) || this._permissionsService.canSetUnderReview(file) ||
this._permissionsService.canAssignToSelf(file) || this._permissionsService.canAssignToSelf(file) ||
this._permissionsService.canAssignUser(file), this._permissionsService.canAssignUser(file),
key: FileStatuses.UNDER_REVIEW, key: WorkflowFileStatuses.UNDER_REVIEW,
color: '#FDBD00', color: '#FDBD00',
}, },
{ {
label: fileStatusTranslations[FileStatuses.UNDER_APPROVAL], label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
enterFn: this._underApprovalFn(reloadDossiers), enterFn: this._underApprovalFn(reloadDossiers),
enterPredicate: (file: File) => enterPredicate: (file: File) =>
this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file), this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file),
key: FileStatuses.UNDER_APPROVAL, key: WorkflowFileStatuses.UNDER_APPROVAL,
color: '#374C81', color: '#374C81',
}, },
{ {
label: fileStatusTranslations[FileStatuses.APPROVED], label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
enterFn: this._approveFn(reloadDossiers), enterFn: this._approveFn(reloadDossiers),
enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved, enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved,
key: FileStatuses.APPROVED, key: WorkflowFileStatuses.APPROVED,
color: '#48C9F7', color: '#48C9F7',
}, },
], ],
@ -141,7 +141,7 @@ export class ConfigService {
checkedRequiredFilters: () => NestedFilter[], checkedRequiredFilters: () => NestedFilter[],
checkedNotRequiredFilters: () => NestedFilter[], checkedNotRequiredFilters: () => NestedFilter[],
) { ) {
const allDistinctFileStatuses = new Set<string>(); const allDistinctWorkflowFileStatuses = new Set<string>();
const allDistinctPeople = new Set<string>(); const allDistinctPeople = new Set<string>();
const allDistinctAddedDates = new Set<string>(); const allDistinctAddedDates = new Set<string>();
const allDistinctNeedsWork = new Set<string>(); const allDistinctNeedsWork = new Set<string>();
@ -152,7 +152,7 @@ export class ConfigService {
entities.forEach(file => { entities.forEach(file => {
allDistinctPeople.add(file.currentReviewer); allDistinctPeople.add(file.currentReviewer);
allDistinctFileStatuses.add(file.status); allDistinctWorkflowFileStatuses.add(file.workflowStatus);
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY')); allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'));
if (file.analysisRequired) { if (file.analysisRequired) {
@ -196,11 +196,11 @@ export class ConfigService {
}); });
if (listingMode === ListingModes.table) { if (listingMode === ListingModes.table) {
const statusFilters = [...allDistinctFileStatuses].map( const statusFilters = [...allDistinctWorkflowFileStatuses].map(
status => status =>
new NestedFilter({ new NestedFilter({
id: status, id: status,
label: this._translateService.instant(fileStatusTranslations[status]), label: this._translateService.instant(workflowFileStatusTranslations[status]),
}), }),
); );
@ -209,7 +209,7 @@ export class ConfigService {
label: this._translateService.instant('filters.status'), label: this._translateService.instant('filters.status'),
icon: 'red:status', icon: 'red:status',
filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]), filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
checker: keyChecker('status'), checker: keyChecker('workflowStatus'),
}); });
} }
@ -333,12 +333,12 @@ export class ConfigService {
{ {
id: 'unassigned', id: 'unassigned',
label: this._translateService.instant('dossier-overview.quick-filters.unassigned'), label: this._translateService.instant('dossier-overview.quick-filters.unassigned'),
checker: (file: File) => !file.currentReviewer, checker: (file: File) => file.isUnassigned,
}, },
{ {
id: 'assigned-to-others', id: 'assigned-to-others',
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'), label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'),
checker: (file: File) => !!file.currentReviewer && file.currentReviewer !== this._userService.currentUser.id, checker: (file: File) => !file.isUnassigned && file.currentReviewer !== this._userService.currentUser.id,
}, },
].map(filter => new NestedFilter(filter)); ].map(filter => new NestedFilter(filter));
} }

View File

@ -11,7 +11,7 @@ import {
TemplateRef, TemplateRef,
ViewChild, ViewChild,
} from '@angular/core'; } from '@angular/core';
import { Dossier, DossierAttributeWithValue, File, FileStatus, IFileAttributeConfig } from '@red/domain'; import { Dossier, DossierAttributeWithValue, File, IFileAttributeConfig, WorkflowFileStatus } from '@red/domain';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service'; import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
import { FileUploadModel } from '@upload-download/model/file-upload.model'; import { FileUploadModel } from '@upload-download/model/file-upload.model';
@ -74,7 +74,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
analysisForced: boolean; analysisForced: boolean;
displayedInFileListAttributes: IFileAttributeConfig[] = []; displayedInFileListAttributes: IFileAttributeConfig[] = [];
displayedAttributes: IFileAttributeConfig[] = []; displayedAttributes: IFileAttributeConfig[] = [];
readonly workflowConfig: WorkflowConfig<File, FileStatus> = this._configService.workflowConfig(() => this.reloadFiles()); readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus> = this._configService.workflowConfig(() => this.reloadFiles());
readonly actionConfigs: readonly ActionConfig[]; readonly actionConfigs: readonly ActionConfig[];
readonly dossier$: Observable<Dossier>; readonly dossier$: Observable<Dossier>;
readonly dossierId: string; readonly dossierId: string;
@ -265,7 +265,8 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
'primaryAttribute', 'primaryAttribute',
'numberOfPages', 'numberOfPages',
'assignee', 'assignee',
'status', 'workflowStatus',
'processingStatus',
'lastUpdated', 'lastUpdated',
'lastUploaded', 'lastUploaded',
'lastProcessed', 'lastProcessed',

View File

@ -4,7 +4,7 @@ import { FilterService, mapEach } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { combineLatest, Observable } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { Dossier, DossierStats, DossierStatuses, FileCountPerWorkflowStatus, StatusSorter } from '@red/domain'; import { Dossier, DossierStats, DossierStatuses, FileCountPerWorkflowStatus, StatusSorter } from '@red/domain';
import { fileStatusTranslations } from '../../../../translations/file-status-translations'; import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations';
import { TranslateChartService } from '@services/translate-chart.service'; import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators'; import { filter, map, switchMap } from 'rxjs/operators';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -60,7 +60,7 @@ export class DossiersListingDetailsComponent {
({ ({
value: chartData[status], value: chartData[status],
color: status, color: status,
label: fileStatusTranslations[status], label: workflowFileStatusTranslations[status],
key: status, key: status,
} as DoughnutChartConfig), } as DoughnutChartConfig),
); );

View File

@ -5,7 +5,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { UserPreferenceService } from '@services/user-preference.service'; import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
import { dossierMemberChecker, dossierTemplateChecker, RedactionFilterSorter } from '@utils/index'; import { dossierMemberChecker, dossierTemplateChecker, RedactionFilterSorter } from '@utils/index';
import { workloadTranslations } from '../../translations/workload-translations'; import { workloadTranslations } from '../../translations/workload-translations';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
@ -113,7 +113,7 @@ export class ConfigService {
status => status =>
new NestedFilter({ new NestedFilter({
id: status, id: status,
label: this._translateService.instant(fileStatusTranslations[status]), label: this._translateService.instant(workflowFileStatusTranslations[status]),
}), }),
); );

View File

@ -35,7 +35,7 @@
<iqser-status-bar [configs]="statusBarConfig" [small]="true"></iqser-status-bar> <iqser-status-bar [configs]="statusBarConfig" [small]="true"></iqser-status-bar>
<div class="all-caps-label mr-16 ml-8"> <div class="all-caps-label mr-16 ml-8">
{{ translations[file.status] | translate }} {{ translations[file.workflowStatus] | translate }}
<span *ngIf="isUnderReviewOrApproval">{{ 'by' | translate }}:</span> <span *ngIf="isUnderReviewOrApproval">{{ 'by' | translate }}:</span>
</div> </div>
</ng-container> </ng-container>

View File

@ -33,7 +33,7 @@ import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
import { FileActionService } from '../../shared/services/file-action.service'; import { FileActionService } from '../../shared/services/file-action.service';
import { AnnotationDrawService } from '../../services/annotation-draw.service'; import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationProcessingService } from '../../services/annotation-processing.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { Dossier, File, FileStatus, User, ViewMode } from '@red/domain'; import { Dossier, File, User, ViewMode, WorkflowFileStatus } from '@red/domain';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { Observable, timer } from 'rxjs'; import { Observable, timer } from 'rxjs';
import { UserPreferenceService } from '@services/user-preference.service'; import { UserPreferenceService } from '@services/user-preference.service';
@ -44,7 +44,7 @@ import { FileWorkloadComponent } from '../../components/file-workload/file-workl
import { DossiersDialogService } from '../../services/dossiers-dialog.service'; import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { clearStamps, stampPDFPage } from '@utils/page-stamper'; import { clearStamps, stampPDFPage } from '@utils/page-stamper';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
import { handleFilterDelta } from '@utils/filter-utils'; import { handleFilterDelta } from '@utils/filter-utils';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FileActionsComponent } from '../../shared/components/file-actions/file-actions.component'; import { FileActionsComponent } from '../../shared/components/file-actions/file-actions.component';
@ -65,7 +65,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
}) })
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach { export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach {
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
readonly translations = fileStatusTranslations; readonly translations = workflowFileStatusTranslations;
dialogRef: MatDialogRef<unknown>; dialogRef: MatDialogRef<unknown>;
viewMode: ViewMode = 'STANDARD'; viewMode: ViewMode = 'STANDARD';
@ -183,12 +183,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
: this._translateService.instant('file-preview.assign-reviewer'); : this._translateService.instant('file-preview.assign-reviewer');
} }
get statusBarConfig(): [{ length: number; color: FileStatus }] { get statusBarConfig(): [{ length: number; color: WorkflowFileStatus }] {
return [{ length: 1, color: this.file.status }]; return [{ length: 1, color: this.file.workflowStatus }];
} }
get isUnderReviewOrApproval(): boolean { get isUnderReviewOrApproval(): boolean {
return this.file.status === 'UNDER_REVIEW' || this.file.status === 'UNDER_APPROVAL'; return this.file.isUnderReview || this.file.isUnderApproval;
} }
canAssign(file: File): boolean { canAssign(file: File): boolean {

View File

@ -15,7 +15,7 @@ import { merge, Observable } from 'rxjs';
import { debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators'; import { debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { RouterHistoryService } from '@services/router-history.service'; import { RouterHistoryService } from '@services/router-history.service';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
@ -37,7 +37,7 @@ function toSearchInput(query: string, dossierIds: List | string): ISearchInput {
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class SearchScreenComponent extends ListingComponent<ISearchListItem> implements OnDestroy { export class SearchScreenComponent extends ListingComponent<ISearchListItem> implements OnDestroy {
readonly fileStatusTranslations = fileStatusTranslations; readonly fileStatusTranslations = workflowFileStatusTranslations;
readonly searchPositions = SearchPositions; readonly searchPositions = SearchPositions;
readonly tableHeaderLabel = _('search-screen.table-header'); readonly tableHeaderLabel = _('search-screen.table-header');
@ -139,7 +139,7 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> imp
dossierId, dossierId,
unmatched: unmatchedTerms || null, unmatched: unmatchedTerms || null,
highlights, highlights,
status: file.status, status: file.workflowStatus,
numberOfPages: file.numberOfPages, numberOfPages: file.numberOfPages,
dossierName: this._dossiersService.find(dossierId).dossierName, dossierName: this._dossiersService.find(dossierId).dossierName,
filename: file.filename, filename: file.filename,

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { Dossier, File, FileStatus } from '@red/domain'; import { Dossier, File, WorkflowFileStatus } from '@red/domain';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { import {
@ -45,7 +45,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
@Output() readonly actionPerformed = new EventEmitter<string>(); @Output() readonly actionPerformed = new EventEmitter<string>();
dossier$: Observable<Dossier>; dossier$: Observable<Dossier>;
statusBarConfig?: List<StatusBarConfig<FileStatus>>; statusBarConfig?: List<StatusBarConfig<WorkflowFileStatus>>;
tooltipPosition?: 'below' | 'above'; tooltipPosition?: 'below' | 'above';
toggleTooltip?: string; toggleTooltip?: string;
assignTooltip?: string; assignTooltip?: string;
@ -234,7 +234,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
} }
setup() { setup() {
this.statusBarConfig = [{ color: this.file.status, length: 1 }]; this.statusBarConfig = [{ color: this.file.workflowStatus, length: 1 }];
this.tooltipPosition = this.isFilePreview ? 'below' : 'above'; this.tooltipPosition = this.isFilePreview ? 'below' : 'above';
this.assignTooltip = this.file.isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'); this.assignTooltip = this.file.isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer');
this.buttonType = this.isFilePreview ? CircleButtonTypes.default : CircleButtonTypes.dark; this.buttonType = this.isFilePreview ? CircleButtonTypes.default : CircleButtonTypes.dark;

View File

@ -1,18 +1,21 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FileStatus } from '@red/domain'; import { ProcessingFileStatus, WorkflowFileStatus } from '@red/domain';
export const fileStatusTranslations: { [key in FileStatus]: string } = { export const workflowFileStatusTranslations: { [key in WorkflowFileStatus]: string } = {
APPROVED: _('file-status.approved'), APPROVED: _('file-status.approved'),
UNASSIGNED: _('file-status.unassigned'),
UNDER_APPROVAL: _('file-status.under-approval'),
UNDER_REVIEW: _('file-status.under-review'),
};
export const processingFileStatusTranslations: { [key in ProcessingFileStatus]: string } = {
PROCESSED: _('file-status.processed'),
DELETED: _('file-status.deleted'), DELETED: _('file-status.deleted'),
ERROR: _('file-status.error'), ERROR: _('file-status.error'),
EXCLUDED: _('file-status.excluded'),
FULLREPROCESS: _('file-status.full-reprocess'), FULLREPROCESS: _('file-status.full-reprocess'),
INDEXING: _('file-status.indexing'), INDEXING: _('file-status.indexing'),
OCR_PROCESSING: _('file-status.ocr-processing'), OCR_PROCESSING: _('file-status.ocr-processing'),
PROCESSING: _('file-status.processing'), PROCESSING: _('file-status.processing'),
REPROCESS: _('file-status.reprocess'), REPROCESS: _('file-status.reprocess'),
UNASSIGNED: _('file-status.unassigned'),
UNDER_APPROVAL: _('file-status.under-approval'),
UNDER_REVIEW: _('file-status.under-review'),
UNPROCESSED: _('file-status.unprocessed'), UNPROCESSED: _('file-status.unprocessed'),
}; };

View File

@ -22,7 +22,7 @@ export class PermissionsService {
} }
canToggleAnalysis(file: File, dossier: Dossier): boolean { canToggleAnalysis(file: File, dossier: Dossier): boolean {
return this.isReviewerOrApprover(file, dossier) && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(file.status); return this.isReviewerOrApprover(file, dossier) && (file.isUnassigned || file.isUnderReview || file.isUnderApproval);
} }
canReanalyseFile(file: File, dossier: Dossier): boolean { canReanalyseFile(file: File, dossier: Dossier): boolean {
@ -102,8 +102,9 @@ export class PermissionsService {
return dossier?.memberIds.includes(user.id); return dossier?.memberIds.includes(user.id);
} }
// TODO: Remove '?', after we make sure file is loaded before page
canPerformAnnotationActions(file: File): boolean { canPerformAnnotationActions(file: File): boolean {
return ['UNDER_REVIEW', 'UNDER_APPROVAL'].includes(file?.status) && this.isFileReviewer(file); return (file?.isUnderReview || file?.isUnderApproval) && this.isFileReviewer(file);
} }
canUndoApproval(file: File): boolean { canUndoApproval(file: File): boolean {
@ -111,7 +112,7 @@ export class PermissionsService {
} }
canMarkPagesAsViewed(file: File): boolean { canMarkPagesAsViewed(file: File): boolean {
return ['UNDER_REVIEW', 'UNDER_APPROVAL'].includes(file?.status) && this.isFileReviewer(file); return (file.isUnderReview || file.isUnderApproval) && this.isFileReviewer(file);
} }
canDownloadFiles(file: File): boolean { canDownloadFiles(file: File): boolean {
@ -137,7 +138,7 @@ export class PermissionsService {
canExcludePages(file: File): boolean { canExcludePages(file: File): boolean {
const dossier = this._getDossier(file); const dossier = this._getDossier(file);
return ['UNDER_REVIEW', 'UNDER_APPROVAL'].includes(file.status) && (this.isFileReviewer(file) || this.isApprover(dossier)); return (file.isUnderReview || file.isUnderApproval) && (this.isFileReviewer(file) || this.isApprover(dossier));
} }
canDeleteComment(comment: IComment, file: File) { canDeleteComment(comment: IComment, file: File) {

View File

@ -698,8 +698,7 @@
"file-listing": { "file-listing": {
"file-entry": { "file-entry": {
"file-error": "Re-processing required", "file-error": "Re-processing required",
"file-pending": "Pending...", "file-pending": "Pending..."
"file-processing": "Processing"
} }
}, },
"filters": { "filters": {
@ -1081,10 +1080,10 @@
"approved": "Approved", "approved": "Approved",
"deleted": "Deleted", "deleted": "Deleted",
"error": "Re-processing required", "error": "Re-processing required",
"excluded": "Excluded",
"full-reprocess": "Processing", "full-reprocess": "Processing",
"indexing": "Processing", "indexing": "Processing",
"ocr-processing": "OCR Processing", "ocr-processing": "OCR Processing",
"processed": "Processed",
"processing": "Processing", "processing": "Processing",
"reprocess": "Processing", "reprocess": "Processing",
"unassigned": "Unassigned", "unassigned": "Unassigned",

@ -1 +1 @@
Subproject commit 8beb712c5492dd77e81f46cb67752615f582ecf4 Subproject commit 643df41d09d3a1d159adbb1f2cf3cefa26002bc8

View File

@ -1,26 +1,4 @@
export const WorkflowFileStatuses = { import { ProcessingFileStatus, WorkflowFileStatus } from '../files';
APPROVED: 'APPROVED',
UNASSIGNED: 'UNASSIGNED',
UNDER_APPROVAL: 'UNDER_APPROVAL',
UNDER_REVIEW: 'UNDER_REVIEW',
} as const;
export type WorkflowFileStatus = keyof typeof WorkflowFileStatuses;
export type FileCountPerWorkflowStatus = { [key in WorkflowFileStatus]?: number }; export type FileCountPerWorkflowStatus = { [key in WorkflowFileStatus]?: number };
export const ProcessingFileStatuses = {
DELETED: 'DELETED',
ERROR: 'ERROR',
FULLREPROCESS: 'FULLREPROCESS',
INDEXING: 'INDEXING',
OCR_PROCESSING: 'OCR_PROCESSING',
PROCESSED: 'PROCESSED',
PROCESSING: 'PROCESSING',
REPROCESS: 'REPROCESS',
UNPROCESSED: 'UNPROCESSED',
} as const;
export type ProcessingFileStatus = keyof typeof ProcessingFileStatuses;
export type FileCountPerProcessingStatus = { [key in ProcessingFileStatus]?: number }; export type FileCountPerProcessingStatus = { [key in ProcessingFileStatus]?: number };

View File

@ -1,15 +1,15 @@
import { Entity, List } from '@iqser/common-ui'; import { Entity, List } from '@iqser/common-ui';
import { StatusSorter } from '../shared'; import { StatusSorter } from '../shared';
import { FileStatus, FileStatuses } from './types'; import { ProcessingFileStatus, ProcessingFileStatuses, WorkflowFileStatus, WorkflowFileStatuses } from './types';
import { IFile } from './file'; import { IFile } from './file';
import { FileAttributes, IFileAttributesConfig } from '../file-attributes'; import { FileAttributes, IFileAttributesConfig } from '../file-attributes';
const processingStatuses: List<FileStatus> = [ const processingStatuses: List<ProcessingFileStatus> = [
FileStatuses.REPROCESS, ProcessingFileStatuses.REPROCESS,
FileStatuses.FULLREPROCESS, ProcessingFileStatuses.FULLREPROCESS,
FileStatuses.OCR_PROCESSING, ProcessingFileStatuses.OCR_PROCESSING,
FileStatuses.INDEXING, ProcessingFileStatuses.INDEXING,
FileStatuses.PROCESSING, ProcessingFileStatuses.PROCESSING,
] as const; ] as const;
export class File extends Entity<IFile> implements IFile { export class File extends Entity<IFile> implements IFile {
@ -40,10 +40,11 @@ export class File extends Entity<IFile> implements IFile {
readonly numberOfAnalyses: number; readonly numberOfAnalyses: number;
readonly numberOfPages?: number; readonly numberOfPages?: number;
readonly rulesVersion?: number; readonly rulesVersion?: number;
readonly status: FileStatus;
readonly uploader?: string; readonly uploader?: string;
readonly excludedPages?: number[]; readonly excludedPages?: number[];
readonly hasSuggestions: boolean; readonly hasSuggestions: boolean;
readonly processingStatus: ProcessingFileStatus;
readonly workflowStatus: WorkflowFileStatus;
readonly primaryAttribute?: string; readonly primaryAttribute?: string;
lastOpened = false; lastOpened = false;
@ -90,30 +91,31 @@ export class File extends Entity<IFile> implements IFile {
this.lastUploaded = file.lastUploaded; this.lastUploaded = file.lastUploaded;
this.legalBasisVersion = file.legalBasisVersion; this.legalBasisVersion = file.legalBasisVersion;
this.numberOfAnalyses = file.numberOfAnalyses; this.numberOfAnalyses = file.numberOfAnalyses;
this.status = ['REPROCESS', 'FULLREPROCESS', 'INDEXING'].includes(file.status) ? FileStatuses.PROCESSING : file.status; this.processingStatus = file.processingStatus;
this.isError = this.status === FileStatuses.ERROR; this.workflowStatus = file.workflowStatus;
this.isError = this.processingStatus === ProcessingFileStatuses.ERROR;
this.numberOfPages = this.isError ? 0 : file.numberOfPages ?? 0; this.numberOfPages = this.isError ? 0 : file.numberOfPages ?? 0;
this.rulesVersion = file.rulesVersion; this.rulesVersion = file.rulesVersion;
this.uploader = file.uploader; this.uploader = file.uploader;
this.excludedPages = file.excludedPages; this.excludedPages = file.excludedPages;
this.hasSuggestions = !!file.hasSuggestions; this.hasSuggestions = !!file.hasSuggestions;
this.statusSort = StatusSorter[this.status]; this.statusSort = StatusSorter[this.workflowStatus];
if (this.lastUpdated && this.lastOCRTime) { if (this.lastUpdated && this.lastOCRTime) {
this.cacheIdentifier = btoa((this.lastUploaded ?? '') + this.lastOCRTime); this.cacheIdentifier = btoa((this.lastUploaded ?? '') + this.lastOCRTime);
} }
this.hintsOnly = this.hasHints && !this.hasRedactions; this.hintsOnly = this.hasHints && !this.hasRedactions;
this.hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions; this.hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
this.isUnassigned = !this.currentReviewer; this.isPending = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;
this.isProcessing = processingStatuses.includes(this.status); this.isProcessing = processingStatuses.includes(this.processingStatus);
this.isApproved = this.status === FileStatuses.APPROVED; this.isApproved = this.workflowStatus === WorkflowFileStatuses.APPROVED;
this.isPending = this.status === FileStatuses.UNPROCESSED; this.isUnassigned = this.workflowStatus === WorkflowFileStatuses.UNASSIGNED;
this.isUnderReview = this.status === FileStatuses.UNDER_REVIEW; this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW;
this.isUnderApproval = this.status === FileStatuses.UNDER_APPROVAL; this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL;
this.canBeApproved = !this.analysisRequired && !this.hasSuggestions; this.canBeApproved = !this.analysisRequired && !this.hasSuggestions;
this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0; this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0;
this.isWorkable = !this.isProcessing && this.canBeOpened; this.isWorkable = !this.isProcessing && this.canBeOpened;
this.canBeOCRed = !this.excluded && !this.lastOCRTime && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(this.status); this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isUnassigned || this.isUnderReview || this.isUnderApproval);
if (fileAttributesConfig) { if (fileAttributesConfig) {
const primary = fileAttributesConfig.fileAttributeConfigs?.find(c => c.primaryAttribute); const primary = fileAttributesConfig.fileAttributeConfigs?.find(c => c.primaryAttribute);

View File

@ -1,7 +1,7 @@
/** /**
* Object containing information on a specific file. * Object containing information on a specific file.
*/ */
import { FileStatus } from './types'; import { ProcessingFileStatus, WorkflowFileStatus } from './types';
import { FileAttributes } from '../file-attributes'; import { FileAttributes } from '../file-attributes';
export interface IFile { export interface IFile {
@ -130,12 +130,12 @@ export interface IFile {
* Shows if the file is soft deleted. * Shows if the file is soft deleted.
*/ */
readonly softDeleted?: string; readonly softDeleted?: string;
/**
* The status of the file with regard to its analysis an review processes.
*/
readonly status: FileStatus;
/** /**
* The ID of the user who uploaded the file. * The ID of the user who uploaded the file.
*/ */
readonly uploader?: string; readonly uploader?: string;
readonly processingStatus: ProcessingFileStatus;
readonly workflowStatus: WorkflowFileStatus;
} }

View File

@ -1,17 +1,22 @@
export const FileStatuses = { export const WorkflowFileStatuses = {
APPROVED: 'APPROVED', APPROVED: 'APPROVED',
DELETED: 'DELETED',
ERROR: 'ERROR',
EXCLUDED: 'EXCLUDED',
FULLREPROCESS: 'FULLREPROCESS',
INDEXING: 'INDEXING',
OCR_PROCESSING: 'OCR_PROCESSING',
PROCESSING: 'PROCESSING',
REPROCESS: 'REPROCESS',
UNASSIGNED: 'UNASSIGNED', UNASSIGNED: 'UNASSIGNED',
UNDER_APPROVAL: 'UNDER_APPROVAL', UNDER_APPROVAL: 'UNDER_APPROVAL',
UNDER_REVIEW: 'UNDER_REVIEW', UNDER_REVIEW: 'UNDER_REVIEW',
} as const;
export type WorkflowFileStatus = keyof typeof WorkflowFileStatuses;
export const ProcessingFileStatuses = {
DELETED: 'DELETED',
ERROR: 'ERROR',
FULLREPROCESS: 'FULLREPROCESS',
INDEXING: 'INDEXING',
OCR_PROCESSING: 'OCR_PROCESSING',
PROCESSED: 'PROCESSED',
PROCESSING: 'PROCESSING',
REPROCESS: 'REPROCESS',
UNPROCESSED: 'UNPROCESSED', UNPROCESSED: 'UNPROCESSED',
} as const; } as const;
export type FileStatus = keyof typeof FileStatuses; export type ProcessingFileStatus = keyof typeof ProcessingFileStatuses;

View File

@ -1,32 +1,22 @@
import { FileStatus } from '../../files'; import { WorkflowFileStatus } from '../../files';
type StatusSorterItem = { key: FileStatus } | FileStatus | string; type StatusSorterItem = { key: WorkflowFileStatus } | WorkflowFileStatus | string;
type Sorter = Record<FileStatus, number> & { type Sorter = Record<WorkflowFileStatus, number> & {
byStatus: <T extends StatusSorterItem>(a: T, b: T) => number; byStatus: <T extends StatusSorterItem>(a: T, b: T) => number;
}; };
export const StatusSorter: Sorter = { export const StatusSorter: Sorter = {
ERROR: 0, UNASSIGNED: 1,
DELETED: 0, UNDER_REVIEW: 2,
FULLREPROCESS: 0, UNDER_APPROVAL: 3,
EXCLUDED: 0, APPROVED: 4,
INDEXING: 0,
UNPROCESSED: 1,
REPROCESS: 5,
PROCESSING: 5,
OCR_PROCESSING: 7,
UNASSIGNED: 10,
UNDER_REVIEW: 15,
UNDER_APPROVAL: 20,
APPROVED: 25,
byStatus: (a: StatusSorterItem, b: StatusSorterItem): number => { byStatus: (a: StatusSorterItem, b: StatusSorterItem): number => {
if (typeof a !== typeof b) { if (typeof a !== typeof b) {
throw TypeError('Used different types when calling StatusSorter.byStatus1'); throw TypeError('Used different types when calling StatusSorter.byStatus1');
} }
const x = typeof a === 'string' ? (a as FileStatus) : a.key; const x = typeof a === 'string' ? (a as WorkflowFileStatus) : a.key;
const y = typeof b === 'string' ? (b as FileStatus) : b.key; const y = typeof b === 'string' ? (b as WorkflowFileStatus) : b.key;
return StatusSorter[x] - StatusSorter[y]; return StatusSorter[x] - StatusSorter[y];
}, },
}; };