status / assignee updates

This commit is contained in:
Timo Bejan 2021-11-23 12:04:12 +02:00
parent e4eebe5396
commit 2c7194f6ef
21 changed files with 65 additions and 52 deletions

View File

@ -47,7 +47,7 @@ timo-redaction-dev.iqser.cloud
| useradmin | RED_ADMIN, RED_USER | has super power ! |
| manageradmin | RED_ADMIN RED_MANAGER RED_USER | has super super power ! |
Password for all users is `OsloImWinter`
Password for all users is `OsloImWinter!23`
```

View File

@ -57,7 +57,7 @@ export class AssignReviewerApproverDialogComponent {
}
for (const file of this.data.files) {
if (file.currentReviewer !== this.selectedUser) {
if (file.assignee !== this.selectedUser) {
return true;
}
}
@ -77,8 +77,8 @@ export class AssignReviewerApproverDialogComponent {
private get _uniqueReviewers(): Set<string> {
const uniqueReviewers = new Set<string>();
for (const file of this.data.files) {
if (file.currentReviewer) {
uniqueReviewers.add(file.currentReviewer);
if (file.assignee) {
uniqueReviewers.add(file.assignee);
}
}
return uniqueReviewers;

View File

@ -38,7 +38,7 @@ export class DossierOverviewBulkActionsComponent {
get allSelectedFilesCanBeAssignedIntoSameState() {
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
(acc, file) => acc && (file.isUnderReview || file.isUnassigned),
(acc, file) => acc && (file.isUnderReview || file.isNew),
true,
);
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);

View File

@ -21,7 +21,7 @@
</div>
<div class="user-column cell">
<redaction-initials-avatar [user]="file.currentReviewer" [withName]="true"></redaction-initials-avatar>
<redaction-initials-avatar [user]="file.assignee" [withName]="true"></redaction-initials-avatar>
</div>
<div class="cell">

View File

@ -9,7 +9,7 @@
</div>
<div class="user">
<redaction-initials-avatar [user]="file.currentReviewer"></redaction-initials-avatar>
<redaction-initials-avatar [user]="file.assignee"></redaction-initials-avatar>
</div>
</div>

View File

@ -61,8 +61,8 @@ export class ConfigService {
itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
columns: [
{
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNASSIGNED],
key: WorkflowFileStatuses.UNASSIGNED,
label: workflowFileStatusTranslations[WorkflowFileStatuses.NEW],
key: WorkflowFileStatuses.NEW,
enterFn: this._unassignFn,
enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file),
color: '#D3D5DA',
@ -162,7 +162,7 @@ export class ConfigService {
const filterGroups: IFilterGroup[] = [];
entities.forEach(file => {
allDistinctPeople.add(file.currentReviewer);
allDistinctPeople.add(file.assignee);
allDistinctWorkflowFileStatuses.add(file.workflowStatus);
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'));
@ -318,11 +318,11 @@ export class ConfigService {
_recentlyModifiedChecker = (file: File) =>
moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
_assignedToMeChecker = (file: File) => file.currentReviewer === this._userService.currentUser.id;
_assignedToMeChecker = (file: File) => file.assignee === this._userService.currentUser.id;
_unassignedChecker = (file: File) => file.isUnassigned;
_unassignedChecker = (file: File) => file.isNew;
_assignedToOthersChecker = (file: File) => !file.isUnassigned && file.currentReviewer !== this._userService.currentUser.id;
_assignedToOthersChecker = (file: File) => !file.isNew && file.assignee !== this._userService.currentUser.id;
private _quickFilters(entities: File[]): NestedFilter[] {
const recentPeriod = this._appConfigService.values.RECENT_PERIOD_IN_HOURS;

View File

@ -46,8 +46,8 @@
<redaction-initials-avatar
*ngIf="!editingReviewer"
[user]="file.currentReviewer"
[withName]="!!file.currentReviewer"
[user]="file.assignee"
[withName]="!!file.assignee"
tooltipPosition="below"
></redaction-initials-avatar>
<div
@ -62,15 +62,13 @@
(save)="assignReviewer(file, $event)"
*ngIf="editingReviewer"
[options]="usersOptions(file, dossier)"
[value]="file.currentReviewer"
[value]="file.assignee"
></redaction-assign-user-dropdown>
<div *ngIf="canAssign(file)" class="assign-actions-wrapper">
<iqser-circle-button
(action)="editingReviewer = true"
*ngIf="
(permissionsService.canAssignUser(file) || permissionsService.canUnassignUser(file)) && !!file.currentReviewer
"
*ngIf="(permissionsService.canAssignUser(file) || permissionsService.canUnassignUser(file)) && !!file.assignee"
[tooltip]="assignTooltip(file)"
icon="iqser:edit"
tooltipPosition="below"

View File

@ -178,7 +178,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
assignOrChangeReviewerTooltip(file: File): string {
return file.currentReviewer
return file.assignee
? this._translateService.instant('file-preview.change-reviewer')
: this._translateService.instant('file-preview.assign-reviewer');
}
@ -206,7 +206,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
canAssignReviewer(file: File, dossier: Dossier): boolean {
return !file.currentReviewer && this.permissionsService.canAssignUser(file) && dossier.hasReviewers;
return !file.assignee && this.permissionsService.canAssignUser(file) && dossier.hasReviewers;
}
async updateViewMode(): Promise<void> {
@ -487,12 +487,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
async assignReviewer(file: File, user: User | string) {
const reviewerId = typeof user === 'string' ? user : user?.id;
const reviewerName = this._userService.getNameForId(reviewerId);
const assigneeId = typeof user === 'string' ? user : user?.id;
const reviewerName = this._userService.getNameForId(assigneeId);
const { dossierId, fileId, filename } = file;
this._loadingService.start();
await this._filesService.setReviewerFor([fileId], dossierId, reviewerId).toPromise();
await this._filesService.setReviewerFor([fileId], dossierId, assigneeId).toPromise();
this._loadingService.stop();
this._toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } });

View File

@ -22,7 +22,7 @@ export class FileAssignService {
async assignToMe(files: File[]) {
return new Promise<void>((resolve, reject) => {
const atLeastOneFileHasReviewer = files.reduce((acc, fs) => acc || !!fs.currentReviewer, false);
const atLeastOneFileHasReviewer = files.reduce((acc, fs) => acc || !!fs.assignee, false);
if (atLeastOneFileHasReviewer) {
const data = new ConfirmationDialogInput({
title: _('confirmation-dialog.assign-file-to-me.title'),
@ -58,9 +58,9 @@ export class FileAssignService {
const currentDossier = this._dossiersService.find(file.dossierId);
const eligibleUsersIds = this._getUserIds(mode, currentDossier);
if (file.isUnassigned) {
if (file.isNew) {
await this._makeAssignFileRequest(currentUserId, mode, [file]);
} else if (file.currentReviewer === currentUserId) {
} else if (file.assignee === currentUserId) {
if (eligibleUsersIds.includes(currentUserId)) {
await this._makeAssignFileRequest(currentUserId, mode, [file]);
} else if (eligibleUsersIds.length === 1) {

View File

@ -3,6 +3,7 @@ import { ProcessingFileStatus, WorkflowFileStatus } from '@red/domain';
export const workflowFileStatusTranslations: { [key in WorkflowFileStatus]: string } = {
APPROVED: _('file-status.approved'),
NEW: _('file-status.new'),
UNASSIGNED: _('file-status.unassigned'),
UNDER_APPROVAL: _('file-status.under-approval'),
UNDER_REVIEW: _('file-status.under-review'),

View File

@ -22,32 +22,32 @@ export class FilesService extends EntitiesService<File, IFile> {
/** Reload dossier files + stats. */
loadAll(dossierId: string) {
const files$ = this.getFor(dossierId).pipe(mapEach(file => new File(file, this._userService.getNameForId(file.currentReviewer))));
const files$ = this.getFor(dossierId).pipe(mapEach(file => new File(file, this._userService.getNameForId(file.assignee))));
const loadStats$ = files$.pipe(switchMap(files => this._dossierStatsService.getFor([dossierId]).pipe(mapTo(files))));
return loadStats$.pipe(tap(files => this._filesMapService.set(dossierId, files)));
}
reload(dossierId: string, fileId: string): Observable<File> {
return super._getOne([dossierId, fileId]).pipe(
map(file => new File(file, this._userService.getNameForId(file.currentReviewer))),
map(file => new File(file, this._userService.getNameForId(file.assignee))),
switchMap(file => this._dossierStatsService.getFor([dossierId]).pipe(mapTo(file))),
tap(file => this._filesMapService.replace(file)),
);
}
@Validate()
setUnderApprovalFor(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, approverId: string) {
setUnderApprovalFor(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, asigneeId: string) {
const url = `${this._defaultModelPath}/under-approval/${dossierId}/bulk`;
return this._post<unknown>(fileIds, url, [{ key: 'approverId', value: approverId }]).pipe(switchMapTo(this.loadAll(dossierId)));
return this._post<unknown>(fileIds, url, [{ key: 'assigneeId', value: asigneeId }]).pipe(switchMapTo(this.loadAll(dossierId)));
}
/**
* Assigns a reviewer for a list of files.
*/
@Validate()
setReviewerFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string, reviewerId: string) {
const url = `${this._defaultModelPath}/set-reviewer/${dossierId}/bulk`;
return this._post<unknown>(filesIds, url, [{ key: 'reviewerId', value: reviewerId }]).pipe(switchMapTo(this.loadAll(dossierId)));
setReviewerFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string, assigneeId: string) {
const url = `${this._defaultModelPath}/under-review/${dossierId}/bulk`;
return this._post<unknown>(filesIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(switchMapTo(this.loadAll(dossierId)));
}
/**

View File

@ -19,20 +19,20 @@ export class PermissionsService {
}
canToggleAnalysis(file: File): boolean {
return this.isReviewerOrApprover(file) && (file.isUnassigned || file.isUnderReview || file.isUnderApproval);
return this.isReviewerOrApprover(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
}
canReanalyseFile(file: File): boolean {
return this.isReviewerOrApprover(file) || file.isUnassigned || (file.isError && file.isUnassigned);
return this.isReviewerOrApprover(file) || file.isNew || (file.isError && file.isNew);
}
isFileReviewer(file: File): boolean {
return file.currentReviewer === this._userService.currentUser.id;
return file.assignee === this._userService.currentUser.id;
}
canDeleteFile(file: File): boolean {
const dossier = this._getDossier(file);
return (this.isOwner(dossier) && !file.isApproved) || file.isUnassigned;
return (this.isOwner(dossier) && !file.isApproved) || file.isNew;
}
canAssignToSelf(file: File): boolean {
@ -43,8 +43,8 @@ export class PermissionsService {
if (precondition) {
if (
(file.isUnassigned || (file.isUnderReview && !this.isFileReviewer(file))) &&
(this.isApprover(dossier) || isTheOnlyReviewer || (this.isDossierReviewer(dossier) && file.isUnassigned))
(file.isNew || (file.isUnderReview && !this.isFileReviewer(file))) &&
(this.isApprover(dossier) || isTheOnlyReviewer || (this.isDossierReviewer(dossier) && file.isNew))
) {
return true;
}
@ -57,7 +57,7 @@ export class PermissionsService {
const precondition = !file.isProcessing && !file.isError && !file.isApproved && this.isApprover(dossier);
if (precondition) {
if ((file.isUnassigned || file.isUnderReview) && dossier.hasReviewers) {
if ((file.isNew || file.isUnderReview) && dossier.hasReviewers) {
return true;
}
if (file.isUnderApproval && dossier.approverIds.length > 1) {

View File

@ -1,7 +1,7 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dev-06.iqser.cloud/redaction-gateway-v1",
"API_URL": "https://dev-04.iqser.cloud/redaction-gateway-v1",
"APP_NAME": "RedactManager",
"AUTO_READ_TIME": 1.5,
"BACKEND_APP_VERSION": "4.4.40",
@ -17,7 +17,7 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://dev-06.iqser.cloud/auth/realms/redaction",
"OAUTH_URL": "https://dev-04.iqser.cloud/auth/realms/redaction",
"RECENT_PERIOD_IN_HOURS": 24,
"SELECTION_MODE": "structural"
}

View File

@ -733,7 +733,8 @@
"assigned-to-me": "Assigned to me",
"assigned-to-others": "Assigned to others",
"recent": "Recent ({hours} h)",
"unassigned": "Unassigned"
"unassigned": "Unassigned",
"new": "New"
},
"reanalyse-dossier": {
"error": "Failed to schedule files for reanalysis. Please try again.",
@ -1097,6 +1098,7 @@
"processing": "Processing...",
"reprocess": "Processing",
"unassigned": "Unassigned",
"new": "New",
"under-approval": "Under Approval",
"under-review": "Under Review",
"unprocessed": "Unprocessed"
@ -1187,6 +1189,7 @@
},
"initials-avatar": {
"unassigned": "Unassigned",
"new": "New",
"you": "You"
},
"justifications": "Justifications",

View File

@ -1,7 +1,7 @@
@use 'variables';
@use 'common-mixins';
.UNASSIGNED {
.NEW {
stroke: variables.$grey-5;
background-color: variables.$grey-5;
}

@ -1 +1 @@
Subproject commit 4621e95481fc48b682c46d75d94b5af66426368e
Subproject commit 5d6e3053fe6a92eb7bc9fe3e44934bd3c6f5943d

View File

@ -10,7 +10,7 @@ export class File extends Entity<IFile> implements IFile {
readonly analysisDuration?: number;
readonly analysisRequired: boolean;
readonly approvalDate?: string;
readonly currentReviewer?: string;
readonly assignee?: string;
readonly dictionaryVersion?: number;
readonly dossierDictionaryVersion?: number;
readonly dossierId: string;
@ -26,6 +26,7 @@ export class File extends Entity<IFile> implements IFile {
readonly lastOCRTime?: string;
readonly lastProcessed?: string;
readonly lastReviewer?: string;
readonly lastApprover?: string;
readonly lastUpdated?: string;
readonly lastUploaded?: string;
readonly legalBasisVersion?: number;
@ -43,7 +44,8 @@ export class File extends Entity<IFile> implements IFile {
readonly cacheIdentifier?: string;
readonly hintsOnly: boolean;
readonly hasNone: boolean;
readonly isUnassigned: boolean;
readonly isNew: boolean;
// readonly isUnassigned: boolean;
readonly isError: boolean;
readonly isProcessing: boolean;
readonly isApproved: boolean;
@ -61,7 +63,7 @@ export class File extends Entity<IFile> implements IFile {
this.analysisDuration = file.analysisDuration;
this.analysisRequired = !!file.analysisRequired && !file.excluded;
this.approvalDate = file.approvalDate;
this.currentReviewer = file.currentReviewer;
this.assignee = file.assignee;
this.dictionaryVersion = file.dictionaryVersion;
this.dossierDictionaryVersion = file.dossierDictionaryVersion;
this.dossierId = file.dossierId;
@ -77,6 +79,7 @@ export class File extends Entity<IFile> implements IFile {
this.lastOCRTime = file.lastOCRTime;
this.lastProcessed = file.lastProcessed;
this.lastReviewer = file.lastReviewer;
this.lastApprover = file.lastApprover;
this.lastUpdated = file.lastUpdated;
this.lastUploaded = file.lastUploaded;
this.legalBasisVersion = file.legalBasisVersion;
@ -99,12 +102,13 @@ export class File extends Entity<IFile> implements IFile {
this.isPending = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;
this.isProcessing = isProcessingStatuses.includes(this.processingStatus);
this.isApproved = this.workflowStatus === WorkflowFileStatuses.APPROVED;
this.isUnassigned = this.workflowStatus === WorkflowFileStatuses.UNASSIGNED;
this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW;
// this.isUnassigned = !this.assignee;
this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW;
this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL;
this.canBeApproved = !this.analysisRequired && !this.hasSuggestions;
this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0;
this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isUnassigned || this.isUnderReview || this.isUnderApproval);
this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isNew || this.isUnderReview || this.isUnderApproval);
if (!this.fileAttributes || !this.fileAttributes.attributeIdToValue) {
this.fileAttributes = { attributeIdToValue: {} };

View File

@ -28,7 +28,7 @@ export interface IFile {
/**
* The current reviewer's (if any) user id.
*/
readonly currentReviewer?: string;
readonly assignee?: string;
/**
* Shows which dictionary versions was used during the analysis.
*/
@ -102,6 +102,10 @@ export interface IFile {
* The last reviewer's (if any) user id.
*/
readonly lastReviewer?: string;
/**
* The last approver's (if any) user id.
*/
readonly lastApprover?: string;
/**
* Date and time when the file was last updated.
*/

View File

@ -2,6 +2,7 @@ import { List } from '@iqser/common-ui';
export const WorkflowFileStatuses = {
APPROVED: 'APPROVED',
NEW: 'NEW',
UNASSIGNED: 'UNASSIGNED',
UNDER_APPROVAL: 'UNDER_APPROVAL',
UNDER_REVIEW: 'UNDER_REVIEW',

View File

@ -6,6 +6,7 @@ export const ReportStatuses = {
OCR_PROCESSING: 'OCR_PROCESSING',
PROCESSING: 'PROCESSING',
REPROCESS: 'REPROCESS',
NEW: 'NEW',
UNASSIGNED: 'UNASSIGNED',
UNDER_APPROVAL: 'UNDER_APPROVAL',
UNDER_REVIEW: 'UNDER_REVIEW',

View File

@ -6,6 +6,7 @@ type Sorter = Record<WorkflowFileStatus, number> & {
};
export const StatusSorter: Sorter = {
NEW: 0,
UNASSIGNED: 1,
UNDER_REVIEW: 2,
UNDER_APPROVAL: 3,