From 1cf0f97121b96a0512cff3d9414b172c206190d7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 3 Jan 2023 16:15:59 +0100 Subject: [PATCH 01/28] 98211: Added advanced workflow models --- src/app/core/core.module.ts | 6 +++++ ...ing-reviewer-action-advanced-info.model.ts | 27 +++++++++++++++++++ .../reviewer-action-advanced-info.model.ts | 16 +++++++++++ ...ewer-action-advanced-info.resource-type.ts | 25 +++++++++++++++++ ...ect-reviewer-action-advanced-info.model.ts | 18 +++++++++++++ .../models/workflow-action-object.model.ts | 20 ++++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts create mode 100644 src/app/core/tasks/models/reviewer-action-advanced-info.model.ts create mode 100644 src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts create mode 100644 src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 8d8a614a89..df71f740da 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -162,6 +162,9 @@ import { SearchConfig } from './shared/search/search-filters/search-config.model import { SequenceService } from './shared/sequence.service'; import { GroupDataService } from './eperson/group-data.service'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; +import { RatingReviewerActionAdvancedInfo } from './tasks/models/rating-reviewer-action-advanced-info.model'; +import { ReviewerActionAdvancedInfo } from './tasks/models/reviewer-action-advanced-info.model'; +import { SelectReviewerActionAdvancedInfo } from './tasks/models/select-reviewer-action-advanced-info.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -332,6 +335,9 @@ export const models = Version, VersionHistory, WorkflowAction, + ReviewerActionAdvancedInfo, + RatingReviewerActionAdvancedInfo, + SelectReviewerActionAdvancedInfo, TemplateItem, Feature, Authorization, diff --git a/src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts b/src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts new file mode 100644 index 0000000000..2759d3edf2 --- /dev/null +++ b/src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts @@ -0,0 +1,27 @@ +import { typedObject } from '../../cache/builders/build-decorators'; +import { inheritSerialization, autoserialize } from 'cerialize'; +import { RATING_REVIEWER_ACTION_ADVANCED_INFO } from './reviewer-action-advanced-info.resource-type'; +import { ReviewerActionAdvancedInfo } from './reviewer-action-advanced-info.model'; + +/** + * A model class for a {@link RatingReviewerActionAdvancedInfo} + */ +@typedObject +@inheritSerialization(ReviewerActionAdvancedInfo) +export class RatingReviewerActionAdvancedInfo extends ReviewerActionAdvancedInfo { + + static type = RATING_REVIEWER_ACTION_ADVANCED_INFO; + + /** + * Whether the description is required. + */ + @autoserialize + descriptionRequired: boolean; + + /** + * The maximum value. + */ + @autoserialize + maxValue: number; + +} diff --git a/src/app/core/tasks/models/reviewer-action-advanced-info.model.ts b/src/app/core/tasks/models/reviewer-action-advanced-info.model.ts new file mode 100644 index 0000000000..dc423eec51 --- /dev/null +++ b/src/app/core/tasks/models/reviewer-action-advanced-info.model.ts @@ -0,0 +1,16 @@ +import { typedObject } from '../../cache/builders/build-decorators'; +import { autoserialize } from 'cerialize'; +import { REVIEWER_ACTION_ADVANCED_INFO } from './reviewer-action-advanced-info.resource-type'; + +/** + * A model class for a {@link ReviewerActionAdvancedInfo} + */ +@typedObject +export class ReviewerActionAdvancedInfo { + + static type = REVIEWER_ACTION_ADVANCED_INFO; + + @autoserialize + id: string; + +} diff --git a/src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts b/src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts new file mode 100644 index 0000000000..876f37495e --- /dev/null +++ b/src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts @@ -0,0 +1,25 @@ +import { ResourceType } from '../../shared/resource-type'; + +/** + * The resource type for {@link ReviewerActionAdvancedInfo} + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const REVIEWER_ACTION_ADVANCED_INFO = new ResourceType('revieweraction'); + +/** + * The resource type for {@link RatingReviewerActionAdvancedInfo} + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const RATING_REVIEWER_ACTION_ADVANCED_INFO = new ResourceType('ratingrevieweraction'); + +/** + * The resource type for {@link SelectReviewerActionAdvancedInfo} + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SELECT_REVIEWER_ACTION_ADVANCED_INFO = new ResourceType('selectrevieweraction'); diff --git a/src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts b/src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts new file mode 100644 index 0000000000..cd8812f6af --- /dev/null +++ b/src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts @@ -0,0 +1,18 @@ +import { typedObject } from '../../cache/builders/build-decorators'; +import { inheritSerialization, autoserialize } from 'cerialize'; +import { SELECT_REVIEWER_ACTION_ADVANCED_INFO } from './reviewer-action-advanced-info.resource-type'; +import { ReviewerActionAdvancedInfo } from './reviewer-action-advanced-info.model'; + +/** + * A model class for a {@link SelectReviewerActionAdvancedInfo} + */ +@typedObject +@inheritSerialization(ReviewerActionAdvancedInfo) +export class SelectReviewerActionAdvancedInfo extends ReviewerActionAdvancedInfo { + + static type = SELECT_REVIEWER_ACTION_ADVANCED_INFO; + + @autoserialize + group: string; + +} diff --git a/src/app/core/tasks/models/workflow-action-object.model.ts b/src/app/core/tasks/models/workflow-action-object.model.ts index 720d817859..6fa103bbea 100644 --- a/src/app/core/tasks/models/workflow-action-object.model.ts +++ b/src/app/core/tasks/models/workflow-action-object.model.ts @@ -2,6 +2,7 @@ import { inheritSerialization, autoserialize } from 'cerialize'; import { typedObject } from '../../cache/builders/build-decorators'; import { DSpaceObject } from '../../shared/dspace-object.model'; import { WORKFLOW_ACTION } from './workflow-action-object.resource-type'; +import { ReviewerActionAdvancedInfo } from './reviewer-action-advanced-info.model'; /** * A model class for a WorkflowAction @@ -22,4 +23,23 @@ export class WorkflowAction extends DSpaceObject { */ @autoserialize options: string[]; + + /** + * Whether this action has advanced options + */ + @autoserialize + advanced: boolean; + + /** + * The advanced options that the user can select at this action + */ + @autoserialize + advancedOptions: string[]; + + /** + * The advanced info required by the advanced options + */ + @autoserialize + advancedInfo: ReviewerActionAdvancedInfo[]; + } From 0fb9970734f0d38acaaa5d10d48660775d4f15fb Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 3 Jan 2023 20:26:50 +0100 Subject: [PATCH 02/28] 98211: Added button to my workflow task page --- ...claimed-task-actions-abstract.component.ts | 16 ++++++ ...task-action-rating-reviewer.component.html | 4 ++ ...task-action-rating-reviewer.component.scss | 0 ...k-action-rating-reviewer.component.spec.ts | 27 +++++++++ ...d-task-action-rating-reviewer.component.ts | 55 ++++++++++++++++++ ...task-action-select-reviewer.component.html | 4 ++ ...task-action-select-reviewer.component.scss | 0 ...k-action-select-reviewer.component.spec.ts | 27 +++++++++ ...d-task-action-select-reviewer.component.ts | 57 +++++++++++++++++++ src/app/shared/shared.module.ts | 9 ++- .../workflowitems-edit-page-routing-paths.ts | 10 ++++ .../workflowitems-edit-page-routing.module.ts | 24 +++++++- src/assets/i18n/en.json5 | 15 +++++ 13 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.scss create mode 100644 src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.scss create mode 100644 src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts new file mode 100644 index 0000000000..a99639bd17 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { ClaimedTaskActionsAbstractComponent } from './claimed-task-actions-abstract.component'; + +/** + * Abstract component for rendering an advanced claimed task's action + * To create a child-component for a new option: + * - Set the "option" of the component + * - Add a @rendersWorkflowTaskOption annotation to your component providing the same enum value + * - Optionally overwrite createBody if the request body requires more than just the option + */ +@Component({ + selector: 'ds-advanced-claimed-task-action-abstract', + template: '' +}) +export abstract class AdvancedClaimedTaskActionsAbstractComponent extends ClaimedTaskActionsAbstractComponent { +} diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html new file mode 100644 index 0000000000..8ed7b18640 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.scss b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts new file mode 100644 index 0000000000..f99c0558c6 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts @@ -0,0 +1,27 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + AdvancedClaimedTaskActionRatingReviewerComponent +} from './advanced-claimed-task-action-rating-reviewer.component'; + +describe('AdvancedClaimedTaskActionsRatingReviewerComponent', () => { + let component: AdvancedClaimedTaskActionRatingReviewerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AdvancedClaimedTaskActionRatingReviewerComponent, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedClaimedTaskActionRatingReviewerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts new file mode 100644 index 0000000000..0fb660b6e6 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts @@ -0,0 +1,55 @@ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { SearchService } from '../../../../core/shared/search/search.service'; +import { RequestService } from '../../../../core/data/request.service'; +import { + getWorkflowRatingReviewerRoute +} from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; +import { + AdvancedClaimedTaskActionsAbstractComponent +} from '../abstract/advanced-claimed-task-actions-abstract.component'; + +export const WORKFLOW_ADVANCED_TASK_OPTION_RATING_REVIEWER = 'submit_rating_reviewer'; + +@Component({ + selector: 'ds-advanced-claimed-task-action-rating-reviewer', + templateUrl: './advanced-claimed-task-action-rating-reviewer.component.html', + styleUrls: ['./advanced-claimed-task-action-rating-reviewer.component.scss'] +}) +export class AdvancedClaimedTaskActionRatingReviewerComponent extends AdvancedClaimedTaskActionsAbstractComponent { + + /** + * This component represents the advanced select option + */ + option = WORKFLOW_ADVANCED_TASK_OPTION_RATING_REVIEWER; + + /** + * Route to the workflow's task page + */ + workflowTaskPageRoute: string; + + constructor( + protected injector: Injector, + protected router: Router, + protected notificationsService: NotificationsService, + protected translate: TranslateService, + protected searchService: SearchService, + protected requestService: RequestService, + ) { + super(injector, router, notificationsService, translate, searchService, requestService); + } + + ngOnInit(): void { + this.initPageRoute(); + } + + /** + * Initialise the route to the rating reviewer's page + */ + initPageRoute() { + this.workflowTaskPageRoute = getWorkflowRatingReviewerRoute(this.object.id); + } + +} diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html new file mode 100644 index 0000000000..8ed7b18640 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.scss b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts new file mode 100644 index 0000000000..24b8501f55 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts @@ -0,0 +1,27 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + AdvancedClaimedTaskActionSelectReviewerComponent +} from './advanced-claimed-task-action-select-reviewer.component'; + +describe('AdvancedClaimedTaskActionsSelectReviewerComponent', () => { + let component: AdvancedClaimedTaskActionSelectReviewerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AdvancedClaimedTaskActionSelectReviewerComponent, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedClaimedTaskActionSelectReviewerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts new file mode 100644 index 0000000000..4ce3ec224a --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts @@ -0,0 +1,57 @@ +import { Component, Injector } from '@angular/core'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { + AdvancedClaimedTaskActionsAbstractComponent +} from '../abstract/advanced-claimed-task-actions-abstract.component'; +import { Router } from '@angular/router'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { SearchService } from '../../../../core/shared/search/search.service'; +import { RequestService } from '../../../../core/data/request.service'; +import { + getWorkflowSelectReviewerRoute +} from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; + +export const WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; + +@rendersWorkflowTaskOption(WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER) +@Component({ + selector: 'ds-advanced-claimed-task-action-select-reviewer', + templateUrl: './advanced-claimed-task-action-select-reviewer.component.html', + styleUrls: ['./advanced-claimed-task-action-select-reviewer.component.scss'] +}) +export class AdvancedClaimedTaskActionSelectReviewerComponent extends AdvancedClaimedTaskActionsAbstractComponent { + + /** + * This component represents the advanced select option + */ + option = WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER; + + /** + * Route to the workflow's task page + */ + workflowTaskPageRoute: string; + + constructor( + protected injector: Injector, + protected router: Router, + protected notificationsService: NotificationsService, + protected translate: TranslateService, + protected searchService: SearchService, + protected requestService: RequestService, + ) { + super(injector, router, notificationsService, translate, searchService, requestService); + } + + ngOnInit(): void { + this.initPageRoute(); + } + + /** + * Initialise the route to the select reviewer's page + */ + initPageRoute() { + this.workflowTaskPageRoute = getWorkflowSelectReviewerRoute(this.object.id); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 715ee66a99..9a859dff9b 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,6 +177,10 @@ import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/ import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component'; import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; +import { AdvancedClaimedTaskActionSelectReviewerComponent } from './mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component'; +import { + AdvancedClaimedTaskActionRatingReviewerComponent +} from './mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -346,6 +350,8 @@ const COMPONENTS = [ CommunitySidebarSearchListElementComponent, SearchNavbarComponent, ScopeSelectorModalComponent, + AdvancedClaimedTaskActionSelectReviewerComponent, + AdvancedClaimedTaskActionRatingReviewerComponent, ]; const ENTRY_COMPONENTS = [ @@ -402,6 +408,8 @@ const ENTRY_COMPONENTS = [ OnClickMenuItemComponent, TextMenuItemComponent, ScopeSelectorModalComponent, + AdvancedClaimedTaskActionSelectReviewerComponent, + AdvancedClaimedTaskActionRatingReviewerComponent, ]; const SHARED_ITEM_PAGE_COMPONENTS = [ @@ -436,7 +444,6 @@ const DIRECTIVES = [ ClaimedTaskActionsDirective, FileValueAccessorDirective, FileValidator, - ClaimedTaskActionsDirective, NgForTrackByIdDirective, MetadataFieldValidator, HoverClassDirective diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts index e2d969a872..c39bd1fc48 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts @@ -1,5 +1,6 @@ import { URLCombiner } from '../core/url-combiner/url-combiner'; import { getWorkflowItemModuleRoute } from '../app-routing-paths'; +import { RATING_REVIEWER_ACTION_ADVANCED_INFO } from '../core/tasks/models/reviewer-action-advanced-info.resource-type'; export function getWorkflowItemPageRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId).toString(); @@ -20,7 +21,16 @@ export function getWorkflowItemSendBackRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_SEND_BACK_PATH).toString(); } +export function getWorkflowSelectReviewerRoute(wfiId: string) { + return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_SELECT_REVIEWER_PATH).toString(); +} +export function getWorkflowRatingReviewerRoute(wfiId: string) { + return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_RATING_REVIEWER_PATH).toString(); +} + export const WORKFLOW_ITEM_EDIT_PATH = 'edit'; export const WORKFLOW_ITEM_DELETE_PATH = 'delete'; export const WORKFLOW_ITEM_VIEW_PATH = 'view'; export const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback'; +export const WORKFLOW_SELECT_REVIEWER_PATH = 'selectreviewer'; +export const WORKFLOW_RATING_REVIEWER_PATH = 'ratingreviewer'; diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts index 9c24bacb98..722e8b3768 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts @@ -7,7 +7,9 @@ import { WORKFLOW_ITEM_DELETE_PATH, WORKFLOW_ITEM_EDIT_PATH, WORKFLOW_ITEM_SEND_BACK_PATH, - WORKFLOW_ITEM_VIEW_PATH + WORKFLOW_ITEM_VIEW_PATH, + WORKFLOW_SELECT_REVIEWER_PATH, + WORKFLOW_RATING_REVIEWER_PATH, } from './workflowitems-edit-page-routing-paths'; import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component'; import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component'; @@ -59,7 +61,25 @@ import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item- breadcrumb: I18nBreadcrumbResolver }, data: { title: 'workflow-item.send-back.title', breadcrumbKey: 'workflow-item.edit' } - } + }, + { + canActivate: [AuthenticatedGuard], + path: WORKFLOW_SELECT_REVIEWER_PATH, + component: ThemedWorkflowItemSendBackComponent, + resolve: { + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'workflow-item.select-reviewer.title', breadcrumbKey: 'workflow-item.edit' } + }, + { + canActivate: [AuthenticatedGuard], + path: WORKFLOW_RATING_REVIEWER_PATH, + component: ThemedWorkflowItemSendBackComponent, + resolve: { + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'workflow-item.rating-reviewer.title', breadcrumbKey: 'workflow-item.edit' } + }, ] }] ) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index f742273edb..f2b6a6459a 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4003,6 +4003,15 @@ "submission.workflow.generic.view-help": "Select this option to view the item's metadata.", + "submission.workflow.generic.submit_select_reviewer": "Select Reviewer", + + "submission.workflow.generic.submit_select_reviewer-help": "", + + + "submission.workflow.generic.submit_rating_reviewer": "Rating Reviewer", + + "submission.workflow.generic.submit_rating_reviewer-help": "", + "submission.workflow.tasks.claimed.approve": "Approve", @@ -4156,6 +4165,12 @@ "workflow-item.view.breadcrumbs": "Workflow View", + "workflow-item.select-reviewer.title": "Select Reviewer", + + + "workflow-item.rating-reviewer.title": "Rating Reviewer", + + "idle-modal.header": "Session will expire soon", "idle-modal.info": "For security reasons, user sessions expire after {{ timeToExpire }} minutes of inactivity. Your session will expire soon. Would you like to extend it or log out?", From 947658dd08e7aacf1d5eec880427e17ed1fcadff Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 4 Jan 2023 17:01:48 +0100 Subject: [PATCH 03/28] 98211: Created advanced workflow task pages --- ...nced-workflow-action-abstract.component.ts | 8 +++ ...task-action-rating-reviewer.component.html | 2 +- ...d-task-action-rating-reviewer.component.ts | 13 +++-- ...task-action-select-reviewer.component.html | 2 +- ...d-task-action-select-reviewer.component.ts | 9 ++-- .../claimed-task-actions-decorator.ts | 29 ++++++++-- ...vanced-workflow-action-page.component.html | 5 ++ ...vanced-workflow-action-page.component.scss | 0 ...ced-workflow-action-page.component.spec.ts | 25 +++++++++ ...advanced-workflow-action-page.component.ts | 22 ++++++++ ...flow-action-rating-reviewer.component.html | 1 + ...flow-action-rating-reviewer.component.scss | 0 ...w-action-rating-reviewer.component.spec.ts | 25 +++++++++ ...rkflow-action-rating-reviewer.component.ts | 15 ++++++ ...flow-action-select-reviewer.component.html | 1 + ...flow-action-select-reviewer.component.scss | 0 ...w-action-select-reviewer.component.spec.ts | 25 +++++++++ ...rkflow-action-select-reviewer.component.ts | 16 ++++++ .../advanced-workflow-action.component.html | 1 + .../advanced-workflow-action.component.scss | 0 ...advanced-workflow-action.component.spec.ts | 25 +++++++++ .../advanced-workflow-action.component.ts | 29 ++++++++++ ...advanced-claimed-task-actions.directive.ts | 16 ++++++ ...ced-workflow-actions-loader.component.html | 1 + ...ced-workflow-actions-loader.component.scss | 0 ...-workflow-actions-loader.component.spec.ts | 25 +++++++++ ...anced-workflow-actions-loader.component.ts | 54 +++++++++++++++++++ .../workflowitems-edit-page-routing-paths.ts | 11 ++-- .../workflowitems-edit-page-routing.module.ts | 21 +++----- .../workflowitems-edit-page.module.ts | 18 ++++++- src/assets/i18n/en.json5 | 6 +-- src/themes/custom/theme.module.ts | 3 ++ 32 files changed, 368 insertions(+), 40 deletions(-) create mode 100644 src/app/shared/mydspace-actions/claimed-task/abstract/advanced-workflow-action-abstract.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.html create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.scss create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.scss create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.scss create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.scss create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-workflow-action-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-workflow-action-abstract.component.ts new file mode 100644 index 0000000000..be6935d900 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-workflow-action-abstract.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-advanced-workflow-action-abstract', + template: '', +}) +export abstract class AdvancedWorkflowActionAbstractComponent { +} diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html index 8ed7b18640..84d3aa7cc7 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html @@ -1,4 +1,4 @@ diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts index 0fb660b6e6..bfa676b30e 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts @@ -4,12 +4,13 @@ import { NotificationsService } from '../../../notifications/notifications.servi import { TranslateService } from '@ngx-translate/core'; import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; -import { - getWorkflowRatingReviewerRoute -} from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; +import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { AdvancedClaimedTaskActionsAbstractComponent } from '../abstract/advanced-claimed-task-actions-abstract.component'; +import { + ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER +} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; export const WORKFLOW_ADVANCED_TASK_OPTION_RATING_REVIEWER = 'submit_rating_reviewer'; @@ -25,6 +26,8 @@ export class AdvancedClaimedTaskActionRatingReviewerComponent extends AdvancedCl */ option = WORKFLOW_ADVANCED_TASK_OPTION_RATING_REVIEWER; + workflowType = ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER; + /** * Route to the workflow's task page */ @@ -48,8 +51,8 @@ export class AdvancedClaimedTaskActionRatingReviewerComponent extends AdvancedCl /** * Initialise the route to the rating reviewer's page */ - initPageRoute() { - this.workflowTaskPageRoute = getWorkflowRatingReviewerRoute(this.object.id); + initPageRoute(): void { + this.workflowTaskPageRoute = getAdvancedWorkflowRoute(this.object.id); } } diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html index 8ed7b18640..84d3aa7cc7 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html @@ -1,4 +1,4 @@ diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts index 4ce3ec224a..6cb6c68816 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts @@ -8,9 +8,10 @@ import { NotificationsService } from '../../../notifications/notifications.servi import { TranslateService } from '@ngx-translate/core'; import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; +import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { - getWorkflowSelectReviewerRoute -} from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; + ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER +} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; export const WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; @@ -27,6 +28,8 @@ export class AdvancedClaimedTaskActionSelectReviewerComponent extends AdvancedCl */ option = WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER; + workflowType = ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; + /** * Route to the workflow's task page */ @@ -51,7 +54,7 @@ export class AdvancedClaimedTaskActionSelectReviewerComponent extends AdvancedCl * Initialise the route to the select reviewer's page */ initPageRoute() { - this.workflowTaskPageRoute = getWorkflowSelectReviewerRoute(this.object.id); + this.workflowTaskPageRoute = getAdvancedWorkflowRoute(this.object.id); } } diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts index a115c4e5b8..8e4dede9bb 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts @@ -1,23 +1,44 @@ import { hasNoValue } from '../../../empty.util'; -const map = new Map(); +const workflowOptions = new Map(); +const advancedWorkflowOptions = new Map(); /** * Decorator used for rendering ClaimedTaskActions pages by option type */ export function rendersWorkflowTaskOption(option: string) { return function decorator(component: any) { - if (hasNoValue(map.get(option))) { - map.set(option, component); + if (hasNoValue(workflowOptions.get(option))) { + workflowOptions.set(option, component); } else { throw new Error(`There can't be more than one component to render ClaimedTaskActions for option "${option}"`); } }; } +/** + * Decorator used for rendering AdvancedClaimedTaskActions pages by option type + */ +export function rendersAdvancedWorkflowTaskOption(option: string) { + return function decorator(component: any) { + if (hasNoValue(advancedWorkflowOptions.get(option))) { + advancedWorkflowOptions.set(option, component); + } else { + throw new Error(`There can't be more than one component to render AdvancedClaimedTaskActions for option "${option}"`); + } + }; +} + /** * Get the component used for rendering a ClaimedTaskActions page by option type */ export function getComponentByWorkflowTaskOption(option: string) { - return map.get(option); + return workflowOptions.get(option); +} + +/** + * Get the component used for rendering a AdvancedClaimedTaskActions page by option type + */ +export function getAdvancedComponentByWorkflowTaskOption(option: string) { + return advancedWorkflowOptions.get(option); } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.html new file mode 100644 index 0000000000..7b0528d906 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.html @@ -0,0 +1,5 @@ +
+

{{'workflow-item.' + type + '.header' | translate}}

+ + +
diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts new file mode 100644 index 0000000000..ac713fa443 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action-page.component'; + +describe('AdvancedWorkflowActionPageComponent', () => { + let component: AdvancedWorkflowActionPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AdvancedWorkflowActionPageComponent, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedWorkflowActionPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts new file mode 100644 index 0000000000..e6c7da7d59 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts @@ -0,0 +1,22 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'ds-advanced-workflow-action-page', + templateUrl: './advanced-workflow-action-page.component.html', + styleUrls: ['./advanced-workflow-action-page.component.scss'] +}) +export class AdvancedWorkflowActionPageComponent implements OnInit { + + public type: string; + + constructor( + protected route: ActivatedRoute, + ) { + } + + ngOnInit(): void { + this.type = this.route.snapshot.queryParams.workflow; + } + +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html new file mode 100644 index 0000000000..1b26ba3599 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html @@ -0,0 +1 @@ +

advanced-workflow-action-rating-reviewer works!

diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts new file mode 100644 index 0000000000..c08e3c0a78 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvancedWorkflowActionRatingReviewerComponent } from './advanced-workflow-action-rating-reviewer.component'; + +describe('AdvancedWorkflowActionRatingReviewerComponent', () => { + let component: AdvancedWorkflowActionRatingReviewerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdvancedWorkflowActionRatingReviewerComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedWorkflowActionRatingReviewerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts new file mode 100644 index 0000000000..4e0185401d --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { + rendersAdvancedWorkflowTaskOption +} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; + +export const ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER = 'ratingrevieweraction'; + +@rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER) +@Component({ + selector: 'ds-advanced-workflow-action-rating-reviewer', + templateUrl: './advanced-workflow-action-rating-reviewer.component.html', + styleUrls: ['./advanced-workflow-action-rating-reviewer.component.scss'] +}) +export class AdvancedWorkflowActionRatingReviewerComponent { +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html new file mode 100644 index 0000000000..4380e45594 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html @@ -0,0 +1 @@ +advancedInfo: {{ (workflowAction | async)?.advancedInfo | json }} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts new file mode 100644 index 0000000000..b4f420774b --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvancedWorkflowActionSelectReviewerComponent } from './advanced-workflow-action-select-reviewer.component'; + +describe('AdvancedWorkflowActionSelectReviewerComponent', () => { + let component: AdvancedWorkflowActionSelectReviewerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdvancedWorkflowActionSelectReviewerComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedWorkflowActionSelectReviewerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts new file mode 100644 index 0000000000..8ba2850202 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { + rendersAdvancedWorkflowTaskOption +} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; +import { AdvancedWorkflowActionComponent } from '../advanced-workflow-action/advanced-workflow-action.component'; + +export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; + +@rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER) +@Component({ + selector: 'ds-advanced-workflow-action-select-reviewer', + templateUrl: './advanced-workflow-action-select-reviewer.component.html', + styleUrls: ['./advanced-workflow-action-select-reviewer.component.scss'], +}) +export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkflowActionComponent { +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html new file mode 100644 index 0000000000..4fbedb9f3e --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html @@ -0,0 +1 @@ +

advanced-workflow-action works!

diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts new file mode 100644 index 0000000000..974dd4c257 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvancedWorkflowActionComponent } from './advanced-workflow-action.component'; + +describe('AdvancedWorkflowActionComponent', () => { + let component: AdvancedWorkflowActionComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdvancedWorkflowActionComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedWorkflowActionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts new file mode 100644 index 0000000000..0554e8463f --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit } from '@angular/core'; +import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; + +@Component({ + selector: 'ds-advanced-workflow-action', + templateUrl: './advanced-workflow-action.component.html', + styleUrls: ['./advanced-workflow-action.component.scss'] +}) +export class AdvancedWorkflowActionComponent implements OnInit { + + workflowAction: Observable; + + constructor( + protected route: ActivatedRoute, + protected workflowActionService: WorkflowActionDataService, + ) { + } + + ngOnInit(): void { + this.workflowAction = this.workflowActionService.findById(this.route.snapshot.queryParams.workflow).pipe( + getFirstSucceededRemoteDataPayload(), + ); + } + +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts new file mode 100644 index 0000000000..b2ed49b502 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts @@ -0,0 +1,16 @@ +import { Directive, ViewContainerRef } from '@angular/core'; + +@Directive({ + selector: '[dsAdvancedClaimedTaskActions]', +}) +/** + * Directive used as a hook to know where to inject the dynamic Advanced Claimed Task Actions component + */ +export class AdvancedClaimedTaskActionsDirective { + + constructor( + public viewContainerRef: ViewContainerRef, + ) { + } + +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html new file mode 100644 index 0000000000..2374ed7913 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html @@ -0,0 +1 @@ + diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts new file mode 100644 index 0000000000..49cf92b171 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AdvancedWorkflowActionsLoaderComponent } from './advanced-workflow-actions-loader.component'; + +describe('AdvancedWorkflowActionsLoaderComponent', () => { + let component: AdvancedWorkflowActionsLoaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AdvancedWorkflowActionsLoaderComponent, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedWorkflowActionsLoaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts new file mode 100644 index 0000000000..ac6d2f171d --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts @@ -0,0 +1,54 @@ +import { Component, Input, ViewChild, ComponentFactoryResolver, OnInit } from '@angular/core'; +import { hasValue } from '../../../shared/empty.util'; +import { + getAdvancedComponentByWorkflowTaskOption +} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; +import { AdvancedClaimedTaskActionsDirective } from './advanced-claimed-task-actions.directive'; +import { Router } from '@angular/router'; +import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; + +@Component({ + selector: 'ds-advanced-workflow-actions-loader', + templateUrl: './advanced-workflow-actions-loader.component.html', + styleUrls: ['./advanced-workflow-actions-loader.component.scss'], +}) +export class AdvancedWorkflowActionsLoaderComponent implements OnInit { + + /** + * The name of the type to render + * Passed on to the decorator to fetch the relevant component for this option + */ + @Input() type: string; + + /** + * Directive to determine where the dynamic child component is located + */ + @ViewChild(AdvancedClaimedTaskActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedClaimedTaskActionsDirective; + + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private router: Router, + ) { + } + + /** + * Fetch, create and initialize the relevant component + */ + ngOnInit(): void { + const comp = this.getComponentByWorkflowTaskOption(this.type); + if (hasValue(comp)) { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); + + const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; + viewContainerRef.clear(); + viewContainerRef.createComponent(componentFactory); + } else { + void this.router.navigate([PAGE_NOT_FOUND_PATH]); + } + } + + getComponentByWorkflowTaskOption(option: string) { + return getAdvancedComponentByWorkflowTaskOption(option); + } + +} diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts index c39bd1fc48..ece61f0321 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts @@ -1,6 +1,5 @@ import { URLCombiner } from '../core/url-combiner/url-combiner'; import { getWorkflowItemModuleRoute } from '../app-routing-paths'; -import { RATING_REVIEWER_ACTION_ADVANCED_INFO } from '../core/tasks/models/reviewer-action-advanced-info.resource-type'; export function getWorkflowItemPageRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId).toString(); @@ -21,16 +20,12 @@ export function getWorkflowItemSendBackRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_SEND_BACK_PATH).toString(); } -export function getWorkflowSelectReviewerRoute(wfiId: string) { - return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_SELECT_REVIEWER_PATH).toString(); -} -export function getWorkflowRatingReviewerRoute(wfiId: string) { - return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_RATING_REVIEWER_PATH).toString(); +export function getAdvancedWorkflowRoute(wfiId: string) { + return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, ADVANCED_WORKFLOW_PATH).toString(); } export const WORKFLOW_ITEM_EDIT_PATH = 'edit'; export const WORKFLOW_ITEM_DELETE_PATH = 'delete'; export const WORKFLOW_ITEM_VIEW_PATH = 'view'; export const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback'; -export const WORKFLOW_SELECT_REVIEWER_PATH = 'selectreviewer'; -export const WORKFLOW_RATING_REVIEWER_PATH = 'ratingreviewer'; +export const ADVANCED_WORKFLOW_PATH = 'advanced'; diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts index 722e8b3768..06536d5816 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts @@ -8,8 +8,7 @@ import { WORKFLOW_ITEM_EDIT_PATH, WORKFLOW_ITEM_SEND_BACK_PATH, WORKFLOW_ITEM_VIEW_PATH, - WORKFLOW_SELECT_REVIEWER_PATH, - WORKFLOW_RATING_REVIEWER_PATH, + ADVANCED_WORKFLOW_PATH, } from './workflowitems-edit-page-routing-paths'; import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component'; import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component'; @@ -17,6 +16,9 @@ import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/t import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; import { ItemFromWorkflowResolver } from './item-from-workflow.resolver'; import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-page.component'; +import { + AdvancedWorkflowActionPageComponent +} from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; @NgModule({ imports: [ @@ -64,21 +66,12 @@ import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item- }, { canActivate: [AuthenticatedGuard], - path: WORKFLOW_SELECT_REVIEWER_PATH, - component: ThemedWorkflowItemSendBackComponent, + path: ADVANCED_WORKFLOW_PATH, + component: AdvancedWorkflowActionPageComponent, resolve: { breadcrumb: I18nBreadcrumbResolver }, - data: { title: 'workflow-item.select-reviewer.title', breadcrumbKey: 'workflow-item.edit' } - }, - { - canActivate: [AuthenticatedGuard], - path: WORKFLOW_RATING_REVIEWER_PATH, - component: ThemedWorkflowItemSendBackComponent, - resolve: { - breadcrumb: I18nBreadcrumbResolver - }, - data: { title: 'workflow-item.rating-reviewer.title', breadcrumbKey: 'workflow-item.edit' } + data: { title: 'workflow-item.advanced.title', breadcrumbKey: 'workflow-item.edit' } }, ] }] diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts index e99a3ca958..2e1ae4c820 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -9,6 +9,16 @@ import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component'; import { StatisticsModule } from '../statistics/statistics.module'; import { ItemPageModule } from '../item-page/item-page.module'; +import { + AdvancedWorkflowActionsLoaderComponent +} from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component'; +import { AdvancedWorkflowActionRatingReviewerComponent } from './advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; +import { AdvancedWorkflowActionSelectReviewerComponent } from './advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; +import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; +import { + AdvancedClaimedTaskActionsDirective +} from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive'; +import { AdvancedWorkflowActionComponent } from './advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component'; @NgModule({ imports: [ @@ -23,7 +33,13 @@ import { ItemPageModule } from '../item-page/item-page.module'; WorkflowItemDeleteComponent, ThemedWorkflowItemDeleteComponent, WorkflowItemSendBackComponent, - ThemedWorkflowItemSendBackComponent + ThemedWorkflowItemSendBackComponent, + AdvancedWorkflowActionsLoaderComponent, + AdvancedWorkflowActionRatingReviewerComponent, + AdvancedWorkflowActionSelectReviewerComponent, + AdvancedWorkflowActionPageComponent, + AdvancedClaimedTaskActionsDirective, + AdvancedWorkflowActionComponent, ] }) /** diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index f2b6a6459a..5f3ac8e069 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4164,11 +4164,11 @@ "workflow-item.view.breadcrumbs": "Workflow View", + "workflow-item.advanced.title": "Advanced workflow", - "workflow-item.select-reviewer.title": "Select Reviewer", + "workflow-item.selectrevieweraction.header": "Select Reviewer", - - "workflow-item.rating-reviewer.title": "Rating Reviewer", + "workflow-item.ratingrevieweraction.header": "Rating Reviewer", "idle-modal.header": "Session will expire soon", diff --git a/src/themes/custom/theme.module.ts b/src/themes/custom/theme.module.ts index e2e97b9087..98b8c553af 100644 --- a/src/themes/custom/theme.module.ts +++ b/src/themes/custom/theme.module.ts @@ -178,6 +178,9 @@ const DECLARATIONS = [ ResourcePoliciesModule, ComcolModule ], + exports: [ + FullItemPageComponent + ], declarations: DECLARATIONS }) From 1a8320c4f780fd95442c79a381a3fb756ad94e25 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 6 Jan 2023 14:16:30 +0100 Subject: [PATCH 04/28] 98211: Added ModifyItemOverviewComponent & confirm action button to AdvancedWorkflowActionSelectReviewerComponent --- ...d-task-action-select-reviewer.component.ts | 5 +-- ...flow-action-select-reviewer.component.html | 17 +++++++- ...rkflow-action-select-reviewer.component.ts | 12 ++++++ .../advanced-workflow-action.component.html | 1 - .../advanced-workflow-action.component.scss | 0 .../advanced-workflow-action.component.ts | 43 ++++++++++++++++--- .../workflowitems-edit-page.module.ts | 2 - src/assets/i18n/en.json5 | 16 +++++++ 8 files changed, 82 insertions(+), 14 deletions(-) delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.scss diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts index 6cb6c68816..fb45e911c5 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts @@ -10,11 +10,8 @@ import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { - ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER + ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER } from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; - -export const WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; - @rendersWorkflowTaskOption(WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER) @Component({ selector: 'ds-advanced-claimed-task-action-select-reviewer', diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html index 4380e45594..64e5638de6 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html @@ -1 +1,16 @@ -advancedInfo: {{ (workflowAction | async)?.advancedInfo | json }} +
+ advancedInfo: {{ (workflowAction$ | async)?.advancedInfo | json }} + + + + +
+ + +
+
diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index 8ba2850202..df867d5595 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -4,6 +4,7 @@ import { } from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; import { AdvancedWorkflowActionComponent } from '../advanced-workflow-action/advanced-workflow-action.component'; +export const WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; @rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER) @@ -13,4 +14,15 @@ export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; styleUrls: ['./advanced-workflow-action-select-reviewer.component.scss'], }) export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkflowActionComponent { + + getType(): string { + return ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; + } + + createBody(): any { + return { + [WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER]: true, + }; + } + } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html deleted file mode 100644 index 4fbedb9f3e..0000000000 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.html +++ /dev/null @@ -1 +0,0 @@ -

advanced-workflow-action works!

diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts index 0554e8463f..bef619ce45 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts @@ -1,29 +1,60 @@ import { Component, OnInit } from '@angular/core'; import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; +import { WorkflowItemActionPageComponent } from '../../workflow-item-action-page.component'; +import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { RouteService } from '../../../core/services/route.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; +import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; +import { map } from 'rxjs/operators'; +import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; @Component({ selector: 'ds-advanced-workflow-action', - templateUrl: './advanced-workflow-action.component.html', - styleUrls: ['./advanced-workflow-action.component.scss'] + template: '', }) -export class AdvancedWorkflowActionComponent implements OnInit { +export abstract class AdvancedWorkflowActionComponent extends WorkflowItemActionPageComponent implements OnInit { - workflowAction: Observable; + workflowAction$: Observable; constructor( protected route: ActivatedRoute, + protected workflowItemService: WorkflowItemDataService, + protected router: Router, + protected routeService: RouteService, + protected notificationsService: NotificationsService, + protected translationService: TranslateService, protected workflowActionService: WorkflowActionDataService, + protected claimedTaskDataService: ClaimedTaskDataService, ) { + super(route, workflowItemService, router, routeService, notificationsService, translationService); } ngOnInit(): void { - this.workflowAction = this.workflowActionService.findById(this.route.snapshot.queryParams.workflow).pipe( + super.ngOnInit(); + this.workflowAction$ = this.workflowActionService.findById(this.route.snapshot.queryParams.workflow).pipe( getFirstSucceededRemoteDataPayload(), ); } + /** + * Submits the task with the given {@link createBody}. + * + * @param id + */ + sendRequest(id: string): Observable { + return this.claimedTaskDataService.submitTask(id, this.createBody()).pipe( + map((processTaskResponse: ProcessTaskResponse) => processTaskResponse.hasSucceeded), + ); + } + + /** + * The body that needs to be passed to the {@link ClaimedTaskDataService}.submitTask(). + */ + abstract createBody(): any; + } diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts index 2e1ae4c820..b230ffe65c 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -18,7 +18,6 @@ import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/ import { AdvancedClaimedTaskActionsDirective } from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive'; -import { AdvancedWorkflowActionComponent } from './advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component'; @NgModule({ imports: [ @@ -39,7 +38,6 @@ import { AdvancedWorkflowActionComponent } from './advanced-workflow-action/adva AdvancedWorkflowActionSelectReviewerComponent, AdvancedWorkflowActionPageComponent, AdvancedClaimedTaskActionsDirective, - AdvancedWorkflowActionComponent, ] }) /** diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5f3ac8e069..b410a26ac1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4166,8 +4166,24 @@ "workflow-item.advanced.title": "Advanced workflow", + + "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", + + "workflow-item.selectrevieweraction.notification.success.content": "The reviewer for this workflow item has been successfully selected", + + "workflow-item.selectrevieweraction.notification.error.title": "Something went wrong", + + "workflow-item.selectrevieweraction.notification.error.content": "Couldn't select the reviewer for this workflow item", + + "workflow-item.selectrevieweraction.title": "Select Reviewer", + "workflow-item.selectrevieweraction.header": "Select Reviewer", + "workflow-item.selectrevieweraction.button.cancel": "Cancel", + + "workflow-item.selectrevieweraction.button.confirm": "Confirm", + + "workflow-item.ratingrevieweraction.header": "Rating Reviewer", From a03a150015e1c703f66897408c40fc80aae4f03a Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 6 Jan 2023 18:10:41 +0100 Subject: [PATCH 05/28] 98211: Added tests --- ...claimed-task-actions-abstract.component.ts | 16 +++ ...task-action-rating-reviewer.component.html | 4 +- ...k-action-rating-reviewer.component.spec.ts | 65 +++++++++- ...task-action-select-reviewer.component.html | 4 +- ...k-action-select-reviewer.component.spec.ts | 67 +++++++++- ...d-task-action-select-reviewer.component.ts | 9 +- .../testing/claimed-task-data-service.stub.ts | 10 ++ src/app/shared/testing/data-service.stub.ts | 12 ++ .../workflow-action-data-service.stub.ts | 15 +++ .../workflow-item-data-service.stub.ts | 5 + ...ced-workflow-action-page.component.spec.ts | 17 +++ ...w-action-rating-reviewer.component.spec.ts | 52 +++++++- ...w-action-select-reviewer.component.spec.ts | 58 ++++++++- ...advanced-workflow-action.component.spec.ts | 115 ++++++++++++++++-- ...-workflow-actions-loader.component.spec.ts | 9 ++ 15 files changed, 427 insertions(+), 31 deletions(-) create mode 100644 src/app/shared/testing/claimed-task-data-service.stub.ts create mode 100644 src/app/shared/testing/data-service.stub.ts create mode 100644 src/app/shared/testing/workflow-action-data-service.stub.ts create mode 100644 src/app/shared/testing/workflow-item-data-service.stub.ts diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts index a99639bd17..8738f7745b 100644 --- a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts @@ -13,4 +13,20 @@ import { ClaimedTaskActionsAbstractComponent } from './claimed-task-actions-abst template: '' }) export abstract class AdvancedClaimedTaskActionsAbstractComponent extends ClaimedTaskActionsAbstractComponent { + + workflowType: string; + + /** + * Route to the workflow's task page + */ + workflowTaskPageRoute: string; + + openAdvancedClaimedTaskTab(): void { + void this.router.navigate([this.workflowTaskPageRoute], { + queryParams: { + workflow: this.workflowType, + }, + }); + } + } diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html index 84d3aa7cc7..856b585fb3 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html @@ -1,4 +1,4 @@ - diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts index f99c0558c6..ed2fc42310 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts @@ -2,26 +2,85 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedClaimedTaskActionRatingReviewerComponent } from './advanced-claimed-task-action-rating-reviewer.component'; +import { By } from '@angular/platform-browser'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { ClaimedTaskDataServiceStub } from '../../../testing/claimed-task-data-service.stub'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { RouterStub } from '../../../testing/router.stub'; +import { SearchServiceStub } from '../../../testing/search-service.stub'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { RequestService } from '../../../../core/data/request.service'; +import { Router } from '@angular/router'; +import { SearchService } from '../../../../core/shared/search/search.service'; +import { Location } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { + ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER +} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; -describe('AdvancedClaimedTaskActionsRatingReviewerComponent', () => { +const taskId = 'claimed-task-1'; + +describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { + const object = Object.assign(new ClaimedTask(), { id: taskId }); let component: AdvancedClaimedTaskActionRatingReviewerComponent; let fixture: ComponentFixture; + let claimedTaskDataService: ClaimedTaskDataServiceStub; + let notificationService: NotificationsServiceStub; + let router: RouterStub; + let searchService: SearchServiceStub; + beforeEach(async () => { + claimedTaskDataService = new ClaimedTaskDataServiceStub(); + notificationService = new NotificationsServiceStub(); + router = new RouterStub(); + searchService = new SearchServiceStub(); + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ], declarations: [ AdvancedClaimedTaskActionRatingReviewerComponent, ], + providers: [ + { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, + { provide: NotificationsService, useValue: notificationService }, + { provide: RequestService, useValue: {} }, + { provide: Router, useValue: router }, + { provide: SearchService, useValue: searchService }, + Location, + ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AdvancedClaimedTaskActionRatingReviewerComponent); component = fixture.componentInstance; + component.object = object; fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + it('should display select reviewer button', () => { + const btn = fixture.debugElement.query(By.css('.ratingReviewerAction')); + + expect(btn).not.toBeNull(); + }); + + it('should navigate to the advanced workflow page when clicked', () => { + fixture.debugElement.query(By.css('.ratingReviewerAction')).nativeElement.click(); + + expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${taskId}/advanced`], { + queryParams: { + workflow: ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER, + }, + }); }); }); diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html index 84d3aa7cc7..6a45e7e02c 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.html @@ -1,4 +1,4 @@ - diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts index 24b8501f55..0c91898a97 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts @@ -2,26 +2,87 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedClaimedTaskActionSelectReviewerComponent } from './advanced-claimed-task-action-select-reviewer.component'; +import { Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router.stub'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { TranslateModule } from '@ngx-translate/core'; +import { SearchServiceStub } from '../../../testing/search-service.stub'; +import { SearchService } from '../../../../core/shared/search/search.service'; +import { RequestService } from '../../../../core/data/request.service'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +import { ClaimedTaskDataServiceStub } from '../../../testing/claimed-task-data-service.stub'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { By } from '@angular/platform-browser'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Location } from '@angular/common'; +import { + ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER +} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; -describe('AdvancedClaimedTaskActionsSelectReviewerComponent', () => { +const taskId = 'claimed-task-1'; + +describe('AdvancedClaimedTaskActionSelectReviewerComponent', () => { + const object = Object.assign(new ClaimedTask(), { id: taskId }); let component: AdvancedClaimedTaskActionSelectReviewerComponent; let fixture: ComponentFixture; + let claimedTaskDataService: ClaimedTaskDataServiceStub; + let notificationService: NotificationsServiceStub; + let router: RouterStub; + let searchService: SearchServiceStub; + beforeEach(async () => { + claimedTaskDataService = new ClaimedTaskDataServiceStub(); + notificationService = new NotificationsServiceStub(); + router = new RouterStub(); + searchService = new SearchServiceStub(); + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ], declarations: [ AdvancedClaimedTaskActionSelectReviewerComponent, + NgbTooltip, ], + providers: [ + { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, + { provide: NotificationsService, useValue: notificationService }, + { provide: RequestService, useValue: {} }, + { provide: Router, useValue: router }, + { provide: SearchService, useValue: searchService }, + Location, + ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AdvancedClaimedTaskActionSelectReviewerComponent); component = fixture.componentInstance; + component.object = object; fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + it('should display select reviewer button', () => { + const btn = fixture.debugElement.query(By.css('.selectReviewerAction')); + + expect(btn).not.toBeNull(); + }); + + it('should navigate to the advanced workflow page when clicked', () => { + fixture.debugElement.query(By.css('.selectReviewerAction')).nativeElement.click(); + + expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${taskId}/advanced`], { + queryParams: { + workflow: ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, + }, + }); }); }); diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts index fb45e911c5..7b2aa015af 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts @@ -10,8 +10,10 @@ import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { - ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER + ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, + WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER } from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; + @rendersWorkflowTaskOption(WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER) @Component({ selector: 'ds-advanced-claimed-task-action-select-reviewer', @@ -27,11 +29,6 @@ export class AdvancedClaimedTaskActionSelectReviewerComponent extends AdvancedCl workflowType = ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; - /** - * Route to the workflow's task page - */ - workflowTaskPageRoute: string; - constructor( protected injector: Injector, protected router: Router, diff --git a/src/app/shared/testing/claimed-task-data-service.stub.ts b/src/app/shared/testing/claimed-task-data-service.stub.ts new file mode 100644 index 0000000000..ef04e3bf27 --- /dev/null +++ b/src/app/shared/testing/claimed-task-data-service.stub.ts @@ -0,0 +1,10 @@ +import { Observable, EMPTY } from 'rxjs'; +import { ProcessTaskResponse } from '../../core/tasks/models/process-task-response'; + +export class ClaimedTaskDataServiceStub { + + public submitTask(_scopeId: string, _body: any): Observable { + return EMPTY; + } + +} diff --git a/src/app/shared/testing/data-service.stub.ts b/src/app/shared/testing/data-service.stub.ts new file mode 100644 index 0000000000..0904775b5d --- /dev/null +++ b/src/app/shared/testing/data-service.stub.ts @@ -0,0 +1,12 @@ +import { FollowLinkConfig } from '../utils/follow-link-config.model'; +import { Observable, EMPTY } from 'rxjs'; +import { RemoteData } from '../../core/data/remote-data'; +import { CacheableObject } from '../../core/cache/object-cache.reducer'; + +export abstract class DataServiceStub { + + findById(_id: string, _useCachedVersionIfAvailable = true, _reRequestOnStale = true, ..._linksToFollow: FollowLinkConfig[]): Observable> { + return EMPTY; + } + +} diff --git a/src/app/shared/testing/workflow-action-data-service.stub.ts b/src/app/shared/testing/workflow-action-data-service.stub.ts new file mode 100644 index 0000000000..f05d995f49 --- /dev/null +++ b/src/app/shared/testing/workflow-action-data-service.stub.ts @@ -0,0 +1,15 @@ +import { FindListOptions } from '../../core/data/request.models'; +import { FollowLinkConfig } from '../utils/follow-link-config.model'; +import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; +import { Observable, EMPTY } from 'rxjs'; +import { RemoteData } from '../../core/data/remote-data'; +import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; +import { DataServiceStub } from './data-service.stub'; + +export class WorkflowActionDataServiceStub extends DataServiceStub { + + public findByItem(_uuid: string, _useCachedVersionIfAvailable = false, _reRequestOnStale = true, _options: FindListOptions = {}, ..._linksToFollow: FollowLinkConfig[]): Observable> { + return EMPTY; + } + +} diff --git a/src/app/shared/testing/workflow-item-data-service.stub.ts b/src/app/shared/testing/workflow-item-data-service.stub.ts new file mode 100644 index 0000000000..20cde6bf9b --- /dev/null +++ b/src/app/shared/testing/workflow-item-data-service.stub.ts @@ -0,0 +1,5 @@ +import { DataServiceStub } from './data-service.stub'; +import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; + +export class WorkflowItemDataServiceStub extends DataServiceStub { +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts index ac713fa443..f2511f0490 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts @@ -1,5 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action-page.component'; +import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; describe('AdvancedWorkflowActionPageComponent', () => { let component: AdvancedWorkflowActionPageComponent; @@ -7,9 +9,24 @@ describe('AdvancedWorkflowActionPageComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ], declarations: [ AdvancedWorkflowActionPageComponent, ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + queryParams: convertToParamMap({ + workflow: 'testaction', + }), + }, + }, + }, + ], }).compileComponents(); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts index c08e3c0a78..244e397303 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts @@ -1,16 +1,60 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { AdvancedWorkflowActionRatingReviewerComponent } from './advanced-workflow-action-rating-reviewer.component'; +import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { of as observableOf } from 'rxjs'; +import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { RouteService } from '../../../core/services/route.service'; +import { routeServiceStub } from '../../../shared/testing/route-service.stub'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { WorkflowActionDataServiceStub } from '../../../shared/testing/workflow-action-data-service.stub'; +import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; + +const workflowId = '1'; describe('AdvancedWorkflowActionRatingReviewerComponent', () => { let component: AdvancedWorkflowActionRatingReviewerComponent; let fixture: ComponentFixture; + let claimedTaskDataService: ClaimedTaskDataServiceStub; + let notificationService: NotificationsServiceStub; + let workflowActionDataService: WorkflowItemDataServiceStub; + let workflowItemDataService: WorkflowItemDataServiceStub; + beforeEach(async () => { + claimedTaskDataService = new ClaimedTaskDataServiceStub(); + notificationService = new NotificationsServiceStub(); + workflowActionDataService = new WorkflowActionDataServiceStub(); + workflowItemDataService = new WorkflowItemDataServiceStub(); + await TestBed.configureTestingModule({ - declarations: [ AdvancedWorkflowActionRatingReviewerComponent ] - }) - .compileComponents(); + declarations: [ + AdvancedWorkflowActionRatingReviewerComponent, + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: observableOf({ + id: workflowId, + }), + snapshot: { + queryParams: convertToParamMap({ + workflow: 'testaction', + }), + }, + }, + }, + { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, + { provide: NotificationsService, useValue: notificationService }, + { provide: RouteService, useValue: routeServiceStub }, + { provide: WorkflowActionDataService, useValue: workflowActionDataService }, + { provide: WorkflowItemDataService, useValue: workflowItemDataService }, + ], + }).compileComponents(); }); beforeEach(() => { diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts index b4f420774b..a3ede08b1d 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts @@ -1,16 +1,66 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { AdvancedWorkflowActionSelectReviewerComponent } from './advanced-workflow-action-select-reviewer.component'; +import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; +import { RouterTestingModule } from '@angular/router/testing'; +import { WorkflowActionDataServiceStub } from '../../../shared/testing/workflow-action-data-service.stub'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { RouteService } from '../../../core/services/route.service'; +import { routeServiceStub } from '../../../shared/testing/route-service.stub'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { TranslateModule } from '@ngx-translate/core'; +import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; +import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; +import { of as observableOf } from 'rxjs'; + +const workflowId = '1'; describe('AdvancedWorkflowActionSelectReviewerComponent', () => { let component: AdvancedWorkflowActionSelectReviewerComponent; let fixture: ComponentFixture; + let claimedTaskDataService: ClaimedTaskDataServiceStub; + let notificationService: NotificationsServiceStub; + let workflowActionDataService: WorkflowItemDataServiceStub; + let workflowItemDataService: WorkflowItemDataServiceStub; + beforeEach(async () => { + claimedTaskDataService = new ClaimedTaskDataServiceStub(); + notificationService = new NotificationsServiceStub(); + workflowActionDataService = new WorkflowActionDataServiceStub(); + workflowItemDataService = new WorkflowItemDataServiceStub(); + await TestBed.configureTestingModule({ - declarations: [ AdvancedWorkflowActionSelectReviewerComponent ] - }) - .compileComponents(); + imports: [ + RouterTestingModule, + TranslateModule.forRoot(), + ], + declarations: [ + AdvancedWorkflowActionSelectReviewerComponent, + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: observableOf({ + id: workflowId, + }), + snapshot: { + queryParams: convertToParamMap({ + workflow: 'testaction', + }), + }, + }, + }, + { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, + { provide: NotificationsService, useValue: notificationService }, + { provide: RouteService, useValue: routeServiceStub }, + { provide: WorkflowActionDataService, useValue: workflowActionDataService }, + { provide: WorkflowItemDataService, useValue: workflowItemDataService }, + ], + }).compileComponents(); }); beforeEach(() => { diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts index 974dd4c257..b836a8704a 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts @@ -1,25 +1,126 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { AdvancedWorkflowActionComponent } from './advanced-workflow-action.component'; +import { Component } from '@angular/core'; +import { MockComponent } from 'ng-mocks'; +import { DSOSelectorComponent } from '../../../shared/dso-selector/dso-selector/dso-selector.component'; +import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; +import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; +import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { of as observableOf } from 'rxjs'; +import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { RouteService } from '../../../core/services/route.service'; +import { routeServiceStub } from '../../../shared/testing/route-service.stub'; +import { TranslateModule } from '@ngx-translate/core'; +import { WorkflowActionDataServiceStub } from '../../../shared/testing/workflow-action-data-service.stub'; +import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; + +const workflowId = '1'; describe('AdvancedWorkflowActionComponent', () => { let component: AdvancedWorkflowActionComponent; let fixture: ComponentFixture; + let claimedTaskDataService: ClaimedTaskDataServiceStub; + let notificationService: NotificationsServiceStub; + let workflowActionDataService: WorkflowActionDataServiceStub; + let workflowItemDataService: WorkflowItemDataServiceStub; + beforeEach(async () => { + claimedTaskDataService = new ClaimedTaskDataServiceStub(); + notificationService = new NotificationsServiceStub(); + workflowActionDataService = new WorkflowActionDataServiceStub(); + workflowItemDataService = new WorkflowItemDataServiceStub(); + + await TestBed.configureTestingModule({ - declarations: [ AdvancedWorkflowActionComponent ] - }) - .compileComponents(); + imports: [ + TranslateModule.forRoot(), + RouterTestingModule, + ], + declarations: [ + TestComponent, + MockComponent(DSOSelectorComponent), + ], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: observableOf({ + id: workflowId, + }), + snapshot: { + queryParams: convertToParamMap({ + workflow: 'testaction', + }), + }, + }, + }, + { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, + { provide: NotificationsService, useValue: notificationService }, + { provide: RouteService, useValue: routeServiceStub }, + { provide: WorkflowActionDataService, useValue: workflowActionDataService }, + { provide: WorkflowItemDataService, useValue: workflowItemDataService }, + ], + }).compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(AdvancedWorkflowActionComponent); + fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; + component.ngOnInit(); fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('sendRequest', () => { + it('should return true if the request succeeded', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true, 200))); + spyOn(workflowActionDataService, 'findById'); + + const result = component.sendRequest(workflowId); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith('1', { + 'submit_test': true, + }); + result.subscribe((value: boolean) => { + expect(value).toBeTrue(); + }); + }); + + it('should return false if the request didn\'t succeeded', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(false, 404))); + spyOn(workflowActionDataService, 'findById'); + + const result = component.sendRequest(workflowId); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith('1', { + 'submit_test': true, + }); + result.subscribe((value: boolean) => { + expect(value).toBeFalse(); + }); + }); }); }); + +@Component({ + selector: 'ds-test-cmp', + template: '' +}) +class TestComponent extends AdvancedWorkflowActionComponent { + + createBody(): any { + return { + 'submit_test': true, + }; + } + + getType(): string { + return 'testaction'; + } + +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts index 49cf92b171..321a6b954d 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts @@ -1,15 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedWorkflowActionsLoaderComponent } from './advanced-workflow-actions-loader.component'; +import { Router } from '@angular/router'; +import { RouterStub } from '../../../shared/testing/router.stub'; describe('AdvancedWorkflowActionsLoaderComponent', () => { let component: AdvancedWorkflowActionsLoaderComponent; let fixture: ComponentFixture; + let router: RouterStub; + beforeEach(async () => { + router = new RouterStub(); + await TestBed.configureTestingModule({ declarations: [ AdvancedWorkflowActionsLoaderComponent, ], + providers: [ + { provide: Router, useValue: router }, + ], }).compileComponents(); }); From 5a5884dd1a4c6a8efa337a44eb52b35af99ab343 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Mon, 9 Jan 2023 10:03:04 +0100 Subject: [PATCH 06/28] 98344: Implemented the Select reviewers advanced step --- .../access-control/access-control.module.ts | 11 +- .../eperson-list/eperson-list.component.html | 146 +++++++ .../eperson-list.component.spec.ts | 247 ++++++++++++ .../eperson-list/eperson-list.component.ts | 358 ++++++++++++++++++ .../members-list.component.spec.ts | 1 + .../members-list/members-list.component.ts | 18 +- src/app/core/data/request.service.spec.ts | 13 + src/app/core/data/request.service.ts | 20 +- ...flow-action-select-reviewer.component.html | 12 +- ...flow-action-select-reviewer.component.scss | 7 + ...rkflow-action-select-reviewer.component.ts | 70 +++- .../reviewers-list.component.html | 0 .../reviewers-list.component.spec.ts | 212 +++++++++++ .../reviewers-list.component.ts | 124 ++++++ .../workflowitems-edit-page.module.ts | 22 +- src/assets/i18n/en.json5 | 48 +++ 16 files changed, 1281 insertions(+), 28 deletions(-) create mode 100644 src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html create mode 100644 src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts create mode 100644 src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.html create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts diff --git a/src/app/access-control/access-control.module.ts b/src/app/access-control/access-control.module.ts index 891238bbed..2c9932d387 100644 --- a/src/app/access-control/access-control.module.ts +++ b/src/app/access-control/access-control.module.ts @@ -10,6 +10,7 @@ import { MembersListComponent } from './group-registry/group-form/members-list/m import { SubgroupsListComponent } from './group-registry/group-form/subgroup-list/subgroups-list.component'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; import { FormModule } from '../shared/form/form.module'; +import { EPersonListComponent } from './group-registry/group-form/eperson-list/eperson-list.component'; @NgModule({ imports: [ @@ -17,7 +18,10 @@ import { FormModule } from '../shared/form/form.module'; SharedModule, RouterModule, AccessControlRoutingModule, - FormModule + FormModule, + ], + exports: [ + EPersonListComponent, ], declarations: [ EPeopleRegistryComponent, @@ -25,8 +29,9 @@ import { FormModule } from '../shared/form/form.module'; GroupsRegistryComponent, GroupFormComponent, SubgroupsListComponent, - MembersListComponent - ] + MembersListComponent, + EPersonListComponent, + ], }) /** * This module handles all components related to the access control pages diff --git a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html new file mode 100644 index 0000000000..e4a507ae19 --- /dev/null +++ b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html @@ -0,0 +1,146 @@ + +

{{messagePrefix + '.head' | translate}}

+ + +
+
+ +
+
+
+ + + + +
+
+
+ +
+
+ + + +
+ + + + + + + + + + + + + + + + + +
{{messagePrefix + '.table.id' | translate}}{{messagePrefix + '.table.name' | translate}}{{messagePrefix + '.table.identity' | translate}}{{messagePrefix + '.table.edit' | translate}}
{{ePerson.eperson.id}}{{ePerson.eperson.name}} + {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
+ {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} +
+
+ + + +
+
+
+ +
+ + + +

{{messagePrefix + '.headMembers' | translate}}

+ + + +
+ + + + + + + + + + + + + + + + + +
{{messagePrefix + '.table.id' | translate}}{{messagePrefix + '.table.name' | translate}}{{messagePrefix + '.table.identity' | translate}}{{messagePrefix + '.table.edit' | translate}}
{{ePerson.eperson.id}}{{ePerson.eperson.name}} + {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
+ {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} +
+
+ + +
+
+
+ +
+ + + +
diff --git a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts new file mode 100644 index 0000000000..8077139026 --- /dev/null +++ b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts @@ -0,0 +1,247 @@ +import { CommonModule } from '@angular/common'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule, By } from '@angular/platform-browser'; +import { Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { Observable, of as observableOf } from 'rxjs'; +import { RestResponse } from '../../../../core/cache/response.models'; +import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { Group } from '../../../../core/eperson/models/group.model'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock'; +import { EPersonListComponent } from './eperson-list.component'; +import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock'; +import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock'; +import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock'; +import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; +import { RouterMock } from '../../../../shared/mocks/router.mock'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; + +describe('EPersonListComponent', () => { + let component: EPersonListComponent; + let fixture: ComponentFixture; + let translateService: TranslateService; + let builderService: FormBuilderService; + let ePersonDataServiceStub: any; + let groupsDataServiceStub: any; + let activeGroup; + let allEPersons; + let allGroups; + let epersonMembers; + let subgroupMembers; + let paginationService; + + beforeEach(waitForAsync(() => { + activeGroup = GroupMock; + epersonMembers = [EPersonMock2]; + subgroupMembers = [GroupMock2]; + allEPersons = [EPersonMock, EPersonMock2]; + allGroups = [GroupMock, GroupMock2]; + ePersonDataServiceStub = { + activeGroup: activeGroup, + epersonMembers: epersonMembers, + subgroupMembers: subgroupMembers, + findAllByHref(href: string): Observable>> { + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getEPersonMembers())); + }, + searchByScope(scope: string, query: string): Observable>> { + if (query === '') { + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allEPersons)); + } + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); + }, + clearEPersonRequests() { + // empty + }, + clearLinkRequests() { + // empty + }, + getEPeoplePageRouterLink(): string { + return '/access-control/epeople'; + } + }; + groupsDataServiceStub = { + activeGroup: activeGroup, + epersonMembers: epersonMembers, + subgroupMembers: subgroupMembers, + allGroups: allGroups, + getActiveGroup(): Observable { + return observableOf(activeGroup); + }, + getEPersonMembers() { + return this.epersonMembers; + }, + searchGroups(query: string): Observable>> { + if (query === '') { + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), this.allGroups)); + } + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); + }, + addMemberToGroup(parentGroup, eperson: EPerson): Observable { + this.epersonMembers = [...this.epersonMembers, eperson]; + return observableOf(new RestResponse(true, 200, 'Success')); + }, + clearGroupsRequests() { + // empty + }, + clearGroupLinkRequests() { + // empty + }, + getGroupEditPageRouterLink(group: Group): string { + return '/access-control/groups/' + group.id; + }, + deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable { + this.epersonMembers = this.epersonMembers.find((eperson: EPerson) => { + if (eperson.id !== epersonToDelete.id) { + return eperson; + } + }); + if (this.epersonMembers === undefined) { + this.epersonMembers = []; + } + return observableOf(new RestResponse(true, 200, 'Success')); + } + }; + builderService = getMockFormBuilderService(); + translateService = getMockTranslateService(); + + paginationService = new PaginationServiceStub(); + TestBed.configureTestingModule({ + imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [EPersonListComponent], + providers: [EPersonListComponent, + { provide: EPersonDataService, useValue: ePersonDataServiceStub }, + { provide: GroupDataService, useValue: groupsDataServiceStub }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: FormBuilderService, useValue: builderService }, + { provide: Router, useValue: new RouterMock() }, + { provide: PaginationService, useValue: paginationService }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EPersonListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + afterEach(fakeAsync(() => { + fixture.destroy(); + flush(); + component = null; + fixture.debugElement.nativeElement.remove(); + })); + + it('should create EpeopleListComponent', inject([EPersonListComponent], (comp: EPersonListComponent) => { + expect(comp).toBeDefined(); + })); + + it('should show list of eperson members of current active group', () => { + const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(epersonIdsFound.length).toEqual(1); + epersonMembers.map((eperson: EPerson) => { + expect(epersonIdsFound.find((foundEl) => { + return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + })).toBeTruthy(); + }); + }); + + describe('search', () => { + describe('when searching without query', () => { + let epersonsFound; + beforeEach(fakeAsync(() => { + component.search({ scope: 'metadata', query: '' }); + tick(); + fixture.detectChanges(); + epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); + })); + + it('should display all epersons', () => { + expect(epersonsFound.length).toEqual(2); + }); + + describe('if eperson is already a eperson', () => { + it('should have delete button, else it should have add button', () => { + activeGroup.epersons.map((eperson: EPerson) => { + epersonsFound.map((foundEPersonRowElement) => { + if (foundEPersonRowElement.debugElement !== undefined) { + const epersonId = foundEPersonRowElement.debugElement.query(By.css('td:first-child')); + const addButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-plus')); + const deleteButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-trash-alt')); + if (epersonId.nativeElement.textContent === eperson.id) { + expect(addButton).toBeUndefined(); + expect(deleteButton).toBeDefined(); + } else { + expect(deleteButton).toBeUndefined(); + expect(addButton).toBeDefined(); + } + } + }); + }); + }); + }); + + describe('if first add button is pressed', () => { + beforeEach(fakeAsync(() => { + const addButton = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-plus')); + addButton.nativeElement.click(); + tick(); + fixture.detectChanges(); + })); + it('all groups in search member of selected group', () => { + epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); + expect(epersonsFound.length).toEqual(2); + epersonsFound.map((foundEPersonRowElement) => { + if (foundEPersonRowElement.debugElement !== undefined) { + const addButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-plus')); + const deleteButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-trash-alt')); + expect(addButton).toBeUndefined(); + expect(deleteButton).toBeDefined(); + } + }); + }); + }); + + describe('if first delete button is pressed', () => { + beforeEach(fakeAsync(() => { + const addButton = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-trash-alt')); + addButton.nativeElement.click(); + tick(); + fixture.detectChanges(); + })); + it('first eperson in search delete button, because now member', () => { + epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); + epersonsFound.map((foundEPersonRowElement) => { + if (foundEPersonRowElement.debugElement !== undefined) { + const addButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-plus')); + const deleteButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-trash-alt')); + expect(deleteButton).toBeUndefined(); + expect(addButton).toBeDefined(); + } + }); + }); + }); + }); + }); + +}); diff --git a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts new file mode 100644 index 0000000000..9eafe10daa --- /dev/null +++ b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts @@ -0,0 +1,358 @@ +import { Component, OnDestroy, OnInit, Input } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { Group } from '../../../../core/eperson/models/group.model'; +import { + getAllCompletedRemoteData, + getFirstSucceededRemoteData, + getRemoteDataPayload, + getFirstCompletedRemoteData +} from '../../../../core/shared/operators'; +import { + BehaviorSubject, + Subscription, + combineLatest as observableCombineLatest, + Observable, + ObservedValueOf, + of as observableOf +} from 'rxjs'; +import { PaginatedList, buildPaginatedList } from '../../../../core/data/paginated-list.model'; +import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; +import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; +import { switchMap, map, take, mergeMap } from 'rxjs/operators'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; + +/** + * Keys to keep track of specific subscriptions + */ +enum SubKey { + ActiveGroup, + MembersDTO, + SearchResultsDTO, +} + +export interface EPersonActionConfig { + css?: string; + disabled: boolean; + icon: string; +} + +export interface EPersonListActionConfig { + add: EPersonActionConfig; + remove: EPersonActionConfig; +} + +@Component({ + selector: 'ds-eperson-list', + templateUrl: './eperson-list.component.html' +}) +export class EPersonListComponent implements OnInit, OnDestroy { + + @Input() + messagePrefix: string; + + @Input() + actionConfig: EPersonListActionConfig = { + add: { + css: 'btn-outline-primary', + disabled: false, + icon: 'fas fa-plus fa-fw', + }, + remove: { + css: 'btn-outline-danger', + disabled: false, + icon: 'fas fa-trash-alt fa-fw' + }, + }; + + /** + * EPeople being displayed in search result, initially all members, after search result of search + */ + ePeopleSearchDtos: BehaviorSubject> = new BehaviorSubject>(undefined); + /** + * List of EPeople members of currently active group being edited + */ + ePeopleMembersOfGroupDtos: BehaviorSubject> = new BehaviorSubject>(undefined); + + /** + * Pagination config used to display the list of EPeople that are result of EPeople search + */ + configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + id: 'sml', + pageSize: 5, + currentPage: 1 + }); + /** + * Pagination config used to display the list of EPerson Membes of active group being edited + */ + config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { + id: 'ml', + pageSize: 5, + currentPage: 1 + }); + + /** + * Map of active subscriptions + */ + subs: Map = new Map(); + + // The search form + searchForm; + + // Current search in edit group - epeople search form + currentSearchQuery: string; + currentSearchScope: string; + + // Whether or not user has done a EPeople search yet + searchDone: boolean; + + // current active group being edited + groupBeingEdited: Group; + + constructor( + protected groupDataService: GroupDataService, + public ePersonDataService: EPersonDataService, + protected translateService: TranslateService, + protected notificationsService: NotificationsService, + protected formBuilder: FormBuilder, + protected paginationService: PaginationService, + private router: Router + ) { + this.currentSearchQuery = ''; + this.currentSearchScope = 'metadata'; + } + + ngOnInit(): void { + this.searchForm = this.formBuilder.group(({ + scope: 'metadata', + query: '', + })); + this.subs.set(SubKey.ActiveGroup, this.groupDataService.getActiveGroup().subscribe((activeGroup: Group) => { + if (activeGroup != null) { + this.groupBeingEdited = activeGroup; + this.retrieveMembers(this.config.currentPage); + } + })); + } + + /** + * Retrieve the EPersons that are members of the group + * + * @param page the number of the page to retrieve + * @private + */ + retrieveMembers(page: number): void { + this.unsubFrom(SubKey.MembersDTO); + this.subs.set(SubKey.MembersDTO, + this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( + switchMap((currentPagination) => { + return this.ePersonDataService.findAllByHref(this.groupBeingEdited._links.epersons.href, { + currentPage: currentPagination.currentPage, + elementsPerPage: currentPagination.pageSize + } + ); + }), + getAllCompletedRemoteData(), + map((rd: RemoteData) => { + if (rd.hasFailed) { + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage })); + } else { + return rd; + } + }), + switchMap((epersonListRD: RemoteData>) => { + const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => { + const dto$: Observable = observableCombineLatest( + this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { + const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); + epersonDtoModel.eperson = member; + epersonDtoModel.memberOfGroup = isMember; + return epersonDtoModel; + }); + return dto$; + })); + return dtos$.pipe(map((dtos: EpersonDtoModel[]) => { + return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); + })); + })) + .subscribe((paginatedListOfDTOs: PaginatedList) => { + this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs); + })); + } + + /** + * Whether the given ePerson is a member of the group currently being edited + * @param possibleMember EPerson that is a possible member (being tested) of the group currently being edited + */ + isMemberOfGroup(possibleMember: EPerson): Observable { + return this.groupDataService.getActiveGroup().pipe(take(1), + mergeMap((group: Group) => { + if (group != null) { + return this.ePersonDataService.findAllByHref(group._links.epersons.href, { + currentPage: 1, + elementsPerPage: 9999 + }, false) + .pipe( + getFirstSucceededRemoteData(), + getRemoteDataPayload(), + map((listEPeopleInGroup: PaginatedList) => listEPeopleInGroup.page.filter((ePersonInList: EPerson) => ePersonInList.id === possibleMember.id)), + map((epeople: EPerson[]) => epeople.length > 0)); + } else { + return observableOf(false); + } + })); + } + + /** + * Unsubscribe from a subscription if it's still subscribed, and remove it from the map of + * active subscriptions + * + * @param key The key of the subscription to unsubscribe from + * @private + */ + protected unsubFrom(key: SubKey) { + if (this.subs.has(key)) { + this.subs.get(key).unsubscribe(); + this.subs.delete(key); + } + } + + /** + * Deletes a given EPerson from the members list of the group currently being edited + * @param ePerson EPerson we want to delete as member from group that is currently being edited + */ + deleteMemberFromGroup(ePerson: EpersonDtoModel) { + this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { + if (activeGroup != null) { + const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson); + this.showNotifications('deleteMember', response, ePerson.eperson.name, activeGroup); + this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery }); + } else { + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); + } + }); + } + + /** + * Adds a given EPerson to the members list of the group currently being edited + * @param ePerson EPerson we want to add as member to group that is currently being edited + */ + addMemberToGroup(ePerson: EpersonDtoModel) { + ePerson.memberOfGroup = true; + this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { + if (activeGroup != null) { + const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson); + this.showNotifications('addMember', response, ePerson.eperson.name, activeGroup); + } else { + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); + } + }); + } + + /** + * Search in the EPeople by name, email or metadata + * @param data Contains scope and query param + */ + search(data: any) { + this.unsubFrom(SubKey.SearchResultsDTO); + this.subs.set(SubKey.SearchResultsDTO, + this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe( + switchMap((paginationOptions) => { + + const query: string = data.query; + const scope: string = data.scope; + if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) { + this.router.navigate([], { + queryParamsHandling: 'merge' + }); + this.currentSearchQuery = query; + this.paginationService.resetPage(this.configSearch.id); + } + if (scope != null && this.currentSearchScope !== scope && this.groupBeingEdited) { + this.router.navigate([], { + queryParamsHandling: 'merge' + }); + this.currentSearchScope = scope; + this.paginationService.resetPage(this.configSearch.id); + } + this.searchDone = true; + + return this.ePersonDataService.searchByScope(this.currentSearchScope, this.currentSearchQuery, { + currentPage: paginationOptions.currentPage, + elementsPerPage: paginationOptions.pageSize + }); + }), + getAllCompletedRemoteData(), + map((rd: RemoteData) => { + if (rd.hasFailed) { + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage })); + } else { + return rd; + } + }), + switchMap((epersonListRD: RemoteData>) => { + const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => { + const dto$: Observable = observableCombineLatest( + this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { + const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); + epersonDtoModel.eperson = member; + epersonDtoModel.memberOfGroup = isMember; + return epersonDtoModel; + }); + return dto$; + })); + return dtos$.pipe(map((dtos: EpersonDtoModel[]) => { + return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); + })); + })) + .subscribe((paginatedListOfDTOs: PaginatedList) => { + this.ePeopleSearchDtos.next(paginatedListOfDTOs); + })); + } + + /** + * unsub all subscriptions + */ + ngOnDestroy(): void { + for (const key of this.subs.keys()) { + this.unsubFrom(key); + } + this.paginationService.clearPagination(this.config.id); + this.paginationService.clearPagination(this.configSearch.id); + } + + /** + * Shows a notification based on the success/failure of the request + * @param messageSuffix Suffix for message + * @param response RestResponse observable containing success/failure request + * @param nameObject Object request was about + * @param activeGroup Group currently being edited + */ + showNotifications(messageSuffix: string, response: Observable>, nameObject: string, activeGroup: Group) { + response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData) => { + if (rd.hasSucceeded) { + this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.success.' + messageSuffix, { name: nameObject })); + this.ePersonDataService.clearLinkRequests(activeGroup._links.epersons.href); + } else { + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.' + messageSuffix, { name: nameObject })); + } + }); + } + + /** + * Reset all input-fields to be empty and search all search + */ + clearFormAndResetResult() { + this.searchForm.patchValue({ + query: '', + }); + this.search({ query: '' }); + } + +} diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts index 0b19b17100..0fa405a1c9 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts @@ -149,6 +149,7 @@ describe('MembersListComponent', () => { fixture.destroy(); flush(); component = null; + fixture.debugElement.nativeElement.remove(); })); it('should create MembersListComponent', inject([MembersListComponent], (comp: MembersListComponent) => { diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts index 54d144da51..8bc540641e 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts @@ -94,13 +94,15 @@ export class MembersListComponent implements OnInit, OnDestroy { paginationSub: Subscription; - constructor(private groupDataService: GroupDataService, - public ePersonDataService: EPersonDataService, - private translateService: TranslateService, - private notificationsService: NotificationsService, - private formBuilder: FormBuilder, - private paginationService: PaginationService, - private router: Router) { + constructor( + protected groupDataService: GroupDataService, + public ePersonDataService: EPersonDataService, + private translateService: TranslateService, + private notificationsService: NotificationsService, + protected formBuilder: FormBuilder, + private paginationService: PaginationService, + private router: Router + ) { this.currentSearchQuery = ''; this.currentSearchScope = 'metadata'; } @@ -124,7 +126,7 @@ export class MembersListComponent implements OnInit, OnDestroy { * @param page the number of the page to retrieve * @private */ - private retrieveMembers(page: number) { + protected retrieveMembers(page: number) { this.unsubFrom(SubKey.MembersDTO); this.subs.set(SubKey.MembersDTO, this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index 7a07f6fe10..fb4edff76f 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -592,6 +592,19 @@ describe('RequestService', () => { 'property1=multiple%0Alines%0Ato%0Asend&property2=sp%26ci%40l%20characters&sp%26ci%40l-chars%20in%20prop=test123' ); }); + + it('should properly encode the body with an array', () => { + const body = { + 'property1': 'multiple\nlines\nto\nsend', + 'property2': 'sp&ci@l characters', + 'sp&ci@l-chars in prop': 'test123', + 'arrayParam': ['arrayValue1', 'arrayValue2'], + }; + const queryParams = service.uriEncodeBody(body); + expect(queryParams).toEqual( + 'property1=multiple%0Alines%0Ato%0Asend&property2=sp%26ci%40l%20characters&sp%26ci%40l-chars%20in%20prop=test123&arrayParam=arrayValue1&arrayParam=arrayValue2' + ); + }); }); }); diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index 14499b8214..f26b36da08 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -252,8 +252,8 @@ export class RequestService { /** * Convert request Payload to a URL-encoded string * - * e.g. uriEncodeBody({param: value, param1: value1}) - * returns: param=value¶m1=value1 + * e.g. uriEncodeBody({param: value, param1: value1, param2: [value3, value4]}) + * returns: param=value¶m1=value1¶m2=value3¶m2=value4 * * @param body * The request Payload to convert @@ -264,11 +264,19 @@ export class RequestService { let queryParams = ''; if (isNotEmpty(body) && typeof body === 'object') { Object.keys(body) - .forEach((param) => { + .forEach((param: string) => { const encodedParam = encodeURIComponent(param); - const encodedBody = encodeURIComponent(body[param]); - const paramValue = `${encodedParam}=${encodedBody}`; - queryParams = isEmpty(queryParams) ? queryParams.concat(paramValue) : queryParams.concat('&', paramValue); + if (Array.isArray(body[param])) { + for (const element of body[param]) { + const encodedBody = encodeURIComponent(element); + const paramValue = `${encodedParam}=${encodedBody}`; + queryParams = isEmpty(queryParams) ? queryParams.concat(paramValue) : queryParams.concat('&', paramValue); + } + } else { + const encodedBody = encodeURIComponent(body[param]); + const paramValue = `${encodedParam}=${encodedBody}`; + queryParams = isEmpty(queryParams) ? queryParams.concat(paramValue) : queryParams.concat('&', paramValue); + } }); } return queryParams; diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html index 64e5638de6..3009cc0771 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html @@ -1,5 +1,15 @@
- advancedInfo: {{ (workflowAction$ | async)?.advancedInfo | json }} +

{{ 'advanced-workflow-action.select-reviewer.description-multiple' | translate }}

+

{{ 'advanced-workflow-action.select-reviewer.description-single' | translate }}

+ + diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss index e69de29bb2..65f38247c8 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.scss @@ -0,0 +1,7 @@ +:host ::ng-deep { + .reviewersListWithGroup { + #search, #search + form, #search + form + ds-pagination { + display: none !important; + } + } +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index df867d5595..29949a0ae3 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -1,8 +1,17 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { rendersAdvancedWorkflowTaskOption } from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; import { AdvancedWorkflowActionComponent } from '../advanced-workflow-action/advanced-workflow-action.component'; +import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; +import { + SelectReviewerActionAdvancedInfo +} from '../../../core/tasks/models/select-reviewer-action-advanced-info.model'; +import { + EPersonListActionConfig +} from '../../../access-control/group-registry/group-form/eperson-list/eperson-list.component'; +import { Subscription } from 'rxjs'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; export const WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; @@ -13,7 +22,63 @@ export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; templateUrl: './advanced-workflow-action-select-reviewer.component.html', styleUrls: ['./advanced-workflow-action-select-reviewer.component.scss'], }) -export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkflowActionComponent { +export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkflowActionComponent implements OnInit, OnDestroy { + + multipleReviewers = true; + + selectedReviewers: EPerson[]; + + reviewersListActionConfig: EPersonListActionConfig; + + /** + * When the component is created the value is `undefined`, afterwards it will be set to either the group id or `null`. + * It needs to be subscribed in the **ngOnInit()** because otherwise some unnecessary request will be made. + */ + groupId?: string | null; + + subs: Subscription[] = []; + + ngOnDestroy(): void { + this.subs.forEach((subscription: Subscription) => subscription.unsubscribe()); + } + + ngOnInit(): void { + super.ngOnInit(); + if (this.multipleReviewers) { + this.reviewersListActionConfig = { + add: { + css: 'btn-outline-primary', + disabled: false, + icon: 'fas fa-plus', + }, + remove: { + css: 'btn-outline-danger', + disabled: false, + icon: 'fas fa-minus' + }, + }; + } else { + this.reviewersListActionConfig = { + add: { + css: 'btn-outline-primary', + disabled: false, + icon: 'fas fa-check', + }, + remove: { + css: 'btn-primary', + disabled: true, + icon: 'fas fa-check' + }, + }; + } + this.subs.push(this.workflowAction$.subscribe((workflowAction: WorkflowAction) => { + if (workflowAction) { + this.groupId = (workflowAction.advancedInfo as SelectReviewerActionAdvancedInfo[])[0].group; + } else { + this.groupId = null; + } + })); + } getType(): string { return ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; @@ -22,6 +87,7 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf createBody(): any { return { [WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER]: true, + eperson: this.selectedReviewers.map((ePerson: EPerson) => ePerson.id), }; } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts new file mode 100644 index 0000000000..bf27b1e79f --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts @@ -0,0 +1,212 @@ +import { CommonModule } from '@angular/common'; +import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core'; +import { ComponentFixture, fakeAsync, flush, inject, TestBed, waitForAsync } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule, By } from '@angular/platform-browser'; +import { Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { Observable, of as observableOf } from 'rxjs'; +import { RestResponse } from '../../../../core/cache/response.models'; +import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { Group } from '../../../../core/eperson/models/group.model'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock'; +import { ReviewersListComponent } from './reviewers-list.component'; +import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock'; +import { + createSuccessfulRemoteDataObject$, + createNoContentRemoteDataObject$ +} from '../../../../shared/remote-data.utils'; +import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock'; +import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock'; +import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock'; +import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; +import { RouterMock } from '../../../../shared/mocks/router.mock'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; + +describe('ReviewersListComponent', () => { + let component: ReviewersListComponent; + let fixture: ComponentFixture; + let translateService: TranslateService; + let builderService: FormBuilderService; + let ePersonDataServiceStub: any; + let groupsDataServiceStub: any; + let activeGroup; + let allEPersons; + let allGroups; + let epersonMembers; + let subgroupMembers; + let paginationService; + + beforeEach(waitForAsync(() => { + activeGroup = GroupMock; + epersonMembers = [EPersonMock2]; + subgroupMembers = [GroupMock2]; + allEPersons = [EPersonMock, EPersonMock2]; + allGroups = [GroupMock, GroupMock2]; + ePersonDataServiceStub = { + activeGroup: activeGroup, + epersonMembers: epersonMembers, + subgroupMembers: subgroupMembers, + findAllByHref(href: string): Observable>> { + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getEPersonMembers())); + }, + searchByScope(scope: string, query: string): Observable>> { + if (query === '') { + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allEPersons)); + } + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); + }, + clearEPersonRequests() { + // empty + }, + clearLinkRequests() { + // empty + }, + getEPeoplePageRouterLink(): string { + return '/access-control/epeople'; + } + }; + groupsDataServiceStub = { + activeGroup: activeGroup, + epersonMembers: epersonMembers, + subgroupMembers: subgroupMembers, + allGroups: allGroups, + getActiveGroup(): Observable { + return observableOf(activeGroup); + }, + getEPersonMembers() { + return this.epersonMembers; + }, + searchGroups(query: string): Observable>> { + if (query === '') { + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), this.allGroups)); + } + return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); + }, + addMemberToGroup(parentGroup, eperson: EPerson): Observable { + this.epersonMembers = [...this.epersonMembers, eperson]; + return observableOf(new RestResponse(true, 200, 'Success')); + }, + clearGroupsRequests() { + // empty + }, + clearGroupLinkRequests() { + // empty + }, + getGroupEditPageRouterLink(group: Group): string { + return '/access-control/groups/' + group.id; + }, + deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable { + this.epersonMembers = this.epersonMembers.find((eperson: EPerson) => { + if (eperson.id !== epersonToDelete.id) { + return eperson; + } + }); + if (this.epersonMembers === undefined) { + this.epersonMembers = []; + } + return observableOf(new RestResponse(true, 200, 'Success')); + }, + findById(id: string) { + for (const group of allGroups) { + if (group.id === id) { + console.log('found', group); + return createSuccessfulRemoteDataObject$(group); + } + } + return createNoContentRemoteDataObject$(); + }, + editGroup() { + // empty + } + }; + builderService = getMockFormBuilderService(); + translateService = getMockTranslateService(); + + paginationService = new PaginationServiceStub(); + TestBed.configureTestingModule({ + imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + declarations: [ReviewersListComponent], + providers: [ReviewersListComponent, + { provide: EPersonDataService, useValue: ePersonDataServiceStub }, + { provide: GroupDataService, useValue: groupsDataServiceStub }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: FormBuilderService, useValue: builderService }, + { provide: Router, useValue: new RouterMock() }, + { provide: PaginationService, useValue: paginationService }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReviewersListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + afterEach(fakeAsync(() => { + fixture.destroy(); + flush(); + component = null; + fixture.debugElement.nativeElement.remove(); + })); + + it('should create ReviewersListComponent', inject([ReviewersListComponent], (comp: ReviewersListComponent) => { + expect(comp).toBeDefined(); + })); + + describe('when no group is selected', () => { + beforeEach(() => { + component.ngOnChanges({ + groupId: new SimpleChange(undefined, null, true) + }); + fixture.detectChanges(); + }); + + it('should show no epersons because no group is selected', () => { + const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(epersonIdsFound.length).toEqual(0); + epersonMembers.map((eperson: EPerson) => { + expect(epersonIdsFound.find((foundEl) => { + return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + })).not.toBeTruthy(); + }); + }); + }); + + describe('when group is selected', () => { + beforeEach(() => { + component.ngOnChanges({ + groupId: new SimpleChange(undefined, GroupMock.id, true) + }); + fixture.detectChanges(); + }); + + it('should show all eperson members of group', () => { + const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(epersonIdsFound.length).toEqual(1); + epersonMembers.map((eperson: EPerson) => { + expect(epersonIdsFound.find((foundEl) => { + return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + })).toBeTruthy(); + }); + }); + }); + +}); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts new file mode 100644 index 0000000000..14159450ff --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts @@ -0,0 +1,124 @@ +import { Component, OnDestroy, OnInit, Input, OnChanges, SimpleChanges, EventEmitter, Output } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { Group } from '../../../../core/eperson/models/group.model'; +import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; +import { + EPersonListComponent, + EPersonListActionConfig +} from '../../../../access-control/group-registry/group-form/eperson-list/eperson-list.component'; +import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { Observable, of as observableOf } from 'rxjs'; +import { hasValue } from '../../../../shared/empty.util'; +import { PaginatedList } from '../../../../core/data/paginated-list.model'; + +/** + * Keys to keep track of specific subscriptions + */ +enum SubKey { + ActiveGroup, + MembersDTO, + SearchResultsDTO, +} + +@Component({ + selector: 'ds-reviewers-list', + // templateUrl: './reviewers-list.component.html', + templateUrl: '../../../../access-control/group-registry/group-form/eperson-list/eperson-list.component.html', +}) +export class ReviewersListComponent extends EPersonListComponent implements OnInit, OnChanges, OnDestroy { + + @Input() + groupId: string | null; + + @Input() + actionConfig: EPersonListActionConfig; + + @Input() + multipleReviewers: boolean; + + @Output() + selectedReviewersUpdated: EventEmitter = new EventEmitter(); + + selectedReviewers: EpersonDtoModel[] = []; + + constructor(protected groupService: GroupDataService, + public ePersonDataService: EPersonDataService, + translateService: TranslateService, + notificationsService: NotificationsService, + formBuilder: FormBuilder, + paginationService: PaginationService, + router: Router) { + super(groupService, ePersonDataService, translateService, notificationsService, formBuilder, paginationService, router); + } + + ngOnInit() { + this.searchForm = this.formBuilder.group(({ + scope: 'metadata', + query: '', + })); + } + + ngOnChanges(changes: SimpleChanges): void { + this.groupId = changes.groupId.currentValue; + if (changes.groupId.currentValue !== changes.groupId.previousValue) { + if (this.groupId === null) { + this.retrieveMembers(this.config.currentPage); + } else { + this.subs.set(SubKey.ActiveGroup, this.groupService.findById(this.groupId).pipe( + getFirstSucceededRemoteDataPayload(), + ).subscribe((activeGroup: Group) => { + if (activeGroup != null) { + this.groupDataService.editGroup(activeGroup); + this.groupBeingEdited = activeGroup; + this.retrieveMembers(this.config.currentPage); + } + })); + } + } + } + + retrieveMembers(page: number): void { + this.config.currentPage = page; + if (this.groupId === null) { + this.unsubFrom(SubKey.MembersDTO); + const paginatedListOfDTOs: PaginatedList = new PaginatedList(); + paginatedListOfDTOs.page = this.selectedReviewers; + this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs); + } else { + super.retrieveMembers(page); + } + } + + isMemberOfGroup(possibleMember: EPerson): Observable { + return observableOf(hasValue(this.selectedReviewers.find((reviewer: EpersonDtoModel) => reviewer.eperson.id === possibleMember.id))); + } + + deleteMemberFromGroup(ePerson: EpersonDtoModel) { + ePerson.memberOfGroup = false; + const index = this.selectedReviewers.indexOf(ePerson); + if (index !== -1) { + this.selectedReviewers.splice(index, 1); + } + this.selectedReviewersUpdated.emit(this.selectedReviewers.map((epersonDtoModel: EpersonDtoModel) => epersonDtoModel.eperson)); + } + + addMemberToGroup(ePerson: EpersonDtoModel) { + ePerson.memberOfGroup = true; + if (!this.multipleReviewers) { + for (const selectedReviewer of this.selectedReviewers) { + selectedReviewer.memberOfGroup = false; + } + this.selectedReviewers = []; + } + this.selectedReviewers.push(ePerson); + this.selectedReviewersUpdated.emit(this.selectedReviewers.map((epersonDtoModel: EpersonDtoModel) => epersonDtoModel.eperson)); + } + +} diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts index b230ffe65c..080ca96468 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -18,16 +18,21 @@ import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/ import { AdvancedClaimedTaskActionsDirective } from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive'; +import { AccessControlModule } from '../access-control/access-control.module'; +import { + ReviewersListComponent +} from './advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component'; @NgModule({ - imports: [ - WorkflowItemsEditPageRoutingModule, - CommonModule, - SharedModule, - SubmissionModule, - StatisticsModule, - ItemPageModule - ], + imports: [ + WorkflowItemsEditPageRoutingModule, + CommonModule, + SharedModule, + SubmissionModule, + StatisticsModule, + ItemPageModule, + AccessControlModule + ], declarations: [ WorkflowItemDeleteComponent, ThemedWorkflowItemDeleteComponent, @@ -38,6 +43,7 @@ import { AdvancedWorkflowActionSelectReviewerComponent, AdvancedWorkflowActionPageComponent, AdvancedClaimedTaskActionsDirective, + ReviewersListComponent, ] }) /** diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index b410a26ac1..9d455fb422 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -539,6 +539,54 @@ "admin.metadata-import.page.error.addFile": "Select file first!", + "advanced-workflow-action.select-reviewer.description-single": "Please select a single reviewer below before submitting", + + "advanced-workflow-action.select-reviewer.description-multiple": "Please select one or more reviewers below before submitting", + + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.head": "EPeople", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.head": "Add EPeople", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.button.see-all": "Browse All", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.headMembers": "Current Members", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.metadata": "Metadata", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.scope.email": "E-mail (exact)", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.search.button": "Search", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.id": "ID", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.name": "Name", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.identity": "Identity", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.email": "Email", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.netid": "NetID", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit": "Remove / Add", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.remove": "Remove member with name \"{{name}}\"", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.addMember": "Successfully added member: \"{{name}}\"", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.addMember": "Failed to add member: \"{{name}}\"", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.success.deleteMember": "Successfully deleted member: \"{{name}}\"", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.deleteMember": "Failed to delete member: \"{{name}}\"", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.table.edit.buttons.add": "Add member with name \"{{name}}\"", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.notification.failure.noActiveGroup": "No current active group, submit a name first.", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-members-yet": "No members in group yet, search and add.", + + "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-items": "No EPeople found in that search", "auth.errors.invalid-user": "Invalid email address or password.", From f4a303f80854f27cce069d437f37a30372a38854 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Mon, 9 Jan 2023 17:30:00 +0100 Subject: [PATCH 07/28] 98344: Added decline task simple workflow step --- ...d-task-actions-decline-task.component.html | 12 +++ ...d-task-actions-decline-task.component.scss | 0 ...ask-actions-decline-task.component.spec.ts | 90 +++++++++++++++++++ ...med-task-actions-decline-task.component.ts | 34 +++++++ src/app/shared/shared.module.ts | 3 + .../testing/claimed-task-data-service.stub.ts | 6 ++ src/assets/i18n/en.json5 | 4 + 7 files changed, 149 insertions(+) create mode 100644 src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.scss create mode 100644 src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts diff --git a/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.html b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.html new file mode 100644 index 0000000000..38ed36c0f1 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.html @@ -0,0 +1,12 @@ + diff --git a/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.scss b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.spec.ts new file mode 100644 index 0000000000..ef132ff522 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.spec.ts @@ -0,0 +1,90 @@ +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { getMockSearchService } from '../../../mocks/search-service.mock'; +import { getMockRequestService } from '../../../mocks/request.service.mock'; +import { PoolTaskDataService } from '../../../../core/tasks/pool-task-data.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; +import { Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router.stub'; +import { SearchService } from '../../../../core/shared/search/search.service'; +import { RequestService } from '../../../../core/data/request.service'; +import { ClaimedTaskActionsDeclineTaskComponent } from './claimed-task-actions-decline-task.component'; +import { ClaimedTaskDataServiceStub } from '../../../testing/claimed-task-data-service.stub'; + +let component: ClaimedTaskActionsDeclineTaskComponent; +let fixture: ComponentFixture; + +const searchService = getMockSearchService(); + +const requestService = getMockRequestService(); + +let mockPoolTaskDataService: PoolTaskDataService; + +describe('ClaimedTaskActionsDeclineTaskComponent', () => { + const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' }); + + let claimedTaskService: ClaimedTaskDataServiceStub; + + beforeEach(waitForAsync(() => { + claimedTaskService = new ClaimedTaskDataServiceStub(); + + mockPoolTaskDataService = new PoolTaskDataService(null, null, null, null, null, null, null, null); + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }) + ], + providers: [ + { provide: ClaimedTaskDataService, useValue: claimedTaskService }, + { provide: Injector, useValue: {} }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: Router, useValue: new RouterStub() }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestService }, + { provide: PoolTaskDataService, useValue: mockPoolTaskDataService }, + ], + declarations: [ClaimedTaskActionsDeclineTaskComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedTaskActionsDeclineTaskComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClaimedTaskActionsDeclineTaskComponent); + component = fixture.componentInstance; + component.object = object; + spyOn(component, 'initReloadAnchor').and.returnValue(undefined); + fixture.detectChanges(); + }); + + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + it('should display decline button', () => { + const btn = fixture.debugElement.query(By.css('.declineTaskAction')); + + expect(btn).not.toBeNull(); + }); + + it('should display spin icon when decline is pending', () => { + component.processing$.next(true); + fixture.detectChanges(); + + const span = fixture.debugElement.query(By.css('.declineTaskAction .fa-spin')); + + expect(span).not.toBeNull(); + }); + +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts new file mode 100644 index 0000000000..ab867d62d6 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts @@ -0,0 +1,34 @@ +import { Component, Injector } from '@angular/core'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { Router } from '@angular/router'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { SearchService } from '../../../../core/shared/search/search.service'; +import { RequestService } from '../../../../core/data/request.service'; + +export const WORKFLOW_TASK_OPTION_DECLINE_TASK = 'submit_decline_task'; + +@rendersWorkflowTaskOption(WORKFLOW_TASK_OPTION_DECLINE_TASK) +@Component({ + selector: 'ds-claimed-task-actions-decline-task', + templateUrl: './claimed-task-actions-decline-task.component.html', + styleUrls: ['./claimed-task-actions-decline-task.component.scss'] +}) +/** + * Component for displaying and processing the decline task action on a workflow task item + */ +export class ClaimedTaskActionsDeclineTaskComponent extends ClaimedTaskActionsAbstractComponent { + + option = WORKFLOW_TASK_OPTION_DECLINE_TASK; + + constructor(protected injector: Injector, + protected router: Router, + protected notificationsService: NotificationsService, + protected translate: TranslateService, + protected searchService: SearchService, + protected requestService: RequestService) { + super(injector, router, notificationsService, translate, searchService, requestService); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9a859dff9b..1cc0a86115 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -181,6 +181,7 @@ import { AdvancedClaimedTaskActionSelectReviewerComponent } from './mydspace-act import { AdvancedClaimedTaskActionRatingReviewerComponent } from './mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component'; +import { ClaimedTaskActionsDeclineTaskComponent } from './mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -265,6 +266,7 @@ const COMPONENTS = [ ClaimedTaskActionsRejectComponent, ClaimedTaskActionsReturnToPoolComponent, ClaimedTaskActionsEditMetadataComponent, + ClaimedTaskActionsDeclineTaskComponent, ClaimedTaskActionsLoaderComponent, ItemActionsComponent, PoolTaskActionsComponent, @@ -389,6 +391,7 @@ const ENTRY_COMPONENTS = [ LogInOidcComponent, BundleListElementComponent, ClaimedTaskActionsApproveComponent, + ClaimedTaskActionsDeclineTaskComponent, ClaimedTaskActionsRejectComponent, ClaimedTaskActionsReturnToPoolComponent, ClaimedTaskActionsEditMetadataComponent, diff --git a/src/app/shared/testing/claimed-task-data-service.stub.ts b/src/app/shared/testing/claimed-task-data-service.stub.ts index ef04e3bf27..3a9f3d852d 100644 --- a/src/app/shared/testing/claimed-task-data-service.stub.ts +++ b/src/app/shared/testing/claimed-task-data-service.stub.ts @@ -1,5 +1,7 @@ import { Observable, EMPTY } from 'rxjs'; import { ProcessTaskResponse } from '../../core/tasks/models/process-task-response'; +import { ClaimedTask } from '../../core/tasks/models/claimed-task-object.model'; +import { RemoteData } from '../../core/data/remote-data'; export class ClaimedTaskDataServiceStub { @@ -7,4 +9,8 @@ export class ClaimedTaskDataServiceStub { return EMPTY; } + public findByItem(_uuid: string): Observable> { + return EMPTY; + } + } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 9d455fb422..6addb5a81b 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4069,6 +4069,10 @@ "submission.workflow.tasks.claimed.edit_help": "Select this option to change the item's metadata.", + "submission.workflow.tasks.claimed.decline": "Decline", + + "submission.workflow.tasks.claimed.decline_help": "", + "submission.workflow.tasks.claimed.reject.reason.info": "Please enter your reason for rejecting the submission into the box below, indicating whether the submitter may fix a problem and resubmit.", "submission.workflow.tasks.claimed.reject.reason.placeholder": "Describe the reason of reject", From b3510534895b15882c0bc5ef68a6102307a0bfcd Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 10 Jan 2023 14:10:44 +0100 Subject: [PATCH 08/28] 98344: Fixed workflow id passed to backend instead of claimed task id --- ...claimed-task-actions-abstract.component.ts | 24 +++++++++++++++++-- ...k-action-rating-reviewer.component.spec.ts | 14 +++++++++-- ...d-task-action-rating-reviewer.component.ts | 17 ------------- ...k-action-select-reviewer.component.spec.ts | 14 +++++++++-- ...d-task-action-select-reviewer.component.ts | 12 ---------- ...rkflow-action-select-reviewer.component.ts | 2 +- .../advanced-workflow-action.component.ts | 18 ++++++++++++++ 7 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts index 8738f7745b..3774b60f1a 100644 --- a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts @@ -1,5 +1,8 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ClaimedTaskActionsAbstractComponent } from './claimed-task-actions-abstract.component'; +import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; +import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; +import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; /** * Abstract component for rendering an advanced claimed task's action @@ -12,7 +15,7 @@ import { ClaimedTaskActionsAbstractComponent } from './claimed-task-actions-abst selector: 'ds-advanced-claimed-task-action-abstract', template: '' }) -export abstract class AdvancedClaimedTaskActionsAbstractComponent extends ClaimedTaskActionsAbstractComponent { +export abstract class AdvancedClaimedTaskActionsAbstractComponent extends ClaimedTaskActionsAbstractComponent implements OnInit { workflowType: string; @@ -21,10 +24,27 @@ export abstract class AdvancedClaimedTaskActionsAbstractComponent extends Claime */ workflowTaskPageRoute: string; + ngOnInit(): void { + super.ngOnInit(); + this.initPageRoute(); + } + + /** + * Initialise the route to the advanced workflow page + */ + initPageRoute() { + this.subs.push(this.object.workflowitem.pipe( + getFirstSucceededRemoteDataPayload() + ).subscribe((workflowItem: WorkflowItem) => { + this.workflowTaskPageRoute = getAdvancedWorkflowRoute(workflowItem.id); + })); + } + openAdvancedClaimedTaskTab(): void { void this.router.navigate([this.workflowTaskPageRoute], { queryParams: { workflow: this.workflowType, + claimedTask: this.object.id, }, }); } diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts index ed2fc42310..563af2810e 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts @@ -19,11 +19,19 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER } from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; +import { of as observableOf } from 'rxjs'; +import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; const taskId = 'claimed-task-1'; +const workflowId = 'workflow-1'; describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { - const object = Object.assign(new ClaimedTask(), { id: taskId }); + const object = Object.assign(new ClaimedTask(), { + id: taskId, + workflowitem: observableOf(Object.assign(new WorkflowItem(), { + id: workflowId, + })), + }); let component: AdvancedClaimedTaskActionRatingReviewerComponent; let fixture: ComponentFixture; @@ -75,11 +83,13 @@ describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { }); it('should navigate to the advanced workflow page when clicked', () => { + component.workflowTaskPageRoute = `/workflowitems/${workflowId}/advanced`; fixture.debugElement.query(By.css('.ratingReviewerAction')).nativeElement.click(); - expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${taskId}/advanced`], { + expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${workflowId}/advanced`], { queryParams: { workflow: ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER, + claimedTask: taskId, }, }); }); diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts index bfa676b30e..20fe58c80e 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts @@ -4,7 +4,6 @@ import { NotificationsService } from '../../../notifications/notifications.servi import { TranslateService } from '@ngx-translate/core'; import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; -import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { AdvancedClaimedTaskActionsAbstractComponent } from '../abstract/advanced-claimed-task-actions-abstract.component'; @@ -28,11 +27,6 @@ export class AdvancedClaimedTaskActionRatingReviewerComponent extends AdvancedCl workflowType = ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER; - /** - * Route to the workflow's task page - */ - workflowTaskPageRoute: string; - constructor( protected injector: Injector, protected router: Router, @@ -44,15 +38,4 @@ export class AdvancedClaimedTaskActionRatingReviewerComponent extends AdvancedCl super(injector, router, notificationsService, translate, searchService, requestService); } - ngOnInit(): void { - this.initPageRoute(); - } - - /** - * Initialise the route to the rating reviewer's page - */ - initPageRoute(): void { - this.workflowTaskPageRoute = getAdvancedWorkflowRoute(this.object.id); - } - } diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts index 0c91898a97..db670e3cdc 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts @@ -20,11 +20,19 @@ import { Location } from '@angular/common'; import { ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER } from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; +import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; +import { of as observableOf } from 'rxjs'; const taskId = 'claimed-task-1'; +const workflowId = 'workflow-1'; describe('AdvancedClaimedTaskActionSelectReviewerComponent', () => { - const object = Object.assign(new ClaimedTask(), { id: taskId }); + const object = Object.assign(new ClaimedTask(), { + id: taskId, + workflowitem: observableOf(Object.assign(new WorkflowItem(), { + id: workflowId, + })), + }); let component: AdvancedClaimedTaskActionSelectReviewerComponent; let fixture: ComponentFixture; @@ -77,11 +85,13 @@ describe('AdvancedClaimedTaskActionSelectReviewerComponent', () => { }); it('should navigate to the advanced workflow page when clicked', () => { + component.workflowTaskPageRoute = `/workflowitems/${workflowId}/advanced`; fixture.debugElement.query(By.css('.selectReviewerAction')).nativeElement.click(); - expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${taskId}/advanced`], { + expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${workflowId}/advanced`], { queryParams: { workflow: ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, + claimedTask: taskId, }, }); }); diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts index 7b2aa015af..c7a2a2e545 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts @@ -8,7 +8,6 @@ import { NotificationsService } from '../../../notifications/notifications.servi import { TranslateService } from '@ngx-translate/core'; import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; -import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER @@ -40,15 +39,4 @@ export class AdvancedClaimedTaskActionSelectReviewerComponent extends AdvancedCl super(injector, router, notificationsService, translate, searchService, requestService); } - ngOnInit(): void { - this.initPageRoute(); - } - - /** - * Initialise the route to the select reviewer's page - */ - initPageRoute() { - this.workflowTaskPageRoute = getAdvancedWorkflowRoute(this.object.id); - } - } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index 29949a0ae3..d7a885f067 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -26,7 +26,7 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf multipleReviewers = true; - selectedReviewers: EPerson[]; + selectedReviewers: EPerson[] = []; reviewersListActionConfig: EPersonListActionConfig; diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts index bef619ce45..f40cde6fa6 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts @@ -41,6 +41,24 @@ export abstract class AdvancedWorkflowActionComponent extends WorkflowItemAction ); } + /** + * Performs the action and shows a notification based on the outcome of the action + */ + performAction() { + this.sendRequest(this.route.snapshot.queryParams.claimedTask).subscribe((successful: boolean) => { + if (successful) { + const title = this.translationService.get('workflow-item.' + this.type + '.notification.success.title'); + const content = this.translationService.get('workflow-item.' + this.type + '.notification.success.content'); + this.notificationsService.success(title, content); + this.previousPage(); + } else { + const title = this.translationService.get('workflow-item.' + this.type + '.notification.error.title'); + const content = this.translationService.get('workflow-item.' + this.type + '.notification.error.content'); + this.notificationsService.error(title, content); + } + }); + } + /** * Submits the task with the given {@link createBody}. * From 36891ef3bfe685669e924add39ee184c07a612db Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 30 Dec 2022 10:11:57 +0100 Subject: [PATCH 09/28] 98204: Edit collection assign roles tab breaks when no workflow groups --- .../collection-roles/collection-roles.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts b/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts index a6c37cbc45..68bdf7b561 100644 --- a/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts @@ -62,7 +62,7 @@ export class CollectionRolesComponent implements OnInit { name: 'bitstream_read', href: collection._links.bitstreamReadGroup.href, }, - ...collection._links.workflowGroups, + ...(collection._links.workflowGroups ? collection._links.workflowGroups : []), ]), ); } From ef9373baec155a16cef6df7a6b04f7fc8570b288 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 10 Jan 2023 17:56:32 +0100 Subject: [PATCH 10/28] 98376: Fixed collection-roles page for collections with single workflowGroup --- .../collection-roles.component.ts | 45 +++++++++++-------- src/assets/i18n/en.json5 | 5 +++ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts b/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts index 68bdf7b561..0177cc3a38 100644 --- a/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.ts @@ -6,6 +6,7 @@ import { RemoteData } from '../../../core/data/remote-data'; import { Collection } from '../../../core/shared/collection.model'; import { getRemoteDataPayload, getFirstSucceededRemoteData } from '../../../core/shared/operators'; import { HALLink } from '../../../core/shared/hal-link.model'; +import { hasValue } from '../../../shared/empty.util'; /** * Component for managing a collection's roles @@ -45,25 +46,31 @@ export class CollectionRolesComponent implements OnInit { ); this.comcolRoles$ = this.collection$.pipe( - map((collection) => [ - { - name: 'collection-admin', - href: collection._links.adminGroup.href, - }, - { - name: 'submitters', - href: collection._links.submittersGroup.href, - }, - { - name: 'item_read', - href: collection._links.itemReadGroup.href, - }, - { - name: 'bitstream_read', - href: collection._links.bitstreamReadGroup.href, - }, - ...(collection._links.workflowGroups ? collection._links.workflowGroups : []), - ]), + map((collection) => { + let workflowGroups: HALLink[] | HALLink = hasValue(collection._links.workflowGroups) ? collection._links.workflowGroups : []; + if (!Array.isArray(workflowGroups)) { + workflowGroups = [workflowGroups]; + } + return [ + { + name: 'collection-admin', + href: collection._links.adminGroup.href, + }, + { + name: 'submitters', + href: collection._links.submittersGroup.href, + }, + { + name: 'item_read', + href: collection._links.itemReadGroup.href, + }, + { + name: 'bitstream_read', + href: collection._links.bitstreamReadGroup.href, + }, + ...workflowGroups, + ]; + }), ); } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 6addb5a81b..97af59d159 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1179,6 +1179,11 @@ "comcol-role.edit.reviewer.description": "Reviewers are able to accept or reject incoming submissions. However, they are not able to edit the submission's metadata.", + "comcol-role.edit.scorereviewers.name": "Score Reviewers", + + "comcol-role.edit.scorereviewers.description": "Reviewers are able to give a score to incoming submissions, this will define whether the submission will be rejected or not.", + + "community.form.abstract": "Short Description", From 7cee2aac6f277989c5ed0ce8425b27665d07a7e0 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 10 Jan 2023 18:59:06 +0100 Subject: [PATCH 11/28] 98376: Added rating button to ratingreviewaction claimed task and created page --- package.json | 1 + ...claimed-task-action-rating.component.html} | 2 +- ...claimed-task-action-rating.component.scss} | 0 ...imed-task-action-rating.component.spec.ts} | 20 +++--- ...d-claimed-task-action-rating.component.ts} | 19 +++--- src/app/shared/shared.module.ts | 8 +-- ...flow-action-rating-reviewer.component.html | 1 - ...rkflow-action-rating-reviewer.component.ts | 15 ----- ...nced-workflow-action-rating.component.html | 44 +++++++++++++ ...ced-workflow-action-rating.component.scss} | 0 ...-workflow-action-rating.component.spec.ts} | 31 +++++++-- ...vanced-workflow-action-rating.component.ts | 66 +++++++++++++++++++ .../advanced-workflow-action.component.ts | 2 +- .../workflowitems-edit-page.module.ts | 40 +++++++---- src/assets/i18n/en.json5 | 30 ++++++++- yarn.lock | 7 ++ 16 files changed, 220 insertions(+), 66 deletions(-) rename src/app/shared/mydspace-actions/claimed-task/{rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html => rating/advanced-claimed-task-action-rating.component.html} (66%) rename src/app/shared/mydspace-actions/claimed-task/{rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.scss => rating/advanced-claimed-task-action-rating.component.scss} (100%) rename src/app/shared/mydspace-actions/claimed-task/{rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts => rating/advanced-claimed-task-action-rating.component.spec.ts} (83%) rename src/app/shared/mydspace-actions/claimed-task/{rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts => rating/advanced-claimed-task-action-rating.component.ts} (62%) delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.html rename src/app/workflowitems-edit-page/advanced-workflow-action/{advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.scss => advanced-workflow-action-rating/advanced-workflow-action-rating.component.scss} (100%) rename src/app/workflowitems-edit-page/advanced-workflow-action/{advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts => advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts} (72%) create mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts diff --git a/package.json b/package.json index 278afdf6c3..71b744fe77 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "ng-mocks": "11.11.2", "ng2-file-upload": "1.4.0", "ng2-nouislider": "^1.8.3", + "ngx-bootstrap": "7.1.2", "ngx-infinite-scroll": "^10.0.1", "ngx-moment": "^5.0.0", "ngx-pagination": "5.0.0", diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.html similarity index 66% rename from src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html rename to src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.html index 856b585fb3..5b63aa381e 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.html @@ -1,4 +1,4 @@ diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.scss b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.scss similarity index 100% rename from src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.scss rename to src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.scss diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.spec.ts similarity index 83% rename from src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts rename to src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.spec.ts index 563af2810e..c192a68aa7 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.spec.ts @@ -1,7 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - AdvancedClaimedTaskActionRatingReviewerComponent -} from './advanced-claimed-task-action-rating-reviewer.component'; +import { AdvancedClaimedTaskActionRatingComponent } from './advanced-claimed-task-action-rating.component'; import { By } from '@angular/platform-browser'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { ClaimedTaskDataServiceStub } from '../../../testing/claimed-task-data-service.stub'; @@ -17,23 +15,23 @@ import { Location } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { - ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER -} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; + ADVANCED_WORKFLOW_ACTION_RATING +} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component'; import { of as observableOf } from 'rxjs'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; const taskId = 'claimed-task-1'; const workflowId = 'workflow-1'; -describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { +describe('AdvancedClaimedTaskActionRatingComponent', () => { const object = Object.assign(new ClaimedTask(), { id: taskId, workflowitem: observableOf(Object.assign(new WorkflowItem(), { id: workflowId, })), }); - let component: AdvancedClaimedTaskActionRatingReviewerComponent; - let fixture: ComponentFixture; + let component: AdvancedClaimedTaskActionRatingComponent; + let fixture: ComponentFixture; let claimedTaskDataService: ClaimedTaskDataServiceStub; let notificationService: NotificationsServiceStub; @@ -51,7 +49,7 @@ describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { TranslateModule.forRoot(), ], declarations: [ - AdvancedClaimedTaskActionRatingReviewerComponent, + AdvancedClaimedTaskActionRatingComponent, ], providers: [ { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, @@ -66,7 +64,7 @@ describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { }); beforeEach(() => { - fixture = TestBed.createComponent(AdvancedClaimedTaskActionRatingReviewerComponent); + fixture = TestBed.createComponent(AdvancedClaimedTaskActionRatingComponent); component = fixture.componentInstance; component.object = object; fixture.detectChanges(); @@ -88,7 +86,7 @@ describe('AdvancedClaimedTaskActionRatingReviewerComponent', () => { expect(router.navigate).toHaveBeenCalledWith([`/workflowitems/${workflowId}/advanced`], { queryParams: { - workflow: ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER, + workflow: ADVANCED_WORKFLOW_ACTION_RATING, claimedTask: taskId, }, }); diff --git a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts similarity index 62% rename from src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts rename to src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts index 20fe58c80e..2c2983c25c 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts @@ -8,24 +8,25 @@ import { AdvancedClaimedTaskActionsAbstractComponent } from '../abstract/advanced-claimed-task-actions-abstract.component'; import { - ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER -} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; - -export const WORKFLOW_ADVANCED_TASK_OPTION_RATING_REVIEWER = 'submit_rating_reviewer'; + ADVANCED_WORKFLOW_ACTION_RATING, + WORKFLOW_ADVANCED_TASK_OPTION_RATING, +} from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +@rendersWorkflowTaskOption(WORKFLOW_ADVANCED_TASK_OPTION_RATING) @Component({ selector: 'ds-advanced-claimed-task-action-rating-reviewer', - templateUrl: './advanced-claimed-task-action-rating-reviewer.component.html', - styleUrls: ['./advanced-claimed-task-action-rating-reviewer.component.scss'] + templateUrl: './advanced-claimed-task-action-rating.component.html', + styleUrls: ['./advanced-claimed-task-action-rating.component.scss'] }) -export class AdvancedClaimedTaskActionRatingReviewerComponent extends AdvancedClaimedTaskActionsAbstractComponent { +export class AdvancedClaimedTaskActionRatingComponent extends AdvancedClaimedTaskActionsAbstractComponent { /** * This component represents the advanced select option */ - option = WORKFLOW_ADVANCED_TASK_OPTION_RATING_REVIEWER; + option = WORKFLOW_ADVANCED_TASK_OPTION_RATING; - workflowType = ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER; + workflowType = ADVANCED_WORKFLOW_ACTION_RATING; constructor( protected injector: Injector, diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 1cc0a86115..d5bb76c295 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -179,8 +179,8 @@ import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { AdvancedClaimedTaskActionSelectReviewerComponent } from './mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component'; import { - AdvancedClaimedTaskActionRatingReviewerComponent -} from './mydspace-actions/claimed-task/rating-reviewer/advanced-claimed-task-action-rating-reviewer.component'; + AdvancedClaimedTaskActionRatingComponent +} from './mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component'; import { ClaimedTaskActionsDeclineTaskComponent } from './mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component'; const MODULES = [ @@ -353,7 +353,7 @@ const COMPONENTS = [ SearchNavbarComponent, ScopeSelectorModalComponent, AdvancedClaimedTaskActionSelectReviewerComponent, - AdvancedClaimedTaskActionRatingReviewerComponent, + AdvancedClaimedTaskActionRatingComponent, ]; const ENTRY_COMPONENTS = [ @@ -412,7 +412,7 @@ const ENTRY_COMPONENTS = [ TextMenuItemComponent, ScopeSelectorModalComponent, AdvancedClaimedTaskActionSelectReviewerComponent, - AdvancedClaimedTaskActionRatingReviewerComponent, + AdvancedClaimedTaskActionRatingComponent, ]; const SHARED_ITEM_PAGE_COMPONENTS = [ diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html deleted file mode 100644 index 1b26ba3599..0000000000 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.html +++ /dev/null @@ -1 +0,0 @@ -

advanced-workflow-action-rating-reviewer works!

diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts deleted file mode 100644 index 4e0185401d..0000000000 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component } from '@angular/core'; -import { - rendersAdvancedWorkflowTaskOption -} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; - -export const ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER = 'ratingrevieweraction'; - -@rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_RATING_REVIEWER) -@Component({ - selector: 'ds-advanced-workflow-action-rating-reviewer', - templateUrl: './advanced-workflow-action-rating-reviewer.component.html', - styleUrls: ['./advanced-workflow-action-rating-reviewer.component.scss'] -}) -export class AdvancedWorkflowActionRatingReviewerComponent { -} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.html new file mode 100644 index 0000000000..c74006550c --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.html @@ -0,0 +1,44 @@ +
+

+ {{ 'advanced-workflow-action.rating.description-requiredDescription' | translate }} +

+

+ {{ 'advanced-workflow-action.rating.description' | translate }} +

+ +
+
+ + + + {{ 'advanced-workflow-action.rating.form.review.error' | translate }} + +
+ +
+ + + +
+
+ + + + +
+ + +
+
diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.scss similarity index 100% rename from src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.scss rename to src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.scss diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts similarity index 72% rename from src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts rename to src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts index 244e397303..a75d4e30a2 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdvancedWorkflowActionRatingReviewerComponent } from './advanced-workflow-action-rating-reviewer.component'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { AdvancedWorkflowActionRatingComponent } from './advanced-workflow-action-rating.component'; +import { ActivatedRoute, convertToParamMap, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -12,12 +12,17 @@ import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { WorkflowActionDataServiceStub } from '../../../shared/testing/workflow-action-data-service.stub'; import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; +import { RouterStub } from '../../../shared/testing/router.stub'; +import { TranslateModule } from '@ngx-translate/core'; +import { VarDirective } from '../../../shared/utils/var.directive'; +import { RatingModule } from 'ngx-bootstrap/rating'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; const workflowId = '1'; -describe('AdvancedWorkflowActionRatingReviewerComponent', () => { - let component: AdvancedWorkflowActionRatingReviewerComponent; - let fixture: ComponentFixture; +describe('AdvancedWorkflowActionRatingComponent', () => { + let component: AdvancedWorkflowActionRatingComponent; + let fixture: ComponentFixture; let claimedTaskDataService: ClaimedTaskDataServiceStub; let notificationService: NotificationsServiceStub; @@ -31,8 +36,15 @@ describe('AdvancedWorkflowActionRatingReviewerComponent', () => { workflowItemDataService = new WorkflowItemDataServiceStub(); await TestBed.configureTestingModule({ + imports: [ + FormsModule, + RatingModule, + ReactiveFormsModule, + TranslateModule.forRoot(), + ], declarations: [ - AdvancedWorkflowActionRatingReviewerComponent, + AdvancedWorkflowActionRatingComponent, + VarDirective, ], providers: [ { @@ -51,6 +63,7 @@ describe('AdvancedWorkflowActionRatingReviewerComponent', () => { { provide: ClaimedTaskDataService, useValue: claimedTaskDataService }, { provide: NotificationsService, useValue: notificationService }, { provide: RouteService, useValue: routeServiceStub }, + { provide: Router, useValue: new RouterStub() }, { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, ], @@ -58,11 +71,15 @@ describe('AdvancedWorkflowActionRatingReviewerComponent', () => { }); beforeEach(() => { - fixture = TestBed.createComponent(AdvancedWorkflowActionRatingReviewerComponent); + fixture = TestBed.createComponent(AdvancedWorkflowActionRatingComponent); component = fixture.componentInstance; fixture.detectChanges(); }); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + it('should create', () => { expect(component).toBeTruthy(); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts new file mode 100644 index 0000000000..c669cfcfe9 --- /dev/null +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts @@ -0,0 +1,66 @@ +import { Component, OnInit } from '@angular/core'; +import { + rendersAdvancedWorkflowTaskOption +} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; +import { AdvancedWorkflowActionComponent } from '../advanced-workflow-action/advanced-workflow-action.component'; +import { FormGroup, FormControl } from '@angular/forms'; +import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; +import { + RatingReviewerActionAdvancedInfo +} from '../../../core/tasks/models/rating-reviewer-action-advanced-info.model'; + +export const WORKFLOW_ADVANCED_TASK_OPTION_RATING = 'rating'; +export const ADVANCED_WORKFLOW_ACTION_RATING = 'ratingreviewaction'; + +@rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_RATING) +@Component({ + selector: 'ds-advanced-workflow-action-rating-reviewer', + templateUrl: './advanced-workflow-action-rating.component.html', + styleUrls: ['./advanced-workflow-action-rating.component.scss'], + preserveWhitespaces: false, +}) +export class AdvancedWorkflowActionRatingComponent extends AdvancedWorkflowActionComponent implements OnInit { + + ratingForm: FormGroup; + + ngOnInit() { + super.ngOnInit(); + this.ratingForm = new FormGroup({ + review: new FormControl(''), + rating: new FormControl(0), + }); + } + + /** + * Only run **performAction()** when the form has been correctly filled in + */ + performAction(): void { + this.ratingForm.updateValueAndValidity(); + if (this.ratingForm.valid) { + super.performAction(); + } else { + this.ratingForm.markAllAsTouched(); + } + } + + createBody(): any { + const body = { + [WORKFLOW_ADVANCED_TASK_OPTION_RATING]: true, + score: this.ratingForm.get('rating').value, + }; + if (this.ratingForm.get('review').value !== '') { + const description: string = this.ratingForm.get('review').value; + Object.assign(body, { description }); + } + return body; + } + + getType(): string { + return ADVANCED_WORKFLOW_ACTION_RATING; + } + + getAdvancedInfo(workflowAction: WorkflowAction | null): RatingReviewerActionAdvancedInfo | null { + return workflowAction ? (workflowAction.advancedInfo[0] as RatingReviewerActionAdvancedInfo) : null; + } + +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts index f40cde6fa6..256a4d8915 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts @@ -44,7 +44,7 @@ export abstract class AdvancedWorkflowActionComponent extends WorkflowItemAction /** * Performs the action and shows a notification based on the outcome of the action */ - performAction() { + performAction(): void { this.sendRequest(this.route.snapshot.queryParams.claimedTask).subscribe((successful: boolean) => { if (successful) { const title = this.translationService.get('workflow-item.' + this.type + '.notification.success.title'); diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts index 080ca96468..dc60bd7d2b 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -6,15 +6,23 @@ import { SubmissionModule } from '../submission/submission.module'; import { WorkflowItemDeleteComponent } from './workflow-item-delete/workflow-item-delete.component'; import { WorkflowItemSendBackComponent } from './workflow-item-send-back/workflow-item-send-back.component'; import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component'; -import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component'; +import { + ThemedWorkflowItemSendBackComponent +} from './workflow-item-send-back/themed-workflow-item-send-back.component'; import { StatisticsModule } from '../statistics/statistics.module'; import { ItemPageModule } from '../item-page/item-page.module'; import { AdvancedWorkflowActionsLoaderComponent } from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component'; -import { AdvancedWorkflowActionRatingReviewerComponent } from './advanced-workflow-action/advanced-workflow-action-rating-reviewer/advanced-workflow-action-rating-reviewer.component'; -import { AdvancedWorkflowActionSelectReviewerComponent } from './advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; -import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; +import { + AdvancedWorkflowActionRatingComponent +} from './advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component'; +import { + AdvancedWorkflowActionSelectReviewerComponent +} from './advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; +import { + AdvancedWorkflowActionPageComponent +} from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; import { AdvancedClaimedTaskActionsDirective } from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive'; @@ -22,24 +30,28 @@ import { AccessControlModule } from '../access-control/access-control.module'; import { ReviewersListComponent } from './advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component'; +import { FormModule } from '../shared/form/form.module'; +import { RatingModule } from 'ngx-bootstrap/rating'; @NgModule({ - imports: [ - WorkflowItemsEditPageRoutingModule, - CommonModule, - SharedModule, - SubmissionModule, - StatisticsModule, - ItemPageModule, - AccessControlModule - ], + imports: [ + WorkflowItemsEditPageRoutingModule, + CommonModule, + SharedModule, + SubmissionModule, + StatisticsModule, + ItemPageModule, + AccessControlModule, + FormModule, + RatingModule, + ], declarations: [ WorkflowItemDeleteComponent, ThemedWorkflowItemDeleteComponent, WorkflowItemSendBackComponent, ThemedWorkflowItemSendBackComponent, AdvancedWorkflowActionsLoaderComponent, - AdvancedWorkflowActionRatingReviewerComponent, + AdvancedWorkflowActionRatingComponent, AdvancedWorkflowActionSelectReviewerComponent, AdvancedWorkflowActionPageComponent, AdvancedClaimedTaskActionsDirective, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 97af59d159..57f93ee906 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -539,6 +539,17 @@ "admin.metadata-import.page.error.addFile": "Select file first!", + "advanced-workflow-action.rating.form.rating.label": "Rating", + + "advanced-workflow-action.rating.form.review.label": "Review", + + "advanced-workflow-action.rating.form.review.error": "You must enter a review to submit this rating", + + "advanced-workflow-action.rating.description": "Please select a rating below", + + "advanced-workflow-action.rating.description-requiredDescription": "Please select a rating below and also add a review", + + "advanced-workflow-action.select-reviewer.description-single": "Please select a single reviewer below before submitting", "advanced-workflow-action.select-reviewer.description-multiple": "Please select one or more reviewers below before submitting", @@ -4061,9 +4072,9 @@ "submission.workflow.generic.submit_select_reviewer-help": "", - "submission.workflow.generic.submit_rating_reviewer": "Rating Reviewer", + "submission.workflow.generic.rating": "Rate", - "submission.workflow.generic.submit_rating_reviewer-help": "", + "submission.workflow.generic.rating-help": "", "submission.workflow.tasks.claimed.approve": "Approve", @@ -4241,8 +4252,21 @@ "workflow-item.selectrevieweraction.button.confirm": "Confirm", - "workflow-item.ratingrevieweraction.header": "Rating Reviewer", + "workflow-item.ratingreviewaction.notification.success.title": "Rating review", + "workflow-item.ratingreviewaction.notification.success.content": "The rating for this item workflow item has been successfully submitted", + + "workflow-item.ratingreviewaction.notification.error.title": "Something went wrong", + + "workflow-item.ratingreviewaction.notification.error.content": "Couldn't rate this item", + + "workflow-item.ratingreviewaction.title": "Rate this item", + + "workflow-item.ratingreviewaction.header": "Rate this item", + + "workflow-item.ratingreviewaction.button.cancel": "Cancel", + + "workflow-item.ratingreviewaction.button.confirm": "Confirm", "idle-modal.header": "Session will expire soon", diff --git a/yarn.lock b/yarn.lock index 0f8ef0a6e6..555bce24ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9357,6 +9357,13 @@ ng2-nouislider@^1.8.3: resolved "https://registry.yarnpkg.com/ng2-nouislider/-/ng2-nouislider-1.8.3.tgz#c9c1ec77b6b5a909f87368ba799914208baa13a6" integrity sha512-Vl8tHCcJ/ioJLAs2t6FBC35sZq1P/O5ZdqdFwYxOCOMVbILGWNg+2gWZIjFstvv9pqb/mVvVUYe6qGG/mA/RBQ== +ngx-bootstrap@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-7.1.2.tgz#cf06a05cc22a74b882b45882a1f64f72ddece23d" + integrity sha512-VsZwlGVM+qYOAdvEzzH3wqCjvj3yHec4n82Tho0ve6mhN1GTRu9WPrPmvxp3cFjIu8VQbREUeKPfK0ooO8U5eg== + dependencies: + tslib "^2.0.0" + ngx-infinite-scroll@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-10.0.1.tgz#6f51f2f8775a7c50d1dd8bad125d4e748abbe880" From 11b6ec9a9e86f2b39a1fd6b588681b483f879e5a Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 11 Jan 2023 20:28:54 +0100 Subject: [PATCH 12/28] 98376: Replaced EPersonListComponent with MembersListComponent & display error for empty reviewers list --- .../access-control/access-control.module.ts | 4 +- .../eperson-list/eperson-list.component.html | 146 ------- .../eperson-list.component.spec.ts | 247 ------------ .../eperson-list/eperson-list.component.ts | 358 ------------------ .../members-list/members-list.component.html | 29 +- .../members-list/members-list.component.ts | 131 ++++--- .../modify-item-overview.component.html | 2 +- ...flow-action-select-reviewer.component.html | 5 +- ...rkflow-action-select-reviewer.component.ts | 13 +- .../reviewers-list.component.ts | 11 +- src/assets/i18n/en.json5 | 2 + 11 files changed, 123 insertions(+), 825 deletions(-) delete mode 100644 src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html delete mode 100644 src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts delete mode 100644 src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts diff --git a/src/app/access-control/access-control.module.ts b/src/app/access-control/access-control.module.ts index 2c9932d387..99aaaaf133 100644 --- a/src/app/access-control/access-control.module.ts +++ b/src/app/access-control/access-control.module.ts @@ -10,7 +10,6 @@ import { MembersListComponent } from './group-registry/group-form/members-list/m import { SubgroupsListComponent } from './group-registry/group-form/subgroup-list/subgroups-list.component'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; import { FormModule } from '../shared/form/form.module'; -import { EPersonListComponent } from './group-registry/group-form/eperson-list/eperson-list.component'; @NgModule({ imports: [ @@ -21,7 +20,7 @@ import { EPersonListComponent } from './group-registry/group-form/eperson-list/e FormModule, ], exports: [ - EPersonListComponent, + MembersListComponent, ], declarations: [ EPeopleRegistryComponent, @@ -30,7 +29,6 @@ import { EPersonListComponent } from './group-registry/group-form/eperson-list/e GroupFormComponent, SubgroupsListComponent, MembersListComponent, - EPersonListComponent, ], }) /** diff --git a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html deleted file mode 100644 index e4a507ae19..0000000000 --- a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.html +++ /dev/null @@ -1,146 +0,0 @@ - -

{{messagePrefix + '.head' | translate}}

- - -
-
- -
-
-
- - - - -
-
-
- -
-
- - - -
- - - - - - - - - - - - - - - - - -
{{messagePrefix + '.table.id' | translate}}{{messagePrefix + '.table.name' | translate}}{{messagePrefix + '.table.identity' | translate}}{{messagePrefix + '.table.edit' | translate}}
{{ePerson.eperson.id}}{{ePerson.eperson.name}} - {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
- {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} -
-
- - - -
-
-
- -
- - - -

{{messagePrefix + '.headMembers' | translate}}

- - - -
- - - - - - - - - - - - - - - - - -
{{messagePrefix + '.table.id' | translate}}{{messagePrefix + '.table.name' | translate}}{{messagePrefix + '.table.identity' | translate}}{{messagePrefix + '.table.edit' | translate}}
{{ePerson.eperson.id}}{{ePerson.eperson.name}} - {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
- {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} -
-
- - -
-
-
- -
- - - -
diff --git a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts deleted file mode 100644 index 8077139026..0000000000 --- a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.spec.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { BrowserModule, By } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { Observable, of as observableOf } from 'rxjs'; -import { RestResponse } from '../../../../core/cache/response.models'; -import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; -import { GroupDataService } from '../../../../core/eperson/group-data.service'; -import { EPerson } from '../../../../core/eperson/models/eperson.model'; -import { Group } from '../../../../core/eperson/models/group.model'; -import { PageInfo } from '../../../../core/shared/page-info.model'; -import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; -import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock'; -import { EPersonListComponent } from './eperson-list.component'; -import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock'; -import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock'; -import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock'; -import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock'; -import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; -import { RouterMock } from '../../../../shared/mocks/router.mock'; -import { PaginationService } from '../../../../core/pagination/pagination.service'; -import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; - -describe('EPersonListComponent', () => { - let component: EPersonListComponent; - let fixture: ComponentFixture; - let translateService: TranslateService; - let builderService: FormBuilderService; - let ePersonDataServiceStub: any; - let groupsDataServiceStub: any; - let activeGroup; - let allEPersons; - let allGroups; - let epersonMembers; - let subgroupMembers; - let paginationService; - - beforeEach(waitForAsync(() => { - activeGroup = GroupMock; - epersonMembers = [EPersonMock2]; - subgroupMembers = [GroupMock2]; - allEPersons = [EPersonMock, EPersonMock2]; - allGroups = [GroupMock, GroupMock2]; - ePersonDataServiceStub = { - activeGroup: activeGroup, - epersonMembers: epersonMembers, - subgroupMembers: subgroupMembers, - findAllByHref(href: string): Observable>> { - return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getEPersonMembers())); - }, - searchByScope(scope: string, query: string): Observable>> { - if (query === '') { - return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allEPersons)); - } - return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); - }, - clearEPersonRequests() { - // empty - }, - clearLinkRequests() { - // empty - }, - getEPeoplePageRouterLink(): string { - return '/access-control/epeople'; - } - }; - groupsDataServiceStub = { - activeGroup: activeGroup, - epersonMembers: epersonMembers, - subgroupMembers: subgroupMembers, - allGroups: allGroups, - getActiveGroup(): Observable { - return observableOf(activeGroup); - }, - getEPersonMembers() { - return this.epersonMembers; - }, - searchGroups(query: string): Observable>> { - if (query === '') { - return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), this.allGroups)); - } - return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); - }, - addMemberToGroup(parentGroup, eperson: EPerson): Observable { - this.epersonMembers = [...this.epersonMembers, eperson]; - return observableOf(new RestResponse(true, 200, 'Success')); - }, - clearGroupsRequests() { - // empty - }, - clearGroupLinkRequests() { - // empty - }, - getGroupEditPageRouterLink(group: Group): string { - return '/access-control/groups/' + group.id; - }, - deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable { - this.epersonMembers = this.epersonMembers.find((eperson: EPerson) => { - if (eperson.id !== epersonToDelete.id) { - return eperson; - } - }); - if (this.epersonMembers === undefined) { - this.epersonMembers = []; - } - return observableOf(new RestResponse(true, 200, 'Success')); - } - }; - builderService = getMockFormBuilderService(); - translateService = getMockTranslateService(); - - paginationService = new PaginationServiceStub(); - TestBed.configureTestingModule({ - imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), - ], - declarations: [EPersonListComponent], - providers: [EPersonListComponent, - { provide: EPersonDataService, useValue: ePersonDataServiceStub }, - { provide: GroupDataService, useValue: groupsDataServiceStub }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() }, - { provide: FormBuilderService, useValue: builderService }, - { provide: Router, useValue: new RouterMock() }, - { provide: PaginationService, useValue: paginationService }, - ], - schemas: [NO_ERRORS_SCHEMA] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(EPersonListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - afterEach(fakeAsync(() => { - fixture.destroy(); - flush(); - component = null; - fixture.debugElement.nativeElement.remove(); - })); - - it('should create EpeopleListComponent', inject([EPersonListComponent], (comp: EPersonListComponent) => { - expect(comp).toBeDefined(); - })); - - it('should show list of eperson members of current active group', () => { - const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); - expect(epersonIdsFound.length).toEqual(1); - epersonMembers.map((eperson: EPerson) => { - expect(epersonIdsFound.find((foundEl) => { - return (foundEl.nativeElement.textContent.trim() === eperson.uuid); - })).toBeTruthy(); - }); - }); - - describe('search', () => { - describe('when searching without query', () => { - let epersonsFound; - beforeEach(fakeAsync(() => { - component.search({ scope: 'metadata', query: '' }); - tick(); - fixture.detectChanges(); - epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); - })); - - it('should display all epersons', () => { - expect(epersonsFound.length).toEqual(2); - }); - - describe('if eperson is already a eperson', () => { - it('should have delete button, else it should have add button', () => { - activeGroup.epersons.map((eperson: EPerson) => { - epersonsFound.map((foundEPersonRowElement) => { - if (foundEPersonRowElement.debugElement !== undefined) { - const epersonId = foundEPersonRowElement.debugElement.query(By.css('td:first-child')); - const addButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-plus')); - const deleteButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-trash-alt')); - if (epersonId.nativeElement.textContent === eperson.id) { - expect(addButton).toBeUndefined(); - expect(deleteButton).toBeDefined(); - } else { - expect(deleteButton).toBeUndefined(); - expect(addButton).toBeDefined(); - } - } - }); - }); - }); - }); - - describe('if first add button is pressed', () => { - beforeEach(fakeAsync(() => { - const addButton = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-plus')); - addButton.nativeElement.click(); - tick(); - fixture.detectChanges(); - })); - it('all groups in search member of selected group', () => { - epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); - expect(epersonsFound.length).toEqual(2); - epersonsFound.map((foundEPersonRowElement) => { - if (foundEPersonRowElement.debugElement !== undefined) { - const addButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-plus')); - const deleteButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-trash-alt')); - expect(addButton).toBeUndefined(); - expect(deleteButton).toBeDefined(); - } - }); - }); - }); - - describe('if first delete button is pressed', () => { - beforeEach(fakeAsync(() => { - const addButton = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-trash-alt')); - addButton.nativeElement.click(); - tick(); - fixture.detectChanges(); - })); - it('first eperson in search delete button, because now member', () => { - epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); - epersonsFound.map((foundEPersonRowElement) => { - if (foundEPersonRowElement.debugElement !== undefined) { - const addButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-plus')); - const deleteButton = foundEPersonRowElement.debugElement.query(By.css('td:last-child .fa-trash-alt')); - expect(deleteButton).toBeUndefined(); - expect(addButton).toBeDefined(); - } - }); - }); - }); - }); - }); - -}); diff --git a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts b/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts deleted file mode 100644 index 9eafe10daa..0000000000 --- a/src/app/access-control/group-registry/group-form/eperson-list/eperson-list.component.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { Component, OnDestroy, OnInit, Input } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; -import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; -import { GroupDataService } from '../../../../core/eperson/group-data.service'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; -import { PaginationService } from '../../../../core/pagination/pagination.service'; -import { Group } from '../../../../core/eperson/models/group.model'; -import { - getAllCompletedRemoteData, - getFirstSucceededRemoteData, - getRemoteDataPayload, - getFirstCompletedRemoteData -} from '../../../../core/shared/operators'; -import { - BehaviorSubject, - Subscription, - combineLatest as observableCombineLatest, - Observable, - ObservedValueOf, - of as observableOf -} from 'rxjs'; -import { PaginatedList, buildPaginatedList } from '../../../../core/data/paginated-list.model'; -import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; -import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; -import { switchMap, map, take, mergeMap } from 'rxjs/operators'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { EPerson } from '../../../../core/eperson/models/eperson.model'; - -/** - * Keys to keep track of specific subscriptions - */ -enum SubKey { - ActiveGroup, - MembersDTO, - SearchResultsDTO, -} - -export interface EPersonActionConfig { - css?: string; - disabled: boolean; - icon: string; -} - -export interface EPersonListActionConfig { - add: EPersonActionConfig; - remove: EPersonActionConfig; -} - -@Component({ - selector: 'ds-eperson-list', - templateUrl: './eperson-list.component.html' -}) -export class EPersonListComponent implements OnInit, OnDestroy { - - @Input() - messagePrefix: string; - - @Input() - actionConfig: EPersonListActionConfig = { - add: { - css: 'btn-outline-primary', - disabled: false, - icon: 'fas fa-plus fa-fw', - }, - remove: { - css: 'btn-outline-danger', - disabled: false, - icon: 'fas fa-trash-alt fa-fw' - }, - }; - - /** - * EPeople being displayed in search result, initially all members, after search result of search - */ - ePeopleSearchDtos: BehaviorSubject> = new BehaviorSubject>(undefined); - /** - * List of EPeople members of currently active group being edited - */ - ePeopleMembersOfGroupDtos: BehaviorSubject> = new BehaviorSubject>(undefined); - - /** - * Pagination config used to display the list of EPeople that are result of EPeople search - */ - configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'sml', - pageSize: 5, - currentPage: 1 - }); - /** - * Pagination config used to display the list of EPerson Membes of active group being edited - */ - config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'ml', - pageSize: 5, - currentPage: 1 - }); - - /** - * Map of active subscriptions - */ - subs: Map = new Map(); - - // The search form - searchForm; - - // Current search in edit group - epeople search form - currentSearchQuery: string; - currentSearchScope: string; - - // Whether or not user has done a EPeople search yet - searchDone: boolean; - - // current active group being edited - groupBeingEdited: Group; - - constructor( - protected groupDataService: GroupDataService, - public ePersonDataService: EPersonDataService, - protected translateService: TranslateService, - protected notificationsService: NotificationsService, - protected formBuilder: FormBuilder, - protected paginationService: PaginationService, - private router: Router - ) { - this.currentSearchQuery = ''; - this.currentSearchScope = 'metadata'; - } - - ngOnInit(): void { - this.searchForm = this.formBuilder.group(({ - scope: 'metadata', - query: '', - })); - this.subs.set(SubKey.ActiveGroup, this.groupDataService.getActiveGroup().subscribe((activeGroup: Group) => { - if (activeGroup != null) { - this.groupBeingEdited = activeGroup; - this.retrieveMembers(this.config.currentPage); - } - })); - } - - /** - * Retrieve the EPersons that are members of the group - * - * @param page the number of the page to retrieve - * @private - */ - retrieveMembers(page: number): void { - this.unsubFrom(SubKey.MembersDTO); - this.subs.set(SubKey.MembersDTO, - this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( - switchMap((currentPagination) => { - return this.ePersonDataService.findAllByHref(this.groupBeingEdited._links.epersons.href, { - currentPage: currentPagination.currentPage, - elementsPerPage: currentPagination.pageSize - } - ); - }), - getAllCompletedRemoteData(), - map((rd: RemoteData) => { - if (rd.hasFailed) { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage })); - } else { - return rd; - } - }), - switchMap((epersonListRD: RemoteData>) => { - const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => { - const dto$: Observable = observableCombineLatest( - this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { - const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); - epersonDtoModel.eperson = member; - epersonDtoModel.memberOfGroup = isMember; - return epersonDtoModel; - }); - return dto$; - })); - return dtos$.pipe(map((dtos: EpersonDtoModel[]) => { - return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); - })); - })) - .subscribe((paginatedListOfDTOs: PaginatedList) => { - this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs); - })); - } - - /** - * Whether the given ePerson is a member of the group currently being edited - * @param possibleMember EPerson that is a possible member (being tested) of the group currently being edited - */ - isMemberOfGroup(possibleMember: EPerson): Observable { - return this.groupDataService.getActiveGroup().pipe(take(1), - mergeMap((group: Group) => { - if (group != null) { - return this.ePersonDataService.findAllByHref(group._links.epersons.href, { - currentPage: 1, - elementsPerPage: 9999 - }, false) - .pipe( - getFirstSucceededRemoteData(), - getRemoteDataPayload(), - map((listEPeopleInGroup: PaginatedList) => listEPeopleInGroup.page.filter((ePersonInList: EPerson) => ePersonInList.id === possibleMember.id)), - map((epeople: EPerson[]) => epeople.length > 0)); - } else { - return observableOf(false); - } - })); - } - - /** - * Unsubscribe from a subscription if it's still subscribed, and remove it from the map of - * active subscriptions - * - * @param key The key of the subscription to unsubscribe from - * @private - */ - protected unsubFrom(key: SubKey) { - if (this.subs.has(key)) { - this.subs.get(key).unsubscribe(); - this.subs.delete(key); - } - } - - /** - * Deletes a given EPerson from the members list of the group currently being edited - * @param ePerson EPerson we want to delete as member from group that is currently being edited - */ - deleteMemberFromGroup(ePerson: EpersonDtoModel) { - this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { - if (activeGroup != null) { - const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson); - this.showNotifications('deleteMember', response, ePerson.eperson.name, activeGroup); - this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery }); - } else { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); - } - }); - } - - /** - * Adds a given EPerson to the members list of the group currently being edited - * @param ePerson EPerson we want to add as member to group that is currently being edited - */ - addMemberToGroup(ePerson: EpersonDtoModel) { - ePerson.memberOfGroup = true; - this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { - if (activeGroup != null) { - const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson); - this.showNotifications('addMember', response, ePerson.eperson.name, activeGroup); - } else { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); - } - }); - } - - /** - * Search in the EPeople by name, email or metadata - * @param data Contains scope and query param - */ - search(data: any) { - this.unsubFrom(SubKey.SearchResultsDTO); - this.subs.set(SubKey.SearchResultsDTO, - this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe( - switchMap((paginationOptions) => { - - const query: string = data.query; - const scope: string = data.scope; - if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) { - this.router.navigate([], { - queryParamsHandling: 'merge' - }); - this.currentSearchQuery = query; - this.paginationService.resetPage(this.configSearch.id); - } - if (scope != null && this.currentSearchScope !== scope && this.groupBeingEdited) { - this.router.navigate([], { - queryParamsHandling: 'merge' - }); - this.currentSearchScope = scope; - this.paginationService.resetPage(this.configSearch.id); - } - this.searchDone = true; - - return this.ePersonDataService.searchByScope(this.currentSearchScope, this.currentSearchQuery, { - currentPage: paginationOptions.currentPage, - elementsPerPage: paginationOptions.pageSize - }); - }), - getAllCompletedRemoteData(), - map((rd: RemoteData) => { - if (rd.hasFailed) { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage })); - } else { - return rd; - } - }), - switchMap((epersonListRD: RemoteData>) => { - const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => { - const dto$: Observable = observableCombineLatest( - this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { - const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); - epersonDtoModel.eperson = member; - epersonDtoModel.memberOfGroup = isMember; - return epersonDtoModel; - }); - return dto$; - })); - return dtos$.pipe(map((dtos: EpersonDtoModel[]) => { - return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); - })); - })) - .subscribe((paginatedListOfDTOs: PaginatedList) => { - this.ePeopleSearchDtos.next(paginatedListOfDTOs); - })); - } - - /** - * unsub all subscriptions - */ - ngOnDestroy(): void { - for (const key of this.subs.keys()) { - this.unsubFrom(key); - } - this.paginationService.clearPagination(this.config.id); - this.paginationService.clearPagination(this.configSearch.id); - } - - /** - * Shows a notification based on the success/failure of the request - * @param messageSuffix Suffix for message - * @param response RestResponse observable containing success/failure request - * @param nameObject Object request was about - * @param activeGroup Group currently being edited - */ - showNotifications(messageSuffix: string, response: Observable>, nameObject: string, activeGroup: Group) { - response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData) => { - if (rd.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.success.' + messageSuffix, { name: nameObject })); - this.ePersonDataService.clearLinkRequests(activeGroup._links.epersons.href); - } else { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.' + messageSuffix, { name: nameObject })); - } - }); - } - - /** - * Reset all input-fields to be empty and search all search - */ - clearFormAndResetResult() { - this.searchForm.patchValue({ - query: '', - }); - this.search({ query: '' }); - } - -} diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html index e5932edf05..e4a507ae19 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html @@ -55,18 +55,20 @@
- -
@@ -113,10 +115,19 @@
- +
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts index 8bc540641e..bdeb49decd 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts @@ -1,30 +1,32 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, Input } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { - Observable, - of as observableOf, - Subscription, - BehaviorSubject, - combineLatest as observableCombineLatest, - ObservedValueOf, -} from 'rxjs'; -import { map, mergeMap, switchMap, take } from 'rxjs/operators'; -import {buildPaginatedList, PaginatedList} from '../../../../core/data/paginated-list.model'; -import { RemoteData } from '../../../../core/data/remote-data'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service'; -import { EPerson } from '../../../../core/eperson/models/eperson.model'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; import { Group } from '../../../../core/eperson/models/group.model'; import { + getAllCompletedRemoteData, getFirstSucceededRemoteData, - getFirstCompletedRemoteData, getAllCompletedRemoteData, getRemoteDataPayload + getRemoteDataPayload, + getFirstCompletedRemoteData } from '../../../../core/shared/operators'; -import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { + BehaviorSubject, + Subscription, + combineLatest as observableCombineLatest, + Observable, + ObservedValueOf, + of as observableOf +} from 'rxjs'; +import { PaginatedList, buildPaginatedList } from '../../../../core/data/paginated-list.model'; +import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; -import {EpersonDtoModel} from '../../../../core/eperson/models/eperson-dto.model'; -import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { switchMap, map, take, mergeMap } from 'rxjs/operators'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { EPerson } from '../../../../core/eperson/models/eperson.model'; /** * Keys to keep track of specific subscriptions @@ -35,6 +37,17 @@ enum SubKey { SearchResultsDTO, } +export interface EPersonActionConfig { + css?: string; + disabled: boolean; + icon: string; +} + +export interface EPersonListActionConfig { + add: EPersonActionConfig; + remove: EPersonActionConfig; +} + @Component({ selector: 'ds-members-list', templateUrl: './members-list.component.html' @@ -47,6 +60,20 @@ export class MembersListComponent implements OnInit, OnDestroy { @Input() messagePrefix: string; + @Input() + actionConfig: EPersonListActionConfig = { + add: { + css: 'btn-outline-primary', + disabled: false, + icon: 'fas fa-plus fa-fw', + }, + remove: { + css: 'btn-outline-danger', + disabled: false, + icon: 'fas fa-trash-alt fa-fw' + }, + }; + /** * EPeople being displayed in search result, initially all members, after search result of search */ @@ -91,23 +118,20 @@ export class MembersListComponent implements OnInit, OnDestroy { // current active group being edited groupBeingEdited: Group; - paginationSub: Subscription; - - constructor( protected groupDataService: GroupDataService, public ePersonDataService: EPersonDataService, - private translateService: TranslateService, - private notificationsService: NotificationsService, + protected translateService: TranslateService, + protected notificationsService: NotificationsService, protected formBuilder: FormBuilder, - private paginationService: PaginationService, + protected paginationService: PaginationService, private router: Router ) { this.currentSearchQuery = ''; this.currentSearchScope = 'metadata'; } - ngOnInit() { + ngOnInit(): void { this.searchForm = this.formBuilder.group(({ scope: 'metadata', query: '', @@ -126,7 +150,7 @@ export class MembersListComponent implements OnInit, OnDestroy { * @param page the number of the page to retrieve * @private */ - protected retrieveMembers(page: number) { + retrieveMembers(page: number): void { this.unsubFrom(SubKey.MembersDTO); this.subs.set(SubKey.MembersDTO, this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( @@ -137,36 +161,36 @@ export class MembersListComponent implements OnInit, OnDestroy { } ); }), - getAllCompletedRemoteData(), - map((rd: RemoteData) => { - if (rd.hasFailed) { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', {cause: rd.errorMessage})); - } else { - return rd; - } - }), - switchMap((epersonListRD: RemoteData>) => { - const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => { - const dto$: Observable = observableCombineLatest( - this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { - const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); - epersonDtoModel.eperson = member; - epersonDtoModel.memberOfGroup = isMember; - return epersonDtoModel; - }); - return dto$; + getAllCompletedRemoteData(), + map((rd: RemoteData) => { + if (rd.hasFailed) { + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage })); + } else { + return rd; + } + }), + switchMap((epersonListRD: RemoteData>) => { + const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => { + const dto$: Observable = observableCombineLatest( + this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => { + const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); + epersonDtoModel.eperson = member; + epersonDtoModel.memberOfGroup = isMember; + return epersonDtoModel; + }); + return dto$; + })); + return dtos$.pipe(map((dtos: EpersonDtoModel[]) => { + return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); + })); + })) + .subscribe((paginatedListOfDTOs: PaginatedList) => { + this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs); })); - return dtos$.pipe(map((dtos: EpersonDtoModel[]) => { - return buildPaginatedList(epersonListRD.payload.pageInfo, dtos); - })); - })) - .subscribe((paginatedListOfDTOs: PaginatedList) => { - this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs); - })); } /** - * Whether or not the given ePerson is a member of the group currently being edited + * Whether the given ePerson is a member of the group currently being edited * @param possibleMember EPerson that is a possible member (being tested) of the group currently being edited */ isMemberOfGroup(possibleMember: EPerson): Observable { @@ -195,7 +219,7 @@ export class MembersListComponent implements OnInit, OnDestroy { * @param key The key of the subscription to unsubscribe from * @private */ - private unsubFrom(key: SubKey) { + protected unsubFrom(key: SubKey) { if (this.subs.has(key)) { this.subs.get(key).unsubscribe(); this.subs.delete(key); @@ -270,7 +294,7 @@ export class MembersListComponent implements OnInit, OnDestroy { getAllCompletedRemoteData(), map((rd: RemoteData) => { if (rd.hasFailed) { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', {cause: rd.errorMessage})); + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage })); } else { return rd; } @@ -333,4 +357,5 @@ export class MembersListComponent implements OnInit, OnDestroy { }); this.search({ query: '' }); } + } diff --git a/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.html b/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.html index ce6e01df3d..dbef279d8c 100644 --- a/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.html +++ b/src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html index 3009cc0771..d4ac620811 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.html @@ -7,9 +7,12 @@ [groupId]="groupId" [ngClass]="groupId ? 'reviewersListWithGroup' : ''" [multipleReviewers]="multipleReviewers" - (selectedReviewersUpdated)="selectedReviewers = $event" + (selectedReviewersUpdated)="selectedReviewers = $event; displayError = false" messagePrefix="advanced-workflow-action-select-reviewer.groups.form.reviewers-list" > + + {{ 'advanced-workflow-action.select-reviewer.no-reviewer-selected.error' | translate }} + diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index d7a885f067..f766c97f39 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -9,7 +9,7 @@ import { } from '../../../core/tasks/models/select-reviewer-action-advanced-info.model'; import { EPersonListActionConfig -} from '../../../access-control/group-registry/group-form/eperson-list/eperson-list.component'; +} from '../../../access-control/group-registry/group-form/members-list/members-list.component'; import { Subscription } from 'rxjs'; import { EPerson } from '../../../core/eperson/models/eperson.model'; @@ -38,6 +38,8 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf subs: Subscription[] = []; + displayError = false; + ngOnDestroy(): void { this.subs.forEach((subscription: Subscription) => subscription.unsubscribe()); } @@ -84,6 +86,15 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf return ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; } + performAction(): void { + if (this.selectedReviewers.length > 0) { + super.performAction(); + } else { + this.displayError = true; + } + console.log(this.displayError); + } + createBody(): any { return { [WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER]: true, diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts index 14159450ff..0cd485e638 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts @@ -8,15 +8,14 @@ import { NotificationsService } from '../../../../shared/notifications/notificat import { PaginationService } from '../../../../core/pagination/pagination.service'; import { Group } from '../../../../core/eperson/models/group.model'; import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; -import { - EPersonListComponent, - EPersonListActionConfig -} from '../../../../access-control/group-registry/group-form/eperson-list/eperson-list.component'; import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { Observable, of as observableOf } from 'rxjs'; import { hasValue } from '../../../../shared/empty.util'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; +import { + MembersListComponent, EPersonListActionConfig +} from '../../../../access-control/group-registry/group-form/members-list/members-list.component'; /** * Keys to keep track of specific subscriptions @@ -30,9 +29,9 @@ enum SubKey { @Component({ selector: 'ds-reviewers-list', // templateUrl: './reviewers-list.component.html', - templateUrl: '../../../../access-control/group-registry/group-form/eperson-list/eperson-list.component.html', + templateUrl: '../../../../access-control/group-registry/group-form/members-list/members-list.component.html', }) -export class ReviewersListComponent extends EPersonListComponent implements OnInit, OnChanges, OnDestroy { +export class ReviewersListComponent extends MembersListComponent implements OnInit, OnChanges, OnDestroy { @Input() groupId: string | null; diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 57f93ee906..aa2f59e7ee 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -599,6 +599,8 @@ "advanced-workflow-action-select-reviewer.groups.form.reviewers-list.no-items": "No EPeople found in that search", + "advanced-workflow-action.select-reviewer.no-reviewer-selected.error": "No reviewer selected.", + "auth.errors.invalid-user": "Invalid email address or password.", From bd2135bcd29d3a459a5f2d25295db7fcb62b0dc5 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Wed, 11 Jan 2023 20:45:04 +0100 Subject: [PATCH 13/28] 98376: scoreReview workflow - slight refactor REST changes --- ...vanced-workflow-action-rating.component.ts | 8 ++++---- src/assets/i18n/en.json5 | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts index c669cfcfe9..eb06e49243 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts @@ -9,8 +9,8 @@ import { RatingReviewerActionAdvancedInfo } from '../../../core/tasks/models/rating-reviewer-action-advanced-info.model'; -export const WORKFLOW_ADVANCED_TASK_OPTION_RATING = 'rating'; -export const ADVANCED_WORKFLOW_ACTION_RATING = 'ratingreviewaction'; +export const WORKFLOW_ADVANCED_TASK_OPTION_RATING = 'submit_score'; +export const ADVANCED_WORKFLOW_ACTION_RATING = 'scorereviewaction'; @rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_RATING) @Component({ @@ -49,8 +49,8 @@ export class AdvancedWorkflowActionRatingComponent extends AdvancedWorkflowActio score: this.ratingForm.get('rating').value, }; if (this.ratingForm.get('review').value !== '') { - const description: string = this.ratingForm.get('review').value; - Object.assign(body, { description }); + const review: string = this.ratingForm.get('review').value; + Object.assign(body, { review: review }); } return body; } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index aa2f59e7ee..6b5b317c6e 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4074,9 +4074,9 @@ "submission.workflow.generic.submit_select_reviewer-help": "", - "submission.workflow.generic.rating": "Rate", + "submission.workflow.generic.submit_score": "Rate", - "submission.workflow.generic.rating-help": "", + "submission.workflow.generic.submit_score-help": "", "submission.workflow.tasks.claimed.approve": "Approve", @@ -4254,21 +4254,21 @@ "workflow-item.selectrevieweraction.button.confirm": "Confirm", - "workflow-item.ratingreviewaction.notification.success.title": "Rating review", + "workflow-item.scorereviewaction.notification.success.title": "Rating review", - "workflow-item.ratingreviewaction.notification.success.content": "The rating for this item workflow item has been successfully submitted", + "workflow-item.scorereviewaction.notification.success.content": "The rating for this item workflow item has been successfully submitted", - "workflow-item.ratingreviewaction.notification.error.title": "Something went wrong", + "workflow-item.scorereviewaction.notification.error.title": "Something went wrong", - "workflow-item.ratingreviewaction.notification.error.content": "Couldn't rate this item", + "workflow-item.scorereviewaction.notification.error.content": "Couldn't rate this item", - "workflow-item.ratingreviewaction.title": "Rate this item", + "workflow-item.scorereviewaction.title": "Rate this item", - "workflow-item.ratingreviewaction.header": "Rate this item", + "workflow-item.scorereviewaction.header": "Rate this item", - "workflow-item.ratingreviewaction.button.cancel": "Cancel", + "workflow-item.scorereviewaction.button.cancel": "Cancel", - "workflow-item.ratingreviewaction.button.confirm": "Confirm", + "workflow-item.scorereviewaction.button.confirm": "Confirm", "idle-modal.header": "Session will expire soon", From fe61bb7b6b42ddab2b22f1341491e4158cd5e0c3 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 17 Jan 2023 19:00:00 +0100 Subject: [PATCH 14/28] 98535: Cleaned up code & added documentation --- src/app/core/core.module.ts | 12 ++++---- .../models/advanced-workflow-info.model.ts | 11 +++++++ .../advanced-workflow-info.resource-type.ts | 17 +++++++++++ .../rating-advanced-workflow-info.model.ts | 28 +++++++++++++++++ ...ing-reviewer-action-advanced-info.model.ts | 27 ----------------- .../reviewer-action-advanced-info.model.ts | 16 ---------- ...ewer-action-advanced-info.resource-type.ts | 25 ---------------- ...ect-reviewer-action-advanced-info.model.ts | 18 ----------- ...t-reviewer-advanced-workflow-info.model.ts | 19 ++++++++++++ .../models/workflow-action-object.model.ts | 4 +-- ...claimed-task-actions-abstract.component.ts | 11 +++++-- ...ed-claimed-task-action-rating.component.ts | 9 ++++-- ...d-task-action-select-reviewer.component.ts | 9 ++++-- ...advanced-workflow-action-page.component.ts | 4 +++ ...vanced-workflow-action-rating.component.ts | 20 ++++++++----- ...rkflow-action-select-reviewer.component.ts | 20 +++++++++---- .../reviewers-list.component.ts | 30 +++++++++++++++++-- ...advanced-workflow-action.component.spec.ts | 3 +- .../advanced-workflow-action.component.ts | 8 ++++- ...ced-workflow-actions-loader.component.html | 2 +- ...anced-workflow-actions-loader.component.ts | 11 ++++--- ...=> advanced-workflow-actions.directive.ts} | 4 +-- .../workflowitems-edit-page.module.ts | 6 ++-- 23 files changed, 184 insertions(+), 130 deletions(-) create mode 100644 src/app/core/tasks/models/advanced-workflow-info.model.ts create mode 100644 src/app/core/tasks/models/advanced-workflow-info.resource-type.ts create mode 100644 src/app/core/tasks/models/rating-advanced-workflow-info.model.ts delete mode 100644 src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts delete mode 100644 src/app/core/tasks/models/reviewer-action-advanced-info.model.ts delete mode 100644 src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts delete mode 100644 src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts create mode 100644 src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts rename src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/{advanced-claimed-task-actions.directive.ts => advanced-workflow-actions.directive.ts} (73%) diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index df71f740da..254f6d2478 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -162,9 +162,9 @@ import { SearchConfig } from './shared/search/search-filters/search-config.model import { SequenceService } from './shared/sequence.service'; import { GroupDataService } from './eperson/group-data.service'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; -import { RatingReviewerActionAdvancedInfo } from './tasks/models/rating-reviewer-action-advanced-info.model'; -import { ReviewerActionAdvancedInfo } from './tasks/models/reviewer-action-advanced-info.model'; -import { SelectReviewerActionAdvancedInfo } from './tasks/models/select-reviewer-action-advanced-info.model'; +import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model'; +import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model'; +import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -335,9 +335,9 @@ export const models = Version, VersionHistory, WorkflowAction, - ReviewerActionAdvancedInfo, - RatingReviewerActionAdvancedInfo, - SelectReviewerActionAdvancedInfo, + AdvancedWorkflowInfo, + RatingAdvancedWorkflowInfo, + SelectReviewerAdvancedWorkflowInfo, TemplateItem, Feature, Authorization, diff --git a/src/app/core/tasks/models/advanced-workflow-info.model.ts b/src/app/core/tasks/models/advanced-workflow-info.model.ts new file mode 100644 index 0000000000..87991a375c --- /dev/null +++ b/src/app/core/tasks/models/advanced-workflow-info.model.ts @@ -0,0 +1,11 @@ +import { autoserialize } from 'cerialize'; + +/** + * An abstract model class for a {@link AdvancedWorkflowInfo} + */ +export abstract class AdvancedWorkflowInfo { + + @autoserialize + id: string; + +} diff --git a/src/app/core/tasks/models/advanced-workflow-info.resource-type.ts b/src/app/core/tasks/models/advanced-workflow-info.resource-type.ts new file mode 100644 index 0000000000..4e7793f875 --- /dev/null +++ b/src/app/core/tasks/models/advanced-workflow-info.resource-type.ts @@ -0,0 +1,17 @@ +import { ResourceType } from '../../shared/resource-type'; + +/** + * The resource type for {@link RatingAdvancedWorkflowInfo} + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const RATING_ADVANCED_WORKFLOW_INFO = new ResourceType('ratingrevieweraction'); + +/** + * The resource type for {@link SelectReviewerAdvancedWorkflowInfo} + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const SELECT_REVIEWER_ADVANCED_WORKFLOW_INFO = new ResourceType('selectrevieweraction'); diff --git a/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts b/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts new file mode 100644 index 0000000000..b7861d4fe4 --- /dev/null +++ b/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts @@ -0,0 +1,28 @@ +import { typedObject } from '../../cache/builders/build-decorators'; +import { inheritSerialization, autoserialize } from 'cerialize'; +import { RATING_ADVANCED_WORKFLOW_INFO } from './advanced-workflow-info.resource-type'; +import { AdvancedWorkflowInfo } from './advanced-workflow-info.model'; +import { ResourceType } from '../../shared/resource-type'; + +/** + * A model class for a {@link RatingAdvancedWorkflowInfo} + */ +@typedObject +@inheritSerialization(AdvancedWorkflowInfo) +export class RatingAdvancedWorkflowInfo extends AdvancedWorkflowInfo { + + static type: ResourceType = RATING_ADVANCED_WORKFLOW_INFO; + + /** + * Whether the description is required. + */ + @autoserialize + descriptionRequired: boolean; + + /** + * The maximum value. + */ + @autoserialize + maxValue: number; + +} diff --git a/src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts b/src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts deleted file mode 100644 index 2759d3edf2..0000000000 --- a/src/app/core/tasks/models/rating-reviewer-action-advanced-info.model.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { typedObject } from '../../cache/builders/build-decorators'; -import { inheritSerialization, autoserialize } from 'cerialize'; -import { RATING_REVIEWER_ACTION_ADVANCED_INFO } from './reviewer-action-advanced-info.resource-type'; -import { ReviewerActionAdvancedInfo } from './reviewer-action-advanced-info.model'; - -/** - * A model class for a {@link RatingReviewerActionAdvancedInfo} - */ -@typedObject -@inheritSerialization(ReviewerActionAdvancedInfo) -export class RatingReviewerActionAdvancedInfo extends ReviewerActionAdvancedInfo { - - static type = RATING_REVIEWER_ACTION_ADVANCED_INFO; - - /** - * Whether the description is required. - */ - @autoserialize - descriptionRequired: boolean; - - /** - * The maximum value. - */ - @autoserialize - maxValue: number; - -} diff --git a/src/app/core/tasks/models/reviewer-action-advanced-info.model.ts b/src/app/core/tasks/models/reviewer-action-advanced-info.model.ts deleted file mode 100644 index dc423eec51..0000000000 --- a/src/app/core/tasks/models/reviewer-action-advanced-info.model.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { typedObject } from '../../cache/builders/build-decorators'; -import { autoserialize } from 'cerialize'; -import { REVIEWER_ACTION_ADVANCED_INFO } from './reviewer-action-advanced-info.resource-type'; - -/** - * A model class for a {@link ReviewerActionAdvancedInfo} - */ -@typedObject -export class ReviewerActionAdvancedInfo { - - static type = REVIEWER_ACTION_ADVANCED_INFO; - - @autoserialize - id: string; - -} diff --git a/src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts b/src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts deleted file mode 100644 index 876f37495e..0000000000 --- a/src/app/core/tasks/models/reviewer-action-advanced-info.resource-type.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ResourceType } from '../../shared/resource-type'; - -/** - * The resource type for {@link ReviewerActionAdvancedInfo} - * - * Needs to be in a separate file to prevent circular - * dependencies in webpack. - */ -export const REVIEWER_ACTION_ADVANCED_INFO = new ResourceType('revieweraction'); - -/** - * The resource type for {@link RatingReviewerActionAdvancedInfo} - * - * Needs to be in a separate file to prevent circular - * dependencies in webpack. - */ -export const RATING_REVIEWER_ACTION_ADVANCED_INFO = new ResourceType('ratingrevieweraction'); - -/** - * The resource type for {@link SelectReviewerActionAdvancedInfo} - * - * Needs to be in a separate file to prevent circular - * dependencies in webpack. - */ -export const SELECT_REVIEWER_ACTION_ADVANCED_INFO = new ResourceType('selectrevieweraction'); diff --git a/src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts b/src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts deleted file mode 100644 index cd8812f6af..0000000000 --- a/src/app/core/tasks/models/select-reviewer-action-advanced-info.model.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { typedObject } from '../../cache/builders/build-decorators'; -import { inheritSerialization, autoserialize } from 'cerialize'; -import { SELECT_REVIEWER_ACTION_ADVANCED_INFO } from './reviewer-action-advanced-info.resource-type'; -import { ReviewerActionAdvancedInfo } from './reviewer-action-advanced-info.model'; - -/** - * A model class for a {@link SelectReviewerActionAdvancedInfo} - */ -@typedObject -@inheritSerialization(ReviewerActionAdvancedInfo) -export class SelectReviewerActionAdvancedInfo extends ReviewerActionAdvancedInfo { - - static type = SELECT_REVIEWER_ACTION_ADVANCED_INFO; - - @autoserialize - group: string; - -} diff --git a/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts b/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts new file mode 100644 index 0000000000..b87770596e --- /dev/null +++ b/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts @@ -0,0 +1,19 @@ +import { typedObject } from '../../cache/builders/build-decorators'; +import { inheritSerialization, autoserialize } from 'cerialize'; +import { SELECT_REVIEWER_ADVANCED_WORKFLOW_INFO } from './advanced-workflow-info.resource-type'; +import { AdvancedWorkflowInfo } from './advanced-workflow-info.model'; +import { ResourceType } from '../../shared/resource-type'; + +/** + * A model class for a {@link SelectReviewerAdvancedWorkflowInfo} + */ +@typedObject +@inheritSerialization(AdvancedWorkflowInfo) +export class SelectReviewerAdvancedWorkflowInfo extends AdvancedWorkflowInfo { + + static type: ResourceType = SELECT_REVIEWER_ADVANCED_WORKFLOW_INFO; + + @autoserialize + group: string; + +} diff --git a/src/app/core/tasks/models/workflow-action-object.model.ts b/src/app/core/tasks/models/workflow-action-object.model.ts index 6fa103bbea..0896e6b8f8 100644 --- a/src/app/core/tasks/models/workflow-action-object.model.ts +++ b/src/app/core/tasks/models/workflow-action-object.model.ts @@ -2,7 +2,7 @@ import { inheritSerialization, autoserialize } from 'cerialize'; import { typedObject } from '../../cache/builders/build-decorators'; import { DSpaceObject } from '../../shared/dspace-object.model'; import { WORKFLOW_ACTION } from './workflow-action-object.resource-type'; -import { ReviewerActionAdvancedInfo } from './reviewer-action-advanced-info.model'; +import { AdvancedWorkflowInfo } from './advanced-workflow-info.model'; /** * A model class for a WorkflowAction @@ -40,6 +40,6 @@ export class WorkflowAction extends DSpaceObject { * The advanced info required by the advanced options */ @autoserialize - advancedInfo: ReviewerActionAdvancedInfo[]; + advancedInfo: AdvancedWorkflowInfo[]; } diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts index 3774b60f1a..d3f2ce4b6e 100644 --- a/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/advanced-claimed-task-actions-abstract.component.ts @@ -7,9 +7,8 @@ import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/wo /** * Abstract component for rendering an advanced claimed task's action * To create a child-component for a new option: - * - Set the "option" of the component + * - Set the "option" and "workflowType" of the component * - Add a @rendersWorkflowTaskOption annotation to your component providing the same enum value - * - Optionally overwrite createBody if the request body requires more than just the option */ @Component({ selector: 'ds-advanced-claimed-task-action-abstract', @@ -17,7 +16,10 @@ import { getAdvancedWorkflowRoute } from '../../../../workflowitems-edit-page/wo }) export abstract class AdvancedClaimedTaskActionsAbstractComponent extends ClaimedTaskActionsAbstractComponent implements OnInit { - workflowType: string; + /** + * The {@link WorkflowAction} id of the advanced workflow that needs to be opened. + */ + abstract workflowType: string; /** * Route to the workflow's task page @@ -40,6 +42,9 @@ export abstract class AdvancedClaimedTaskActionsAbstractComponent extends Claime })); } + /** + * Navigates to the advanced workflow page based on the {@link workflow}. + */ openAdvancedClaimedTaskTab(): void { void this.router.navigate([this.workflowTaskPageRoute], { queryParams: { diff --git a/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts index 2c2983c25c..8699dc702e 100644 --- a/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts @@ -9,11 +9,14 @@ import { } from '../abstract/advanced-claimed-task-actions-abstract.component'; import { ADVANCED_WORKFLOW_ACTION_RATING, - WORKFLOW_ADVANCED_TASK_OPTION_RATING, + ADVANCED_WORKFLOW_TASK_OPTION_RATING, } from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component'; import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; -@rendersWorkflowTaskOption(WORKFLOW_ADVANCED_TASK_OPTION_RATING) +/** + * Advanced Workflow button that redirect to the {@link AdvancedWorkflowActionRatingComponent} + */ +@rendersWorkflowTaskOption(ADVANCED_WORKFLOW_TASK_OPTION_RATING) @Component({ selector: 'ds-advanced-claimed-task-action-rating-reviewer', templateUrl: './advanced-claimed-task-action-rating.component.html', @@ -24,7 +27,7 @@ export class AdvancedClaimedTaskActionRatingComponent extends AdvancedClaimedTas /** * This component represents the advanced select option */ - option = WORKFLOW_ADVANCED_TASK_OPTION_RATING; + option = ADVANCED_WORKFLOW_TASK_OPTION_RATING; workflowType = ADVANCED_WORKFLOW_ACTION_RATING; diff --git a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts index c7a2a2e545..7473c737d9 100644 --- a/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts @@ -10,10 +10,13 @@ import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; import { ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER, - WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER + ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER } from '../../../../workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component'; -@rendersWorkflowTaskOption(WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER) +/** + * Advanced Workflow button that redirect to the {@link AdvancedWorkflowActionSelectReviewerComponent} + */ +@rendersWorkflowTaskOption(ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER) @Component({ selector: 'ds-advanced-claimed-task-action-select-reviewer', templateUrl: './advanced-claimed-task-action-select-reviewer.component.html', @@ -24,7 +27,7 @@ export class AdvancedClaimedTaskActionSelectReviewerComponent extends AdvancedCl /** * This component represents the advanced select option */ - option = WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER; + option = ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER; workflowType = ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts index e6c7da7d59..91dce19a5e 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts @@ -1,6 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +/** + * The Advanced Workflow page containing the correct {@link AdvancedWorkflowActionComponent} + * based on the route parameters. + */ @Component({ selector: 'ds-advanced-workflow-action-page', templateUrl: './advanced-workflow-action-page.component.html', diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts index eb06e49243..f98e83f8be 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts @@ -6,12 +6,15 @@ import { AdvancedWorkflowActionComponent } from '../advanced-workflow-action/adv import { FormGroup, FormControl } from '@angular/forms'; import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; import { - RatingReviewerActionAdvancedInfo -} from '../../../core/tasks/models/rating-reviewer-action-advanced-info.model'; + RatingAdvancedWorkflowInfo +} from '../../../core/tasks/models/rating-advanced-workflow-info.model'; -export const WORKFLOW_ADVANCED_TASK_OPTION_RATING = 'submit_score'; +export const ADVANCED_WORKFLOW_TASK_OPTION_RATING = 'submit_score'; export const ADVANCED_WORKFLOW_ACTION_RATING = 'scorereviewaction'; +/** + * The page on which reviewers can rate submitted items. + */ @rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_RATING) @Component({ selector: 'ds-advanced-workflow-action-rating-reviewer', @@ -23,7 +26,7 @@ export class AdvancedWorkflowActionRatingComponent extends AdvancedWorkflowActio ratingForm: FormGroup; - ngOnInit() { + ngOnInit(): void { super.ngOnInit(); this.ratingForm = new FormGroup({ review: new FormControl(''), @@ -43,9 +46,12 @@ export class AdvancedWorkflowActionRatingComponent extends AdvancedWorkflowActio } } + /** + * Returns the task option, the score and the review if one was provided + */ createBody(): any { const body = { - [WORKFLOW_ADVANCED_TASK_OPTION_RATING]: true, + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, score: this.ratingForm.get('rating').value, }; if (this.ratingForm.get('review').value !== '') { @@ -59,8 +65,8 @@ export class AdvancedWorkflowActionRatingComponent extends AdvancedWorkflowActio return ADVANCED_WORKFLOW_ACTION_RATING; } - getAdvancedInfo(workflowAction: WorkflowAction | null): RatingReviewerActionAdvancedInfo | null { - return workflowAction ? (workflowAction.advancedInfo[0] as RatingReviewerActionAdvancedInfo) : null; + getAdvancedInfo(workflowAction: WorkflowAction | null): RatingAdvancedWorkflowInfo | null { + return workflowAction ? (workflowAction.advancedInfo[0] as RatingAdvancedWorkflowInfo) : null; } } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index f766c97f39..3d07acdebe 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -5,17 +5,20 @@ import { import { AdvancedWorkflowActionComponent } from '../advanced-workflow-action/advanced-workflow-action.component'; import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; import { - SelectReviewerActionAdvancedInfo -} from '../../../core/tasks/models/select-reviewer-action-advanced-info.model'; + SelectReviewerAdvancedWorkflowInfo +} from '../../../core/tasks/models/select-reviewer-advanced-workflow-info.model'; import { EPersonListActionConfig } from '../../../access-control/group-registry/group-form/members-list/members-list.component'; import { Subscription } from 'rxjs'; import { EPerson } from '../../../core/eperson/models/eperson.model'; -export const WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; +export const ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; +/** + * The page on which Review Managers can assign Reviewers to review an item. + */ @rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER) @Component({ selector: 'ds-advanced-workflow-action-select-reviewer', @@ -75,7 +78,7 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf } this.subs.push(this.workflowAction$.subscribe((workflowAction: WorkflowAction) => { if (workflowAction) { - this.groupId = (workflowAction.advancedInfo as SelectReviewerActionAdvancedInfo[])[0].group; + this.groupId = (workflowAction.advancedInfo as SelectReviewerAdvancedWorkflowInfo[])[0].group; } else { this.groupId = null; } @@ -86,18 +89,23 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf return ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER; } + /** + * Only performs the action when some reviewers have been selected. + */ performAction(): void { if (this.selectedReviewers.length > 0) { super.performAction(); } else { this.displayError = true; } - console.log(this.displayError); } + /** + * Returns the task option and the selected {@link EPerson} id(s) + */ createBody(): any { return { - [WORKFLOW_ADVANCED_TASK_OPTION_SELECT_REVIEWER]: true, + [ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER]: true, eperson: this.selectedReviewers.map((ePerson: EPerson) => ePerson.id), }; } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts index 0cd485e638..7112a30543 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts @@ -14,7 +14,8 @@ import { Observable, of as observableOf } from 'rxjs'; import { hasValue } from '../../../../shared/empty.util'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { - MembersListComponent, EPersonListActionConfig + MembersListComponent, + EPersonListActionConfig, } from '../../../../access-control/group-registry/group-form/members-list/members-list.component'; /** @@ -26,6 +27,9 @@ enum SubKey { SearchResultsDTO, } +/** + * A custom {@link MembersListComponent} for the advanced SelectReviewer workflow. + */ @Component({ selector: 'ds-reviewers-list', // templateUrl: './reviewers-list.component.html', @@ -83,6 +87,12 @@ export class ReviewersListComponent extends MembersListComponent implements OnIn } } + /** + * Sets the list of currently selected members, when no group is defined the list of {@link selectedReviewers} + * will be set. + * + * @param page The number of the page to retrieve + */ retrieveMembers(page: number): void { this.config.currentPage = page; if (this.groupId === null) { @@ -95,19 +105,35 @@ export class ReviewersListComponent extends MembersListComponent implements OnIn } } + /** + * Checks whether the given {@link possibleMember} is part of the {@link selectedReviewers}. + * + * @param possibleMember The {@link EPerson} that needs to be checked + */ isMemberOfGroup(possibleMember: EPerson): Observable { return observableOf(hasValue(this.selectedReviewers.find((reviewer: EpersonDtoModel) => reviewer.eperson.id === possibleMember.id))); } + /** + * Removes the {@link ePerson} from the {@link selectedReviewers} + * + * @param ePerson The {@link EpersonDtoModel} containg the {@link EPerson} to remove + */ deleteMemberFromGroup(ePerson: EpersonDtoModel) { ePerson.memberOfGroup = false; const index = this.selectedReviewers.indexOf(ePerson); if (index !== -1) { this.selectedReviewers.splice(index, 1); } - this.selectedReviewersUpdated.emit(this.selectedReviewers.map((epersonDtoModel: EpersonDtoModel) => epersonDtoModel.eperson)); + this.selectedReviewersUpdated.emit(this.selectedReviewers.map((ePersonDtoModel: EpersonDtoModel) => ePersonDtoModel.eperson)); } + /** + * Adds the {@link ePerson} to the {@link selectedReviewers} (or replaces it when {@link multipleReviewers} is + * `false`). Afterwards it will emit the list. + * + * @param ePerson The {@link EPerson} to add to the list + */ addMemberToGroup(ePerson: EpersonDtoModel) { ePerson.memberOfGroup = true; if (!this.multipleReviewers) { diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts index b836a8704a..13799fee4c 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts @@ -108,7 +108,8 @@ describe('AdvancedWorkflowActionComponent', () => { }); @Component({ - selector: 'ds-test-cmp', + // tslint:disable-next-line:component-selector + selector: '', template: '' }) class TestComponent extends AdvancedWorkflowActionComponent { diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts index 256a4d8915..982e5f8eac 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts @@ -13,6 +13,12 @@ import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.se import { map } from 'rxjs/operators'; import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +/** + * Abstract component for rendering an advanced claimed task's workflow page + * To create a child-component for a new option: + * - Set the "getType()" of the component + * - Implement the createBody, should always contain at least the ADVANCED_WORKFLOW_TASK_OPTION + */ @Component({ selector: 'ds-advanced-workflow-action', template: '', @@ -62,7 +68,7 @@ export abstract class AdvancedWorkflowActionComponent extends WorkflowItemAction /** * Submits the task with the given {@link createBody}. * - * @param id + * @param id The task id */ sendRequest(id: string): Observable { return this.claimedTaskDataService.submitTask(id, this.createBody()).pipe( diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html index 2374ed7913..0904d0fcde 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html @@ -1 +1 @@ - + diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts index ac6d2f171d..32f14c015d 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts @@ -3,10 +3,13 @@ import { hasValue } from '../../../shared/empty.util'; import { getAdvancedComponentByWorkflowTaskOption } from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; -import { AdvancedClaimedTaskActionsDirective } from './advanced-claimed-task-actions.directive'; +import { AdvancedWorkflowActionsDirective } from './advanced-workflow-actions.directive'; import { Router } from '@angular/router'; import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; +/** + * Component for loading a {@link AdvancedWorkflowActionComponent} depending on the "{@link type}" input + */ @Component({ selector: 'ds-advanced-workflow-actions-loader', templateUrl: './advanced-workflow-actions-loader.component.html', @@ -23,7 +26,7 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit { /** * Directive to determine where the dynamic child component is located */ - @ViewChild(AdvancedClaimedTaskActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedClaimedTaskActionsDirective; + @ViewChild(AdvancedWorkflowActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedWorkflowActionsDirective; constructor( private componentFactoryResolver: ComponentFactoryResolver, @@ -47,8 +50,8 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit { } } - getComponentByWorkflowTaskOption(option: string) { - return getAdvancedComponentByWorkflowTaskOption(option); + getComponentByWorkflowTaskOption(type: string): any { + return getAdvancedComponentByWorkflowTaskOption(type); } } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts similarity index 73% rename from src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts rename to src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts index b2ed49b502..e569f6cc6f 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts @@ -1,12 +1,12 @@ import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ - selector: '[dsAdvancedClaimedTaskActions]', + selector: '[dsAdvancedWorkflowActions]', }) /** * Directive used as a hook to know where to inject the dynamic Advanced Claimed Task Actions component */ -export class AdvancedClaimedTaskActionsDirective { +export class AdvancedWorkflowActionsDirective { constructor( public viewContainerRef: ViewContainerRef, diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts index dc60bd7d2b..aaf75fbb9f 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -24,8 +24,8 @@ import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; import { - AdvancedClaimedTaskActionsDirective -} from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-claimed-task-actions.directive'; + AdvancedWorkflowActionsDirective +} from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive'; import { AccessControlModule } from '../access-control/access-control.module'; import { ReviewersListComponent @@ -54,7 +54,7 @@ import { RatingModule } from 'ngx-bootstrap/rating'; AdvancedWorkflowActionRatingComponent, AdvancedWorkflowActionSelectReviewerComponent, AdvancedWorkflowActionPageComponent, - AdvancedClaimedTaskActionsDirective, + AdvancedWorkflowActionsDirective, ReviewersListComponent, ] }) From 572f5ac4c577057d6df03de894e5d1489b43514a Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 18 Jan 2023 13:45:04 +0100 Subject: [PATCH 15/28] 98535: Added missing tests to the new advanced workflow components --- ...ced-workflow-action-page.component.spec.ts | 6 +- ...d-workflow-action-rating.component.spec.ts | 116 +++++++++++++++++- ...w-action-select-reviewer.component.spec.ts | 67 +++++++++- .../reviewers-list.component.spec.ts | 80 +++++++++--- ...advanced-workflow-action.component.spec.ts | 6 +- ...-workflow-actions-loader.component.spec.ts | 53 +++++++- 6 files changed, 288 insertions(+), 40 deletions(-) diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts index f2511f0490..cbb85b6ad8 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action-page.component'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; describe('AdvancedWorkflowActionPageComponent', () => { @@ -20,9 +20,9 @@ describe('AdvancedWorkflowActionPageComponent', () => { provide: ActivatedRoute, useValue: { snapshot: { - queryParams: convertToParamMap({ + queryParams: { workflow: 'testaction', - }), + }, }, }, }, diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts index a75d4e30a2..4abbe1a0c1 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts @@ -1,6 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdvancedWorkflowActionRatingComponent } from './advanced-workflow-action-rating.component'; -import { ActivatedRoute, convertToParamMap, Router } from '@angular/router'; +import { + AdvancedWorkflowActionRatingComponent, + ADVANCED_WORKFLOW_TASK_OPTION_RATING +} from './advanced-workflow-action-rating.component'; +import { ActivatedRoute, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -17,10 +20,19 @@ import { TranslateModule } from '@ngx-translate/core'; import { VarDirective } from '../../../shared/utils/var.directive'; import { RatingModule } from 'ngx-bootstrap/rating'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; +import { createSuccessfulRemoteDataObject$, createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { Item } from '../../../core/shared/item.model'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +import { RatingAdvancedWorkflowInfo } from '../../../core/tasks/models/rating-advanced-workflow-info.model'; +const claimedTaskId = '2'; const workflowId = '1'; describe('AdvancedWorkflowActionRatingComponent', () => { + const workflowItem: WorkflowItem = new WorkflowItem(); + workflowItem.item = createSuccessfulRemoteDataObject$(new Item()); let component: AdvancedWorkflowActionRatingComponent; let fixture: ComponentFixture; @@ -52,11 +64,13 @@ describe('AdvancedWorkflowActionRatingComponent', () => { useValue: { data: observableOf({ id: workflowId, + wfi: createSuccessfulRemoteDataObject(workflowItem), }), snapshot: { - queryParams: convertToParamMap({ + queryParams: { + claimedTask: claimedTaskId, workflow: 'testaction', - }), + }, }, }, }, @@ -67,6 +81,7 @@ describe('AdvancedWorkflowActionRatingComponent', () => { { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); }); @@ -80,7 +95,96 @@ describe('AdvancedWorkflowActionRatingComponent', () => { fixture.debugElement.nativeElement.remove(); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('performAction', () => { + let ratingAdvancedWorkflowInfo: RatingAdvancedWorkflowInfo; + beforeEach(() => { + ratingAdvancedWorkflowInfo = new RatingAdvancedWorkflowInfo(); + ratingAdvancedWorkflowInfo.maxValue = 5; + spyOn(component, 'getAdvancedInfo').and.returnValue(ratingAdvancedWorkflowInfo); + spyOn(component, 'previousPage'); + // The form validators are set in the HTML code so the getAdvancedInfo needs to return a value + fixture.detectChanges(); + }); + + describe('with required review', () => { + beforeEach(() => { + ratingAdvancedWorkflowInfo.descriptionRequired = true; + fixture.detectChanges(); + }); + + it('should call the claimedTaskDataService with the rating and the required description when it has been rated and return to the mydspace page', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: 'Good job!', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, + review: 'Good job!', + score: 4, + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + + it('should not call the claimedTaskDataService when the required description is empty', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: '', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).not.toHaveBeenCalled(); + expect(notificationService.success).not.toHaveBeenCalled(); + expect(component.previousPage).not.toHaveBeenCalled(); + }); + }); + + describe('with an optional review', () => { + beforeEach(() => { + ratingAdvancedWorkflowInfo.descriptionRequired = false; + fixture.detectChanges(); + }); + + it('should call the claimedTaskDataService with the optional review when provided and return to the mydspace page', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: 'Good job!', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, + review: 'Good job!', + score: 4, + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + + it('should call the claimedTaskDataService when the optional description is empty and return to the mydspace page', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: '', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, + score: 4, + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + }); }); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts index a3ede08b1d..855bde79a7 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts @@ -1,6 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdvancedWorkflowActionSelectReviewerComponent } from './advanced-workflow-action-select-reviewer.component'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { + AdvancedWorkflowActionSelectReviewerComponent, + ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER +} from './advanced-workflow-action-select-reviewer.component'; +import { ActivatedRoute } from '@angular/router'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; import { RouterTestingModule } from '@angular/router/testing'; @@ -14,10 +17,19 @@ import { TranslateModule } from '@ngx-translate/core'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; import { of as observableOf } from 'rxjs'; +import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; +import { createSuccessfulRemoteDataObject$, createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { Item } from '../../../core/shared/item.model'; +import { EPersonMock, EPersonMock2 } from '../../../shared/testing/eperson.mock'; +import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +const claimedTaskId = '2'; const workflowId = '1'; describe('AdvancedWorkflowActionSelectReviewerComponent', () => { + const workflowItem: WorkflowItem = new WorkflowItem(); + workflowItem.item = createSuccessfulRemoteDataObject$(new Item()); let component: AdvancedWorkflowActionSelectReviewerComponent; let fixture: ComponentFixture; @@ -46,11 +58,13 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { useValue: { data: observableOf({ id: workflowId, + wfi: createSuccessfulRemoteDataObject(workflowItem), }), snapshot: { - queryParams: convertToParamMap({ + queryParams: { + claimedTask: claimedTaskId, workflow: 'testaction', - }), + }, }, }, }, @@ -60,6 +74,7 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); }); @@ -67,9 +82,49 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { fixture = TestBed.createComponent(AdvancedWorkflowActionSelectReviewerComponent); component = fixture.componentInstance; fixture.detectChanges(); + spyOn(component, 'previousPage'); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + describe('performAction', () => { + it('should call the claimedTaskDataService with the list of selected ePersons', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.selectedReviewers = [EPersonMock, EPersonMock2]; + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER]: true, + eperson: [EPersonMock.id, EPersonMock2.id], + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + + it('should not call the claimedTaskDataService with the list of selected ePersons when it\'s empty', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.selectedReviewers = []; + + component.performAction(); + + expect(claimedTaskDataService.submitTask).not.toHaveBeenCalled(); + }); + + it('should not call the return to mydspace page when the request failed', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(false))); + component.selectedReviewers = [EPersonMock, EPersonMock2]; + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER]: true, + eperson: [EPersonMock.id, EPersonMock2.id], + }); + expect(notificationService.error).toHaveBeenCalled(); + expect(component.previousPage).not.toHaveBeenCalled(); + }); }); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts index bf27b1e79f..24513cd22f 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core'; -import { ComponentFixture, fakeAsync, flush, inject, TestBed, waitForAsync } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA, SimpleChange, DebugElement } from '@angular/core'; +import { ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { Router } from '@angular/router'; @@ -31,6 +31,7 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio import { RouterMock } from '../../../../shared/mocks/router.mock'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; describe('ReviewersListComponent', () => { let component: ReviewersListComponent; @@ -45,6 +46,8 @@ describe('ReviewersListComponent', () => { let epersonMembers; let subgroupMembers; let paginationService; + let ePersonDtoModel1: EpersonDtoModel; + let ePersonDtoModel2: EpersonDtoModel; beforeEach(waitForAsync(() => { activeGroup = GroupMock; @@ -56,7 +59,7 @@ describe('ReviewersListComponent', () => { activeGroup: activeGroup, epersonMembers: epersonMembers, subgroupMembers: subgroupMembers, - findAllByHref(href: string): Observable>> { + findAllByHref(_href: string): Observable>> { return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getEPersonMembers())); }, searchByScope(scope: string, query: string): Observable>> { @@ -119,7 +122,6 @@ describe('ReviewersListComponent', () => { findById(id: string) { for (const group of allGroups) { if (group.id === id) { - console.log('found', group); return createSuccessfulRemoteDataObject$(group); } } @@ -167,9 +169,12 @@ describe('ReviewersListComponent', () => { fixture.debugElement.nativeElement.remove(); })); - it('should create ReviewersListComponent', inject([ReviewersListComponent], (comp: ReviewersListComponent) => { - expect(comp).toBeDefined(); - })); + beforeEach(() => { + ePersonDtoModel1 = new EpersonDtoModel(); + ePersonDtoModel1.eperson = EPersonMock; + ePersonDtoModel2 = new EpersonDtoModel(); + ePersonDtoModel2.eperson = EPersonMock2; + }); describe('when no group is selected', () => { beforeEach(() => { @@ -179,18 +184,18 @@ describe('ReviewersListComponent', () => { fixture.detectChanges(); }); - it('should show no epersons because no group is selected', () => { - const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); - expect(epersonIdsFound.length).toEqual(0); - epersonMembers.map((eperson: EPerson) => { - expect(epersonIdsFound.find((foundEl) => { - return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + it('should show no ePersons because no group is selected', () => { + const ePersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(ePersonIdsFound.length).toEqual(0); + epersonMembers.map((ePerson: EPerson) => { + expect(ePersonIdsFound.find((foundEl) => { + return (foundEl.nativeElement.textContent.trim() === ePerson.uuid); })).not.toBeTruthy(); }); }); }); - describe('when group is selected', () => { + describe('when a group is selected', () => { beforeEach(() => { component.ngOnChanges({ groupId: new SimpleChange(undefined, GroupMock.id, true) @@ -198,15 +203,50 @@ describe('ReviewersListComponent', () => { fixture.detectChanges(); }); - it('should show all eperson members of group', () => { - const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); - expect(epersonIdsFound.length).toEqual(1); - epersonMembers.map((eperson: EPerson) => { - expect(epersonIdsFound.find((foundEl) => { - return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + it('should show all ePerson members of group', () => { + const ePersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(ePersonIdsFound.length).toEqual(1); + epersonMembers.map((ePerson: EPerson) => { + expect(ePersonIdsFound.find((foundEl: DebugElement) => { + return (foundEl.nativeElement.textContent.trim() === ePerson.uuid); })).toBeTruthy(); }); }); }); + + it('should replace the value when a new member is added when multipleReviewers is false', () => { + spyOn(component.selectedReviewersUpdated, 'emit'); + component.multipleReviewers = false; + component.selectedReviewers = [ePersonDtoModel1]; + + component.addMemberToGroup(ePersonDtoModel2); + + expect(component.selectedReviewers).toEqual([ePersonDtoModel2]); + expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([ePersonDtoModel2.eperson]); + }); + + it('should add the value when a new member is added when multipleReviewers is true', () => { + spyOn(component.selectedReviewersUpdated, 'emit'); + component.multipleReviewers = true; + component.selectedReviewers = [ePersonDtoModel1]; + + component.addMemberToGroup(ePersonDtoModel2); + + expect(component.selectedReviewers).toEqual([ePersonDtoModel1, ePersonDtoModel2]); + expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([ePersonDtoModel1.eperson, ePersonDtoModel2.eperson]); + }); + + it('should delete the member when present', () => { + spyOn(component.selectedReviewersUpdated, 'emit'); + ePersonDtoModel1.memberOfGroup = true; + component.selectedReviewers = [ePersonDtoModel1]; + + component.deleteMemberFromGroup(ePersonDtoModel1); + + expect(component.selectedReviewers).toEqual([]); + expect(ePersonDtoModel1.memberOfGroup).toBeFalse(); + expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([]); + }); + }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts index 13799fee4c..f0c6882617 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts @@ -5,7 +5,7 @@ import { MockComponent } from 'ng-mocks'; import { DSOSelectorComponent } from '../../../shared/dso-selector/dso-selector/dso-selector.component'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; import { RouterTestingModule } from '@angular/router/testing'; @@ -54,9 +54,9 @@ describe('AdvancedWorkflowActionComponent', () => { id: workflowId, }), snapshot: { - queryParams: convertToParamMap({ + queryParams: { workflow: 'testaction', - }), + }, }, }, }, diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts index 321a6b954d..660e228c70 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts @@ -2,6 +2,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedWorkflowActionsLoaderComponent } from './advanced-workflow-actions-loader.component'; import { Router } from '@angular/router'; import { RouterStub } from '../../../shared/testing/router.stub'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { AdvancedWorkflowActionsDirective } from './advanced-workflow-actions.directive'; +import { + rendersAdvancedWorkflowTaskOption +} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; +import { By } from '@angular/platform-browser'; +import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; + +const ADVANCED_WORKFLOW_ACTION_TEST = 'testaction'; describe('AdvancedWorkflowActionsLoaderComponent', () => { let component: AdvancedWorkflowActionsLoaderComponent; @@ -14,21 +23,61 @@ describe('AdvancedWorkflowActionsLoaderComponent', () => { await TestBed.configureTestingModule({ declarations: [ + AdvancedWorkflowActionsDirective, AdvancedWorkflowActionsLoaderComponent, ], providers: [ { provide: Router, useValue: router }, ], + }).overrideComponent(AdvancedWorkflowActionsLoaderComponent, { + set: { + changeDetection: ChangeDetectionStrategy.Default, + entryComponents: [AdvancedWorkflowActionTestComponent], + }, }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AdvancedWorkflowActionsLoaderComponent); component = fixture.componentInstance; + component.type = ADVANCED_WORKFLOW_ACTION_TEST; fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + describe('When the component is rendered', () => { + it('should display the AdvancedWorkflowActionTestComponent when the type has been defined in a rendersAdvancedWorkflowTaskOption', () => { + spyOn(component, 'getComponentByWorkflowTaskOption').and.returnValue(AdvancedWorkflowActionTestComponent); + + component.ngOnInit(); + fixture.detectChanges(); + + expect(component.getComponentByWorkflowTaskOption).toHaveBeenCalledWith(ADVANCED_WORKFLOW_ACTION_TEST); + expect(fixture.debugElement.query(By.css('#AdvancedWorkflowActionsLoaderComponent'))).not.toBeNull(); + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should redirect to page not found when the type has not been defined in a rendersAdvancedWorkflowTaskOption', () => { + spyOn(component, 'getComponentByWorkflowTaskOption').and.returnValue(undefined); + component.type = 'nonexistingaction'; + + component.ngOnInit(); + fixture.detectChanges(); + + expect(component.getComponentByWorkflowTaskOption).toHaveBeenCalledWith('nonexistingaction'); + expect(router.navigate).toHaveBeenCalledWith([PAGE_NOT_FOUND_PATH]); + }); }); }); + +@rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_TEST) +@Component({ + // tslint:disable-next-line:component-selector + selector: '', + template: '', +}) +class AdvancedWorkflowActionTestComponent { +} From 940c6281a2b5a284791792561ae3f095004ea901 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 18 Jan 2023 13:45:04 +0100 Subject: [PATCH 16/28] 98535: Added missing tests to the new advanced workflow components --- ...ced-workflow-action-page.component.spec.ts | 6 +- ...d-workflow-action-rating.component.spec.ts | 116 +++++++++++++++++- ...w-action-select-reviewer.component.spec.ts | 67 +++++++++- ...rkflow-action-select-reviewer.component.ts | 20 +++ .../reviewers-list.component.spec.ts | 80 +++++++++--- ...advanced-workflow-action.component.spec.ts | 6 +- ...-workflow-actions-loader.component.spec.ts | 53 +++++++- 7 files changed, 308 insertions(+), 40 deletions(-) diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts index f2511f0490..cbb85b6ad8 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action-page.component'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; describe('AdvancedWorkflowActionPageComponent', () => { @@ -20,9 +20,9 @@ describe('AdvancedWorkflowActionPageComponent', () => { provide: ActivatedRoute, useValue: { snapshot: { - queryParams: convertToParamMap({ + queryParams: { workflow: 'testaction', - }), + }, }, }, }, diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts index a75d4e30a2..4abbe1a0c1 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts @@ -1,6 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdvancedWorkflowActionRatingComponent } from './advanced-workflow-action-rating.component'; -import { ActivatedRoute, convertToParamMap, Router } from '@angular/router'; +import { + AdvancedWorkflowActionRatingComponent, + ADVANCED_WORKFLOW_TASK_OPTION_RATING +} from './advanced-workflow-action-rating.component'; +import { ActivatedRoute, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -17,10 +20,19 @@ import { TranslateModule } from '@ngx-translate/core'; import { VarDirective } from '../../../shared/utils/var.directive'; import { RatingModule } from 'ngx-bootstrap/rating'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; +import { createSuccessfulRemoteDataObject$, createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { Item } from '../../../core/shared/item.model'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +import { RatingAdvancedWorkflowInfo } from '../../../core/tasks/models/rating-advanced-workflow-info.model'; +const claimedTaskId = '2'; const workflowId = '1'; describe('AdvancedWorkflowActionRatingComponent', () => { + const workflowItem: WorkflowItem = new WorkflowItem(); + workflowItem.item = createSuccessfulRemoteDataObject$(new Item()); let component: AdvancedWorkflowActionRatingComponent; let fixture: ComponentFixture; @@ -52,11 +64,13 @@ describe('AdvancedWorkflowActionRatingComponent', () => { useValue: { data: observableOf({ id: workflowId, + wfi: createSuccessfulRemoteDataObject(workflowItem), }), snapshot: { - queryParams: convertToParamMap({ + queryParams: { + claimedTask: claimedTaskId, workflow: 'testaction', - }), + }, }, }, }, @@ -67,6 +81,7 @@ describe('AdvancedWorkflowActionRatingComponent', () => { { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); }); @@ -80,7 +95,96 @@ describe('AdvancedWorkflowActionRatingComponent', () => { fixture.debugElement.nativeElement.remove(); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('performAction', () => { + let ratingAdvancedWorkflowInfo: RatingAdvancedWorkflowInfo; + beforeEach(() => { + ratingAdvancedWorkflowInfo = new RatingAdvancedWorkflowInfo(); + ratingAdvancedWorkflowInfo.maxValue = 5; + spyOn(component, 'getAdvancedInfo').and.returnValue(ratingAdvancedWorkflowInfo); + spyOn(component, 'previousPage'); + // The form validators are set in the HTML code so the getAdvancedInfo needs to return a value + fixture.detectChanges(); + }); + + describe('with required review', () => { + beforeEach(() => { + ratingAdvancedWorkflowInfo.descriptionRequired = true; + fixture.detectChanges(); + }); + + it('should call the claimedTaskDataService with the rating and the required description when it has been rated and return to the mydspace page', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: 'Good job!', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, + review: 'Good job!', + score: 4, + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + + it('should not call the claimedTaskDataService when the required description is empty', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: '', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).not.toHaveBeenCalled(); + expect(notificationService.success).not.toHaveBeenCalled(); + expect(component.previousPage).not.toHaveBeenCalled(); + }); + }); + + describe('with an optional review', () => { + beforeEach(() => { + ratingAdvancedWorkflowInfo.descriptionRequired = false; + fixture.detectChanges(); + }); + + it('should call the claimedTaskDataService with the optional review when provided and return to the mydspace page', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: 'Good job!', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, + review: 'Good job!', + score: 4, + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + + it('should call the claimedTaskDataService when the optional description is empty and return to the mydspace page', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.ratingForm.setValue({ + review: '', + rating: 4, + }); + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_RATING]: true, + score: 4, + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + }); }); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts index a3ede08b1d..855bde79a7 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts @@ -1,6 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdvancedWorkflowActionSelectReviewerComponent } from './advanced-workflow-action-select-reviewer.component'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { + AdvancedWorkflowActionSelectReviewerComponent, + ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER +} from './advanced-workflow-action-select-reviewer.component'; +import { ActivatedRoute } from '@angular/router'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; import { RouterTestingModule } from '@angular/router/testing'; @@ -14,10 +17,19 @@ import { TranslateModule } from '@ngx-translate/core'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; import { of as observableOf } from 'rxjs'; +import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; +import { createSuccessfulRemoteDataObject$, createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { Item } from '../../../core/shared/item.model'; +import { EPersonMock, EPersonMock2 } from '../../../shared/testing/eperson.mock'; +import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +const claimedTaskId = '2'; const workflowId = '1'; describe('AdvancedWorkflowActionSelectReviewerComponent', () => { + const workflowItem: WorkflowItem = new WorkflowItem(); + workflowItem.item = createSuccessfulRemoteDataObject$(new Item()); let component: AdvancedWorkflowActionSelectReviewerComponent; let fixture: ComponentFixture; @@ -46,11 +58,13 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { useValue: { data: observableOf({ id: workflowId, + wfi: createSuccessfulRemoteDataObject(workflowItem), }), snapshot: { - queryParams: convertToParamMap({ + queryParams: { + claimedTask: claimedTaskId, workflow: 'testaction', - }), + }, }, }, }, @@ -60,6 +74,7 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); }); @@ -67,9 +82,49 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { fixture = TestBed.createComponent(AdvancedWorkflowActionSelectReviewerComponent); component = fixture.componentInstance; fixture.detectChanges(); + spyOn(component, 'previousPage'); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + describe('performAction', () => { + it('should call the claimedTaskDataService with the list of selected ePersons', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.selectedReviewers = [EPersonMock, EPersonMock2]; + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER]: true, + eperson: [EPersonMock.id, EPersonMock2.id], + }); + expect(notificationService.success).toHaveBeenCalled(); + expect(component.previousPage).toHaveBeenCalled(); + }); + + it('should not call the claimedTaskDataService with the list of selected ePersons when it\'s empty', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(true))); + component.selectedReviewers = []; + + component.performAction(); + + expect(claimedTaskDataService.submitTask).not.toHaveBeenCalled(); + }); + + it('should not call the return to mydspace page when the request failed', () => { + spyOn(claimedTaskDataService, 'submitTask').and.returnValue(observableOf(new ProcessTaskResponse(false))); + component.selectedReviewers = [EPersonMock, EPersonMock2]; + + component.performAction(); + + expect(claimedTaskDataService.submitTask).toHaveBeenCalledWith(claimedTaskId, { + [ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER]: true, + eperson: [EPersonMock.id, EPersonMock2.id], + }); + expect(notificationService.error).toHaveBeenCalled(); + expect(component.previousPage).not.toHaveBeenCalled(); + }); }); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index 3d07acdebe..68e67ee95e 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -12,6 +12,13 @@ import { } from '../../../access-control/group-registry/group-form/members-list/members-list.component'; import { Subscription } from 'rxjs'; import { EPerson } from '../../../core/eperson/models/eperson.model'; +import { ActivatedRoute, Router } from '@angular/router'; +import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; +import { RouteService } from '../../../core/services/route.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; export const ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; @@ -43,6 +50,19 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf displayError = false; + constructor( + protected route: ActivatedRoute, + protected workflowItemService: WorkflowItemDataService, + protected router: Router, + protected routeService: RouteService, + protected notificationsService: NotificationsService, + protected translationService: TranslateService, + protected workflowActionService: WorkflowActionDataService, + protected claimedTaskDataService: ClaimedTaskDataService, + ) { + super(route, workflowItemService, router, routeService, notificationsService, translationService, workflowActionService, claimedTaskDataService); + } + ngOnDestroy(): void { this.subs.forEach((subscription: Subscription) => subscription.unsubscribe()); } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts index bf27b1e79f..24513cd22f 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core'; -import { ComponentFixture, fakeAsync, flush, inject, TestBed, waitForAsync } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA, SimpleChange, DebugElement } from '@angular/core'; +import { ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { Router } from '@angular/router'; @@ -31,6 +31,7 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio import { RouterMock } from '../../../../shared/mocks/router.mock'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; describe('ReviewersListComponent', () => { let component: ReviewersListComponent; @@ -45,6 +46,8 @@ describe('ReviewersListComponent', () => { let epersonMembers; let subgroupMembers; let paginationService; + let ePersonDtoModel1: EpersonDtoModel; + let ePersonDtoModel2: EpersonDtoModel; beforeEach(waitForAsync(() => { activeGroup = GroupMock; @@ -56,7 +59,7 @@ describe('ReviewersListComponent', () => { activeGroup: activeGroup, epersonMembers: epersonMembers, subgroupMembers: subgroupMembers, - findAllByHref(href: string): Observable>> { + findAllByHref(_href: string): Observable>> { return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getEPersonMembers())); }, searchByScope(scope: string, query: string): Observable>> { @@ -119,7 +122,6 @@ describe('ReviewersListComponent', () => { findById(id: string) { for (const group of allGroups) { if (group.id === id) { - console.log('found', group); return createSuccessfulRemoteDataObject$(group); } } @@ -167,9 +169,12 @@ describe('ReviewersListComponent', () => { fixture.debugElement.nativeElement.remove(); })); - it('should create ReviewersListComponent', inject([ReviewersListComponent], (comp: ReviewersListComponent) => { - expect(comp).toBeDefined(); - })); + beforeEach(() => { + ePersonDtoModel1 = new EpersonDtoModel(); + ePersonDtoModel1.eperson = EPersonMock; + ePersonDtoModel2 = new EpersonDtoModel(); + ePersonDtoModel2.eperson = EPersonMock2; + }); describe('when no group is selected', () => { beforeEach(() => { @@ -179,18 +184,18 @@ describe('ReviewersListComponent', () => { fixture.detectChanges(); }); - it('should show no epersons because no group is selected', () => { - const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); - expect(epersonIdsFound.length).toEqual(0); - epersonMembers.map((eperson: EPerson) => { - expect(epersonIdsFound.find((foundEl) => { - return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + it('should show no ePersons because no group is selected', () => { + const ePersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(ePersonIdsFound.length).toEqual(0); + epersonMembers.map((ePerson: EPerson) => { + expect(ePersonIdsFound.find((foundEl) => { + return (foundEl.nativeElement.textContent.trim() === ePerson.uuid); })).not.toBeTruthy(); }); }); }); - describe('when group is selected', () => { + describe('when a group is selected', () => { beforeEach(() => { component.ngOnChanges({ groupId: new SimpleChange(undefined, GroupMock.id, true) @@ -198,15 +203,50 @@ describe('ReviewersListComponent', () => { fixture.detectChanges(); }); - it('should show all eperson members of group', () => { - const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); - expect(epersonIdsFound.length).toEqual(1); - epersonMembers.map((eperson: EPerson) => { - expect(epersonIdsFound.find((foundEl) => { - return (foundEl.nativeElement.textContent.trim() === eperson.uuid); + it('should show all ePerson members of group', () => { + const ePersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); + expect(ePersonIdsFound.length).toEqual(1); + epersonMembers.map((ePerson: EPerson) => { + expect(ePersonIdsFound.find((foundEl: DebugElement) => { + return (foundEl.nativeElement.textContent.trim() === ePerson.uuid); })).toBeTruthy(); }); }); }); + + it('should replace the value when a new member is added when multipleReviewers is false', () => { + spyOn(component.selectedReviewersUpdated, 'emit'); + component.multipleReviewers = false; + component.selectedReviewers = [ePersonDtoModel1]; + + component.addMemberToGroup(ePersonDtoModel2); + + expect(component.selectedReviewers).toEqual([ePersonDtoModel2]); + expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([ePersonDtoModel2.eperson]); + }); + + it('should add the value when a new member is added when multipleReviewers is true', () => { + spyOn(component.selectedReviewersUpdated, 'emit'); + component.multipleReviewers = true; + component.selectedReviewers = [ePersonDtoModel1]; + + component.addMemberToGroup(ePersonDtoModel2); + + expect(component.selectedReviewers).toEqual([ePersonDtoModel1, ePersonDtoModel2]); + expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([ePersonDtoModel1.eperson, ePersonDtoModel2.eperson]); + }); + + it('should delete the member when present', () => { + spyOn(component.selectedReviewersUpdated, 'emit'); + ePersonDtoModel1.memberOfGroup = true; + component.selectedReviewers = [ePersonDtoModel1]; + + component.deleteMemberFromGroup(ePersonDtoModel1); + + expect(component.selectedReviewers).toEqual([]); + expect(ePersonDtoModel1.memberOfGroup).toBeFalse(); + expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([]); + }); + }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts index 13799fee4c..f0c6882617 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts @@ -5,7 +5,7 @@ import { MockComponent } from 'ng-mocks'; import { DSOSelectorComponent } from '../../../shared/dso-selector/dso-selector/dso-selector.component'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ClaimedTaskDataServiceStub } from '../../../shared/testing/claimed-task-data-service.stub'; -import { ActivatedRoute, convertToParamMap } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { of as observableOf } from 'rxjs'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; import { RouterTestingModule } from '@angular/router/testing'; @@ -54,9 +54,9 @@ describe('AdvancedWorkflowActionComponent', () => { id: workflowId, }), snapshot: { - queryParams: convertToParamMap({ + queryParams: { workflow: 'testaction', - }), + }, }, }, }, diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts index 321a6b954d..660e228c70 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts @@ -2,6 +2,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedWorkflowActionsLoaderComponent } from './advanced-workflow-actions-loader.component'; import { Router } from '@angular/router'; import { RouterStub } from '../../../shared/testing/router.stub'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { AdvancedWorkflowActionsDirective } from './advanced-workflow-actions.directive'; +import { + rendersAdvancedWorkflowTaskOption +} from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; +import { By } from '@angular/platform-browser'; +import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; + +const ADVANCED_WORKFLOW_ACTION_TEST = 'testaction'; describe('AdvancedWorkflowActionsLoaderComponent', () => { let component: AdvancedWorkflowActionsLoaderComponent; @@ -14,21 +23,61 @@ describe('AdvancedWorkflowActionsLoaderComponent', () => { await TestBed.configureTestingModule({ declarations: [ + AdvancedWorkflowActionsDirective, AdvancedWorkflowActionsLoaderComponent, ], providers: [ { provide: Router, useValue: router }, ], + }).overrideComponent(AdvancedWorkflowActionsLoaderComponent, { + set: { + changeDetection: ChangeDetectionStrategy.Default, + entryComponents: [AdvancedWorkflowActionTestComponent], + }, }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(AdvancedWorkflowActionsLoaderComponent); component = fixture.componentInstance; + component.type = ADVANCED_WORKFLOW_ACTION_TEST; fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + fixture.debugElement.nativeElement.remove(); + }); + + describe('When the component is rendered', () => { + it('should display the AdvancedWorkflowActionTestComponent when the type has been defined in a rendersAdvancedWorkflowTaskOption', () => { + spyOn(component, 'getComponentByWorkflowTaskOption').and.returnValue(AdvancedWorkflowActionTestComponent); + + component.ngOnInit(); + fixture.detectChanges(); + + expect(component.getComponentByWorkflowTaskOption).toHaveBeenCalledWith(ADVANCED_WORKFLOW_ACTION_TEST); + expect(fixture.debugElement.query(By.css('#AdvancedWorkflowActionsLoaderComponent'))).not.toBeNull(); + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should redirect to page not found when the type has not been defined in a rendersAdvancedWorkflowTaskOption', () => { + spyOn(component, 'getComponentByWorkflowTaskOption').and.returnValue(undefined); + component.type = 'nonexistingaction'; + + component.ngOnInit(); + fixture.detectChanges(); + + expect(component.getComponentByWorkflowTaskOption).toHaveBeenCalledWith('nonexistingaction'); + expect(router.navigate).toHaveBeenCalledWith([PAGE_NOT_FOUND_PATH]); + }); }); }); + +@rendersAdvancedWorkflowTaskOption(ADVANCED_WORKFLOW_ACTION_TEST) +@Component({ + // tslint:disable-next-line:component-selector + selector: '', + template: '', +}) +class AdvancedWorkflowActionTestComponent { +} From bd152dc0748a7a65a861ba043e8ac3cbf01a362d Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 2 Feb 2023 17:12:56 +0100 Subject: [PATCH 17/28] 99053: Fixed previous page not always returning the correct value - Fixed previousPage not correctly redirecting to urls with queryParams - Fixed previousPage returning to wrong page because of ReviewersListComponent --- ...vanced-workflow-action-select-reviewer.component.ts | 9 +++++++++ .../workflow-item-action-page.component.ts | 10 ++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index 68e67ee95e..e1eada0c48 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -19,6 +19,7 @@ import { NotificationsService } from '../../../shared/notifications/notification import { TranslateService } from '@ngx-translate/core'; import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; +import { ReviewersListComponent } from './reviewers-list/reviewers-list.component'; export const ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; @@ -130,4 +131,12 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf }; } + /** + * Hardcoded the previous page url because the {@link ReviewersListComponent} changes the previous route when + * switching between the different pages + */ + previousPage(): void { + void this.router.navigate(['/mydspace'], { queryParams: { configuration: 'workflow' } }); + } + } diff --git a/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts b/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts index 7f09f3a3d2..f1740c27a4 100644 --- a/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts +++ b/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts @@ -4,7 +4,7 @@ import { map, switchMap, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { WorkflowItem } from '../core/submission/models/workflowitem.model'; import { Item } from '../core/shared/item.model'; -import { ActivatedRoute, Data, Router } from '@angular/router'; +import { ActivatedRoute, Data, Router, Params } from '@angular/router'; import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service'; import { RouteService } from '../core/services/route.service'; import { NotificationsService } from '../shared/notifications/notifications.service'; @@ -72,7 +72,13 @@ export abstract class WorkflowItemActionPageComponent implements OnInit { if (isEmpty(url)) { url = '/mydspace'; } - this.router.navigateByUrl(url); + const params: Params = {}; + if (url.split('?').length > 1) { + for (const param of url.split('?')[1].split('&')) { + params[param.split('=')[0]] = param.split('=')[1]; + } + } + void this.router.navigate([url.split('?')[0]], { queryParams: params }); } ); } From 581afbbbb6eac8d88f7a6769bab9a3bc580b411f Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 2 Feb 2023 17:13:56 +0100 Subject: [PATCH 18/28] 99053: Hide return to pool button for second step of SelectSingleReviewer --- .../claimed-task/claimed-task-actions.component.html | 5 ----- .../claimed-task/claimed-task-actions.component.ts | 6 ------ 2 files changed, 11 deletions(-) diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html index 7454a80873..7dbb4454c3 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html @@ -13,10 +13,5 @@ {{"submission.workflow.generic.view" | translate}} - - - diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts index 65ddd76bc4..8f4d109ba1 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts @@ -44,12 +44,6 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent>; - /** - * The option used to render the "return to pool" component - * Every claimed task contains this option - */ - public returnToPoolOption = WORKFLOW_TASK_OPTION_RETURN_TO_POOL; - /** * Initialize instance variables * From 34e970827e362d6eb0cbb4e07ad55e45742169c9 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Mon, 6 Feb 2023 18:20:39 +0100 Subject: [PATCH 19/28] 99053: Invalidate cache by default when performing the WorkflowItemActionPageComponent action --- src/app/shared/testing/request-service.stub.ts | 12 ++++++++++++ ...advanced-workflow-action-rating.component.spec.ts | 3 +++ ...workflow-action-select-reviewer.component.spec.ts | 3 +++ ...nced-workflow-action-select-reviewer.component.ts | 4 +++- .../advanced-workflow-action.component.spec.ts | 3 +++ .../advanced-workflow-action.component.ts | 4 +++- .../workflow-item-action-page.component.spec.ts | 9 +++++++-- .../workflow-item-action-page.component.ts | 11 +++++++---- .../workflow-item-delete.component.ts | 3 +-- .../workflow-item-send-back.component.ts | 3 +-- 10 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 src/app/shared/testing/request-service.stub.ts diff --git a/src/app/shared/testing/request-service.stub.ts b/src/app/shared/testing/request-service.stub.ts new file mode 100644 index 0000000000..9add8898df --- /dev/null +++ b/src/app/shared/testing/request-service.stub.ts @@ -0,0 +1,12 @@ +import { Observable, of as observableOf } from 'rxjs'; + +/** + * Stub service for {@link RequestService}. + */ +export class RequestServiceStub { + + removeByHrefSubstring(_href: string): Observable { + return observableOf(true); + } + +} diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts index 4abbe1a0c1..c6a7dc2c07 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.spec.ts @@ -26,6 +26,8 @@ import { Item } from '../../../core/shared/item.model'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; import { RatingAdvancedWorkflowInfo } from '../../../core/tasks/models/rating-advanced-workflow-info.model'; +import { RequestService } from '../../../core/data/request.service'; +import { RequestServiceStub } from '../../../shared/testing/request-service.stub'; const claimedTaskId = '2'; const workflowId = '1'; @@ -80,6 +82,7 @@ describe('AdvancedWorkflowActionRatingComponent', () => { { provide: Router, useValue: new RouterStub() }, { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, + { provide: RequestService, useClass: RequestServiceStub }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts index 855bde79a7..2884dbcc8e 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.spec.ts @@ -23,6 +23,8 @@ import { Item } from '../../../core/shared/item.model'; import { EPersonMock, EPersonMock2 } from '../../../shared/testing/eperson.mock'; import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { RequestService } from '../../../core/data/request.service'; +import { RequestServiceStub } from '../../../shared/testing/request-service.stub'; const claimedTaskId = '2'; const workflowId = '1'; @@ -73,6 +75,7 @@ describe('AdvancedWorkflowActionSelectReviewerComponent', () => { { provide: RouteService, useValue: routeServiceStub }, { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, + { provide: RequestService, useClass: RequestServiceStub }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts index e1eada0c48..b9f90e1736 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts @@ -20,6 +20,7 @@ import { TranslateService } from '@ngx-translate/core'; import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ReviewersListComponent } from './reviewers-list/reviewers-list.component'; +import { RequestService } from 'src/app/core/data/request.service'; export const ADVANCED_WORKFLOW_TASK_OPTION_SELECT_REVIEWER = 'submit_select_reviewer'; export const ADVANCED_WORKFLOW_ACTION_SELECT_REVIEWER = 'selectrevieweraction'; @@ -60,8 +61,9 @@ export class AdvancedWorkflowActionSelectReviewerComponent extends AdvancedWorkf protected translationService: TranslateService, protected workflowActionService: WorkflowActionDataService, protected claimedTaskDataService: ClaimedTaskDataService, + protected requestService: RequestService, ) { - super(route, workflowItemService, router, routeService, notificationsService, translationService, workflowActionService, claimedTaskDataService); + super(route, workflowItemService, router, routeService, notificationsService, translationService, workflowActionService, claimedTaskDataService, requestService); } ngOnDestroy(): void { diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts index f0c6882617..f57adcb05f 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts @@ -18,6 +18,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { WorkflowActionDataServiceStub } from '../../../shared/testing/workflow-action-data-service.stub'; import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; import { WorkflowItemDataServiceStub } from '../../../shared/testing/workflow-item-data-service.stub'; +import { RequestService } from '../../../core/data/request.service'; +import { RequestServiceStub } from '../../../shared/testing/request-service.stub'; const workflowId = '1'; @@ -65,6 +67,7 @@ describe('AdvancedWorkflowActionComponent', () => { { provide: RouteService, useValue: routeServiceStub }, { provide: WorkflowActionDataService, useValue: workflowActionDataService }, { provide: WorkflowItemDataService, useValue: workflowItemDataService }, + { provide: RequestService, useClass: RequestServiceStub }, ], }).compileComponents(); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts index 982e5f8eac..61bc60cdbd 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts @@ -12,6 +12,7 @@ import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operato import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { map } from 'rxjs/operators'; import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; +import { RequestService } from '../../../core/data/request.service'; /** * Abstract component for rendering an advanced claimed task's workflow page @@ -36,8 +37,9 @@ export abstract class AdvancedWorkflowActionComponent extends WorkflowItemAction protected translationService: TranslateService, protected workflowActionService: WorkflowActionDataService, protected claimedTaskDataService: ClaimedTaskDataService, + protected requestService: RequestService, ) { - super(route, workflowItemService, router, routeService, notificationsService, translationService); + super(route, workflowItemService, router, routeService, notificationsService, translationService, requestService); } ngOnInit(): void { diff --git a/src/app/workflowitems-edit-page/workflow-item-action-page.component.spec.ts b/src/app/workflowitems-edit-page/workflow-item-action-page.component.spec.ts index c7b9858836..11f88788dc 100644 --- a/src/app/workflowitems-edit-page/workflow-item-action-page.component.spec.ts +++ b/src/app/workflowitems-edit-page/workflow-item-action-page.component.spec.ts @@ -16,6 +16,8 @@ import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; import { ActivatedRouteStub } from '../shared/testing/active-router.stub'; import { RouterStub } from '../shared/testing/router.stub'; import { NotificationsServiceStub } from '../shared/testing/notifications-service.stub'; +import { RequestService } from '../core/data/request.service'; +import { RequestServiceStub } from '../shared/testing/request-service.stub'; const type = 'testType'; describe('WorkflowItemActionPageComponent', () => { @@ -52,6 +54,7 @@ describe('WorkflowItemActionPageComponent', () => { { provide: RouteService, useValue: {} }, { provide: NotificationsService, useClass: NotificationsServiceStub }, { provide: WorkflowItemDataService, useValue: wfiService }, + { provide: RequestService, useClass: RequestServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] }) @@ -110,8 +113,10 @@ class TestComponent extends WorkflowItemActionPageComponent { protected router: Router, protected routeService: RouteService, protected notificationsService: NotificationsService, - protected translationService: TranslateService) { - super(route, workflowItemService, router, routeService, notificationsService, translationService); + protected translationService: TranslateService, + protected requestService: RequestService, + ) { + super(route, workflowItemService, router, routeService, notificationsService, translationService, requestService); } getType(): string { diff --git a/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts b/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts index f1740c27a4..0f6a222d85 100644 --- a/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts +++ b/src/app/workflowitems-edit-page/workflow-item-action-page.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, forkJoin } from 'rxjs'; import { map, switchMap, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { WorkflowItem } from '../core/submission/models/workflowitem.model'; @@ -11,6 +11,7 @@ import { NotificationsService } from '../shared/notifications/notifications.serv import { RemoteData } from '../core/data/remote-data'; import { getAllSucceededRemoteData, getRemoteDataPayload } from '../core/shared/operators'; import { isEmpty } from '../shared/empty.util'; +import { RequestService } from '../core/data/request.service'; /** * Abstract component representing a page to perform an action on a workflow item @@ -29,7 +30,9 @@ export abstract class WorkflowItemActionPageComponent implements OnInit { protected router: Router, protected routeService: RouteService, protected notificationsService: NotificationsService, - protected translationService: TranslateService) { + protected translationService: TranslateService, + protected requestService: RequestService, + ) { } /** @@ -45,9 +48,9 @@ export abstract class WorkflowItemActionPageComponent implements OnInit { * Performs the action and shows a notification based on the outcome of the action */ performAction() { - this.wfi$.pipe( + forkJoin([this.wfi$, this.requestService.removeByHrefSubstring('/discover')]).pipe( take(1), - switchMap((wfi: WorkflowItem) => this.sendRequest(wfi.id)) + switchMap(([wfi]) => this.sendRequest(wfi.id)) ).subscribe((successful: boolean) => { if (successful) { const title = this.translationService.get('workflow-item.' + this.type + '.notification.success.title'); diff --git a/src/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.ts b/src/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.ts index 451469b5e4..faa2bf543a 100644 --- a/src/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.ts +++ b/src/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.ts @@ -27,7 +27,7 @@ export class WorkflowItemDeleteComponent extends WorkflowItemActionPageComponent protected notificationsService: NotificationsService, protected translationService: TranslateService, protected requestService: RequestService) { - super(route, workflowItemService, router, routeService, notificationsService, translationService); + super(route, workflowItemService, router, routeService, notificationsService, translationService, requestService); } /** @@ -42,7 +42,6 @@ export class WorkflowItemDeleteComponent extends WorkflowItemActionPageComponent * @param id The id of the WorkflowItem */ sendRequest(id: string): Observable { - this.requestService.removeByHrefSubstring('/discover'); return this.workflowItemService.delete(id).pipe( getFirstCompletedRemoteData(), map((response: RemoteData) => response.hasSucceeded) diff --git a/src/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.ts b/src/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.ts index 002e5dcc9a..432b417f43 100644 --- a/src/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.ts +++ b/src/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.ts @@ -23,7 +23,7 @@ export class WorkflowItemSendBackComponent extends WorkflowItemActionPageCompone protected notificationsService: NotificationsService, protected translationService: TranslateService, protected requestService: RequestService) { - super(route, workflowItemService, router, routeService, notificationsService, translationService); + super(route, workflowItemService, router, routeService, notificationsService, translationService, requestService); } /** @@ -38,7 +38,6 @@ export class WorkflowItemSendBackComponent extends WorkflowItemActionPageCompone * @param id The id of the WorkflowItem */ sendRequest(id: string): Observable { - this.requestService.removeByHrefSubstring('/discover'); return this.workflowItemService.sendBack(id); } } From 615c74288efa93b37335045809646a075dd2ecc7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Mon, 6 Feb 2023 19:01:45 +0100 Subject: [PATCH 20/28] 99053: Added DeclinedTask search result banner --- .../my-dspace-page/my-dspace-search.module.ts | 2 + ...med-task-actions-decline-task.component.ts | 16 +++ ...aimed-declined-task-search-result.model.ts | 2 +- ...-declined-task-task-search-result.model.ts | 8 ++ .../my-dspace-item-status-type.ts | 1 + ...-search-result-list-element.component.html | 2 +- ...arch-result-list-element.component.spec.ts | 6 +- ...-search-result-list-element.component.html | 2 +- ...rch-result-list-element.component.spec.ts} | 6 +- ...-search-result-list-element.component.html | 10 ++ ...arch-result-list-element.component.spec.ts | 106 ++++++++++++++++++ ...sk-search-result-list-element.component.ts | 66 +++++++++++ src/assets/i18n/en.json5 | 6 + 13 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 src/app/shared/object-collection/shared/claimed-declined-task-task-search-result.model.ts rename src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/{claimed-declided-search-result-list-element.component.spec.ts => claimed-declined-search-result-list-element.component.spec.ts} (96%) create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.html create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.ts diff --git a/src/app/my-dspace-page/my-dspace-search.module.ts b/src/app/my-dspace-page/my-dspace-search.module.ts index 67a4f0818e..97138ab8db 100644 --- a/src/app/my-dspace-page/my-dspace-search.module.ts +++ b/src/app/my-dspace-page/my-dspace-search.module.ts @@ -16,6 +16,7 @@ import { WorkflowItemSearchResultListElementComponent } from '../shared/object-l import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component'; import { ClaimedApprovedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component'; import { ClaimedDeclinedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component'; +import { ClaimedDeclinedTaskSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -24,6 +25,7 @@ const ENTRY_COMPONENTS = [ ClaimedSearchResultListElementComponent, ClaimedApprovedSearchResultListElementComponent, ClaimedDeclinedSearchResultListElementComponent, + ClaimedDeclinedTaskSearchResultListElementComponent, PoolSearchResultListElementComponent, ItemSearchResultDetailElementComponent, WorkspaceItemSearchResultDetailElementComponent, diff --git a/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts index ab867d62d6..5afbde7d7d 100644 --- a/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts @@ -6,6 +6,12 @@ import { NotificationsService } from '../../../notifications/notifications.servi import { TranslateService } from '@ngx-translate/core'; import { SearchService } from '../../../../core/shared/search/search.service'; import { RequestService } from '../../../../core/data/request.service'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { + ClaimedDeclinedTaskTaskSearchResult +} from '../../../object-collection/shared/claimed-declined-task-task-search-result.model'; +import { Observable, of as observableOf } from 'rxjs'; +import { RemoteData } from 'src/app/core/data/remote-data'; export const WORKFLOW_TASK_OPTION_DECLINE_TASK = 'submit_decline_task'; @@ -31,4 +37,14 @@ export class ClaimedTaskActionsDeclineTaskComponent extends ClaimedTaskActionsAb super(injector, router, notificationsService, translate, searchService, requestService); } + reloadObjectExecution(): Observable | DSpaceObject> { + return observableOf(this.object); + } + + convertReloadedObject(dso: DSpaceObject): DSpaceObject { + return Object.assign(new ClaimedDeclinedTaskTaskSearchResult(), dso, { + indexableObject: dso + }); + } + } diff --git a/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts b/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts index eaf63bf6dd..0afb79a274 100644 --- a/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts +++ b/src/app/shared/object-collection/shared/claimed-declined-task-search-result.model.ts @@ -2,7 +2,7 @@ import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.mode import { SearchResult } from '../../search/models/search-result.model'; /** - * Represents a search result object of a Declined ClaimedTask object + * Represents a search result object of a Declined/Rejected ClaimedTask object (sent back to the submitter) */ export class ClaimedDeclinedTaskSearchResult extends SearchResult { } diff --git a/src/app/shared/object-collection/shared/claimed-declined-task-task-search-result.model.ts b/src/app/shared/object-collection/shared/claimed-declined-task-task-search-result.model.ts new file mode 100644 index 0000000000..dffb3c1313 --- /dev/null +++ b/src/app/shared/object-collection/shared/claimed-declined-task-task-search-result.model.ts @@ -0,0 +1,8 @@ +import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model'; +import { SearchResult } from '../../search/models/search-result.model'; + +/** + * Represents a search result object of a Declined ClaimedTask object (sent back to the Review Managers) + */ +export class ClaimedDeclinedTaskTaskSearchResult extends SearchResult { +} diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts index 5cf4d91b20..ec101f1ee7 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts @@ -6,4 +6,5 @@ export enum MyDspaceItemStatusType { ARCHIVED = 'mydspace.status.archived', DECLINED = 'mydspace.status.declined', APPROVED = 'mydspace.status.approved', + DECLINED_TASk = 'mydspace.status.declined-task', } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html index 8ebcdbd69a..97ea34cef0 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html @@ -1,6 +1,6 @@
{{'item.edit.modify.overview.field'| translate}}